public
Rubygem
Description: DataMapper - Core
Homepage: http://datamapper.org
Clone URL: git://github.com/sam/dm-core.git
Updated Association Proxy classes to encapsulate their state better

* The ManyToOne::Proxy class would return the parent resource directly,
  when instead it should return a copy of itself, and route all method
  calls through its instance (like save), handing off anything unknown
  to the parent.
* Updated the Proxy classes to use a "blank slate", and have all of their
  instance methods removed.  This would allow them to proxy most method
  calls to the underlying objects like a good proxy object should.
* Allow mass-assign to OneToMany associations
Dan Kubb (author)
Fri May 09 02:03:58 -0700 2008
commit  af80e61f2207c9de75d3d1ca84168df3cbadd340
tree    f2a8e0e698baaa335305583c80850f315e9d4578
parent  5b3c7c27c694b586d0ec2f807ae9811407223c84
...
30
31
32
 
 
33
34
35
...
30
31
32
33
34
35
36
37
0
@@ -30,6 +30,8 @@ module DataMapper
0
       end
0
 
0
       class Proxy
0
+        instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? should should_not ].include?(m) }
0
+
0
         def initialize() end
0
 
0
         def save
...
26
27
28
29
 
30
31
32
33
 
34
35
36
...
49
50
51
52
53
54
 
55
56
 
57
58
59
...
76
77
78
 
 
 
 
 
 
 
 
79
80
81
...
26
27
28
 
29
30
31
32
 
33
34
35
36
...
49
50
51
 
 
 
52
53
 
54
55
56
57
...
74
75
76
77
78
79
80
81
82
83
84
85
86
87
0
@@ -26,11 +26,11 @@ module DataMapper
0
 
0
         class_eval <<-EOS, __FILE__, __LINE__
0
           def #{name}
0
-            #{name}_association.parent
0
+            #{name}_association
0
           end
0
 
0
           def #{name}=(parent_resource)
0
-            #{name}_association.parent = parent_resource
0
+            #{name}_association.replace(parent_resource)
0
           end
0
 
0
           private
0
@@ -49,11 +49,9 @@ module DataMapper
0
       end
0
 
0
       class Proxy
0
-        def parent
0
-          @parent_resource ||= @relationship.get_parent(@child_resource)
0
-        end
0
+        instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? should should_not ].include?(m) }
0
 
0
-        def parent=(parent_resource)
0
+        def replace(parent_resource)
0
           @parent_resource = parent_resource
0
           @relationship.attach_parent(@child_resource, @parent_resource) if @parent_resource.nil? || !@parent_resource.new_record?
0
         end
0
@@ -76,6 +74,14 @@ module DataMapper
0
           @relationship   = relationship
0
           @child_resource = child_resource
0
         end
0
+
0
+        def parent
0
+          @parent_resource ||= @relationship.get_parent(@child_resource)
0
+        end
0
+
0
+        def method_missing(method, *args, &block)
0
+          parent.__send__(method, *args, &block)
0
+        end
0
       end # class Proxy
0
     end # module ManyToOne
0
   end # module Associations
...
27
28
29
 
 
 
 
 
 
 
 
 
 
30
31
32
...
40
41
42
43
44
45
 
46
47
 
48
49
 
 
 
50
51
52
...
92
93
94
 
95
96
97
...
105
106
107
 
 
 
 
108
109
110
...
136
137
138
139
140
141
142
143
 
144
145
146
...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
...
50
51
52
 
 
 
53
54
 
55
56
 
57
58
59
60
61
62
...
102
103
104
105
106
107
108
...
116
117
118
119
120
121
122
123
124
125
...
151
152
153
 
 
 
 
 
154
155
156
157
0
@@ -27,6 +27,16 @@ module DataMapper
0
 
0
         class_eval <<-EOS, __FILE__, __LINE__
0
           def #{name}
0
+            #{name}_association
0
+          end
0
+
0
+          def #{name}=(children)
0
+            #{name}_association.replace(children)
0
+          end
0
+
0
+          private
0
+
0
+          def #{name}_association
0
             @#{name}_association ||= begin
0
               relationship = self.class.relationships(repository.name)[:#{name}]
0
               association = Proxy.new(relationship, self)
0
@@ -40,13 +50,13 @@ module DataMapper
0
       end
0
 
0
       class Proxy
0
-        def children
0
-          @children ||= @relationship.get_children(@parent_resource)
0
-        end
0
+        instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? should should_not ].include?(m) }
0
 
0
-        def children=(resources)
0
+        def replace(resources)
0
           each { |resource| remove_resource(resource) }
0
-          replace(resources)
0
+          append_resource(resources)
0
+          children.replace(resources)
0
+          self
0
         end
0
 
0
         def push(*resources)
0
@@ -92,6 +102,7 @@ module DataMapper
0
         def save
0
           save_resources(@dirty_children)
0
           @dirty_children = []
0
+          self
0
         end
0
 
0
         private
0
@@ -105,6 +116,10 @@ module DataMapper
0
           @dirty_children  = []
0
         end
0
 
0
+        def children
0
+          @children ||= @relationship.get_children(@parent_resource)
0
+        end
0
+
0
         def remove_resource(resource)
0
           begin
0
             repository(@relationship.repository_name) do
0
@@ -136,11 +151,7 @@ module DataMapper
0
         end
0
 
0
         def method_missing(method, *args, &block)
0
-          if children.respond_to?(method)
0
-            children.__send__(method, *args, &block)
0
-          else
0
-            super
0
-          end
0
+          children.__send__(method, *args, &block)
0
         end
0
       end # class Proxy
0
     end # module OneToMany
...
30
31
32
33
34
 
35
36
37
...
30
31
32
 
 
33
34
35
36
0
@@ -30,8 +30,7 @@ module DataMapper
0
           end
0
 
0
           def #{name}=(child_resource)
0
-            #{name}_association.clear
0
-            #{name}_association << child_resource unless child_resource.nil?
0
+            #{name}_association.replace(child_resource.nil? ? [] : [ child_resource ])
0
           end
0
 
0
           private
...
300
301
302
303
 
304
305
306
...
419
420
421
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
423
424
...
300
301
302
 
303
304
305
306
...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
0
@@ -300,7 +300,7 @@ begin
0
       end
0
 
0
       it 'should save nil parents as NULL ids' do
0
-        p1,p2 = nil, nil
0
+        p1, p2 = nil, nil
0
 
0
         repository(:sqlite3) do
0
           p1 = Pie.new(:id => 20, :name => "Pie20")
0
@@ -419,6 +419,20 @@ begin
0
         s.host.id.should == 10
0
       end
0
 
0
+      it 'should save the associated instances upon saving of parent when mass-assigned' do
0
+        repository(:sqlite3) do
0
+          h = Host.create(:id => 10, :name => 'host10', :slices => [ Slice.new(:id => 10, :name => 'slice10') ])
0
+        end
0
+
0
+        s = repository(:sqlite3) do
0
+          Slice.first(:id => 10)
0
+        end
0
+
0
+        s.should_not be_nil
0
+        s.host.should_not be_nil
0
+        s.host.id.should == 10
0
+      end
0
+
0
       describe "many-to-one and one-to-many associations combined" do
0
         before do
0
           @adapter = repository(:sqlite3).adapter
...
23
24
25
26
 
27
28
29
...
23
24
25
 
26
27
28
29
0
@@ -23,7 +23,7 @@ describe "DataMapper::Associations::ManyToOne" do
0
         @parent.should_receive(:new_record?).and_return(false)
0
         @relationship.should_receive(:attach_parent).with(@child, @parent)
0
 
0
-        @association.parent = @parent
0
+        @association.replace(@parent)
0
       end
0
     end
0
   end
...
110
111
112
113
 
114
115
116
...
126
127
128
129
130
131
132
133
...
138
139
140
141
142
143
144
145
146
147
 
148
149
150
...
201
202
203
204
 
205
206
207
208
209
 
210
211
 
212
213
214
 
 
215
216
217
...
110
111
112
 
113
114
115
116
...
126
127
128
 
 
129
130
131
...
136
137
138
 
 
139
140
 
 
 
141
142
143
144
...
195
196
197
 
198
199
200
201
202
 
203
204
 
205
206
 
 
207
208
209
210
211
0
@@ -110,7 +110,7 @@ end
0
 
0
 describe DataMapper::Associations::OneToMany::Proxy do
0
   before do
0
-    @parent = mock("parent")
0
+    @parent = mock("parent", :new_record? => true)
0
     @resource = mock("resource", :null_object => true)
0
     @collection = []
0
     @repository = mock("repository", :save => nil)
0
@@ -126,8 +126,6 @@ describe DataMapper::Associations::OneToMany::Proxy do
0
         @collection.should_receive(:<<).with(@resource).once.and_return(@collection)
0
 
0
         @association << @resource
0
-
0
-        @association.instance_variable_get("@dirty_children").should be_empty
0
       end
0
     end
0
 
0
@@ -138,13 +136,9 @@ describe DataMapper::Associations::OneToMany::Proxy do
0
         @collection.should_receive(:<<).with(@resource).once.and_return(@collection)
0
 
0
         @association << @resource
0
-
0
-        @association.instance_variable_get("@dirty_children").should_not be_empty
0
       end
0
 
0
-      it "should save the resource after the parent is saved" do
0
-
0
-      end
0
+      it "should save the resource after the parent is saved"
0
 
0
       it "should add the parent's keys to the resource after the parent is saved"
0
     end
0
@@ -201,17 +195,17 @@ describe DataMapper::Associations::OneToMany::Proxy do
0
       @relationship.should_receive(:attach_parent).with(@resource, nil).once
0
       @resource.should_receive(:save).with(no_args).once
0
 
0
-      @association.children = @children
0
+      @association.replace(@children)
0
     end
0
 
0
     it "should replace the children in the collection" do
0
       @children.should_not == @collection
0
-      @association.children.should == @collection
0
+      @association.entries.should == @collection
0
 
0
-      @association.children = @children
0
+      @association.replace(@children)
0
 
0
-      @collection.should == @children
0
-      @association.children.object_id.should == @collection.object_id
0
+      @children.should == @collection  # collection was modified
0
+      @association.entries.should == @collection
0
     end
0
   end
0
 

Comments