0

I have a Restaurant model that use Geocoder to gather the city, state, and neighborhood on a before_validation callback.

class Restaurant < ActiveRecord::Base
  # attrs: :name, :address, :city_id, :neighborhood_id

  ...

  before_validation :geocode

  geocoded_by :address do |obj,results|
    if geo = results.first
      obj.city = City.where(name: geo.city).first_or_create
      obj.city.update_attributes(state: State.where(name: geo.state).first_or_create)

      obj.neighborhood = Neighborhood.where(name: geo.neighborhood).first_or_create
      obj.neighborhood.update_attributes(city: City.where(name: geo.city).first_or_create)

      obj.longitude = geo.longitude
      obj.latitude = geo.latitude
    end
  end
end

In my City model I've put together a custom slug which uses the city's name and the state's name that it belongs to.

class City < ActiveRecord::Base
  # attrs :name, state_id

  belongs_to :state

  friendly_id :state_slug, use: :slugged

  def state_slug
    "#{name} #{state.name}"
  end
end

Whenever I create a new restaurant I'm the error:

undefined method `name' for nil:NilClass

def state_slug
  "#{name} #{state.name}"
end

Understandably because there isn't a city or state that has yet to be persisted to the database. I'm wondering how can I configure my callback to get this to work?

Richard Peck
  • 76,116
  • 9
  • 93
  • 147
Carl Edwards
  • 13,826
  • 11
  • 57
  • 119

2 Answers2

0

Write this method in your City model. This will generate slug when your state id got changed.

def should_generate_new_friendly_id?
    new_record? || state_id_changed?
end

And make this little change to the following method.

  def state_slug
   "#{name} #{state.name}" if state.present?
  end
Rubyrider
  • 3,567
  • 1
  • 28
  • 34
  • This will be taken care by friendly when you are creating, checkout the first condition new_record?. This will make this happening. Did you face any issue while following my solution? Let me know I can share. This thing can be done in many ways. – Rubyrider Oct 31 '15 at 04:33
0

The only way I can think of is to use inverse_of:

#app/models/state.rb
class State < ActiveRecord::Base
   has_many :cities, inverse_of: :state
end

#app/models/city.rb
class City < ActiveRecord::Base
  belongs_to :state, inverse_of: :cities
end

There isn't much documentation about this; it basically means you're able to call the associated data in their respective models, IE city.state (even if state is not saved).

Thus, if you're setting a state each time you add a city (and they're associated), you should be able to call the following (validation):

#app/models/city.rb
class City < ActiveRecord::Base
   belongs_to :state, inverse_of: :cities
   validates :state, presence: true

   friendly_id :state_slug, use: :slugged

   private

   def state_slug
     "#{name} #{state.name}"
   end
end
Richard Peck
  • 76,116
  • 9
  • 93
  • 147