gorillib 0.4.0pre → 0.4.1pre

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.
Files changed (54) hide show
  1. data/CHANGELOG.md +36 -1
  2. data/Gemfile +23 -19
  3. data/Guardfile +1 -1
  4. data/Rakefile +31 -31
  5. data/TODO.md +2 -30
  6. data/VERSION +1 -1
  7. data/examples/builder/ironfan.rb +4 -4
  8. data/gorillib.gemspec +40 -25
  9. data/lib/gorillib/array/average.rb +13 -0
  10. data/lib/gorillib/array/sorted_median.rb +11 -0
  11. data/lib/gorillib/array/sorted_percentile.rb +11 -0
  12. data/lib/gorillib/array/sorted_sample.rb +12 -0
  13. data/lib/gorillib/builder.rb +8 -14
  14. data/lib/gorillib/collection/has_collection.rb +31 -31
  15. data/lib/gorillib/collection/list_collection.rb +58 -0
  16. data/lib/gorillib/collection/model_collection.rb +63 -0
  17. data/lib/gorillib/collection.rb +57 -85
  18. data/lib/gorillib/logger/log.rb +26 -22
  19. data/lib/gorillib/model/base.rb +52 -39
  20. data/lib/gorillib/model/doc_string.rb +15 -0
  21. data/lib/gorillib/model/factories.rb +56 -61
  22. data/lib/gorillib/model/lint.rb +24 -0
  23. data/lib/gorillib/model/serialization.rb +12 -2
  24. data/lib/gorillib/model/validate.rb +2 -2
  25. data/lib/gorillib/pathname.rb +21 -6
  26. data/lib/gorillib/some.rb +2 -0
  27. data/lib/gorillib/type/extended.rb +0 -2
  28. data/lib/gorillib/type/url.rb +9 -0
  29. data/lib/gorillib/utils/console.rb +4 -1
  30. data/notes/HOWTO.md +22 -0
  31. data/notes/bucket.md +155 -0
  32. data/notes/builder.md +170 -0
  33. data/notes/collection.md +81 -0
  34. data/notes/factories.md +86 -0
  35. data/notes/model-overlay.md +209 -0
  36. data/notes/model.md +135 -0
  37. data/notes/structured-data-classes.md +127 -0
  38. data/spec/array/average_spec.rb +24 -0
  39. data/spec/array/sorted_median_spec.rb +18 -0
  40. data/spec/array/sorted_percentile_spec.rb +24 -0
  41. data/spec/array/sorted_sample_spec.rb +28 -0
  42. data/spec/gorillib/builder_spec.rb +46 -28
  43. data/spec/gorillib/collection_spec.rb +195 -10
  44. data/spec/gorillib/model/lint_spec.rb +28 -0
  45. data/spec/gorillib/model/record/factories_spec.rb +27 -13
  46. data/spec/gorillib/model/serialization_spec.rb +3 -5
  47. data/spec/gorillib/model_spec.rb +86 -104
  48. data/spec/spec_helper.rb +2 -1
  49. data/spec/support/gorillib_test_helpers.rb +83 -7
  50. data/spec/support/model_test_helpers.rb +9 -28
  51. metadata +52 -44
  52. data/lib/gorillib/configurable.rb +0 -28
  53. data/spec/gorillib/configurable_spec.rb +0 -62
  54. data/spec/support/shared_examples/included_module.rb +0 -20
@@ -22,18 +22,24 @@ describe '', :model_spec => true do
22
22
  ff = Gorillib::Factory( ->(obj){ "bob says #{obj}" } )
23
23
  ff.receive(3).should == "bob says 3"
24
24
  end
25
- it 'uses a factory directly' do
25
+ it 'returns anything that responds to #receive directly' do
26
+ ff = Object.new ; ff.define_singleton_method(:receive){}
27
+ Gorillib::Factory(ff).should equal(ff)
28
+ end
29
+ it 'returns a factory directly' do
26
30
  ff = Gorillib::Factory::SymbolFactory.new
27
- Gorillib::Factory(ff).should == ff
28
- Gorillib::Factory(Gorillib::Factory::SymbolFactory).should == Gorillib::Factory::SymbolFactory
31
+ Gorillib::Factory(ff).should equal(ff)
32
+ end
33
+ it 'does not look up factory **classes**' do
34
+ ->{ Gorillib::Factory(Gorillib::Factory::SymbolFactory) }.should raise_error(ArgumentError, /Don\'t know which factory makes/)
29
35
  end
30
36
  it 'looks up factories by typename' do
31
- Gorillib::Factory(:symbol ).should == Gorillib::Factory::SymbolFactory
32
- Gorillib::Factory(:identical).should == Gorillib::Factory::IdenticalFactory
37
+ Gorillib::Factory(:symbol ).should be_a(Gorillib::Factory::SymbolFactory)
38
+ Gorillib::Factory(:identical).should == (::Whatever)
33
39
  end
34
40
  it 'looks up factories by class' do
35
- Gorillib::Factory(Symbol).should == Gorillib::Factory::SymbolFactory
36
- Gorillib::Factory(String).should == Gorillib::Factory::StringFactory
41
+ Gorillib::Factory(Symbol).should be_a(Gorillib::Factory::SymbolFactory)
42
+ Gorillib::Factory(String).should be_a(Gorillib::Factory::StringFactory)
37
43
  end
38
44
  it 'calls Gorillib::Factory.receive' do
39
45
  x = mock
@@ -86,9 +92,10 @@ describe '', :model_spec => true do
86
92
  shared_examples_for :it_is_registered_as do |*keys|
87
93
  it "the factory for #{keys}" do
88
94
  keys.each do |key|
89
- Gorillib::Factory(key).should == described_class
95
+ Gorillib::Factory(key).should be_a(described_class)
90
96
  end
91
- Gorillib::Factory.send(:factories).to_hash.select{|key,val| val == described_class }.keys.should == keys
97
+ its_factory = Gorillib::Factory(keys.first)
98
+ Gorillib::Factory.send(:factories).to_hash.select{|key,val| val.equal?(its_factory) }.keys.should == keys
92
99
  end
93
100
  end
94
101
 
@@ -177,12 +184,19 @@ describe '', :model_spec => true do
177
184
  its(:typename){ should == :boolean }
178
185
  end
179
186
 
180
- describe Gorillib::Factory::IdenticalFactory do
187
+ describe ::Whatever do
181
188
  it_behaves_like :it_considers_native, true, false, nil, 3, '', 'a string', :a_symbol, [], {}, ->(){ 'a proc' }, Module.new, Complex(1,3), Object.new
182
- it_behaves_like :it_is_registered_as, :identical, :whatever
189
+ it "it is itself the factory for :identical and :whatever" do
190
+ keys = [Whatever, :identical, :whatever]
191
+ keys.each do |key|
192
+ Gorillib::Factory(key).should equal(described_class)
193
+ end
194
+ its_factory = ::Whatever
195
+ Gorillib::Factory.send(:factories).to_hash.select{|key,val| val.equal?(its_factory) }.keys.should == keys
196
+ end
183
197
  end
184
- describe ::Whatever do
185
- it{ described_class.should equal(Gorillib::Factory::IdenticalFactory) }
198
+ describe Gorillib::Factory::IdenticalFactory do
199
+ it{ described_class.should equal(Whatever) }
186
200
  end
187
201
 
188
202
  describe Gorillib::Factory::ModuleFactory do
@@ -1,8 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'model_test_helpers'
3
3
 
4
- require 'json'
5
-
6
4
  require 'multi_json'
7
5
  #
8
6
  require 'gorillib/model'
@@ -17,13 +15,13 @@ describe Gorillib::Model, :model_spec => true do
17
15
  end
18
16
  let :wired_garage_hash do
19
17
  { :cars => [
20
- {:name=>:wildcat, :make_model=>"Buick Wildcat", :year=>1968, :doors=>2, :engine=>{:name=>" engine", :carburetor=>:stock, :volume=>455, :cylinders=>8, :owner=>nil}},
21
- {:name=>:ford_39, :make_model=>"Ford Tudor Sedan", :year=>1939, :doors=>2, :engine=>nil}, ] }
18
+ {:name=>:wildcat, :make_model=>"Buick Wildcat", :year=>1968, :doors=>2, :engine=>{:name=>" engine", :carburetor=>:stock, :volume=>455, :cylinders=>8, :owner=>nil, :_type => "gorillib.test.engine"}, :_type => "gorillib.test.car"},
19
+ {:name=>:ford_39, :make_model=>"Ford Tudor Sedan", :year=>1939, :doors=>2, :engine=>nil, :_type => "gorillib.test.car"}, ], :_type => "gorillib.test.garage" }
22
20
  end
23
21
 
24
22
  describe 'to_json' do
25
23
  it 'recursively serializes' do
26
- MultiJson.load(wildcat.to_json).should == {"name"=>"wildcat","make_model"=>"Buick Wildcat","year"=>1968,"doors"=>2,"engine"=>{"name"=>" engine","carburetor"=>"stock","volume"=>455,"cylinders"=>8,"owner"=>nil}}
24
+ MultiJson.load(wildcat.to_json).should == {"name"=>"wildcat","make_model"=>"Buick Wildcat","year"=>1968,"doors"=>2,"engine"=>{"name"=>" engine","carburetor"=>"stock","volume"=>455,"cylinders"=>8,"owner"=>nil,"_type"=>"gorillib.test.engine"},"_type"=>"gorillib.test.car"}
27
25
  end
28
26
  it 'recursively serializes' do
29
27
  subject.to_json.should == MultiJson.dump(wired_garage_hash)
@@ -4,14 +4,19 @@ require 'model_test_helpers'
4
4
  require 'gorillib/model'
5
5
 
6
6
  describe Gorillib::Model, :model_spec => true do
7
+ let(:smurf_class) do
8
+ class Gorillib::Test::Smurf
9
+ include Gorillib::Model
10
+ field :smurfiness, Integer
11
+ field :weapon, Symbol
12
+ end
13
+ Gorillib::Test::Smurf
14
+ end
15
+ let(:poppa_smurf ){ smurf_class.receive(:name => 'Poppa Smurf', :smurfiness => 9, :weapon => 'staff') }
16
+ let(:smurfette ){ smurf_class.receive(:name => 'Smurfette', :smurfiness => 11, :weapon => 'charm') }
7
17
 
8
- let(:simple_model){ class Gorillib::Test::TestClass ; include Gorillib::Model ; field :my_field, Whatever ; self ; end }
9
- let(:anon_class){ Class.new{ include Gorillib::Model ; field :my_field, :whatever } }
10
- let(:example_inst){ subject.receive(:my_field => 69) }
11
- let(:example_val){ mock('example val') }
12
-
13
- let(:complex_class) do
14
- class Gorillib::Test::ComplexModel
18
+ let(:simple_model) do
19
+ class Gorillib::Test::SimpleModel
15
20
  include Gorillib::Model
16
21
  field :my_field, :whatever
17
22
  field :str_field, String
@@ -19,66 +24,78 @@ describe Gorillib::Model, :model_spec => true do
19
24
  self
20
25
  end
21
26
  end
22
- let(:complex_subclass){ Gorillib::Test::TestSubclass = Class.new(complex_class){ field :zyzzyva, Integer; field :acme, Integer } }
27
+ let(:subclassed_model) do
28
+ class Gorillib::Test::SubclassedModel < simple_model ; field :zyzzyva, Integer; field :acme, Integer ; end
29
+ Gorillib::Test::SubclassedModel
30
+ end
31
+ let(:nested_model) do
32
+ smurf_class = self.smurf_class
33
+ Gorillib::Test::NestedModel = Class.new(simple_model){ field :smurf, smurf_class }
34
+ Gorillib::Test::NestedModel
35
+ end
23
36
 
24
- subject{ complex_class }
37
+ let(:described_class){ simple_model }
38
+ let(:example_inst){ described_class.receive(:my_field => 69) }
39
+
40
+ #
41
+ # IT BEHAVES LIKE A MODEL
42
+ # (maybe you wouldn't notice if it was just one little line)
43
+ #
44
+ it_behaves_like 'a model'
45
+
46
+ # --------------------------------------------------------------------------
25
47
 
26
48
  context 'examples' do
27
- let(:nested_class){ Class.new(complex_class){ field :another_model, self } }
28
49
  it 'type-converts values' do
29
- obj = complex_class.receive({
50
+ obj = simple_model.receive({
30
51
  :my_field => 'accepted as-is', :str_field => :bob, :sym_field => 'converted_to_sym'
31
52
  })
32
53
  obj.attributes.should == { :my_field => 'accepted as-is', :str_field => 'bob', :sym_field=>:converted_to_sym }
33
54
  end
34
55
  it 'handles nested structures' do
35
- obj = nested_class.receive({ :my_field => 69 })
36
- obj.attributes.should == { :my_field => 69, :str_field => nil, :sym_field=>nil, :another_model => nil }
37
- deep_obj = nested_class.receive(:my_field => 111, :str_field => 'deep, man',
38
- :another_model => { :my_field => 69, :another_model => nil })
39
- deep_obj.attributes.should == { :my_field => 111, :str_field => 'deep, man', :sym_field=>nil, :another_model => obj }
56
+ deep_obj = nested_model.receive(:str_field => 'deep, man', :smurf => poppa_smurf.attributes)
57
+ deep_obj.attributes.should == { :str_field => 'deep, man', :smurf => poppa_smurf, :sym_field=>nil, :my_field => nil, }
40
58
  end
41
59
  end
42
60
 
43
61
  context ".field" do
44
- subject{ simple_model }
45
62
  it "describes an attribute" do
46
- example_inst.attributes.should == { :my_field => 69 }
63
+ example_inst.compact_attributes.should == { :my_field => 69 }
47
64
  example_inst.write_attribute(:my_field, 3).should == 3
48
- example_inst.attributes.should == { :my_field => 3 }
65
+ example_inst.compact_attributes.should == { :my_field => 3 }
49
66
  example_inst.read_attribute(:my_field).should == 3
50
67
  end
51
68
  it 'inherits fields from its parent class, even if they are added later' do
52
- complex_class.field_names.should == [:my_field, :str_field, :sym_field]
53
- complex_subclass.field_names.should == [:my_field, :str_field, :sym_field, :zyzzyva, :acme]
54
- complex_class.field :banksy, String
55
- complex_class.field_names.should == [:my_field, :str_field, :sym_field, :banksy ]
56
- complex_subclass.field_names.should == [:my_field, :str_field, :sym_field, :banksy, :zyzzyva, :acme]
69
+ simple_model.field_names.should == [:my_field, :str_field, :sym_field]
70
+ subclassed_model.field_names.should == [:my_field, :str_field, :sym_field, :zyzzyva, :acme]
71
+ simple_model.field :banksy, String
72
+ simple_model.field_names.should == [:my_field, :str_field, :sym_field, :banksy ]
73
+ subclassed_model.field_names.should == [:my_field, :str_field, :sym_field, :banksy, :zyzzyva, :acme]
57
74
  end
58
75
 
59
76
  it "supplies a reader method #foo to call read_attribute(:foo)" do
60
- example_inst.should_receive(:read_attribute).with(:my_field).and_return(example_val)
61
- example_inst.my_field.should == example_val
77
+ example_inst.should_receive(:read_attribute).with(:my_field).and_return(mock_val)
78
+ example_inst.my_field.should == mock_val
62
79
  end
63
80
  it "supplies a writer method #foo= to call write_attribute(:foo)" do
64
- example_inst.should_receive(:write_attribute).with(:my_field, example_val)
65
- (example_inst.my_field = example_val).should == example_val
81
+ example_inst.should_receive(:write_attribute).with(:my_field, mock_val)
82
+ (example_inst.my_field = mock_val).should == mock_val
66
83
  end
67
84
  it "supplies a receiver method #receive_foo to call write_attribute(:foo) and return self" do
68
- example_inst.should_receive(:write_attribute).with(:my_field, example_val)
69
- (example_inst.receive_my_field(example_val)).should == example_inst
85
+ example_inst.should_receive(:write_attribute).with(:my_field, mock_val)
86
+ (example_inst.receive_my_field(mock_val)).should == example_inst
70
87
  end
71
88
  it "sets visibility of reader with :reader => ()" do
72
- subject.field :test_field, Integer, :reader => :private, :writer => false
73
- subject.public_instance_methods.should_not include(:test_field)
74
- subject.private_instance_methods.should include(:test_field)
75
- subject.public_instance_methods.should_not include(:test_field=)
76
- subject.private_instance_methods.should_not include(:test_field=)
89
+ described_class.field :test_field, Integer, :reader => :private, :writer => false
90
+ described_class.public_instance_methods.should_not include(:test_field)
91
+ described_class.private_instance_methods.should include(:test_field)
92
+ described_class.public_instance_methods.should_not include(:test_field=)
93
+ described_class.private_instance_methods.should_not include(:test_field=)
77
94
  end
78
95
  end
79
96
 
80
97
  context '.field' do
81
- subject{ complex_class.new }
98
+ subject{ described_class.new }
82
99
  let(:sample_val){ 'bob' }
83
100
  let(:raw_val){ :bob }
84
101
  it_behaves_like 'a model field', :str_field
@@ -86,7 +103,7 @@ describe Gorillib::Model, :model_spec => true do
86
103
 
87
104
  context '#attributes' do
88
105
  it "maps field names to attribute values" do
89
- example_inst = subject.receive({:my_field=>7, :str_field=>'yo', :sym_field=>:sup})
106
+ example_inst = simple_model.receive({:my_field=>7, :str_field=>'yo', :sym_field=>:sup})
90
107
  example_inst.attributes.should == {:my_field=>7, :str_field=>'yo', :sym_field=>:sup}
91
108
  end
92
109
  it "includes all field names, set and unset" do
@@ -101,8 +118,8 @@ describe Gorillib::Model, :model_spec => true do
101
118
  example_inst.attributes.should == {:my_field=>'int', :str_field=>'str', :sym_field=>'sym'}
102
119
  end
103
120
  it "is an empty hash if there are no fields" do
104
- subject = Class.new{ include Gorillib::Model }
105
- subject.new.attributes.should == {}
121
+ model_with_no_fields = Class.new{ include Gorillib::Model }
122
+ model_with_no_fields.new.attributes.should == {}
106
123
  end
107
124
  end
108
125
 
@@ -116,9 +133,6 @@ describe Gorillib::Model, :model_spec => true do
116
133
  example_inst.unset_attribute(:my_field ).should == 69
117
134
  example_inst.unset_attribute(:str_field).should == nil
118
135
  end
119
- it "raises an error if the field does not exist" do
120
- ->{ example_inst.unset_attribute(:fnord) }.should raise_error(Gorillib::Model::UnknownFieldError, /unknown field: fnord/)
121
- end
122
136
  end
123
137
 
124
138
  context '#update_attributes' do
@@ -160,8 +174,8 @@ describe Gorillib::Model, :model_spec => true do
160
174
  end
161
175
 
162
176
  context '#== -- two models are equal if' do
163
- let(:subklass){ Class.new(subject) }
164
- let(:obj_2){ subject.receive(:my_field => 69) }
177
+ let(:subklass){ Class.new(described_class) }
178
+ let(:obj_2){ described_class.receive(:my_field => 69) }
165
179
  let(:obj_3){ subklass.receive(:my_field => 69) }
166
180
 
167
181
  it 'they have the same class' do
@@ -172,7 +186,7 @@ describe Gorillib::Model, :model_spec => true do
172
186
  end
173
187
  it 'and the same attributes' do
174
188
  example_inst.attributes.should == obj_2.attributes
175
- example_inst.should == obj_2
189
+ example_inst.should == obj_2
176
190
  obj_2.my_field = 100
177
191
  example_inst.should_not == obj_2
178
192
  end
@@ -180,80 +194,48 @@ describe Gorillib::Model, :model_spec => true do
180
194
 
181
195
  context ".fields" do
182
196
  it 'is a hash of Gorillib::Model::Field objects' do
183
- subject.fields.keys.should == [:my_field, :str_field, :sym_field]
184
- subject.fields.values.each{|f| f.should be_a(Gorillib::Model::Field) }
185
- subject.fields.values.map(&:name).should == [:my_field, :str_field, :sym_field]
197
+ described_class.fields.keys.should == [:my_field, :str_field, :sym_field]
198
+ described_class.fields.values.each{|f| f.should be_a(Gorillib::Model::Field) }
199
+ described_class.fields.values.map(&:name).should == [:my_field, :str_field, :sym_field]
186
200
  end
187
201
  end
188
202
 
189
203
  context '.has_field?' do
190
204
  it 'is true if the field exists' do
191
- complex_class.has_field?( :my_field).should be_true
192
- complex_subclass.has_field?(:my_field).should be_true
193
- complex_subclass.has_field?(:zyzzyva ).should be_true
205
+ simple_model.has_field?( :my_field).should be_true
206
+ subclassed_model.has_field?(:my_field).should be_true
207
+ subclassed_model.has_field?(:zyzzyva ).should be_true
194
208
  end
195
209
  it 'is false if it does not exist' do
196
- complex_class.has_field?( :zyzzyva).should be_false
197
- complex_class.has_field?( :fnord ).should be_false
198
- complex_subclass.has_field?(:fnord ).should be_false
210
+ simple_model.has_field?( :zyzzyva).should be_false
211
+ simple_model.has_field?( :fnord ).should be_false
212
+ subclassed_model.has_field?(:fnord ).should be_false
199
213
  end
200
214
  end
201
215
 
202
216
  context '.field_names' do
203
217
  it 'lists fields in order by class, then in order added' do
204
- subject.field_names.should == [:my_field, :str_field, :sym_field]
205
- complex_subclass.field_names.should == [:my_field, :str_field, :sym_field, :zyzzyva, :acme]
206
- subject.field :banksy, String
207
- subject.field_names.should == [:my_field, :str_field, :sym_field, :banksy ]
208
- complex_subclass.field_names.should == [:my_field, :str_field, :sym_field, :banksy, :zyzzyva, :acme]
218
+ described_class.field_names.should == [:my_field, :str_field, :sym_field]
219
+ subclassed_model.field_names.should == [:my_field, :str_field, :sym_field, :zyzzyva, :acme]
220
+ described_class.field :banksy, String
221
+ described_class.field_names.should == [:my_field, :str_field, :sym_field, :banksy ]
222
+ subclassed_model.field_names.should == [:my_field, :str_field, :sym_field, :banksy, :zyzzyva, :acme]
209
223
  end
210
224
  end
211
225
 
212
226
  context '.typename' do
213
227
  it 'has a typename that matches its underscored class name' do
214
- subject.typename.should == 'gorillib.test.complex_model'
215
- end
216
- end
217
-
218
- context '.receive' do
219
- it 'creates a new instance' do
220
- obj = example_inst
221
- subject.should_receive(:new).with().and_return(obj)
222
- result = subject.receive(:my_field => 12)
223
- result.should equal(obj)
224
- result.my_field.should == 12
225
- end
226
- it 'calls receive! to set the attributes, and returns the object' do
227
- obj = example_inst
228
- subject.should_receive(:new).with().and_return(obj)
229
- obj.should_receive(:receive!).with(:my_field => 12)
230
- subject.receive(:my_field => 12).should equal(obj)
231
- end
232
-
233
- it 'uses the given type if the _type attribute is a factory' do
234
- obj = complex_class.receive(:my_field => 12, :acme => 3, :_type => complex_subclass)
235
- obj.should be_a(complex_subclass)
236
- end
237
-
238
- it 'complains if the given type is not right' do
239
- mock_factory = mock ; mock_factory.stub(:receive! => {}, :receive => mock, :new => mock_factory)
240
- mock_factory.should_receive(:<=).and_return(false)
241
- complex_class.should_receive(:warn).with(/doesn't match type/)
242
- complex_class.receive(:my_field => 12, :acme => 3, :_type => mock_factory)
243
- end
244
-
245
- it 'uses the given type if the _type attribute is a typename' do
246
- complex_subclass.typename.should == 'gorillib.test.test_subclass'
247
- obj = complex_class.receive(:my_field => 12, :acme => 3, :_type => 'gorillib.test.test_subclass')
248
- obj.should be_a(complex_subclass)
228
+ described_class.typename.should == 'gorillib.test.simple_model'
249
229
  end
250
230
  end
251
231
 
252
232
  describe Gorillib::Model::NamedSchema do
253
- subject{ simple_model }
254
233
  context ".meta_module" do
234
+ let(:basic_field_names){ [ :my_field, :my_field=, :receive_my_field, :receive_str_field, :receive_sym_field, :str_field, :str_field=, :sym_field, :sym_field= ]}
235
+ let(:anon_class){ Class.new{ include Gorillib::Model ; field :my_field, :whatever } }
236
+
255
237
  it "is named for the class (if the class is named)" do
256
- subject.send(:meta_module).should == Meta::Gorillib::Test::TestClassType
238
+ described_class.send(:meta_module).should == Meta::Gorillib::Test::SimpleModelType
257
239
  end
258
240
  it "is anonymous if the class is anonymous" do
259
241
  anon_class.name.should be_nil
@@ -261,19 +243,19 @@ describe Gorillib::Model, :model_spec => true do
261
243
  anon_class.send(:meta_module).name.should be_nil
262
244
  end
263
245
  it "carries the field-specfic accessor and receive methods" do
264
- subject.send(:meta_module).public_instance_methods.sort.should == [:my_field, :my_field=, :receive_my_field]
265
- anon_class.send(:meta_module).public_instance_methods.sort.should == [:my_field, :my_field=, :receive_my_field]
246
+ described_class.send(:meta_module).public_instance_methods.sort.should == basic_field_names
247
+ anon_class.send(:meta_module).public_instance_methods.sort.should == [:my_field, :my_field=, :receive_my_field]
266
248
  end
267
249
  it "is injected right after the Gorillib::Model module" do
268
- subject.ancestors.first(4).should == [subject, Meta::Gorillib::Test::TestClassType, Gorillib::Model, Object]
269
- subject.should < Meta::Gorillib::Test::TestClassType
250
+ described_class.ancestors.first(4).should == [described_class, Meta::Gorillib::Test::SimpleModelType, Gorillib::Model, Object]
251
+ described_class.should < Meta::Gorillib::Test::SimpleModelType
270
252
  end
271
253
  it "retrieves an existing named module if one exists" do
272
254
  Gorillib::Test.should_not be_const_defined(:TestClass)
273
- module Meta::Gorillib::Test::TestClassType ; def kilroy_was_here() '23 skidoo' ; end ; end
274
- subject.send(:meta_module).public_instance_methods.sort.should == [:kilroy_was_here, :my_field, :my_field=, :receive_my_field]
275
- Gorillib::Test.should be_const_defined(:TestClass)
276
- subject.send(:meta_module).should == Meta::Gorillib::Test::TestClassType
255
+ module Meta::Gorillib::Test::SimpleModelType ; def kilroy_was_here() '23 skidoo' ; end ; end
256
+ described_class.send(:meta_module).public_instance_methods.sort.should == (basic_field_names + [:kilroy_was_here]).sort
257
+ Gorillib::Test.should be_const_defined(:SimpleModel)
258
+ described_class.send(:meta_module).should == Meta::Gorillib::Test::SimpleModelType
277
259
  end
278
260
  end
279
261
  end
data/spec/spec_helper.rb CHANGED
@@ -12,8 +12,9 @@ $LOAD_PATH.unshift(GORILLIB_ROOT_DIR('lib'))
12
12
  $LOAD_PATH.unshift(GORILLIB_ROOT_DIR('spec/support'))
13
13
  require 'gorillib_test_helpers'
14
14
  Dir[GORILLIB_ROOT_DIR('spec/support/matchers/*.rb')].each {|f| require f}
15
- Dir[GORILLIB_ROOT_DIR('spec/support/shared_examples/*.rb')].each {|f| require f}
16
15
 
17
16
  RSpec.configure do |config|
18
17
  include Gorillib::TestHelpers
18
+
19
+ config.treat_symbols_as_metadata_keys_with_true_values = true
19
20
  end
@@ -1,6 +1,83 @@
1
1
  require 'gorillib/utils/capture_output'
2
2
  require 'gorillib/utils/nuke_constants'
3
3
 
4
+ shared_examples_for 'a model' do
5
+
6
+ context 'initialize' do
7
+ it "has no required args" do
8
+ obj = smurf_class.new
9
+ obj.compact_attributes.should == {}
10
+ end
11
+ it "takes the last hashlike arg as the attributes" do
12
+ obj = smurf_class.new :smurfiness => 3, :weapon => :smurfchucks
13
+ obj.compact_attributes.should == { :smurfiness => 3, :weapon => :smurfchucks }
14
+ end
15
+ it "takes all preceding args as positional, clobbering values set in attrs" do
16
+ obj = smurf_class.new 7, :smurfing_stars
17
+ obj.compact_attributes.should == { :smurfiness => 7, :weapon => :smurfing_stars }
18
+ obj = smurf_class.new 7, :smurfing_stars, :smurfiness => 3, :weapon => :smurfchucks
19
+ obj.compact_attributes.should == { :smurfiness => 7, :weapon => :smurfing_stars }
20
+ end
21
+ it "does nothing special with a nil positional arg -- it clobbers anything there setting the attribute to nil" do
22
+ obj = smurf_class.new nil, :smurfiness => 3
23
+ obj.compact_attributes.should == { :smurfiness => nil }
24
+ end
25
+ it "raises an error if too many positional args are given" do
26
+ ->{ smurf_class.new 7, :smurfing_stars, :azrael }.should raise_error(ArgumentError, /wrong number of arguments.*3.*0\.\.2/)
27
+ end
28
+ it "always takes the last hash arg as the attrs -- even if it is in the positional slot of a hash field" do
29
+ smurf_class.field :hashie, Hash
30
+ obj = smurf_class.new({:smurfiness => 3, :weapon => :smurfchucks})
31
+ obj.compact_attributes.should == { :smurfiness => 3, :weapon => :smurfchucks }
32
+ obj = smurf_class.new(3, :smurfchucks, { :weapon => :bastard_smurf })
33
+ obj.compact_attributes.should == { :smurfiness => 3, :weapon => :smurfchucks }
34
+ obj = smurf_class.new(3, :smurfchucks, {:this => :that}, { :weapon => :bastard_smurf })
35
+ obj.compact_attributes.should == { :smurfiness => 3, :weapon => :smurfchucks, :hashie => {:this => :that} }
36
+ end
37
+ end
38
+
39
+ context 'receive' do
40
+ let(:my_attrs){ { :smurfiness => 900, :weapon => :wood_smurfer } }
41
+ let(:subklass){ class ::Gorillib::Test::SubSmurf < smurf_class ; end ; ::Gorillib::Test::SubSmurf }
42
+
43
+ it "returns nil if given a single nil arg" do
44
+ smurf_class.receive(nil).should == nil
45
+ end
46
+ it "returns the object if given a single object of the model class" do
47
+ smurf_class.receive(poppa_smurf).should equal(poppa_smurf)
48
+ end
49
+ it "raises an error if the attributes are not hashlike" do
50
+ ->{ smurf_class.receive('DURRRR') }.should raise_error(ArgumentError, /attributes .* like a hash: "DURRRR"/)
51
+ end
52
+ context "with hashlike args," do
53
+ before{ Gorillib::Factory.send(:factories).reject!{|th, type| th.to_s =~ /gorillib\.test/ }}
54
+
55
+ it "instantiates the object, passing it the attrs and block" do
56
+ my_attrs = { :smurfiness => 900, :weapon => :wood_smurfer }
57
+ smurf_class.should_receive(:new).with(my_attrs)
58
+ smurf_class.receive(my_attrs)
59
+ end
60
+ it "retrieves the right factory if :_type is present" do
61
+ my_attrs = self.my_attrs.merge(:_type => 'gorillib.test.smurf')
62
+ smurf_class.should_receive(:new).with(my_attrs)
63
+ smurf_class.receive(my_attrs)
64
+ end
65
+ it "retrieves the right factory if :_type is present" do
66
+ my_attrs = self.my_attrs.merge(:_type => 'gorillib.test.sub_smurf')
67
+ subklass.should_receive(:new).with(my_attrs)
68
+ smurf_class.receive(my_attrs)
69
+ end
70
+ it 'complains if the given type is not right' do
71
+ mock_factory = mock ; mock_factory.stub(:receive! => {}, :receive => mock, :new => mock_factory)
72
+ mock_factory.should_receive(:<=).and_return(false)
73
+ smurf_class.should_receive(:warn).with(/factory .* is not a type of Gorillib::Test::Smurf/)
74
+ smurf_class.receive(:my_field => 12, :acme => 3, :_type => mock_factory)
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+
4
81
  shared_examples_for "a model field" do |field_name|
5
82
  it('gives the model a field'){ subject.class.should have_field(field_name) }
6
83
 
@@ -10,12 +87,11 @@ shared_examples_for "a model field" do |field_name|
10
87
  subject.read_attribute(field_name).should == sample_val
11
88
  end
12
89
  it "if unset, calls #read_unset_attribute" do
13
- mock_val = mock
14
90
  subject.should_receive(:read_unset_attribute).with(field_name).and_return(mock_val)
15
91
  subject.read_attribute(field_name).should == mock_val
16
92
  end
17
- it "raises an error if the field does not exist" do
18
- ->{ subject.read_attribute(:fnord) }.should raise_error(Gorillib::Model::UnknownFieldError, /unknown field: fnord/)
93
+ it "does **not** raise an error if the field does not exist (require 'model/lint' if you want it to)" do
94
+ ->{ subject.read_attribute(:fnord) }.should_not raise_error(Gorillib::Model::UnknownFieldError, /unknown field: fnord/)
19
95
  end
20
96
  end
21
97
 
@@ -27,8 +103,8 @@ shared_examples_for "a model field" do |field_name|
27
103
  it('returns the new value') do
28
104
  subject.write_attribute(field_name, sample_val).should == sample_val
29
105
  end
30
- it "raises an error if the field does not exist" do
31
- ->{ subject.write_attribute(:fnord, 8) }.should raise_error(Gorillib::Model::UnknownFieldError, /unknown field: fnord/)
106
+ it "does **not** raise an error if the field does not exist (require 'model/lint' if you want it to)" do
107
+ ->{ subject.write_attribute(:fnord, 8) }.should_not raise_error(Gorillib::Model::UnknownFieldError, /unknown field: fnord/)
32
108
  end
33
109
  end
34
110
 
@@ -44,8 +120,8 @@ shared_examples_for "a model field" do |field_name|
44
120
  it('is false if never written') do
45
121
  subject.attribute_set?(field_name).should be_false
46
122
  end
47
- it "raises an error if the field does not exist" do
48
- ->{ subject.attribute_set?(:fnord) }.should raise_error(Gorillib::Model::UnknownFieldError, /unknown field: fnord/)
123
+ it "does **not** raise an error if the field does not exist (require 'model/lint' if you want it to)" do
124
+ ->{ subject.attribute_set?(:fnord) }.should_not raise_error(Gorillib::Model::UnknownFieldError, /unknown field: fnord/)
49
125
  end
50
126
  end
51
127
 
@@ -4,34 +4,15 @@ module Meta ; module Gorillib ; module Test ; end ; end ; end
4
4
  shared_context 'model', :model_spec => true do
5
5
  after(:each){ Gorillib::Test.nuke_constants ; Meta::Gorillib::Test.nuke_constants }
6
6
 
7
- let(:smurf_klass) do
8
- class Gorillib::Test::Smurf
9
- include Gorillib::Model
10
-
11
- field :smurfiness, Integer
12
- field :weapon, Symbol
13
- end
14
- Gorillib::Test::Smurf
15
- end
16
-
17
- let(:smurfette_klass) do
18
- class Gorillib::Test::Smurfette < smurf_klass
19
- field :blondness, Boolean
20
- end
21
- Gorillib::Test::Smurfette
22
- end
23
-
24
- let(:poppa_smurf ){ smurf_klass.receive(:name => 'Poppa Smurf', :smurfiness => 9, :weapon => 'staff') }
25
- let(:smurfette ){ smurf_klass.receive(:name => 'Smurfette', :smurfiness => 11, :weapon => 'charm') }
26
- let(:generic_smurf){ smurf_klass.receive(:name => 'Generic Smurf', :smurfiness => 2, :weapon => 'fists') }
7
+ let(:mock_val){ mock('mock value') }
27
8
 
28
9
  let(:engine_class) do
29
10
  class Gorillib::Test::Engine
30
11
  include Gorillib::Builder
31
- field :name, Symbol, :default => ->{ "#{owner? ? owner.name : ''} engine"}
32
- field :carburetor, Symbol, :default => :stock
33
- field :volume, Integer, :doc => 'displacement volume, in in^3'
34
- field :cylinders, Integer
12
+ magic :name, Symbol, :default => ->{ "#{owner? ? owner.name : ''} engine"}
13
+ magic :carburetor, Symbol, :default => :stock
14
+ magic :volume, Integer, :doc => 'displacement volume, in in^3'
15
+ magic :cylinders, Integer
35
16
  member :owner, Whatever
36
17
  self
37
18
  end
@@ -42,10 +23,10 @@ shared_context 'model', :model_spec => true do
42
23
  engine_class
43
24
  class Gorillib::Test::Car
44
25
  include Gorillib::Builder
45
- field :name, Symbol
46
- field :make_model, String
47
- field :year, Integer
48
- field :doors, Integer
26
+ magic :name, Symbol
27
+ magic :make_model, String
28
+ magic :year, Integer
29
+ magic :doors, Integer
49
30
  member :engine, Gorillib::Test::Engine
50
31
  self
51
32
  end