makandra / active_type

Make any Ruby object quack like ActiveRecord
MIT License
1.09k stars 74 forks source link

i18n_key for derived record is the same as parent record #115

Closed fsateler closed 4 years ago

fsateler commented 4 years ago

Which creates pollution for the base class translations.

The setup

en:
  activerecord:
    attributes:
      some_record_extended:
        new_attribute: My Fancy Attribute
class SomeRecord < ActiveRecord::Base
end

class SomeRecordExtended < ActiveType::Record[::SomeRecord]
  attr_accessor :new_attribute
end

Expected Result

SomeRecordExtended.human_attribute_name :new_attribute # => 'My Fancy Attribute'

Actual Result

SomeRecordExtended.human_attribute_name :new_attribute # => 'New attribute'

Investigation

Turns out that ActiveType::RecordExtension::Inheritance simply delegates model_name to the base record class:

https://github.com/makandra/active_type/blob/0973a8fb300f7c2dcb8d7aa903119dd23506ccaf/lib/active_type/record_extension/inheritance.rb#L30-L32

AFAICT, this necessary because it is desired that param_key and friends be the same. However, this has the problem that i18n_key is the same too:

SomeRecord.model_name.i18n_key # => 'some_record'
SomeRecordExtended.model_name.i18n_key # => 'some_record'

This results in having to declare the extended properties in the translation for the base record class, which is confusing and pollutes the translations.

Solutions

My first local hack was to do this:

def self.model_name
  super.tap{|n| n.instance_variable_set(:@i18n_key, name.underscore) }
end

Which is of course super hacky. An alternative would be to create a Name subclass:

class ActiveTypeName < ActiveModel::Name
  def initialize(klass, extended_record_base_klass)
    super(extended_record_base_klass)
    @i18n_key = klass.name.underscore
  end

  attr_reader :i18n_key # Redefine to avoid depending on internal rails behavior
end

and then redefine model_name:

def model_name
   @model_name ||= ActiveTypeName.new(self, extended_record_base_class)
end

A third alternative would be to monkey_patch i18n_key:

def model_name
   @model_name ||= begin
     dup_model_name = extended_record_base_class.model_name.dup
     key = name.underscore
     dup_model_name.define_singleton_method(:i18n_key) { key }
     dup_model_name
   end
end
fsateler commented 4 years ago

I've posted a PR implementing the third option. Seems like the less intrusive to me.