-
-
Notifications
You must be signed in to change notification settings - Fork 729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
2765 Fix geocoding to display enterprises on map #2872
Changes from all commits
b109f6d
c4437a6
34849c4
5021ed9
a8705ca
61797ff
8fb81bb
e96cab9
d197c85
9698fd3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,27 @@ | ||
Spree::Address.class_eval do | ||
has_one :enterprise | ||
has_one :enterprise, dependent: :restrict | ||
belongs_to :country, class_name: "Spree::Country" | ||
|
||
after_save :touch_enterprise | ||
|
||
geocoded_by :geocode_address | ||
|
||
delegate :name, :to => :state, :prefix => true, :allow_nil => true | ||
delegate :name, to: :state, prefix: true, allow_nil: true | ||
|
||
def geocode_address | ||
geocode_address = [address1, address2, zipcode, city, country.andand.name, state.andand.name] | ||
filtered_address = geocode_address.select{ |field| !field.nil? && field != '' } | ||
filtered_address.compact.join(', ') | ||
render_address([address1, address2, zipcode, city, country.andand.name, state.andand.name]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these methods should be in a separate service, some sort of geocoder_service that formats addresses according to different given inputs. And this service methods should be unit tested at least with simple positive examples. another option, is to add functional tests to avoid this bug from re-appearing without us noticing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that these methods would be better in a separate service. But I didn't want to go too far out of scope. All commits except the last one with the initializer are pure refactoring and don't contribute anything to fixing the original issue. It was just me understanding the code and cleaning it up along the way. I thought about not including them in the PR, but then I thought it might be useful to others to know where the geocoding is actually happening. Before we pull the rendering out into a service, we should discuss using our own model and table for enterprise addresses. We have some ugly hacks to use We also added the By the way, the rendering method is unit tested: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ahaha, I meant unit tests on the rendering methods only! the tests were there already. sorry about that. the full refactor of address is a more complicated issue, agree. thanks for the details! I think we could still just extract these methods to a geocoding service separate from the address class but fully appreciate what you said about refactoring. it's important we feel encouraged to refactor but don't feel pressed to make it perfect. a little improvement is always very welcome! |
||
end | ||
|
||
def full_address | ||
full_address = [address1, address2, city, zipcode, state.andand.name] | ||
filtered_address = full_address.select{ |field| !field.nil? && field != '' } | ||
filtered_address.compact.join(', ') | ||
render_address([address1, address2, city, zipcode, state.andand.name]) | ||
end | ||
|
||
def address_part1 | ||
address_part1 = [address1, address2] | ||
filtered_address = address_part1.select{ |field| !field.nil? && field != '' } | ||
filtered_address.compact.join(', ') | ||
render_address([address1, address2]) | ||
end | ||
|
||
def address_part2 | ||
address_part2= [city, zipcode, state.andand.name] | ||
filtered_address = address_part2.select{ |field| !field.nil? && field != '' } | ||
filtered_address.compact.join(', ') | ||
render_address([city, zipcode, state.andand.name]) | ||
end | ||
|
||
private | ||
|
@@ -38,26 +30,7 @@ def touch_enterprise | |
enterprise.andand.touch | ||
end | ||
|
||
# We have a hard-to-track-down bug around invalid addresses with all-nil fields finding | ||
# their way into the database. I don't know what the source of them is, so this patch | ||
# is designed to track them down. | ||
# This is intended to be a temporary investigative measure, and should be removed from the | ||
# code base shortly. If it's past 17-10-2013, take it out. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's exactly 5 years later!!! |
||
# | ||
#-- Rohan, 17-9-2913 | ||
def create | ||
if self.zipcode.nil? | ||
Bugsnag.notify RuntimeError.new('Creating a Spree::Address with nil values') | ||
end | ||
|
||
super | ||
end | ||
|
||
def update(attribute_names = @attributes.keys) | ||
if self.zipcode.nil? | ||
Bugsnag.notify RuntimeError.new('Updating a Spree::Address with nil values') | ||
end | ||
|
||
super(attribute_names) | ||
def render_address(parts) | ||
parts.select(&:present?).join(', ') | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Google requires an API key with a billing account to use their API. | ||
# The key is stored in config/application.yml. | ||
Geocoder.configure( | ||
use_https: true, | ||
api_key: ENV.fetch('GOOGLE_MAPS_API_KEY', nil) | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,72 @@ | ||
require 'spec_helper' | ||
|
||
describe Spree::Address do | ||
let(:address) { build(:address) } | ||
let(:enterprise_address) { build(:address, enterprise: build(:enterprise)) } | ||
|
||
describe "associations" do | ||
it { should have_one(:enterprise) } | ||
it { is_expected.to have_one(:enterprise) } | ||
end | ||
|
||
describe "delegation" do | ||
it { should delegate(:name).to(:state).with_prefix } | ||
it { is_expected.to delegate(:name).to(:state).with_prefix } | ||
end | ||
|
||
describe "geocode address" do | ||
let(:address) { FactoryBot.build(:address) } | ||
describe "destroy" do | ||
it "can be deleted" do | ||
expect { address.destroy }.to_not raise_error | ||
end | ||
|
||
it "cannot be deleted with associated enterprise" do | ||
expect do | ||
enterprise_address.destroy | ||
end.to raise_error ActiveRecord::DeleteRestrictionError | ||
end | ||
end | ||
|
||
describe "geocode address" do | ||
it "should include address1, address2, zipcode, city, state and country" do | ||
address.geocode_address.should include(address.address1) | ||
address.geocode_address.should include(address.address2) | ||
address.geocode_address.should include(address.zipcode) | ||
address.geocode_address.should include(address.city) | ||
address.geocode_address.should include(address.state.name) | ||
address.geocode_address.should include(address.country.name) | ||
expect(address.geocode_address).to include(address.address1) | ||
expect(address.geocode_address).to include(address.address2) | ||
expect(address.geocode_address).to include(address.zipcode) | ||
expect(address.geocode_address).to include(address.city) | ||
expect(address.geocode_address).to include(address.state.name) | ||
expect(address.geocode_address).to include(address.country.name) | ||
end | ||
|
||
it "should not include empty fields" do | ||
address.address2 = nil | ||
address.city = "" | ||
|
||
address.geocode_address.split(',').length.should eql(4) | ||
expect(address.geocode_address.split(',').length).to eql(4) | ||
end | ||
end | ||
|
||
describe "full address" do | ||
let(:address) { FactoryBot.build(:address) } | ||
|
||
it "should include address1, address2, zipcode, city and state" do | ||
address.full_address.should include(address.address1) | ||
address.full_address.should include(address.address2) | ||
address.full_address.should include(address.zipcode) | ||
address.full_address.should include(address.city) | ||
address.full_address.should include(address.state.name) | ||
address.full_address.should_not include(address.country.name) | ||
expect(address.full_address).to include(address.address1) | ||
expect(address.full_address).to include(address.address2) | ||
expect(address.full_address).to include(address.zipcode) | ||
expect(address.full_address).to include(address.city) | ||
expect(address.full_address).to include(address.state.name) | ||
expect(address.full_address).not_to include(address.country.name) | ||
end | ||
|
||
it "should not include empty fields" do | ||
address.address2 = nil | ||
address.city = "" | ||
|
||
address.full_address.split(',').length.should eql(3) | ||
expect(address.full_address.split(',').length).to eql(3) | ||
end | ||
end | ||
|
||
describe "setters" do | ||
it "lets us set a country" do | ||
expect { Spree::Address.new.country = "A country" }.to raise_error ActiveRecord::AssociationTypeMismatch | ||
end | ||
end | ||
|
||
describe "notifying bugsnag when saved with missing data" do | ||
it "notifies on create" do | ||
Bugsnag.should_receive(:notify) | ||
a = Spree::Address.new zipcode: nil | ||
a.save validate: false | ||
end | ||
|
||
it "notifies on update" do | ||
Bugsnag.should_receive(:notify) | ||
a = create(:address) | ||
a.zipcode = nil | ||
a.save validate: false | ||
expect do | ||
Spree::Address.new.country = "A country" | ||
end.to raise_error ActiveRecord::AssociationTypeMismatch | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a test. Could be a simple one trying to delete an address used in an enterprise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I think I agree that a unit test could be useful to validate that this Rails magic works as intended. But I hesitated because we are not deleting address anywhere in the code. So if it's not a requirement, if it's not a specification to delete an address, why should it be added to the specs? I'll still do it for code coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool, thanks!
imo, if it's worth a change in the code, it's worth a test. the only case I would skip the test is if it is too difficult to test for the value it adds, which is not the case. this is only my opinion :-)