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