record-cache 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  # Record Cache files
2
2
  ["query", "version_store", "multi_read",
3
- "strategy/base", "strategy/id_cache", "strategy/index_cache", "strategy/request_cache",
3
+ "strategy/util", "strategy/base", "strategy/id_cache", "strategy/index_cache", "strategy/request_cache",
4
4
  "statistics", "dispatcher", "base"].each do |file|
5
5
  require File.dirname(__FILE__) + "/record_cache/#{file}.rb"
6
6
  end
@@ -12,7 +12,6 @@ module RecordCache
12
12
  end
13
13
 
14
14
  # Set equality of an attribute (usually found in where clause)
15
- # Returns false if another attribute values was already set (making this query uncachable)
16
15
  def where(attribute, values)
17
16
  @wheres[attribute.to_sym] = values if attribute
18
17
  end
@@ -1,8 +1,6 @@
1
1
  module RecordCache
2
2
  module Strategy
3
3
  class Base
4
- CLASS_KEY = :c
5
- ATTRIBUTES_KEY = :a
6
4
 
7
5
  def initialize(base, strategy_id, record_store, options)
8
6
  @base = base
@@ -14,8 +12,8 @@ module RecordCache
14
12
  # Fetch all records and sort and filter locally
15
13
  def fetch(query)
16
14
  records = fetch_records(query)
17
- filter!(records, query.wheres) if query.wheres.size > 0
18
- sort!(records, query.sort_orders) if query.sorted?
15
+ Util.filter!(records, query.wheres) if query.wheres.size > 0
16
+ Util.sort!(records, query.sort_orders) if query.sorted?
19
17
  records
20
18
  end
21
19
 
@@ -67,88 +65,6 @@ module RecordCache
67
65
  "#{cache_key}v#{version.to_s}".freeze
68
66
  end
69
67
 
70
- # serialize one record before adding it to the cache
71
- # creates a shallow clone with a version and without associations
72
- def serialize(record)
73
- {CLASS_KEY => record.class.name,
74
- ATTRIBUTES_KEY => record.instance_variable_get(:@attributes)}.freeze
75
- end
76
-
77
- # deserialize a cached record
78
- def deserialize(serialized)
79
- record = serialized[CLASS_KEY].constantize.new
80
- attributes = serialized[ATTRIBUTES_KEY]
81
- record.instance_variable_set(:@attributes, Hash[attributes])
82
- record.instance_variable_set(:@new_record, false)
83
- record.instance_variable_set(:@changed_attributes, {})
84
- record.instance_variable_set(:@previously_changed, {})
85
- record
86
- end
87
-
88
- private
89
-
90
- # Filter the cached records in memory
91
- # only simple x = y or x IN (a,b,c) can be handled
92
- def filter!(records, wheres)
93
- wheres.each_pair do |attr, value|
94
- if value.is_a?(Array)
95
- records.reject! { |record| !value.include?(record.send(attr)) }
96
- else
97
- records.reject! { |record| record.send(attr) != value }
98
- end
99
- end
100
- end
101
-
102
- # Sort the cached records in memory
103
- def sort!(records, sort_orders)
104
- records.sort!(&sort_proc(sort_orders))
105
- Collator.clear
106
- records
107
- end
108
-
109
- # Retrieve the Proc based on the order by attributes
110
- # Note: Case insensitive sorting with collation is used for Strings
111
- def sort_proc(sort_orders)
112
- # [['(COLLATER.collate(x.name) || NIL_COMES_FIRST)', 'COLLATER.collate(y.name)'], ['(y.updated_at || NIL_COMES_FIRST)', 'x.updated_at']]
113
- sort = sort_orders.map do |attr, asc|
114
- lr = ["x.", "y."]
115
- lr.reverse! unless asc
116
- lr.each{ |s| s << attr }
117
- lr.each{ |s| s.replace("Collator.collate(#{s})") } if @base.columns_hash[attr].type == :string
118
- lr[0].replace("(#{lr[0]} || NIL_COMES_FIRST)")
119
- lr
120
- end
121
- # ['[(COLLATER.collate(x.name) || NIL_COMES_FIRST), (y.updated_at || NIL_COMES_FIRST)]', '[COLLATER.collate(y.name), x.updated_at]']
122
- sort = sort.transpose.map{|s| s.size == 1 ? s.first : "[#{s.join(',')}]"}
123
- # Proc.new{ |x,y| { ([(COLLATER.collate(x.name) || NIL_COMES_FIRST), (y.updated_at || NIL_COMES_FIRST)] <=> [COLLATER.collate(y.name), x.updated_at]) || 1 }
124
- eval("Proc.new{ |x,y| (#{sort[0]} <=> #{sort[1]}) || 1 }")
125
- end
126
-
127
- # If +x.nil?+ this class will return -1 for +x <=> y+
128
- NIL_COMES_FIRST = ((class NilComesFirst; def <=>(y); -1; end; end); NilComesFirst.new)
129
-
130
- # StringCollator uses the Rails transliterate method for collation
131
- module Collator
132
- @collated = []
133
-
134
- def self.clear
135
- @collated.each { |string| string.send(:remove_instance_variable, :@rc_collated) }
136
- @collated.clear
137
- end
138
-
139
- def self.collate(string)
140
- collated = string.instance_variable_get(:@rc_collated)
141
- return collated if collated
142
- normalized = ActiveSupport::Multibyte::Unicode.normalize(ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c).mb_chars
143
- collated = I18n.transliterate(normalized).downcase.mb_chars
144
- # transliterate will replace ignored/unknown chars with ? the following line replaces ? with the original character
145
- collated.chars.each_with_index{ |c, i| collated[i] = normalized[i] if c == '?' } if collated.index('?')
146
- # puts "collation: #{string} => #{collated.to_s}"
147
- string.instance_variable_set(:@rc_collated, collated)
148
- @collated << string
149
- collated
150
- end
151
- end
152
68
  end
153
69
  end
154
70
  end
@@ -16,7 +16,7 @@ module RecordCache
16
16
  else
17
17
  # update the version store and add the record to the cache
18
18
  new_version = version_store.increment(key)
19
- record_store.write(versioned_key(key, new_version), serialize(record))
19
+ record_store.write(versioned_key(key, new_version), Util.serialize(record))
20
20
  end
21
21
  end
22
22
 
@@ -56,7 +56,7 @@ module RecordCache
56
56
  # retrieve the records from the cache with the given keys
57
57
  def from_cache(id_to_versioned_key_map)
58
58
  records = record_store.read_multi(*(id_to_versioned_key_map.values)).values.compact
59
- records.map{ |record| deserialize(record) }
59
+ records.map{ |record| Util.deserialize(record) }
60
60
  end
61
61
 
62
62
  # retrieve the records with the given ids from the database
@@ -72,7 +72,7 @@ module RecordCache
72
72
  versioned_key = versioned_key(key, version_store.renew(key))
73
73
  end
74
74
  # store the record based on the versioned key
75
- record_store.write(versioned_key, serialize(record))
75
+ record_store.write(versioned_key, Util.serialize(record))
76
76
  end
77
77
  records
78
78
  end
@@ -0,0 +1,114 @@
1
+ # Utility methods for the Cache Strategies
2
+ module RecordCache
3
+ module Strategy
4
+ module Util
5
+ CLASS_KEY = :c
6
+ ATTRIBUTES_KEY = :a
7
+
8
+ class << self
9
+
10
+ # serialize one record before adding it to the cache
11
+ # creates a shallow clone with a version and without associations
12
+ def serialize(record)
13
+ {CLASS_KEY => record.class.name,
14
+ ATTRIBUTES_KEY => record.instance_variable_get(:@attributes)}.freeze
15
+ end
16
+
17
+ # deserialize a cached record
18
+ def deserialize(serialized)
19
+ record = serialized[CLASS_KEY].constantize.new
20
+ attributes = serialized[ATTRIBUTES_KEY]
21
+ record.instance_variable_set(:@attributes, Hash[attributes])
22
+ record.instance_variable_set(:@new_record, false)
23
+ record.instance_variable_set(:@changed_attributes, {})
24
+ record.instance_variable_set(:@previously_changed, {})
25
+ record
26
+ end
27
+
28
+ # Filter the cached records in memory
29
+ # only simple x = y or x IN (a,b,c) can be handled
30
+ # Example:
31
+ # RecordCache::Strategy::Util.filter!(Apple.all, :price => [0.49, 0.59, 0.69], :name => "Green Apple")
32
+ def filter!(records, wheres)
33
+ wheres.each_pair do |attr, value|
34
+ attr = attr.to_sym
35
+ if value.is_a?(Array)
36
+ records.reject! { |record| !value.include?(record.send(attr)) }
37
+ else
38
+ records.reject! { |record| record.send(attr) != value }
39
+ end
40
+ end
41
+ end
42
+
43
+ # Sort the cached records in memory, similar to MySql sorting rules including collatiom
44
+ # Simply provide the Symbols of the attributes to sort in Ascending order, or use
45
+ # [<attribute>, false] for Descending order.
46
+ # Example:
47
+ # RecordCache::Strategy::Util.sort!(Apple.all, :name)
48
+ # RecordCache::Strategy::Util.sort!(Apple.all, [:name, false])
49
+ # RecordCache::Strategy::Util.sort!(Apple.all, [:price, false], :name)
50
+ # RecordCache::Strategy::Util.sort!(Apple.all, [:price, false], [:name, true])
51
+ # RecordCache::Strategy::Util.sort!(Apple.all, [[:price, false], [:name, true]])
52
+ def sort!(records, *sort_orders)
53
+ return records if records.empty? || sort_orders.empty?
54
+ if sort_orders.first.is_a?(Array) && sort_orders.first.first.is_a?(Array)
55
+ sort_orders = sort_orders.first
56
+ else
57
+ sort_orders = sort_orders.map{ |order| order.is_a?(Array) ? order : [order, true] } unless sort_orders.all?{ |order| order.is_a?(Array) }
58
+ end
59
+ records.sort!(&sort_proc(records.first.class, sort_orders))
60
+ Collator.clear
61
+ records
62
+ end
63
+
64
+ private
65
+
66
+ # Retrieve the Proc based on the order by attributes
67
+ # Note: Case insensitive sorting with collation is used for Strings
68
+ def sort_proc(base, sort_orders)
69
+ # [['(COLLATER.collate(x.name) || NIL_COMES_FIRST)', 'COLLATER.collate(y.name)'], ['(y.updated_at || NIL_COMES_FIRST)', 'x.updated_at']]
70
+ sort = sort_orders.map do |attr, asc|
71
+ attr = attr.to_s
72
+ lr = ["x.", "y."]
73
+ lr.reverse! unless asc
74
+ lr.each{ |s| s << attr }
75
+ lr.each{ |s| s.replace("Collator.collate(#{s})") } if base.columns_hash[attr].type == :string
76
+ lr[0].replace("(#{lr[0]} || NIL_COMES_FIRST)")
77
+ lr
78
+ end
79
+ # ['[(COLLATER.collate(x.name) || NIL_COMES_FIRST), (y.updated_at || NIL_COMES_FIRST)]', '[COLLATER.collate(y.name), x.updated_at]']
80
+ sort = sort.transpose.map{|s| s.size == 1 ? s.first : "[#{s.join(',')}]"}
81
+ # Proc.new{ |x,y| { ([(COLLATER.collate(x.name) || NIL_COMES_FIRST), (y.updated_at || NIL_COMES_FIRST)] <=> [COLLATER.collate(y.name), x.updated_at]) || 1 }
82
+ eval("Proc.new{ |x,y| (#{sort[0]} <=> #{sort[1]}) || 1 }")
83
+ end
84
+
85
+ # If +x.nil?+ this class will return -1 for +x <=> y+
86
+ NIL_COMES_FIRST = ((class NilComesFirst; def <=>(y); -1; end; end); NilComesFirst.new)
87
+
88
+ # StringCollator uses the Rails transliterate method for collation
89
+ module Collator
90
+ @collated = []
91
+
92
+ def self.clear
93
+ @collated.each { |string| string.send(:remove_instance_variable, :@rc_collated) }
94
+ @collated.clear
95
+ end
96
+
97
+ def self.collate(string)
98
+ collated = string.instance_variable_get(:@rc_collated)
99
+ return collated if collated
100
+ normalized = ActiveSupport::Multibyte::Unicode.normalize(ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c).mb_chars
101
+ collated = I18n.transliterate(normalized).downcase.mb_chars
102
+ # transliterate will replace ignored/unknown chars with ? the following line replaces ? with the original character
103
+ collated.chars.each_with_index{ |c, i| collated[i] = normalized[i] if c == '?' } if collated.index('?')
104
+ # puts "collation: #{string} => #{collated.to_s}"
105
+ string.instance_variable_set(:@rc_collated, collated)
106
+ @collated << string
107
+ collated
108
+ end
109
+ end
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -1,5 +1,5 @@
1
1
  module RecordCache # :nodoc:
2
2
  module Version # :nodoc:
3
- STRING = '0.1.1'
3
+ STRING = '0.1.2'
4
4
  end
5
5
  end
@@ -23,7 +23,7 @@ module RecordCache
23
23
  end
24
24
 
25
25
  # In case the version store did not have a key anymore, call this methods
26
- # to reset the key with a unique new key
26
+ # to reset the key with a (unique) new version
27
27
  def renew(key)
28
28
  new_version = (Time.current.to_f * 10000).to_i
29
29
  @store.write(key, new_version)
@@ -43,7 +43,7 @@ module RecordCache
43
43
  version
44
44
  end
45
45
 
46
- # Delete key from the version store, in case the record(s) are destroyed
46
+ # Delete key from the version store (records cached in the Record Store belonging to this key will become unreachable)
47
47
  def delete(key)
48
48
  deleted = @store.delete(key)
49
49
  RecordCache::Base.logger.debug("Version Store: deleted #{key}") if RecordCache::Base.logger.debug?
@@ -29,14 +29,6 @@ describe RecordCache::Strategy::Base do
29
29
  Banana.record_cache[:id].send(:versioned_key, "rc/Banana/1", 2312423).should == "rc/Banana/1v2312423"
30
30
  end
31
31
 
32
- it "should serialize a record (currently Active Record only)" do
33
- Banana.record_cache[:id].send(:serialize, Banana.find(1)).should == {:a=>{"name"=>"Blue Banana 1", "id"=>1, "store_id"=>2, "person_id"=>4}, :c=>"Banana"}
34
- end
35
-
36
- it "should deserialize a record (currently Active Record only)" do
37
- Banana.record_cache[:id].send(:deserialize, {:a=>{"name"=>"Blue Banana 1", "id"=>1, "store_id"=>2, "person_id"=>4}, :c=>"Banana"}).should == Banana.find(1)
38
- end
39
-
40
32
  context "filter" do
41
33
  it "should apply filter on :id cache hits" do
42
34
  lambda{ @apples = Apple.where(:id => [1,2]).where(:name => "Adams Apple 1").all }.should use_cache(Apple).on(:id)
@@ -0,0 +1,228 @@
1
+ $KCODE = 'UTF8'
2
+ require 'spec_helper'
3
+
4
+ describe RecordCache::Strategy::Util do
5
+
6
+ it "should serialize a record (currently Active Record only)" do
7
+ subject.serialize(Banana.find(1)).should == {:a=>{"name"=>"Blue Banana 1", "id"=>1, "store_id"=>2, "person_id"=>4}, :c=>"Banana"}
8
+ end
9
+
10
+ it "should deserialize a record (currently Active Record only)" do
11
+ subject.deserialize({:a=>{"name"=>"Blue Banana 1", "id"=>1, "store_id"=>2, "person_id"=>4}, :c=>"Banana"}).should == Banana.find(1)
12
+ end
13
+
14
+ context "filter" do
15
+ it "should apply filter" do
16
+ apples = Apple.where(:id => [1,2]).all
17
+ subject.filter!(apples, :name => "Adams Apple 1")
18
+ apples.should == [Apple.find_by_name("Adams Apple 1")]
19
+ end
20
+
21
+ it "should return empty array when filter does not match any record" do
22
+ apples = Apple.where(:id => [1,2]).all
23
+ subject.filter!(apples, :name => "Adams Apple Pie")
24
+ apples.should be_empty
25
+ end
26
+
27
+ it "should filter on text" do
28
+ apples = Apple.where(:id => [1,2]).all
29
+ subject.filter!(apples, :name => "Adams Apple 1")
30
+ apples.should == [Apple.find_by_name("Adams Apple 1")]
31
+ end
32
+
33
+ it "should filter on integers" do
34
+ apples = Apple.where(:id => [1,2,8,9]).all
35
+ subject.filter!(apples, :store_id => 2)
36
+ apples.map(&:id).sort.should == [8,9]
37
+ end
38
+
39
+ it "should filter on dates" do
40
+ people = Person.where(:id => [1,2,3]).all
41
+ subject.filter!(people, :birthday => Date.civil(1953,11,11))
42
+ people.size.should == 1
43
+ people.first.name.should == "Blue"
44
+ end
45
+
46
+ it "should filter on floats" do
47
+ people = Person.where(:id => [1,2,3]).all
48
+ subject.filter!(people, :height => 1.75)
49
+ people.size.should == 2
50
+ people.map(&:name).sort.should == ["Blue", "Cris"]
51
+ end
52
+
53
+ it "should filter on arrays" do
54
+ apples = Apple.where(:id => [1,2,8,9])
55
+ subject.filter!(apples, :store_id => [2, 4])
56
+ apples.map(&:id).sort.should == [8,9]
57
+ end
58
+
59
+ it "should filter on multiple fields" do
60
+ # make sure two apples exist with the same name
61
+ apple = Apple.find(8)
62
+ apple.name = Apple.find(9).name
63
+ apple.save!
64
+
65
+ apples = Apple.where(:id => [1,2,3,8,9,10]).all
66
+ subject.filter!(apples, :store_id => [2, 4], :name => apple.name)
67
+ apples.size.should == 2
68
+ apples.map(&:name).should == [apple.name, apple.name]
69
+ apples.map(&:id).sort.should == [8,9]
70
+ end
71
+
72
+ end
73
+
74
+ context "sort" do
75
+ it "should accept a Symbol as a sort order" do
76
+ people = Person.where(:id => [1,2,3]).all
77
+ subject.sort!(people, :name)
78
+ people.map(&:name).should == ["Adam", "Blue", "Cris"]
79
+ end
80
+
81
+ it "should accept a single Array as a sort order" do
82
+ people = Person.where(:id => [1,2,3]).all
83
+ subject.sort!(people, [:name, false])
84
+ people.map(&:name).should == ["Cris", "Blue", "Adam"]
85
+ end
86
+
87
+ it "should accept multiple Symbols as a sort order" do
88
+ people = Person.where(:id => [2,3,4,5]).all
89
+ subject.sort!(people, :height, :id)
90
+ people.map(&:height).should == [1.69, 1.75, 1.75, 1.91]
91
+ people.map(&:id).should == [4, 2, 3, 5]
92
+ end
93
+
94
+ it "should accept a mix of Symbols and Arrays as a sort order" do
95
+ people = Person.where(:id => [2,3,4,5]).all
96
+ subject.sort!(people, [:height, false], :id)
97
+ people.map(&:height).should == [1.91, 1.75, 1.75, 1.69]
98
+ people.map(&:id).should == [5, 2, 3, 4]
99
+ end
100
+
101
+ it "should accept multiple Arrays as a sort order" do
102
+ people = Person.where(:id => [2,3,4,5]).all
103
+ subject.sort!(people, [:height, false], [:id, false])
104
+ people.map(&:height).should == [1.91, 1.75, 1.75, 1.69]
105
+ people.map(&:id).should == [5, 3, 2, 4]
106
+ end
107
+
108
+ it "should accept an Array with Arrays as a sort order (default used by record cache)" do
109
+ people = Person.where(:id => [2,3,4,5]).all
110
+ subject.sort!(people, [[:height, false], [:id, false]])
111
+ people.map(&:height).should == [1.91, 1.75, 1.75, 1.69]
112
+ people.map(&:id).should == [5, 3, 2, 4]
113
+ end
114
+
115
+ it "should order nil first for ASC" do
116
+ apples = Apple.where(:store_id => 1).all
117
+ subject.sort!(apples, [:person_id, true])
118
+ apples.map(&:person_id).should == [nil, nil, 4, 4, 5]
119
+ end
120
+
121
+ it "should order nil last for DESC" do
122
+ apples = Apple.where(:store_id => 1).all
123
+ subject.sort!(apples, [:person_id, false])
124
+ apples.map(&:person_id).should == [5, 4, 4, nil, nil]
125
+ end
126
+
127
+ it "should order ascending on text" do
128
+ people = Person.where(:id => [1,2,3,4]).all
129
+ subject.sort!(people, [:name, true])
130
+ people.map(&:name).should == ["Adam", "Blue", "Cris", "Fry"]
131
+ end
132
+
133
+ it "should order descending on text" do
134
+ people = Person.where(:id => [1,2,3,4]).all
135
+ subject.sort!(people, [:name, false])
136
+ people.map(&:name).should == ["Fry", "Cris", "Blue", "Adam"]
137
+ end
138
+
139
+ it "should order ascending on integers" do
140
+ people = Person.where(:id => [4,2,1,3]).all
141
+ subject.sort!(people, [:id, true])
142
+ people.map(&:id).should == [1,2,3,4]
143
+ end
144
+
145
+ it "should order descending on integers" do
146
+ people = Person.where(:id => [4,2,1,3]).all
147
+ subject.sort!(people, [:id, false])
148
+ people.map(&:id).should == [4,3,2,1]
149
+ end
150
+
151
+ it "should order ascending on dates" do
152
+ people = Person.where(:id => [1,2,3,4]).all
153
+ subject.sort!(people, [:birthday, true])
154
+ people.map(&:birthday).should == [Date.civil(1953,11,11), Date.civil(1975,03,20), Date.civil(1975,03,20), Date.civil(1985,01,20)]
155
+ end
156
+
157
+ it "should order descending on dates" do
158
+ people = Person.where(:id => [1,2,3,4]).all
159
+ subject.sort!(people, [:birthday, false])
160
+ people.map(&:birthday).should == [Date.civil(1985,01,20), Date.civil(1975,03,20), Date.civil(1975,03,20), Date.civil(1953,11,11)]
161
+ end
162
+
163
+ it "should order ascending on float" do
164
+ people = Person.where(:id => [1,2,3,4]).all
165
+ subject.sort!(people, [:height, true])
166
+ people.map(&:height).should == [1.69, 1.75, 1.75, 1.83]
167
+ end
168
+
169
+ it "should order descending on float" do
170
+ people = Person.where(:id => [1,2,3,4]).all
171
+ subject.sort!(people, [:height, false])
172
+ people.map(&:height).should == [1.83, 1.75, 1.75, 1.69]
173
+ end
174
+
175
+ it "should order on multiple fields (ASC + ASC)" do
176
+ people = Person.where(:id => [2,3,4,5]).all
177
+ subject.sort!(people, [:height, true], [:id, true])
178
+ people.map(&:height).should == [1.69, 1.75, 1.75, 1.91]
179
+ people.map(&:id).should == [4, 2, 3, 5]
180
+ end
181
+
182
+ it "should order on multiple fields (ASC + DESC)" do
183
+ people = Person.where(:id => [2,3,4,5]).all
184
+ subject.sort!(people, [:height, true], [:id, false])
185
+ people.map(&:height).should == [1.69, 1.75, 1.75, 1.91]
186
+ people.map(&:id).should == [4, 3, 2, 5]
187
+ end
188
+
189
+ it "should order on multiple fields (DESC + ASC)" do
190
+ people = Person.where(:id => [2,3,4,5]).all
191
+ subject.sort!(people, [:height, false], [:id, true])
192
+ people.map(&:height).should == [1.91, 1.75, 1.75, 1.69]
193
+ people.map(&:id).should == [5, 2, 3, 4]
194
+ end
195
+
196
+ it "should order on multiple fields (DESC + DESC)" do
197
+ people = Person.where(:id => [2,3,4,5]).all
198
+ subject.sort!(people, [:height, false], [:id, false])
199
+ people.map(&:height).should == [1.91, 1.75, 1.75, 1.69]
200
+ people.map(&:id).should == [5, 3, 2, 4]
201
+ end
202
+
203
+ it "should use mysql style collation" do
204
+ ids = []
205
+ ids << Person.create!(:name => "ċedriĉ 3").id # latin other special
206
+ ids << Person.create!(:name => "a cedric").id # first in ascending order
207
+ ids << Person.create!(:name => "čedriĉ 4").id # latin another special
208
+ ids << Person.create!(:name => "ćedriĉ Last").id # latin special lowercase
209
+ ids << Person.create!(:name => "sedric 1").id # second to last latin in ascending order
210
+ ids << Person.create!(:name => "Cedric 2").id # ascii uppercase
211
+ ids << Person.create!(:name => "čedriĉ คฉ Almost last cedric").id # latin special, with non-latin
212
+ ids << Person.create!(:name => "Sedric 2").id # last latin in ascending order
213
+ ids << Person.create!(:name => "1 cedric").id # numbers before characters
214
+ ids << Person.create!(:name => "cedric 1").id # ascii lowercase
215
+ ids << Person.create!(:name => "คฉ Really last").id # non-latin characters last in ascending order
216
+ ids << Person.create!(:name => "čedriĉ ꜩ Last").id # latin special, with latin non-collateable
217
+
218
+ names_asc = ["1 cedric", "a cedric", "cedric 1", "Cedric 2", "ċedriĉ 3", "čedriĉ 4", "ćedriĉ Last", "čedriĉ คฉ Almost last cedric", "čedriĉ ꜩ Last", "sedric 1", "Sedric 2", "คฉ Really last"]
219
+ people = Person.where(:id => ids).all
220
+ subject.sort!(people, [:name, true])
221
+ people.map(&:name).should == names_asc
222
+
223
+ subject.sort!(people, [:name, false])
224
+ people.map(&:name).should == names_asc.reverse
225
+ end
226
+ end
227
+
228
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: record-cache
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 1
10
- version: 0.1.1
9
+ - 2
10
+ version: 0.1.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Orslumen
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-10-07 00:00:00 Z
18
+ date: 2011-10-20 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: rails
@@ -23,7 +23,7 @@ dependencies:
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
24
  none: false
25
25
  requirements:
26
- - - ">"
26
+ - - ">="
27
27
  - !ruby/object:Gem::Version
28
28
  hash: 7
29
29
  segments:
@@ -38,7 +38,7 @@ dependencies:
38
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
- - - ">"
41
+ - - ">="
42
42
  - !ruby/object:Gem::Version
43
43
  hash: 7
44
44
  segments:
@@ -168,6 +168,7 @@ files:
168
168
  - lib/record_cache/strategy/id_cache.rb
169
169
  - lib/record_cache/strategy/index_cache.rb
170
170
  - lib/record_cache/strategy/request_cache.rb
171
+ - lib/record_cache/strategy/util.rb
171
172
  - lib/record_cache/test/resettable_version_store.rb
172
173
  - lib/record_cache/version.rb
173
174
  - lib/record_cache/version_store.rb
@@ -183,6 +184,7 @@ files:
183
184
  - spec/lib/strategy/id_cache_spec.rb
184
185
  - spec/lib/strategy/index_cache_spec.rb
185
186
  - spec/lib/strategy/request_cache_spec.rb
187
+ - spec/lib/strategy/util_spec.rb
186
188
  - spec/lib/version_store_spec.rb
187
189
  - spec/models/apple.rb
188
190
  - spec/models/banana.rb
@@ -226,7 +228,7 @@ rubyforge_project:
226
228
  rubygems_version: 1.8.11
227
229
  signing_key:
228
230
  specification_version: 3
229
- summary: Record Cache v0.1.1 transparantly stores Records in a Cache Store and retrieve those Records from the store when queried (by ID) using Active Model.
231
+ summary: Record Cache v0.1.2 transparantly stores Records in a Cache Store and retrieve those Records from the store when queried (by ID) using Active Model.
230
232
  test_files:
231
233
  - spec/db/database.yml
232
234
  - spec/db/schema.rb
@@ -240,6 +242,7 @@ test_files:
240
242
  - spec/lib/strategy/id_cache_spec.rb
241
243
  - spec/lib/strategy/index_cache_spec.rb
242
244
  - spec/lib/strategy/request_cache_spec.rb
245
+ - spec/lib/strategy/util_spec.rb
243
246
  - spec/lib/version_store_spec.rb
244
247
  - spec/models/apple.rb
245
248
  - spec/models/banana.rb