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
@@ -0,0 +1,53 @@
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
+ module Zermelo
15
+
16
+ module Record
17
+
18
+ extend ActiveSupport::Concern
19
+
20
+ include Zermelo::Records::InstMethods
21
+
22
+ included do
23
+ include ActiveModel::AttributeMethods
24
+ extend ActiveModel::Callbacks
25
+ extend ActiveModel::Naming
26
+ include ActiveModel::Dirty
27
+ include ActiveModel::Validations
28
+ include ActiveModel::Validations::Callbacks
29
+
30
+ extend Zermelo::Records::Attributes
31
+
32
+ # include ActiveModel::MassAssignmentSecurity
33
+
34
+ @lock = Monitor.new
35
+
36
+ extend Zermelo::Records::ClassMethods
37
+ extend Zermelo::Associations::ClassMethods
38
+
39
+ attr_accessor :attributes
40
+
41
+ define_model_callbacks :create, :update, :destroy
42
+
43
+ attribute_method_suffix "=" # attr_writers
44
+ # attribute_method_suffix "" # attr_readers # DEPRECATED
45
+
46
+ validates_with Zermelo::Records::TypeValidator
47
+
48
+ define_attributes :id => :string
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,32 @@
1
+ require 'zermelo'
2
+
3
+ module Zermelo
4
+ module Records
5
+ module Attributes
6
+
7
+ def attribute_types
8
+ ret = nil
9
+ @lock.synchronize do
10
+ ret = (@attribute_types ||= {}).dup
11
+ end
12
+ ret
13
+ end
14
+
15
+ protected
16
+
17
+ def define_attributes(options = {})
18
+ options.each_pair do |key, value|
19
+ raise "Unknown attribute type ':#{value}' for ':#{key}'" unless
20
+ Zermelo.valid_type?(value)
21
+ self.define_attribute_methods([key])
22
+ end
23
+ @lock.synchronize do
24
+ (@attribute_types ||= {}).update(options)
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -3,9 +3,11 @@ require 'securerandom'
3
3
 
4
4
  require 'zermelo'
5
5
 
6
- require 'zermelo/backends/influxdb_backend'
7
- require 'zermelo/backends/redis_backend'
6
+ require 'zermelo/backends/influxdb'
7
+ require 'zermelo/backends/redis'
8
+ require 'zermelo/backends/stub'
8
9
 
10
+ require 'zermelo/records/attributes'
9
11
  require 'zermelo/records/key'
10
12
 
11
13
  module Zermelo
@@ -14,6 +16,8 @@ module Zermelo
14
16
 
15
17
  module ClassMethods
16
18
 
19
+ include Zermelo::Records::Attributes
20
+
17
21
  extend Forwardable
18
22
 
19
23
  def_delegators :filter, :intersect, :union, :diff, :sort,
@@ -40,14 +44,6 @@ module Zermelo
40
44
  backend.delete(ids_key, id.to_s)
41
45
  end
42
46
 
43
- def attribute_types
44
- ret = nil
45
- @lock.synchronize do
46
- ret = (@attribute_types ||= {}).dup
47
- end
48
- ret
49
- end
50
-
51
47
  def lock(*klasses, &block)
52
48
  klasses += [self] unless klasses.include?(self)
53
49
  backend.lock(*klasses, &block)
@@ -62,8 +58,8 @@ module Zermelo
62
58
  yield
63
59
  rescue Exception => e
64
60
  backend.abort_transaction
65
- p e.message
66
- puts e.backtrace.join("\n")
61
+ # p e.message
62
+ # puts e.backtrace.join("\n")
67
63
  failed = true
68
64
  ensure
69
65
  backend.commit_transaction unless failed
@@ -80,23 +76,14 @@ module Zermelo
80
76
 
81
77
  protected
82
78
 
83
- def define_attributes(options = {})
84
- options.each_pair do |key, value|
85
- raise "Unknown attribute type ':#{value}' for ':#{key}'" unless
86
- Zermelo.valid_type?(value)
87
- self.define_attribute_methods([key])
88
- end
89
- @lock.synchronize do
90
- (@attribute_types ||= {}).update(options)
91
- end
92
- end
93
-
94
79
  def set_backend(backend_type)
95
80
  @backend ||= case backend_type.to_sym
96
81
  when :influxdb
97
- Zermelo::Backends::InfluxDBBackend.new
82
+ Zermelo::Backends::InfluxDB.new
98
83
  when :redis
99
- Zermelo::Backends::RedisBackend.new
84
+ Zermelo::Backends::Redis.new
85
+ when :stub
86
+ Zermelo::Backends::Stub.new
100
87
  end
101
88
  end
102
89
 
@@ -1,6 +1,6 @@
1
1
  require 'active_support/concern'
2
2
 
3
- require 'zermelo/records/base'
3
+ require 'zermelo/record'
4
4
 
5
5
  # a record is a row in a time series (named for the record class)
6
6
 
@@ -21,17 +21,16 @@ require 'zermelo/records/base'
21
21
 
22
22
  module Zermelo
23
23
  module Records
24
- module InfluxDBRecord
24
+ module InfluxDB
25
25
  extend ActiveSupport::Concern
26
26
 
27
- include Zermelo::Records::Base
27
+ include Zermelo::Record
28
28
 
29
29
  included do
30
30
  set_backend :influxdb
31
31
 
32
32
  define_attributes :time => :timestamp
33
33
  end
34
-
35
34
  end
36
35
  end
37
36
  end
@@ -7,11 +7,11 @@ module Zermelo
7
7
  # module renamed to avoid ActiveSupport::Concern deprecation warning
8
8
  module InstMethods
9
9
 
10
- def initialize(attributes = {})
10
+ def initialize(attrs = {})
11
11
  @is_new = true
12
- @attributes = {}
13
- attributes.each_pair do |k, v|
14
- self.send("#{k}=".to_sym, v)
12
+ @attributes = self.class.attribute_types.keys.inject({}) do |memo, ak|
13
+ memo[ak.to_s] = attrs[ak]
14
+ memo
15
15
  end
16
16
  end
17
17
 
@@ -46,6 +46,7 @@ module Zermelo
46
46
  class_key = self.class.send(:class_key)
47
47
 
48
48
  # TODO: check for record existence in backend-agnostic fashion
49
+ # TODO fail if id not found
49
50
  @is_new = false
50
51
 
51
52
  attr_types = self.class.attribute_types.reject {|k, v| k == :id}
@@ -55,11 +56,11 @@ module Zermelo
55
56
  :id => self.id, :name => name, :type => type, :object => :attribute)
56
57
  end
57
58
 
58
- attrs = backend.get_multiple(*attrs_to_load)[class_key][self.id]
59
+ result = backend.get_multiple(*attrs_to_load)
60
+ attrs = result[class_key][self.id] unless result.empty?
59
61
  end
60
62
 
61
- return false unless attrs.present?
62
- @attributes.update(attrs)
63
+ @attributes.update(attrs) unless attrs.nil? || attrs.empty?
63
64
  true
64
65
  end
65
66
 
@@ -109,7 +110,7 @@ module Zermelo
109
110
  attr_keys = attribute_keys
110
111
 
111
112
  if creating
112
- attribute_keys.each_pair do |att, attr_key|
113
+ attr_keys.each_pair do |att, attr_key|
113
114
  apply_attribute.call(att, attr_key, [nil, @attributes[att]])
114
115
  end
115
116
  else
@@ -6,7 +6,9 @@ module Zermelo
6
6
  # id / if nil, it's a class variable
7
7
  # object / :association, :attribute or :index
8
8
  # accessor / if a complex type, some way of getting sub-value
9
- attr_reader :klass, :id, :name, :accessor, :type, :object
9
+ attr_reader :klass, :name, :accessor, :type, :object
10
+
11
+ attr_accessor :id
10
12
 
11
13
  # TODO better validation of data, e.g. accessor valid for type, etc.
12
14
  def initialize(opts = {})
@@ -0,0 +1,17 @@
1
+ require 'active_support/concern'
2
+
3
+ require 'zermelo/record'
4
+
5
+ module Zermelo
6
+ module Records
7
+ module Redis
8
+ extend ActiveSupport::Concern
9
+
10
+ include Zermelo::Record
11
+
12
+ included do
13
+ set_backend :redis
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'active_support/concern'
2
+
3
+ require 'zermelo/record'
4
+
5
+ module Zermelo
6
+ module Records
7
+ module Stub
8
+ extend ActiveSupport::Concern
9
+
10
+ include Zermelo::Record
11
+
12
+ included do
13
+ set_backend :stub
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Zermelo
2
- VERSION = '1.1.0'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -1,6 +1,75 @@
1
1
  require 'spec_helper'
2
+ require 'zermelo/records/redis'
2
3
  require 'zermelo/associations/index'
3
4
 
4
- describe Zermelo::Associations::Index, :redis => true do
5
+ describe Zermelo::Associations::Index do
6
+
7
+ context 'redis', :redis => true do
8
+
9
+ let(:redis) { Zermelo.redis }
10
+
11
+ let(:time) { Time.now }
12
+
13
+ module ZermeloExamples
14
+ class RedisIndex
15
+ include Zermelo::Records::Redis
16
+ define_attributes :emotion => :string
17
+ validates :emotion, :presence => true, :inclusion => {:in => %w(happy sad indifferent)}
18
+ index_by :emotion
19
+ end
20
+ end
21
+
22
+ it 'adds an entry to a set indexing an attribute' do
23
+ example = ZermeloExamples::RedisIndex.new(:id => '1',
24
+ :emotion => 'happy')
25
+ expect(example).to be_valid
26
+ expect(example.save).to be true
27
+
28
+ expect(redis.exists('redis_index::indices:by_emotion:string:happy')).to be true
29
+ expect(redis.smembers('redis_index::indices:by_emotion:string:happy')).to eq([
30
+ '1'
31
+ ])
32
+ end
33
+
34
+ it 'removes an entry from a sorted set indexing an attribute' do
35
+ example = ZermeloExamples::RedisIndex.new(:id => '1',
36
+ :emotion => 'happy')
37
+ example.save
38
+
39
+ example_2 = ZermeloExamples::RedisIndex.new(:id => '2',
40
+ :emotion => 'happy')
41
+ example_2.save
42
+
43
+ expect(redis.smembers('redis_index::indices:by_emotion:string:happy')).to eq([
44
+ '1', '2'
45
+ ])
46
+
47
+ example.destroy
48
+
49
+ expect(redis.smembers('redis_index::indices:by_emotion:string:happy')).to eq([
50
+ '2'
51
+ ])
52
+ end
53
+
54
+ it 'changes an entry in a sorted set indexing an attribute' do
55
+ example = ZermeloExamples::RedisIndex.new(:id => '1',
56
+ :emotion => 'happy')
57
+ example.save
58
+
59
+ expect(redis.smembers('redis_index::indices:by_emotion:string:happy')).to eq([
60
+ '1'
61
+ ])
62
+
63
+ example.emotion = 'sad'
64
+ example.save
65
+
66
+ expect(redis.exists('redis_index::indices:by_emotion:string:happy')).to be false
67
+ expect(redis.exists('redis_index::indices:by_emotion:string:sad')).to be true
68
+ expect(redis.smembers('redis_index::indices:by_emotion:string:sad')).to eq([
69
+ '1'
70
+ ])
71
+ end
72
+
73
+ end
5
74
 
6
75
  end