save_queue 0.3.0 → 0.4.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.
data/.travis.yml CHANGED
@@ -1,10 +1,11 @@
1
+ #script: "rspec spec --tag ~ci:false"
1
2
  rvm:
2
3
  - 1.8.7
3
4
  - 1.9.2
4
5
  - 1.9.3
5
- #- jruby-19mode # Not working on travis
6
+ - jruby-19mode
6
7
  - jruby-18mode
7
- - rbx-19mode
8
+ #- rbx-19mode # Not working on travis
8
9
  - rbx-18mode
9
10
  - ruby-head
10
11
  - ree
data/HISTORY.md CHANGED
@@ -1,14 +1,21 @@
1
+ 0.4.x
2
+ =====
3
+ - Remove changes trakcing functional from SaveQueue::Object and move it to Dirty plugin.
4
+ + Dirty plugin.
5
+ * Notification plugin dont mark object as saved by default.
6
+ * Refactor to 'tell dont ask' pattern.
7
+
1
8
  0.3.x
2
9
  =====
3
- + Notification plugin
4
- + add save! method
5
- + refactor Validation plugin
6
- + bugfixing
10
+ + Notification plugin.
11
+ + Add save! method.
12
+ + Refactor Validation plugin.
13
+ + Bugfixing.
7
14
 
8
15
  0.2.x
9
16
  =====
10
- Implemented main functionality, add Validation plugin
17
+ Implemented main functionality, add Validation plugin.
11
18
 
12
19
  0.0.1
13
20
  =====
14
- Initial release. Add README, LICENSE, .gitignore, etc
21
+ Initial release. Add README, LICENSE, .gitignore, etc.
data/README.md CHANGED
@@ -1,8 +1,18 @@
1
1
  Save Queue
2
2
  ==========
3
- Save Queue allows to push objects to other object's queue for a delayed save.
4
- Queue save will be triggered by object#save.
3
+ [![Build Status](https://secure.travis-ci.org/AlexParamonov/save_queue.png)](http://travis-ci.org/AlexParamonov/save_queue)
4
+ [![Gemnasium Build Status](https://gemnasium.com/AlexParamonov/save_queue.png)](http://gemnasium.com/AlexParamonov/save_queue)
5
5
 
6
+ Save Queue creates queue of objects inside holder object.
7
+ Queue will be saved and cleared after successfull holder object save.
8
+
9
+
10
+ **Holder object** is object that include SaveQueue.
11
+ **Object in queue** is any instance of any class, that include SaveQueue or respond to #save method.
12
+
13
+
14
+ There are plugins, that alter queue or holder object behavior, find them in 'save_queue/plugins' directory and read about
15
+ them in Plugins section of [readme](https://github.com/AlexParamonov/save_queue/blob/develop/README.md)
6
16
 
7
17
  Contents
8
18
  ---------
@@ -10,9 +20,9 @@ Contents
10
20
  1. Contributing
11
21
  1. Usage
12
22
  * Getting started
13
- * Tracking changes
14
23
  * Error handling
15
24
  1. Plugins
25
+ * Dirty
16
26
  * Validation
17
27
  * Notification
18
28
  1. Creating your own Queues / TODO
@@ -27,50 +37,42 @@ Installation
27
37
 
28
38
  Contributing
29
39
  -------------
30
- __Please help to improve this project!__
40
+ **Please help to improve this project!**
31
41
 
32
- See [contributing guide](http://github.com/AlexParamonov/save_queue/blob/master/CONTRIBUTING.md) for best practices
42
+
43
+ See [contributing guide](http://github.com/AlexParamonov/save_queue/blob/master/CONTRIBUTING.md) for best practices
44
+ I am using [gitflow](https://github.com/nvie/gitflow). Develop branch is most featured and usually far above master.
33
45
 
34
46
  Usage
35
47
  -----
36
48
 
37
49
  ### Getting started
38
50
 
39
- 1. include SaveQueue:
40
-
41
- require 'save_queue'
42
-
43
- class Artice
44
- include SaveQueue
45
- end
46
-
47
- 2. call \#mark_as_changed method when object gets dirty:
51
+ 1. Include SaveQueue:
48
52
 
49
53
  require 'save_queue'
50
54
 
51
55
  class Artice
52
56
  include SaveQueue
53
57
 
54
- def change_attribute attr, value
55
- @attributes ||= {}
56
- @attributes[attr] = value
57
- mark_as_changed # call this and object will be marked for save
58
+ def save
59
+ puts "article saved!"
58
60
  end
59
61
  end
60
62
 
61
- 3. add SaveQueue to some other classes (or implement #save and #has_unsaved_changes? in it):
63
+ 1. Add SaveQueue to some other classes (or implement #save method in it):
62
64
 
63
65
  require 'save_queue'
64
66
 
65
67
  class Tag
66
68
  include SaveQueue
67
69
 
68
- @attributes ||= {}
69
- @attributes[attr] = value
70
- mark_as_changed # call this and object will be marked for save
70
+ def save
71
+ puts "tag saved!"
72
+ end
71
73
  end
72
74
 
73
- 4. Add some functionality:
75
+ 1. Add some functionality:
74
76
 
75
77
  class Artice
76
78
  def tags=(tag_objects)
@@ -81,83 +83,60 @@ Usage
81
83
  def add_tag(tag)
82
84
  @tags ||= []
83
85
  @tags << tag
84
- save_queue.add tag # you may use also #<<, #push methods
86
+ save_queue.add tag # methods #<<, #push can be also used to add object to queue
87
+ # You may also do
88
+ # tag.save_queue << self
89
+ # save_queue will not circle in this case
85
90
  end
86
91
  end
87
92
 
88
- 6. Voila!
93
+ 1. Voila!
89
94
 
90
95
  article = Article.new
91
96
 
92
- tag_objects = []
93
- 3.times do
94
- tag = Tag.new
95
- tag.change_attribute :title, "new tag"
96
- tag.should_receive(:save).once
97
- tag_objects << tag
98
- end
99
-
100
- article.tags = tag_objects
97
+ # Create 3 tags and add them to the article
98
+ article.tags =
99
+ 3.times.map do
100
+ tag = Tag.new
101
+ tag.should_receive(:save).once
102
+
103
+ tag
104
+ end
101
105
 
106
+ # Add single tag
102
107
  tag = Tag.new
103
- tag.change_attribute :title, "single tag"
104
108
  tag.should_receive(:save).once
105
109
 
106
110
  article.add_tag tag
107
111
 
108
- # that will save article and all tags in this article if article
109
- # and tags are valid, and if article.save and all tag.save returns true
110
- # You may also use #save! method, that will trigger save_queue.save! and
112
+ # that will save article and all tags in this article if article.save
113
+ # and all tag.save returns true.
114
+ # You may also use #save! method, that will delegate to article.save! and
111
115
  # raise SaveQueue::FailedSaveError on fail
112
116
  article.save.should be_true
113
117
 
118
+ # Output:
119
+ # article saved!
120
+ # tag saved!
121
+ # tag saved!
122
+ # tag saved!
123
+ # tag saved!
124
+
125
+ # empty the queue after successfull save
126
+ article.save_queue.should be_empty
127
+
128
+ article.save
129
+ # Output:
130
+ # article saved!
131
+
114
132
  # You may call save on queue explicitly:
115
133
  #
116
134
  # article.save_queue.save
117
135
  # article.save
118
136
 
119
- 7. Read README for more details :)
120
-
121
-
122
- ### Tracking changes
123
- By default SaveQueue provide changes tracking functional.
124
- In order to use it, call #mark_as_changed method in your mutator methods like this:
125
-
126
- require "save_queue"
127
- class Artice
128
- include SaveQueue
129
-
130
- def change_attribute attr, value
131
- @attributes[attr] = value
132
- mark_as_changed # call this and object will be marked for save
133
- end
134
- end
135
-
136
- If you want to mark object as saved, you may use #mark_as_saved method. SaveQueue will automatically call #mark_as_saved
137
- after saving an object.
138
- This marks are used when SaveQueue calls #save. Object will be saved only, if it #has_unsaved_changes? returns true.
139
- There are some docs from spec tests:
140
-
141
- #has_unsaved_changes?
142
- should return true for changed object
143
- should return false for unchanged object
144
- should return false for new object
145
-
146
- If you have custom logic for marking objects dirty then you may want to override
147
- \#has_unsaved_changes? method, methods #mark_as_saved and #mark_as_changed in you class like this:
148
-
149
- def has_unsaved_changes?
150
- dirty? # dirty is your custom method to determine has object unsaved_changes or not
151
- end
152
-
153
- def mark_as_saved
154
- # your custom methods
155
- end
156
-
157
- def mark_as_changed
158
- # your custom methods
159
- end
137
+ 1. To save object _only_ if it was changed, include Dirty module described below.
160
138
 
139
+ 1. Continue reading for more details.
161
140
 
162
141
  ### Error handling
163
142
 
@@ -167,7 +146,6 @@ SaveQueue assumes, that #save method returns true/false and #save! raise an Exce
167
146
  # You may use article.save_queue.errors.any? or article.save_queue.errors.empty? also
168
147
 
169
148
  # returns a [Hash] that contains information about saving proccess:
170
- # @option [Array<Object>] :processed
171
149
  # @option [Array<Object>] :saved
172
150
  # @option [Object] :failed
173
151
  # @option [Array<Object>] :pending
@@ -180,7 +158,6 @@ SaveQueue assumes, that #save method returns true/false and #save! raise an Exce
180
158
  rescue SaveQueue::FailedSaveError => error
181
159
 
182
160
  # returns a [Hash] that contains information about saving proccess:
183
- # @option [Array<Object>] :processed
184
161
  # @option [Array<Object>] :saved
185
162
  # @option [Object] :failed
186
163
  # @option [Array<Object>] :pending
@@ -189,16 +166,64 @@ SaveQueue assumes, that #save method returns true/false and #save! raise an Exce
189
166
  article.save_queue.errors[:save] # also set
190
167
  end
191
168
 
192
-
169
+ Queue is not cleared after fail. Possible to fix errors and rerun queue.save
193
170
 
194
171
  Plugins
195
172
  -------
196
- I am trying to extract any "extra" functionality into separate plugins, that you may want to include.
173
+ Any extract any "extra" functionality goes into bundled plugins.
174
+
175
+ ### Dirty
176
+
177
+ SaveQueue::Plugins::Dirty module provide changes tracking functional.
178
+ In order to use it include this module and call #mark_as_changed method in mutator methods like this:
179
+
180
+ require "save_queue"
181
+ require "save_queue/plugins/dirty"
182
+
183
+ class Artice
184
+ include SaveQueue
185
+ include SaveQueue::Plugins::Dirty
186
+
187
+ def initialize
188
+ @attributes = {}
189
+ end
190
+
191
+ def change_attribute attr, value
192
+ @attributes[attr] = value
193
+ mark_as_changed # call this and object will be marked for a save
194
+ end
195
+ end
196
+
197
+ To mark object as saved, call #mark_as_saved method. SaveQueue Dirty plugin will automatically call
198
+ \#mark_as_saved method after saving an object.
199
+ This marks are used when SaveQueue calls #save. Object will be saved only, if it #has_unsaved_changes? method returns true.
200
+ There are some docs from spec tests:
201
+
202
+ #has_unsaved_changes?
203
+ should return true for changed object
204
+ should return false for unchanged object
205
+ should return false for new object
206
+
207
+ If you have custom logic for marking objects dirty then you may want to overwrite
208
+ \#has_unsaved_changes? method, methods #mark_as_saved and #mark_as_changed in you class like this:
209
+
210
+ def has_unsaved_changes?
211
+ dirty? # dirty is your custom method to determine has object unsaved_changes or not
212
+ end
213
+
214
+ def mark_as_saved
215
+ # your custom methods
216
+ end
217
+
218
+ def mark_as_changed
219
+ # your custom methods
220
+ end
221
+
197
222
 
198
223
  ### Validation
199
224
 
200
- If you want to use validation, include SaveQueue::Plugins::Validation and implement #valid? method.
201
- You may got failed objects from save_queue.errors\[:validation] array.
225
+ To use validation include SaveQueue::Plugins::Validation and implement #valid? method.
226
+ Failed objects are stored in save_queue.errors\[:validation] array.
202
227
  \save_queue.errors are empty if no errors occurs
203
228
 
204
229
  require 'save_queue'
@@ -218,23 +243,58 @@ You may got failed objects from save_queue.errors\[:validation] array.
218
243
 
219
244
  There are specs for them:
220
245
 
221
- ValidQueue
222
- invalid objects
223
- save
224
- should not be saved
225
- should set errors
226
- save!
227
- should raise SaveQueue::FailedValidationError exception
228
- should set errors
229
- valid objects
230
- save
231
- should be saved
232
- should not set errors
233
- save!
234
- should not raise an exception
235
- should not set errors
236
-
237
- Also you got more error hangling options:
246
+ SaveQueue::Plugins::Validation::Queue
247
+ is empty
248
+ should be valid
249
+ #save
250
+ should be true
251
+ #save!
252
+ should not raise Exception
253
+ contains valid objects
254
+ #save
255
+ should be true
256
+ should save all elements
257
+ #save!
258
+ should not raise Exception
259
+ should save all elements
260
+ #valid?
261
+ should be true
262
+ should not has any errors
263
+ #validate!
264
+ should not raise any exception
265
+ should not has any errors
266
+ contains invalid objects
267
+ behaves like queue with invalid objects
268
+ #save
269
+ should be false
270
+ should not save elements
271
+ #save!
272
+ should raise SaveQueue::FailedValidationError
273
+ should not save elements
274
+ #valid?
275
+ should be false
276
+ should set errors
277
+ #validate!
278
+ should raise SaveQueue::FailedValidationError exception
279
+ should set errors
280
+ contains mix of valid and invalid objects
281
+ #save should call #valid?
282
+ #save! should call #validate!
283
+ behaves like queue with invalid objects
284
+ #save
285
+ should be false
286
+ should not save elements
287
+ #save!
288
+ should raise SaveQueue::FailedValidationError
289
+ should not save elements
290
+ #valid?
291
+ should be false
292
+ should set errors
293
+ #validate!
294
+ should raise SaveQueue::FailedValidationError exception
295
+ should set errors
296
+
297
+ Plugin adds SaveQueue::FailedValidationError:
238
298
 
239
299
  # Note: queue was not saved. You dont need to do a cleanup
240
300
  unless article.save then
@@ -250,7 +310,7 @@ Also you got more error hangling options:
250
310
  article.save_queue.errors[:validation] # also set
251
311
  end
252
312
 
253
- You may catch both save and validation errors by
313
+ To catch both save and validation errors use SaveQueue::Error:
254
314
 
255
315
  begin
256
316
  article.save!
@@ -258,21 +318,24 @@ You may catch both save and validation errors by
258
318
  # do something
259
319
  end
260
320
 
261
- if you want not to save an object if save_queue is invalid then add this check to your save method (or any other method that you use, ex: valid?):
321
+ To not save an object if save_queue is invalid, add this condition to #save method:
262
322
 
263
323
  def save
264
324
  return false unless save_queue.valid?
265
325
  #..
266
326
  end
267
327
 
268
- or you may add it to your validation.
269
- Note, that #valid? and #validate return true/false and #validate! raises SaveQueue::FailedValidationError exception
328
+ or add it to validation (to object#valid? method for example).
329
+ Note, that queue#valid? and queue#validate return true/false and queue#validate! raises SaveQueue::FailedValidationError exception.
330
+ Queue is not creared if validation fails.
270
331
 
271
332
  ### Notification
272
333
 
273
- If you want to use notification, include SaveQueue::Plugins::Notification.
274
- You'll get object notified by #queue_changed_event method, which by default call #mark_as_changed method if queue was successfuly changed.
275
- You may override this method in your object if you want to.
334
+ To use notification include SaveQueue::Plugins::Notification.
335
+ Holder object will be notified by #queue_changed_event method.
336
+ Overwrite this method to implement your functionality, for ex logging.
337
+
338
+ require 'save_queue/plugins/notification'
276
339
 
277
340
  class Artice
278
341
  include SaveQueue
@@ -280,21 +343,17 @@ You may override this method in your object if you want to.
280
343
  end
281
344
 
282
345
  article = Article.new
283
- article.mark_as_saved
284
- article.save_queue << tag
285
- article.should have_unsaved changes
346
+ article.save_queue << tag # this will trigger callback #queue_changed_event on article
347
+
286
348
 
287
349
  class Artice
288
350
  def queue_changed_event(result, object)
289
- super
290
351
  puts "queue was changed!"
291
352
  end
292
353
  end
293
354
 
294
355
  article = Article.new
295
- article.mark_as_saved
296
- article.save_queue << tag # "queue was changed!"
297
- article.should have_unsaved changes
356
+ article.save_queue.add tag # Outputs "queue was changed!"
298
357
 
299
358
 
300
359
  Creating your own Queues
@@ -305,9 +364,8 @@ Creating your own Queues
305
364
  FAQ
306
365
  ---
307
366
 
308
- __Q: I use #write method to store object, how can i use SaveQueue?__
309
-
310
- A: You may implement save method like this:
367
+ __Q: I use #write method to store object, how can i use SaveQueue?__
368
+ A: You may implement #save method like this:
311
369
 
312
370
  class Artice
313
371
  # @return [boolean]
@@ -316,14 +374,12 @@ A: You may implement save method like this:
316
374
  end
317
375
  end
318
376
 
319
- Note that SaveQueue assumes, that #save method returns true/false and #save! raise an Exception if save failed
320
-
321
- __Q: Where i can get more information?__
322
-
323
- A: See test specs for more details.
377
+ Note that SaveQueue assumes, that #save method returns true/false and #save! raise an Exception if save failed.
324
378
 
325
- __How?__
326
379
 
380
+ __Q: Where i can get more information?__
381
+ A: See test specs for more details.
382
+ __How?__
327
383
  clone git project by
328
384
 
329
385
  git clone git://github.com/AlexParamonov/save_queue.git
@@ -333,10 +389,11 @@ cd into it and run bundle
333
389
  cd save_queue
334
390
  bundle
335
391
 
336
- and run rake
392
+ and then rake
337
393
 
338
394
  rake
339
395
 
396
+ docs will be printed to your console :)
340
397
 
341
398
  Requirements
342
399
  ------------
@@ -352,6 +409,7 @@ tested with Ruby
352
409
  * 1.9.2
353
410
  * 1.9.3
354
411
  * jruby-18mode
412
+ * jruby-19mode
355
413
  * rbx-19mode
356
414
  * rbx-18mode
357
415
  * ruby-head
@@ -2,11 +2,12 @@ module SaveQueue
2
2
  class Error < RuntimeError; end
3
3
  class FailedSaveError < Error
4
4
  attr_reader :context
5
+
5
6
  def initialize(context_hash)
6
7
  @context = context_hash
7
8
  end
8
9
 
9
- def to_s # Some default way to display errors
10
+ def to_s
10
11
  "#{super}: " + @context.to_s
11
12
  end
12
13
  end
@@ -0,0 +1,21 @@
1
+ require 'hooks'
2
+ require 'save_queue/object_queue'
3
+
4
+ module SaveQueue
5
+ module Object
6
+ module QueueClassManagement
7
+ def queue_class
8
+ @queue_class ||= ObjectQueue
9
+ end
10
+
11
+ def queue_class=(klass)
12
+ raise "Your Queue implementation: #{klass} should include Hooks module!" unless klass.include? Hooks
13
+ @queue_class = klass
14
+ end
15
+
16
+ def inherited base
17
+ base.queue_class = self.queue_class
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,49 +1,40 @@
1
1
  require 'save_queue/object_queue'
2
+ require 'save_queue/object/queue_class_management'
2
3
 
3
4
  module SaveQueue
4
5
  module Object
5
- def self.included base
6
- base.class_eval do
7
-
8
- class<<self
9
- attr_reader :queue_class
10
-
11
- def queue_class=(klass)
12
- raise "Your Queue implementation: #{klass} should include Hooks module!" unless klass.include? Hooks
13
- @queue_class = klass
14
- end
15
- end
6
+ attr_reader :processed
16
7
 
17
- def self.inherited base
18
- base.queue_class = self.queue_class
19
- end
20
-
21
- self.queue_class ||= ObjectQueue
22
- end
8
+ def self.included base
9
+ base.send :extend, QueueClassManagement
23
10
  end
24
11
 
25
-
26
12
  module RunAlwaysFirst
27
13
  # can not reilly on save! here, because client may not define it at all
28
14
  def save(*args)
29
- super_result = true
30
- super_result = super if defined?(super)
15
+ no_recursion do
16
+ super_result =
17
+ _sq_around_original_save do
18
+ super if defined?(super)
19
+ end
31
20
 
32
- return false unless !!super_result
21
+ return false if false == super_result
22
+ return false unless save_queue.save
33
23
 
34
- mark_as_saved
35
- if save_queue.save
36
- true == super_result ? true : super_result # super_result may be not boolean, String for ex
37
- else
38
- false
24
+ super_result || true
39
25
  end
40
26
  end
41
27
 
42
- # Suppose,that save! raise an Exception if failed to save an object
28
+ # TODO squash with save method
29
+ # Expect save! to raise an Exception if failed to save an object
43
30
  def save!
44
- super if defined?(super)
45
- mark_as_saved
46
- save_queue.save!
31
+ no_recursion do
32
+ _sq_around_original_save do
33
+ super if defined?(super)
34
+ end
35
+
36
+ save_queue.save!
37
+ end
47
38
  end
48
39
  end
49
40
 
@@ -55,29 +46,26 @@ module SaveQueue
55
46
  extend RunAlwaysFirst
56
47
  end
57
48
 
58
- def mark_as_changed
59
- instance_variable_set "@_changed_mark", true
60
- end
61
-
62
- # @returns [Boolean] true if object has been modified
63
- def has_unsaved_changes?
64
- status = instance_variable_get("@_changed_mark")
65
- status.nil? ? false : status
49
+ def save_queue
50
+ @_save_queue
66
51
  end
67
52
 
68
- def save_queue
69
- instance_variable_get "@_save_queue"
53
+ private
54
+ def create_queue
55
+ # queue_class located in QueueClassManagement
56
+ @_save_queue = self.class.queue_class.new
70
57
  end
71
58
 
72
- def mark_as_saved
73
- instance_variable_set "@_changed_mark", false
59
+ def _sq_around_original_save
60
+ yield
74
61
  end
75
62
 
76
- private
77
- def create_queue
78
- klass = self.class.queue_class
79
- queue = klass.new
80
- instance_variable_set "@_save_queue", queue
63
+ def no_recursion
64
+ return true if @_no_recursion_saving_flag
65
+ @_no_recursion_saving_flag = true
66
+ yield
67
+ ensure
68
+ @_no_recursion_saving_flag = false
81
69
  end
82
70
  end
83
71
  end