aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/CHANGELOG2
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb44
-rw-r--r--activemodel/lib/active_model/errors.rb4
-rw-r--r--activemodel/lib/active_model/naming.rb15
-rw-r--r--activemodel/lib/active_model/translation.rb2
-rw-r--r--activemodel/test/cases/attribute_methods_test.rb10
-rw-r--r--activemodel/test/cases/translation_test.rb7
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb19
-rw-r--r--activemodel/test/models/person.rb7
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