active_hash 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ module ActiveYaml
2
+
3
+ class Base < ActiveFile::Base
4
+ class << self
5
+ def load_file
6
+ YAML.load_file(full_path)
7
+ end
8
+
9
+ def extension
10
+ "yml"
11
+ end
12
+
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,265 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ActiveFile::Base do
4
+ before do
5
+ class Country < ActiveFile::Base
6
+ end
7
+ end
8
+
9
+ after do
10
+ Object.send :remove_const, :Country
11
+ end
12
+
13
+ describe ".reload_active_file" do
14
+
15
+ it "returns false by default" do
16
+ Country.reload_active_file.should be_nil
17
+ end
18
+
19
+ end
20
+
21
+ describe ".filename=" do
22
+ before do
23
+ Country.filename = "foo-izzle"
24
+
25
+ class Bar < ActiveFile::Base
26
+ self.filename = "bar-izzle"
27
+ end
28
+ end
29
+
30
+ it "sets the filename on a per-subclass basis" do
31
+ Country.filename.should == "foo-izzle"
32
+ Bar.filename.should == "bar-izzle"
33
+ end
34
+ end
35
+
36
+ describe ".set_filename" do
37
+ before do
38
+ Country.set_filename "foo-izzle"
39
+
40
+ class Bar < ActiveFile::Base
41
+ set_filename "bar-izzle"
42
+ end
43
+ end
44
+
45
+ it "sets the filename on a per-subclass basis" do
46
+ Country.filename.should == "foo-izzle"
47
+ Bar.filename.should == "bar-izzle"
48
+ end
49
+ end
50
+
51
+ describe ".root_path=" do
52
+ before do
53
+ Country.root_path = "foo-izzle"
54
+
55
+ class Bar < ActiveFile::Base
56
+ self.root_path = "bar-izzle"
57
+ end
58
+ end
59
+
60
+ it "sets the root_path on a per-subclass basis" do
61
+ Country.root_path.should == "foo-izzle"
62
+ Bar.root_path.should == "bar-izzle"
63
+ end
64
+ end
65
+
66
+ describe ".set_root_path" do
67
+ before do
68
+ Country.set_root_path "foo-izzle"
69
+
70
+ class Bar < ActiveFile::Base
71
+ set_root_path "bar-izzle"
72
+ end
73
+ end
74
+
75
+ it "sets the root_path on a per-subclass basis" do
76
+ Country.root_path.should == "foo-izzle"
77
+ Bar.root_path.should == "bar-izzle"
78
+ end
79
+ end
80
+
81
+ describe ".full_path" do
82
+ it "defaults to the directory of the calling file" do
83
+ class Country
84
+ def self.extension() "foo" end
85
+ end
86
+
87
+ Country.full_path.should == "#{Dir.pwd}/countries.foo"
88
+ end
89
+ end
90
+
91
+ describe ".all" do
92
+ before do
93
+ class MyClass
94
+ end
95
+ end
96
+
97
+ it "loads the data from the load_file method" do
98
+ class Country
99
+ class << self
100
+ def extension
101
+ "myfile"
102
+ end
103
+
104
+ def load_file
105
+ MyClass.load_file(full_path)
106
+ end
107
+ end
108
+ end
109
+
110
+ Country.reload_active_file = true
111
+ File.stub!(:mtime).and_return(1234)
112
+ MyClass.should_receive(:load_file).and_return([{:id => 1}, {:id => 2}, {:id => 3}])
113
+
114
+ records = Country.all
115
+ records.length.should == 3
116
+ records.should =~ [Country.new(:id => 1), Country.new(:id => 2), Country.new(:id => 3)]
117
+ end
118
+
119
+ context "with reload=true" do
120
+ it "does not re-fetch the data if the file's mtime has not changed" do
121
+ class Country < ActiveFile::Base
122
+ class << self
123
+ def extension
124
+ "myfile"
125
+ end
126
+
127
+ def load_file
128
+ MyClass.load_file(full_path)
129
+ end
130
+ end
131
+ end
132
+
133
+ Country.reload_active_file = true
134
+ File.stub!(:mtime).and_return(1234)
135
+ MyClass.should_receive(:load_file).once.and_return([{:foo => :bar}])
136
+ Country.all
137
+ Country.all
138
+ end
139
+
140
+ it "does re-fetch the data if the yaml file's mtime has changed" do
141
+ class Country < ActiveFile::Base
142
+ class << self
143
+ def extension
144
+ "myfile"
145
+ end
146
+
147
+ def load_file
148
+ MyClass.load_file(full_path)
149
+ end
150
+ end
151
+ end
152
+
153
+ Country.reload_active_file = true
154
+ MyClass.should_receive(:load_file).twice.and_return([{:foo => :bar}])
155
+
156
+ File.stub!(:mtime).and_return(1234)
157
+ Country.all
158
+
159
+ File.stub!(:mtime).and_return(3456)
160
+ Country.all
161
+ end
162
+ end
163
+
164
+ context "with reload=false" do
165
+ it "does not re-fetch the data after the first call to .all" do
166
+ class Country < ActiveFile::Base
167
+ class << self
168
+ def extension
169
+ "myfile"
170
+ end
171
+
172
+ def load_file
173
+ MyClass.load_file(full_path)
174
+ end
175
+ end
176
+ end
177
+
178
+ File.stub!(:mtime).once.and_return(1234)
179
+ MyClass.should_receive(:load_file).once.and_return([{:foo => :bar}])
180
+ Country.all
181
+
182
+ Country.all
183
+ end
184
+
185
+ it "does not re-fetch the data if the yaml file's mtime has changed" do
186
+ class Country < ActiveFile::Base
187
+ class << self
188
+ def extension
189
+ "myfile"
190
+ end
191
+
192
+ def load_file
193
+ MyClass.load_file(full_path)
194
+ end
195
+ end
196
+ end
197
+
198
+ MyClass.should_receive(:load_file).once.and_return([{:foo => :bar}])
199
+
200
+ File.stub!(:mtime).and_return(1234)
201
+ Country.all
202
+
203
+ File.stub!(:mtime).and_return(3456)
204
+ Country.all
205
+ end
206
+
207
+ it "does not fetch data if data has been set to non-nil" do
208
+ class Country < ActiveFile::Base
209
+ class << self
210
+ def extension
211
+ "myfile"
212
+ end
213
+
214
+ def load_file
215
+ MyClass.load_file(full_path)
216
+ end
217
+ end
218
+ end
219
+
220
+ MyClass.should_not_receive(:load_file)
221
+ Country.data = [{:foo => :bar}]
222
+ Country.all
223
+ end
224
+
225
+ it "does not fetch data if data has been set to nil" do
226
+ class Country < ActiveFile::Base
227
+ class << self
228
+ def extension
229
+ "myfile"
230
+ end
231
+
232
+ def load_file
233
+ MyClass.load_file(full_path)
234
+ end
235
+ end
236
+ end
237
+
238
+ MyClass.should_not_receive(:load_file)
239
+ Country.data = nil
240
+ Country.all
241
+ end
242
+
243
+ it "does re-fetch data if force is true" do
244
+ class Country < ActiveFile::Base
245
+ class << self
246
+ def extension
247
+ "myfile"
248
+ end
249
+
250
+ def load_file
251
+ MyClass.load_file(full_path)
252
+ end
253
+ end
254
+ end
255
+
256
+ MyClass.should_receive(:load_file).once.and_return([{:foo => :bar}])
257
+ Country.data = nil
258
+ Country.reload(true)
259
+ Country.all.first.foo.should == :bar
260
+ end
261
+
262
+ end
263
+ end
264
+
265
+ end
@@ -0,0 +1,541 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ActiveHash, "Base" do
4
+
5
+ before do
6
+ class Country < ActiveHash::Base
7
+ end
8
+ end
9
+
10
+ after do
11
+ Object.send :remove_const, :Country
12
+ end
13
+
14
+ describe ".fields" do
15
+ before do
16
+ Country.fields :name, :iso_name
17
+ end
18
+
19
+ it "defines a reader for each field" do
20
+ Country.new.should respond_to(:name)
21
+ Country.new.should respond_to(:iso_name)
22
+ end
23
+
24
+ it "defines interrogator methods for each field" do
25
+ Country.new.should respond_to(:name?)
26
+ Country.new.should respond_to(:iso_name?)
27
+ end
28
+
29
+ it "defines single finder methods for each field" do
30
+ Country.should respond_to(:find_by_name)
31
+ Country.should respond_to(:find_by_iso_name)
32
+ end
33
+
34
+ it "defines array finder methods for each field" do
35
+ Country.should respond_to(:find_all_by_name)
36
+ Country.should respond_to(:find_all_by_iso_name)
37
+ end
38
+
39
+ it "defines single finder methods for all combinations of fields" do
40
+ Country.should respond_to(:find_by_name_and_iso_name)
41
+ Country.should respond_to(:find_by_iso_name_and_name)
42
+ end
43
+
44
+ it "defines array finder methods for all combinations of fields" do
45
+ Country.should respond_to(:find_all_by_name_and_iso_name)
46
+ Country.should respond_to(:find_all_by_iso_name_and_name)
47
+ end
48
+ end
49
+
50
+ describe ".data=" do
51
+ before do
52
+ class Region < ActiveHash::Base
53
+ field :description
54
+ end
55
+ end
56
+
57
+ it "populates the object with data" do
58
+ Country.data = [{:name => "US"}, {:name => "Canada"}]
59
+ Country.data.should == [{:name => "US"}, {:name => "Canada"}]
60
+ end
61
+
62
+ it "allows each of it's subclasses to have it's own data" do
63
+ Country.data = [{:name => "US"}, {:name => "Canada"}]
64
+ Region.data = [{:description => "A big region"}, {:description => "A remote region"}]
65
+
66
+ Country.data.should == [{:name => "US"}, {:name => "Canada"}]
67
+ Region.data.should == [{:description => "A big region"}, {:description => "A remote region"}]
68
+ end
69
+ end
70
+
71
+ describe ".all" do
72
+ before do
73
+ Country.field :name
74
+ Country.data = [
75
+ {:id => 1, :name => "US"},
76
+ {:id => 2, :name => "Canada"}
77
+ ]
78
+ end
79
+
80
+ it "returns an empty array if data is nil" do
81
+ Country.data = nil
82
+ Country.all.should be_empty
83
+ end
84
+
85
+ it "returns all data as inflated objects" do
86
+ Country.all.all?{|country| country.should be_kind_of(Country)}
87
+ end
88
+
89
+ it "populates the data correctly" do
90
+ records = Country.all
91
+ records.first.id.should == 1
92
+ records.first.name.should == "US"
93
+ records.last.id.should == 2
94
+ records.last.name.should == "Canada"
95
+ end
96
+
97
+ it "re-populates the records after data= is called" do
98
+ Country.data = [
99
+ {:id => 45, :name => "Canada"}
100
+ ]
101
+ records = Country.all
102
+ records.first.id.should == 45
103
+ records.first.name.should == "Canada"
104
+ records.length.should == 1
105
+ end
106
+ end
107
+
108
+ describe ".count" do
109
+ before do
110
+ Country.data = [
111
+ {:id => 1, :name => "US"},
112
+ {:id => 2, :name => "Canada"}
113
+ ]
114
+ end
115
+
116
+ it "returns the number of elements in the array" do
117
+ Country.count.should == 2
118
+ end
119
+ end
120
+
121
+ describe ".first" do
122
+ before do
123
+ Country.data = [
124
+ {:id => 1, :name => "US"},
125
+ {:id => 2, :name => "Canada"}
126
+ ]
127
+ end
128
+
129
+ it "returns the first object" do
130
+ Country.first.should == Country.new(:id => 1)
131
+ end
132
+ end
133
+
134
+ describe ".last" do
135
+ before do
136
+ Country.data = [
137
+ {:id => 1, :name => "US"},
138
+ {:id => 2, :name => "Canada"}
139
+ ]
140
+ end
141
+
142
+ it "returns the last object" do
143
+ Country.last.should == Country.new(:id => 2)
144
+ end
145
+ end
146
+
147
+ describe ".find" do
148
+ before do
149
+ Country.data = [
150
+ {:id => 1, :name => "US"},
151
+ {:id => 2, :name => "Canada"}
152
+ ]
153
+ end
154
+
155
+ context "with an id" do
156
+ it "finds the record with the specified id" do
157
+ Country.find(2).id.should == 2
158
+ end
159
+
160
+ it "finds the record with the specified id as a string" do
161
+ Country.find("2").id.should == 2
162
+ end
163
+ end
164
+
165
+ context "with :all" do
166
+ it "returns all records" do
167
+ Country.find(:all).should =~ [Country.new(:id => 1), Country.new(:id => 2)]
168
+ end
169
+ end
170
+
171
+ context "with 2 arguments" do
172
+ it "returns the record with the given id and ignores the conditions" do
173
+ Country.find(1, :conditions => "foo=bar").should == Country.new(:id => 1)
174
+ Country.find(:all, :conditions => "foo=bar").length.should == 2
175
+ end
176
+ end
177
+
178
+ context "with an array of ids" do
179
+ before do
180
+ Country.data = [
181
+ {:id => 1},
182
+ {:id => 2},
183
+ {:id => 3}
184
+ ]
185
+ end
186
+
187
+ it "returns all matching ids" do
188
+ Country.find([1,3]).should =~ [Country.new(:id => 1), Country.new(:id => 3)]
189
+ end
190
+ end
191
+ end
192
+
193
+ describe ".find_by_id" do
194
+ before do
195
+ Country.data = [
196
+ {:id => 1, :name => "US"},
197
+ {:id => 2, :name => "Canada"}
198
+ ]
199
+ end
200
+
201
+ context "with an id" do
202
+ it "finds the record with the specified id" do
203
+ Country.find_by_id(2).id.should == 2
204
+ end
205
+ end
206
+
207
+ context "with nil" do
208
+ it "returns nil" do
209
+ Country.find_by_id(nil).should be_nil
210
+ end
211
+ end
212
+
213
+ context "with an id not present" do
214
+ it "returns nil" do
215
+ Country.find_by_id(4567).should be_nil
216
+ end
217
+ end
218
+ end
219
+
220
+ describe "custom finders" do
221
+ before do
222
+ Country.fields :name, :monarch, :language
223
+
224
+ # Start ids above 4 lest we get nil and think it's an AH::Base model with id=4.
225
+ Country.data = [
226
+ {:id => 11, :name => nil, :monarch => nil, :language => "Latin"},
227
+ {:id => 12, :name => "US", :monarch => nil, :language => "English"},
228
+ {:id => 13, :name => "Canada", :monarch => "The Crown of England", :language => "English"},
229
+ {:id => 14, :name => "UK", :monarch => "The Crown of England", :language => "English"}
230
+ ]
231
+ end
232
+
233
+ describe "find_by_<field_name>" do
234
+ describe "with a match" do
235
+ context "for a non-nil argument" do
236
+ it "returns the first matching record" do
237
+ Country.find_by_name("US").id.should == 12
238
+ end
239
+ end
240
+
241
+ context "for a nil argument" do
242
+ it "returns the first matching record" do
243
+ Country.find_by_name(nil).id.should == 11
244
+ end
245
+ end
246
+ end
247
+
248
+ describe "without a match" do
249
+ before do
250
+ Country.data = []
251
+ end
252
+
253
+ context "for a non-nil argument" do
254
+ it "returns nil" do
255
+ Country.find_by_name("Mexico").should be_nil
256
+ end
257
+ end
258
+
259
+ context "for a nil argument" do
260
+ it "returns nil" do
261
+ Country.find_by_name(nil).should be_nil
262
+ end
263
+ end
264
+ end
265
+ end
266
+
267
+ describe "find_all_by_<field_name>" do
268
+ describe "with matches" do
269
+ it "returns all matching records" do
270
+ countries = Country.find_all_by_monarch("The Crown of England")
271
+ countries.length.should == 2
272
+ countries.first.name.should == "Canada"
273
+ countries.last.name.should == "UK"
274
+ end
275
+ end
276
+
277
+ describe "without matches" do
278
+ it "returns an empty array" do
279
+ Country.find_all_by_name("Mexico").should be_empty
280
+ end
281
+ end
282
+ end
283
+
284
+ describe "find_by_<field_one>_and_<field_two>" do
285
+ describe "with a match" do
286
+ it "returns the first matching record" do
287
+ Country.find_by_name_and_monarch("Canada", "The Crown of England").id.should == 13
288
+ Country.find_by_monarch_and_name("The Crown of England", "Canada").id.should == 13
289
+ end
290
+ end
291
+
292
+ describe "without a match" do
293
+ it "returns nil" do
294
+ Country.find_by_name_and_monarch("US", "The Crown of England").should be_nil
295
+ end
296
+ end
297
+
298
+ describe "for fields the class doesn't have" do
299
+ it "raises a NoMethodError" do
300
+ lambda {
301
+ Country.find_by_name_and_shoe_size("US", 10)
302
+ }.should raise_error(NoMethodError, "undefined method `find_by_name_and_shoe_size' for Country:Class")
303
+ end
304
+ end
305
+ end
306
+
307
+ describe "find_all_by_<field_one>_and_<field_two>" do
308
+ describe "with matches" do
309
+ it "returns all matching records" do
310
+ countries = Country.find_all_by_monarch_and_language("The Crown of England", "English")
311
+ countries.length.should == 2
312
+ countries.first.name.should == "Canada"
313
+ countries.last.name.should == "UK"
314
+ end
315
+ end
316
+
317
+ describe "without matches" do
318
+ it "returns an empty array" do
319
+ Country.find_all_by_monarch_and_language("Shaka Zulu", "Zulu").should be_empty
320
+ end
321
+ end
322
+ end
323
+ end
324
+
325
+
326
+ describe "#attributes" do
327
+ it "returns the hash passed in the initializer" do
328
+ country = Country.new(:foo => :bar)
329
+ country.attributes.should == {:foo => :bar}
330
+ end
331
+
332
+ it "symbolizes keys" do
333
+ country = Country.new("foo" => :bar)
334
+ country.attributes.should == {:foo => :bar}
335
+ end
336
+ end
337
+
338
+ describe "reader methods" do
339
+ context "for regular fields" do
340
+ before do
341
+ Country.fields :name, :iso_name
342
+ end
343
+
344
+ it "returns the given attribute when present" do
345
+ country = Country.new(:name => "Spain")
346
+ country.name.should == "Spain"
347
+ end
348
+
349
+ it "returns nil when not present" do
350
+ country = Country.new
351
+ country.name.should be_nil
352
+ end
353
+ end
354
+
355
+ context "for fields with default values" do
356
+ before do
357
+ Country.field :name, :default => "foobar"
358
+ end
359
+
360
+ it "returns the given attribute when present" do
361
+ country = Country.new(:name => "Spain")
362
+ country.name.should == "Spain"
363
+ end
364
+
365
+ it "returns the default value when not present" do
366
+ country = Country.new
367
+ country.name.should == "foobar"
368
+ end
369
+ end
370
+ end
371
+
372
+ describe "interrogator methods" do
373
+ before do
374
+ Country.fields :name, :iso_name
375
+ end
376
+
377
+ it "returns true if the given attribute is non-blank" do
378
+ country = Country.new(:name => "Spain")
379
+ country.should be_name
380
+ end
381
+
382
+ it "returns false if the given attribute is blank" do
383
+ country = Country.new(:name => " ")
384
+ country.should_not be_name
385
+ end
386
+
387
+ it "returns false if the given attribute was not passed" do
388
+ country = Country.new
389
+ country.should_not be_name
390
+ end
391
+ end
392
+
393
+ describe "#id" do
394
+ context "when passed an id" do
395
+ it "returns the id as an integer" do
396
+ country = Country.new :id => "1"
397
+ country.id.should == 1
398
+ end
399
+ end
400
+ context "when not passed an id" do
401
+ it "returns nil" do
402
+ country = Country.new
403
+ country.id.should be_nil
404
+ end
405
+ end
406
+ end
407
+
408
+ describe "#new_record?" do
409
+ it "should be false" do
410
+ Country.new.should_not be_new_record
411
+ end
412
+ end
413
+
414
+ describe "#quoted_id" do
415
+ it "should return id" do
416
+ Country.new(:id => 2).quoted_id.should == 2
417
+ end
418
+ end
419
+
420
+ describe "#to_param" do
421
+ it "should return id as a string" do
422
+ Country.new(:id => 2).to_param.should == "2"
423
+ end
424
+ end
425
+
426
+ describe "#eql?" do
427
+ before do
428
+ class Region < ActiveHash::Base
429
+ end
430
+ end
431
+
432
+ it "should return true with the same class and id" do
433
+ Country.new(:id => 23).eql?(Country.new(:id => 23)).should be_true
434
+ end
435
+
436
+ it "should return false with the same class and different ids" do
437
+ Country.new(:id => 24).eql?(Country.new(:id => 23)).should be_false
438
+ end
439
+
440
+ it "should return false with the different classes and the same id" do
441
+ Country.new(:id => 23).eql?(Region.new(:id => 23)).should be_false
442
+ end
443
+
444
+ it "returns false when id is nil" do
445
+ Country.new.eql?(Country.new).should be_false
446
+ end
447
+ end
448
+
449
+ describe "#==" do
450
+ before do
451
+ class Region < ActiveHash::Base
452
+ end
453
+ end
454
+
455
+ it "should return true with the same class and id" do
456
+ Country.new(:id => 23).should == Country.new(:id => 23)
457
+ end
458
+
459
+ it "should return false with the same class and different ids" do
460
+ Country.new(:id => 24).should_not == Country.new(:id => 23)
461
+ end
462
+
463
+ it "should return false with the different classes and the same id" do
464
+ Country.new(:id => 23).should_not == Region.new(:id => 23)
465
+ end
466
+
467
+ it "returns false when id is nil" do
468
+ Country.new.should_not == Country.new
469
+ end
470
+ end
471
+
472
+ describe "#hash" do
473
+ it "returns id for hash" do
474
+ Country.new(:id => 45).hash.should == 45.hash
475
+ Country.new.hash.should == nil.hash
476
+ end
477
+
478
+ it "is hashable" do
479
+ { Country.new(:id => 4) => "bar"}.should == {Country.new(:id => 4) => "bar" }
480
+ { Country.new(:id => 3) => "bar"}.should_not == {Country.new(:id => 4) => "bar" }
481
+ end
482
+ end
483
+
484
+ describe "#readonly?" do
485
+ it "returns true" do
486
+ Country.new.should be_readonly
487
+ end
488
+ end
489
+
490
+ describe "auto-discovery of fields" do
491
+ it "dynamically creates fields for all keys in the hash" do
492
+ Country.data = [
493
+ {:field1 => "foo"},
494
+ {:field2 => "bar"},
495
+ {:field3 => "biz"}
496
+ ]
497
+
498
+ Country.all
499
+
500
+ [:field1, :field2, :field3].each do |field|
501
+ Country.should respond_to("find_by_#{field}")
502
+ Country.should respond_to("find_all_by_#{field}")
503
+ Country.new.should respond_to(field)
504
+ Country.new.should respond_to("#{field}?")
505
+ end
506
+ end
507
+
508
+ it "doesn't override methods already defined" do
509
+ Country.class_eval do
510
+ class << self
511
+ def find_by_name(name)
512
+ "find_by_name defined manually"
513
+ end
514
+
515
+ def find_all_by_name(name)
516
+ "find_all_by_name defined manually"
517
+ end
518
+ end
519
+
520
+ def name
521
+ "name defined manually"
522
+ end
523
+
524
+ def name?
525
+ "name? defined manually"
526
+ end
527
+ end
528
+
529
+ Country.data = [
530
+ {:name => "foo"}
531
+ ]
532
+
533
+ Country.all
534
+ Country.find_by_name("foo").should == "find_by_name defined manually"
535
+ Country.find_all_by_name("foo").should == "find_all_by_name defined manually"
536
+ Country.new.name.should == "name defined manually"
537
+ Country.new.name?.should == "name? defined manually"
538
+ end
539
+ end
540
+
541
+ end