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