squares 0.2.9 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ccabaf667507b7c385bb90a3d678c52efe24a46
4
- data.tar.gz: 26050ec6702c082cbf7f6dc2fe3e14fccefb669c
3
+ metadata.gz: 5881a376cfce79ade243a19dfa8f3a9b112d46e6
4
+ data.tar.gz: 8530ebccc40ed950e0b1298d524d6e57da969149
5
5
  SHA512:
6
- metadata.gz: b19a525035738a4c114fb53c2ea848ec30e3fcce50dea155e120768f32e5003b2421e8f697e20c6171e1d3cc58e331429171f7a1a4e89b64c9d249fc116bd87c
7
- data.tar.gz: ba24c3380162919427a2b40a302741047851fff7cea759cdb57776e221897179d2fbc0185b75bc8a2d61183b8fd64b01d08f862d77e1d98a8f0ad1fbc2cbaece
6
+ metadata.gz: a16c5788667935c396834e0bac628ffee25259472e2a62025d19b38e6838bdfc0e7c047892ecb3e2a90a94f339d66b99dc18c4aa45dce6297b0e4226f1d43600
7
+ data.tar.gz: a02e43ca8c0141b600c4a2c3840b5ce3d0960b0797226385d4b30f2e7c76d17865436021151bc8aefc53ed4b1a727523be02be857c3d1596ae6a327b4efa7103
data/lib/squares/base.rb CHANGED
@@ -4,17 +4,50 @@ module Squares
4
4
 
5
5
  def initialize *args
6
6
  apply *args
7
+ trigger :after_initialize
7
8
  end
8
9
 
9
10
  def save
10
- store[@id] = Marshal.dump self
11
+ trigger :before_save
12
+ @_changed = false
13
+ store[@id] = Marshal.dump self.dup
14
+ trigger :after_save
11
15
  nil
12
16
  end
13
17
 
18
+ def delete
19
+ self.class.delete self.id
20
+ end
21
+
22
+ def destroy
23
+ trigger :before_destroy
24
+ delete
25
+ end
26
+
14
27
  def == other
15
28
  @id == other.id && properties_equal(other)
16
29
  end
17
30
 
31
+ def [](key)
32
+ get_property key
33
+ end
34
+
35
+ def []=(key, value)
36
+ set_property key, value
37
+ end
38
+
39
+ def update_properties new_properties
40
+ new_properties.each do |key, value|
41
+ self[key] = value if valid_property?(key)
42
+ end
43
+ save
44
+ end
45
+ alias_method :update_attributes, :update_properties
46
+
47
+ def changed?
48
+ @_changed
49
+ end
50
+
18
51
  def properties
19
52
  self.class.properties
20
53
  end
@@ -27,8 +60,28 @@ module Squares
27
60
  h
28
61
  end
29
62
 
63
+ def valid_property? property
64
+ self.class.valid_property? property
65
+ end
66
+
67
+ def defaults
68
+ self.class.defaults
69
+ end
70
+
30
71
  private
31
72
 
73
+ def trigger hook_name
74
+ return if @hook_callback_in_progress
75
+ hooks = self.class.hooks
76
+ if hooks && hooks[hook_name]
77
+ hooks[hook_name].each do |hook|
78
+ @hook_callback_in_progress = true
79
+ self.instance_eval &hook
80
+ @hook_callback_in_progress = false
81
+ end
82
+ end
83
+ end
84
+
32
85
  def properties_equal other
33
86
  ! properties.detect do |property|
34
87
  self.send(property) != other.send(property)
@@ -37,15 +90,47 @@ module Squares
37
90
 
38
91
  def apply *args
39
92
  @id, values = *args
40
- value_hash = values.to_h
41
- self.class.properties.each do |property|
42
- value = value_hash.has_key?(property) ? value_hash[property] : default_for(property)
43
- self.send "#{property.to_s.gsub(/\?$/,'')}=".to_sym, value
93
+ values_hash = values.to_h
94
+ properties_sorted_by_defaults.each do |property|
95
+ value = values_hash.has_key?(property) ? values_hash[property] : default_for(property)
96
+ set_property property, value
97
+ end
98
+ end
99
+
100
+ def properties_sorted_by_defaults
101
+ (properties || []).sort do |a,b|
102
+ a_val = defaults[a].respond_to?(:call) ? 1 : 0
103
+ b_val = defaults[b].respond_to?(:call) ? 1 : 0
104
+ a_val <=> b_val
44
105
  end
45
106
  end
46
107
 
47
108
  def default_for property
48
- self.class.defaults[property]
109
+ defaults[property].respond_to?(:call) ?
110
+ defaults[property].call(self) :
111
+ defaults[property]
112
+ end
113
+
114
+ def get_property property
115
+ instance_variable_get "@#{instance_var_string_for property}"
116
+ end
117
+
118
+ def set_property property, value
119
+ unless valid_property?(property)
120
+ raise ArgumentError.new("\"#{property}\" is not a valid property of #{self.class}")
121
+ end
122
+ instance_variable_set("@#{instance_var_string_for property}", value).tap do |value|
123
+ @_changed = true
124
+ end
125
+ end
126
+
127
+ def instance_var_string_for property
128
+ property = self.class.normalize_property property
129
+ if property.to_s.match(/\?$/)
130
+ "#{property.to_s.gsub(/\?$/,'')}__question__".to_sym
131
+ else
132
+ property
133
+ end
49
134
  end
50
135
 
51
136
  def store
@@ -58,7 +143,9 @@ module Squares
58
143
 
59
144
  def [] id
60
145
  if item = store[id]
61
- Marshal.restore item
146
+ Marshal.restore(item).tap do |item|
147
+ item.instance_eval 'trigger :after_find'
148
+ end
62
149
  end
63
150
  end
64
151
  alias_method :find, :[]
@@ -75,7 +162,11 @@ module Squares
75
162
  something which responds to #to_h"
76
163
  ERR
77
164
  end
78
- instance.tap { |i| i.save }
165
+ instance.tap do |i|
166
+ i.instance_eval 'trigger :before_create'
167
+ i.save
168
+ i.instance_eval 'trigger :after_create'
169
+ end
79
170
  end
80
171
  alias_method :create, :[]=
81
172
 
@@ -94,6 +185,17 @@ module Squares
94
185
  store.values.map{ |i| Marshal.restore i }
95
186
  end
96
187
 
188
+ def valid_property? property
189
+ !!normalize_property(property)
190
+ end
191
+
192
+ def normalize_property property
193
+ unless properties.include?(property)
194
+ property = "#{property}?".to_sym if properties.include?("#{property}?".to_sym)
195
+ end
196
+ properties.include?(property) && property
197
+ end
198
+
97
199
  def delete id
98
200
  store.delete id
99
201
  end
@@ -101,24 +203,46 @@ module Squares
101
203
  def each &block
102
204
  values.each &block
103
205
  end
104
- alias_method :where, :select
105
206
 
106
- def property prop, opts
207
+ def where(*args, &block)
208
+ result_set = []
209
+ if block
210
+ result_set = values.select(&block)
211
+ return result_set if result_set.empty?
212
+ end
213
+
214
+ args.each do |arg|
215
+ if arg.kind_of?(Hash)
216
+ result_set = (!result_set.empty? ? result_set : values).reject do |i|
217
+ failed_matches = 0
218
+ arg.each do |k,v|
219
+ raise ArgumentError.new("\"#{k}\" is not a valid property of #{self}") unless valid_property?(k)
220
+ failed_matches += 1 unless i[k] == v
221
+ end
222
+ failed_matches > 0
223
+ end
224
+ elsif arg.kind_of?(Symbol)
225
+ raise ArgumentError.new("\"#{arg}\" is not a valid property of #{self}") unless valid_property?(arg)
226
+ result_set = (result_set.empty? ? values : result_set).select do |i|
227
+ i[arg]
228
+ end
229
+ end
230
+ end
231
+ result_set
232
+ end
233
+
234
+ def property prop, opts={}
107
235
  @_properties ||= []
108
236
  @_properties << prop
109
237
  uniquify_properties
110
- if prop.to_s.match(/\?$/)
111
- define_method prop do
112
- instance_variable_get "@#{self.class.instance_var_for prop}"
113
- end
114
- define_method prop.to_s.gsub(/\?$/, '=') do |v|
115
- instance_variable_set "@#{self.class.instance_var_for prop}", v
116
- end
117
- else
118
- attr_accessor prop
238
+ define_method prop do
239
+ get_property prop
119
240
  end
120
- if default = opts[:default]
121
- defaults[prop] = default
241
+ define_method "#{prop.to_s.gsub(/\?$/, '')}=" do |v|
242
+ set_property prop, v
243
+ end
244
+ if opts.has_key?(:default)
245
+ defaults[prop] = opts[:default]
122
246
  end
123
247
  end
124
248
 
@@ -148,20 +272,53 @@ module Squares
148
272
  @store ||= {}
149
273
  end
150
274
 
151
- def instance_var_for property
152
- if property.to_s.match(/\?$/)
153
- "#{property.to_s.gsub(/\?$/,'')}__question__".to_sym
154
- else
155
- property
156
- end
157
- end
158
-
159
275
  def models
160
276
  @_models.uniq.sort { |a,b| a.to_s <=> b.to_s }
161
277
  end
162
278
 
279
+ ### hooks
280
+ def hooks
281
+ @_hooks
282
+ end
283
+
284
+ def before_create *args, &block
285
+ add_hook :before_create, args, block
286
+ end
287
+
288
+ def after_create *args, &block
289
+ add_hook :after_create, args, block
290
+ end
291
+
292
+ def after_initialize *args, &block
293
+ add_hook :after_initialize, args, block
294
+ end
295
+
296
+ def after_find *args, &block
297
+ add_hook :after_find, args, block
298
+ end
299
+
300
+ def before_save *args, &block
301
+ add_hook :before_save, args, block
302
+ end
303
+
304
+ def after_save *args, &block
305
+ add_hook :after_save, args, block
306
+ end
307
+
308
+ def before_destroy *args, &block
309
+ add_hook :before_destroy, args, block
310
+ end
311
+
312
+ ### /hooks
313
+
163
314
  private
164
315
 
316
+ def add_hook hook_id, args, block
317
+ @_hooks ||= {}
318
+ @_hooks[hook_id] ||= []
319
+ @_hooks[hook_id] << block
320
+ end
321
+
165
322
  def uniquify_properties
166
323
  @_properties = @_properties.uniq.compact
167
324
  end
@@ -1,3 +1,3 @@
1
1
  module Squares
2
- VERSION = "0.2.9"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -4,11 +4,15 @@ require 'squares/base'
4
4
  module Marvel
5
5
  class SuperHero < Squares::Base
6
6
  properties :real_name, :special_powers
7
+ property :caped?, default: false
7
8
  end
8
9
  class Villain < Squares::Base
9
10
  properties :vehicle, :lair
10
11
  property :really_evil?, default: true
11
12
  end
13
+ class Sidekick < Squares::Base
14
+ property :catch_phrase
15
+ end
12
16
  end
13
17
 
14
18
  module Squares
@@ -22,7 +26,7 @@ module Squares
22
26
  Given(:powers) { ['super strength', 'strategy', 'leadership'] }
23
27
  When(:hero) { test_class.new id, real_name: name, special_powers: powers }
24
28
 
25
- describe 'class' do
29
+ describe 'class methods' do
26
30
 
27
31
  describe '.underscore_name' do
28
32
  Then { test_class.underscore_name == 'marvel/super_hero' }
@@ -35,22 +39,22 @@ module Squares
35
39
  end
36
40
 
37
41
  describe '.properties' do
38
- Then { test_class.properties == [:real_name, :special_powers] }
42
+ Then { test_class.properties == [:real_name, :special_powers, :caped?] }
39
43
  end
40
44
 
41
- describe '.use_storage' do
45
+ describe '.storage' do
42
46
  Given(:storage) { {attack: :fwoosh } }
43
47
  When { test_class.store = storage }
44
48
  Then { test_class.store == storage }
45
49
  end
46
50
 
47
- describe '.models lists defined models (inheritors)' do
51
+ describe '.models lists defined models (inheritors, sorted alphabetically)' do
48
52
  When(:result) { described_class.models }
49
- Then { result == [ Marvel::SuperHero, Marvel::Villain ] }
53
+ Then { result == [ Marvel::Sidekick, Marvel::SuperHero, Marvel::Villain ] }
50
54
  end
51
55
 
52
56
  describe '.[]' do
53
- Given { storage[id] = Marshal.dump hero }
57
+ Given { storage[id] = Marshal.dump hero }
54
58
  When(:recovered_hero) { Marvel::SuperHero['Captain America'] }
55
59
  Then { recovered_hero.class == Marvel::SuperHero }
56
60
  Then { recovered_hero.id == 'Captain America' }
@@ -103,7 +107,7 @@ module Squares
103
107
  Then { expect(test_class).to be_includes(id) }
104
108
  end
105
109
 
106
- describe 'more cool stuff' do
110
+ describe 'enumerable collections' do
107
111
  Given do
108
112
  Marvel::SuperHero['Superman'] = { real_name: 'Clark Kent', special_powers: ['flying'] }
109
113
  Marvel::SuperHero['Hulk'] = { real_name: 'Bruce Banner', special_powers: ['smash'] }
@@ -111,8 +115,48 @@ module Squares
111
115
  end
112
116
 
113
117
  describe '.where' do
114
- When(:result) { Marvel::SuperHero.where { |h| h.real_name =~ /^Bruce/ } }
115
- Then { result.count == 2 }
118
+ describe 'accepts a block of code (essentailly like .select)' do
119
+ When(:result) { Marvel::SuperHero.where { |h| h.real_name =~ /^Bruce/ } }
120
+ Then { result.count == 2 }
121
+ end
122
+
123
+ describe 'accepts a hash' do
124
+ When(:result) { Marvel::SuperHero.where( real_name: 'Clark Kent' ) }
125
+ Then { result.count == 1 }
126
+ end
127
+
128
+ describe 'accepts a symbol' do
129
+ Given { Marvel::SuperHero['The Sponge'] = { special_powers: ['soaking'] } }
130
+ Then { Marvel::SuperHero.where( :special_powers ).count == 4 }
131
+ Then { Marvel::SuperHero.where( :real_name ).count == 3 }
132
+ end
133
+
134
+ describe 'accepts boolean symbols' do
135
+ Given do
136
+ %w[ Superman Batman ].each do |h|
137
+ hero = Marvel::SuperHero.find h
138
+ hero.caped = true
139
+ hero.save
140
+ end
141
+ end
142
+ Then { Marvel::SuperHero.where(:caped?).count == 2 }
143
+ Then { Marvel::SuperHero.where(:caped).count == 2 }
144
+ end
145
+
146
+ describe 'accepts both block and args' do
147
+ Given do
148
+ Marvel::SuperHero.find('Superman').tap do |superman|
149
+ superman.special_powers = ['smash']
150
+ end.save
151
+ end
152
+ When(:result) { Marvel::SuperHero.where(special_powers: ['smash']) { |h| h.real_name =~ /^Bruce/ } }
153
+ Then { result.count == 1 }
154
+ end
155
+
156
+ context 'with invalid properties as arguments' do
157
+ Given(:bogus_hash) { { bogus: 'hash' } }
158
+ Then { expect{Marvel::SuperHero.where(bogus_hash)}.to raise_error(ArgumentError) }
159
+ end
116
160
  end
117
161
 
118
162
  describe 'models are enumerable!' do
@@ -121,15 +165,29 @@ module Squares
121
165
  end
122
166
  end
123
167
 
124
- end
168
+ end # class methods
125
169
 
126
- describe 'instances' do
170
+ describe 'instance methods' do
127
171
  describe '#{{property}}' do
128
172
  Then { hero.id == id }
129
173
  Then { hero.real_name == name }
130
174
  Then { hero.special_powers == powers }
131
175
  end
132
176
 
177
+ describe '#{{boolean}}?' do
178
+ When(:villain) { Marvel::Villain.new 'Lizard Man', really_evil?: false }
179
+ describe 'has an accessor (ends with "?")' do
180
+ Then { expect(villain).to respond_to(:really_evil?) }
181
+ Then { expect(villain).to_not be_really_evil }
182
+ end
183
+
184
+ describe 'also has a setter (ends with "=")' do
185
+ Then { expect(villain).to respond_to(:really_evil=) }
186
+ When { villain.really_evil = true }
187
+ Then { expect(villain).to be_really_evil }
188
+ end
189
+ end
190
+
133
191
  describe '#save' do
134
192
  When { hero.save }
135
193
  When(:frozen_hero) { Marshal.restore storage.values.first }
@@ -138,16 +196,22 @@ module Squares
138
196
  Then { frozen_hero.class == Marvel::SuperHero }
139
197
  end
140
198
 
199
+ describe '#delete' do
200
+ Given(:villain) { Marvel::Villain.create 'Lizard Man', lair: 'lab' }
201
+ When { villain.delete }
202
+ Then { expect(Marvel::Villain[villain.id]).to be_nil }
203
+ end
204
+
141
205
  describe '#to_h' do
142
206
  Given(:hero1) { test_class.new id, real_name: name, special_powers: powers }
143
207
  context 'default key name' do
144
- Given(:expected_hash) { { id: id, real_name: name, special_powers: powers } }
208
+ Given(:expected_hash) { { id: id, real_name: name, special_powers: powers, caped?: false } }
145
209
  When(:result) { hero1.to_h }
146
210
  Then { expect(result).to eq(expected_hash) }
147
211
  end
148
212
 
149
213
  context 'custom key name' do
150
- Given(:expected_hash) { { hero: id, real_name: name, special_powers: powers } }
214
+ Given(:expected_hash) { { hero: id, real_name: name, special_powers: powers, caped?: false } }
151
215
  When(:result) { hero1.to_h(:hero) }
152
216
  Then { expect(result).to eq(expected_hash) }
153
217
  end
@@ -167,34 +231,253 @@ module Squares
167
231
  end
168
232
  end
169
233
 
170
- describe 'default values' do
171
- Given do
172
- class Marvel::SuperHero < Squares::Base
173
- property :hair_color, default: 'black'
234
+ describe '#[]' do
235
+ context 'valid key' do
236
+ Then { expect(hero[:real_name]).to eq(name) }
237
+ end
238
+
239
+ context 'key which is not a property' do
240
+ Then { expect(hero[:insurance_company]).to be_nil }
241
+ end
242
+
243
+ context 'boolean properties' do
244
+ When(:villain) { Marvel::Villain.new 'Lizard Man', really_evil?: true }
245
+ Then { expect(villain[:really_evil?]).to eq(true) }
246
+ Then { expect(villain[:really_evil]).to eq(true) }
247
+ end
248
+ end
249
+
250
+ describe '#[]=' do
251
+ context 'valid property' do
252
+ When { hero[:real_name] = 'Bruce Banner' }
253
+ Then { expect(hero.real_name).to eq('Bruce Banner') }
254
+ end
255
+
256
+ context 'key which is not a property' do
257
+ Then { expect { hero[:ip_address] = '127.0.0.1' }.to raise_error(ArgumentError) }
258
+ end
259
+
260
+ context 'boolean properties' do
261
+ When(:villain) { Marvel::Villain.new 'Lizard Man', really_evil?: false }
262
+
263
+ describe 'can be set using the symbol ending in "?"' do
264
+ When { villain[:really_evil?] = true }
265
+ Then { expect(villain).to be_really_evil }
266
+ end
267
+
268
+ describe 'can also be set using the variant without the "?"' do
269
+ When { villain[:really_evil] = true }
270
+ Then { expect(villain).to be_really_evil }
271
+ end
272
+ end
273
+ end
274
+
275
+ describe '#update_properties' do
276
+ Given(:villain) { Marvel::Villain.create 'Dr. Doom', vehicle: 'jet', lair: 'Latveria', really_evil?: false }
277
+ Given(:new_properties) { { vehicle: 'train', hairstyle: 'bald' } }
278
+ When { villain.update_properties new_properties }
279
+
280
+ describe 'un-updated properties stay the same' do
281
+ Then { expect(villain.lair).to eq('Latveria') }
282
+ end
283
+
284
+ describe 'updated properties are changed' do
285
+ Then { expect(villain.vehicle).to eq('train') }
286
+ end
287
+
288
+ describe 'invalid properties do nothing' do
289
+ Then { expect(villain.instance_variable_get("@hairstyle")).to be_nil }
290
+ end
291
+
292
+ describe 'boolean properties' do
293
+ context 'where key ends in "?"' do
294
+ Given { new_properties[:really_evil?] = true }
295
+ Then { expect(villain).to be_really_evil }
296
+ end
297
+ context 'where key does not end with "?"' do
298
+ Given { new_properties[:really_evil] = true }
299
+ Then { expect(villain).to be_really_evil }
174
300
  end
175
301
  end
176
- When(:hero) { test_class.new id, real_name: name }
177
- Then { hero.special_powers == nil }
178
- Then { hero.hair_color == 'black' }
179
- Then { Marvel::SuperHero.defaults == { hair_color: 'black' } }
302
+
303
+ describe 'writes changes to the data store' do
304
+ Then { expect(Marvel::Villain.find('Dr. Doom').vehicle).to eq('train') }
305
+ end
306
+ end
307
+
308
+ describe '#changed?' do
309
+ Given(:hero) { test_class.new id, real_name: name, special_powers: powers }
310
+ context 'new, unsaved' do
311
+ Then { expect(hero).to be_changed }
312
+ end
313
+ context 'after save' do
314
+ When { hero.save }
315
+ Then { expect(hero).to_not be_changed }
316
+ end
317
+ context 'freshly retrieved from storage' do
318
+ Given { hero.save }
319
+ When(:found_hero) { test_class.find id }
320
+ Then { expect(found_hero).to_not be_changed }
321
+ end
322
+ context 'unchanged, but then modified' do
323
+ Given { hero.save }
324
+ When { hero.real_name = 'Fred Flintstone' }
325
+ Then { expect(hero).to be_changed }
326
+ end
327
+ context 'after create' do
328
+ Given(:made_hero) { test_class.create 'Iron Man', real_name: 'Tony Stark', special_powers: ['snark'] }
329
+ Then { expect(made_hero).to_not be_changed }
330
+ end
180
331
  end
181
332
 
333
+ describe 'default values' do
334
+ context 'as a simple value' do
335
+ Given do
336
+ class Marvel::SuperHero < Squares::Base
337
+ property :hair_color, default: 'black'
338
+ end
339
+ end
340
+ When(:hero) { test_class.new id, real_name: name }
341
+
342
+ Then { hero.special_powers == nil }
343
+ Then { hero.hair_color == 'black' }
344
+ Then { Marvel::SuperHero.defaults == { caped?: false, hair_color: 'black' } }
345
+ end
346
+
347
+ context 'as a callback' do
348
+ Given do
349
+ class Marvel::SuperHero < Squares::Base
350
+ property :hungry?, default: lambda{ |i| i.earthling? && i.body_mass > 150 }
351
+ property :body_mass, default: 160
352
+ property :earthling?, default: true
353
+ end
354
+ end
355
+ Given(:the_fly) { test_class.new 'The Fly', body_mass: 110 }
356
+ Given(:manhunter) { test_class.new 'Manhunter', earthling?: false }
357
+ Given(:colossus) { test_class.new 'Colossus', body_mass: 350 }
358
+ Then { expect(the_fly).to_not be_hungry }
359
+ Then { expect(manhunter).to_not be_hungry }
360
+ Then { expect(colossus).to be_hungry }
361
+ end
362
+
363
+ end
182
364
  end
183
365
 
184
- describe "models' properties don't bleed into each other" do
366
+ describe 'instance properties' do
185
367
  When(:villain) { Marvel::Villain.new 'Dr. Octopus', vehicle: 'jets', lair: 'abandonned sewer' }
186
- Then { villain.class == Marvel::Villain }
187
- Then { Marvel::Villain.properties == [:vehicle, :lair, :really_evil?] }
188
- Then { villain.properties == [:vehicle, :lair, :really_evil?] }
189
- Then { expect(villain).to_not respond_to(:hair_color) }
190
- Then { expect(villain).to respond_to(:really_evil?) }
191
- Then { expect(villain).to respond_to(:really_evil=) }
192
- Then { expect(villain).to be_really_evil }
193
- end
194
368
 
195
- describe 'question? properties' do
196
- When(:villain) { Marvel::Villain.new 'Lizard Man', really_evil?: false }
197
- Then { expect(villain).to_not be_really_evil }
369
+ describe "are the same as the Model's properties" do
370
+ Then { villain.properties == Marvel::Villain.properties }
371
+ end
372
+
373
+ describe "don't bleed into other types" do
374
+ Then { expect(villain).to_not respond_to(:real_name) }
375
+ end
198
376
  end
377
+
378
+ describe 'hooks' do
379
+ Given(:hook_spy) { double }
380
+ Given { expect(hook_spy).to receive(hook) }
381
+ Given { allow_any_instance_of(Marvel::Sidekick).to receive(:spy).and_return(hook_spy) }
382
+ Given { Marvel::Sidekick.instance_variable_set "@_hooks", nil }
383
+
384
+ describe '.before_create' do
385
+ Given(:hook) { :before_create }
386
+ Given do
387
+ class Marvel::Sidekick < Squares::Base
388
+ before_create { spy.before_create }
389
+ end
390
+ end
391
+ When { Marvel::Sidekick.create 'Robin', catch_phrase: 'Holy _, Batman!' }
392
+ Then { 'hooked' }
393
+ end
394
+
395
+ describe '.after_create' do
396
+ Given(:hook) { :after_create }
397
+ Given do
398
+ class Marvel::Sidekick < Squares::Base
399
+ after_create { spy.after_create }
400
+ end
401
+ end
402
+ When { Marvel::Sidekick.create 'Robin', catch_phrase: 'Holy _, Batman!' }
403
+ Then { 'hooked' }
404
+ end
405
+
406
+ describe '.after_initialize' do
407
+ Given(:hook) { :after_initialize }
408
+ Given do
409
+ class Marvel::Sidekick < Squares::Base
410
+ after_initialize { spy.after_initialize }
411
+ end
412
+ end
413
+ When { Marvel::Sidekick.new 'Robin', catch_phrase: 'Holy _, Batman!' }
414
+ Then { 'hooked' }
415
+ end
416
+
417
+ describe '.after_find' do
418
+ Given(:hook) { :after_find }
419
+ Given do
420
+ class Marvel::Sidekick < Squares::Base
421
+ after_find { spy.after_find }
422
+ end
423
+ end
424
+ Given { Marvel::Sidekick.create 'Robin', catch_phrase: 'Holy _, Batman!' }
425
+ When(:result) { Marvel::Sidekick.find 'Robin' }
426
+ Then { 'hooked' }
427
+ end
428
+
429
+ describe '.before_save' do
430
+ Given(:hook) { :before_save }
431
+ Given do
432
+ class Marvel::Sidekick < Squares::Base
433
+ before_save { spy.before_save }
434
+ end
435
+ end
436
+ Given(:robin) { Marvel::Sidekick.new 'Robin', catch_phrase: 'Holy _, Batman!' }
437
+ When { robin.save }
438
+ Then { 'hooked' }
439
+ end
440
+
441
+ describe '.after_save' do
442
+ Given(:hook) { :after_save }
443
+ Given do
444
+ class Marvel::Sidekick < Squares::Base
445
+ after_save { spy.after_save }
446
+ end
447
+ end
448
+ Given(:robin) { Marvel::Sidekick.new 'Robin', catch_phrase: 'Holy _, Batman!' }
449
+ When { robin.save }
450
+ Then { 'hooked' }
451
+ end
452
+
453
+ describe '.before_destroy' do
454
+ Given(:hook) { :before_destroy }
455
+ Given do
456
+ class Marvel::Sidekick < Squares::Base
457
+ before_destroy { spy.before_destroy }
458
+ end
459
+ end
460
+ Given(:robin) { Marvel::Sidekick.create 'Robin', catch_phrase: 'Holy _, Batman!' }
461
+ When { robin.destroy }
462
+ Then { 'hooked' }
463
+ end
464
+
465
+ describe 'hook callbacks do not fire other hook callbacks' do
466
+ Given(:hook) { :after_initialize }
467
+ Given { expect(hook_spy).to_not receive(:before_save) }
468
+ Given do
469
+ class Marvel::Sidekick < Squares::Base
470
+ after_initialize do
471
+ self.spy.after_initialize
472
+ save
473
+ end
474
+ before_save { spy.before_save }
475
+ end
476
+ end
477
+ When { Marvel::Sidekick.new 'Robin', catch_phrase: 'Holy _, Batman!' }
478
+ Then { 'hooked' }
479
+ end
480
+
481
+ end # instance methods
199
482
  end
200
483
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squares
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Helbling
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-16 00:00:00.000000000 Z
11
+ date: 2015-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler