redis_orm 0.6 → 0.6.1

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.
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