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 +4 -0
- data/Gemfile +8 -0
- data/Manifest +3 -1
- data/README.md +15 -3
- data/Rakefile +1 -6
- data/benchmarks/sortable_benchmark.rb +45 -0
- data/lib/redis_orm/redis_orm.rb +130 -24
- data/redis_orm.gemspec +9 -9
- data/test/exceptions_test.rb +5 -0
- data/test/options_test.rb +16 -15
- data/test/{order_test.rb → sortable_test.rb} +65 -4
- metadata +26 -26
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
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
|
data/lib/redis_orm/redis_orm.rb
CHANGED
@@ -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
|
-
|
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
|
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.
|
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
|
-
|
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
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
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
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
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
|
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.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 = [
|
9
|
-
s.date = %q{2011-
|
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 = [
|
13
|
-
s.files = [
|
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 = [
|
16
|
-
s.require_paths = [
|
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
|
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 = [
|
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
|
data/test/exceptions_test.rb
CHANGED
@@ -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
|
data/test/options_test.rb
CHANGED
@@ -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
|
-
|
92
|
-
Photo.find(:
|
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"}).
|
101
|
-
Photo.find(:first, :conditions => {:image => "boobs.png"}).
|
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"}).
|
104
|
-
Photo.find(:first, :conditions => {:image => "boobs.png", :image_type => "png"}).
|
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"}).
|
107
|
-
Photo.find(:last, :conditions => {:image => "boobs.png"}).
|
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"}).
|
110
|
-
Photo.find(:last, :conditions => {:image => "boobs.png", :image_type => "png"}).
|
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"}).
|
115
|
-
Photo.first(:conditions => {:image => "boobs.png"}).
|
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"}).
|
118
|
-
Photo.last(:conditions => {:image => "boobs.png", :image_type => "png"}).
|
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.
|
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.
|
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:
|
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-
|
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: &
|
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: *
|
24
|
+
version_requirements: *17352020
|
26
25
|
- !ruby/object:Gem::Dependency
|
27
26
|
name: activemodel
|
28
|
-
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: *
|
35
|
+
version_requirements: *17367880
|
37
36
|
- !ruby/object:Gem::Dependency
|
38
37
|
name: redis
|
39
|
-
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: *
|
46
|
+
version_requirements: *17367380
|
48
47
|
- !ruby/object:Gem::Dependency
|
49
48
|
name: uuid
|
50
|
-
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: *
|
57
|
+
version_requirements: *17366880
|
59
58
|
- !ruby/object:Gem::Dependency
|
60
59
|
name: rspec
|
61
|
-
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: *
|
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
|
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/
|
158
|
+
- test/callbacks_test.rb
|
159
|
+
- test/exceptions_test.rb
|
154
160
|
- test/associations_test.rb
|
155
161
|
- test/validations_test.rb
|
156
|
-
- test/
|
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
|