diff options
Diffstat (limited to 'activemodel')
-rw-r--r-- | activemodel/CHANGELOG | 2 | ||||
-rw-r--r-- | activemodel/lib/active_model/attribute_methods.rb | 44 | ||||
-rw-r--r-- | activemodel/lib/active_model/errors.rb | 4 | ||||
-rw-r--r-- | activemodel/lib/active_model/naming.rb | 15 | ||||
-rw-r--r-- | activemodel/lib/active_model/translation.rb | 2 | ||||
-rw-r--r-- | activemodel/test/cases/attribute_methods_test.rb | 10 | ||||
-rw-r--r-- | activemodel/test/cases/translation_test.rb | 7 | ||||
-rw-r--r-- | activemodel/test/cases/validations/i18n_validation_test.rb | 19 | ||||
-rw-r--r-- | activemodel/test/models/person.rb | 7 |
9 files changed, 46 insertions, 64 deletions
diff --git a/activemodel/CHANGELOG b/activemodel/CHANGELOG index 9dd5e03685..3082c7186a 100644 --- a/activemodel/CHANGELOG +++ b/activemodel/CHANGELOG @@ -2,6 +2,8 @@ * Added ActiveModel::SecurePassword to encapsulate dead-simple password usage with BCrypt encryption and salting [DHH] +* ActiveModel::AttributeMethods allows attributes to be defined on demand [Alexander Uvarov] + *Rails 3.0.2 (unreleased)* diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 8f3782eb48..2a99450a3d 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -260,30 +260,30 @@ module ActiveModel # end # end def define_attribute_methods(attr_names) - return if attribute_methods_generated? - attr_names.each do |attr_name| - attribute_method_matchers.each do |matcher| - unless instance_method_already_implemented?(matcher.method_name(attr_name)) - generate_method = "define_method_#{matcher.prefix}attribute#{matcher.suffix}" + attr_names.each { |attr_name| define_attribute_method(attr_name) } + end + + def define_attribute_method(attr_name) + attribute_method_matchers.each do |matcher| + unless instance_method_already_implemented?(matcher.method_name(attr_name)) + generate_method = "define_method_#{matcher.prefix}attribute#{matcher.suffix}" - if respond_to?(generate_method) - send(generate_method, attr_name) - else - method_name = matcher.method_name(attr_name) + if respond_to?(generate_method) + send(generate_method, attr_name) + else + method_name = matcher.method_name(attr_name) - generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 - if method_defined?('#{method_name}') - undef :'#{method_name}' - end - define_method('#{method_name}') do |*args| - send('#{matcher.method_missing_target}', '#{attr_name}', *args) - end - STR - end + generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 + if method_defined?('#{method_name}') + undef :'#{method_name}' + end + define_method('#{method_name}') do |*args| + send('#{matcher.method_missing_target}', '#{attr_name}', *args) + end + STR end end end - @attribute_methods_generated = true end # Removes all the previously dynamically defined methods from the class @@ -291,7 +291,6 @@ module ActiveModel generated_attribute_methods.module_eval do instance_methods.each { |m| undef_method(m) } end - @attribute_methods_generated = nil end # Returns true if the attribute methods defined have been generated. @@ -303,11 +302,6 @@ module ActiveModel end end - # Returns true if the attribute methods defined have been generated. - def attribute_methods_generated? - @attribute_methods_generated ||= nil - end - protected def instance_method_already_implemented?(method_name) method_defined?(method_name) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 5e3cf510b0..c2f0228785 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -295,8 +295,8 @@ module ActiveModel type = options.delete(:message) if options[:message].is_a?(Symbol) defaults = @base.class.lookup_ancestors.map do |klass| - [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}", - :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ] + [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.attributes.#{attribute}.#{type}", + :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.#{type}" ] end defaults << options.delete(:message) diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 3f430f94a6..eb9b847509 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -4,7 +4,7 @@ require 'active_support/core_ext/module/introspection' module ActiveModel class Name < String - attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key, :i18n_key + attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key alias_method :cache_key, :collection def initialize(klass, namespace = nil) @@ -20,7 +20,6 @@ module ActiveModel @partial_path = "#{@collection}/#{@element}".freeze @param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze - @i18n_key = _singularize(self, '.').to_sym end # Transform the model name into a more humane format, using I18n. By default, @@ -34,7 +33,7 @@ module ActiveModel @klass.respond_to?(:i18n_scope) defaults = @klass.lookup_ancestors.map do |klass| - klass.model_name.i18n_key + klass.model_name.underscore.to_sym end defaults << options[:default] if options[:default] @@ -45,10 +44,9 @@ module ActiveModel end private - - def _singularize(string, replacement='_') - ActiveSupport::Inflector.underscore(string).tr('/', replacement) - end + def _singularize(str) + ActiveSupport::Inflector.underscore(str).tr('/', '_') + end end # == Active Model Naming @@ -64,9 +62,6 @@ module ActiveModel # BookCover.model_name # => "BookCover" # BookCover.model_name.human # => "Book cover" # - # BookCover.model_name.i18n_key # => "book_cover" - # BookModule::BookCover.model_name.i18n_key # => "book_module.book_cover" - # # Providing the functionality that ActiveModel::Naming provides in your object # is required to pass the Active Model Lint test. So either extending the provided # method below, or rolling your own is required. diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index 920a133159..dbb76244e4 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -44,7 +44,7 @@ module ActiveModel # Specify +options+ with additional translating options. def human_attribute_name(attribute, options = {}) defaults = lookup_ancestors.map do |klass| - :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}" + :"#{self.i18n_scope}.attributes.#{klass.model_name.underscore}.#{attribute}" end defaults << :"attributes.#{attribute}" diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb index 422aa25668..b001adb35a 100644 --- a/activemodel/test/cases/attribute_methods_test.rb +++ b/activemodel/test/cases/attribute_methods_test.rb @@ -42,10 +42,16 @@ class AttributeMethodsTest < ActiveModel::TestCase ModelWithAttributes2.send(:attribute_method_matchers) end + test '#define_attribute_method generates attribute method' do + ModelWithAttributes.define_attribute_method(:foo) + + assert_respond_to ModelWithAttributes.new, :foo + assert_equal "value of foo", ModelWithAttributes.new.foo + end + test '#define_attribute_methods generates attribute methods' do ModelWithAttributes.define_attribute_methods([:foo]) - assert ModelWithAttributes.attribute_methods_generated? assert_respond_to ModelWithAttributes.new, :foo assert_equal "value of foo", ModelWithAttributes.new.foo end @@ -53,7 +59,6 @@ class AttributeMethodsTest < ActiveModel::TestCase test '#define_attribute_methods generates attribute methods with spaces in their names' do ModelWithAttributesWithSpaces.define_attribute_methods([:'foo bar']) - assert ModelWithAttributesWithSpaces.attribute_methods_generated? assert_respond_to ModelWithAttributesWithSpaces.new, :'foo bar' assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.send(:'foo bar') end @@ -69,7 +74,6 @@ class AttributeMethodsTest < ActiveModel::TestCase ModelWithAttributes.define_attribute_methods([:foo]) ModelWithAttributes.undefine_attribute_methods - assert !ModelWithAttributes.attribute_methods_generated? assert !ModelWithAttributes.new.respond_to?(:foo) assert_raises(NoMethodError) { ModelWithAttributes.new.foo } end diff --git a/activemodel/test/cases/translation_test.rb b/activemodel/test/cases/translation_test.rb index c299d6eb5e..1b1d972d5c 100644 --- a/activemodel/test/cases/translation_test.rb +++ b/activemodel/test/cases/translation_test.rb @@ -49,6 +49,13 @@ class ActiveModelI18nTests < ActiveModel::TestCase assert_equal 'person name attribute', Child.human_attribute_name('name') end + def test_translated_model_attributes_with_attribute_matching_namespaced_model_name + I18n.backend.store_translations 'en', :activemodel => {:attributes => {:person => {:gender => 'person gender'}, :"person/gender" => {:attribute => 'person gender attribute'}}} + + assert_equal 'person gender', Person.human_attribute_name('gender') + assert_equal 'person gender attribute', Person::Gender.human_attribute_name('attribute') + end + def test_translated_model_names I18n.backend.store_translations 'en', :activemodel => {:models => {:person => 'person model'} } assert_equal 'person model', Person.model_name.human diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index 5cb7bff4e7..e9f0e430fe 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -55,14 +55,6 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal ["Person's name not found"], @person.errors.full_messages end - def test_errors_full_messages_translates_human_attribute_name_for_model_in_module_attributes - I18n.backend.store_translations('en', :activemodel => {:attributes => {:person_module => {:person => {:name => "Person in Module's name"}}}}) - person = PersonModule::Person.new - person.errors.add(:name, 'not found') - PersonModule::Person.expects(:human_attribute_name).with(:name, :default => 'Name').returns("Person in Module's name") - assert_equal ["Person in Module's name not found"], person.errors.full_messages - end - def test_errors_full_messages_uses_format I18n.backend.store_translations('en', :errors => {:format => "Field %{attribute} %{message}"}) @person.errors.add('name', 'empty') @@ -371,15 +363,4 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal ["I am a custom error"], @person.errors[:title] end - def test_model_with_module_i18n_scope - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person_module => {:person => {:blank => 'generic blank'}}}}} - PersonModule::Person.validates_presence_of :title - person = PersonModule::Person.new - person.valid? - assert_equal ['generic blank'], person.errors[:title] - - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person_module => {:person => {:attributes => {:title => {:blank => 'title cannot be blank'}}}}}}} - person.valid? - assert_equal ['title cannot be blank'], person.errors[:title] - end end diff --git a/activemodel/test/models/person.rb b/activemodel/test/models/person.rb index eb84f7a27d..e896e90f98 100644 --- a/activemodel/test/models/person.rb +++ b/activemodel/test/models/person.rb @@ -9,10 +9,9 @@ class Person end end -class Child < Person +class Person::Gender + extend ActiveModel::Translation end -module PersonModule - class Person < ::Person - end +class Child < Person end |