diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index b1c5987fe4ae7..8da82aab84f62 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -35,4 +35,8 @@ *Bryan Ricker* +* Add support for wrapping parameters matching aliased attribute names. + + *Patrick Van Stee* + Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/actionpack/CHANGELOG.md) for previous changes. diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index c9f1d8dcb4097..7a6cd6c09ccf4 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -44,7 +44,7 @@ module ActionController # # On ActiveRecord models with no +:include+ or +:exclude+ option set, # it will only wrap the parameters returned by the class method - # attribute_names. + # attribute_method_names. # # If you're going to pass the parameters to an +ActiveModel+ object (such as # User.new(params[:user])), you might consider passing the model class to @@ -106,8 +106,8 @@ def include @include_set = true unless super || exclude - if m.respond_to?(:attribute_names) && m.attribute_names.any? - self.include = m.attribute_names + if m.respond_to?(:attribute_method_names) && m.attribute_method_names.any? + self.include = m.attribute_method_names end end end diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index d87e2b85b0653..cc795a08beb86 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -155,8 +155,8 @@ def test_nested_params end def test_derived_wrapped_keys_from_matching_model - User.expects(:respond_to?).with(:attribute_names).returns(true) - User.expects(:attribute_names).twice.returns(["username"]) + User.expects(:respond_to?).with(:attribute_method_names).returns(true) + User.expects(:attribute_method_names).twice.returns(["username"]) with_default_wrapper_options do @request.env['CONTENT_TYPE'] = 'application/json' @@ -167,8 +167,8 @@ def test_derived_wrapped_keys_from_matching_model def test_derived_wrapped_keys_from_specified_model with_default_wrapper_options do - Person.expects(:respond_to?).with(:attribute_names).returns(true) - Person.expects(:attribute_names).twice.returns(["username"]) + Person.expects(:respond_to?).with(:attribute_method_names).returns(true) + Person.expects(:attribute_method_names).twice.returns(["username"]) UsersController.wrap_parameters Person @@ -179,8 +179,8 @@ def test_derived_wrapped_keys_from_specified_model end def test_not_wrapping_abstract_model - User.expects(:respond_to?).with(:attribute_names).returns(true) - User.expects(:attribute_names).returns([]) + User.expects(:respond_to?).with(:attribute_method_names).returns(true) + User.expects(:attribute_method_names).returns([]) with_default_wrapper_options do @request.env['CONTENT_TYPE'] = 'application/json' @@ -209,13 +209,13 @@ def parse end class SampleOne - def self.attribute_names + def self.attribute_method_names ["username"] end end class SampleTwo - def self.attribute_names + def self.attribute_method_names ["title"] end end @@ -298,7 +298,7 @@ class IrregularInflectionParamsWrapperTest < ActionController::TestCase include ParamsWrapperTestHelp class ParamswrappernewsItem - def self.attribute_names + def self.attribute_method_names ['test_attr'] end end diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 6fc34ecd60b66..0dde69e114b78 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -7,4 +7,9 @@ *Nick Sutterer* +* Add the `ActiveModel#attribute_method_names` method, which returns an + array of attribute names and aliases as strings. + + *Patrick Van Stee* + Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activemodel/CHANGELOG.md) for previous changes. diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 98cde8ba59198..1e7aef119189e 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -72,9 +72,10 @@ module AttributeMethods CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/ included do - class_attribute :attribute_aliases, :attribute_method_matchers, instance_writer: false + class_attribute :attribute_aliases, :attribute_method_matchers, :defined_attribute_names, instance_writer: false self.attribute_aliases = {} self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new] + self.defined_attribute_names = [] end module ClassMethods @@ -276,6 +277,10 @@ def define_attribute_methods(*attr_names) # person.name # => "Bob" # person.name_short? # => true def define_attribute_method(attr_name) + attr_name = attr_name.to_s + + self.defined_attribute_names << attr_name unless defined_attribute_names.include?(attr_name) + attribute_method_matchers.each do |matcher| method_name = matcher.method_name(attr_name) @@ -285,10 +290,11 @@ def define_attribute_method(attr_name) if respond_to?(generate_method, true) send(generate_method, attr_name) else - define_proxy_call true, generated_attribute_methods, method_name, matcher.method_missing_target, attr_name.to_s + define_proxy_call true, generated_attribute_methods, method_name, matcher.method_missing_target, attr_name end end end + attribute_method_matchers_cache.clear end @@ -322,6 +328,22 @@ def undefine_attribute_methods attribute_method_matchers_cache.clear end + # Returns an array of attribute names and aliases as strings. + # + # class Person + # include ActiveModel::AttributeMethods + # + # define_attribute_method :name + # + # alias_attribute :nickname, :name + # end + # + # Person.attribute_method_names + # # => ["name", "nickname"] + def attribute_method_names + defined_attribute_names + attribute_aliases.keys + end + # Returns true if the attribute methods defined have been generated. def generated_attribute_methods #:nodoc: @generated_attribute_methods ||= Module.new.tap { |mod| include mod } diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb index 25eb4860e32a8..4ba7b0808db12 100644 --- a/activemodel/test/cases/attribute_methods_test.rb +++ b/activemodel/test/cases/attribute_methods_test.rb @@ -186,6 +186,15 @@ def foo assert_equal "value of end", ModelWithRubyKeywordNamedAttributes.new.to end + test '#attribute_method_names returns attribute names and attribute aliases' do + klass = Class.new(ModelWithAttributes) do + define_attribute_methods :foo + alias_attribute :bar, :foo + end + + assert_equal ["foo", "bar"], klass.attribute_method_names + end + test '#undefine_attribute_methods removes attribute methods' do ModelWithAttributes.define_attribute_methods(:foo) ModelWithAttributes.undefine_attribute_methods diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 28839a9c4b550..6ba2e865de469 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -514,7 +514,7 @@ def index app_file 'app/models/post.rb', <<-RUBY class Post - def self.attribute_names + def self.attribute_method_names %w(title) end end