save_queue 0.3.0 → 0.4.0

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