zermelo 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -1
  3. data/.travis.yml +3 -5
  4. data/CHANGELOG.md +8 -2
  5. data/lib/zermelo/associations/belongs_to.rb +3 -3
  6. data/lib/zermelo/associations/has_and_belongs_to_many.rb +3 -3
  7. data/lib/zermelo/associations/has_many.rb +3 -3
  8. data/lib/zermelo/associations/has_one.rb +3 -3
  9. data/lib/zermelo/associations/has_sorted_set.rb +4 -4
  10. data/lib/zermelo/associations/index.rb +1 -2
  11. data/lib/zermelo/associations/unique_index.rb +1 -2
  12. data/lib/zermelo/backends/base.rb +1 -1
  13. data/lib/zermelo/backends/influxdb_backend.rb +29 -18
  14. data/lib/zermelo/backends/redis_backend.rb +106 -6
  15. data/lib/zermelo/filters/base.rb +34 -57
  16. data/lib/zermelo/filters/influxdb_filter.rb +22 -70
  17. data/lib/zermelo/filters/redis_filter.rb +35 -482
  18. data/lib/zermelo/filters/steps/list_step.rb +79 -0
  19. data/lib/zermelo/filters/steps/set_step.rb +176 -0
  20. data/lib/zermelo/filters/steps/sort_step.rb +85 -0
  21. data/lib/zermelo/filters/steps/sorted_set_step.rb +156 -0
  22. data/lib/zermelo/records/class_methods.rb +16 -4
  23. data/lib/zermelo/records/influxdb_record.rb +2 -0
  24. data/lib/zermelo/records/instance_methods.rb +4 -4
  25. data/lib/zermelo/records/key.rb +2 -0
  26. data/lib/zermelo/version.rb +1 -1
  27. data/lib/zermelo.rb +9 -1
  28. data/spec/lib/zermelo/records/influxdb_record_spec.rb +186 -10
  29. data/spec/lib/zermelo/records/redis_record_spec.rb +11 -4
  30. data/spec/spec_helper.rb +12 -10
  31. metadata +5 -11
  32. data/lib/zermelo/filters/steps/diff_range_step.rb +0 -17
  33. data/lib/zermelo/filters/steps/diff_step.rb +0 -17
  34. data/lib/zermelo/filters/steps/intersect_range_step.rb +0 -17
  35. data/lib/zermelo/filters/steps/intersect_step.rb +0 -17
  36. data/lib/zermelo/filters/steps/limit_step.rb +0 -17
  37. data/lib/zermelo/filters/steps/offset_step.rb +0 -17
  38. data/lib/zermelo/filters/steps/union_range_step.rb +0 -17
  39. data/lib/zermelo/filters/steps/union_step.rb +0 -17
  40. data/lib/zermelo/records/collection.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a7492dc2e67e9921a87e1dbea33eb721464dce5f
4
- data.tar.gz: 62906c88696b9eb2fdfb8d2a94d0244184cd33da
3
+ metadata.gz: c9908061576a4c3d173358524eaf988a6d16bd33
4
+ data.tar.gz: 843acdcd8b10b1b13407a7c1440f30f87412ac71
5
5
  SHA512:
6
- metadata.gz: dde70cf8e7c241eeef5de1925a67719a6c3779cc7afc784ffdca13e5925a146e8b8cb1797b4f70eb1c377a990057a1f2a008f94156201c81bcf705f999c61f27
7
- data.tar.gz: 62fe986cbc4ab69699bfc2286ef58c17b8fa12c4e79c0c11a2d82ce805637c7f66a002a94b3e785bb4cb7d34fc1ffe74a7ce6f47c1d7ffb8ed8b82f56e34de58
6
+ metadata.gz: 5365e1259edae7641187444a6dce2f903be37ae3787f47c5d12006d8f4dd7cf308e6874e54509dabd1ac336a62c39e166839b8ffd76d8126530e43fcc6488a22
7
+ data.tar.gz: c405762c072ab4f1fe5d366e9997448ba6f35d4b202a6be33535481abc20a1fdf6cbb1d14f87cd4bae37a082cbd9dbe9be3010785c453dd22622064bf5c3bea7
data/.rspec CHANGED
@@ -1,5 +1,5 @@
1
1
  --color
2
- --format progress
2
+ --format documentation
3
3
  --format html
4
4
  --out tmp/spec.html
5
5
  --require ./spec/support/uncolored_doc_formatter.rb
data/.travis.yml CHANGED
@@ -8,15 +8,13 @@ services:
8
8
  - redis-server
9
9
  before_install:
10
10
  - gem --version
11
- - wget http://s3.amazonaws.com/influxdb/influxdb_0.8.8_amd64.deb
11
+ - wget https://s3.amazonaws.com/influxdb/influxdb_0.8.8_amd64.deb
12
12
  - sudo dpkg -i influxdb_0.8.8_amd64.deb
13
13
  - sudo /etc/init.d/influxdb start
14
14
  - sleep 8
15
15
  - 'curl -X POST ''http://localhost:8086/db?u=root&p=root'' -d ''{"name": "zermelo_test"}'''
16
- - 'curl -X POST ''http://localhost:8086/db/zermelo_test/users?u=root&p=root'' -d
17
- ''{"name": "zermelo", "password": "zermelo"}'''
18
- - 'curl -X POST ''http://localhost:8086/db/zermelo_test/users/zermelo?u=root&p=root''
19
- -d ''{"admin": true}'''
16
+ - 'curl -X POST ''http://localhost:8086/db/zermelo_test/users?u=root&p=root'' -d ''{"name": "zermelo", "password": "zermelo"}'''
17
+ - 'curl -X POST ''http://localhost:8086/db/zermelo_test/users/zermelo?u=root&p=root'' -d ''{"admin": true}'''
20
18
  notifications:
21
19
  hipchat:
22
20
  template:
data/CHANGELOG.md CHANGED
@@ -1,10 +1,16 @@
1
1
  ## Zermelo Changelog
2
2
 
3
+ # 1.1.0 - 2015-04-09
4
+
5
+ * refactored query builder to improve composability
6
+ * bugfix; source key not included when intersecting redis sets
7
+ * log redis return values as well when redis log is used
8
+
3
9
  # 1.0.1 - 2015-02-04
4
10
 
5
- * bugfix has_one class locking (779907d8d)
11
+ * bugfix; has_one class locking (779907d8d)
6
12
  * add save! command, raise exceptions on error (#13)
7
13
 
8
14
  # 1.0.0 - 2015-01-28
9
15
 
10
- * Initial release
16
+ * Initial release
@@ -14,7 +14,7 @@ module Zermelo
14
14
  @backend = parent.send(:backend)
15
15
 
16
16
  @record_ids_key = Zermelo::Records::Key.new(
17
- :klass => parent.class.send(:class_key),
17
+ :klass => parent.class,
18
18
  :id => parent.id,
19
19
  :name => 'belongs_to',
20
20
  :type => :hash,
@@ -88,10 +88,10 @@ module Zermelo
88
88
  @backend.clear(@record_ids_key)
89
89
  end
90
90
 
91
- def self.associated_ids_for(backend, class_key, name, inversed, *these_ids)
91
+ def self.associated_ids_for(backend, klass, name, inversed, *these_ids)
92
92
  these_ids.each_with_object({}) do |this_id, memo|
93
93
  key = Zermelo::Records::Key.new(
94
- :klass => class_key,
94
+ :klass => klass,
95
95
  :id => this_id,
96
96
  :name => 'belongs_to',
97
97
  :type => :hash,
@@ -23,7 +23,7 @@ module Zermelo
23
23
  @backend = parent.send(:backend)
24
24
 
25
25
  @record_ids_key = Zermelo::Records::Key.new(
26
- :klass => parent.class.send(:class_key),
26
+ :klass => parent.class,
27
27
  :id => parent.id,
28
28
  :name => "#{name}_ids",
29
29
  :type => :set,
@@ -110,10 +110,10 @@ module Zermelo
110
110
  @backend.filter(@record_ids_key, @associated_class)
111
111
  end
112
112
 
113
- def self.associated_ids_for(backend, class_key, name, *these_ids)
113
+ def self.associated_ids_for(backend, klass, name, *these_ids)
114
114
  these_ids.each_with_object({}) do |this_id, memo|
115
115
  key = Zermelo::Records::Key.new(
116
- :klass => class_key,
116
+ :klass => klass,
117
117
  :id => this_id,
118
118
  :name => "#{name}_ids",
119
119
  :type => :set,
@@ -19,7 +19,7 @@ module Zermelo
19
19
  @backend = parent.send(:backend)
20
20
 
21
21
  @record_ids_key = Zermelo::Records::Key.new(
22
- :klass => parent.class.send(:class_key),
22
+ :klass => parent.class,
23
23
  :id => parent.id,
24
24
  :name => "#{name}_ids",
25
25
  :type => :set,
@@ -102,10 +102,10 @@ module Zermelo
102
102
  @backend.filter(@record_ids_key, @associated_class)
103
103
  end
104
104
 
105
- def self.associated_ids_for(backend, class_key, name, *these_ids)
105
+ def self.associated_ids_for(backend, klass, name, *these_ids)
106
106
  these_ids.each_with_object({}) do |this_id, memo|
107
107
  key = Zermelo::Records::Key.new(
108
- :klass => class_key,
108
+ :klass => klass,
109
109
  :id => this_id,
110
110
  :name => "#{name}_ids",
111
111
  :type => :set,
@@ -9,7 +9,7 @@ module Zermelo
9
9
 
10
10
  # TODO would be better as a 'has_one' hash, a bit like belongs_to
11
11
  @record_id_key = Zermelo::Records::Key.new(
12
- :klass => parent.class.send(:class_key),
12
+ :klass => parent.class,
13
13
  :id => parent.id,
14
14
  :name => "#{name}_id",
15
15
  :type => :string,
@@ -91,10 +91,10 @@ module Zermelo
91
91
  @backend.clear(@record_id_key)
92
92
  end
93
93
 
94
- def self.associated_ids_for(backend, class_key, name, *these_ids)
94
+ def self.associated_ids_for(backend, klass, name, *these_ids)
95
95
  these_ids.each_with_object({}) do |this_id, memo|
96
96
  key = Zermelo::Records::Key.new(
97
- :klass => class_key,
97
+ :klass => klass,
98
98
  :id => this_id,
99
99
  :name => "#{name}_id",
100
100
  :type => :string,
@@ -21,7 +21,7 @@ module Zermelo
21
21
  @backend = parent.send(:backend)
22
22
 
23
23
  @record_ids_key = Zermelo::Records::Key.new(
24
- :klass => parent.class.send(:class_key),
24
+ :klass => parent.class,
25
25
  :id => parent.id,
26
26
  :name => "#{name}_ids",
27
27
  :type => :sorted_set,
@@ -32,7 +32,7 @@ module Zermelo
32
32
  @associated_class = data.data_klass
33
33
  @lock_klasses = [data.data_klass] + data.related_klasses
34
34
  @inverse = data.inverse
35
- @sort_key = data.sort_key
35
+ @sort_key = data.sort_key || backend.default_sort_key
36
36
  @callbacks = data.callbacks
37
37
  end
38
38
  end
@@ -106,10 +106,10 @@ module Zermelo
106
106
  @backend.filter(@record_ids_key, @associated_class)
107
107
  end
108
108
 
109
- def self.associated_ids_for(backend, class_key, name, *these_ids)
109
+ def self.associated_ids_for(backend, klass, name, *these_ids)
110
110
  these_ids.each_with_object({}) do |this_id, memo|
111
111
  key = Zermelo::Records::Key.new(
112
- :klass => class_key,
112
+ :klass => klass,
113
113
  :id => this_id,
114
114
  :name => "#{name}_ids",
115
115
  :type => :sorted_set,
@@ -9,7 +9,6 @@ module Zermelo
9
9
  @attribute_name = name
10
10
 
11
11
  @backend = parent_klass.send(:backend)
12
- @class_key = parent_klass.send(:class_key)
13
12
 
14
13
  @indexers = {}
15
14
 
@@ -38,7 +37,7 @@ module Zermelo
38
37
  raise "Can't index '#{@value}' (#{@attribute_type}" if index_keys.nil?
39
38
 
40
39
  @indexers[index_keys.join(":")] ||= Zermelo::Records::Key.new(
41
- :klass => @class_key,
40
+ :klass => @parent_klass,
42
41
  :name => "by_#{@attribute_name}:#{index_keys.join(':')}",
43
42
  :type => :set,
44
43
  :object => :index
@@ -9,7 +9,6 @@ module Zermelo
9
9
  @attribute_name = name
10
10
 
11
11
  @backend = parent_klass.send(:backend)
12
- @class_key = parent_klass.send(:class_key)
13
12
 
14
13
  @indexers = {}
15
14
 
@@ -32,7 +31,7 @@ module Zermelo
32
31
 
33
32
  def key
34
33
  @indexer ||= Zermelo::Records::Key.new(
35
- :klass => @class_key,
34
+ :klass => @parent_klass,
36
35
  :name => "by_#{@attribute_name}",
37
36
  :type => :hash,
38
37
  :object => :index
@@ -73,7 +73,7 @@ module Zermelo
73
73
  end
74
74
 
75
75
  def get(attr_key)
76
- get_multiple(attr_key)[attr_key.klass][attr_key.id][attr_key.name.to_s]
76
+ get_multiple(attr_key)[attr_key.klass.send(:class_key)][attr_key.id][attr_key.name.to_s]
77
77
  end
78
78
 
79
79
  def lock(*klasses, &block)
@@ -15,6 +15,10 @@ module Zermelo
15
15
 
16
16
  include Zermelo::Backends::Base
17
17
 
18
+ def default_sorted_set_key
19
+ :time
20
+ end
21
+
18
22
  def filter(ids_key, record)
19
23
  Zermelo::Filters::InfluxDBFilter.new(self, ids_key, record)
20
24
  end
@@ -22,28 +26,30 @@ module Zermelo
22
26
  # TODO get filter calling this instead of using same logic
23
27
  def exists?(key)
24
28
  return if key.id.nil?
25
- Zermelo.influxdb.query("SELECT id FROM /#{key.klass}\\/.*/ LIMIT 1").size > 0
29
+ class_key = attr_key.klass.send(:class_key)
30
+ Zermelo.influxdb.query("SELECT id FROM /#{class_key}\\/.*/ LIMIT 1").size > 0
26
31
  end
27
32
 
28
33
  # nb: does lots of queries, should batch, but ensuring single operations are correct
29
34
  # for now
30
35
  def get_multiple(*attr_keys)
31
36
  attr_keys.inject({}) do |memo, attr_key|
37
+ class_key = attr_key.klass.send(:class_key)
32
38
  begin
33
39
  records = Zermelo.influxdb.query("SELECT #{attr_key.name} FROM " +
34
- "\"#{attr_key.klass}/#{attr_key.id}\" LIMIT 1")["#{attr_key.klass}/#{attr_key.id}"]
40
+ "\"#{class_key}/#{attr_key.id}\" LIMIT 1")["#{class_key}/#{attr_key.id}"]
35
41
  rescue InfluxDB::Error => ide
36
42
  raise unless
37
- /^Field #{attr_key.name} doesn't exist in series #{attr_key.klass}\/#{attr_key.id}$/ === ide.message
43
+ /^Field #{attr_key.name} doesn't exist in series #{class_key}\/#{attr_key.id}$/ === ide.message
38
44
 
39
45
  records = []
40
46
  end
41
47
  value = (records && !records.empty?) ? records.first[attr_key.name.to_s] : nil
42
48
 
43
- memo[attr_key.klass] ||= {}
44
- memo[attr_key.klass][attr_key.id] ||= {}
49
+ memo[class_key] ||= {}
50
+ memo[class_key][attr_key.id] ||= {}
45
51
 
46
- memo[attr_key.klass][attr_key.id][attr_key.name.to_s] = if value.nil?
52
+ memo[class_key][attr_key.id][attr_key.name.to_s] = if value.nil?
47
53
  nil
48
54
  else
49
55
 
@@ -121,18 +127,20 @@ module Zermelo
121
127
 
122
128
  next if key.id.nil?
123
129
 
124
- records[key.klass] ||= {}
125
- records[key.klass][key.id] ||= {}
130
+ class_key = key.klass.send(:class_key)
131
+
132
+ records[class_key] ||= {}
133
+ records[class_key][key.id] ||= {}
126
134
 
127
- records[key.klass][key.id][key.name] = case op
135
+ records[class_key][key.id][key.name] = case op
128
136
  when :set
129
137
  case key.type
130
138
  when :string, :integer
131
- value.to_s
139
+ value.nil? ? nil : value.to_s
132
140
  when :timestamp
133
- value.to_f
141
+ value.nil? ? nil : value.to_f
134
142
  when :boolean
135
- (!!value).to_s
143
+ value.nil? ? nil : (!!value).to_s
136
144
  when :list, :hash
137
145
  value
138
146
  when :set
@@ -144,27 +152,30 @@ module Zermelo
144
152
  value
145
153
  when :set
146
154
  value.to_a
155
+ when :sorted_set
156
+ (1...value.size).step(2).collect {|i| value[i] }
147
157
  end
148
158
  when :purge
149
- purges << "\"#{key.klass}/#{key.id}\""
159
+ purges << "\"#{class_key}/#{key.id}\""
150
160
  end
151
161
 
152
162
  end
153
163
 
154
- records.each_pair do |klass, klass_records|
164
+ records.each_pair do |class_key, klass_records|
155
165
  klass_records.each_pair do |id, data|
156
166
  begin
157
- prior = Zermelo.influxdb.query("SELECT * FROM \"#{klass}/#{id}\" LIMIT 1")["#{klass}/#{id}"]
167
+ prior = Zermelo.influxdb.query("SELECT * FROM \"#{class_key}/#{id}\" LIMIT 1")["#{class_key}/#{id}"]
158
168
  rescue InfluxDB::Error => ide
159
169
  raise unless
160
- (/^Couldn't look up columns for series: #{klass}\/#{id}$/ === ide.message) ||
170
+ (/^Couldn't look up columns for series: #{class_key}\/#{id}$/ === ide.message) ||
161
171
  (/^Couldn't look up columns$/ === ide.message) ||
162
- (/^Couldn't find series: #{klass}\/#{id}$/ === ide.message)
172
+ (/^Couldn't find series: #{class_key}\/#{id}$/ === ide.message)
163
173
 
164
174
  prior = nil
165
175
  end
166
176
  record = prior.nil? ? {} : prior.first.delete_if {|k,v| ["time", "sequence_number"].include?(k) }
167
- Zermelo.influxdb.write_point("#{klass}/#{id}", record.merge(data).merge('id' => id))
177
+ data.delete('time') if data.has_key?('time') && data['time'].nil?
178
+ Zermelo.influxdb.write_point("#{class_key}/#{id}", record.merge(data).merge('id' => id))
168
179
  end
169
180
  end
170
181
 
@@ -11,6 +11,10 @@ module Zermelo
11
11
 
12
12
  include Zermelo::Backends::Base
13
13
 
14
+ def default_sorted_set_key
15
+ :timestamp
16
+ end
17
+
14
18
  def generate_lock
15
19
  Zermelo::Locks::RedisLock.new
16
20
  end
@@ -23,9 +27,11 @@ module Zermelo
23
27
  attr_keys.inject({}) do |memo, attr_key|
24
28
  redis_attr_key = key_to_redis_key(attr_key)
25
29
 
26
- memo[attr_key.klass] ||= {}
27
- memo[attr_key.klass][attr_key.id] ||= {}
28
- memo[attr_key.klass][attr_key.id][attr_key.name.to_s] = if Zermelo::COLLECTION_TYPES.has_key?(attr_key.type)
30
+ class_key = attr_key.klass.send(:class_key)
31
+
32
+ memo[class_key] ||= {}
33
+ memo[class_key][attr_key.id] ||= {}
34
+ memo[class_key][attr_key.id][attr_key.name.to_s] = if Zermelo::COLLECTION_TYPES.has_key?(attr_key.type)
29
35
 
30
36
  case attr_key.type
31
37
  when :list
@@ -126,8 +132,9 @@ module Zermelo
126
132
  true
127
133
  end
128
134
 
129
- # used by redis_filter
130
135
  def key_to_redis_key(key)
136
+ class_key = key.klass.send(:class_key)
137
+
131
138
  obj = case key.object
132
139
  when :attribute
133
140
  'attrs'
@@ -135,11 +142,104 @@ module Zermelo
135
142
  'assocs'
136
143
  when :index
137
144
  'indices'
145
+ when :temporary
146
+ 'tmp'
138
147
  end
139
148
 
140
149
  name = Zermelo::COLLECTION_TYPES.has_key?(key.type) ? ":#{key.name}" : ''
141
150
 
142
- "#{key.klass}:#{key.id.nil? ? '' : key.id}:#{obj}#{name}"
151
+ "#{class_key}:#{key.id.nil? ? '' : key.id}:#{obj}#{name}"
152
+ end
153
+
154
+ def temp_key_wrap
155
+ return unless block_given?
156
+ temp_keys = []
157
+ begin
158
+ yield(temp_keys)
159
+ rescue
160
+ raise
161
+ ensure
162
+ unless temp_keys.empty?
163
+ Zermelo.redis.del(*temp_keys.collect {|tk| key_to_redis_key(tk)})
164
+ temp_keys.clear
165
+ end
166
+ end
167
+ end
168
+
169
+ # TODO converge usage of idx_class and _index lookup invocation
170
+
171
+ def index_lookup(att, associated_class, idx_class, value, attr_type, temp_keys)
172
+ case value
173
+ # when Zermelo::Filters::RedisFilter
174
+
175
+ # TODO (maybe) if a filter from different backend, resolve to ids and
176
+ # put that in a Redis temp set
177
+
178
+ # collection, should_be_deleted = value.resolve_steps
179
+
180
+ # if should_be_deleted
181
+ # temp_sets << collection.name
182
+ # end
183
+
184
+ # unless :set.eql?(collection.type)
185
+ # raise "Unsure as yet if non-sets are safe as Filter step values"
186
+ # end
187
+
188
+ when Regexp
189
+ raise "Can't query non-string values via regexp" unless :string.eql?(attr_type)
190
+
191
+ idx_key = associated_class.send(:temp_key, :set)
192
+ temp_keys << idx_key
193
+ idx_result = key_to_redis_key(idx_key)
194
+
195
+ starts_with_string_re = /^string:/
196
+ case idx_class.name
197
+ when 'Zermelo::Associations::UniqueIndex'
198
+ index_key = key_to_redis_key(Zermelo::Records::Key.new(
199
+ :klass => associated_class,
200
+ :name => "by_#{att}",
201
+ :type => :hash,
202
+ :object => :index
203
+ ))
204
+ candidates = Zermelo.redis.hgetall(index_key)
205
+ matching_ids = candidates.values_at(*candidates.keys.select {|k|
206
+ (starts_with_string_re === k) &&
207
+ (value === unescape_key_name(k.sub(starts_with_string_re, '')))
208
+ })
209
+ Zermelo.redis.sadd(idx_result, matching_ids) unless matching_ids.empty?
210
+ when 'Zermelo::Associations::Index'
211
+ key_root = key_to_redis_key(Zermelo::Records::Key.new(
212
+ :klass => associated_class,
213
+ :name => "by_#{att}:string",
214
+ :type => :set,
215
+ :object => :index
216
+ ))
217
+
218
+ matching_sets = Zermelo.redis.keys(key_root + ":*").inject([]) do |memo, k|
219
+ k =~ /^#{key_root}:(.+)$/
220
+ memo << k if value === $1
221
+ memo
222
+ end
223
+
224
+ Zermelo.redis.sunionstore(idx_result, matching_sets) unless matching_sets.empty?
225
+ end
226
+ idx_key
227
+ else
228
+ index = associated_class.send("#{att}_index")
229
+
230
+ case index
231
+ when Zermelo::Associations::UniqueIndex
232
+ idx_key = associated_class.send(:temp_key, :set)
233
+ temp_keys << idx_key
234
+
235
+ Zermelo.redis.sadd(key_to_redis_key(idx_key),
236
+ Zermelo.redis.hget(key_to_redis_key(index.key),
237
+ index_keys(attr_type, value).join(':')))
238
+ idx_key
239
+ when Zermelo::Associations::Index
240
+ index.key(value)
241
+ end
242
+ end
143
243
  end
144
244
 
145
245
  private
@@ -230,7 +330,7 @@ module Zermelo
230
330
 
231
331
  elsif :purge.eql?(op)
232
332
  # TODO get keys for all assocs & indices, purge them too
233
- purges << ["#{key.klass}:#{key.id}:attrs"]
333
+ purges << ["#{key.klass.send(:class_key)}:#{key.id}:attrs"]
234
334
  else
235
335
  simple_attr_key = key_to_redis_key(key)
236
336
  simple_attrs[simple_attr_key] ||= {}
@@ -2,15 +2,10 @@ require 'active_support/concern'
2
2
 
3
3
  require 'zermelo/records/errors'
4
4
 
5
- require 'zermelo/filters/steps/diff_range_step'
6
- require 'zermelo/filters/steps/diff_step'
7
- require 'zermelo/filters/steps/intersect_range_step'
8
- require 'zermelo/filters/steps/intersect_step'
9
- require 'zermelo/filters/steps/limit_step'
10
- require 'zermelo/filters/steps/offset_step'
5
+ require 'zermelo/filters/steps/list_step'
6
+ require 'zermelo/filters/steps/set_step'
7
+ require 'zermelo/filters/steps/sorted_set_step'
11
8
  require 'zermelo/filters/steps/sort_step'
12
- require 'zermelo/filters/steps/union_range_step'
13
- require 'zermelo/filters/steps/union_step'
14
9
 
15
10
  module Zermelo
16
11
 
@@ -24,75 +19,74 @@ module Zermelo
24
19
 
25
20
  # initial set a Zermelo::Record::Key object
26
21
  # associated_class the class of the result record
27
- def initialize(data_backend, initial_set, associated_class, ancestor = nil, step = nil)
22
+ def initialize(data_backend, initial_key, associated_class, ancestor = nil, step = nil)
28
23
  @backend = data_backend
29
- @initial_set = initial_set
24
+ @initial_key = initial_key
30
25
  @associated_class = associated_class
31
26
  @steps = ancestor.nil? ? [] : ancestor.steps.dup
32
27
  @steps << step unless step.nil?
33
28
  end
34
29
 
35
- # TODO each step type have class methods list its acceptable input types, and
36
- # have a class method giving its output type
37
-
38
30
  def intersect(attrs = {})
39
- self.class.new(@backend, @initial_set, @associated_class, self,
40
- ::Zermelo::Filters::Steps::IntersectStep.new({}, attrs))
31
+ self.class.new(@backend, @initial_key, @associated_class, self,
32
+ ::Zermelo::Filters::Steps::SetStep.new({:op => :intersect}, attrs))
41
33
  end
42
34
 
43
35
  def union(attrs = {})
44
- self.class.new(@backend, @initial_set, @associated_class, self,
45
- ::Zermelo::Filters::Steps::UnionStep.new({}, attrs))
36
+ self.class.new(@backend, @initial_key, @associated_class, self,
37
+ ::Zermelo::Filters::Steps::SetStep.new({:op => :union}, attrs))
46
38
  end
47
39
 
48
40
  def diff(attrs = {})
49
- self.class.new(@backend, @initial_set, @associated_class, self,
50
- ::Zermelo::Filters::Steps::DiffStep.new({}, attrs))
41
+ self.class.new(@backend, @initial_key, @associated_class, self,
42
+ ::Zermelo::Filters::Steps::SetStep.new({:op => :diff}, attrs))
51
43
  end
52
44
 
53
45
  def sort(keys, opts = {})
54
- self.class.new(@backend, @initial_set, @associated_class, self,
46
+ self.class.new(@backend, @initial_key, @associated_class, self,
55
47
  ::Zermelo::Filters::Steps::SortStep.new({:keys => keys,
56
48
  :desc => opts[:desc], :limit => opts[:limit],
57
49
  :offset => opts[:offset]}, {})
58
50
  )
59
51
  end
60
52
 
61
- def limit(amount)
62
- self.class.new(@backend, @initial_set, @associated_class, self,
63
- ::Zermelo::Filters::Steps::LimitStep.new({:amount => amount}, {}))
53
+ def offset(amount, opts = {})
54
+ self.class.new(@backend, @initial_key, @associated_class, self,
55
+ ::Zermelo::Filters::Steps::ListStep.new({:offset => amount,
56
+ :limit => opts[:limit]}, {}))
64
57
  end
65
58
 
66
- def offset(amount)
67
- self.class.new(@backend, @initial_set, @associated_class, self,
68
- ::Zermelo::Filters::Steps::OffsetStep.new({:amount => amount}, {}))
59
+ def page(num, opts = {})
60
+ per_page = opts[:per_page].to_i || 20
61
+ start = per_page * (num - 1)
62
+ finish = start + (per_page - 1)
63
+ self.class.new(@backend, @initial_key, @associated_class, self,
64
+ ::Zermelo::Filters::Steps::ListStep.new({:offset => start,
65
+ :limit => per_page}, {}))
69
66
  end
70
67
 
71
68
  def intersect_range(start, finish, attrs_opts = {})
72
- self.class.new(@backend, @initial_set, @associated_class, self,
73
- ::Zermelo::Filters::Steps::IntersectRangeStep.new(
74
- {:start => start, :finish => finish,
75
- :desc => attrs_opts.delete(:desc),
69
+ self.class.new(@backend, @initial_key, @associated_class, self,
70
+ ::Zermelo::Filters::Steps::SortedSetStep.new(
71
+ {:op => :intersect_range, :start => start, :finish => finish,
76
72
  :by_score => attrs_opts.delete(:by_score)},
77
73
  attrs_opts)
78
74
  )
79
75
  end
80
76
 
81
77
  def union_range(start, finish, attrs_opts = {})
82
- self.class.new(@backend, @initial_set, @associated_class, self,
83
- ::Zermelo::Filters::Steps::UnionRangeStep.new(
84
- {:start => start, :finish => finish,
85
- :desc => attrs_opts.delete(:desc),
78
+ self.class.new(@backend, @initial_key, @associated_class, self,
79
+ ::Zermelo::Filters::Steps::SortedSetStep.new(
80
+ {:op => :union_range, :start => start, :finish => finish,
86
81
  :by_score => attrs_opts.delete(:by_score)},
87
82
  attrs_opts)
88
83
  )
89
84
  end
90
85
 
91
86
  def diff_range(start, finish, attrs_opts = {})
92
- self.class.new(@backend, @initial_set, @associated_class, self,
93
- ::Zermelo::Filters::Steps::DiffRangeStep.new(
94
- {:start => start, :finish => finish,
95
- :desc => attrs_opts.delete(:desc),
87
+ self.class.new(@backend, @initial_key, @associated_class, self,
88
+ ::Zermelo::Filters::Steps::SortedSetStep.new(
89
+ {:op => :diff_range, :start => start, :finish => finish,
96
90
  :by_score => attrs_opts.delete(:by_score)},
97
91
  attrs_opts)
98
92
  )
@@ -141,23 +135,6 @@ module Zermelo
141
135
  lock { _all }
142
136
  end
143
137
 
144
- # NB makes no sense to apply this without order clause
145
- def page(num, opts = {})
146
- ret = nil
147
- per_page = opts[:per_page].to_i || 20
148
- if (num > 0) && (per_page > 0)
149
- lock do
150
- start = per_page * (num - 1)
151
- finish = start + (per_page - 1)
152
- @steps += [Zermelo::Filters::Steps::OffsetStep.new({:amount => start}, {}),
153
- Zermelo::Filters::Steps::LimitStep.new({:amount => per_page}, {})]
154
- page_ids = _ids
155
- ret = page_ids.collect {|f_id| _load(f_id)} unless page_ids.nil?
156
- end
157
- end
158
- ret || []
159
- end
160
-
161
138
  def collect(&block)
162
139
  lock { _ids.collect {|id| block.call(_load(id))} }
163
140
  end
@@ -191,11 +168,11 @@ module Zermelo
191
168
  case klass.name
192
169
  when ::Zermelo::Associations::BelongsTo.name
193
170
  klass.send(:associated_ids_for, @backend,
194
- @associated_class.send(:class_key), name,
171
+ @associated_class, name,
195
172
  options[:inversed].is_a?(TrueClass), *_ids)
196
173
  else
197
174
  klass.send(:associated_ids_for, @backend,
198
- @associated_class.send(:class_key), name, *_ids)
175
+ @associated_class, name, *_ids)
199
176
  end
200
177
  }
201
178
  end