redis_orm 0.6 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ v0.6.1 [05-12-2011]
2
+ * rewritten sortable functionality for attributes which values are strings
3
+ * added Gemfile to the project, improved tests
4
+
1
5
  v0.6 [12-09-2011]
2
6
  * added equality operator for object, #to_s method for inspecting objects, #find! which could throw RecordNotFound error
3
7
  * added self.descendants class method which returns all inherited from RedisOrm::Base classes
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'activemodel', '>= 3.0.0'
4
+ gem 'activesupport', '>= 3.0.0'
5
+ gem 'redis', '>= 2.2.0'
6
+ gem 'uuid', '>= 2.3.2'
7
+
8
+ gem 'rspec', '>= 2.5.0'
data/Manifest CHANGED
@@ -1,8 +1,10 @@
1
1
  CHANGELOG
2
+ Gemfile
2
3
  LICENSE
3
4
  Manifest
4
5
  README.md
5
6
  Rakefile
7
+ benchmarks/sortable_benchmark.rb
6
8
  lib/redis_orm.rb
7
9
  lib/redis_orm/active_model_behavior.rb
8
10
  lib/redis_orm/associations/belongs_to.rb
@@ -24,9 +26,9 @@ test/exceptions_test.rb
24
26
  test/has_one_has_many_test.rb
25
27
  test/indices_test.rb
26
28
  test/options_test.rb
27
- test/order_test.rb
28
29
  test/polymorphic_test.rb
29
30
  test/redis.conf
31
+ test/sortable_test.rb
30
32
  test/test_helper.rb
31
33
  test/uuid_as_id_test.rb
32
34
  test/validations_test.rb
data/README.md CHANGED
@@ -29,15 +29,25 @@ end
29
29
 
30
30
  ## Installing redis_orm
31
31
 
32
+ stable release:
33
+
34
+ ```sh
32
35
  gem install redis_orm
36
+ ```
33
37
 
34
- or
38
+ or edge version:
35
39
 
40
+ ```sh
36
41
  git clone git://github.com/german/redis_orm.git
37
-
38
42
  cd redis_orm
39
-
40
43
  bundle install
44
+ ```
45
+
46
+ To run the tests you should have redis installed already. Please check [Redis download/installation page](http://redis.io/download).
47
+
48
+ ```sh
49
+ rake test
50
+ ```
41
51
 
42
52
  ## Setting up a connection to the redis server
43
53
 
@@ -84,6 +94,8 @@ Following options are available in property declaration:
84
94
 
85
95
  if *true* is specified then you could sort records by this property later
86
96
 
97
+ *Note* that when you're using :sortable option redis_orm maintains one additional list per attribute. Also note that the #create method could be 3 times slower in some cases (this will be improved in future releases), while the #find performance is basically the same (see the "benchmarks/sortable_benchmark.rb").
98
+
87
99
  ## Searching records by the value
88
100
 
89
101
  Usually it's done via declaring an index and using *:conditions* hash or dynamic finders. For example:
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'rake'
4
4
  #=begin
5
5
  require 'echoe'
6
6
 
7
- Echoe.new('redis_orm', '0.6') do |p|
7
+ Echoe.new('redis_orm', '0.6.1') 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,13 +26,8 @@ Rake::TestTask.new(:test) do |t|
26
26
  end
27
27
  =end
28
28
 
29
- #require 'rspec/core/rake_task'
30
29
  task :test do |t|
31
30
  Dir['test/**/*_test.rb'].each do |file|
32
31
  puts `ruby -I./lib #{file}`
33
32
  end
34
33
  end
35
- #task :default => :test
36
- #RSpec::Core::RakeTask.new(:spec) do |t|
37
- # t.pattern = 'test/**/*_test.rb'
38
- #end
@@ -0,0 +1,45 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/redis_orm')
2
+ require 'benchmark'
3
+
4
+ class User < RedisOrm::Base
5
+ property :name, String
6
+ property :age, Integer
7
+ property :wage, Float
8
+
9
+ index :name
10
+ index :age
11
+ end
12
+
13
+ class SortableUser < RedisOrm::Base
14
+ property :name, String, :sortable => true
15
+ property :age, Integer, :sortable => true
16
+ property :wage, Float, :sortable => true
17
+
18
+ index :name
19
+ index :age
20
+ end
21
+
22
+ path_to_conf = File.dirname(File.expand_path(__FILE__)) + "/../test/redis.conf"
23
+ $redis_pid = spawn 'redis-server ' + path_to_conf, :out => "/dev/null"
24
+ sleep(0.3) # must be some delay otherwise "Connection refused - Unable to connect to Redis"
25
+ path_to_socket = File.dirname(File.expand_path(__FILE__)) + "/../redis.sock"
26
+ begin
27
+ $redis = Redis.new(:host => 'localhost', :path => path_to_socket)
28
+ rescue => e
29
+ puts 'Unable to create connection to the redis server: ' + e.message.inspect
30
+ Process.kill 9, $redis_pid.to_i if $redis_pid
31
+ end
32
+
33
+ n = 100
34
+ Benchmark.bmbm do |x|
35
+ x.report("creating regular user:") { for i in 1..n; u = User.create(:name => "user#{i}", :age => i, :wage => 100*i); end}
36
+ x.report("creating user w/ sortable attrs:") { for i in 1..n; u = SortableUser.create(:name => "user#{i}", :age => i, :wage => 100*i); end }
37
+ end
38
+
39
+ Benchmark.bmbm do |x|
40
+ x.report("finding regular users:") { User.find(:all, :limit => 5, :offset => 10) }
41
+ x.report("finding users w/ sortable attrs:") { SortableUser.find(:all, :limit => 5, :offset => 10, :order => [:name, :asc]) }
42
+ end
43
+
44
+ $redis.flushall if $redis
45
+ Process.kill 9, $redis_pid.to_i if $redis_pid
@@ -193,6 +193,8 @@ module RedisOrm
193
193
  [options[:offset].to_i, options[:limit].to_i]
194
194
  elsif options[:limit]
195
195
  [0, options[:limit].to_i]
196
+ else
197
+ [0, -1]
196
198
  end
197
199
 
198
200
  order_max_limit = Time.now.to_f
@@ -214,30 +216,66 @@ module RedisOrm
214
216
  end
215
217
  end
216
218
 
219
+ order_by_property_is_string = false
220
+
217
221
  # if not array => created_at native order (in which ids were pushed to "#{model_name}:ids" set by default)
218
222
  direction = if !options[:order].blank?
219
- if options[:order].is_a?(Array)
223
+ property = {}
224
+ dir = if options[:order].is_a?(Array)
225
+ property = @@properties[model_name].detect{|prop| prop[:name].to_s == options[:order].first.to_s}
220
226
  # for String values max limit for search key could be 1.0, but for Numeric values there's actually no limit
221
227
  order_max_limit = 100_000_000_000
222
228
  ids_key = "#{prepared_index}:#{options[:order].first}_ids"
223
229
  options[:order].size == 2 ? options[:order].last : 'asc'
224
230
  else
231
+ property = @@properties[model_name].detect{|prop| prop[:name].to_s == options[:order].to_s}
225
232
  ids_key = prepared_index
226
233
  options[:order]
227
234
  end
235
+ if property && property[:class].eql?("String") && property[:options][:sortable]
236
+ order_by_property_is_string = true
237
+ end
238
+ dir
228
239
  else
229
240
  ids_key = prepared_index
230
241
  'asc'
231
242
  end
232
243
 
233
- if index && index[:options][:unique]
234
- id = $redis.get prepared_index
235
- model_name.to_s.camelize.constantize.find(id)
236
- else
244
+ if order_by_property_is_string
237
245
  if direction.to_s == 'desc'
238
- $redis.zrevrangebyscore(ids_key, order_max_limit, 0, :limit => limit).compact.collect{|id| find(id)}
246
+ ids_length = $redis.llen(ids_key)
247
+ limit = if options[:offset] && options[:limit]
248
+ [(ids_length - options[:offset].to_i - options[:limit].to_i), (ids_length - options[:offset].to_i - 1)]
249
+ elsif options[:limit]
250
+ [ids_length - options[:limit].to_i, ids_length]
251
+ elsif options[:offset]
252
+ [0, (ids_length - options[:offset].to_i - 1)]
253
+ else
254
+ [0, -1]
255
+ end
256
+ $redis.lrange(ids_key, *limit).reverse.compact.collect{|id| find(id.split(':').last)}
257
+ else
258
+ limit = if options[:offset] && options[:limit]
259
+ [options[:offset].to_i, (options[:offset].to_i + options[:limit].to_i)]
260
+ elsif options[:limit]
261
+ [0, options[:limit].to_i - 1]
262
+ elsif options[:offset]
263
+ [options[:offset].to_i, -1]
264
+ else
265
+ [0, -1]
266
+ end
267
+ $redis.lrange(ids_key, *limit).compact.collect{|id| find(id.split(':').last)}
268
+ end
269
+ else
270
+ if index && index[:options][:unique]
271
+ id = $redis.get prepared_index
272
+ model_name.to_s.camelize.constantize.find(id)
239
273
  else
240
- $redis.zrangebyscore(ids_key, 0, order_max_limit, :limit => limit).compact.collect{|id| find(id)}
274
+ if direction.to_s == 'desc'
275
+ $redis.zrevrangebyscore(ids_key, order_max_limit, 0, :limit => limit).compact.collect{|id| find(id)}
276
+ else
277
+ $redis.zrangebyscore(ids_key, 0, order_max_limit, :limit => limit).compact.collect{|id| find(id)}
278
+ end
241
279
  end
242
280
  end
243
281
  end
@@ -464,12 +502,20 @@ module RedisOrm
464
502
 
465
503
  prev_prop_value = instance_variable_get(:"@#{prop[:name]}_changes").first
466
504
  prop_value = instance_variable_get(:"@#{prop[:name]}")
467
-
505
+ # TODO DRY in destroy also
468
506
  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
507
+ if prop[:class].eql?("String")
508
+ $redis.lrem "#{model_name}:#{prop[:name]}_ids", 1, "#{prev_prop_value}:#{@id}"
509
+ # remove id from every indexed property
510
+ @@indices[model_name].each do |index|
511
+ $redis.lrem "#{construct_prepared_index(index)}:#{prop[:name]}_ids", 1, "#{prop_value}:#{@id}"
512
+ end
513
+ else
514
+ $redis.zrem "#{model_name}:#{prop[:name]}_ids", @id
515
+ # remove id from every indexed property
516
+ @@indices[model_name].each do |index|
517
+ $redis.zrem "#{construct_prepared_index(index)}:#{prop[:name]}_ids", @id
518
+ end
473
519
  end
474
520
  end
475
521
 
@@ -571,18 +617,28 @@ module RedisOrm
571
617
 
572
618
  # if some property need to be sortable add id of the record to the appropriate sorted set
573
619
  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
620
+ property_value = instance_variable_get(:"@#{prop[:name]}").to_s
621
+ if prop[:class].eql?("String")
622
+ sortable_key = "#{model_name}:#{prop[:name]}_ids"
623
+ el_or_position_to_insert = find_position_to_insert(sortable_key, property_value)
624
+ el_or_position_to_insert == 0 ? $redis.lpush(sortable_key, "#{property_value}:#{@id}") : $redis.linsert(sortable_key, "AFTER", el_or_position_to_insert, "#{property_value}:#{@id}")
625
+ # add to every indexed property
626
+ @@indices[model_name].each do |index|
627
+ sortable_key = "#{construct_prepared_index(index)}:#{prop[:name]}_ids"
628
+ el_or_position_to_insert == 0 ? $redis.lpush(sortable_key, "#{property_value}:#{@id}") : $redis.linsert(sortable_key, "AFTER", el_or_position_to_insert, "#{property_value}:#{@id}")
629
+ end
630
+ else
631
+ score = case prop[:class]
632
+ when "Integer"; property_value.to_f
633
+ when "Float"; property_value.to_f
634
+ when "RedisOrm::Boolean"; (property_value == true ? 1.0 : 0.0)
635
+ when "Time"; property_value.to_f
636
+ end
637
+ $redis.zadd "#{model_name}:#{prop[:name]}_ids", score, @id
638
+ # add to every indexed property
639
+ @@indices[model_name].each do |index|
640
+ $redis.zadd "#{construct_prepared_index(index)}:#{prop[:name]}_ids", score, @id
641
+ end
586
642
  end
587
643
  end
588
644
  end
@@ -612,6 +668,47 @@ module RedisOrm
612
668
  true # if there were no errors just return true, so *if* conditions would work
613
669
  end
614
670
 
671
+ def find_position_to_insert(sortable_key, value)
672
+ end_index = $redis.llen(sortable_key)
673
+
674
+ return 0 if end_index == 0
675
+
676
+ start_index = 0
677
+ pivot_index = end_index / 2
678
+
679
+ start_el = $redis.lindex(sortable_key, start_index)
680
+ end_el = $redis.lindex(sortable_key, end_index - 1)
681
+ pivot_el = $redis.lindex(sortable_key, pivot_index)
682
+
683
+ while start_index != end_index
684
+ # aa..ab..ac..bd <- ad
685
+ if start_el.split(':').first > value # Michael > Abe
686
+ return 0
687
+ elsif end_el.split(':').first < value # Abe < Todd
688
+ return end_el
689
+ elsif start_el.split(':').first == value # Abe == Abe
690
+ return start_el
691
+ elsif pivot_el.split(':').first == value # Todd == Todd
692
+ return pivot_el
693
+ elsif end_el.split(':').first == value
694
+ return end_el
695
+ elsif (start_el.split(':').first < value) && (pivot_el.split(':').first > value)
696
+ start_index = start_index
697
+ prev_pivot_index = pivot_index
698
+ pivot_index = start_index + ((end_index - pivot_index) / 2)
699
+ end_index = prev_pivot_index
700
+ elsif (pivot_el.split(':').first < value) && (end_el.split(':').first > value) # M < V && Y > V
701
+ start_index = pivot_index
702
+ pivot_index = pivot_index + ((end_index - pivot_index) / 2)
703
+ end_index = end_index
704
+ end
705
+ start_el = $redis.lindex(sortable_key, start_index)
706
+ end_el = $redis.lindex(sortable_key, end_index - 1)
707
+ pivot_el = $redis.lindex(sortable_key, pivot_index)
708
+ end
709
+ start_el
710
+ end
711
+
615
712
  def update_attributes(attributes)
616
713
  if attributes.is_a?(Hash)
617
714
  attributes.each do |key, value|
@@ -632,7 +729,16 @@ module RedisOrm
632
729
  end
633
730
 
634
731
  @@properties[model_name].each do |prop|
732
+ property_value = instance_variable_get(:"@#{prop[:name]}").to_s
635
733
  $redis.hdel("#{model_name}:#{@id}", prop[:name].to_s)
734
+
735
+ if prop[:options][:sortable]
736
+ if prop[:class].eql?("String")
737
+ $redis.lrem "#{model_name}:#{prop[:name]}_ids", 1, "#{property_value}:#{@id}"
738
+ else
739
+ $redis.zrem "#{model_name}:#{prop[:name]}_ids", @id
740
+ end
741
+ end
636
742
  end
637
743
 
638
744
  $redis.zrem "#{model_name}:ids", @id
@@ -2,22 +2,22 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{redis_orm}
5
- s.version = "0.6"
5
+ s.version = "0.6.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
- s.authors = ["Dmitrii Samoilov"]
9
- s.date = %q{2011-09-12}
8
+ s.authors = [%q{Dmitrii Samoilov}]
9
+ s.date = %q{2011-12-05}
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", "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"]
12
+ s.extra_rdoc_files = [%q{CHANGELOG}, %q{LICENSE}, %q{README.md}, %q{lib/redis_orm.rb}, %q{lib/redis_orm/active_model_behavior.rb}, %q{lib/redis_orm/associations/belongs_to.rb}, %q{lib/redis_orm/associations/has_many.rb}, %q{lib/redis_orm/associations/has_many_helper.rb}, %q{lib/redis_orm/associations/has_many_proxy.rb}, %q{lib/redis_orm/associations/has_one.rb}, %q{lib/redis_orm/redis_orm.rb}, %q{lib/redis_orm/utils.rb}]
13
+ s.files = [%q{CHANGELOG}, %q{Gemfile}, %q{LICENSE}, %q{Manifest}, %q{README.md}, %q{Rakefile}, %q{benchmarks/sortable_benchmark.rb}, %q{lib/redis_orm.rb}, %q{lib/redis_orm/active_model_behavior.rb}, %q{lib/redis_orm/associations/belongs_to.rb}, %q{lib/redis_orm/associations/has_many.rb}, %q{lib/redis_orm/associations/has_many_helper.rb}, %q{lib/redis_orm/associations/has_many_proxy.rb}, %q{lib/redis_orm/associations/has_one.rb}, %q{lib/redis_orm/redis_orm.rb}, %q{lib/redis_orm/utils.rb}, %q{redis_orm.gemspec}, %q{test/association_indices_test.rb}, %q{test/associations_test.rb}, %q{test/atomicity_test.rb}, %q{test/basic_functionality_test.rb}, %q{test/callbacks_test.rb}, %q{test/changes_array_test.rb}, %q{test/dynamic_finders_test.rb}, %q{test/exceptions_test.rb}, %q{test/has_one_has_many_test.rb}, %q{test/indices_test.rb}, %q{test/options_test.rb}, %q{test/polymorphic_test.rb}, %q{test/redis.conf}, %q{test/sortable_test.rb}, %q{test/test_helper.rb}, %q{test/uuid_as_id_test.rb}, %q{test/validations_test.rb}]
14
14
  s.homepage = %q{https://github.com/german/redis_orm}
15
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Redis_orm", "--main", "README.md"]
16
- s.require_paths = ["lib"]
15
+ s.rdoc_options = [%q{--line-numbers}, %q{--inline-source}, %q{--title}, %q{Redis_orm}, %q{--main}, %q{README.md}]
16
+ s.require_paths = [%q{lib}]
17
17
  s.rubyforge_project = %q{redis_orm}
18
- s.rubygems_version = %q{1.6.2}
18
+ s.rubygems_version = %q{1.8.6}
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/order_test.rb", "test/indices_test.rb", "test/changes_array_test.rb", "test/callbacks_test.rb", "test/basic_functionality_test.rb"]
20
+ s.test_files = [%q{test/atomicity_test.rb}, %q{test/indices_test.rb}, %q{test/sortable_test.rb}, %q{test/uuid_as_id_test.rb}, %q{test/test_helper.rb}, %q{test/options_test.rb}, %q{test/callbacks_test.rb}, %q{test/exceptions_test.rb}, %q{test/associations_test.rb}, %q{test/validations_test.rb}, %q{test/basic_functionality_test.rb}, %q{test/dynamic_finders_test.rb}, %q{test/changes_array_test.rb}, %q{test/polymorphic_test.rb}, %q{test/association_indices_test.rb}, %q{test/has_one_has_many_test.rb}]
21
21
 
22
22
  if s.respond_to? :specification_version then
23
23
  s.specification_version = 3
@@ -47,4 +47,9 @@ describe "exceptions test" do
47
47
  User.find(12).should == nil
48
48
  lambda{ User.find! 12 }.should raise_error(RedisOrm::RecordNotFound)
49
49
  end
50
+
51
+ it "should throw an exception if there was an error while creating object with #create! method" do
52
+ jigsaw = Jigsaw.create :title => "jigsaw"
53
+ lambda { User.create!(:name => "John", :age => 44, :profile => jigsaw) }.should raise_error(RedisOrm::TypeMismatchError)
54
+ end
50
55
  end
@@ -69,7 +69,7 @@ describe "test options" do
69
69
  Album.find(:all, :limit => 1).size.should == 1
70
70
  Album.find!(:all, :limit => 1).size.should == 1
71
71
  end
72
-
72
+
73
73
  it "should return correct array when :limit and :offset options are provided" do
74
74
  @album.photos.count.should == 0
75
75
 
@@ -88,8 +88,9 @@ describe "test options" do
88
88
  @album.photos.find(:all, :limit => 1, :offset => 1).size.should == 1
89
89
 
90
90
  Photo.find(:all).size.should == 2
91
- Photo.find(:first).id.should == @photo1.id
92
- Photo.find(:last).id.should == @photo2.id
91
+
92
+ Photo.find(:first).should == @photo1
93
+ Photo.find(:last).should == @photo2
93
94
 
94
95
  Photo.find(:all, :conditions => {:image => "facepalm.jpg"}).size.should == 1
95
96
  Photo.find(:all, :conditions => {:image => "boobs.png"}).size.should == 1
@@ -97,25 +98,25 @@ describe "test options" do
97
98
  Photo.find(:all, :conditions => {:image => "facepalm.jpg", :image_type => "jpg"}).size.should == 1
98
99
  Photo.find(:all, :conditions => {:image => "boobs.png", :image_type => "png"}).size.should == 1
99
100
 
100
- Photo.find(:first, :conditions => {:image => "facepalm.jpg"}).id.should == @photo1.id
101
- Photo.find(:first, :conditions => {:image => "boobs.png"}).id.should == @photo2.id
101
+ Photo.find(:first, :conditions => {:image => "facepalm.jpg"}).should == @photo1
102
+ Photo.find(:first, :conditions => {:image => "boobs.png"}).should == @photo2
102
103
 
103
- Photo.find(:first, :conditions => {:image => "facepalm.jpg", :image_type => "jpg"}).id.should == @photo1.id
104
- Photo.find(:first, :conditions => {:image => "boobs.png", :image_type => "png"}).id.should == @photo2.id
104
+ Photo.find(:first, :conditions => {:image => "facepalm.jpg", :image_type => "jpg"}).should == @photo1
105
+ Photo.find(:first, :conditions => {:image => "boobs.png", :image_type => "png"}).should == @photo2
105
106
 
106
- Photo.find(:last, :conditions => {:image => "facepalm.jpg"}).id.should == @photo1.id
107
- Photo.find(:last, :conditions => {:image => "boobs.png"}).id.should == @photo2.id
107
+ Photo.find(:last, :conditions => {:image => "facepalm.jpg"}).should == @photo1
108
+ Photo.find(:last, :conditions => {:image => "boobs.png"}).should == @photo2
108
109
 
109
- Photo.find(:last, :conditions => {:image => "facepalm.jpg", :image_type => "jpg"}).id.should == @photo1.id
110
- Photo.find(:last, :conditions => {:image => "boobs.png", :image_type => "png"}).id.should == @photo2.id
110
+ Photo.find(:last, :conditions => {:image => "facepalm.jpg", :image_type => "jpg"}).should == @photo1
111
+ Photo.find(:last, :conditions => {:image => "boobs.png", :image_type => "png"}).should == @photo2
111
112
  end
112
113
 
113
114
  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
115
+ Photo.first(:conditions => {:image => "facepalm.jpg"}).should == @photo1
116
+ Photo.first(:conditions => {:image => "boobs.png"}).should == @photo2
116
117
 
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
118
+ Photo.last(:conditions => {:image => "facepalm.jpg", :image_type => "jpg"}).should == @photo1
119
+ Photo.last(:conditions => {:image => "boobs.png", :image_type => "png"}).should == @photo2
119
120
  end
120
121
 
121
122
  it "should correctly save boolean values" do
@@ -11,6 +11,11 @@ class User < RedisOrm::Base
11
11
  index :age
12
12
  end
13
13
 
14
+ class SortableUser < RedisOrm::Base
15
+ property :name, String, :sortable => true
16
+ index :name
17
+ end
18
+
14
19
  describe "test options" do
15
20
  before(:each) do
16
21
  @dan = User.create :name => "Daniel", :age => 26, :wage => 40000.0, :address => "Bellevue"
@@ -18,9 +23,9 @@ describe "test options" do
18
23
  @michael = User.create :name => "Michael", :age => 25, :wage => 60000.0, :address => "Bellevue"
19
24
  @todd = User.create :name => "Todd", :age => 22, :wage => 30000.0, :address => "Bellevue"
20
25
  end
21
-
26
+
22
27
  it "should return records in specified order" do
23
- $redis.zcard("user:name_ids").to_i.should == User.count
28
+ $redis.llen("user:name_ids").to_i.should == User.count
24
29
  $redis.zcard("user:age_ids").to_i.should == User.count
25
30
  $redis.zcard("user:wage_ids").to_i.should == User.count
26
31
 
@@ -49,11 +54,11 @@ describe "test options" do
49
54
  User.find(:all, :conditions => {:name => "Abe"}, :order => [:wage, :desc]).should == [@abe, @abe2]
50
55
  User.find(:all, :conditions => {:name => "Abe"}, :order => [:wage, :asc]).should == [@abe2, @abe]
51
56
  end
52
-
57
+
53
58
  it "should update keys after the persisted object was edited and sort properly" do
54
59
  @abe.update_attributes :name => "Zed", :age => 12, :wage => 10.0, :address => "Santa Fe"
55
60
 
56
- $redis.zcard("user:name_ids").to_i.should == User.count
61
+ $redis.llen("user:name_ids").to_i.should == User.count
57
62
  $redis.zcard("user:age_ids").to_i.should == User.count
58
63
  $redis.zcard("user:wage_ids").to_i.should == User.count
59
64
 
@@ -66,4 +71,60 @@ describe "test options" do
66
71
  User.find(:all, :order => [:wage, :asc]).should == [@abe, @todd, @dan, @michael]
67
72
  User.find(:all, :order => [:wage, :desc]).should == [@michael, @dan, @todd, @abe]
68
73
  end
74
+
75
+ it "should update keys after the persisted object was deleted and sort properly" do
76
+ user_count = User.count
77
+ @abe.destroy
78
+
79
+ $redis.llen("user:name_ids").to_i.should == user_count - 1
80
+ $redis.zcard("user:age_ids").to_i.should == user_count - 1
81
+ $redis.zcard("user:wage_ids").to_i.should == user_count - 1
82
+
83
+ User.find(:all, :order => [:name, :asc]).should == [@dan, @michael, @todd]
84
+ User.find(:all, :order => [:name, :desc]).should == [@todd, @michael, @dan]
85
+
86
+ User.find(:all, :order => [:age, :asc]).should == [@todd, @michael, @dan]
87
+ User.find(:all, :order => [:age, :desc]).should == [@dan, @michael, @todd]
88
+
89
+ User.find(:all, :order => [:wage, :asc]).should == [@todd, @dan, @michael]
90
+ User.find(:all, :order => [:wage, :desc]).should == [@michael, @dan, @todd]
91
+ end
92
+
93
+ it "should sort objects with more than 3-4 symbols" do
94
+ vladislav = User.create :name => "Vladislav", :age => 19, :wage => 120.0
95
+ vladimir = User.create :name => "Vladimir", :age => 22, :wage => 220.5
96
+ vlad = User.create :name => "Vlad", :age => 29, :wage => 1200.0
97
+
98
+ User.find(:all, :order => [:name, :desc], :limit => 3).should == [vladislav, vladimir, vlad]
99
+ User.find(:all, :order => [:name, :desc], :limit => 2, :offset => 4).should == [@michael, @dan]
100
+ User.find(:all, :order => [:name, :desc], :offset => 3).should == [@todd, @michael, @dan, @abe]
101
+ User.find(:all, :order => [:name, :desc]).should == [vladislav, vladimir, vlad, @todd, @michael, @dan, @abe]
102
+
103
+ User.find(:all, :order => [:name, :asc], :limit => 3, :offset => 4).should == [vlad, vladimir, vladislav]
104
+ User.find(:all, :order => [:name, :asc], :offset => 3).should == [@todd, vlad, vladimir, vladislav]
105
+ User.find(:all, :order => [:name, :asc], :limit => 3).should == [@abe, @dan, @michael]
106
+ User.find(:all, :order => [:name, :asc]).should == [@abe, @dan, @michael, @todd, vlad, vladimir, vladislav]
107
+ end
108
+
109
+ it "should properly handle multiple users with almost the same names" do
110
+ users = []
111
+ 20.times{|i| users << SortableUser.create(:name => "user#{i}") }
112
+ SortableUser.all(:order => [:name, :asc]).should == users.sort{|n,m| n.name <=> m.name}
113
+ end
114
+
115
+ it "should properly handle multiple users with almost the same names (descending order)" do
116
+ rev_users = []
117
+ 20.times{|i| rev_users << SortableUser.create(:name => "user#{i}") }
118
+ SortableUser.all(:order => [:name, :desc]).should == rev_users.sort{|n,m| n.name <=> m.name}.reverse
119
+ end
120
+
121
+ it "should properly store records with the same names" do
122
+ users = []
123
+ users << SortableUser.create(:name => "user#1")
124
+ users << SortableUser.create(:name => "user#2")
125
+ users << SortableUser.create(:name => "user#1")
126
+ users << SortableUser.create(:name => "user#2")
127
+ SortableUser.all(:order => [:name, :desc]).should == users.sort{|n,m| n.name <=> m.name}.reverse
128
+ SortableUser.all(:order => [:name, :asc]).should == users.sort{|n,m| n.name <=> m.name}
129
+ end
69
130
  end
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.6'
4
+ version: 0.6.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-12 00:00:00.000000000 +03:00
13
- default_executable:
12
+ date: 2011-12-05 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: activesupport
17
- requirement: &74101100 !ruby/object:Gem::Requirement
16
+ requirement: &17352020 !ruby/object:Gem::Requirement
18
17
  none: false
19
18
  requirements:
20
19
  - - ! '>='
@@ -22,10 +21,10 @@ dependencies:
22
21
  version: 3.0.0
23
22
  type: :runtime
24
23
  prerelease: false
25
- version_requirements: *74101100
24
+ version_requirements: *17352020
26
25
  - !ruby/object:Gem::Dependency
27
26
  name: activemodel
28
- requirement: &74100700 !ruby/object:Gem::Requirement
27
+ requirement: &17367880 !ruby/object:Gem::Requirement
29
28
  none: false
30
29
  requirements:
31
30
  - - ! '>='
@@ -33,10 +32,10 @@ dependencies:
33
32
  version: 3.0.0
34
33
  type: :runtime
35
34
  prerelease: false
36
- version_requirements: *74100700
35
+ version_requirements: *17367880
37
36
  - !ruby/object:Gem::Dependency
38
37
  name: redis
39
- requirement: &74100190 !ruby/object:Gem::Requirement
38
+ requirement: &17367380 !ruby/object:Gem::Requirement
40
39
  none: false
41
40
  requirements:
42
41
  - - ! '>='
@@ -44,10 +43,10 @@ dependencies:
44
43
  version: 2.2.0
45
44
  type: :runtime
46
45
  prerelease: false
47
- version_requirements: *74100190
46
+ version_requirements: *17367380
48
47
  - !ruby/object:Gem::Dependency
49
48
  name: uuid
50
- requirement: &74099680 !ruby/object:Gem::Requirement
49
+ requirement: &17366880 !ruby/object:Gem::Requirement
51
50
  none: false
52
51
  requirements:
53
52
  - - ! '>='
@@ -55,10 +54,10 @@ dependencies:
55
54
  version: 2.3.2
56
55
  type: :runtime
57
56
  prerelease: false
58
- version_requirements: *74099680
57
+ version_requirements: *17366880
59
58
  - !ruby/object:Gem::Dependency
60
59
  name: rspec
61
- requirement: &74099250 !ruby/object:Gem::Requirement
60
+ requirement: &17366380 !ruby/object:Gem::Requirement
62
61
  none: false
63
62
  requirements:
64
63
  - - ! '>='
@@ -66,7 +65,7 @@ dependencies:
66
65
  version: 2.5.0
67
66
  type: :development
68
67
  prerelease: false
69
- version_requirements: *74099250
68
+ version_requirements: *17366380
70
69
  description: ORM for Redis (advanced key-value storage) with ActiveRecord API
71
70
  email: germaninthetown@gmail.com
72
71
  executables: []
@@ -86,10 +85,12 @@ extra_rdoc_files:
86
85
  - lib/redis_orm/utils.rb
87
86
  files:
88
87
  - CHANGELOG
88
+ - Gemfile
89
89
  - LICENSE
90
90
  - Manifest
91
91
  - README.md
92
92
  - Rakefile
93
+ - benchmarks/sortable_benchmark.rb
93
94
  - lib/redis_orm.rb
94
95
  - lib/redis_orm/active_model_behavior.rb
95
96
  - lib/redis_orm/associations/belongs_to.rb
@@ -111,13 +112,12 @@ files:
111
112
  - test/has_one_has_many_test.rb
112
113
  - test/indices_test.rb
113
114
  - test/options_test.rb
114
- - test/order_test.rb
115
115
  - test/polymorphic_test.rb
116
116
  - test/redis.conf
117
+ - test/sortable_test.rb
117
118
  - test/test_helper.rb
118
119
  - test/uuid_as_id_test.rb
119
120
  - test/validations_test.rb
120
- has_rdoc: true
121
121
  homepage: https://github.com/german/redis_orm
122
122
  licenses: []
123
123
  post_install_message:
@@ -144,24 +144,24 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
144
  version: '1.2'
145
145
  requirements: []
146
146
  rubyforge_project: redis_orm
147
- rubygems_version: 1.6.2
147
+ rubygems_version: 1.8.6
148
148
  signing_key:
149
149
  specification_version: 3
150
150
  summary: ORM for Redis (advanced key-value storage) with ActiveRecord API
151
151
  test_files:
152
+ - test/atomicity_test.rb
153
+ - test/indices_test.rb
154
+ - test/sortable_test.rb
155
+ - test/uuid_as_id_test.rb
156
+ - test/test_helper.rb
152
157
  - test/options_test.rb
153
- - test/dynamic_finders_test.rb
158
+ - test/callbacks_test.rb
159
+ - test/exceptions_test.rb
154
160
  - test/associations_test.rb
155
161
  - test/validations_test.rb
156
- - test/test_helper.rb
162
+ - test/basic_functionality_test.rb
163
+ - test/dynamic_finders_test.rb
164
+ - test/changes_array_test.rb
157
165
  - test/polymorphic_test.rb
158
- - test/uuid_as_id_test.rb
159
- - test/atomicity_test.rb
160
- - test/exceptions_test.rb
161
166
  - test/association_indices_test.rb
162
167
  - test/has_one_has_many_test.rb
163
- - test/order_test.rb
164
- - test/indices_test.rb
165
- - test/changes_array_test.rb
166
- - test/callbacks_test.rb
167
- - test/basic_functionality_test.rb