object_attorney 1.2.1 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +8 -8
  2. data/db/migrate/20131205114000_create_users.rb +13 -0
  3. data/db/migrate/20131205114900_create_posts.rb +1 -1
  4. data/db/migrate/20131205114901_create_comments.rb +13 -0
  5. data/db/migrate/20131205114902_create_addresses.rb +13 -0
  6. data/lib/object_attorney/association_reflection.rb +52 -15
  7. data/lib/object_attorney/helpers.rb +4 -0
  8. data/lib/object_attorney/nested_objects.rb +88 -21
  9. data/lib/object_attorney/orm.rb +20 -10
  10. data/lib/object_attorney/orm_handlers/smooth_operator.rb +20 -15
  11. data/lib/object_attorney/reflection.rb +35 -0
  12. data/lib/object_attorney/version.rb +1 -1
  13. data/lib/object_attorney.rb +18 -10
  14. data/spec/object_attorney/bulk_post_form_spec.rb +52 -0
  15. data/spec/object_attorney/bulk_posts_allow_only_existing_form_spec.rb +37 -0
  16. data/spec/object_attorney/bulk_posts_allow_only_new_form_spec.rb +39 -0
  17. data/spec/object_attorney/bulk_posts_with_form_objects_form_spec.rb +91 -0
  18. data/spec/object_attorney/post_form_spec.rb +107 -41
  19. data/spec/object_attorney/post_with_comment_form_spec.rb +123 -0
  20. data/spec/object_attorney/post_with_comments_and_address_form_spec.rb +45 -0
  21. data/spec/object_attorney/user_form_spec.rb +61 -0
  22. data/spec/spec_helper.rb +15 -6
  23. data/spec/support/form_objects/bulk_posts_allow_only_existing_form.rb +19 -0
  24. data/spec/support/form_objects/bulk_posts_allow_only_new_form.rb +19 -0
  25. data/spec/support/form_objects/bulk_posts_form.rb +27 -0
  26. data/spec/support/form_objects/bulk_posts_with_form_objects_form.rb +27 -0
  27. data/spec/support/form_objects/comment_form.rb +11 -0
  28. data/spec/support/form_objects/post_form.rb +54 -0
  29. data/spec/support/form_objects/post_with_comment_form.rb +21 -0
  30. data/spec/support/form_objects/post_with_comments_and_address_form.rb +13 -0
  31. data/spec/support/form_objects/user_form.rb +11 -0
  32. data/spec/support/models/address.rb +5 -0
  33. data/spec/support/models/comment.rb +5 -0
  34. data/spec/support/models/post.rb +7 -1
  35. data/spec/support/models/user.rb +7 -0
  36. metadata +44 -19
  37. data/spec/object_attorney/bulk_posts_form_child_spec.rb +0 -191
  38. data/spec/object_attorney/bulk_posts_form_spec.rb +0 -178
  39. data/spec/object_attorney/post_form_child_spec.rb +0 -60
  40. data/spec/support/models/bulk_posts_form.rb +0 -21
  41. data/spec/support/models/bulk_posts_form_child.rb +0 -19
  42. data/spec/support/models/item.rb +0 -3
  43. data/spec/support/models/post_form.rb +0 -13
  44. data/spec/support/models/post_form_child.rb +0 -7
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZDBmMmQ5OTNkMjI2MzRkZGVkNjM5NzgwMDg1NzQ0ZjYzNjhiODUwMQ==
4
+ ZGE1NWUyMGQ1NzE4YjIyMmM0ZmJhN2YxYWJiZjczYmM1MTk5YzA1Mw==
5
5
  data.tar.gz: !binary |-
6
- YmQxOGY0OGNiMzEyNDQzOTcxOGM3MGRmZjhiOTVlYTdiOTdlNGVmMw==
6
+ OTU5Y2FkZThiNDQ2Y2ZjZjhkYmIyNzhlN2ZlNmJhOWQ3OGZmOWU4ZA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZTU2YjYyYzQ4M2JkZDJjZTZlZTg1N2JjMjNmM2JkZjhmMzczNWRiZjQ1MTBk
10
- YzZkYzBiNDg2ZjlhMTU5ZGNhZDIzNWJmNDI4MTQzZWFiYWFjYmQzZjBhZWRl
11
- ZjZlYWY1MDk4ZmVlZThkMGE1YTE5OTg1ODVlOTZmMDJjNjIxNjc=
9
+ OTliNTEyNjU2ZjRhM2QwNjEyOTMyNmFiYzM4ZDJmYTViMmYwMzM0NDE4Y2Fj
10
+ ZGIzMDhhMjk1YjdjZjFjNWExYzU0NTRiZTdmOGM0NDJmZDk5OWExN2M2MWJk
11
+ MTI2MTdhNTIyMmExYTQ1MjVlYjY5NmVlZmI4ZGFmZTEwMzE3MDI=
12
12
  data.tar.gz: !binary |-
13
- NzY5MmJjMTMxMWU0NzVlYzZlZDYwNzM3YzNkM2Y0MzY2ZWIxYWU3MTUwNWYx
14
- NWUxNzE3NWUzNWMzZjc4YjI5YjQ3YTM1Y2Q1YWQ2YWZjZDllOGVjM2YzODMw
15
- YzRkYTA0ODI4NTMwZjRkZTU5MWM3MDJkYWQxYWNlYzA0ZmFiOTY=
13
+ MDdiMmRjMjgxMDdkYmNiODU1MTMxYTA0ODU0M2JjZDVmMDYyYjNhNGFlMzFm
14
+ NDZmODY0NWUxNGYxN2Q2ZTA0NTY3YjBjODMxMTc4Y2IxM2UyNzViZWMyMjA1
15
+ ZGVjNzMyOWEzZGMyNjIzYjA1MzY0NzkzMWJmMGEzODRiMzE0OTU=
@@ -0,0 +1,13 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def up
3
+ create_table :users do |t|
4
+ t.string :email
5
+ t.boolean :admin, default: false
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def down
11
+ drop_table :users
12
+ end
13
+ end
@@ -3,7 +3,7 @@ class CreatePosts < ActiveRecord::Migration
3
3
  create_table :posts do |t|
4
4
  t.string :title
5
5
  t.text :body
6
- t.boolean :admin, default: false
6
+ t.integer :user_id
7
7
  t.timestamps
8
8
  end
9
9
  end
@@ -0,0 +1,13 @@
1
+ class CreateComments < ActiveRecord::Migration
2
+ def up
3
+ create_table :comments do |t|
4
+ t.text :body
5
+ t.integer :post_id
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def down
11
+ drop_table :comments
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class CreateAddresses < ActiveRecord::Migration
2
+ def up
3
+ create_table :addresses do |t|
4
+ t.string :street
5
+ t.integer :post_id
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def down
11
+ drop_table :addresses
12
+ end
13
+ end
@@ -1,17 +1,54 @@
1
1
  module ObjectAttorney
2
2
 
3
- class AssociationReflection
4
- attr_reader :klass, :macro, :options, :name
3
+ class AssociationReflection < Reflection
4
+ attr_reader :related_reflection, :macro
5
5
 
6
- def initialize(association, options)
7
- options = options.is_a?(Hash) ? options : { class_name: options }
8
-
9
- @name = association
6
+ def initialize(association, related_reflection, options)
7
+ super(association, options)
10
8
  @macro = options[:macro] || macro_default(association)
11
- @klass = options[:class_name] || klass_default(association)
12
- @options = options
9
+ @related_reflection = related_reflection
10
+ end
11
+
12
+ def primary_key
13
+ @primary_key ||= options[:primary_key] || :id
14
+ end
15
+
16
+ def foreign_key
17
+ @foreign_key ||= options[:foreign_key] || foreign_key_default
18
+ end
19
+
20
+ def set_relational_keys(origin, destination)
21
+ if has_many? || has_one?
22
+ set_foreign_key(destination, primary_key_of(origin))
23
+ elsif belongs_to?
24
+ set_foreign_key(origin, primary_key_of(destination))
25
+ end
26
+ end
27
+
28
+ def set_foreign_key(object, id)
29
+ setter = "#{foreign_key}="
30
+
31
+ if object.respond_to?(setter)
32
+ object.send(setter, id)
33
+ elsif object.respond_to?("send_to_representative")
34
+ object.send_to_representative(setter, id)
35
+ end
36
+ end
37
+
38
+ def primary_key_of(object)
39
+ object.send(primary_key)
40
+ end
13
41
 
14
- @klass = @klass.constantize if @klass.is_a?(String)
42
+ def has_many?
43
+ macro == :has_many
44
+ end
45
+
46
+ def has_one?
47
+ macro == :has_one
48
+ end
49
+
50
+ def belongs_to?
51
+ macro == :belongs_to
15
52
  end
16
53
 
17
54
  private ################################# private
@@ -20,12 +57,12 @@ module ObjectAttorney
20
57
  Helpers.plural?(association) ? :has_many : :belongs_to
21
58
  end
22
59
 
23
- def klass_default(association)
24
- if Helpers.plural?(association)
25
- association.to_s.singularize.camelize
26
- else
27
- association.to_s.camelize
28
- end
60
+ def foreign_key_default
61
+ if has_many? || has_one?
62
+ "#{related_reflection.single_name}_id"
63
+ elsif belongs_to?
64
+ "#{single_name}_id"
65
+ end.to_sym
29
66
  end
30
67
 
31
68
  end
@@ -4,6 +4,10 @@ module ObjectAttorney
4
4
 
5
5
  extend self
6
6
 
7
+ def is_integer?(string)
8
+ string.match(/^(\d)+$/)
9
+ end
10
+
7
11
  def plural?(string)
8
12
  string = string.to_s
9
13
  string == string.pluralize
@@ -1,10 +1,8 @@
1
+ require "object_attorney/association_reflection"
2
+
1
3
  module ObjectAttorney
2
4
  module NestedObjects
3
5
 
4
- def nested_objects
5
- self.class.nested_objects.map { |nested_object_sym| self.send(nested_object_sym) }.flatten
6
- end
7
-
8
6
  def mark_for_destruction
9
7
  @marked_for_destruction = true
10
8
  end
@@ -14,35 +12,54 @@ module ObjectAttorney
14
12
  end
15
13
 
16
14
  def mark_for_destruction_if_necessary(object, attributes)
17
- return nil unless attributes.kind_of?(Hash)
15
+ return nil unless attributes.is_a?(Hash)
18
16
 
19
17
  _destroy = attributes["_destroy"] || attributes[:_destroy]
20
18
 
21
19
  object.mark_for_destruction if ["true", "1", true].include?(_destroy)
22
20
  end
23
21
 
22
+ def nested_objects(macro = nil)
23
+ nested_objects_list = []
24
+
25
+ self.class.reflect_on_all_associations(macro).each do |reflection|
26
+ [*self.send(reflection.name)].each do |nested_object|
27
+ nested_objects_list << [reflection, nested_object]
28
+ end
29
+ end
30
+
31
+ nested_objects_list
32
+ end
33
+
24
34
  protected #################### PROTECTED METHODS DOWN BELOW ######################
25
35
 
26
- def save_nested_objects(save_method)
27
- nested_objects.map do |nested_object|
28
- call_save_or_destroy(nested_object, save_method)
36
+ def save_or_destroy_nested_objects(save_method, association_macro)
37
+ nested_objects(association_macro).map do |reflection, nested_object|
38
+
39
+ populate_foreign_key(self, nested_object, reflection, :has_many)
40
+ populate_foreign_key(self, nested_object, reflection, :has_one)
41
+
42
+ saving_result = call_save_or_destroy(nested_object, save_method)
43
+
44
+ populate_foreign_key(self, nested_object, reflection, :belongs_to)
45
+
46
+ saving_result
47
+
29
48
  end.all?
30
49
  end
31
50
 
32
51
  def validate_nested_objects
33
- #nested_objects.all?(&:valid?) #will not validate all nested_objects
34
- return true if nested_objects.reject(&:marked_for_destruction?).map(&:valid?).all?
52
+ return true if nested_objects.map do |reflection, nested_object|
53
+ nested_object.marked_for_destruction? ? true : nested_object.valid?
54
+ end.all?
55
+
35
56
  import_nested_objects_errors
36
57
  false
37
58
  end
38
59
 
39
60
  def import_nested_objects_errors
40
- self.class.nested_objects.map do |nested_object_sym|
41
-
42
- [*self.send(nested_object_sym)].each do |nested_object|
43
- nested_object.errors.full_messages.each { |message| self.errors.add(nested_object_sym, message) }
44
- end
45
-
61
+ nested_objects.each do |reflection, nested_object|
62
+ nested_object.errors.full_messages.each { |message| self.errors.add(reflection.name, message) }
46
63
  end
47
64
  end
48
65
 
@@ -54,6 +71,12 @@ module ObjectAttorney
54
71
 
55
72
  private #################### PRIVATE METHODS DOWN BELOW ######################
56
73
 
74
+ def populate_foreign_key(origin, destination, reflection, macro)
75
+ return nil if represented_object.blank? || check_if_marked_for_destruction?(destination) || reflection.macro != macro
76
+
77
+ reflection.set_relational_keys(origin, destination)
78
+ end
79
+
57
80
  def self.included(base)
58
81
  base.extend(ClassMethods)
59
82
 
@@ -93,7 +116,7 @@ module ObjectAttorney
93
116
  end
94
117
 
95
118
  def update_existing_nested_objects(existing_and_new_nested_objects, nested_object_name)
96
- (send("existing_#{nested_object_name}") || []).each do |existing_nested_object|
119
+ send("existing_#{nested_object_name}").each do |existing_nested_object|
97
120
  attributes = get_attributes_for_existing(nested_object_name, existing_nested_object)
98
121
 
99
122
  mark_for_destruction_if_necessary(existing_nested_object, attributes)
@@ -108,21 +131,60 @@ module ObjectAttorney
108
131
  next if attributes["id"].present? || attributes[:id].present?
109
132
 
110
133
  new_nested_object = send("build_#{nested_object_name.to_s.singularize}", attributes_without_destroy(attributes))
111
- mark_for_destruction_if_necessary(new_nested_object, attributes)
134
+ next unless new_nested_object
112
135
 
136
+ mark_for_destruction_if_necessary(new_nested_object, attributes)
113
137
  existing_and_new_nested_objects << new_nested_object
114
138
  end
115
139
  end
116
140
 
141
+ def build_nested_object(nested_object_name, attributes = {})
142
+ reflection = self.class.reflect_on_association(nested_object_name)
143
+
144
+ new_nested_object = reflection.klass.new(attributes)
145
+
146
+ populate_foreign_key(self, new_nested_object, reflection, :has_many)
147
+
148
+ new_nested_object
149
+ end
150
+
151
+ def existing_nested_objects(nested_object_name)
152
+ nested_association_klass = self.class.reflect_on_association(nested_object_name).klass
153
+
154
+ existing_list = represented_object.blank? ? nested_association_klass.all : (represented_object.send(nested_object_name) || [])
155
+
156
+ if represented_object.present? && nested_association_klass != self.class.represented_object_class.reflect_on_association(nested_object_name).klass
157
+ existing_list = existing_list.map { |existing_nested_object| nested_association_klass.new({}, existing_nested_object) }
158
+ end
159
+
160
+ existing_list
161
+ end
162
+
117
163
  module ClassMethods
118
164
 
165
+ def has_many(nested_object_name, options = {})
166
+ _accepts_nested_objects_overwrite_macro(nested_object_name, options, :has_many)
167
+ end
168
+
169
+ def has_one(nested_object_name, options = {})
170
+ _accepts_nested_objects_overwrite_macro(nested_object_name, options, :has_one)
171
+ end
172
+
173
+ def belongs_to(nested_object_name, options = {})
174
+ _accepts_nested_objects_overwrite_macro(nested_object_name, options, :belongs_to)
175
+ end
176
+
119
177
  def accepts_nested_objects(nested_object_name, options = {})
120
- reflection = AssociationReflection.new(nested_object_name, options)
178
+ reflection = AssociationReflection.new(nested_object_name, represented_object_reflection, options)
179
+
121
180
  self.instance_variable_set("@#{nested_object_name}_reflection", reflection)
122
181
  self.instance_variable_set("@association_reflections", association_reflections | [reflection])
123
182
 
124
183
  self.send(:attr_accessor, "#{nested_object_name}_attributes".to_sym)
184
+
125
185
  define_method(nested_object_name) { nested_getter(nested_object_name) }
186
+ define_method("build_#{reflection.single_name}") { |attributes = {}, nested_object = nil| build_nested_object(nested_object_name, attributes) }
187
+ define_method("existing_#{nested_object_name}") { existing_nested_objects(nested_object_name) }
126
188
  end
127
189
 
128
190
  def association_reflections
@@ -137,8 +199,13 @@ module ObjectAttorney
137
199
  macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
138
200
  end
139
201
 
140
- def nested_objects
141
- association_reflections.map(&:name)
202
+
203
+ private ############################### PRIVATE METHODS ###########################
204
+
205
+ def _accepts_nested_objects_overwrite_macro(nested_object_name, options, macro)
206
+ default_options = { macro: macro }
207
+ options = options.is_a?(Hash) ? options.merge(default_options) : default_options
208
+ accepts_nested_objects(nested_object_name, options)
142
209
  end
143
210
 
144
211
  end
@@ -26,15 +26,15 @@ module ObjectAttorney
26
26
 
27
27
  def destroy
28
28
  return true if represented_object.blank?
29
- evoke_method_on_object(represented_object, :destroy)
29
+ represented_object.destroy
30
30
  end
31
31
 
32
32
  def call_save_or_destroy(object, save_method)
33
33
  if object == self
34
- represented_object.present? ? evoke_method_on_object(represented_object, save_method) : true
34
+ represented_object.present? ? represented_object.send(save_method) : true
35
35
  else
36
36
  save_method = :destroy if check_if_marked_for_destruction?(object)
37
- evoke_method_on_object(object, save_method)
37
+ object.send(save_method)
38
38
  end
39
39
  end
40
40
 
@@ -48,15 +48,29 @@ module ObjectAttorney
48
48
  end
49
49
 
50
50
  def submit(save_method)
51
- save_result = save_represented_object(save_method)
52
- save_result = save_nested_objects(save_method) if save_result
51
+ save_result = save_or_destroy_nested_objects(save_method, :belongs_to)
52
+ save_result = save_or_destroy_represented_object(save_method) if save_result
53
+ save_result = save_or_destroy_nested_objects(save_method, :has_many) if save_result
54
+ save_result = save_or_destroy_nested_objects(save_method, :has_one) if save_result
53
55
  save_result
54
56
  end
55
57
 
56
- def save_represented_object(save_method)
58
+ def save_or_destroy_represented_object(save_method)
57
59
  return true if represented_object.blank?
58
60
  call_save_or_destroy(represented_object, save_method)
59
61
  end
62
+
63
+ def self.included(base)
64
+ base.extend(ClassMethods)
65
+ end
66
+
67
+ module ClassMethods
68
+
69
+ def all(*args)
70
+ represented_object_class.all(*args).map { |represented_object| self.new({}, represented_object) }
71
+ end
72
+
73
+ end
60
74
 
61
75
  private #################### PRIVATE METHODS DOWN BELOW ######################
62
76
 
@@ -64,9 +78,5 @@ module ObjectAttorney
64
78
  object.respond_to?(:marked_for_destruction?) ? object.marked_for_destruction? : false
65
79
  end
66
80
 
67
- def evoke_method_on_object(object, method)
68
- object.send(method)
69
- end
70
-
71
81
  end
72
82
  end
@@ -16,15 +16,15 @@ module ObjectAttorney
16
16
 
17
17
  def destroy(options = {})
18
18
  return true if represented_object.blank?
19
- evoke_method_on_object(represented_object, :destroy, options)
19
+ represented_object.destroy(options).ok?
20
20
  end
21
21
 
22
22
  def call_save_or_destroy(object, save_method, options = {})
23
23
  if object == self || object == represented_object
24
- represented_object.present? ? evoke_method_on_object(represented_object, save_method, options) : true
24
+ represented_object.present? ? represented_object.send(save_method, options).ok? : true
25
25
  else
26
26
  save_method = :destroy if check_if_marked_for_destruction?(object)
27
- evoke_method_on_object(object, save_method, options)
27
+ object.send(save_method, options).ok?
28
28
  end
29
29
  end
30
30
 
@@ -35,26 +35,31 @@ module ObjectAttorney
35
35
  end
36
36
 
37
37
  def submit(save_method, options = {})
38
- save_result = save_represented_object(save_method, options)
39
- save_result = save_nested_objects(save_method) if save_result
38
+ save_result = save_or_destroy_nested_objects(save_method, :belongs_to, options)
39
+ save_result = save_or_destroy_represented_object(save_method, options) if save_result
40
+ save_result = save_or_destroy_nested_objects(save_method, :has_many, options) if save_result
41
+ save_result = save_or_destroy_nested_objects(save_method, :has_one, options) if save_result
40
42
  save_result
41
43
  end
42
44
 
43
- def save_represented_object(save_method, options = {})
45
+ def save_or_destroy_represented_object(save_method, options = {})
44
46
  return true if represented_object.blank?
45
- call_save_or_destroy(represented_object, save_method, options).ok?
47
+ call_save_or_destroy(represented_object, save_method, options)
46
48
  end
49
+
50
+ def save_or_destroy_nested_objects(save_method, association_macro, options = {})
51
+ nested_objects(association_macro).map do |reflection, nested_object|
52
+
53
+ populate_foreign_key(self, nested_object, reflection, :has_many)
54
+ populate_foreign_key(self, nested_object, reflection, :has_one)
47
55
 
48
- def save_nested_objects(save_method, options = {})
49
- nested_objects.map do |nested_object|
50
- call_save_or_destroy(nested_object, save_method, options).ok?
51
- end.all?
52
- end
56
+ saving_result = call_save_or_destroy(nested_object, save_method, options)
53
57
 
54
- private #################### PRIVATE METHODS DOWN BELOW ######################
58
+ populate_foreign_key(nested_object, self, reflection, :belongs_to)
55
59
 
56
- def evoke_method_on_object(object, method, options = {})
57
- object.send(method, options)
60
+ saving_result
61
+
62
+ end.all?
58
63
  end
59
64
 
60
65
  end
@@ -0,0 +1,35 @@
1
+ module ObjectAttorney
2
+
3
+ class Reflection
4
+ attr_reader :name, :klass, :options
5
+
6
+ def initialize(class_name, options)
7
+ options = options.is_a?(Hash) ? options : {}
8
+
9
+ @name, @options = class_name, options
10
+
11
+ @klass = options[:class_name] || klass_default(@name)
12
+ @klass = @klass.constantize if @klass.is_a?(String)
13
+ end
14
+
15
+ def single_name
16
+ @single_name ||= options[:single_name] || name.to_s.singularize
17
+ end
18
+
19
+ def plural_name
20
+ @plural_name ||= options[:plural_name] || name.to_s.pluralize
21
+ end
22
+
23
+ private ################################# private
24
+
25
+ def klass_default(class_name)
26
+ if Helpers.plural?(class_name)
27
+ class_name.to_s.singularize.camelize
28
+ else
29
+ class_name.to_s.camelize
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -1,3 +1,3 @@
1
1
  module ObjectAttorney
2
- VERSION = "1.2.1"
2
+ VERSION = "2.1.1"
3
3
  end
@@ -1,29 +1,26 @@
1
1
  require "object_attorney/version"
2
2
  require "object_attorney/helpers"
3
- require "object_attorney/association_reflection"
3
+ require "object_attorney/reflection"
4
4
  require "object_attorney/nested_objects"
5
5
  require "object_attorney/orm"
6
6
  require 'active_record'
7
7
 
8
8
  module ObjectAttorney
9
9
 
10
- def initialize(attributes = {}, object = nil, options = {})
11
- if !attributes.kind_of?(Hash) && object.blank?
10
+ def initialize(attributes = {}, object = nil)
11
+ if !attributes.is_a?(Hash) && object.blank?
12
12
  object = attributes
13
13
  attributes = nil
14
14
  end
15
15
 
16
16
  attributes = {} if attributes.blank?
17
17
 
18
- if !attributes.include?("id") && object.kind_of?(String)
19
- attributes["id"] = object
20
- object = nil
21
- end
22
-
23
18
  @represented_object = object if object.present?
24
19
 
25
20
  assign_attributes attributes
26
21
  mark_for_destruction_if_necessary(self, attributes)
22
+
23
+ init(attributes)
27
24
  end
28
25
 
29
26
  def assign_attributes(attributes = {})
@@ -38,10 +35,17 @@ module ObjectAttorney
38
35
  respond_to?(attribute) ? send(attribute) : nil
39
36
  end
40
37
 
38
+ def send_to_representative(method_name, *args)
39
+ return false if represented_object.blank?
40
+
41
+ represented_object.send(method_name, *args)
42
+ end
43
+
41
44
  protected #################### PROTECTED METHODS DOWN BELOW ######################
42
45
 
46
+ def init(attributes); end
47
+
43
48
  def allowed_attribute(attribute)
44
- attribute = attribute.to_s
45
49
  respond_to?("#{attribute}=")
46
50
  end
47
51
 
@@ -86,9 +90,13 @@ module ObjectAttorney
86
90
  module ClassMethods
87
91
 
88
92
  def represents(represented_object_name, options = {})
89
- self.instance_variable_set("@represented_object_reflection", AssociationReflection.new(represented_object_name, options))
93
+ self.instance_variable_set("@represented_object_reflection", Reflection.new(represented_object_name, options))
90
94
 
91
95
  define_method(represented_object_name) { represented_object }
96
+
97
+ if options.include?(:properties)
98
+ delegate_properties(*options[:properties], to: represented_object_name)
99
+ end
92
100
  end
93
101
 
94
102
  def represented_object_reflection
@@ -0,0 +1,52 @@
1
+ require "spec_helper"
2
+
3
+
4
+ shared_examples "a BulkPostsForm" do
5
+
6
+ it "1. Creating multiple 'Post's, with a tabless model 'BulkPostsForm' has if it had 'accepts_nested_attributes_for :posts'" do
7
+ params = {
8
+ bulk_post: {
9
+ posts_attributes: {
10
+ "0" => { title: "My title1" },
11
+ "1" => { title: "My title2" }
12
+ }
13
+ }
14
+ }
15
+
16
+ described_class.new(params[:bulk_post]).save
17
+
18
+ Post.all.count.should == 2
19
+ end
20
+
21
+ it "2. Creating new 'Post', editing another and deleting yet another." do
22
+ params = {
23
+ bulk_post: {
24
+ posts_attributes: {
25
+ "0" => { title: "new post" },
26
+ "2" => { id: 1, title: 'altered post' },
27
+ "1" => { id: 2, title: 'to be destroyed', _destroy: true }
28
+ }
29
+ }
30
+ }
31
+
32
+ Post.create(title: "My title1")
33
+ Post.create(title: "My title2")
34
+ Post.all.count.should == 2
35
+ Post.first.title.should == 'My title1'
36
+
37
+ described_class.new(params[:bulk_post]).save
38
+
39
+ Post.all.count.should == 2
40
+ Post.find_by_id(1).title.should == 'altered post'
41
+ Post.find_by_id(3).title.should == 'new post'
42
+ end
43
+
44
+ end
45
+
46
+ describe BulkPostsForm::Base do
47
+ it_behaves_like 'a BulkPostsForm'
48
+ end
49
+
50
+ describe BulkPostsForm::Explicit do
51
+ it_behaves_like 'a BulkPostsForm'
52
+ end
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+
3
+ shared_examples "a BulkPostsAllowOnlyExistingForm" do
4
+
5
+ it "1. Tabless model 'BulkPostsAllowOnlyNew' only accepts editings 'Post' requests and ignores new requests." do
6
+ params = {
7
+ bulk_post: {
8
+ posts_attributes: {
9
+ "0" => { title: "new post" },
10
+ "1" => { id: 1, title: 'altered post' },
11
+ "2" => { id: 2, title: '', _destroy: true }
12
+ }
13
+ }
14
+ }
15
+
16
+ Post.create(title: "My title1")
17
+ Post.create(title: "My title2")
18
+ Post.all.count.should == 2
19
+ Post.find_by_id(1).title.should == 'My title1'
20
+ Post.find_by_id(2).title.should == 'My title2'
21
+
22
+ buld_posts_form = described_class.new(params[:bulk_post])
23
+ buld_posts_form.save
24
+
25
+ Post.all.count.should == 1
26
+ Post.first.title.should == 'altered post'
27
+ end
28
+
29
+ end
30
+
31
+ describe BulkPostsAllowOnlyExistingForm::Base do
32
+ it_behaves_like 'a BulkPostsAllowOnlyExistingForm'
33
+ end
34
+
35
+ describe BulkPostsAllowOnlyExistingForm::Explicit do
36
+ it_behaves_like 'a BulkPostsAllowOnlyExistingForm'
37
+ end