redis_orm 0.5.1 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,12 @@
1
+ v0.6 [12-09-2011]
2
+ * added equality operator for object, #to_s method for inspecting objects, #find! which could throw RecordNotFound error
3
+ * added self.descendants class method which returns all inherited from RedisOrm::Base classes
4
+ * introduced :sortable option (in property declaration and #find conditions hash) - rudimentary ability to sort records by any property (not just by default 'created_at')
5
+ * now handling models withing modules definitions (test for this in associations_test.rb)
6
+ * properly handling :as parameter in options for has_many/belongs_to self-references
7
+ * binding related models while creating model instance (like this: Article.create(:comment => comment))
8
+ * bunch of small fixes, updated tests and README.md
9
+
1
10
  v0.5.1 [27-07-2011]
2
11
  * added support of uuid as an id/primary key
3
12
  * added documentation on uuid support and connection to the redis server
data/Manifest CHANGED
@@ -11,6 +11,7 @@ lib/redis_orm/associations/has_many_helper.rb
11
11
  lib/redis_orm/associations/has_many_proxy.rb
12
12
  lib/redis_orm/associations/has_one.rb
13
13
  lib/redis_orm/redis_orm.rb
14
+ lib/redis_orm/utils.rb
14
15
  redis_orm.gemspec
15
16
  test/association_indices_test.rb
16
17
  test/associations_test.rb
@@ -23,6 +24,7 @@ test/exceptions_test.rb
23
24
  test/has_one_has_many_test.rb
24
25
  test/indices_test.rb
25
26
  test/options_test.rb
27
+ test/order_test.rb
26
28
  test/polymorphic_test.rb
27
29
  test/redis.conf
28
30
  test/test_helper.rb
data/README.md CHANGED
@@ -27,6 +27,18 @@ class User < RedisOrm::Base
27
27
  end
28
28
  ```
29
29
 
30
+ ## Installing redis_orm
31
+
32
+ gem install redis_orm
33
+
34
+ or
35
+
36
+ git clone git://github.com/german/redis_orm.git
37
+
38
+ cd redis_orm
39
+
40
+ bundle install
41
+
30
42
  ## Setting up a connection to the redis server
31
43
 
32
44
  If you are using Rails you should initialize redis and set up global $redis variable in *config/initializers/redis.rb* file:
@@ -68,6 +80,10 @@ Following options are available in property declaration:
68
80
 
69
81
  The default value of the attribute when it's getting saved w/o any.
70
82
 
83
+ * **:sortable**
84
+
85
+ if *true* is specified then you could sort records by this property later
86
+
71
87
  ## Searching records by the value
72
88
 
73
89
  Usually it's done via declaring an index and using *:conditions* hash or dynamic finders. For example:
@@ -102,7 +118,11 @@ To extract all or part of the associated records you could use 4 options:
102
118
 
103
119
  * :order
104
120
 
105
- Either :desc or :asc (default), since records are stored with *Time.now.to_f* scores, by default they could be fetched only in that (or reversed) order. To store them in different order you should *zadd* record's id to some other sorted list manually.
121
+ Either :desc or :asc (default), since records are stored with *Time.now.to_f* scores, by default they could be fetched only in that (or reversed) order. To order by different property you should:
122
+
123
+ 1. specify *:sortable => true* as option in property declaration
124
+
125
+ 2. specify the property by which you wish to order *:order => [:name, :desc]* or *:order => [:name]* (:asc order is default)
106
126
 
107
127
  * :conditions
108
128
 
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'rake'
4
4
  #=begin
5
5
  require 'echoe'
6
6
 
7
- Echoe.new('redis_orm', '0.5.1') do |p|
7
+ Echoe.new('redis_orm', '0.6') do |p|
8
8
  p.description = "ORM for Redis (advanced key-value storage) with ActiveRecord API"
9
9
  p.url = "https://github.com/german/redis_orm"
10
10
  p.author = "Dmitrii Samoilov"
@@ -26,8 +26,13 @@ Rake::TestTask.new(:test) do |t|
26
26
  end
27
27
  =end
28
28
 
29
+ #require 'rspec/core/rake_task'
29
30
  task :test do |t|
30
31
  Dir['test/**/*_test.rb'].each do |file|
31
32
  puts `ruby -I./lib #{file}`
32
33
  end
33
34
  end
35
+ #task :default => :test
36
+ #RSpec::Core::RakeTask.new(:spec) do |t|
37
+ # t.pattern = 'test/**/*_test.rb'
38
+ #end
data/lib/redis_orm.rb CHANGED
@@ -13,6 +13,8 @@ require File.join(File.dirname(File.expand_path(__FILE__)), 'redis_orm', 'associ
13
13
  require File.join(File.dirname(File.expand_path(__FILE__)), 'redis_orm', 'associations', 'has_many')
14
14
  require File.join(File.dirname(File.expand_path(__FILE__)), 'redis_orm', 'associations', 'has_one')
15
15
 
16
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'redis_orm', 'utils')
17
+
16
18
  class String
17
19
  def i18n_key
18
20
  self.to_s.tableize
@@ -1,14 +1,12 @@
1
1
  module ActiveModelBehavior
2
2
  module ClassMethods
3
3
  def model_name
4
- #@_model_name ||= ActiveModel::Name.new(self).to_s.downcase
5
4
  @_model_name ||= ActiveModel::Name.new(self).to_s.tableize.singularize
6
5
  end
7
6
  end
8
7
 
9
8
  module InstanceMethods
10
9
  def model_name
11
- #@_model_name ||= ActiveModel::Name.new(self.class).to_s.downcase
12
10
  @_model_name ||= ActiveModel::Name.new(self.class).to_s.tableize.singularize
13
11
  end
14
12
  end
@@ -14,20 +14,22 @@ module RedisOrm
14
14
  class_associations = class_variable_get(:"@@associations")
15
15
  class_variable_get(:"@@associations")[model_name] << {:type => :belongs_to, :foreign_model => foreign_model, :options => options}
16
16
 
17
- foreign_model_name = if options[:as]
18
- options[:as].to_sym
19
- else
20
- foreign_model.to_sym
21
- end
22
-
23
- define_method foreign_model_name.to_sym do
17
+ foreign_model_name = options[:as] ? options[:as].to_sym : foreign_model.to_sym
18
+
19
+ define_method foreign_model_name do
24
20
  if options[:polymorphic]
25
21
  model_type = $redis.get("#{model_name}:#{id}:#{foreign_model_name}_type")
26
22
  if model_type
27
23
  model_type.to_s.camelize.constantize.find($redis.get "#{model_name}:#{@id}:#{foreign_model_name}_id")
28
24
  end
29
25
  else
30
- foreign_model.to_s.camelize.constantize.find($redis.get "#{model_name}:#{@id}:#{foreign_model_name}")
26
+ # find model even if it's in some module
27
+ full_model_scope = RedisOrm::Base.descendants.detect{|desc| desc.to_s.split('::').include?(foreign_model.to_s.camelize) }
28
+ if full_model_scope
29
+ full_model_scope.find($redis.get "#{model_name}:#{@id}:#{foreign_model_name}")
30
+ else
31
+ foreign_model.to_s.camelize.constantize.find($redis.get "#{model_name}:#{@id}:#{foreign_model_name}")
32
+ end
31
33
  end
32
34
  end
33
35
 
@@ -36,6 +38,8 @@ module RedisOrm
36
38
  define_method "#{foreign_model_name}=" do |assoc_with_record|
37
39
  # we need to store this to clear old association later
38
40
  old_assoc = self.send(foreign_model_name)
41
+ # find model even if it's in some module
42
+ full_model_scope = RedisOrm::Base.descendants.detect{|desc| desc.to_s.split('::').include?(foreign_model.to_s.camelize) }
39
43
 
40
44
  if options[:polymorphic]
41
45
  $redis.set("#{model_name}:#{id}:#{foreign_model_name}_type", assoc_with_record.model_name)
@@ -43,7 +47,7 @@ module RedisOrm
43
47
  else
44
48
  if assoc_with_record.nil?
45
49
  $redis.del("#{model_name}:#{id}:#{foreign_model_name}")
46
- elsif assoc_with_record.model_name == foreign_model.to_s
50
+ elsif [foreign_model.to_s, full_model_scope.model_name].include?(assoc_with_record.model_name)
47
51
  $redis.set("#{model_name}:#{id}:#{foreign_model_name}", assoc_with_record.id)
48
52
  else
49
53
  raise TypeMismatchError
@@ -8,11 +8,7 @@ module RedisOrm
8
8
  class_associations = class_variable_get(:"@@associations")
9
9
  class_associations[model_name] << {:type => :has_many, :foreign_models => foreign_models, :options => options}
10
10
 
11
- foreign_models_name = if options[:as]
12
- options[:as].to_sym
13
- else
14
- foreign_models.to_sym
15
- end
11
+ foreign_models_name = options[:as] ? options[:as].to_sym : foreign_models.to_sym
16
12
 
17
13
  define_method foreign_models_name.to_sym do
18
14
  Associations::HasManyProxy.new(model_name, id, foreign_models, options)
@@ -51,17 +47,20 @@ module RedisOrm
51
47
  save_index_for_associated_record(index, record, [model_name, id, record.model_name.pluralize]) # record.model_name.pluralize => foreign_models_name
52
48
  end
53
49
 
54
- if !options[:as]
55
- # article.comments = [comment1, comment2]
56
- # iterate through the array of comments and create backlink
57
- # check whether *record* object has *has_many* declaration and TODO it states *self.model_name* in plural
58
- if class_associations[record.model_name].detect{|h| h[:type] == :has_many && h[:foreign_models] == model_name.pluralize.to_sym} #&& !$redis.zrank("#{record.model_name}:#{record.id}:#{model_name.pluralize}", id)#record.model_name.to_s.camelize.constantize.find(id).nil?
59
- $redis.zadd("#{record.model_name}:#{record.id}:#{model_name.pluralize}", Time.now.to_f, id)
60
- # check whether *record* object has *has_one* declaration and TODO it states *self.model_name*
61
- elsif record.get_associations.detect{|h| [:has_one, :belongs_to].include?(h[:type]) && h[:foreign_model] == model_name.to_sym}
62
- # overwrite assoc anyway so we don't need to check record.send(model_name.to_sym).nil? here
63
- $redis.set("#{record.model_name}:#{record.id}:#{model_name}", id)
64
- end
50
+ # article.comments = [comment1, comment2]
51
+ # iterate through the array of comments and create backlink
52
+ # check whether *record* object has *has_many* declaration and it states *self.model_name* in plural
53
+ if assoc = class_associations[record.model_name].detect{|h| h[:type] == :has_many && h[:foreign_models] == model_name.pluralize.to_sym} #&& !$redis.zrank("#{record.model_name}:#{record.id}:#{model_name.pluralize}", id)#record.model_name.to_s.camelize.constantize.find(id).nil?
54
+ assoc_foreign_models_name = assoc[:options][:as] ? assoc[:options][:as] : model_name.pluralize
55
+ key = "#{record.model_name}:#{record.id}:#{assoc_foreign_models_name}"
56
+ $redis.zadd(key, Time.now.to_f, id) if !$redis.zrank(key, id)
57
+ end
58
+
59
+ # check whether *record* object has *has_one* declaration and it states *self.model_name*
60
+ if assoc = record.get_associations.detect{|h| [:has_one, :belongs_to].include?(h[:type]) && h[:foreign_model] == model_name.to_sym}
61
+ foreign_model_name = assoc[:options][:as] ? assoc[:options][:as] : model_name
62
+ # overwrite assoc anyway so we don't need to check record.send(model_name.to_sym).nil? here
63
+ $redis.set("#{record.model_name}:#{record.id}:#{foreign_model_name}", id)
65
64
  end
66
65
  end
67
66
  end
@@ -82,7 +82,7 @@ module RedisOrm
82
82
  prepared_index = if options[:conditions] && options[:conditions].is_a?(Hash)
83
83
  properties = options[:conditions].collect{|key, value| key}
84
84
 
85
- index = @foreign_models.to_s.singularize.camelize.constantize.find_index(properties)
85
+ index = @foreign_models.to_s.singularize.camelize.constantize.find_indices(properties, :first => true)
86
86
 
87
87
  raise NotIndexFound if !index
88
88
 
@@ -20,6 +20,9 @@ module RedisOrm
20
20
  class NotIndexFound < StandardError
21
21
  end
22
22
 
23
+ class RecordNotFound < StandardError
24
+ end
25
+
23
26
  class TypeMismatchError < StandardError
24
27
  end
25
28
 
@@ -29,7 +32,7 @@ module RedisOrm
29
32
  class Base
30
33
  include ActiveModel::Validations
31
34
  include ActiveModelBehavior
32
-
35
+ include Utils
33
36
  include Associations::HasManyHelper
34
37
 
35
38
  extend Associations::BelongsTo
@@ -39,10 +42,11 @@ module RedisOrm
39
42
  attr_accessor :persisted
40
43
 
41
44
  @@properties = Hash.new{|h,k| h[k] = []}
42
- @@indices = Hash.new{|h,k| h[k] = []} # compound indices are available too
45
+ @@indices = Hash.new{|h,k| h[k] = []} # compound indices are available too
43
46
  @@associations = Hash.new{|h,k| h[k] = []}
44
47
  @@callbacks = Hash.new{|h,k| h[k] = {}}
45
48
  @@use_uuid_as_id = {}
49
+ @@descendants = []
46
50
 
47
51
  class << self
48
52
 
@@ -50,8 +54,14 @@ module RedisOrm
50
54
  [:after_save, :before_save, :after_create, :before_create, :after_destroy, :before_destroy].each do |callback_name|
51
55
  @@callbacks[from.model_name][callback_name] = []
52
56
  end
57
+
58
+ @@descendants << from
53
59
  end
54
-
60
+
61
+ def descendants
62
+ @@descendants
63
+ end
64
+
55
65
  # *options* currently supports
56
66
  # *unique* Boolean
57
67
  # *case_insensitive* Boolean
@@ -126,20 +136,29 @@ module RedisOrm
126
136
  $redis.zcard("#{model_name}:ids").to_i
127
137
  end
128
138
 
129
- def first
130
- id = $redis.zrangebyscore("#{model_name}:ids", 0, Time.now.to_f, :limit => [0, 1])
131
- id.empty? ? nil : find(id[0])
139
+ def first(options = {})
140
+ if options.empty?
141
+ id = $redis.zrangebyscore("#{model_name}:ids", 0, Time.now.to_f, :limit => [0, 1])
142
+ id.empty? ? nil : find(id[0])
143
+ else
144
+ find(:first, options)
145
+ end
132
146
  end
133
147
 
134
- def last
135
- id = $redis.zrevrangebyscore("#{model_name}:ids", Time.now.to_f, 0, :limit => [0, 1])
136
- id.empty? ? nil : find(id[0])
148
+ def last(options = {})
149
+ if options.empty?
150
+ id = $redis.zrevrangebyscore("#{model_name}:ids", Time.now.to_f, 0, :limit => [0, 1])
151
+ id.empty? ? nil : find(id[0])
152
+ else
153
+ find(:last, options)
154
+ end
137
155
  end
138
-
139
- def find_index(properties)
156
+
157
+ def find_indices(properties, options = {})
140
158
  properties.map!{|p| p.to_sym}
141
-
142
- @@indices[model_name].detect do |models_index|
159
+ method = options[:first] ? :detect : :select
160
+
161
+ @@indices[model_name].send(method) do |models_index|
143
162
  if models_index[:name].is_a?(Array) && models_index[:name].size == properties.size
144
163
  # check the elements not taking into account their order
145
164
  (models_index[:name] & properties).size == properties.size
@@ -168,6 +187,7 @@ module RedisOrm
168
187
  prepared_index
169
188
  end
170
189
 
190
+ # TODO refactor this messy function
171
191
  def all(options = {})
172
192
  limit = if options[:limit] && options[:offset]
173
193
  [options[:offset].to_i, options[:limit].to_i]
@@ -175,29 +195,49 @@ module RedisOrm
175
195
  [0, options[:limit].to_i]
176
196
  end
177
197
 
178
- if options[:conditions] && options[:conditions].is_a?(Hash)
198
+ order_max_limit = Time.now.to_f
199
+ ids_key = "#{model_name}:ids"
200
+ index = nil
201
+
202
+ prepared_index = if !options[:conditions].blank? && options[:conditions].is_a?(Hash)
179
203
  properties = options[:conditions].collect{|key, value| key}
180
- index = find_index(properties)
204
+ index = find_indices(properties, :first => true)
181
205
 
182
206
  raise NotIndexFound if !index
183
-
184
- prepared_index = construct_prepared_index(index, options[:conditions])
185
207
 
186
- records = []
208
+ construct_prepared_index(index, options[:conditions])
209
+ else
210
+ if options[:order] && options[:order].is_a?(Array)
211
+ model_name
212
+ else
213
+ ids_key
214
+ end
215
+ end
187
216
 
188
- if index[:options][:unique]
189
- id = $redis.get prepared_index
190
- records << model_name.to_s.camelize.constantize.find(id)
217
+ # if not array => created_at native order (in which ids were pushed to "#{model_name}:ids" set by default)
218
+ direction = if !options[:order].blank?
219
+ if options[:order].is_a?(Array)
220
+ # for String values max limit for search key could be 1.0, but for Numeric values there's actually no limit
221
+ order_max_limit = 100_000_000_000
222
+ ids_key = "#{prepared_index}:#{options[:order].first}_ids"
223
+ options[:order].size == 2 ? options[:order].last : 'asc'
191
224
  else
192
- ids = $redis.zrangebyscore(prepared_index, 0, Time.now.to_f)
193
- records += model_name.to_s.camelize.constantize.find(ids)
194
- end
195
- records
225
+ ids_key = prepared_index
226
+ options[:order]
227
+ end
228
+ else
229
+ ids_key = prepared_index
230
+ 'asc'
231
+ end
232
+
233
+ if index && index[:options][:unique]
234
+ id = $redis.get prepared_index
235
+ model_name.to_s.camelize.constantize.find(id)
196
236
  else
197
- if options[:order].to_s == 'desc'
198
- $redis.zrevrangebyscore("#{model_name}:ids", Time.now.to_f, 0, :limit => limit).compact.collect{|id| find(id)}
237
+ if direction.to_s == 'desc'
238
+ $redis.zrevrangebyscore(ids_key, order_max_limit, 0, :limit => limit).compact.collect{|id| find(id)}
199
239
  else
200
- $redis.zrangebyscore("#{model_name}:ids", 0, Time.now.to_f, :limit => limit).compact.collect{|id| find(id)}
240
+ $redis.zrangebyscore(ids_key, 0, order_max_limit, :limit => limit).compact.collect{|id| find(id)}
201
241
  end
202
242
  end
203
243
  end
@@ -235,6 +275,15 @@ module RedisOrm
235
275
  end
236
276
  end
237
277
 
278
+ def find!(*args)
279
+ result = find(*args)
280
+ if result.nil?
281
+ raise RecordNotFound
282
+ else
283
+ result
284
+ end
285
+ end
286
+
238
287
  def after_save(callback)
239
288
  @@callbacks[model_name][:after_save] << callback
240
289
  end
@@ -262,9 +311,19 @@ module RedisOrm
262
311
  def create(options = {})
263
312
  obj = new(options, nil, false)
264
313
  obj.save
314
+
315
+ # make possible binding related models while creating class instance
316
+ options.each do |k, v|
317
+ if @@associations[model_name].detect{|h| h[:foreign_model] == k || h[:options][:as] == k}
318
+ obj.send("#{k}=", v)
319
+ end
320
+ end
321
+
265
322
  obj
266
323
  end
267
324
 
325
+ alias :create! :create
326
+
268
327
  # dynamic finders
269
328
  def method_missing(method_name, *args, &block)
270
329
  if method_name =~ /^find_(all_)?by_(\w*)/
@@ -276,7 +335,7 @@ module RedisOrm
276
335
  properties.each_with_index do |prop, i|
277
336
  properties_hash.merge!({prop.to_sym => args[i]})
278
337
  end
279
- find_index(properties)
338
+ find_indices(properties, :first => true)
280
339
  end
281
340
 
282
341
  raise NotIndexFound if !index
@@ -314,7 +373,7 @@ module RedisOrm
314
373
  def to_a
315
374
  [self]
316
375
  end
317
-
376
+
318
377
  # is called from RedisOrm::Associations::HasMany to save backlinks to saved records
319
378
  def get_associations
320
379
  @@associations[self.model_name]
@@ -328,6 +387,7 @@ module RedisOrm
328
387
  def initialize(attributes = {}, id = nil, persisted = false)
329
388
  @persisted = persisted
330
389
 
390
+ # if this model uses uuid then id is a string otherwise it should be casted to Integer class
331
391
  id = @@use_uuid_as_id[model_name] ? id : id.to_i
332
392
 
333
393
  instance_variable_set(:"@id", id) if id
@@ -353,6 +413,31 @@ module RedisOrm
353
413
  @id
354
414
  end
355
415
 
416
+ alias :to_key :id
417
+
418
+ def to_s
419
+ inspected = "<#{model_name.capitalize} id: #{@id}, "
420
+ inspected += @@properties[model_name].inject([]) do |sum, prop|
421
+ property_value = instance_variable_get(:"@#{prop[:name]}")
422
+ property_value = '"' + property_value.to_s + '"' if prop[:class].eql?("String")
423
+ property_value = 'nil' if property_value.nil?
424
+ sum << "#{prop[:name]}: " + property_value.to_s
425
+ end.join(', ')
426
+ inspected += ">"
427
+ inspected
428
+ end
429
+
430
+ def ==(other)
431
+ raise "this object could be comparable only with object of the same class" if other.class != self.class
432
+ same = true
433
+ @@properties[model_name].each do |prop|
434
+ self_var = instance_variable_get(:"@#{prop[:name]}")
435
+ same = false if other.send(prop[:name]).to_s != self_var.to_s
436
+ end
437
+ same = false if self.id != other.id
438
+ same
439
+ end
440
+
356
441
  def persisted?
357
442
  @persisted
358
443
  end
@@ -380,6 +465,14 @@ module RedisOrm
380
465
  prev_prop_value = instance_variable_get(:"@#{prop[:name]}_changes").first
381
466
  prop_value = instance_variable_get(:"@#{prop[:name]}")
382
467
 
468
+ if prop[:options][:sortable]
469
+ $redis.zrem "#{model_name}:#{prop[:name]}_ids", @id
470
+ # remove id from every indexed property
471
+ @@indices[model_name].each do |index|
472
+ $redis.zrem "#{construct_prepared_index(index)}:#{prop[:name]}_ids", @id
473
+ end
474
+ end
475
+
383
476
  indices = @@indices[model_name].inject([]) do |sum, models_index|
384
477
  if models_index[:name].is_a?(Array)
385
478
  if models_index[:name].include?(prop[:name])
@@ -475,6 +568,23 @@ module RedisOrm
475
568
  if prop_changes && prop_changes.size > 2
476
569
  instance_variable_set :"@#{prop[:name]}_changes", [prop_changes.last]
477
570
  end
571
+
572
+ # if some property need to be sortable add id of the record to the appropriate sorted set
573
+ if prop[:options][:sortable]
574
+ property_value = instance_variable_get(:"@#{prop[:name]}")
575
+ score = case prop[:class]
576
+ when "Integer"; property_value.to_f
577
+ when "Float"; property_value.to_f
578
+ when "String"; calculate_key_for_zset(property_value)
579
+ when "RedisOrm::Boolean"; (property_value == true ? 1.0 : 0.0)
580
+ when "Time"; property_value.to_f
581
+ end
582
+ $redis.zadd "#{model_name}:#{prop[:name]}_ids", score, @id
583
+ # add to every indexed property
584
+ @@indices[model_name].each do |index|
585
+ $redis.zadd "#{construct_prepared_index(index)}:#{prop[:name]}_ids", score, @id
586
+ end
587
+ end
478
588
  end
479
589
 
480
590
  # save new indices in order to sort by finders
@@ -531,13 +641,16 @@ module RedisOrm
531
641
  if !@@associations[model_name].empty?
532
642
  @@associations[model_name].each do |assoc|
533
643
  if :belongs_to == assoc[:type]
534
- if !self.send(assoc[:foreign_model]).nil?
644
+ # if assoc has :as option
645
+ foreign_model_name = assoc[:options][:as] ? assoc[:options][:as].to_sym : assoc[:foreign_model].to_sym
646
+
647
+ if !self.send(foreign_model_name).nil?
535
648
  @@indices[model_name].each do |index|
536
649
  keys_to_delete = if index[:name].is_a?(Array)
537
650
  full_index = index[:name].inject([]){|sum, index_part| sum << index_part}.join(':')
538
- $redis.keys "#{assoc[:foreign_model]}:#{self.send(assoc[:foreign_model]).id}:#{model_name.to_s.pluralize}:#{full_index}:*"
651
+ $redis.keys "#{foreign_model_name}:#{self.send(foreign_model_name).id}:#{model_name.to_s.pluralize}:#{full_index}:*"
539
652
  else
540
- ["#{assoc[:foreign_model]}:#{self.send(assoc[:foreign_model]).id}:#{model_name.to_s.pluralize}:#{index[:name]}:#{self.send(index[:name])}"]
653
+ ["#{foreign_model_name}:#{self.send(foreign_model_name).id}:#{model_name.to_s.pluralize}:#{index[:name]}:#{self.send(index[:name])}"]
541
654
  end
542
655
  keys_to_delete.each do |key|
543
656
  index[:options][:unique] ? $redis.del(key) : $redis.zrem(key, @id)
@@ -0,0 +1,12 @@
1
+ module RedisOrm
2
+ module Utils
3
+ def calculate_key_for_zset(string)
4
+ return 0.0 if string.nil?
5
+ sum = ""
6
+ string.codepoints.each do |codepoint|
7
+ sum += ("%05i" % codepoint.to_s) # 5 because 65536 => 2 bytes UTF-8
8
+ end
9
+ "0.#{sum}".to_f
10
+ end
11
+ end
12
+ end
data/redis_orm.gemspec CHANGED
@@ -2,22 +2,22 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{redis_orm}
5
- s.version = "0.5.1"
5
+ s.version = "0.6"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Dmitrii Samoilov"]
9
- s.date = %q{2011-07-27}
9
+ s.date = %q{2011-09-12}
10
10
  s.description = %q{ORM for Redis (advanced key-value storage) with ActiveRecord API}
11
11
  s.email = %q{germaninthetown@gmail.com}
12
- s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.md", "lib/redis_orm.rb", "lib/redis_orm/active_model_behavior.rb", "lib/redis_orm/associations/belongs_to.rb", "lib/redis_orm/associations/has_many.rb", "lib/redis_orm/associations/has_many_helper.rb", "lib/redis_orm/associations/has_many_proxy.rb", "lib/redis_orm/associations/has_one.rb", "lib/redis_orm/redis_orm.rb"]
13
- s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.md", "Rakefile", "lib/redis_orm.rb", "lib/redis_orm/active_model_behavior.rb", "lib/redis_orm/associations/belongs_to.rb", "lib/redis_orm/associations/has_many.rb", "lib/redis_orm/associations/has_many_helper.rb", "lib/redis_orm/associations/has_many_proxy.rb", "lib/redis_orm/associations/has_one.rb", "lib/redis_orm/redis_orm.rb", "redis_orm.gemspec", "test/association_indices_test.rb", "test/associations_test.rb", "test/atomicity_test.rb", "test/basic_functionality_test.rb", "test/callbacks_test.rb", "test/changes_array_test.rb", "test/dynamic_finders_test.rb", "test/exceptions_test.rb", "test/has_one_has_many_test.rb", "test/indices_test.rb", "test/options_test.rb", "test/polymorphic_test.rb", "test/redis.conf", "test/test_helper.rb", "test/uuid_as_id_test.rb", "test/validations_test.rb"]
12
+ s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.md", "lib/redis_orm.rb", "lib/redis_orm/active_model_behavior.rb", "lib/redis_orm/associations/belongs_to.rb", "lib/redis_orm/associations/has_many.rb", "lib/redis_orm/associations/has_many_helper.rb", "lib/redis_orm/associations/has_many_proxy.rb", "lib/redis_orm/associations/has_one.rb", "lib/redis_orm/redis_orm.rb", "lib/redis_orm/utils.rb"]
13
+ s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.md", "Rakefile", "lib/redis_orm.rb", "lib/redis_orm/active_model_behavior.rb", "lib/redis_orm/associations/belongs_to.rb", "lib/redis_orm/associations/has_many.rb", "lib/redis_orm/associations/has_many_helper.rb", "lib/redis_orm/associations/has_many_proxy.rb", "lib/redis_orm/associations/has_one.rb", "lib/redis_orm/redis_orm.rb", "lib/redis_orm/utils.rb", "redis_orm.gemspec", "test/association_indices_test.rb", "test/associations_test.rb", "test/atomicity_test.rb", "test/basic_functionality_test.rb", "test/callbacks_test.rb", "test/changes_array_test.rb", "test/dynamic_finders_test.rb", "test/exceptions_test.rb", "test/has_one_has_many_test.rb", "test/indices_test.rb", "test/options_test.rb", "test/order_test.rb", "test/polymorphic_test.rb", "test/redis.conf", "test/test_helper.rb", "test/uuid_as_id_test.rb", "test/validations_test.rb"]
14
14
  s.homepage = %q{https://github.com/german/redis_orm}
15
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Redis_orm", "--main", "README.md"]
16
16
  s.require_paths = ["lib"]
17
17
  s.rubyforge_project = %q{redis_orm}
18
18
  s.rubygems_version = %q{1.6.2}
19
19
  s.summary = %q{ORM for Redis (advanced key-value storage) with ActiveRecord API}
20
- s.test_files = ["test/options_test.rb", "test/dynamic_finders_test.rb", "test/associations_test.rb", "test/validations_test.rb", "test/test_helper.rb", "test/polymorphic_test.rb", "test/uuid_as_id_test.rb", "test/atomicity_test.rb", "test/exceptions_test.rb", "test/association_indices_test.rb", "test/has_one_has_many_test.rb", "test/indices_test.rb", "test/changes_array_test.rb", "test/callbacks_test.rb", "test/basic_functionality_test.rb"]
20
+ s.test_files = ["test/options_test.rb", "test/dynamic_finders_test.rb", "test/associations_test.rb", "test/validations_test.rb", "test/test_helper.rb", "test/polymorphic_test.rb", "test/uuid_as_id_test.rb", "test/atomicity_test.rb", "test/exceptions_test.rb", "test/association_indices_test.rb", "test/has_one_has_many_test.rb", "test/order_test.rb", "test/indices_test.rb", "test/changes_array_test.rb", "test/callbacks_test.rb", "test/basic_functionality_test.rb"]
21
21
 
22
22
  if s.respond_to? :specification_version then
23
23
  s.specification_version = 3
@@ -10,6 +10,27 @@ class Comment < RedisOrm::Base
10
10
  property :body, String
11
11
 
12
12
  belongs_to :article
13
+ has_many :comments, :as => :replies
14
+ belongs_to :comment, :as => :reply_to
15
+ end
16
+
17
+ module BelongsToModelWithinModule
18
+ class Reply < RedisOrm::Base
19
+ property :body, String, :default => "test"
20
+ belongs_to :article, :as => :essay
21
+ end
22
+ end
23
+
24
+ module HasManyModelWithinModule
25
+ class SpecialComment < RedisOrm::Base
26
+ property :body, String, :default => "test"
27
+ belongs_to :brochure, :as => :book
28
+ end
29
+
30
+ class Brochure < RedisOrm::Base
31
+ property :title, String
32
+ has_many :special_comments
33
+ end
13
34
  end
14
35
 
15
36
  class Profile < RedisOrm::Base
@@ -205,9 +226,9 @@ describe "check associations" do
205
226
  @article.categories << [@cat1, @cat2]
206
227
 
207
228
  @cat1.articles.count.should == 1
208
- @cat1.articles[0].id.should == @article.id
229
+ @cat1.articles[0].should == @article
209
230
  @cat2.articles.count.should == 1
210
- @cat2.articles[0].id.should == @article.id
231
+ @cat2.articles[0].should == @article
211
232
 
212
233
  @article.categories.size.should == 2
213
234
  @article.categories.count.should == 2
@@ -218,10 +239,10 @@ describe "check associations" do
218
239
  @article.categories.map{|c| c.id}.include?(@cat3.id).should be
219
240
 
220
241
  @cat1.articles.count.should == 1
221
- @cat1.articles[0].id.should == @article.id
242
+ @cat1.articles[0].should == @article
222
243
 
223
244
  @cat3.articles.count.should == 1
224
- @cat3.articles[0].id.should == @article.id
245
+ @cat3.articles[0].should == @article
225
246
 
226
247
  @cat2.articles.count.should == 0
227
248
 
@@ -303,4 +324,30 @@ describe "check associations" do
303
324
  rf.replay_to.should be
304
325
  rf.replay_to.id.should == Message.first.id
305
326
  end
327
+
328
+ it "should find associations within modules" do
329
+ BelongsToModelWithinModule::Reply.count.should == 0
330
+ essay = Article.create :title => "Red is cluster"
331
+ BelongsToModelWithinModule::Reply.create :essay => essay
332
+ BelongsToModelWithinModule::Reply.count.should == 1
333
+ reply = BelongsToModelWithinModule::Reply.last
334
+ reply.essay.should == essay
335
+
336
+ HasManyModelWithinModule::SpecialComment.count.should == 0
337
+ book = HasManyModelWithinModule::Brochure.create :title => "Red is unstable"
338
+ HasManyModelWithinModule::SpecialComment.create :book => book
339
+ HasManyModelWithinModule::Brochure.count.should == 1
340
+ HasManyModelWithinModule::SpecialComment.count.should == 1
341
+ end
342
+
343
+ it "should properly handle self-referencing model both belongs_to and has_many/has_one associations" do
344
+ comment1 = Comment.create :body => "comment1"
345
+ comment11 = Comment.create :body => "comment1.1"
346
+ comment12 = Comment.create :body => "comment1.2"
347
+
348
+ comment1.replies = [comment11, comment12]
349
+ comment1.replies.count.should == 2
350
+ comment11.reply_to.should == comment1
351
+ comment12.reply_to.should == comment1
352
+ end
306
353
  end
@@ -1,50 +1,30 @@
1
1
  require File.dirname(File.expand_path(__FILE__)) + '/test_helper.rb'
2
2
 
3
3
  class Article < RedisOrm::Base
4
+ use_uuid_as_id
5
+
4
6
  property :title, String
5
7
  property :karma, Integer
6
8
  end
7
9
 
8
10
  describe "check atomicity" do
9
- =begin
10
11
  it "should properly increment property's value" do
11
- @article = Article.new :title => "Simple test atomicity with multiple threads", :karma => 1
12
- @article.save
12
+ article = Article.new :title => "Simple test atomicity with multiple threads", :karma => 1
13
+ article.save
13
14
 
14
- @threads = []
15
-
16
- 50.times do |i|
17
- @threads << Thread.new(i) do
18
- sleep(0.2)
19
- @article.update_attribute :karma, (@article.karma + 1)
20
- end
21
- end
22
-
23
- @threads.each{|thread| thread.join}
24
-
25
- Article.first.karma.should == 51
26
- end
27
- =end
28
- it "should properly increment property's value" do
29
15
  threads = []
30
16
 
31
17
  50.times do |i|
32
- id = i
33
18
  threads << Thread.new(i) do
34
- if id % 2 == 0
35
- art = Article.create :title => "article ##{id}", :karma => id
36
- puts "article.last.id - #{art.id}, article.last.karma - #{art.karma}"
37
- else
38
- puts "id - #{id}, (id / 2) + 1 - #{(id / 2) + 1}"
39
- Article.find((id / 2) + 1).destroy
40
- end
19
+ article.update_attribute :karma, (article.karma + 1)
41
20
  end
42
21
  end
43
22
 
44
23
  threads.each{|thread| thread.join}
45
- Article.count.should == 0
24
+
25
+ Article.first.karma.should == 51
46
26
  end
47
- =begin
27
+
48
28
  it "should properly increment/decrement property's value" do
49
29
  article = Article.create :title => "article #1", :karma => 10
50
30
  threads = []
@@ -60,5 +40,4 @@ describe "check atomicity" do
60
40
  threads.each{|thread| thread.join}
61
41
  article.karma.should == 15
62
42
  end
63
- =end
64
43
  end
@@ -25,7 +25,23 @@ class TimeStamp < RedisOrm::Base
25
25
  timestamps
26
26
  end
27
27
 
28
+ class Person;end
29
+
28
30
  describe "check basic functionality" do
31
+ it "should have 3 models in descendants" do
32
+ RedisOrm::Base.descendants.should include(User, DefaultUser, TimeStamp)
33
+ RedisOrm::Base.descendants.should_not include(Person)
34
+ end
35
+
36
+ it "should return the same user" do
37
+ user = User.new :name => "german"
38
+ user.save
39
+ User.first.should == user
40
+
41
+ user.name = "Anderson"
42
+ User.first.should_not == user
43
+ end
44
+
29
45
  it "test_simple_creation" do
30
46
  User.count.should == 0
31
47
 
@@ -41,5 +41,10 @@ describe "exceptions test" do
41
41
 
42
42
  # RedisOrm::TypeMismatchError
43
43
  lambda { user.profile = jigsaw }.should raise_error
44
- end
44
+ end
45
+
46
+ it "should raise an exception if there is no such record in the storage" do
47
+ User.find(12).should == nil
48
+ lambda{ User.find! 12 }.should raise_error(RedisOrm::RecordNotFound)
49
+ end
45
50
  end
data/test/options_test.rb CHANGED
@@ -59,6 +59,17 @@ describe "test options" do
59
59
  @photo2.image_type.should == "png"
60
60
  end
61
61
 
62
+ it "should behave like expected for #find and #find! methods (nb exceptions with #find! are tested in exceptions_test.rb file)" do
63
+ Album.find(@album.id).should == @album
64
+ Album.find!(@album.id).should == @album
65
+
66
+ Album.find(:first).should == @album
67
+ Album.find!(:first).should == @album
68
+
69
+ Album.find(:all, :limit => 1).size.should == 1
70
+ Album.find!(:all, :limit => 1).size.should == 1
71
+ end
72
+
62
73
  it "should return correct array when :limit and :offset options are provided" do
63
74
  @album.photos.count.should == 0
64
75
 
@@ -99,6 +110,14 @@ describe "test options" do
99
110
  Photo.find(:last, :conditions => {:image => "boobs.png", :image_type => "png"}).id.should == @photo2.id
100
111
  end
101
112
 
113
+ it "should accept options for #first and #last methods" do
114
+ Photo.first(:conditions => {:image => "facepalm.jpg"}).id.should == @photo1.id
115
+ Photo.first(:conditions => {:image => "boobs.png"}).id.should == @photo2.id
116
+
117
+ Photo.last(:conditions => {:image => "facepalm.jpg", :image_type => "jpg"}).id.should == @photo1.id
118
+ Photo.last(:conditions => {:image => "boobs.png", :image_type => "png"}).id.should == @photo2.id
119
+ end
120
+
102
121
  it "should correctly save boolean values" do
103
122
  $redis.hgetall("photo:#{@photo1.id}")["inverted"].should == "true"
104
123
  $redis.hgetall("photo:#{@photo2.id}")["inverted"].should == "false"
@@ -0,0 +1,69 @@
1
+ require File.dirname(File.expand_path(__FILE__)) + '/test_helper.rb'
2
+
3
+ class User < RedisOrm::Base
4
+ property :name, String, :sortable => true
5
+ property :age, Integer, :sortable => true
6
+ property :wage, Float, :sortable => true
7
+
8
+ property :address, String
9
+
10
+ index :name
11
+ index :age
12
+ end
13
+
14
+ describe "test options" do
15
+ before(:each) do
16
+ @dan = User.create :name => "Daniel", :age => 26, :wage => 40000.0, :address => "Bellevue"
17
+ @abe = User.create :name => "Abe", :age => 30, :wage => 100000.0, :address => "Bellevue"
18
+ @michael = User.create :name => "Michael", :age => 25, :wage => 60000.0, :address => "Bellevue"
19
+ @todd = User.create :name => "Todd", :age => 22, :wage => 30000.0, :address => "Bellevue"
20
+ end
21
+
22
+ it "should return records in specified order" do
23
+ $redis.zcard("user:name_ids").to_i.should == User.count
24
+ $redis.zcard("user:age_ids").to_i.should == User.count
25
+ $redis.zcard("user:wage_ids").to_i.should == User.count
26
+
27
+ User.find(:all, :order => [:name, :asc]).should == [@abe, @dan, @michael, @todd]
28
+ User.find(:all, :order => [:name, :desc]).should == [@todd, @michael, @dan, @abe]
29
+
30
+ User.find(:all, :order => [:age, :asc]).should == [@todd, @michael, @dan, @abe]
31
+ User.find(:all, :order => [:age, :desc]).should == [@abe, @dan, @michael, @todd]
32
+
33
+ User.find(:all, :order => [:wage, :asc]).should == [@todd, @dan, @michael, @abe]
34
+ User.find(:all, :order => [:wage, :desc]).should == [@abe, @michael, @dan, @todd]
35
+ end
36
+
37
+ it "should return records which met specified conditions in specified order" do
38
+ @abe2 = User.create :name => "Abe", :age => 12, :wage => 10.0, :address => "Santa Fe"
39
+
40
+ # :asc should be default value for property in :order clause
41
+ User.find(:all, :conditions => {:name => "Abe"}, :order => [:wage]).should == [@abe2, @abe]
42
+
43
+ User.find(:all, :conditions => {:name => "Abe"}, :order => [:wage, :desc]).should == [@abe, @abe2]
44
+ User.find(:all, :conditions => {:name => "Abe"}, :order => [:wage, :asc]).should == [@abe2, @abe]
45
+
46
+ User.find(:all, :conditions => {:name => "Abe"}, :order => [:age, :desc]).should == [@abe, @abe2]
47
+ User.find(:all, :conditions => {:name => "Abe"}, :order => [:age, :asc]).should == [@abe2, @abe]
48
+
49
+ User.find(:all, :conditions => {:name => "Abe"}, :order => [:wage, :desc]).should == [@abe, @abe2]
50
+ User.find(:all, :conditions => {:name => "Abe"}, :order => [:wage, :asc]).should == [@abe2, @abe]
51
+ end
52
+
53
+ it "should update keys after the persisted object was edited and sort properly" do
54
+ @abe.update_attributes :name => "Zed", :age => 12, :wage => 10.0, :address => "Santa Fe"
55
+
56
+ $redis.zcard("user:name_ids").to_i.should == User.count
57
+ $redis.zcard("user:age_ids").to_i.should == User.count
58
+ $redis.zcard("user:wage_ids").to_i.should == User.count
59
+
60
+ User.find(:all, :order => [:name, :asc]).should == [@dan, @michael, @todd, @abe]
61
+ User.find(:all, :order => [:name, :desc]).should == [@abe, @todd, @michael, @dan]
62
+
63
+ User.find(:all, :order => [:age, :asc]).should == [@abe, @todd, @michael, @dan]
64
+ User.find(:all, :order => [:age, :desc]).should == [@dan, @michael, @todd, @abe]
65
+
66
+ User.find(:all, :order => [:wage, :asc]).should == [@abe, @todd, @dan, @michael]
67
+ User.find(:all, :order => [:wage, :desc]).should == [@michael, @dan, @todd, @abe]
68
+ end
69
+ end
data/test/test_helper.rb CHANGED
@@ -8,7 +8,12 @@ RSpec.configure do |config|
8
8
  $redis_pid = spawn 'redis-server ' + path_to_conf, :out => "/dev/null"
9
9
  sleep(0.3) # must be some delay otherwise "Connection refused - Unable to connect to Redis"
10
10
  path_to_socket = File.dirname(File.expand_path(__FILE__)) + "/../redis.sock"
11
- $redis = Redis.new(:host => 'localhost', :path => path_to_socket)
11
+ begin
12
+ $redis = Redis.new(:host => 'localhost', :path => path_to_socket)
13
+ rescue => e
14
+ puts 'Unable to create connection to the redis server: ' + e.message.inspect
15
+ Process.kill 9, $redis_pid.to_i if $redis_pid
16
+ end
12
17
  end
13
18
 
14
19
  config.after(:all) do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis_orm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: '0.6'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-07-27 00:00:00.000000000 +03:00
12
+ date: 2011-09-12 00:00:00.000000000 +03:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
- requirement: &87829630 !ruby/object:Gem::Requirement
17
+ requirement: &74101100 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 3.0.0
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *87829630
25
+ version_requirements: *74101100
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: activemodel
28
- requirement: &87829290 !ruby/object:Gem::Requirement
28
+ requirement: &74100700 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 3.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *87829290
36
+ version_requirements: *74100700
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: redis
39
- requirement: &87828950 !ruby/object:Gem::Requirement
39
+ requirement: &74100190 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 2.2.0
45
45
  type: :runtime
46
46
  prerelease: false
47
- version_requirements: *87828950
47
+ version_requirements: *74100190
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: uuid
50
- requirement: &87828600 !ruby/object:Gem::Requirement
50
+ requirement: &74099680 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: 2.3.2
56
56
  type: :runtime
57
57
  prerelease: false
58
- version_requirements: *87828600
58
+ version_requirements: *74099680
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: rspec
61
- requirement: &87828250 !ruby/object:Gem::Requirement
61
+ requirement: &74099250 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ! '>='
@@ -66,7 +66,7 @@ dependencies:
66
66
  version: 2.5.0
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *87828250
69
+ version_requirements: *74099250
70
70
  description: ORM for Redis (advanced key-value storage) with ActiveRecord API
71
71
  email: germaninthetown@gmail.com
72
72
  executables: []
@@ -83,6 +83,7 @@ extra_rdoc_files:
83
83
  - lib/redis_orm/associations/has_many_proxy.rb
84
84
  - lib/redis_orm/associations/has_one.rb
85
85
  - lib/redis_orm/redis_orm.rb
86
+ - lib/redis_orm/utils.rb
86
87
  files:
87
88
  - CHANGELOG
88
89
  - LICENSE
@@ -97,6 +98,7 @@ files:
97
98
  - lib/redis_orm/associations/has_many_proxy.rb
98
99
  - lib/redis_orm/associations/has_one.rb
99
100
  - lib/redis_orm/redis_orm.rb
101
+ - lib/redis_orm/utils.rb
100
102
  - redis_orm.gemspec
101
103
  - test/association_indices_test.rb
102
104
  - test/associations_test.rb
@@ -109,6 +111,7 @@ files:
109
111
  - test/has_one_has_many_test.rb
110
112
  - test/indices_test.rb
111
113
  - test/options_test.rb
114
+ - test/order_test.rb
112
115
  - test/polymorphic_test.rb
113
116
  - test/redis.conf
114
117
  - test/test_helper.rb
@@ -157,6 +160,7 @@ test_files:
157
160
  - test/exceptions_test.rb
158
161
  - test/association_indices_test.rb
159
162
  - test/has_one_has_many_test.rb
163
+ - test/order_test.rb
160
164
  - test/indices_test.rb
161
165
  - test/changes_array_test.rb
162
166
  - test/callbacks_test.rb