zermelo 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +76 -52
- data/lib/zermelo/associations/association_data.rb +4 -3
- data/lib/zermelo/associations/class_methods.rb +37 -50
- data/lib/zermelo/associations/index.rb +3 -1
- data/lib/zermelo/associations/multiple.rb +247 -0
- data/lib/zermelo/associations/range_index.rb +44 -0
- data/lib/zermelo/associations/singular.rb +193 -0
- data/lib/zermelo/associations/unique_index.rb +4 -3
- data/lib/zermelo/backend.rb +120 -0
- data/lib/zermelo/backends/{influxdb_backend.rb → influxdb.rb} +87 -31
- data/lib/zermelo/backends/{redis_backend.rb → redis.rb} +53 -58
- data/lib/zermelo/backends/stub.rb +43 -0
- data/lib/zermelo/filter.rb +194 -0
- data/lib/zermelo/filters/index_range.rb +22 -0
- data/lib/zermelo/filters/{influxdb_filter.rb → influxdb.rb} +12 -11
- data/lib/zermelo/filters/redis.rb +173 -0
- data/lib/zermelo/filters/steps/list_step.rb +48 -30
- data/lib/zermelo/filters/steps/set_step.rb +148 -89
- data/lib/zermelo/filters/steps/sort_step.rb +2 -2
- data/lib/zermelo/record.rb +53 -0
- data/lib/zermelo/records/attributes.rb +32 -0
- data/lib/zermelo/records/class_methods.rb +12 -25
- data/lib/zermelo/records/{influxdb_record.rb → influxdb.rb} +3 -4
- data/lib/zermelo/records/instance_methods.rb +9 -8
- data/lib/zermelo/records/key.rb +3 -1
- data/lib/zermelo/records/redis.rb +17 -0
- data/lib/zermelo/records/stub.rb +17 -0
- data/lib/zermelo/version.rb +1 -1
- data/spec/lib/zermelo/associations/index_spec.rb +70 -1
- data/spec/lib/zermelo/associations/multiple_spec.rb +1084 -0
- data/spec/lib/zermelo/associations/range_index_spec.rb +77 -0
- data/spec/lib/zermelo/associations/singular_spec.rb +149 -0
- data/spec/lib/zermelo/associations/unique_index_spec.rb +58 -2
- data/spec/lib/zermelo/filter_spec.rb +363 -0
- data/spec/lib/zermelo/locks/redis_lock_spec.rb +3 -3
- data/spec/lib/zermelo/records/instance_methods_spec.rb +206 -0
- data/spec/spec_helper.rb +9 -1
- data/spec/support/mock_logger.rb +48 -0
- metadata +31 -46
- data/lib/zermelo/associations/belongs_to.rb +0 -115
- data/lib/zermelo/associations/has_and_belongs_to_many.rb +0 -128
- data/lib/zermelo/associations/has_many.rb +0 -120
- data/lib/zermelo/associations/has_one.rb +0 -109
- data/lib/zermelo/associations/has_sorted_set.rb +0 -124
- data/lib/zermelo/backends/base.rb +0 -115
- data/lib/zermelo/filters/base.rb +0 -212
- data/lib/zermelo/filters/redis_filter.rb +0 -111
- data/lib/zermelo/filters/steps/sorted_set_step.rb +0 -156
- data/lib/zermelo/records/base.rb +0 -62
- data/lib/zermelo/records/redis_record.rb +0 -27
- data/spec/lib/zermelo/associations/belongs_to_spec.rb +0 -6
- data/spec/lib/zermelo/associations/has_many_spec.rb +0 -6
- data/spec/lib/zermelo/associations/has_one_spec.rb +0 -6
- data/spec/lib/zermelo/associations/has_sorted_set.spec.rb +0 -6
- data/spec/lib/zermelo/backends/influxdb_backend_spec.rb +0 -0
- data/spec/lib/zermelo/backends/moneta_backend_spec.rb +0 -0
- data/spec/lib/zermelo/filters/influxdb_filter_spec.rb +0 -0
- data/spec/lib/zermelo/filters/redis_filter_spec.rb +0 -0
- data/spec/lib/zermelo/records/influxdb_record_spec.rb +0 -434
- data/spec/lib/zermelo/records/key_spec.rb +0 -6
- data/spec/lib/zermelo/records/redis_record_spec.rb +0 -1461
- data/spec/lib/zermelo/records/type_validator_spec.rb +0 -6
- data/spec/lib/zermelo/version_spec.rb +0 -6
- data/spec/lib/zermelo_spec.rb +0 -6
@@ -1,111 +0,0 @@
|
|
1
|
-
require 'zermelo/filters/base'
|
2
|
-
|
3
|
-
# TODO check escaping of ids and index_keys -- shouldn't allow bare :
|
4
|
-
|
5
|
-
module Zermelo
|
6
|
-
|
7
|
-
module Filters
|
8
|
-
|
9
|
-
class RedisFilter
|
10
|
-
|
11
|
-
include Zermelo::Filters::Base
|
12
|
-
|
13
|
-
# TODO polite error when first/last applied to set
|
14
|
-
|
15
|
-
# more step users
|
16
|
-
def first
|
17
|
-
lock {
|
18
|
-
first_id = resolve_steps(:first)
|
19
|
-
first_id.nil? ? nil : _load(first_id)
|
20
|
-
}
|
21
|
-
end
|
22
|
-
|
23
|
-
def last
|
24
|
-
lock {
|
25
|
-
last_id = resolve_steps(:last)
|
26
|
-
last_id.nil? ? nil : _load(last_id)
|
27
|
-
}
|
28
|
-
end
|
29
|
-
# end step users
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def _count
|
34
|
-
resolve_steps(:count)
|
35
|
-
end
|
36
|
-
|
37
|
-
def _ids
|
38
|
-
resolve_steps(:ids)
|
39
|
-
end
|
40
|
-
|
41
|
-
def _exists?(id)
|
42
|
-
return if id.nil?
|
43
|
-
resolve_steps(:exists?, id)
|
44
|
-
end
|
45
|
-
|
46
|
-
# If called with a block -- takes a block and passes the name of a set to
|
47
|
-
# it; deletes all temporary sets once done
|
48
|
-
|
49
|
-
# If called with any arguments -- treats them as a hash of shortcuts
|
50
|
-
|
51
|
-
# If not called with any arguments -- returns two values, the first is
|
52
|
-
# the name of a set containing the filtered ids, the second is a boolean
|
53
|
-
# for whether or not to clear up that set once it's been used
|
54
|
-
|
55
|
-
def resolve_steps(shortcut, *args)
|
56
|
-
if @steps.empty?
|
57
|
-
sc_klass = {
|
58
|
-
:list => Zermelo::Filters::Steps::ListStep,
|
59
|
-
:set => Zermelo::Filters::Steps::SetStep,
|
60
|
-
:sorted_set => Zermelo::Filters::Steps::SortedSetStep
|
61
|
-
}[@initial_key.type]
|
62
|
-
sc = sc_klass.const_get(:REDIS_SHORTCUTS)[shortcut]
|
63
|
-
ret = if sc.nil?
|
64
|
-
yield(@initial_key)
|
65
|
-
else
|
66
|
-
sc.call(*([backend.key_to_redis_key(@initial_key)] + args))
|
67
|
-
end
|
68
|
-
return(ret)
|
69
|
-
end
|
70
|
-
|
71
|
-
idx_attrs = @associated_class.send(:with_index_data) do |d|
|
72
|
-
d.each_with_object({}) do |(name, data), memo|
|
73
|
-
memo[name.to_s] = data.index_klass
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
attr_types = @associated_class.send(:attribute_types)
|
78
|
-
|
79
|
-
backend.temp_key_wrap do |temp_keys|
|
80
|
-
result = nil
|
81
|
-
last_step = @steps.last
|
82
|
-
|
83
|
-
step_opts = {
|
84
|
-
:index_attrs => idx_attrs,
|
85
|
-
:attr_types => attr_types,
|
86
|
-
:temp_keys => temp_keys,
|
87
|
-
:source => @initial_key
|
88
|
-
}
|
89
|
-
|
90
|
-
@steps.each do |step|
|
91
|
-
unless step.class.accepted_types.include?(step_opts[:source].type)
|
92
|
-
raise "'#{step.class.name}' does not accept input type #{step_opts[:source].type}"
|
93
|
-
end
|
94
|
-
|
95
|
-
if step == last_step
|
96
|
-
step_opts.update(:shortcut => shortcut, :shortcut_args => args)
|
97
|
-
end
|
98
|
-
|
99
|
-
result = step.resolve(backend, @associated_class, step_opts)
|
100
|
-
|
101
|
-
step_opts[:source] = result unless step == last_step
|
102
|
-
end
|
103
|
-
|
104
|
-
result
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
end
|
@@ -1,156 +0,0 @@
|
|
1
|
-
require 'zermelo/filters/steps/base_step'
|
2
|
-
|
3
|
-
module Zermelo
|
4
|
-
module Filters
|
5
|
-
class Steps
|
6
|
-
class SortedSetStep < Zermelo::Filters::Steps::BaseStep
|
7
|
-
def self.accepted_types
|
8
|
-
[:sorted_set]
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.returns_type
|
12
|
-
:sorted_set
|
13
|
-
end
|
14
|
-
|
15
|
-
REDIS_SHORTCUTS = {
|
16
|
-
:ids => proc {|key| Zermelo.redis.zrange(key, 0, -1) },
|
17
|
-
:count => proc {|key| Zermelo.redis.zcard(key) },
|
18
|
-
:exists? => proc {|key, id| !Zermelo.redis.zscore(key, id).nil? },
|
19
|
-
:first => proc {|key| Zermelo.redis.zrange(key, 0, 0).first },
|
20
|
-
:last => proc {|key| Zermelo.redis.zrevrange(key, 0, 0).first }
|
21
|
-
}
|
22
|
-
|
23
|
-
def resolve(backend, associated_class, opts = {})
|
24
|
-
op = @options[:op]
|
25
|
-
start = @options[:start]
|
26
|
-
finish = @options[:finish]
|
27
|
-
|
28
|
-
case backend
|
29
|
-
when Zermelo::Backends::RedisBackend
|
30
|
-
source = opts[:source]
|
31
|
-
idx_attrs = opts[:index_attrs]
|
32
|
-
attr_types = opts[:attr_types]
|
33
|
-
temp_keys = opts[:temp_keys]
|
34
|
-
|
35
|
-
range_temp_key = associated_class.send(:temp_key, :sorted_set)
|
36
|
-
temp_keys << range_temp_key
|
37
|
-
range_ids_set = backend.key_to_redis_key(range_temp_key)
|
38
|
-
|
39
|
-
if @options[:by_score]
|
40
|
-
start = '-inf' if start.nil? || (start <= 0)
|
41
|
-
finish = '+inf' if finish.nil? || (finish <= 0)
|
42
|
-
else
|
43
|
-
start = 0 if start.nil?
|
44
|
-
finish = -1 if finish.nil?
|
45
|
-
end
|
46
|
-
|
47
|
-
args = [start, finish]
|
48
|
-
|
49
|
-
if @options[:by_score]
|
50
|
-
query = :zrangebyscore
|
51
|
-
args = args.map(&:to_s)
|
52
|
-
else
|
53
|
-
query = :zrange
|
54
|
-
end
|
55
|
-
|
56
|
-
args << {:with_scores => :true}
|
57
|
-
|
58
|
-
if @options[:limit]
|
59
|
-
args.last.update(:limit => [0, @options[:limit].to_i])
|
60
|
-
end
|
61
|
-
|
62
|
-
r_source = backend.key_to_redis_key(source)
|
63
|
-
args.unshift(r_source)
|
64
|
-
|
65
|
-
range_ids_scores = Zermelo.redis.send(query, *args)
|
66
|
-
|
67
|
-
unless range_ids_scores.empty?
|
68
|
-
Zermelo.redis.zadd(range_ids_set, range_ids_scores.map(&:reverse))
|
69
|
-
end
|
70
|
-
|
71
|
-
self.class.evaluate(backend, @options[:op], associated_class,
|
72
|
-
source, [range_temp_key], temp_keys, opts)
|
73
|
-
|
74
|
-
when Zermelo::Backends::InfluxDBBackend
|
75
|
-
|
76
|
-
query = ''
|
77
|
-
|
78
|
-
unless opts[:first].is_a?(TrueClass)
|
79
|
-
case @options[:op]
|
80
|
-
when :intersect_range, :diff_range
|
81
|
-
query += ' AND '
|
82
|
-
when :union_range
|
83
|
-
query += ' OR '
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
start = nil if !start.nil? && (start <= 0)
|
88
|
-
finish = nil if !finish.nil? && (finish <= 0)
|
89
|
-
|
90
|
-
unless start.nil? && finish.nil?
|
91
|
-
time_q = []
|
92
|
-
|
93
|
-
case @options[:op]
|
94
|
-
when :intersect_range, :union_range
|
95
|
-
unless start.nil?
|
96
|
-
time_q << "(time > #{start - 1}s)"
|
97
|
-
end
|
98
|
-
unless finish.nil?
|
99
|
-
time_q << "(time < #{finish}s)"
|
100
|
-
end
|
101
|
-
when :diff_range
|
102
|
-
unless start.nil?
|
103
|
-
time_q << "(time < #{start}s)"
|
104
|
-
end
|
105
|
-
unless finish.nil?
|
106
|
-
time_q << "(time > #{finish - 1}s)"
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
query += time_q.join(' AND ')
|
111
|
-
end
|
112
|
-
|
113
|
-
query += ")"
|
114
|
-
query
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def self.evaluate(backend, op, associated_class, source, source_keys, temp_keys, opts = {})
|
119
|
-
shortcut = opts[:shortcut]
|
120
|
-
|
121
|
-
weights = case op
|
122
|
-
when :union, :union_range
|
123
|
-
[0.0] * source_keys.length
|
124
|
-
when :diff, :diff_range
|
125
|
-
[-1.0] * source_keys.length
|
126
|
-
end
|
127
|
-
|
128
|
-
r_source = backend.key_to_redis_key(source)
|
129
|
-
r_source_keys = source_keys.collect {|sk| backend.key_to_redis_key(sk) }
|
130
|
-
|
131
|
-
dest_sorted_set = associated_class.send(:temp_key, :sorted_set)
|
132
|
-
temp_keys << dest_sorted_set
|
133
|
-
r_dest_sorted_set = backend.key_to_redis_key(dest_sorted_set)
|
134
|
-
|
135
|
-
case op
|
136
|
-
when :union, :union_range
|
137
|
-
Zermelo.redis.zinterstore(r_dest_sorted_set, r_source_keys, :weights => weights, :aggregate => 'max')
|
138
|
-
Zermelo.redis.zunionstore(r_dest_sorted_set, [r_source, r_dest_sorted_set])
|
139
|
-
when :intersect, :intersect_range
|
140
|
-
Zermelo.redis.zinterstore(r_dest_sorted_set, [r_source] + r_source_keys, :weights => weights, :aggregate => 'max')
|
141
|
-
when :diff, :diff_range
|
142
|
-
# 'zdiffstore' via weights, relies on non-zero scores being used
|
143
|
-
# see https://code.google.com/p/redis/issues/detail?id=579
|
144
|
-
Zermelo.redis.zinterstore(r_dest_sorted_set, r_source_keys, :weights => weights, :aggregate => 'max')
|
145
|
-
Zermelo.redis.zunionstore(r_dest_sorted_set, [r_source, r_dest_sorted_set])
|
146
|
-
Zermelo.redis.zremrangebyscore(r_dest_sorted_set, "0", "0")
|
147
|
-
end
|
148
|
-
|
149
|
-
return dest_sorted_set if shortcut.nil?
|
150
|
-
REDIS_SHORTCUTS[shortcut].call(*([r_dest_sorted_set] + opts[:shortcut_args]))
|
151
|
-
end
|
152
|
-
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
data/lib/zermelo/records/base.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
require 'monitor'
|
2
|
-
|
3
|
-
require 'active_support/concern'
|
4
|
-
require 'active_support/core_ext/object/blank'
|
5
|
-
require 'active_support/inflector'
|
6
|
-
require 'active_model'
|
7
|
-
|
8
|
-
require 'zermelo/associations/class_methods'
|
9
|
-
|
10
|
-
require 'zermelo/records/instance_methods'
|
11
|
-
require 'zermelo/records/class_methods'
|
12
|
-
require 'zermelo/records/type_validator'
|
13
|
-
|
14
|
-
# TODO escape ids and index_keys -- shouldn't allow bare :
|
15
|
-
|
16
|
-
# TODO callbacks on before/after add/delete on association?
|
17
|
-
|
18
|
-
# TODO optional sort via Redis SORT, first/last for has_many via those
|
19
|
-
|
20
|
-
# TODO get DIFF working for exclusion case against ZSETs
|
21
|
-
|
22
|
-
module Zermelo
|
23
|
-
|
24
|
-
module Records
|
25
|
-
|
26
|
-
module Base
|
27
|
-
|
28
|
-
extend ActiveSupport::Concern
|
29
|
-
|
30
|
-
include Zermelo::Records::InstMethods
|
31
|
-
|
32
|
-
included do
|
33
|
-
include ActiveModel::AttributeMethods
|
34
|
-
extend ActiveModel::Callbacks
|
35
|
-
include ActiveModel::Dirty
|
36
|
-
include ActiveModel::Validations
|
37
|
-
include ActiveModel::Validations::Callbacks
|
38
|
-
|
39
|
-
# include ActiveModel::MassAssignmentSecurity
|
40
|
-
|
41
|
-
@lock = Monitor.new
|
42
|
-
|
43
|
-
extend Zermelo::Records::ClassMethods
|
44
|
-
extend Zermelo::Associations::ClassMethods
|
45
|
-
|
46
|
-
attr_accessor :attributes
|
47
|
-
|
48
|
-
define_model_callbacks :create, :update, :destroy
|
49
|
-
|
50
|
-
attribute_method_suffix "=" # attr_writers
|
51
|
-
# attribute_method_suffix "" # attr_readers # DEPRECATED
|
52
|
-
|
53
|
-
validates_with Zermelo::Records::TypeValidator
|
54
|
-
|
55
|
-
define_attributes :id => :string
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
|
3
|
-
require 'zermelo/records/base'
|
4
|
-
|
5
|
-
# TODO check escaping of ids and index_keys -- shouldn't allow bare :, ' '
|
6
|
-
|
7
|
-
# TODO callbacks on before/after add/delete on association?
|
8
|
-
|
9
|
-
module Zermelo
|
10
|
-
|
11
|
-
module Records
|
12
|
-
|
13
|
-
module RedisRecord
|
14
|
-
|
15
|
-
extend ActiveSupport::Concern
|
16
|
-
|
17
|
-
include Zermelo::Records::Base
|
18
|
-
|
19
|
-
included do
|
20
|
-
set_backend :redis
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|