rod 0.6.1 → 0.6.2

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 (50) hide show
  1. data/.gitignore +7 -0
  2. data/{README → README.rdoc} +33 -2
  3. data/Rakefile +7 -2
  4. data/changelog.txt +13 -0
  5. data/contributors.txt +2 -0
  6. data/features/append.feature +221 -0
  7. data/features/assoc_indexing.feature +66 -0
  8. data/features/basic.feature +199 -0
  9. data/features/collection.feature +171 -0
  10. data/features/flat_indexing.feature +140 -0
  11. data/features/fred.feature +49 -0
  12. data/features/inheritence.feature +211 -0
  13. data/features/muliple_db.feature +113 -0
  14. data/features/relationships.feature +195 -0
  15. data/features/segmented_indexing.feature +172 -0
  16. data/features/steps/model.rb +386 -0
  17. data/features/steps/rod.rb +71 -0
  18. data/features/steps/test_helper.rb +5 -0
  19. data/lib/rod/abstract_database.rb +17 -5
  20. data/lib/rod/constants.rb +1 -1
  21. data/lib/rod/database.rb +95 -74
  22. data/lib/rod/join_element.rb +6 -2
  23. data/lib/rod/model.rb +37 -9
  24. data/rod.gemspec +15 -12
  25. data/tests/check_strings.rb +10 -0
  26. data/tests/class_compatibility_create.rb +14 -0
  27. data/tests/class_compatibility_verify.rb +18 -0
  28. data/tests/eff1_test.rb +60 -0
  29. data/tests/eff2_test.rb +61 -0
  30. data/tests/full_runs.rb +68 -0
  31. data/tests/generate_classes_create.rb +25 -0
  32. data/tests/generate_classes_model.rb +23 -0
  33. data/tests/generate_classes_rewrite.rb +7 -0
  34. data/tests/generate_classes_verify.rb +46 -0
  35. data/tests/load_struct.rb +24 -0
  36. data/tests/migration_create.rb +25 -0
  37. data/tests/migration_migrate.rb +22 -0
  38. data/tests/migration_model1.rb +23 -0
  39. data/tests/migration_model2.rb +27 -0
  40. data/tests/migration_verify.rb +56 -0
  41. data/tests/mock_tests.rb +128 -0
  42. data/tests/read_on_create.rb +45 -0
  43. data/tests/save_struct.rb +49 -0
  44. data/tests/structures.rb +52 -0
  45. data/tests/unit/database.rb +60 -0
  46. data/tests/unit/model.rb +36 -0
  47. data/tests/unit/model_tests.rb +116 -0
  48. data/tests/validate_read_on_create.rb +12 -0
  49. data/utils/convert_index.rb +31 -0
  50. metadata +77 -28
@@ -0,0 +1,386 @@
1
+ require 'rspec/expectations'
2
+ require File.join(File.dirname(__FILE__),"test_helper")
3
+
4
+ def get_class(class_name,type=:model)
5
+ klass = RodTest.const_get(class_name) rescue nil
6
+ if klass.nil?
7
+ superclass =
8
+ case type
9
+ when :model
10
+ RodTest::TestModel
11
+ when :db
12
+ Rod::Database
13
+ end
14
+ klass = Class.new(superclass)
15
+ RodTest.const_set(class_name,klass)
16
+ end
17
+ klass
18
+ end
19
+
20
+ def get_db(db_name)
21
+ case db_name
22
+ when "database"
23
+ RodTest::Database
24
+ else
25
+ get_class(db_name,:db)
26
+ end
27
+ end
28
+
29
+ def get_instance(class_name,position,cache=false)
30
+ if cache
31
+ @instances[class_name][get_position(position)]
32
+ else
33
+ get_class(class_name)[get_position(position)]
34
+ end
35
+ end
36
+
37
+ def get_value(value)
38
+ case value
39
+ when /^(-)?\d+\.\d+$/
40
+ value.to_f
41
+ when /^(-)?\d+$/
42
+ value.to_i
43
+ when /^:/
44
+ value[1..-1].to_sym
45
+ else
46
+ value = value.scan(/[^\d\\]+|\\\d+/).map do |segment|
47
+ case segment
48
+ when /\\\d+/
49
+ $&.to_i.chr
50
+ else
51
+ segment
52
+ end
53
+ end.join("")
54
+ value
55
+ end
56
+ end
57
+
58
+ def get_position(position)
59
+ case position
60
+ when "first"
61
+ 0
62
+ when "second"
63
+ 1
64
+ when "third"
65
+ 2
66
+ when "last"
67
+ -1
68
+ when Fixnum
69
+ position
70
+ end
71
+ end
72
+
73
+ ################################################################
74
+ # Given
75
+ ################################################################
76
+ Given /^the model is connected with the default database$/ do
77
+ RodTest::TestModel.send(:database_class,RodTest::Database)
78
+ end
79
+
80
+ Given /^a class (\w+) inherits from ([\w:]+)$/ do |name1,name2|
81
+ if name2 =~ /::/
82
+ base_module = Module
83
+ else
84
+ base_module = RodTest
85
+ end
86
+ class2 = name2.split("::").inject(base_module){|m,n| m.const_get(n)}
87
+ class1 = Class.new(class2)
88
+ RodTest.const_set(name1,class1)
89
+ end
90
+
91
+ Given /^a class (\w+) has an? (\w+) field of type (\w+)( with (\w+) index)?$/ do |class_name,field,type,index,index_type|
92
+ index_type = index_type == "flat" ? :flat : :segmented
93
+ if index
94
+ get_class(class_name).send(:field,field.to_sym,type.to_sym,:index => index_type)
95
+ else
96
+ get_class(class_name).send(:field,field.to_sym,type.to_sym)
97
+ end
98
+ end
99
+
100
+ Given /^a class (\w+) has one (\w+ )?(\w+)( with (\w+) index)?$/ do |class_name,type,assoc,index,index_type|
101
+ options = {}
102
+ unless type.nil?
103
+ case type
104
+ when /polymorphic/
105
+ options[:polymorphic] = true
106
+ end
107
+ end
108
+ unless index.nil?
109
+ index_type = (index_type == "flat" ? :flat : :segmented)
110
+ options[:index] = index_type
111
+ end
112
+ get_class(class_name).send(:has_one,assoc.to_sym,options)
113
+ end
114
+
115
+ Given /^a class (\w+) has many (\w+ )?(\w+)( with (\w+) index)?$/ do |class_name,type,assoc,index,index_type|
116
+ options = {}
117
+ unless type.nil?
118
+ case type
119
+ when /polymorphic/
120
+ options[:polymorphic] = true
121
+ end
122
+ end
123
+ unless index.nil?
124
+ index_type = (index_type == "flat" ? :flat : :segmented)
125
+ options[:index] = index_type
126
+ end
127
+ get_class(class_name).send(:has_many,assoc.to_sym,options)
128
+ end
129
+
130
+ ################################################################
131
+ # When
132
+ ################################################################
133
+ # When I create a Caveman
134
+ When /^I create a(?:nother|n)? (\w+)$/ do |class_name|
135
+ @instance = get_class(class_name).new
136
+ @instances[class_name] ||= []
137
+ @instances[class_name] << @instance
138
+ end
139
+
140
+ # When I create a Caveman with 'Fred' name and 'Flintstone' surname
141
+ When /^I create a(?:nother|n)? (\w+) with (.*)$/ do |class_name,rest|
142
+ hash = {}
143
+ rest.split(/\s+and\s+/).each do |pair|
144
+ matched = pair.match(/'(?<value>[^']*)' (?<name>\w+)/)
145
+ hash[matched[:name].to_sym] = get_value(matched[:value])
146
+ end
147
+ begin
148
+ @instance = get_class(class_name).new(hash)
149
+ @instances[class_name] ||= []
150
+ @instances[class_name] << @instance
151
+ rescue Exception => ex
152
+ @error = ex
153
+ end
154
+ end
155
+
156
+ # When I fetch the first Caveman (created)
157
+ When /^I fetch the (\w+) (\w+)( created)?$/ do |position,class_name,created|
158
+ created = !created.nil?
159
+ @instance = get_instance(class_name,position,created)
160
+ end
161
+
162
+ # When I create and store the following Caveman(s):
163
+ # | name | surname |
164
+ # | Fred | Flintstone |
165
+ When /^I create and store the following (\w+)\(s\):$/ do |class_name,table|
166
+ klass = get_class(class_name)
167
+ table.hashes.each do |attributes|
168
+ instance = klass.new
169
+ attributes.each do |field,value|
170
+ instance.send("#{field}=",get_value(value))
171
+ end
172
+ @instances[class_name] ||= []
173
+ @instances[class_name] << instance
174
+ instance.store
175
+ end
176
+ end
177
+
178
+ # When his name is 'Fred' (multiplied 300 times)
179
+ When /^(?:his|her|its) (\w+) is '([^']*)'(?: multiplied (\d+) times)?$/ do |field,value,multiplier|
180
+ value = get_value(value)
181
+ if multiplier
182
+ value *= multiplier.to_i
183
+ end
184
+ @instance.send("#{field}=".to_sym,value)
185
+ end
186
+
187
+ When /^(his|her|its) (\w+) is nil$/ do |ignore,field|
188
+ @instance.send("#{field}=".to_sym,nil)
189
+ end
190
+
191
+
192
+ When /^(his|her|its) (\w+) is the (\w+) (\w+) created$/ do |ignore,field,position,class_name|
193
+ @instance.send("#{field}=".to_sym,get_instance(class_name,position,true))
194
+ end
195
+
196
+ When /^(his|her|its) (\w+) contain the (\w+) (\w+) created$/ do |ignore,field,position,class_name|
197
+ @instance.send("#{field}".to_sym) << get_instance(class_name,position,true)
198
+ end
199
+
200
+ When /^(his|her|its) (\w+) contain nil$/ do |ignore,field|
201
+ @instance.send("#{field}".to_sym) << nil
202
+ end
203
+
204
+ When /^I store (him|her|it) in the database( (\d+) times)?$/ do |ignore,times,count|
205
+ if times
206
+ count.to_i.times do |index|
207
+ instance = @instance.dup
208
+ instance.store
209
+ end
210
+ else
211
+ @instance.store
212
+ end
213
+ end
214
+
215
+ When /^I store the (\w+) (\w+) in the database$/ do |position,class_name|
216
+ get_instance(class_name,position,true).store
217
+ end
218
+
219
+ When /^I access the (\w+) (\w+) index$/ do |class_name,field|
220
+ get_class(class_name).send("find_by_#{field}",nil)
221
+ end
222
+
223
+ When /^I remember the (\w+) (\w+)$/ do |position,class_name|
224
+ @remembered = get_instance(class_name,position)
225
+ end
226
+
227
+ ################################################################
228
+ # Then
229
+ ################################################################
230
+ Then /^there should be (\d+) (\w+)(\([^)]*\))?$/ do |count,class_name,ignore|
231
+ get_class(class_name).count.should == count.to_i
232
+ end
233
+
234
+ Then /^the (\w+) of the (\w+) ([A-Z]\w+) should be '([^']*)'$/ do |field, position, class_name,value|
235
+ get_instance(class_name,position).send(field.to_sym).should == get_value(value)
236
+ end
237
+
238
+ Then /^the (\w+) of the (\w+) (\w+) should be '([^']*)'( multiplied (\d+) times)$/ do |field, position, class_name,value,multi,multiplier|
239
+ value = get_value(value)
240
+ if multi
241
+ value *= multiplier.to_i
242
+ end
243
+ get_instance(class_name,position).send(field.to_sym).should == value
244
+ end
245
+
246
+ Then /^the (\w+) of the remembered instance should be '([^']*)'( multiplied (\d+) times)?$/ do |field, value,multi,multiplier|
247
+ value = get_value(value)
248
+ if multi
249
+ value *= multiplier.to_i
250
+ end
251
+ @remembered.send(field.to_sym).should == value
252
+ end
253
+
254
+ Then /^the (\w+) (\w+) should not have a (\w+) field$/ do |position, class_name, field|
255
+ (lambda {get_instance(class_name,position).send(field.to_sym)}).should raise_error(NoMethodError)
256
+ end
257
+
258
+ Then /^the (\w+) (\w+) should not have (a|an )?(\w+)$/ do |position, class_name, ignore, assoc|
259
+ (lambda {get_instance(class_name,position).send(assoc.to_sym)}).should raise_error(NoMethodError)
260
+ end
261
+
262
+ Then /^the (\w+) (\w+) should not exist$/ do |position,class_name|
263
+ (lambda {get_instance(class_name,position)}).should raise_error(IndexError)
264
+ end
265
+
266
+ Then /^the (\w+) of the (\w+) (\w+) should be equal to the (\w+) (\w+)$/ do |field, position1,class1,position2,class2|
267
+ get_instance(class1,position1).send(field.to_sym).should ==
268
+ get_instance(class2,position2,true)
269
+ end
270
+
271
+ Then /^the (\w+) of the (\w+) (\w+) should be nil$/ do |field,position1,class1|
272
+ get_instance(class1,position1).send(field.to_sym).should == nil
273
+ end
274
+
275
+
276
+ Then /^the (\w+) (\w+) should have (\d+) (\w+)$/ do |position,class_name,count,field|
277
+ get_instance(class_name,position).send(field.to_sym).count.should == count.to_i
278
+ end
279
+
280
+ Then /^(\w+)(\([^)]*\))? from (\d+) to (\d+) should have '([^']*)' (\w+)$/ do |class_name,ignore,first,last,value,field|
281
+ (first.to_i - 1).upto(last.to_i - 1) do |index|
282
+ get_instance(class_name,index).send(field.to_sym).should == value
283
+ end
284
+ end
285
+
286
+ Then /^(\w+)(\([^)]*\))? from (\d+) to (\d+) should have a(n)? (\w+) equal to the (\w+) (\w+)$/ do |class1,ignore,first,last,ignore1,field,position,class2|
287
+ (first.to_i - 1).upto(last.to_i - 1) do |index|
288
+ get_instance(class1,index).send(field.to_sym).should == get_instance(class2,position)
289
+ end
290
+ end
291
+
292
+ Then /^(\w+)(\([^)]*\))? from (\d+) to (\d+) should have (\d+) (\w+)$/ do |class1,ignore,first,last,count,field|
293
+ (first.to_i - 1).upto(last.to_i - 1) do |index|
294
+ get_instance(class1,index).send(field.to_sym).count.should == count.to_i
295
+ end
296
+ end
297
+
298
+ Then /^(\w+)(\([^)]*\))? from (\d+) to (\d+) should have (\w+) of (\w+) equal to the (\w+) (\w+) created$/ do |class1,ignore,first,last,position1,field,position2,class2|
299
+ (first.to_i - 1).upto(last.to_i - 1) do |index|
300
+ get_instance(class1,index).send(field.to_sym)[get_position(position1)] == get_instance(class2,position2)
301
+ end
302
+ end
303
+
304
+ Then /^the (\w+) of (\w+) of the (\w+) (\w+) should be equal to the (\w+) (\w+)$/ do |position0,field,position1,class1,position2,class2|
305
+ get_instance(class1,position1).send(field.to_sym)[get_position(position0)].should ==
306
+ get_instance(class2,position2,true)
307
+ end
308
+
309
+ Then /^the (\w+) of (\w+) of the (\w+) (\w+) should be nil$/ do |position0,field,position1,class1|
310
+ get_instance(class1,position1).send(field.to_sym)[get_position(position0)].should == nil
311
+ end
312
+
313
+ # Then his name should be 'Fred'
314
+ Then /^(?:his|her|its) (\w+) should be '([^']*)'$/ do |property, value|
315
+ value = get_value(value)
316
+ @instance.send(property.to_sym).should == value
317
+ end
318
+
319
+ # Then his name should be nil
320
+ Then /^(?:his|her|its) (\w+) should be nil$/ do |property|
321
+ @instance.send(property.to_sym).should == nil
322
+ end
323
+
324
+ # Then his items should be empty
325
+ Then /^(?:his|her|its) (\w+) should be empty$/ do |property|
326
+ @instance.send(property.to_sym).should be_empty
327
+ end
328
+
329
+ Then /^the (\w+) (\w+) should be equal with the instance$/ do |position1,class1|
330
+ instance1 = get_instance(class1,position1)
331
+ instance1.should == @instance
332
+ end
333
+
334
+ Then /^the (\w+) (\w+) should be identical with the (\w+) (\w+)$/ do |position1,class1,position2,class2|
335
+ instance1 = get_instance(class1,position1)
336
+ instance2 = get_instance(class2,position2)
337
+ instance1.object_id.should == instance2.object_id
338
+ end
339
+
340
+ Then /^there should be (\d+) (\w+)(?:\([^)]*\))? with '([^']*)' (\w+)$/ do |count,class_name,value,field|
341
+ get_class(class_name).send("find_all_by_#{field}",get_value(value)).count.should == count.to_i
342
+ end
343
+
344
+ # Then there should exist a User with 'Adam' name
345
+ Then /^there should exist a(?:n)? (\w+) with '([^']*)' (\w+)$/ do |class_name,value,field|
346
+ get_class(class_name).send("find_by_#{field}",get_value(value)).should_not == nil
347
+ end
348
+
349
+ # Then there should be 5 User(s) with the first Dog as dog
350
+ Then /^there should be (\d+) (\w+)(?:\([^)]*\))? with the (\w+) (\w+) as (\w+)$/ do |count,class1,position,class2,assoc|
351
+ get_class(class1).send("find_all_by_#{assoc}",get_instance(class2,position)).count.should == count.to_i
352
+ end
353
+
354
+ # Then there should exist a User with the first Dog as dog
355
+ Then /^there should exist a(?:n)? (\w+) with the (\w+) (\w+) as (\w+)$/ do |class1,position,class2,assoc|
356
+ get_class(class1).send("find_by_#{assoc}",get_instance(class2,position)).should_not == nil
357
+ end
358
+
359
+
360
+ Then /^I should be able to iterate( with index)? over these (\w+)\(s\)$/ do |with_index,class_name|
361
+ if with_index
362
+ (lambda {get_class(class_name).each.with_index{|e,i| e}}).should_not raise_error(Exception)
363
+ else
364
+ (lambda {get_class(class_name).each{|e| e}}).should_not raise_error(Exception)
365
+ end
366
+ end
367
+
368
+ Then /^I should be able to find a (\w+) with '([^']*)' (\w+) and '([^']*)' (\w+)$/ do |class_name,value1,field1,value2,field2|
369
+ get_class(class_name).find{|e| e.send(field1) == get_value(value1) && e.send(field2) == get_value(value2)}.should_not == nil
370
+ end
371
+
372
+ Then /^there should be (\d+) (\w+)(\([^)]*\))? with (\w+) (below|above) (\d+)( with index (below|above) (\d+))?$/ do |count1,class_name,ignore,field,relation1,value,with_index,relation2,count2|
373
+ relation1 = relation1 == "below" ? :< : :>
374
+ relation2 = relation2 == "below" ? :< : :>
375
+ if with_index
376
+ get_class(class_name).each.with_index.select{|e,i| e.send(field).send(relation1,value.to_i) &&
377
+ i.send(relation2,count2.to_i)}.size.should == count1.to_i
378
+ else
379
+ get_class(class_name).select{|e| e.send(field).send(relation1, value.to_i)}.size.should == count1.to_i
380
+ end
381
+
382
+ end
383
+
384
+ Then /^([\w:]+) should be raised$/ do |exception|
385
+ @error.class.to_s.should == exception
386
+ end
@@ -0,0 +1,71 @@
1
+ require File.join(File.dirname(__FILE__),"test_helper")
2
+
3
+ #$ROD_DEBUG = true
4
+
5
+ Given /^the library works in development mode$/ do
6
+ Rod::Database.development_mode = true
7
+ end
8
+
9
+ Given /^(the )?(\w+) is created( in (\w+))?$/ do |ignore,db_name,location,location_name|
10
+ get_db(db_name).instance.close_database if get_db(db_name).instance.opened?
11
+ if File.exist?("tmp")
12
+ if location
13
+ db_location = location_name
14
+ else
15
+ db_location = db_name
16
+ end
17
+ end
18
+ get_db(db_name).instance.create_database("tmp/#{db_location}")
19
+ @instances = {}
20
+ end
21
+
22
+ Given /^a class (\w+) is connected to (\w+)$/ do |class_name,db_name|
23
+ get_class(class_name).send(:database_class,get_class(db_name,:db))
24
+ end
25
+
26
+ Given /^the class space is cleared$/ do
27
+ #RodTest::Database.instance.close_database(true) if RodTest::Database.instance.opened?
28
+ RodTest.constants.each do |constant|
29
+ klass = RodTest.const_get(constant)
30
+ if constant.to_s =~ /Database/
31
+ if klass.instance.opened?
32
+ klass.instance.close_database(true)
33
+ end
34
+ end
35
+ RodTest.send(:remove_const,constant)
36
+ end
37
+ # TODO separate step?
38
+ default_db = Class.new(Rod::Database)
39
+ RodTest.const_set("Database",default_db)
40
+ default_model = Class.new(Rod::Model)
41
+ RodTest.const_set("TestModel",default_model)
42
+ end
43
+
44
+ # Should be split
45
+ When /^I reopen (\w+)( for reading)?( in (\w+))?$/ do |db_name,reading,location,location_name|
46
+ if location
47
+ db_location = location_name
48
+ else
49
+ db_location = db_name
50
+ end
51
+ get_db(db_name).instance.close_database
52
+ get_db(db_name).instance.clear_cache
53
+ readonly = reading.nil? ? false : true
54
+ get_db(db_name).instance.open_database("tmp/#{db_location}",readonly)
55
+ end
56
+
57
+ When /^I open (\w+)( for reading)?( in (\w+))?$/ do |db_name,reading,location,location_name|
58
+ if location
59
+ db_location = location_name
60
+ else
61
+ db_location = db_name
62
+ end
63
+ get_db(db_name).instance.clear_cache
64
+ readonly = reading.nil? ? false : true
65
+ get_db(db_name).instance.open_database("tmp/#{db_location}",readonly)
66
+ end
67
+
68
+ Then /^database should be opened for reading$/ do
69
+ RodTest::Database.instance.opened?.should be_true
70
+ RodTest::Database.instance.readonly_data?.should be_true
71
+ end