aqua 0.1.6 → 0.2.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.
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