object_attorney 0.0.2 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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