Permalink
Browse files

Adding ability for delegated setters to use nested attributes to set …

…values on unintialized delgators
  • Loading branch information...
1 parent ffd375f commit ec4fb2de26a6e434b4059accd991c685cb237676 Jason Rust committed May 9, 2012
Showing with 47 additions and 10 deletions.
  1. +23 −6 lib/delegate_all_for.rb
  2. +1 −1 lib/delegate_all_for/version.rb
  3. +23 −3 spec/delegate_all_for/delegate_all_for_spec.rb
View
@@ -28,16 +28,33 @@ def delegate_all_for(*attr_names)
exclude_columns = self.column_names.dup.concat(options[:except].map(&:to_s))
attr_names.each do |association_name|
if reflection = reflect_on_association(association_name)
- delegate_opts = options.slice(:prefix, :allow_nil).merge(to: association_name)
+ to = association_name
+ delegate_opts = options.slice(:prefix, :allow_nil).merge(to: to)
+ method_prefix = delegate_opts[:prefix] ? "#{prefix == true ? to : prefix}_" : ''
+
options[:also_include].each do |m|
class_eval(%{delegate :#{m}, #{delegate_opts}})
end
- (reflection.klass.column_names - exclude_columns).each do |column_name|
- next if column_name.in?(reflection.foreign_key, 'updated_at', 'updated_on', 'created_at', 'created_on')
+
+ (reflection.klass.column_names - exclude_columns).each do |method|
+ next if method.in?(reflection.foreign_key, 'updated_at', 'updated_on', 'created_at', 'created_on')
+ class_eval <<-eoruby, __FILE__, __LINE__ + 1
+ delegate :#{method}, #{delegate_opts}
+ delegate :#{method}?, #{delegate_opts}
+ eoruby
+
+ # Create the setter with support for using nested attributes to set it if the delegated object is not present
+ exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
class_eval <<-eoruby, __FILE__, __LINE__ + 1
- delegate :#{column_name}, #{delegate_opts}
- delegate :#{column_name}?, #{delegate_opts}
- delegate :#{column_name}=, #{delegate_opts.merge(allow_nil: false)} # allow_nil on setters leads to uninituitive behavior
+ def #{method_prefix}#{method}=(*args, &block) # def customer_name(*args, &block)
+ if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
+ #{to}.__send__(:#{method}=, *args, &block) # client.__send__(:name, *args, &block)
+ elsif self.respond_to?(:#{to}_attributes=) # elsif self.respond_to?(:client_attributes=)
+ self.__send__(:#{to}_attributes=, :#{method} => args.first, &block) # self.__send__(:client_attributes=, :name => args.first, &block)
+ else # else
+ #{exception} # # add helpful exception
+ end # end
+ end # end
eoruby
end
else
@@ -1,3 +1,3 @@
module DelegateAllFor
- VERSION = "0.0.6"
+ VERSION = "0.0.7"
end
@@ -42,9 +42,6 @@ def two; 'parent' end
describe ':allow_nil option' do
subject { Parent.new }
its(:four) { should be_nil }
- it 'writer' do
- lambda { subject.four = 'FOUR' }.should raise_error RuntimeError
- end
end
describe 'delegates to child attributes' do
@@ -58,6 +55,29 @@ def two; 'parent' end
it 'does not delegate to timestamp attributes' do
lambda { subject.created_at }.should raise_error NoMethodError
end
+
+ describe 'writer' do
+ it 'sets the value using delegation if child is set' do
+ subject.child.should be_an_instance_of(Child)
+ subject.four = 'FOUR'
+ subject.four.should == 'FOUR'
+ subject.child.four.should == 'FOUR'
+ end
+
+ it 'sets the value using nested attributes if child is not set and nested attributes are supported' do
+ class NestedParent < Parent; accepts_nested_attributes_for :child end
+ subject = NestedParent.new
+ subject.four = 'FOUR'
+ subject.child.should be_an_instance_of(Child)
+ subject.four.should == 'FOUR'
+ subject.child.four.should == 'FOUR'
+ end
+
+ it 'raises an exception if child is not set' do
+ subject = Parent.new
+ lambda { subject.four = 'FOUR' }.should raise_error RuntimeError
+ end
+ end
end
describe 'guards against user error' do

0 comments on commit ec4fb2d

Please sign in to comment.