squares 0.2.9 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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