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.
- 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
@@ -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/
|
7
|
-
require 'zermelo/backends/
|
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::
|
82
|
+
Zermelo::Backends::InfluxDB.new
|
98
83
|
when :redis
|
99
|
-
Zermelo::Backends::
|
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/
|
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
|
24
|
+
module InfluxDB
|
25
25
|
extend ActiveSupport::Concern
|
26
26
|
|
27
|
-
include Zermelo::
|
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(
|
10
|
+
def initialize(attrs = {})
|
11
11
|
@is_new = true
|
12
|
-
@attributes = {}
|
13
|
-
|
14
|
-
|
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
|
-
|
59
|
+
result = backend.get_multiple(*attrs_to_load)
|
60
|
+
attrs = result[class_key][self.id] unless result.empty?
|
59
61
|
end
|
60
62
|
|
61
|
-
|
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
|
-
|
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
|
data/lib/zermelo/records/key.rb
CHANGED
@@ -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, :
|
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 = {})
|
data/lib/zermelo/version.rb
CHANGED
@@ -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
|
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
|