zermelo 1.0.1 → 1.1.0

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