poro 0.1.2 → 0.1.3
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/README.rdoc +16 -16
- data/lib/poro/context.rb +144 -5
- data/lib/poro/contexts/hash_context.rb +21 -5
- data/lib/poro/contexts/mongo_context.rb +36 -7
- data/lib/poro/version.rb +1 -1
- data/spec/context_spec.rb +125 -0
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -160,14 +160,22 @@ mailto:jeff@paploo.net
|
|
160
160
|
|
161
161
|
= Version History
|
162
162
|
|
163
|
-
[0.1.
|
163
|
+
[0.1.3 - 2010-Oct-21] Callbacks and MongoContext Bug Fixes.
|
164
|
+
* Added callbacks for common events.
|
165
|
+
* MongoContext: You can actually remove records now.
|
166
|
+
* MongoContext: Bignum encodes instead of throwing errors.
|
167
|
+
* MongoContext: Made recognition of true/false/nil class
|
168
|
+
during encoding more robust.
|
169
|
+
[0.1.2 - 2010-Sep-30] Feature Additions.
|
164
170
|
* Added a module namespace factory.
|
165
|
-
* HashContext: Find one is faster when the conditions
|
171
|
+
* HashContext: Find one is faster when the conditions
|
172
|
+
restrict on the primary key.
|
166
173
|
* Many HashContext bugs fixed.
|
167
174
|
[0.1.1 - 2010-Sep-24] Minor Additions and Bug Fixes.
|
168
175
|
* MongoContext now can optionally encode Symbols as hashes
|
169
176
|
or just leave them as strings.
|
170
|
-
* MongoContext can have conversion to BSON::ObjectId
|
177
|
+
* MongoContext can have conversion to BSON::ObjectId
|
178
|
+
turned off.
|
171
179
|
* MongoContext can save Sets in various formats.
|
172
180
|
* MongoContext handles namespaced models better.
|
173
181
|
* Context doesn't error when trying to find by id.
|
@@ -176,7 +184,8 @@ mailto:jeff@paploo.net
|
|
176
184
|
to big changes as it is used in the real world.
|
177
185
|
* Only supports MongoDB and Hash Contexts.
|
178
186
|
* No performance testing and optimization yet done.
|
179
|
-
* The documentation is rough around the edges and may
|
187
|
+
* The documentation is rough around the edges and may
|
188
|
+
contain errors.
|
180
189
|
* Spec tests are incomplete.
|
181
190
|
|
182
191
|
= TODO List
|
@@ -184,26 +193,17 @@ mailto:jeff@paploo.net
|
|
184
193
|
The following are the primary TODO items, roughly in priority order:
|
185
194
|
|
186
195
|
* YAML Connection Configuration:
|
187
|
-
* Make a Util module that is able to use a rails-
|
196
|
+
* Make a Util module that is able to use a rails-style YAML
|
188
197
|
file--given by path--to get the elements needed for configuration of a
|
189
198
|
SingleStore factory.
|
190
199
|
* Modify SingleStore to use this file for configuration when appropriate.
|
191
200
|
* Modelify: Break into modules for each piece of functionality.
|
192
|
-
* Modelify: Add callback functionality to Modelify (e.g. before/after save, after initialize).
|
193
|
-
* Add the callbacks to the Context first.
|
194
|
-
* Mongo Context: Add option to encode Sets as one of:
|
195
|
-
* A Set with the raw internal Hash.
|
196
|
-
* A Set with the internal Hash as an Array.
|
197
|
-
* An Array (which will have to be manually turned back into a Set).
|
198
201
|
* Specs: Add specs for Context Find methods.
|
199
|
-
* Check that private methods are private. (Should do on subclasses too.)
|
200
|
-
* Check that the two main find methods pass through to the correct underlying
|
201
|
-
methods or throw an argument when necessary.
|
202
202
|
* Specs: Add spec tests for Mongo Context.
|
203
203
|
* Mongo Context: Split into modules in separate files.
|
204
204
|
* Context: Split out modules into files.
|
205
205
|
* Contexts: Add SQL Context.
|
206
|
-
* Ruby: Verify support for ruby 1.9.
|
206
|
+
* Ruby: Verify support for ruby 1.9.0 and 1.9.1.
|
207
207
|
* Ruby: Evaluate adding support for ruby 1.8.6 and 1.8.7.
|
208
208
|
|
209
209
|
= License
|
@@ -240,5 +240,5 @@ GPL compatible "New BSD License", given below:
|
|
240
240
|
|
241
241
|
Poro::Util::Inflector and its submodules are adapted from ActiveSupport,
|
242
242
|
and its source is redistributed under the MIT license it was originally
|
243
|
-
distributed under.
|
243
|
+
distributed under. The text of this copyright notice is supplied
|
244
244
|
in <tt>poro/util/inflector.rb</tt>.
|
data/lib/poro/context.rb
CHANGED
@@ -111,14 +111,16 @@ module Poro
|
|
111
111
|
# Fetches the object from the store with the given id, or returns nil
|
112
112
|
# if there are none matching.
|
113
113
|
def fetch(id)
|
114
|
-
|
114
|
+
obj = convert_to_plain_object( clean_id(nil) )
|
115
|
+
callback_event(:after_fetch, obj)
|
116
|
+
return obj
|
115
117
|
end
|
116
118
|
|
117
119
|
# Saves the given object to the persistent store using this context.
|
118
120
|
#
|
119
121
|
# Subclasses do not need to call super, but should follow the given rules:
|
120
122
|
#
|
121
|
-
# Returns
|
123
|
+
# Returns the saved object.
|
122
124
|
#
|
123
125
|
# If the object has never been saved, it should be inserted and given
|
124
126
|
# an id. If the object has been added before, the id is used to update
|
@@ -126,7 +128,9 @@ module Poro
|
|
126
128
|
#
|
127
129
|
# Raises an Error if save fails.
|
128
130
|
def save(obj)
|
131
|
+
callback_event(:before_save, obj)
|
129
132
|
obj.id = obj.object_id if obj.respond_to?(:id) && obj.id.nil? && obj.respond_to?(:id=)
|
133
|
+
callback_event(:after_save, obj)
|
130
134
|
return obj
|
131
135
|
end
|
132
136
|
|
@@ -134,13 +138,15 @@ module Poro
|
|
134
138
|
#
|
135
139
|
# Subclasses do not need to call super, but should follow the given rules:
|
136
140
|
#
|
137
|
-
# Returns
|
141
|
+
# Returns the removed object.
|
138
142
|
#
|
139
143
|
# If the object is successfully removed, the id is set to nil.
|
140
144
|
#
|
141
145
|
# Raises an Error is the remove fails.
|
142
146
|
def remove(obj)
|
147
|
+
callback_event(:before_remove, obj)
|
143
148
|
obj.id = nil if obj.respond_to?(:id=)
|
149
|
+
callback_event(:after_remove, obj)
|
144
150
|
return obj
|
145
151
|
end
|
146
152
|
|
@@ -157,7 +163,10 @@ module Poro
|
|
157
163
|
# Any root object returned from a "find" in the data store needs to be
|
158
164
|
# able to be converted
|
159
165
|
def convert_to_plain_object(data, state_info={})
|
160
|
-
|
166
|
+
transformed_data = callback_transform(:before_convert_to_plain_object, data)
|
167
|
+
obj = transformed_data
|
168
|
+
callback_event(:after_convert_to_plain_object, obj)
|
169
|
+
return obj
|
161
170
|
end
|
162
171
|
|
163
172
|
# Convert a plain ol' ruby object into the data store data format this
|
@@ -173,7 +182,10 @@ module Poro
|
|
173
182
|
# Any root object returned from a "find" in the data store needs to be
|
174
183
|
# able to be converted
|
175
184
|
def convert_to_data(obj, state_info={})
|
176
|
-
|
185
|
+
transformed_obj = callback_transform(:before_convert_to_data, obj)
|
186
|
+
data = transformed_obj
|
187
|
+
callback_event(:after_convert_to_data, data)
|
188
|
+
return data
|
177
189
|
end
|
178
190
|
|
179
191
|
private
|
@@ -457,8 +469,135 @@ module Poro
|
|
457
469
|
end
|
458
470
|
end
|
459
471
|
|
472
|
+
module Poro
|
473
|
+
class Context
|
474
|
+
# A mixin to support callbacks. There are three kinds of callbacks:
|
475
|
+
# [Events] Events are callbacks that are passed a handle to the object when
|
476
|
+
# a particular kind of event has occured. These may destructively
|
477
|
+
# edit objects.
|
478
|
+
# [Transform] Transforms are callbacks where each handler is passed the
|
479
|
+
# result of the previous transform, and may return any value.
|
480
|
+
# The issuing object then uses the final value in some way.
|
481
|
+
# [Filters] Calls each callback in sequence, pasing in the issuing object.
|
482
|
+
# Terminates execution on the first callback that is "false" (as
|
483
|
+
# determined by an if statement), or when there are no callbacks
|
484
|
+
# left. Gives the issuing object the result of the last block.
|
485
|
+
#
|
486
|
+
# Contexts issue the following event callbacks:
|
487
|
+
# [:before_save] Called before save; passes the object that is going to be saved.
|
488
|
+
# [:after_save] Called after save; passes the object that was saved.
|
489
|
+
# [:before_remove] Called before removing an object from persistent storage; passes the object that will be removed.
|
490
|
+
# [:after_remove] Called after removing an object from persistent storage; passes the object that was removed.
|
491
|
+
# [:after_fech] Called after an object is fetched from the persistent store; passes the object that was fetched.
|
492
|
+
# [:after_convert_to_plain_object] Called after an object is converted to a plain object from the persistent store but before it is used; passes the plain object.
|
493
|
+
# [:after_convert_to_data] Called after an object is converted to the persistent store's data structure but before it is used; passes the data store's data structure.
|
494
|
+
#
|
495
|
+
# Contexts issue the following transform callbacks:
|
496
|
+
#
|
497
|
+
# [:before_convert_to_plain_object] Called just before a context converts
|
498
|
+
# persistent store data to a plain ruby object;
|
499
|
+
# is passed the persistent store data object;
|
500
|
+
# the result is what is converted.
|
501
|
+
#
|
502
|
+
# In most cases it is better to use the
|
503
|
+
# +after_convert_to_plain_object+ callback event.
|
504
|
+
# [:before_convert_to_data] Called just before a context converts
|
505
|
+
# a plain ruby object to persistent store data;
|
506
|
+
# is passed the plain ruby object;
|
507
|
+
# the result is what is converted.
|
508
|
+
#
|
509
|
+
# In most cases it is better to use the
|
510
|
+
# +before_convert_to_plain_object+ callback event.
|
511
|
+
module CallbackMethods
|
512
|
+
|
513
|
+
# Return the raw array of callbacks. This can be manipulated if more
|
514
|
+
# straightforward methods don't do the trick, but usually this is
|
515
|
+
# a consequence of trying to solve the problem wrong.
|
516
|
+
#
|
517
|
+
# While usually a kind of Proc, callbacks may be any object that responds
|
518
|
+
# to call.
|
519
|
+
def callbacks(event)
|
520
|
+
@event_callbacks ||= {}
|
521
|
+
key = event.to_sym
|
522
|
+
@event_callbacks[key] ||= []
|
523
|
+
return @event_callbacks[key]
|
524
|
+
end
|
525
|
+
|
526
|
+
# Register a callback for a given event.
|
527
|
+
def register_callback(event, &block)
|
528
|
+
callbacks(event) << block
|
529
|
+
end
|
530
|
+
|
531
|
+
# Clear all callbacks for a given event.
|
532
|
+
#
|
533
|
+
# This can be dangerous because
|
534
|
+
def clear_callbacks(event)
|
535
|
+
callbacks(event).clear
|
536
|
+
end
|
537
|
+
|
538
|
+
private
|
539
|
+
|
540
|
+
# Fires the callbacks for the given event; returns the object supplied
|
541
|
+
# for calling.
|
542
|
+
#
|
543
|
+
# * Each registered callback is given the object issued with the call.
|
544
|
+
# * Depending on your uses, the callback may be destructive of the passed object.
|
545
|
+
# * The callback returns are ignored.
|
546
|
+
#
|
547
|
+
# Registration of no callbacks results in no callbacks being called.
|
548
|
+
def callback_event(event, obj)
|
549
|
+
callbacks(event).each {|callback| callback.call(obj)}
|
550
|
+
return obj
|
551
|
+
end
|
552
|
+
|
553
|
+
# Transforms an object through a callback chain; returns the transformed
|
554
|
+
# object.
|
555
|
+
#
|
556
|
+
# * Each registered callback is given the result of the previous callback.
|
557
|
+
# * Callbacks may return the original object (modified or unmodified), a
|
558
|
+
# copy of the original object (modified or unmodified), or an entirely
|
559
|
+
# new object, depending on how the result is used.
|
560
|
+
# * The callback return is passed into the next callback, with the last
|
561
|
+
# return being called to the initial caller.
|
562
|
+
#
|
563
|
+
# Registration of no callbacks results in the return of the original object.
|
564
|
+
def callback_transform(event, initial_obj)
|
565
|
+
return callbacks(event).inject(initial_obj) {|obj, callback| callback.call(obj)}
|
566
|
+
end
|
567
|
+
|
568
|
+
# Executes callbacks until the last true-valued filter; returns the last
|
569
|
+
# true valued object.
|
570
|
+
#
|
571
|
+
# By convention, filter events should end in a question mark to make it
|
572
|
+
# clear that the true/false value is important.
|
573
|
+
#
|
574
|
+
# * Each registered callback is given the original object, making this
|
575
|
+
# behave more like an event than a transform.
|
576
|
+
# * Filters are expected to be non-destructive, as they are used to
|
577
|
+
# determine if an action should take place, rather than to take an
|
578
|
+
# action.
|
579
|
+
# * If the return of a callback is false-values (as determined by an +if+
|
580
|
+
# expression), then the filter chain is halted and the value is returned;
|
581
|
+
# otherwise, the value returned from the last callback is returned.
|
582
|
+
#
|
583
|
+
# Registration of no callbacks results in the return of the +default_value+
|
584
|
+
# argument, which--if not provided--is set to true.
|
585
|
+
def callback_filter?(event, obj, default_result=true)
|
586
|
+
result = default_result
|
587
|
+
callbacks(event).each do |callback|
|
588
|
+
result = callback.call(obj)
|
589
|
+
break unless result
|
590
|
+
end
|
591
|
+
return result
|
592
|
+
end
|
593
|
+
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
460
598
|
module Poro
|
461
599
|
class Context
|
462
600
|
include FindMethods
|
601
|
+
include CallbackMethods
|
463
602
|
end
|
464
603
|
end
|
@@ -12,11 +12,15 @@ module Poro
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def fetch(id)
|
15
|
-
|
15
|
+
obj = convert_to_plain_object( data_store[clean_id(id)] )
|
16
|
+
callback_event(:after_fetch, obj)
|
17
|
+
return obj
|
16
18
|
end
|
17
19
|
|
18
20
|
# Save the object in the underlying hash, using the object id as the key.
|
19
21
|
def save(obj)
|
22
|
+
callback_event(:before_save, obj)
|
23
|
+
|
20
24
|
pk_id = self.primary_key_value(obj)
|
21
25
|
if(pk_id.nil?)
|
22
26
|
pk_id = obj.object_id
|
@@ -24,25 +28,37 @@ module Poro
|
|
24
28
|
end
|
25
29
|
|
26
30
|
data_store[pk_id] = convert_to_data(obj)
|
27
|
-
|
31
|
+
|
32
|
+
callback_event(:after_save, obj)
|
33
|
+
return obj
|
28
34
|
end
|
29
35
|
|
30
36
|
# Remove the object from the underlying hash.
|
31
37
|
def remove(obj)
|
38
|
+
callback_event(:before_remove, obj)
|
39
|
+
|
32
40
|
pk_id = self.primary_key_value(obj)
|
33
41
|
if( pk_id != nil )
|
34
42
|
data_store.delete(pk_id)
|
35
43
|
self.set_primary_key_value(obj, nil)
|
36
44
|
end
|
37
|
-
|
45
|
+
|
46
|
+
callback_event(:after_remove, obj)
|
47
|
+
return obj
|
38
48
|
end
|
39
49
|
|
40
50
|
def convert_to_plain_object(data)
|
41
|
-
|
51
|
+
transformed_data = callback_transform(:before_convert_to_plain_object, data)
|
52
|
+
obj = transformed_data
|
53
|
+
callback_event(:after_convert_to_plain_object, obj)
|
54
|
+
return obj
|
42
55
|
end
|
43
56
|
|
44
57
|
def convert_to_data(obj)
|
45
|
-
|
58
|
+
transformed_obj = callback_transform(:before_convert_to_data, obj)
|
59
|
+
data = transformed_obj
|
60
|
+
callback_event(:after_convert_to_data, data)
|
61
|
+
return data
|
46
62
|
end
|
47
63
|
|
48
64
|
private
|
@@ -91,29 +91,44 @@ module Poro
|
|
91
91
|
|
92
92
|
def fetch(id)
|
93
93
|
data = data_store.find_one( clean_id(id) )
|
94
|
-
|
94
|
+
obj convert_to_plain_object(data)
|
95
|
+
callback_event(:before_fetch, obj)
|
96
|
+
return obj
|
95
97
|
end
|
96
98
|
|
97
99
|
def save(obj)
|
100
|
+
callback_event(:before_save, obj)
|
98
101
|
data = convert_to_data(obj)
|
99
102
|
data_store.save(data)
|
100
103
|
set_primary_key_value(obj, (data['_id'] || data[:_id])) # The pk generator uses a symbol, while everything else uses a string!
|
104
|
+
callback_event(:after_save, obj)
|
101
105
|
return obj
|
102
106
|
end
|
103
107
|
|
104
108
|
def remove(obj)
|
109
|
+
callback_event(:before_remove, obj)
|
110
|
+
data_store.remove( {'_id' => primary_key_value(obj)} )
|
111
|
+
callback_event(:after_remove, obj)
|
105
112
|
return obj
|
106
113
|
end
|
107
114
|
|
108
115
|
def convert_to_plain_object(data, state_info={})
|
116
|
+
transformed_data = callback_transform(:before_convert_to_plain_object, data)
|
117
|
+
|
109
118
|
# If it is a root record, and it has no class name, assume this context's class name.
|
110
|
-
|
111
|
-
obj = route_decode(
|
119
|
+
transformed_data['_class_name'] = self.klass if( transformed_data && transformed_data.kind_of?(Hash) && !state_info[:embedded] )
|
120
|
+
obj = route_decode(transformed_data, state_info)
|
121
|
+
|
122
|
+
callback_event(:after_convert_to_plain_object, obj)
|
112
123
|
return obj
|
113
124
|
end
|
114
125
|
|
115
126
|
def convert_to_data(obj, state_info={})
|
116
|
-
|
127
|
+
transformed_obj = callback_transform(:before_convert_to_data, obj)
|
128
|
+
|
129
|
+
data = route_encode(transformed_obj, state_info)
|
130
|
+
|
131
|
+
callback_event(:after_convert_to_data, data)
|
117
132
|
return data
|
118
133
|
end
|
119
134
|
|
@@ -150,9 +165,9 @@ module Poro
|
|
150
165
|
obj.kind_of?(String) ||
|
151
166
|
obj.kind_of?(Time) ||
|
152
167
|
(!self.encode_symbols && obj.kind_of?(Symbol)) ||
|
153
|
-
obj
|
154
|
-
obj
|
155
|
-
obj.
|
168
|
+
obj.kind_of?(TrueClass) ||
|
169
|
+
obj.kind_of?(FalseClass) ||
|
170
|
+
obj.kind_of?(NilClass) ||
|
156
171
|
obj.kind_of?(BSON::ObjectId) ||
|
157
172
|
obj.kind_of?(BSON::DBRef)
|
158
173
|
)
|
@@ -205,6 +220,8 @@ module Poro
|
|
205
220
|
return encode_array(obj)
|
206
221
|
elsif( obj.kind_of?(Class) )
|
207
222
|
return encode_class(obj)
|
223
|
+
elsif( obj.kind_of?(Bignum) )
|
224
|
+
return encode_bigint(obj)
|
208
225
|
elsif( obj.kind_of?(Set) )
|
209
226
|
return encode_set(obj)
|
210
227
|
elsif( self.encode_symbols && obj.kind_of?(Symbol) )
|
@@ -243,6 +260,11 @@ module Poro
|
|
243
260
|
return {'_class_name' => 'Symbol', 'value' => sym.to_s}
|
244
261
|
end
|
245
262
|
|
263
|
+
# Encodes a big-int, which is too big to be natively encoded in BSON.
|
264
|
+
def encode_bigint(bigint)
|
265
|
+
return {'_class_name' => 'Bignum', 'value' => bigint.to_s}
|
266
|
+
end
|
267
|
+
|
246
268
|
# Encodes a Set as either :raw, :embedded_array, :array.
|
247
269
|
def encode_set(set)
|
248
270
|
method = @set_encoding_method
|
@@ -344,6 +366,8 @@ module Poro
|
|
344
366
|
return decode_class(data)
|
345
367
|
elsif( class_name == 'Symbol' )
|
346
368
|
return decode_symbol(data)
|
369
|
+
elsif( class_name == 'Bignum' )
|
370
|
+
return decode_bigint(data)
|
347
371
|
elsif( class_name == 'Set' )
|
348
372
|
return decode_set(data)
|
349
373
|
elsif( class_name == self.klass.to_s )
|
@@ -384,6 +408,11 @@ module Poro
|
|
384
408
|
end
|
385
409
|
end
|
386
410
|
|
411
|
+
# Decode an encoded bigint.
|
412
|
+
def decode_bigint(bigint_data)
|
413
|
+
return bigint_data['value'].to_i
|
414
|
+
end
|
415
|
+
|
387
416
|
# Decode the set depending on if it was encoded as an array or as a raw
|
388
417
|
# object.
|
389
418
|
def decode_set(set_data)
|
data/lib/poro/version.rb
CHANGED
data/spec/context_spec.rb
CHANGED
@@ -107,4 +107,129 @@ describe "Context" do
|
|
107
107
|
Poro::Context.fetch(@klass_one.new).should == "#{@klass_one}, #{x}"
|
108
108
|
end
|
109
109
|
|
110
|
+
describe 'Callbakcs' do
|
111
|
+
|
112
|
+
before(:each) do
|
113
|
+
@context = @context_klass.new(@klass_one)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should allow direct inspection of callbacks' do
|
117
|
+
bs_callbacks = @context.callbacks(:before_save)
|
118
|
+
bs_callbacks.should be_kind_of(Array)
|
119
|
+
|
120
|
+
as_callbacks = @context.callbacks(:after_save)
|
121
|
+
as_callbacks.should be_kind_of(Array)
|
122
|
+
|
123
|
+
as_callbacks.object_id.should_not == bs_callbacks.object_id
|
124
|
+
@context.callbacks(:before_save).object_id.should == bs_callbacks.object_id
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should allow callback registration' do
|
128
|
+
@context.callbacks(:before_save).should be_empty
|
129
|
+
@context.register_callback(:before_save) {|obj| obj}
|
130
|
+
@context.callbacks(:before_save).length.should == 1
|
131
|
+
|
132
|
+
@context.register_callback(:after_save) {|obj| obj}
|
133
|
+
@context.callbacks(:after_save).length.should == 1
|
134
|
+
|
135
|
+
@context.callbacks(:before_save).length.should == 1
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should clear callbacks' do
|
139
|
+
@context.register_callback(:before_save) {|obj| obj}
|
140
|
+
@context.callbacks(:before_save).length.should == 1
|
141
|
+
@context.clear_callbacks(:before_save)
|
142
|
+
@context.callbacks(:before_save).should be_empty
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should have private firing methods' do
|
146
|
+
@context.private_methods.should include(:callback_event)
|
147
|
+
@context.private_methods.should include(:callback_transform)
|
148
|
+
@context.private_methods.should include(:callback_filter?)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should call event callbacks' do
|
152
|
+
@context.register_callback(:before_save) {|obj| obj[:foo] = 'bar'}
|
153
|
+
@context.register_callback(:before_save) {|obj| obj[:alpha] = 'beta'}
|
154
|
+
@context.register_callback(:after_save) {|obj| obj[:p] = 'q'}
|
155
|
+
|
156
|
+
some_object = {:foo => 'untouched', :value => 12345}
|
157
|
+
result = @context.send(:callback_event, :before_save, some_object)
|
158
|
+
result.object_id.should == some_object.object_id
|
159
|
+
some_object.should == {:foo => 'bar', :value => 12345, :alpha => 'beta'}
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should handle transform callbacks' do
|
163
|
+
@context.register_callback(:before_save) {|obj| obj.merge(:foo => 'bar')}
|
164
|
+
@context.register_callback(:before_save) {|obj| obj.merge(:alpha => 'beta').to_a}
|
165
|
+
@context.register_callback(:after_save) {|obj| 'q'}
|
166
|
+
|
167
|
+
some_object = {:foo => 'untouched', :value => 12345}
|
168
|
+
result = @context.send(:callback_transform, :before_save, some_object)
|
169
|
+
result.should == [[:foo, 'bar'], [:value, 12345], [:alpha, 'beta']]
|
170
|
+
some_object.should == {:foo => 'untouched', :value => 12345}
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should handle no transform callbacks' do
|
174
|
+
@context.callbacks(:before_save).should be_empty
|
175
|
+
|
176
|
+
some_object = {:foo => 'untouched', :value => 12345}
|
177
|
+
result = @context.send(:callback_transform, :before_save, some_object)
|
178
|
+
result.object_id.should == some_object.object_id
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should handle filter callbacks' do
|
182
|
+
@context.register_callback(:should_save?) {|obj| obj[:foo] = 'bar'; nil}
|
183
|
+
@context.register_callback(:should_save?) {|obj| obj[:alpha] = 'beta'; obj}
|
184
|
+
@context.register_callback(:should_remove?) {|obj| obj[:p] = 'q'; :done}
|
185
|
+
|
186
|
+
# Make sure it cancels properly.
|
187
|
+
some_object = {:foo => 'untouched', :value => 12345}
|
188
|
+
result = @context.send(:callback_filter?, :should_save?, some_object)
|
189
|
+
result.should be_nil
|
190
|
+
some_object.should == {:foo => 'bar', :value => 12345}
|
191
|
+
|
192
|
+
# Make sure it still runs properly, even if the default is false.
|
193
|
+
some_object = {:foo => 'untouched', :value => 12345}
|
194
|
+
result = @context.send(:callback_filter?, :should_save?, some_object, false)
|
195
|
+
result.should be_nil
|
196
|
+
some_object.should == {:foo => 'bar', :value => 12345}
|
197
|
+
|
198
|
+
# Make sure it falls off the end correctly.
|
199
|
+
some_object = {:foo => 'untouched', :value => 12345}
|
200
|
+
result = @context.send(:callback_filter?, :should_remove?, some_object)
|
201
|
+
result.should == :done
|
202
|
+
some_object.should == {:foo => 'untouched', :value => 12345, :p => 'q'}
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'should handle no filter callbacks' do
|
206
|
+
@context.callbacks(:save_should?).should be_empty
|
207
|
+
|
208
|
+
# Make sure it defaults to true when there are no callbacks.
|
209
|
+
some_object = {:foo => 'untouched', :value => 12345}
|
210
|
+
result = @context.send(:callback_filter?, :should_save?, some_object)
|
211
|
+
result.should == true
|
212
|
+
some_object.should == {:foo => 'untouched', :value => 12345}
|
213
|
+
|
214
|
+
# Make sure it uses the passed default when there are no callbacks.
|
215
|
+
some_object = {:foo => 'untouched', :value => 12345}
|
216
|
+
result = @context.send(:callback_filter?, :should_save?, some_object, :some_default)
|
217
|
+
result.should == :some_default
|
218
|
+
some_object.should == {:foo => 'untouched', :value => 12345}
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
describe 'FindHelpers' do
|
224
|
+
|
225
|
+
it 'should have base methods private' do
|
226
|
+
pending
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'should pass calls from the main two public methods to their underlying private methods based on argument' do
|
230
|
+
pending
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
110
235
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 3
|
9
|
+
version: 0.1.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Jeff Reinecke
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-10-21 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|