aqua 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.gitignore +2 -1
  2. data/Aqua.gemspec +14 -11
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/lib/aqua.rb +5 -7
  6. data/lib/aqua/object/config.rb +2 -3
  7. data/lib/aqua/object/initializers.rb +309 -0
  8. data/lib/aqua/object/pack.rb +56 -132
  9. data/lib/aqua/object/query.rb +30 -2
  10. data/lib/aqua/object/stub.rb +60 -95
  11. data/lib/aqua/object/tank.rb +1 -0
  12. data/lib/aqua/object/translator.rb +313 -0
  13. data/lib/aqua/object/unpack.rb +26 -227
  14. data/lib/aqua/store/couch_db/couch_db.rb +1 -0
  15. data/lib/aqua/store/couch_db/database.rb +1 -1
  16. data/lib/aqua/store/couch_db/design_document.rb +126 -2
  17. data/lib/aqua/store/couch_db/result_set.rb +36 -0
  18. data/lib/aqua/store/couch_db/storage_methods.rb +182 -17
  19. data/lib/aqua/store/storage.rb +4 -48
  20. data/lib/aqua/support/mash.rb +2 -3
  21. data/lib/aqua/support/set.rb +4 -16
  22. data/spec/object/object_fixtures/array_udder.rb +1 -1
  23. data/spec/object/object_fixtures/persistent.rb +0 -2
  24. data/spec/object/pack_spec.rb +137 -517
  25. data/spec/object/query_spec.rb +36 -6
  26. data/spec/object/stub_spec.rb +10 -9
  27. data/spec/object/translator_packing_spec.rb +402 -0
  28. data/spec/object/translator_unpacking_spec.rb +262 -0
  29. data/spec/object/unpack_spec.rb +162 -320
  30. data/spec/spec_helper.rb +18 -0
  31. data/spec/store/couchdb/design_document_spec.rb +148 -7
  32. data/spec/store/couchdb/result_set_spec.rb +95 -0
  33. data/spec/store/couchdb/storage_methods_spec.rb +150 -10
  34. metadata +13 -9
  35. data/lib/aqua/support/initializers.rb +0 -216
  36. data/spec/object/object_fixtures/grounded.rb +0 -13
  37. data/spec/object/object_fixtures/sugar.rb +0 -4
@@ -15,13 +15,43 @@ describe Aqua::Query do
15
15
  :log => @log,
16
16
  :password => 'my secret!'
17
17
  )
18
- @user.commit!
18
+ @user.commit!
19
+
20
+ @user_2 = User.new(
21
+ :username => 'B',
22
+ :name => ['Burny', 'Tierney'],
23
+ :dob => Date.parse('12/28/1921'),
24
+ :created_at => Time.now + 3600,
25
+ :log => Log.new,
26
+ :password => 'my secret!'
27
+ )
28
+ @user_2.commit!
29
+
19
30
  end
20
31
 
21
- describe 'query_index' do
22
- it 'should be a class method' do
23
- User.should respond_to(:query_index)
24
- end
25
- end
32
+ it 'should be have a class method for #index_on' do
33
+ User.should respond_to(:index_on)
34
+ end
35
+
36
+ it 'should create indexes on the storage class' do
37
+ User.index_on(:created_at)
38
+ User::Storage.indexes.should include('created_at')
39
+ end
40
+
41
+ it 'should query on a time' do
42
+ User.index_on(:created_at)
43
+ users = User.query( :created_at, :equals => @time )
44
+ users.size.should == 1
45
+ users.first.username.should == 'kane'
46
+ end
47
+
48
+ it 'should find all records with an attribute' do
49
+ User.index_on(:created_at)
50
+ users = User.query( :created_at )
51
+ users.size.should == 2
52
+ users.first.username.should == 'kane'
53
+ users.last.username.should == 'B'
54
+ end
55
+
26
56
  end
27
57
 
@@ -18,22 +18,23 @@ describe Aqua::Stub do
18
18
  end
19
19
 
20
20
  describe 'initialization' do
21
- it 'should initialize delegate with a TempStub' do
22
- delegate = @stub.instance_eval( "__getobj__" )
23
- delegate.class.should == Aqua::TempStub
24
- end
25
-
26
- it 'should initialize the delegate_id' do
21
+ it 'should initialize the delegate id' do
27
22
  @stub.instance_eval('delegate_id').should == 'my_great_id'
28
23
  end
29
24
 
30
- it 'should initialize the delegate_class' do
25
+ it 'should initialize the delegate class' do
31
26
  @stub.instance_eval('delegate_class').should == 'Gerbilmiester'
32
- end
27
+ end
28
+
29
+ # This stuff is just scoped out for future use. I would like to have a stub know where it exists
30
+ # in its parent object and be able to replace itself instead of loading a delegate.
31
+ it 'should have a parent object'
32
+ it 'should have a path from parent to self'
33
33
  end
34
34
 
35
35
  describe 'delegation' do
36
- it 'should return correct values for initialized methods' do
36
+ it 'should return correct values for initialized methods' do
37
+ Gerbilmiester.should_not_receive(:load)
37
38
  @stub.gerbil.should == true
38
39
  @stub.bacon.should == 'chunky'
39
40
  end
@@ -0,0 +1,402 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require_fixtures
3
+ require File.dirname(__FILE__) + "/../../lib/aqua/support/set"
4
+
5
+ Aqua.set_storage_engine('CouchDB') # to initialize CouchDB
6
+ CouchDB = Aqua::Store::CouchDB unless defined?( CouchDB )
7
+ Translator = Aqua::Translator unless defined?( Translator )
8
+ Rat = Aqua::Translator::Rat unless defined?( Rat )
9
+
10
+ describe Translator, 'packing' do
11
+ before(:each) do
12
+ User.configure_aqua( :embed => {:stub => :username } )
13
+ @user = User.new(:username => 'Kane')
14
+ @user_init = {"class"=>"Aqua::Stub", "init"=>{ "methods"=>{"username"=>"Kane"}, "class"=>"User", "id"=>"" }}
15
+ @file = File.new(File.dirname(__FILE__) + '/../store/couchdb/fixtures_and_data/image_attach.png')
16
+ @file_pack = {
17
+ 'class' => 'Aqua::FileStub',
18
+ 'init' => {
19
+ 'id' => 'image_attach.png',
20
+ "methods"=>{
21
+ "content_type" => 'image/png',
22
+ "content_length" => {"class"=>"Fixnum", "init"=>"26551"}
23
+ }
24
+ }
25
+ }
26
+ @tempfile = Tempfile.new('temp.txt')
27
+ @tempfile.write('I am a tempfile!')
28
+ @tempfile.rewind
29
+ end
30
+
31
+ describe 'instances' do
32
+ it 'should be itialized with the base object' do
33
+ pending
34
+ @user._packer.base.should == @user
35
+ end
36
+ it 'should have externals'
37
+ it 'should have attachments'
38
+ end
39
+
40
+ def pack( obj )
41
+ Translator.pack_object( obj )
42
+ end
43
+
44
+ describe 'class methods should pack' do
45
+ it 'string' do
46
+ pack('string').should == Rat.new("string")
47
+ end
48
+
49
+ it 'times' do
50
+ time = Time.parse("12/23/69")
51
+ pack(time).should == Rat.new( {"class" => "Time", "init" => time.to_s} )
52
+ end
53
+
54
+ it 'dates' do
55
+ date = Date.parse("12/23/69")
56
+ pack(date).should == Rat.new( {"class" => "Date", "init" => date.to_s} )
57
+ end
58
+
59
+ it 'true' do
60
+ pack( true ).should == Rat.new( true )
61
+ end
62
+
63
+ it 'false' do
64
+ pack( false ).should == Rat.new( false )
65
+ end
66
+
67
+ it 'Symbols' do
68
+ pack( :symbol ).should == Rat.new( {"class" => "Symbol", "init" => "symbol"} )
69
+ end
70
+
71
+ it 'Fixnums' do
72
+ pack( 1234 ).should == Rat.new( {"class" => "Fixnum", 'init' => '1234'} )
73
+ end
74
+
75
+ it 'Bignums' do
76
+ pack( 12345678901234567890 ).should ==
77
+ Rat.new( { "class" => 'Bignum', 'init' => '12345678901234567890' } )
78
+ end
79
+
80
+ it 'Floats' do
81
+ pack( 1.681 ).should == Rat.new( { "class" => 'Float', 'init' => '1.681' } )
82
+ end
83
+
84
+ it 'Rationals' do
85
+ pack( Rational( 1, 17 ) ).should == Rat.new( { "class" => 'Rational', 'init' => ['1','17'] } )
86
+ end
87
+
88
+ it 'Ranges' do
89
+ pack( 1..3 ).should == Rat.new( { "class" => 'Range', 'init' => '1..3' } )
90
+ end
91
+
92
+ it 'arrays of string' do
93
+ pack( ['one', 'two'] ).should == Rat.new( {"class" => 'Array', 'init' => ['one', 'two'] } )
94
+ end
95
+
96
+ it 'mixed arrays' do
97
+ pack( [1, :two] ).should ==
98
+ Rat.new({"class" => 'Array', 'init' => [{"class"=>"Fixnum", "init"=>"1"}, {"class"=>"Symbol", "init"=>"two"} ]})
99
+ end
100
+
101
+ it 'arrays with externals' do
102
+ user = User.new(:username => 'Kane')
103
+ pack( [user, 1] ).should == Rat.new(
104
+ {
105
+ 'class' => 'Array',
106
+ 'init' => [
107
+ @user_init,
108
+ {"class"=>"Fixnum", "init"=>"1"}
109
+ ]
110
+ }, { user => '[0]'}
111
+ )
112
+ end
113
+
114
+ it 'externals with an array of stubbed methods' do
115
+ User.configure_aqua( :embed => {:stub => [:username, :name] } )
116
+ user = User.new(
117
+ :username => 'Kane',
118
+ :name => ['Kane', 'Baccigalupi']
119
+ )
120
+ pack( [user] ).should == Rat.new(
121
+ {
122
+ "class" => 'Array',
123
+ "init" => [
124
+ "class"=>"Aqua::Stub",
125
+ "init"=>{
126
+ "methods"=>{"username"=>"Kane", 'name'=>{"class"=>"Array", "init"=>["Kane", "Baccigalupi"]}},
127
+ "class"=>"User", "id"=>""
128
+ }
129
+ ]
130
+ },
131
+ { user => '[0]'}
132
+ )
133
+ User.configure_aqua( :embed => {:stub => :username } )
134
+ end
135
+
136
+
137
+ it 'arrays with deeply nested externals' do
138
+ user = User.new(:username => 'Kane')
139
+ nested_pack = pack( ['layer 1', ['layer 2', ['layer 3', user ] ] ] )
140
+ nested_pack.should == Rat.new(
141
+ {
142
+ 'class' => 'Array',
143
+ 'init' => [ 'layer 1',
144
+ {
145
+ 'class' => 'Array',
146
+ 'init' => [ 'layer 2',
147
+ {
148
+ 'class' => 'Array',
149
+ 'init' => [ 'layer 3', @user_init ]
150
+ }
151
+ ]
152
+ }
153
+ ]
154
+ },
155
+ {user => '[1][1][1]'}
156
+ )
157
+ end
158
+
159
+ it 'array derivatives' do
160
+ array_derivative = ArrayUdder.new
161
+ array_derivative[0] = 'zero index'
162
+ array_derivative.udder # initializes an ivar
163
+ pack( array_derivative ).should == Rat.new(
164
+ {
165
+ 'class' => "ArrayUdder",
166
+ 'init' => ['zero index'],
167
+ "ivars"=>{"@udder"=>"Squeeze out some array milk"}
168
+ }
169
+ )
170
+ end
171
+
172
+
173
+ it 'hashes' do
174
+ pack({'1' => 'one'}).should == Rat.new( {'class' => 'Hash', 'init' => {'1' => 'one'}} )
175
+ end
176
+
177
+ it 'hashes with externals' do
178
+ user = User.new(:username => 'Kane')
179
+ pack({'user' => user}).should == Rat.new(
180
+ {'class' => 'Hash', 'init' => {
181
+ 'user' => @user_init
182
+ }},
183
+ {user => "['user']"}
184
+ )
185
+ end
186
+
187
+ it 'hashes with object keys' do
188
+ pack({1 => 'one'}).should == Rat.new(
189
+ {'class' => 'Hash', 'init' => {
190
+ '/_OBJECT_0' => 'one',
191
+ '/_OBJECT_KEYS' => [{"class"=>"Fixnum", "init"=>"1"}]
192
+ } }
193
+ )
194
+ end
195
+
196
+ it 'hashes with externals as object keys' do
197
+ user = User.new(:username => 'Kane')
198
+ pack({ user => 'user'}).should == Rat.new(
199
+ { 'class' => 'Hash', 'init' => {
200
+ '/_OBJECT_0' => 'user',
201
+ '/_OBJECT_KEYS' => [@user_init]
202
+ }},
203
+ { user => "['/_OBJECT_KEYS'][0]" }
204
+ )
205
+ end
206
+
207
+ it 'open structs' do
208
+ # open structs store keys as symbols internally, as such there is the object keys below ...
209
+ user = User.new(:username => 'Kane')
210
+ struct = OpenStruct.new( :user => user )
211
+ pack( struct ).should == Rat.new(
212
+ { 'class' => 'OpenStruct',
213
+ 'init' => {
214
+ '/_OBJECT_0' => @user_init,
215
+ '/_OBJECT_KEYS' => [{"class"=>"Symbol", "init"=>"user"}]
216
+ }
217
+ },
218
+ { user => "['/_OBJECT_0']" }
219
+ )
220
+ end
221
+
222
+ it 'files' do
223
+ pack( @file ).should == Rat.new( @file_pack , {}, [ @file ] )
224
+ end
225
+
226
+ it 'tempfiles' do
227
+ pack( @tempfile ).should == Rat.new(
228
+ {
229
+ 'class' => 'Aqua::FileStub',
230
+ 'init' => {
231
+ 'id' => 'temp.txt',
232
+ "methods"=>{
233
+ "content_type" => '', # not sure what's up with the mime determination
234
+ "content_length" => {"class"=>"Fixnum", "init"=>"16"}
235
+ }
236
+ }
237
+ },
238
+ {}, [@tempfile]
239
+ )
240
+ end
241
+
242
+ it 'arrays with files' do
243
+ pack( [@file, 1] ).should == Rat.new({
244
+ 'class' => 'Array',
245
+ 'init' => [
246
+ @file_pack,
247
+ {"class"=>"Fixnum", "init"=>"1"}
248
+ ]
249
+ }, {}, [@file]
250
+ )
251
+ end
252
+
253
+ it 'arrays with deeply nested files' do
254
+ nested_pack = pack( ['layer 1', ['layer 2', ['layer 3', @file ] ] ] )
255
+ nested_pack.should == Rat.new(
256
+ {
257
+ 'class' => 'Array',
258
+ 'init' => [ 'layer 1',
259
+ {
260
+ 'class' => 'Array',
261
+ 'init' => [ 'layer 2',
262
+ {
263
+ 'class' => 'Array',
264
+ 'init' => [ 'layer 3', @file_pack ]
265
+ }
266
+ ]
267
+ }
268
+ ]
269
+ },
270
+ {}, [@file]
271
+ )
272
+ end
273
+
274
+ it 'hashes with files' do
275
+ pack({'attachment' => @file}).should == Rat.new(
276
+ {'class' => 'Hash', 'init' => {
277
+ 'attachment' => @file_pack
278
+ }},
279
+ {}, [@file]
280
+ )
281
+ end
282
+
283
+ it 'hashes with file keys' do
284
+ pack({ @file => 'attachment'}).should == Rat.new(
285
+ { 'class' => 'Hash', 'init' => {
286
+ '/_OBJECT_0' => 'attachment',
287
+ '/_OBJECT_KEYS' => [@file_pack]
288
+ }},
289
+ {}, [@file]
290
+ )
291
+ end
292
+
293
+ it 'hash derivatives' do
294
+ hashish = CannedHash.new( 1 => 'one' )
295
+ hashish.yum # sets instance variable
296
+ pack(hashish).should == Rat.new(
297
+ {'class' => 'CannedHash', 'init' => {
298
+ '/_OBJECT_0' => 'one',
299
+ '/_OBJECT_KEYS' => [{"class"=>"Fixnum", "init"=>"1"}]
300
+ }, 'ivars' => {'@yum' => 'Corned Beef!'} }
301
+ )
302
+ end
303
+
304
+ it 'sets' do
305
+ pack(Set.new(['a', 'b'])).should == Rat.new(
306
+ { 'class' => 'Set', 'init' =>{'class'=> 'Array', 'init' =>['a', 'b']} }
307
+ )
308
+ end
309
+
310
+ it 'embedable objects' do
311
+ log = Log.new(:message => "Hi!")
312
+ pack( log ).should == Rat.new(
313
+ {'class' => 'Log', 'ivars' => {'@message' => 'Hi!'}}
314
+ )
315
+ end
316
+
317
+ it 'an array with an embeddable object' do
318
+ log = Log.new(:message => "Hi!")
319
+ pack( [log] ).should == Rat.new(
320
+ {'class' => 'Array', 'init' => [
321
+ {'class' => 'Log', 'ivars' => {'@message' => 'Hi!'}}
322
+ ]}
323
+ )
324
+ end
325
+
326
+ it 'aquatic base objects in non-stub form' do
327
+ pack( @user ).pack['class'].should == 'User'
328
+ end
329
+
330
+ it 'embedded objects in an ivar' do
331
+ @user.log = Log.new(:message => 'Hi!')
332
+ pack( @user ).should == Rat.new(
333
+ {'class' => 'User', 'ivars' => {
334
+ '@log' => {'class' => 'Log', 'ivars' => {'@message' => 'Hi!'}},
335
+ '@username' => 'Kane'
336
+ }}
337
+ )
338
+ end
339
+
340
+ it 'externals in an ivar' do
341
+ otter = User.new(:username => 'Otter')
342
+ @user.other_user = otter
343
+ pack( @user ).should == Rat.new(
344
+ {'class' => 'User', 'ivars' => {
345
+ '@other_user' => {'class' => 'Aqua::Stub', 'init' => {'class' => 'User', 'methods' => {'username' => 'Otter'}, 'id' => ''}},
346
+ '@username' => 'Kane'
347
+ }}, {otter => "['ivars']['@other_user']"}
348
+ )
349
+ end
350
+
351
+ it 'self-referential externals' do
352
+ @user.other_user = @user
353
+ pack( @user ).should == Rat.new(
354
+ {'class' => 'User', 'ivars' => {
355
+ '@other_user' => {'class' => 'Aqua::Stub', 'init' => {'class' => 'User', 'methods' => {'username' => 'Kane'}, 'id' => ''}},
356
+ '@username' => 'Kane'
357
+ }}, {@user => "['ivars']['@other_user']"}
358
+ )
359
+ end
360
+
361
+ it 'self-referential embedded at first-generation' do
362
+ log = Log.new
363
+ log.message = log
364
+ pack( log ).should == Rat.new(
365
+ {'class' => 'Log', 'ivars' => {
366
+ '@message' => {'class' => 'Aqua::Stub', 'init' => {'class' => 'Log', 'id' => ''}}
367
+ }}, {log => "['ivars']['@message']" }
368
+ )
369
+ end
370
+
371
+ it 'deeply nested self-referential embedded' do
372
+ pending( )
373
+ # create two class methods for packing ivars, one self referential
374
+ # depend on the instance method to determine if the object passed in is the base object
375
+ # query will have to search for either relf-referential or normal form
376
+ # unpacking should recognize self-referential form
377
+ log = Log.new
378
+ log.message = ['one', log]
379
+ pack( log ).should == Rat.new(
380
+ {'class' => 'Log', 'ivars' => {
381
+ '@message' => {
382
+ 'class' => 'Array',
383
+ 'init' => ['one', {
384
+ 'class' => 'Aqua::Stub',
385
+ 'init' => {'class' => 'Log', 'id' => 'self'}
386
+ }]
387
+ }
388
+ }}
389
+ )
390
+ end
391
+
392
+ it 'nil' do
393
+ pack( nil ).pack.should == {'class' => 'NilClass', 'init' => '' }
394
+ end
395
+
396
+ describe 'classes' do
397
+ it 'should pack class variables'
398
+ it 'should pack class level instance variables'
399
+ it 'should pack class definition'
400
+ end
401
+ end
402
+ end