zermelo 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +76 -52
  4. data/lib/zermelo/associations/association_data.rb +4 -3
  5. data/lib/zermelo/associations/class_methods.rb +37 -50
  6. data/lib/zermelo/associations/index.rb +3 -1
  7. data/lib/zermelo/associations/multiple.rb +247 -0
  8. data/lib/zermelo/associations/range_index.rb +44 -0
  9. data/lib/zermelo/associations/singular.rb +193 -0
  10. data/lib/zermelo/associations/unique_index.rb +4 -3
  11. data/lib/zermelo/backend.rb +120 -0
  12. data/lib/zermelo/backends/{influxdb_backend.rb → influxdb.rb} +87 -31
  13. data/lib/zermelo/backends/{redis_backend.rb → redis.rb} +53 -58
  14. data/lib/zermelo/backends/stub.rb +43 -0
  15. data/lib/zermelo/filter.rb +194 -0
  16. data/lib/zermelo/filters/index_range.rb +22 -0
  17. data/lib/zermelo/filters/{influxdb_filter.rb → influxdb.rb} +12 -11
  18. data/lib/zermelo/filters/redis.rb +173 -0
  19. data/lib/zermelo/filters/steps/list_step.rb +48 -30
  20. data/lib/zermelo/filters/steps/set_step.rb +148 -89
  21. data/lib/zermelo/filters/steps/sort_step.rb +2 -2
  22. data/lib/zermelo/record.rb +53 -0
  23. data/lib/zermelo/records/attributes.rb +32 -0
  24. data/lib/zermelo/records/class_methods.rb +12 -25
  25. data/lib/zermelo/records/{influxdb_record.rb → influxdb.rb} +3 -4
  26. data/lib/zermelo/records/instance_methods.rb +9 -8
  27. data/lib/zermelo/records/key.rb +3 -1
  28. data/lib/zermelo/records/redis.rb +17 -0
  29. data/lib/zermelo/records/stub.rb +17 -0
  30. data/lib/zermelo/version.rb +1 -1
  31. data/spec/lib/zermelo/associations/index_spec.rb +70 -1
  32. data/spec/lib/zermelo/associations/multiple_spec.rb +1084 -0
  33. data/spec/lib/zermelo/associations/range_index_spec.rb +77 -0
  34. data/spec/lib/zermelo/associations/singular_spec.rb +149 -0
  35. data/spec/lib/zermelo/associations/unique_index_spec.rb +58 -2
  36. data/spec/lib/zermelo/filter_spec.rb +363 -0
  37. data/spec/lib/zermelo/locks/redis_lock_spec.rb +3 -3
  38. data/spec/lib/zermelo/records/instance_methods_spec.rb +206 -0
  39. data/spec/spec_helper.rb +9 -1
  40. data/spec/support/mock_logger.rb +48 -0
  41. metadata +31 -46
  42. data/lib/zermelo/associations/belongs_to.rb +0 -115
  43. data/lib/zermelo/associations/has_and_belongs_to_many.rb +0 -128
  44. data/lib/zermelo/associations/has_many.rb +0 -120
  45. data/lib/zermelo/associations/has_one.rb +0 -109
  46. data/lib/zermelo/associations/has_sorted_set.rb +0 -124
  47. data/lib/zermelo/backends/base.rb +0 -115
  48. data/lib/zermelo/filters/base.rb +0 -212
  49. data/lib/zermelo/filters/redis_filter.rb +0 -111
  50. data/lib/zermelo/filters/steps/sorted_set_step.rb +0 -156
  51. data/lib/zermelo/records/base.rb +0 -62
  52. data/lib/zermelo/records/redis_record.rb +0 -27
  53. data/spec/lib/zermelo/associations/belongs_to_spec.rb +0 -6
  54. data/spec/lib/zermelo/associations/has_many_spec.rb +0 -6
  55. data/spec/lib/zermelo/associations/has_one_spec.rb +0 -6
  56. data/spec/lib/zermelo/associations/has_sorted_set.spec.rb +0 -6
  57. data/spec/lib/zermelo/backends/influxdb_backend_spec.rb +0 -0
  58. data/spec/lib/zermelo/backends/moneta_backend_spec.rb +0 -0
  59. data/spec/lib/zermelo/filters/influxdb_filter_spec.rb +0 -0
  60. data/spec/lib/zermelo/filters/redis_filter_spec.rb +0 -0
  61. data/spec/lib/zermelo/records/influxdb_record_spec.rb +0 -434
  62. data/spec/lib/zermelo/records/key_spec.rb +0 -6
  63. data/spec/lib/zermelo/records/redis_record_spec.rb +0 -1461
  64. data/spec/lib/zermelo/records/type_validator_spec.rb +0 -6
  65. data/spec/lib/zermelo/version_spec.rb +0 -6
  66. 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
@@ -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
@@ -1,6 +0,0 @@
1
- require 'spec_helper'
2
- require 'zermelo/associations/belongs_to'
3
-
4
- describe Zermelo::Associations::BelongsTo, :redis => true do
5
-
6
- end
@@ -1,6 +0,0 @@
1
- require 'spec_helper'
2
- require 'zermelo/associations/has_many'
3
-
4
- describe Zermelo::Associations::HasMany, :redis => true do
5
-
6
- end
@@ -1,6 +0,0 @@
1
- require 'spec_helper'
2
- require 'zermelo/associations/has_one'
3
-
4
- describe Zermelo::Associations::HasOne, :redis => true do
5
-
6
- end
@@ -1,6 +0,0 @@
1
- require 'spec_helper'
2
- require 'zermelo/associations/has_sorted_set'
3
-
4
- describe Zermelo::Associations::HasSortedSet, :redis => true do
5
-
6
- end
File without changes
File without changes
File without changes
File without changes