object_attorney 0.0.2 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ *.DS_Store
@@ -1,12 +1,9 @@
1
1
  require "object_attorney/version"
2
+ require "object_attorney/nested_objects"
3
+ require "object_attorney/orm"
2
4
 
3
5
  module ObjectAttorney
4
6
 
5
- ################# EXAMPLES ##################
6
- # represents :user
7
- # accepts_nested_objects :addess, :posts
8
- #############################################
9
-
10
7
  def initialize(attributes = {}, object = nil, options = {})
11
8
  if !attributes.kind_of?(Hash) && object.blank?
12
9
  object = attributes
@@ -20,50 +17,12 @@ module ObjectAttorney
20
17
  object = nil
21
18
  end
22
19
 
23
- @object = object if object.present?
24
- @trusted_data = options[:trusted_data] if options.present? && options.kind_of?(Hash)
20
+ @represented_object = object if object.present?
25
21
 
26
22
  assign_attributes attributes
27
23
  mark_for_destruction_if_necessary(self, attributes)
28
24
  end
29
25
 
30
- def nested_objects
31
- unless nested_objects_updated
32
- @nested_objects = self.class.instance_variable_get("@nested_objects").map { |nested_object_sym| self.send(nested_object_sym) }.flatten
33
- nested_objects_updated = true
34
- end
35
-
36
- @nested_objects
37
- end
38
-
39
- def new_record?
40
- @object.try_or_return(:new_record?, true)
41
- end
42
-
43
- def persisted?
44
- @object.try_or_return(:persisted?, false)
45
- end
46
-
47
- def mark_for_destruction
48
- @marked_for_destruction = true
49
- end
50
-
51
- def marked_for_destruction?
52
- @marked_for_destruction
53
- end
54
-
55
- def save
56
- save_process
57
- end
58
-
59
- def save!
60
- save_process true
61
- end
62
-
63
- def destroy
64
- @object.try_or_return(:destroy, true) && nested_objects.all?(&:destroy)
65
- end
66
-
67
26
  def assign_attributes(attributes = {})
68
27
  return if attributes.blank?
69
28
 
@@ -72,234 +31,91 @@ module ObjectAttorney
72
31
  end
73
32
  end
74
33
 
75
- def mark_for_destruction_if_necessary(object, attributes)
76
- return nil unless attributes.kind_of?(Hash)
77
-
78
- _destroy = attributes["_destroy"] || attributes[:_destroy]
79
-
80
- object.mark_for_destruction if ["true", "1"].include?(_destroy)
81
- end
82
-
83
34
  def read_attribute_for_serialization(attribute)
84
35
  respond_to?(attribute) ? send(attribute) : nil
85
36
  end
86
37
 
87
38
  protected #--------------------------------------------------protected
88
39
 
89
- def self.included(base)
90
- base.extend(ClassMethods)
91
-
92
- base.class_eval do
93
- include ActiveModel::Validations
94
- include ActiveModel::Conversion
95
-
96
- attr_accessor :nested_objects_updated, :trusted_data
97
- validate :validate_own_object, :validate_nested_objects
98
-
99
- def valid?
100
- override_valid? ? true : super
101
- end
102
- end
103
-
104
- base.instance_variable_set("@nested_objects", [])
105
- base.instance_variable_set("@white_list", [])
106
- base.instance_variable_set("@black_list", [])
107
- end
108
-
109
40
  def allowed_attribute(attribute)
110
41
  attribute = attribute.to_s
111
42
 
112
- return false if !respond_to?("#{attribute}=") || black_list.include?(attribute)
113
- return true if self.class.instance_variable_get("@white_list").empty?
114
-
115
- self.class.instance_variable_get("@white_list").include?(attribute)
116
- end
117
-
118
- def black_list
119
- [*self.class.instance_variable_get("@black_list"), "trusted_data", "nested_objects_updated", "_destroy"]
120
- end
121
-
122
- def save_process(raise_exception = false)
123
- before_save
124
- save_result = raise_exception ? _save! : _save
125
- after_save if save_result
126
- save_result
127
- end
128
-
129
- def before_save; end
130
- def after_save; end
131
-
132
- def _save
133
- begin
134
- ActiveRecord::Base.transaction { _save! }
135
- rescue
136
- valid?
137
- false
138
- end
139
- end
43
+ return false if !respond_to?("#{attribute}=") || self.class.black_list.include?(attribute)
44
+ return true if self.class.white_list.empty?
140
45
 
141
- def _save!
142
- result = (save_or_raise_rollback! ? save_or_destroy_nested_objects : false)
143
- valid?
144
- result
46
+ self.class.white_list.include?(attribute)
145
47
  end
146
48
 
147
- def save_or_raise_rollback!
148
- if valid?
149
- save_own_object
150
- else
151
- raise ActiveRecord::Rollback
152
- end
49
+ def validate_represented_object
50
+ valid = override_validations? ? true : try_or_return(@represented_object, :valid?, true)
51
+ import_represented_object_errors unless valid
52
+ valid
153
53
  end
154
54
 
155
- def save_own_object
156
- @object.try_or_return(:save!, true)
55
+ def import_represented_object_errors
56
+ @represented_object.errors.each { |key, value| self.errors.add(key, value) }
157
57
  end
158
58
 
159
- def save_or_destroy_nested_objects
160
- result = nested_objects.map do |nested_object|
161
- nested_object.marked_for_destruction? ? nested_object.destroy : nested_object.save!
162
- end.all?
163
-
164
- self.errors.add(:base, "Some errors where found while saving the nested objects.") unless result
59
+ private #################### PRIVATE METHODS DOWN BELOW ######################
165
60
 
166
- result
167
- end
168
-
169
- def validate_own_object
170
- valid = override_valid? ? true : @object.try_or_return(:valid?, true)
171
- import_own_object_errors unless valid
172
- valid
173
- end
174
-
175
- def validate_nested_objects
176
- #nested_objects.all?(&:valid?) #will not validate all nested_objects
177
- return true if nested_objects.reject(&:marked_for_destruction?).map(&:valid?).all?
178
- import_nested_objects_errors
179
- false
180
- end
61
+ def self.included(base)
62
+ base.extend(ClassMethods)
181
63
 
182
- def get_attributes_for_existing(nested_object_name, existing_nested_object)
183
- attributes = send("#{nested_object_name}_attributes")
184
- return {} if attributes.blank?
185
- attributes.present? ? (attributes.values.select { |x| x[:id].to_i == existing_nested_object.id }.first || {}) : {}
186
- end
64
+ base.class_eval do
65
+ include ActiveModel::Validations
66
+ include ActiveModel::Conversion
67
+ include ObjectAttorney::NestedObjects
68
+ include ObjectAttorney::ORM
187
69
 
188
- def import_own_object_errors
189
- @object.errors.each { |key, value| self.errors.add(key, value) }
190
- end
70
+ validate :validate_represented_object
191
71
 
192
- def import_nested_objects_errors
193
- self.class.instance_variable_get("@nested_objects").map do |nested_object_sym|
194
-
195
- [*self.send(nested_object_sym)].each do |nested_object|
196
- nested_object.errors.full_messages.each { |message| self.errors.add(nested_object_sym, message) }
72
+ def valid?
73
+ override_validations? ? true : super
197
74
  end
198
-
199
75
  end
200
76
  end
201
77
 
202
- # def return_hash_with_existing_errors(hash, object = nil)
203
- # object ||= self
204
- # object.errors.empty? ? hash : hash.merge({ errors: object.errors })
205
- # end
206
-
207
- private #------------------------------ private
208
-
209
- def attributes_without_destroy(attributes)
210
- return nil unless attributes.kind_of?(Hash)
211
-
212
- _attributes = attributes.dup
213
- _attributes.delete("_destroy")
214
- _attributes.delete(:_destroy)
215
-
216
- _attributes
217
- end
218
-
219
- def override_valid?
78
+ def override_validations?
220
79
  marked_for_destruction?
221
80
  end
222
81
 
223
- def nested_getter(nested_object_name)
224
- nested_instance_variable = self.instance_variable_get("@#{nested_object_name}")
225
-
226
- if nested_instance_variable.nil?
227
- nested_instance_variable = get_existing_and_new_nested_objects(nested_object_name)
228
- self.instance_variable_set("@#{nested_object_name}", nested_instance_variable)
229
- end
230
-
231
- nested_instance_variable
82
+ def try_or_return(object, method, default_value)
83
+ returning_value = object.try(method)
84
+ returning_value.nil? ? default_value : returning_value
232
85
  end
233
86
 
234
- def get_existing_and_new_nested_objects(nested_object_name)
235
- existing_and_new_nested_objects = []
236
-
237
- update_existing_nested_objects(existing_and_new_nested_objects, nested_object_name)
238
- build_new_nested_objects(existing_and_new_nested_objects, nested_object_name)
239
-
240
- existing_and_new_nested_objects
241
- end
242
-
243
- def update_existing_nested_objects(existing_and_new_nested_objects, nested_object_name)
244
- (send("existing_#{nested_object_name}") || []).each do |existing_nested_object|
245
- attributes = get_attributes_for_existing(nested_object_name, existing_nested_object)
87
+ module ClassMethods
246
88
 
247
- mark_for_destruction_if_necessary(existing_nested_object, attributes)
248
- existing_nested_object.assign_attributes(attributes_without_destroy(attributes))
89
+ def represents(represented_object, represented_object_class = nil)
90
+ represented_object_class ||= represented_object.to_s.camelize
249
91
 
250
- existing_and_new_nested_objects << existing_nested_object
92
+ define_method(represented_object) do
93
+ @represented_object ||= represented_object_class.constantize.new
94
+ end
251
95
  end
252
- end
253
-
254
- def build_new_nested_objects(existing_and_new_nested_objects, nested_object_name)
255
- (send("#{nested_object_name}_attributes") || {}).values.each do |attributes|
256
- next if attributes["id"].present?
257
96
 
258
- new_nested_object = send("build_#{nested_object_name.to_s.singularize}", attributes_without_destroy(attributes))
259
- mark_for_destruction_if_necessary(new_nested_object, attributes)
260
-
261
- existing_and_new_nested_objects << new_nested_object
97
+ def delegate_properties(*properties, options)
98
+ properties.each { |property| delegate_propertiy(property, options) }
262
99
  end
263
- end
264
100
 
265
- module ClassMethods
266
-
267
- attr_accessor :own_object_class
268
-
269
- def represents(own_object)
270
- define_method(own_object) do
271
- own_object_class = self.class.instance_variable_get(:@own_object_class)
272
- own_object_class ||= own_object.to_s.camelize
273
- @object ||= own_object_class.constantize.new
274
- end
101
+ def delegate_propertiy(property, options)
102
+ delegate property, "#{property}=", options
275
103
  end
276
104
 
277
- def accepts_nested_objects(*nested_objects_list)
278
- self.instance_variable_set("@nested_objects", nested_objects_list)
279
- self.send(:attr_accessor, *nested_objects_list.map { |attribute| "#{attribute}_attributes".to_sym })
280
- define_nested_objects_getter_methods nested_objects_list
105
+ def attr_white_list=(*white_list)
106
+ @white_list = white_list.map(&:to_s)
281
107
  end
282
108
 
283
- def attr_white_list(*white_list)
284
- self.class.instance_variable_set("@white_list", white_list.map(&:to_s))
109
+ def white_list
110
+ @white_list ||= []
285
111
  end
286
112
 
287
113
  def attr_black_list(*black_list)
288
- self.class.instance_variable_set("@black_list", black_list.map(&:to_s))
289
- end
290
-
291
- def reflect_on_association(association)
292
- nil
114
+ @black_list = black_list.map(&:to_s)
293
115
  end
294
116
 
295
- private #------------------------ private
296
-
297
- def define_nested_objects_getter_methods(nested_objects_list)
298
- nested_objects_list.each do |nested_object_name|
299
- define_method(nested_object_name) do
300
- nested_getter(nested_object_name)
301
- end
302
- end
117
+ def black_list
118
+ @black_list ||= ["_destroy"]
303
119
  end
304
120
 
305
121
  end
@@ -0,0 +1,138 @@
1
+ module ObjectAttorney
2
+ module NestedObjects
3
+
4
+ def nested_objects
5
+ self.class.instance_variable_get("@nested_objects").map { |nested_object_sym| self.send(nested_object_sym) }.flatten
6
+ end
7
+
8
+ def mark_for_destruction
9
+ @marked_for_destruction = true
10
+ end
11
+
12
+ def marked_for_destruction?
13
+ @marked_for_destruction
14
+ end
15
+
16
+ def mark_for_destruction_if_necessary(object, attributes)
17
+ return nil unless attributes.kind_of?(Hash)
18
+
19
+ _destroy = attributes["_destroy"] || attributes[:_destroy]
20
+
21
+ object.mark_for_destruction if ["true", "1"].include?(_destroy)
22
+ end
23
+
24
+ protected #################### PROTECTED METHODS DOWN BELOW ######################
25
+
26
+ def validate_nested_objects
27
+ #nested_objects.all?(&:valid?) #will not validate all nested_objects
28
+ return true if nested_objects.reject(&:marked_for_destruction?).map(&:valid?).all?
29
+ import_nested_objects_errors
30
+ false
31
+ end
32
+
33
+ def import_nested_objects_errors
34
+ self.class.instance_variable_get("@nested_objects").map do |nested_object_sym|
35
+
36
+ [*self.send(nested_object_sym)].each do |nested_object|
37
+ nested_object.errors.full_messages.each { |message| self.errors.add(nested_object_sym, message) }
38
+ end
39
+
40
+ end
41
+ end
42
+
43
+ def get_attributes_for_existing(nested_object_name, existing_nested_object)
44
+ attributes = send("#{nested_object_name}_attributes")
45
+ return {} if attributes.blank?
46
+ attributes.present? ? (attributes.values.select { |x| x[:id].to_i == existing_nested_object.id }.first || {}) : {}
47
+ end
48
+
49
+ private #################### PRIVATE METHODS DOWN BELOW ######################
50
+
51
+ def self.included(base)
52
+ base.extend(ClassMethods)
53
+
54
+ base.class_eval do
55
+ validate :validate_nested_objects
56
+ end
57
+
58
+ base.instance_variable_set("@nested_objects", [])
59
+ end
60
+
61
+ def attributes_without_destroy(attributes)
62
+ return nil unless attributes.kind_of?(Hash)
63
+
64
+ _attributes = attributes.dup
65
+ _attributes.delete("_destroy")
66
+ _attributes.delete(:_destroy)
67
+
68
+ _attributes
69
+ end
70
+
71
+ def nested_getter(nested_object_name)
72
+ nested_instance_variable = self.instance_variable_get("@#{nested_object_name}")
73
+
74
+ if nested_instance_variable.nil?
75
+ nested_instance_variable = get_existing_and_new_nested_objects(nested_object_name)
76
+ self.instance_variable_set("@#{nested_object_name}", nested_instance_variable)
77
+ end
78
+
79
+ nested_instance_variable
80
+ end
81
+
82
+ def get_existing_and_new_nested_objects(nested_object_name)
83
+ existing_and_new_nested_objects = []
84
+
85
+ update_existing_nested_objects(existing_and_new_nested_objects, nested_object_name)
86
+ build_new_nested_objects(existing_and_new_nested_objects, nested_object_name)
87
+
88
+ existing_and_new_nested_objects
89
+ end
90
+
91
+ def update_existing_nested_objects(existing_and_new_nested_objects, nested_object_name)
92
+ (send("existing_#{nested_object_name}") || []).each do |existing_nested_object|
93
+ attributes = get_attributes_for_existing(nested_object_name, existing_nested_object)
94
+
95
+ mark_for_destruction_if_necessary(existing_nested_object, attributes)
96
+ existing_nested_object.assign_attributes(attributes_without_destroy(attributes))
97
+
98
+ existing_and_new_nested_objects << existing_nested_object
99
+ end
100
+ end
101
+
102
+ def build_new_nested_objects(existing_and_new_nested_objects, nested_object_name)
103
+ (send("#{nested_object_name}_attributes") || {}).values.each do |attributes|
104
+ next if attributes["id"].present?
105
+
106
+ new_nested_object = send("build_#{nested_object_name.to_s.singularize}", attributes_without_destroy(attributes))
107
+ mark_for_destruction_if_necessary(new_nested_object, attributes)
108
+
109
+ existing_and_new_nested_objects << new_nested_object
110
+ end
111
+ end
112
+
113
+ module ClassMethods
114
+
115
+ def accepts_nested_objects(*nested_objects_list)
116
+ self.instance_variable_set("@nested_objects", nested_objects_list)
117
+ self.send(:attr_accessor, *nested_objects_list.map { |attribute| "#{attribute}_attributes".to_sym })
118
+ define_nested_objects_getter_methods nested_objects_list
119
+ end
120
+
121
+ def reflect_on_association(association)
122
+ nil
123
+ end
124
+
125
+ private #################### PRIVATE METHODS DOWN BELOW ######################
126
+
127
+ def define_nested_objects_getter_methods(nested_objects_list)
128
+ nested_objects_list.each do |nested_object_name|
129
+ define_method(nested_object_name) do
130
+ nested_getter(nested_object_name)
131
+ end
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1,33 @@
1
+ module ActiveModel
2
+
3
+ module Validations
4
+
5
+ class NestedUniquenessValidator < EachValidator
6
+ def validate_each(record, attr_name, value)
7
+ uniq_value, existing_objects, first_element = options[:uniq_value], [], nil
8
+
9
+ record.send(attr_name).each do |object|
10
+ next if object.marked_for_destruction?
11
+
12
+ first_element = object if first_element.nil?
13
+
14
+ if existing_objects.include?(object.send(uniq_value))
15
+ object.errors.add(uniq_value, :taken)
16
+ record.errors.add(attr_name, :taken)
17
+ else
18
+ existing_objects << object.send(uniq_value)
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ module HelperMethods
26
+
27
+ def validates_nested_uniqueness(*attr_names)
28
+ validates_with NestedUniquenessValidator, _merge_attributes(attr_names)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,60 @@
1
+ require "object_attorney/orm_handlers/smooth_operator"
2
+
3
+ module ObjectAttorney
4
+ module ORM
5
+
6
+ def new_record?
7
+ try_or_return(@represented_object, :new_record?, true)
8
+ end
9
+
10
+ def persisted?
11
+ try_or_return(@represented_object, :persisted?, false)
12
+ end
13
+
14
+ def save
15
+ save!(:save)
16
+ end
17
+
18
+ def save!(save_method = :save!)
19
+ before_save
20
+ save_result = valid? ? save_after_validations(save_method) : false
21
+ after_save if valid? && save_result
22
+ save_result
23
+ end
24
+
25
+ def destroy
26
+ return true if @represented_object.blank?
27
+ evoke_method_on_object(@represented_object, :destroy)
28
+ end
29
+
30
+ def call_save_or_destroy(object, save_method)
31
+ if object == self
32
+ @represented_object.present? ? evoke_method_on_object(@represented_object, save_method) : true
33
+ else
34
+ save_method = :destroy if check_if_marked_for_destruction?(object)
35
+ evoke_method_on_object(object, save_method)
36
+ end
37
+ end
38
+
39
+ protected #################### PROTECTED METHODS DOWN BELOW ######################
40
+
41
+ def before_save; end
42
+ def after_save; end
43
+
44
+ def save_after_validations(save_method)
45
+ return true if @represented_object.blank?
46
+ evoke_method_on_object(@represented_object, save_method)
47
+ end
48
+
49
+ private #################### PRIVATE METHODS DOWN BELOW ######################
50
+
51
+ def check_if_marked_for_destruction?(object)
52
+ object.respond_to?(:marked_for_destruction?) ? object.marked_for_destruction? : false
53
+ end
54
+
55
+ def evoke_method_on_object(object, method)
56
+ object.send(method)
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,51 @@
1
+ module ObjectAttorney
2
+ module OrmHandlers
3
+
4
+ module SmoothOperator
5
+
6
+ def save(options = {})
7
+ save!(options, :save)
8
+ end
9
+
10
+ def save!(options = {}, save_method = :save!)
11
+ before_save
12
+ save_result = valid? ? save_after_validations(save_method, options) : false
13
+ after_save if valid? && save_result
14
+ save_result
15
+ end
16
+
17
+ def destroy(options = {})
18
+ return true if @represented_object.blank?
19
+ evoke_method_on_object(@represented_object, :destroy, options)
20
+ end
21
+
22
+ def rollback(options = {})
23
+ @represented_object.rollback(options)
24
+ end
25
+
26
+ def call_save_or_destroy(object, save_method, options = {})
27
+ if object == self
28
+ @represented_object.present? ? evoke_method_on_object(@represented_object, save_method, options) : true
29
+ else
30
+ save_method = :destroy if check_if_marked_for_destruction?(object)
31
+ evoke_method_on_object(object, save_method, options)
32
+ end
33
+ end
34
+
35
+ protected #################### PROTECTED METHODS DOWN BELOW ######################
36
+
37
+ def save_after_validations(save_method, options = {})
38
+ return true if @represented_object.blank?
39
+ evoke_method_on_object(@represented_object, save_method, options)
40
+ end
41
+
42
+ private #################### PRIVATE METHODS DOWN BELOW ######################
43
+
44
+ def evoke_method_on_object(object, method, options = {})
45
+ object.send(method, options)
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -1,3 +1,3 @@
1
1
  module ObjectAttorney
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.9"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: object_attorney
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-02 00:00:00.000000000 Z
12
+ date: 2013-08-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -56,6 +56,10 @@ files:
56
56
  - README.md
57
57
  - Rakefile
58
58
  - lib/object_attorney.rb
59
+ - lib/object_attorney/nested_objects.rb
60
+ - lib/object_attorney/nested_uniqueness_validator.rb
61
+ - lib/object_attorney/orm.rb
62
+ - lib/object_attorney/orm_handlers/smooth_operator.rb
59
63
  - lib/object_attorney/version.rb
60
64
  - object_attorney.gemspec
61
65
  homepage: https://github.com/goncalvesjoao/object_attorney