carlosantoniodasilva / i18n_alchemy

I18n date/number parsing/localization - RMU Project
http://mendicantuniversity.org/
MIT License
159 stars 34 forks source link

Exception when localize a model with polymorphic belongs to #18

Open sobrinho opened 12 years ago

sobrinho commented 12 years ago

Hey mate,

Seems like we have a "integration" problem with active record and i18n alchemy but I'm not sure.

Just to you follow my production code, we can't accept nested attributes for polymorphic associations:

class Person < ActiveRecord::Base
  belongs_to :personable, :polymorphic => true

  accepts_nested_attributes_for :personable
end
Loading development environment (Rails 3.2.3)
irb(main):001:0> person = Person.new
=> #<Person ...>
irb(main):002:0> person.localized
=> #<Person ...>
irb(main):003:0> person.personable
=> nil
irb(main):004:0> person.assign_attributes(:personable_type => 'Company', :personable_attributes => { :cnpj => 'nohup' })
ArgumentError: Cannot build association personable. Are you trying to build a polymorphic one-to-one association?
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/activerecord-3.2.3/lib/active_record/nested_attributes.rb:339:in `assign_nested_attributes_for_one_to_one_association'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/activerecord-3.2.3/lib/active_record/nested_attributes.rb:288:in `personable_attributes='
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/activerecord-3.2.3/lib/active_record/attribute_assignment.rb:94:in `block in assign_attributes'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/activerecord-3.2.3/lib/active_record/attribute_assignment.rb:93:in `each'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/activerecord-3.2.3/lib/active_record/attribute_assignment.rb:93:in `assign_attributes'
    from (irb):4
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/railties-3.2.3/lib/rails/commands/console.rb:47:in `start'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/railties-3.2.3/lib/rails/commands/console.rb:8:in `start'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/railties-3.2.3/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

It's ok, not the best solution but you can work around it:

class Person < ActiveRecord::Base
  belongs_to :personable, :polymorphic => true

  accepts_nested_attributes_for :personable

  def build_personable(attributes, options = {})
    self.personable = personable_type.constantize.new(attributes, options)
  end
end

And, it just works now:

Loading development environment (Rails 3.2.3)
irb(main):001:0> person = Person.new
=> #<Person ...>
irb(main):002:0> person.localized
=> #<Person ...>
irb(main):003:0> person.personable
=> nil
irb(main):004:0> person.assign_attributes(:personable_type => 'Company', :personable_attributes => { :cnpj => 'nohup' })
=> nil
irb(main):005:0> person.personable
=> #<Company ...>

But, if we try to use localized version:

irb(main):006:0> person.localized.assign_attributes(:personable_type => 'Company', :personable_attributes => { :cnpj => 'nohup' })
NameError: uninitialized constant Person::Personable
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/activerecord-3.2.3/lib/active_record/inheritance.rb:119:in `compute_type'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/activerecord-3.2.3/lib/active_record/reflection.rb:172:in `klass'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/bundler/gems/i18n_alchemy-259277ade608/lib/i18n_alchemy/association_parser.rb:49:in `proxy'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/bundler/gems/i18n_alchemy-259277ade608/lib/i18n_alchemy/association_parser.rb:25:in `parse'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/bundler/gems/i18n_alchemy-259277ade608/lib/i18n_alchemy/attributes_parsing.rb:43:in `block in parse_attributes'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/bundler/gems/i18n_alchemy-259277ade608/lib/i18n_alchemy/attributes_parsing.rb:40:in `each'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/bundler/gems/i18n_alchemy-259277ade608/lib/i18n_alchemy/attributes_parsing.rb:40:in `parse_attributes'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/bundler/gems/i18n_alchemy-259277ade608/lib/i18n_alchemy/attributes_parsing.rb:12:in `assign_attributes'
    from (irb):6
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/railties-3.2.3/lib/rails/commands/console.rb:47:in `start'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/railties-3.2.3/lib/rails/commands/console.rb:8:in `start'
    from /Users/sobrinho/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/railties-3.2.3/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

The alchemy offending code is that:

def proxy
  @proxy ||= @association.klass.new.localized
end

This won't work because polymorphic associations do not have the class defined.

I'm not sure about using build_#{association_name} because we will mutate the object, but maybe that is expected if we are receiving the #{association_name}_attributes in a localized proxy.

Anyway, I need this polymorphic association to be assigned localized.

Thoughts? :-)

sobrinho commented 12 years ago

I forgot, we are workarounding this issue using that bizarre code:

class Person < ActiveRecord::Base
  belongs_to :personable, :polymorphic => true

  accepts_nested_attributes_for :personable

  def personable_attributes=(personable_attributes, options = {})
    self.personable ||= personable_type.constantize.new
    personable.localized.assign_attributes(personable_attributes, options)
  end
end

This version is more ugly than first option, do not work for all cases and assume we want localized version all the time but it's working for now ;)

carlosantoniodasilva commented 12 years ago

I don't have an idea off the top of my head, but I'll try to search and think what can be done. Meanwhile if you want to send a failing test case, would help :). Thanks!

sebastianludwig commented 11 years ago

Any news on this one?

carlosantoniodasilva commented 11 years ago

Not actually, I haven't stopped to put much thought on it :)

I may have some time available to get back to it in a couple weeks, in the meanwhile any help would appreciated. Thanks!