zermelo 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|