zermelo 1.1.0 → 1.2.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 (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