active_data 0.3.0 → 1.0.0

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.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rspec +0 -1
  4. data/.rvmrc +1 -1
  5. data/.travis.yml +13 -6
  6. data/Appraisals +7 -0
  7. data/Gemfile +1 -5
  8. data/Guardfile +68 -15
  9. data/README.md +144 -2
  10. data/active_data.gemspec +19 -11
  11. data/gemfiles/rails.4.0.gemfile +14 -0
  12. data/gemfiles/rails.4.1.gemfile +14 -0
  13. data/gemfiles/rails.4.2.gemfile +14 -0
  14. data/gemfiles/rails.5.0.gemfile +14 -0
  15. data/lib/active_data.rb +120 -3
  16. data/lib/active_data/active_record/associations.rb +50 -0
  17. data/lib/active_data/active_record/nested_attributes.rb +24 -0
  18. data/lib/active_data/config.rb +40 -0
  19. data/lib/active_data/errors.rb +93 -0
  20. data/lib/active_data/extensions.rb +33 -0
  21. data/lib/active_data/model.rb +16 -74
  22. data/lib/active_data/model/associations.rb +84 -15
  23. data/lib/active_data/model/associations/base.rb +79 -0
  24. data/lib/active_data/model/associations/collection/embedded.rb +12 -0
  25. data/lib/active_data/model/associations/collection/proxy.rb +32 -0
  26. data/lib/active_data/model/associations/collection/referenced.rb +26 -0
  27. data/lib/active_data/model/associations/embeds_many.rb +124 -18
  28. data/lib/active_data/model/associations/embeds_one.rb +90 -15
  29. data/lib/active_data/model/associations/nested_attributes.rb +180 -0
  30. data/lib/active_data/model/associations/references_many.rb +96 -0
  31. data/lib/active_data/model/associations/references_one.rb +83 -0
  32. data/lib/active_data/model/associations/reflections/base.rb +100 -0
  33. data/lib/active_data/model/associations/reflections/embeds_many.rb +25 -0
  34. data/lib/active_data/model/associations/reflections/embeds_one.rb +49 -0
  35. data/lib/active_data/model/associations/reflections/reference_reflection.rb +45 -0
  36. data/lib/active_data/model/associations/reflections/references_many.rb +28 -0
  37. data/lib/active_data/model/associations/reflections/references_one.rb +28 -0
  38. data/lib/active_data/model/associations/validations.rb +63 -0
  39. data/lib/active_data/model/attributes.rb +247 -0
  40. data/lib/active_data/model/attributes/attribute.rb +73 -0
  41. data/lib/active_data/model/attributes/base.rb +116 -0
  42. data/lib/active_data/model/attributes/collection.rb +17 -0
  43. data/lib/active_data/model/attributes/dictionary.rb +26 -0
  44. data/lib/active_data/model/attributes/localized.rb +42 -0
  45. data/lib/active_data/model/attributes/reference_many.rb +21 -0
  46. data/lib/active_data/model/attributes/reference_one.rb +42 -0
  47. data/lib/active_data/model/attributes/reflections/attribute.rb +55 -0
  48. data/lib/active_data/model/attributes/reflections/base.rb +62 -0
  49. data/lib/active_data/model/attributes/reflections/collection.rb +10 -0
  50. data/lib/active_data/model/attributes/reflections/dictionary.rb +13 -0
  51. data/lib/active_data/model/attributes/reflections/localized.rb +43 -0
  52. data/lib/active_data/model/attributes/reflections/reference_many.rb +10 -0
  53. data/lib/active_data/model/attributes/reflections/reference_one.rb +58 -0
  54. data/lib/active_data/model/attributes/reflections/represents.rb +55 -0
  55. data/lib/active_data/model/attributes/represents.rb +64 -0
  56. data/lib/active_data/model/callbacks.rb +71 -0
  57. data/lib/active_data/model/conventions.rb +35 -0
  58. data/lib/active_data/model/dirty.rb +77 -0
  59. data/lib/active_data/model/lifecycle.rb +307 -0
  60. data/lib/active_data/model/localization.rb +21 -0
  61. data/lib/active_data/model/persistence.rb +57 -0
  62. data/lib/active_data/model/primary.rb +51 -0
  63. data/lib/active_data/model/scopes.rb +77 -0
  64. data/lib/active_data/model/validations.rb +27 -0
  65. data/lib/active_data/model/validations/associated.rb +19 -0
  66. data/lib/active_data/model/validations/nested.rb +39 -0
  67. data/lib/active_data/railtie.rb +7 -0
  68. data/lib/active_data/version.rb +1 -1
  69. data/spec/lib/active_data/active_record/associations_spec.rb +149 -0
  70. data/spec/lib/active_data/active_record/nested_attributes_spec.rb +16 -0
  71. data/spec/lib/active_data/config_spec.rb +44 -0
  72. data/spec/lib/active_data/model/associations/embeds_many_spec.rb +362 -52
  73. data/spec/lib/active_data/model/associations/embeds_one_spec.rb +250 -31
  74. data/spec/lib/active_data/model/associations/nested_attributes_spec.rb +23 -0
  75. data/spec/lib/active_data/model/associations/references_many_spec.rb +196 -0
  76. data/spec/lib/active_data/model/associations/references_one_spec.rb +134 -0
  77. data/spec/lib/active_data/model/associations/reflections/embeds_many_spec.rb +144 -0
  78. data/spec/lib/active_data/model/associations/reflections/embeds_one_spec.rb +116 -0
  79. data/spec/lib/active_data/model/associations/reflections/references_many_spec.rb +255 -0
  80. data/spec/lib/active_data/model/associations/reflections/references_one_spec.rb +208 -0
  81. data/spec/lib/active_data/model/associations/validations_spec.rb +153 -0
  82. data/spec/lib/active_data/model/associations_spec.rb +189 -0
  83. data/spec/lib/active_data/model/attributes/attribute_spec.rb +144 -0
  84. data/spec/lib/active_data/model/attributes/base_spec.rb +82 -0
  85. data/spec/lib/active_data/model/attributes/collection_spec.rb +73 -0
  86. data/spec/lib/active_data/model/attributes/dictionary_spec.rb +93 -0
  87. data/spec/lib/active_data/model/attributes/localized_spec.rb +88 -33
  88. data/spec/lib/active_data/model/attributes/reflections/attribute_spec.rb +72 -0
  89. data/spec/lib/active_data/model/attributes/reflections/base_spec.rb +56 -0
  90. data/spec/lib/active_data/model/attributes/reflections/collection_spec.rb +37 -0
  91. data/spec/lib/active_data/model/attributes/reflections/dictionary_spec.rb +43 -0
  92. data/spec/lib/active_data/model/attributes/reflections/localized_spec.rb +37 -0
  93. data/spec/lib/active_data/model/attributes/reflections/represents_spec.rb +70 -0
  94. data/spec/lib/active_data/model/attributes/represents_spec.rb +153 -0
  95. data/spec/lib/active_data/model/attributes_spec.rb +243 -0
  96. data/spec/lib/active_data/model/callbacks_spec.rb +338 -0
  97. data/spec/lib/active_data/model/conventions_spec.rb +12 -0
  98. data/spec/lib/active_data/model/dirty_spec.rb +75 -0
  99. data/spec/lib/active_data/model/lifecycle_spec.rb +330 -0
  100. data/spec/lib/active_data/model/nested_attributes.rb +202 -0
  101. data/spec/lib/active_data/model/persistence_spec.rb +47 -0
  102. data/spec/lib/active_data/model/primary_spec.rb +84 -0
  103. data/spec/lib/active_data/model/scopes_spec.rb +88 -0
  104. data/spec/lib/active_data/model/typecasting_spec.rb +192 -0
  105. data/spec/lib/active_data/model/validations/associated_spec.rb +94 -0
  106. data/spec/lib/active_data/model/validations/nested_spec.rb +93 -0
  107. data/spec/lib/active_data/model/validations_spec.rb +31 -0
  108. data/spec/lib/active_data/model_spec.rb +1 -32
  109. data/spec/lib/active_data_spec.rb +12 -0
  110. data/spec/spec_helper.rb +39 -0
  111. data/spec/support/model_helpers.rb +10 -0
  112. metadata +246 -54
  113. data/gemfiles/Gemfile.rails-3 +0 -14
  114. data/lib/active_data/attributes/base.rb +0 -69
  115. data/lib/active_data/attributes/localized.rb +0 -42
  116. data/lib/active_data/model/associations/association.rb +0 -30
  117. data/lib/active_data/model/attributable.rb +0 -122
  118. data/lib/active_data/model/collectionizable.rb +0 -55
  119. data/lib/active_data/model/collectionizable/proxy.rb +0 -42
  120. data/lib/active_data/model/extensions.rb +0 -9
  121. data/lib/active_data/model/extensions/array.rb +0 -24
  122. data/lib/active_data/model/extensions/big_decimal.rb +0 -17
  123. data/lib/active_data/model/extensions/boolean.rb +0 -38
  124. data/lib/active_data/model/extensions/date.rb +0 -17
  125. data/lib/active_data/model/extensions/date_time.rb +0 -17
  126. data/lib/active_data/model/extensions/float.rb +0 -17
  127. data/lib/active_data/model/extensions/hash.rb +0 -22
  128. data/lib/active_data/model/extensions/integer.rb +0 -17
  129. data/lib/active_data/model/extensions/localized.rb +0 -22
  130. data/lib/active_data/model/extensions/object.rb +0 -17
  131. data/lib/active_data/model/extensions/string.rb +0 -17
  132. data/lib/active_data/model/extensions/time.rb +0 -17
  133. data/lib/active_data/model/localizable.rb +0 -31
  134. data/lib/active_data/model/nested_attributes.rb +0 -58
  135. data/lib/active_data/model/parameterizable.rb +0 -29
  136. data/lib/active_data/validations.rb +0 -7
  137. data/lib/active_data/validations/associated.rb +0 -17
  138. data/spec/lib/active_data/model/attributable_spec.rb +0 -191
  139. data/spec/lib/active_data/model/collectionizable_spec.rb +0 -60
  140. data/spec/lib/active_data/model/nested_attributes_spec.rb +0 -67
  141. data/spec/lib/active_data/model/type_cast_spec.rb +0 -116
  142. data/spec/lib/active_data/validations/associated_spec.rb +0 -88
@@ -0,0 +1,64 @@
1
+ module ActiveData
2
+ module Model
3
+ module Attributes
4
+ class Represents < Attribute
5
+ delegate :reader, :reader_before_type_cast, :writer, to: :reflection
6
+
7
+ def write value
8
+ return if readonly?
9
+ pollute do
10
+ reset
11
+ reference.send(writer, value)
12
+ end
13
+ end
14
+
15
+ def reset
16
+ super
17
+ remove_variable(:cached_value, :cached_value_before_type_cast)
18
+ end
19
+
20
+ def read
21
+ reset if cached_value != read_value
22
+ variable_cache(:value) do
23
+ normalize(enumerize(defaultize(cached_value, read_before_type_cast)))
24
+ end
25
+ end
26
+
27
+ def read_before_type_cast
28
+ reset if cached_value_before_type_cast != read_value_before_type_cast
29
+ variable_cache(:value_before_type_cast) do
30
+ defaultize(cached_value_before_type_cast)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def reference
37
+ owner.send(reflection.reference)
38
+ end
39
+
40
+ def read_value
41
+ ref = reference
42
+ ref.public_send(reader) if ref
43
+ end
44
+
45
+ def cached_value
46
+ variable_cache(:cached_value) { read_value }
47
+ end
48
+
49
+ def read_value_before_type_cast
50
+ ref = reference
51
+ if ref
52
+ ref.respond_to?(reader_before_type_cast) ?
53
+ ref.public_send(reader_before_type_cast) :
54
+ ref.public_send(reader)
55
+ end
56
+ end
57
+
58
+ def cached_value_before_type_cast
59
+ variable_cache(:cached_value_before_type_cast) { read_value_before_type_cast }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,71 @@
1
+ module ActiveData
2
+ module Model
3
+
4
+ # == Callbacks for ActiveData::Model lifecycle
5
+ #
6
+ # Provides ActiveModel callbacks support for lifecycle
7
+ # actions.
8
+ #
9
+ # class Book
10
+ # include ActiveData::Model
11
+ #
12
+ # attribute :id, Integer
13
+ # attribute :title, String
14
+ #
15
+ # define_save do
16
+ # REDIS.set(id, attributes.to_json)
17
+ # end
18
+ #
19
+ # define_destroy do
20
+ # REDIS.del(instance.id)
21
+ # end
22
+ #
23
+ # after_initialize :setup_id
24
+ # before_save :do_something
25
+ # around_update do |&block|
26
+ # ...
27
+ # block.call
28
+ # ...
29
+ # end
30
+ # after_destroy { ... }
31
+ # end
32
+ #
33
+ module Callbacks
34
+ extend ActiveSupport::Concern
35
+
36
+ included do
37
+ extend ActiveModel::Callbacks
38
+
39
+ include ActiveModel::Validations::Callbacks
40
+ include Lifecycle
41
+ prepend PrependMethods
42
+
43
+ define_model_callbacks :initialize, only: :after
44
+ define_model_callbacks :save, :create, :update, :destroy
45
+ end
46
+
47
+ module PrependMethods
48
+ def initialize *_
49
+ super(*_)
50
+ run_callbacks :initialize
51
+ end
52
+
53
+ def save_object &block
54
+ run_callbacks(:save) { super(&block) }
55
+ end
56
+
57
+ def create_object &block
58
+ run_callbacks(:create) { super(&block) }
59
+ end
60
+
61
+ def update_object &block
62
+ run_callbacks(:update) { super(&block) }
63
+ end
64
+
65
+ def destroy_object &block
66
+ run_callbacks(:destroy) { super(&block) }
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,35 @@
1
+ module ActiveData
2
+ module Model
3
+ module Conventions
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ delegate :logger, to: ActiveData
8
+ self.include_root_in_json = ActiveData.include_root_in_json
9
+ end
10
+
11
+ def persisted?
12
+ false
13
+ end
14
+
15
+ def new_record?
16
+ !persisted?
17
+ end
18
+ alias_method :new_object?, :new_record?
19
+
20
+ module ClassMethods
21
+ def i18n_scope
22
+ ActiveData.i18n_scope
23
+ end
24
+
25
+ def to_ary
26
+ nil
27
+ end
28
+
29
+ def primary_name
30
+ nil
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,77 @@
1
+ module ActiveData
2
+ module Model
3
+ module Dirty
4
+ extend ActiveSupport::Concern
5
+
6
+ ::Module.class_eval do
7
+ alias_method :unconcerned_append_features, :append_features
8
+ end
9
+
10
+ DIRTY_CLONE = ActiveModel::Dirty.clone
11
+ DIRTY_CLONE.class_eval do
12
+ def self.append_features(base)
13
+ unconcerned_append_features(base)
14
+ end
15
+ def self.included(base)
16
+ end
17
+ end
18
+
19
+ included do
20
+ include DIRTY_CLONE
21
+
22
+ unless method_defined?(:set_attribute_was)
23
+ def set_attribute_was(attr, old_value)
24
+ changed_attributes[attr] = old_value
25
+ end
26
+ private :set_attribute_was
27
+ end
28
+
29
+ unless method_defined?(:clear_changes_information)
30
+ if method_defined?(:reset_changes)
31
+ def clear_changes_information
32
+ reset_changes
33
+ end
34
+ else
35
+ def clear_changes_information
36
+ @previously_changed = nil
37
+ @changed_attributes = nil
38
+ end
39
+ end
40
+ end
41
+
42
+ attribute_names(false).each do |name|
43
+ define_dirty name, generated_attributes_methods
44
+ end
45
+ _attribute_aliases.keys.each do |name|
46
+ define_dirty name, generated_attributes_methods
47
+ end
48
+ end
49
+
50
+ module ClassMethods
51
+ def define_dirty method, target = self
52
+ reflection = reflect_on_attribute(method)
53
+ name = reflection ? reflection.name : method
54
+
55
+ %w[changed? change will_change! was
56
+ previously_changed? previous_change].each do |suffix|
57
+ target.class_eval <<-RUBY, __FILE__, __LINE__ + 1
58
+ def #{method}_#{suffix}
59
+ attribute_#{suffix} '#{name}'
60
+ end
61
+ RUBY
62
+ end
63
+
64
+ target.class_eval <<-RUBY, __FILE__, __LINE__ + 1
65
+ def restore_#{method}!
66
+ restore_attribute! '#{name}'
67
+ end
68
+ RUBY
69
+ end
70
+
71
+ def dirty?
72
+ true
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,307 @@
1
+ module ActiveData
2
+ module Model
3
+
4
+ # == Lifecycle methods for ActiveData::Model
5
+ #
6
+ # Provides methods +save+ and +destroy+ and its bang variants.
7
+ # Also, patches +create+ and +update_attributes+ methods by adding
8
+ # save at the end.
9
+ #
10
+ # You can define save or destroy performers with <tt>define_<action></tt>
11
+ # methods. Create and update performers might be defined instead of
12
+ # save performer:
13
+ #
14
+ # class Book
15
+ # include ActiveData::Model
16
+ # include ActiveData::Model::Lifecycle
17
+ #
18
+ # attribute :id, Integer
19
+ # attribute :title, String
20
+ #
21
+ # define_save do # executes in the instance scope
22
+ # REDIS.set(id, attributes.to_json)
23
+ # end
24
+ #
25
+ # define_destroy do
26
+ # REDIS.del(id)
27
+ # end
28
+ # end
29
+ #
30
+ # class Author
31
+ # include ActiveData::Model
32
+ # include ActiveData::Model::Lifecycle
33
+ #
34
+ # attribute :id, Integer
35
+ # attribute :name, String
36
+ #
37
+ # define_create do # will be called on create only
38
+ # REDIS.sadd('author_ids', id)
39
+ # REDIS.set(id, attributes.to_json)
40
+ # end
41
+ #
42
+ # define_update do # will be called on update only
43
+ # REDIS.set(id, attributes.to_json)
44
+ # end
45
+ # end
46
+ #
47
+ # In case of undefined performer ActiveData::UnsavableObject
48
+ # or ActiveData::UndestroyableObject will be raised respectively.
49
+ #
50
+ # If performers was not defined in model, they cat be passed as
51
+ # blocks to `save`, `update` and `destroy` methods:
52
+ #
53
+ # authos.save { REDIS.set(id, attributes.to_json) }
54
+ # authos.update { REDIS.set(id, attributes.to_json) }
55
+ # authos.destroy { REDIS.del(id) }
56
+ #
57
+ # Save and destroy processes acts almost the save way as
58
+ # ActiveRecord's (with +persisted?+ and +destroyed?+ methods
59
+ # affecting).
60
+ #
61
+ module Lifecycle
62
+ extend ActiveSupport::Concern
63
+
64
+ included do
65
+ include Persistence
66
+
67
+ class_attribute *[:save, :create, :update, :destroy].map { |action| "_#{action}_performer" }
68
+ private *[:save, :create, :update, :destroy].map { |action| "_#{action}_performer=" }
69
+ end
70
+
71
+ module ClassMethods
72
+
73
+ # <tt>define_<action></tt> methods define performers for lifecycle
74
+ # actions. Every action block must return boolean result, which
75
+ # would mean the action success. If action performed unsuccessfully
76
+ # ActiveData::ObjectNotSaved or ActiveData::ObjectNotDestroyed will
77
+ # be raised respectively in case of bang methods using.
78
+ #
79
+ # class Author
80
+ # define_create { true }
81
+ # end
82
+ #
83
+ # Author.new.save # => true
84
+ # Author.new.save! # => true
85
+ #
86
+ # class Author
87
+ # define_create { false }
88
+ # end
89
+ #
90
+ # Author.new.save # => false
91
+ # Author.new.save! # => ActiveData::ObjectNotSaved
92
+ #
93
+ # Also performers blocks are executed in the instance context, but
94
+ # instance also passed as argument
95
+ #
96
+ # define_update do |instance|
97
+ # instance.attributes.to_json
98
+ # end
99
+ #
100
+ # +define_create+ and +define_update+ performers has higher priority
101
+ # than +define_save+.
102
+ #
103
+ # class Author
104
+ # define_update { ... }
105
+ # define_save { ... }
106
+ # end
107
+ #
108
+ # author = Author.create # using define_save performer
109
+ # author.update_attributes(...) # using define_update performer
110
+ #
111
+ [:save, :create, :update, :destroy].each do |action|
112
+ define_method "define_#{action}" do |&block|
113
+ send("_#{action}_performer=", block)
114
+ end
115
+ end
116
+
117
+ # Initializes new instance with attributes passed and calls +save+
118
+ # on it. Returns instance in any case.
119
+ #
120
+ def create *args
121
+ new(*args).tap(&:save)
122
+ end
123
+
124
+ # Initializes new instance with attributes passed and calls +save!+
125
+ # on it. Returns instance in case of success and raises ActiveData::ValidationError
126
+ # or ActiveData::ObjectNotSaved in case of validation or saving fail respectively.
127
+ #
128
+ def create! *args
129
+ new(*args).tap(&:save!)
130
+ end
131
+ end
132
+
133
+ # <tt>define_<action></tt> on instance level works the same
134
+ # way as class <tt>define_<action></tt> methods, but defines
135
+ # performers for instance only
136
+ #
137
+ # user.define_save do
138
+ # REDIS.set(id, attributes.to_json)
139
+ # end
140
+ # user.save! # => will use instance-level performer
141
+ #
142
+ [:save, :create, :update, :destroy].each do |action|
143
+ define_method "define_#{action}" do |&block|
144
+ send("_#{action}_performer=", block)
145
+ end
146
+ end
147
+
148
+ # Assigns passed attributes and calls +save+
149
+ # Returns true or false in case of successful or unsuccessful
150
+ # saving respectively.
151
+ #
152
+ # author.update(name: 'Donald')
153
+ #
154
+ # If update performer is not defined with `define_update`
155
+ # or `define_save`, it raises ActiveData::UnsavableObject.
156
+ # Also save performer block might be passed instead of in-class
157
+ # performer definition:
158
+ #
159
+ # author.update(name: 'Donald') { REDIS.set(id, attributes.to_json) }
160
+ #
161
+ def update attributes, &block
162
+ assign_attributes(attributes) && save(&block)
163
+ end
164
+ alias_method :update_attributes, :update
165
+
166
+ # Assigns passed attributes and calls +save!+
167
+ # Returns true in case of success and raises ActiveData::ValidationError
168
+ # or ActiveData::ObjectNotSaved in case of validation or
169
+ # saving fail respectively.
170
+ #
171
+ # author.update!(name: 'Donald')
172
+ #
173
+ # If update performer is not defined with `define_update`
174
+ # or `define_save`, it raises ActiveData::UnsavableObject.
175
+ # Also save performer block might be passed instead of in-class
176
+ # performer definition:
177
+ #
178
+ # author.update!(name: 'Donald') { REDIS.set(id, attributes.to_json) }
179
+ #
180
+ def update! attributes, &block
181
+ assign_attributes(attributes) && save!(&block)
182
+ end
183
+ alias_method :update_attributes!, :update!
184
+
185
+ # # Saves object by calling save performer defined with +define_save+,
186
+ # +define_create+ or +define_update+ methods.
187
+ # Returns true or false in case of successful
188
+ # or unsuccessful saving respectively. Changes +persisted?+ to true
189
+ #
190
+ # author.save
191
+ #
192
+ # If save performer is not defined with `define_update` or
193
+ # `define_create` or `define_save`, it raises ActiveData::UnsavableObject.
194
+ # Also save performer block might be passed instead of in-class
195
+ # performer definition:
196
+ #
197
+ # author.save { REDIS.set(id, attributes.to_json) }
198
+ #
199
+ def save options = {}, &block
200
+ raise ActiveData::UnsavableObject unless block || savable?
201
+ valid? && save_object(&block)
202
+ end
203
+
204
+ # Saves object by calling save performer defined with +define_save+,
205
+ # +define_create+ or +define_update+ methods.
206
+ # Returns true in case of success and raises ActiveData::ValidationError
207
+ # or ActiveData::ObjectNotSaved in case of validation or
208
+ # saving fail respectively. Changes +persisted?+ to true
209
+ #
210
+ # author.save!
211
+ #
212
+ # If save performer is not defined with `define_update` or
213
+ # `define_create` or `define_save`, it raises ActiveData::UnsavableObject.
214
+ # Also save performer block might be passed instead of in-class
215
+ # performer definition:
216
+ #
217
+ # author.save! { REDIS.set(id, attributes.to_json) }
218
+ #
219
+ def save! options = {}, &block
220
+ raise ActiveData::UnsavableObject unless block || savable?
221
+ validate!
222
+ save_object(&block) or raise ActiveData::ObjectNotSaved
223
+ end
224
+
225
+ # Destroys object by calling the destroy performer.
226
+ # Returns instance in any case. Changes +persisted?+
227
+ # to false and +destroyed?+ to true in case of success.
228
+ #
229
+ # author.destroy
230
+ #
231
+ # If destroy performer is not defined with `define_destroy`,
232
+ # it raises ActiveData::UndestroyableObject.
233
+ # Also destroy performer block might be passed instead of in-class
234
+ # performer definition:
235
+ #
236
+ # author.destroy { REDIS.del(id) }
237
+ #
238
+ def destroy &block
239
+ raise ActiveData::UndestroyableObject unless block || destroyable?
240
+ destroy_object(&block)
241
+ self
242
+ end
243
+
244
+ # Destroys object by calling the destroy performer.
245
+ # In case of success returns instance and changes +persisted?+
246
+ # to false and +destroyed?+ to true.
247
+ # Raises ActiveData::ObjectNotDestroyed in case of fail.
248
+ #
249
+ # author.destroy!
250
+ #
251
+ # If destroy performer is not defined with `define_destroy`,
252
+ # it raises ActiveData::UndestroyableObject.
253
+ # Also destroy performer block might be passed instead of in-class
254
+ # performer definition:
255
+ #
256
+ # author.destroy! { REDIS.del(id) }
257
+ #
258
+ def destroy! &block
259
+ raise ActiveData::UndestroyableObject unless block || destroyable?
260
+ destroy_object(&block) or raise ActiveData::ObjectNotDestroyed
261
+ self
262
+ end
263
+
264
+ private
265
+
266
+ def savable?
267
+ !!((persisted? ? _update_performer : _create_performer) || _save_performer)
268
+ end
269
+
270
+ def save_object &block
271
+ apply_association_changes! if respond_to?(:apply_association_changes!)
272
+ result = persisted? ? update_object(&block) : create_object(&block)
273
+ mark_persisted! if result
274
+ result
275
+ end
276
+
277
+ def create_object &block
278
+ performer = block || _create_performer || _save_performer
279
+ !!performer_exec(&performer)
280
+ end
281
+
282
+ def update_object &block
283
+ performer = block || _update_performer || _save_performer
284
+ !!performer_exec(&performer)
285
+ end
286
+
287
+ def destroyable?
288
+ !!_destroy_performer
289
+ end
290
+
291
+ def destroy_object &block
292
+ performer = block || _destroy_performer
293
+ result = !!performer_exec(&performer)
294
+ mark_destroyed! if result
295
+ result
296
+ end
297
+
298
+ def performer_exec &block
299
+ if block.arity == 1
300
+ block.call(self)
301
+ else
302
+ instance_exec(&block)
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end