diff --git a/lib/her/model/attributes.rb b/lib/her/model/attributes.rb index 1737c488..e0fc29cd 100644 --- a/lib/her/model/attributes.rb +++ b/lib/her/model/attributes.rb @@ -55,9 +55,19 @@ def method_missing(method, *args, &blk) # @private def respond_to_missing?(method, include_private = false) + return false if Thread.current[:her_respond_to_missing] method.to_s =~ /[?=]$/ || @_her_attributes.include?(method) || super end + def respond_to_without_missing?(method, include_private = false) + Thread.current[:her_respond_to_missing] = true + respond_to?(method, include_private) + ensure + # Normally we would use nil to delete the variable but this is + # slightly more expensive and performance counts here. + Thread.current[:her_respond_to_missing] = false + end + # Assign new attributes to a resource # # @example @@ -201,10 +211,9 @@ def use_setter_methods(model, params = {}) reserved = [:id, model.class.primary_key, *model.class.association_keys] model.class.attributes *params.keys.reject { |k| reserved.include?(k) } - setter_method_names = model.class.setter_method_names params.each_with_object({}) do |(key, value), memo| setter_method = "#{key}=" - if setter_method_names.include?(setter_method) + if model.respond_to_without_missing?(setter_method) model.send setter_method, value else memo[key.to_sym] = value @@ -278,15 +287,6 @@ def store_metadata(value = nil) store_her_data(:metadata, value) end - # @private - def setter_method_names - @_her_setter_method_names ||= begin - instance_methods.each_with_object(Set.new) do |method, memo| - memo << method.to_s if method.to_s.end_with?('=') - end - end - end - private # @private diff --git a/spec/model/attributes_spec.rb b/spec/model/attributes_spec.rb index 1e5f91de..850921e4 100644 --- a/spec/model/attributes_spec.rb +++ b/spec/model/attributes_spec.rb @@ -242,6 +242,18 @@ def document=(document) @user = Foo::User.find(1) expect(@user.document).to eq("http://example.com") end + + it 'exposes the method to respond_to? and respond_to_without_missing?' do + @user = Foo::User.find(1) + expect(@user.respond_to?(:document=)).to be_truthy + expect(@user.respond_to_without_missing?(:document=)).to be_truthy + end + + it 'exposes a non-existent method to respond_to? but not respond_to_without_missing?' do + @user = Foo::User.find(1) + expect(@user.respond_to?(:nonexistent=)).to be_truthy + expect(@user.respond_to_without_missing?(:nonexistent=)).to be_falsey + end end context "for predicate method" do