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
@@ -1,120 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
|
-
module Zermelo
|
4
|
-
module Associations
|
5
|
-
class HasMany
|
6
|
-
|
7
|
-
extend Forwardable
|
8
|
-
|
9
|
-
def_delegators :filter, :intersect, :union, :diff, :sort,
|
10
|
-
:find_by_id, :find_by_ids, :find_by_id!, :find_by_ids!,
|
11
|
-
:page, :all, :each, :collect, :map,
|
12
|
-
:select, :find_all, :reject, :destroy_all,
|
13
|
-
:ids, :count, :empty?, :exists?,
|
14
|
-
:associated_ids_for
|
15
|
-
|
16
|
-
def initialize(parent, name)
|
17
|
-
@parent = parent
|
18
|
-
|
19
|
-
@backend = parent.send(:backend)
|
20
|
-
|
21
|
-
@record_ids_key = Zermelo::Records::Key.new(
|
22
|
-
:klass => parent.class,
|
23
|
-
:id => parent.id,
|
24
|
-
:name => "#{name}_ids",
|
25
|
-
:type => :set,
|
26
|
-
:object => :association
|
27
|
-
)
|
28
|
-
|
29
|
-
parent.class.send(:with_association_data, name.to_sym) do |data|
|
30
|
-
@associated_class = data.data_klass
|
31
|
-
@lock_klasses = [data.data_klass] + data.related_klasses
|
32
|
-
@inverse = data.inverse
|
33
|
-
@callbacks = data.callbacks
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def <<(record)
|
38
|
-
add(record)
|
39
|
-
self # for << 'a' << 'b'
|
40
|
-
end
|
41
|
-
|
42
|
-
def add(*records)
|
43
|
-
raise 'No records to add' if records.empty?
|
44
|
-
raise 'Invalid record class' if records.any? {|r| !r.is_a?(@associated_class)}
|
45
|
-
raise 'Record(s) must have been saved' unless records.all? {|r| r.persisted?} # may need to be moved
|
46
|
-
@parent.class.lock(*@lock_klasses) do
|
47
|
-
ba = @callbacks[:before_add]
|
48
|
-
if ba.nil? || !@parent.respond_to?(ba) || !@parent.send(ba, *records).is_a?(FalseClass)
|
49
|
-
unless @inverse.nil?
|
50
|
-
records.each do |record|
|
51
|
-
@associated_class.send(:load, record.id).send("#{@inverse}=", @parent)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
new_txn = @backend.begin_transaction
|
56
|
-
@backend.add(@record_ids_key, records.map(&:id))
|
57
|
-
@backend.commit_transaction if new_txn
|
58
|
-
aa = @callbacks[:after_add]
|
59
|
-
@parent.send(aa, *records) if !aa.nil? && @parent.respond_to?(aa)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# TODO support dependent delete, for now just deletes the association
|
65
|
-
def delete(*records)
|
66
|
-
raise 'No records to delete' if records.empty?
|
67
|
-
raise 'Invalid record class' if records.any? {|r| !r.is_a?(@associated_class)}
|
68
|
-
raise 'Record(s) must have been saved' unless records.all? {|r| r.persisted?} # may need to be moved
|
69
|
-
@parent.class.lock(*@lock_klasses) do
|
70
|
-
br = @callbacks[:before_remove]
|
71
|
-
if br.nil? || !@parent.respond_to?(br) || !@parent.send(br, *records).is_a?(FalseClass)
|
72
|
-
unless @inverse.nil?
|
73
|
-
records.each do |record|
|
74
|
-
@associated_class.send(:load, record.id).send("#{@inverse}=", nil)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
new_txn = @backend.begin_transaction
|
79
|
-
@backend.delete(@record_ids_key, records.map(&:id))
|
80
|
-
@backend.commit_transaction if new_txn
|
81
|
-
ar = @callbacks[:after_remove]
|
82
|
-
@parent.send(ar, *records) if !ar.nil? && @parent.respond_to?(ar)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
# associated will be a belongs_to; on remove already runs inside a lock and transaction
|
90
|
-
def on_remove
|
91
|
-
unless @inverse.nil?
|
92
|
-
self.ids.each do |record_id|
|
93
|
-
@associated_class.send(:load, record_id).send("#{@inverse}=", nil)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
@backend.clear(@record_ids_key)
|
97
|
-
end
|
98
|
-
|
99
|
-
# creates a new filter class each time it's called, to store the
|
100
|
-
# state for this particular filter chain
|
101
|
-
def filter
|
102
|
-
@backend.filter(@record_ids_key, @associated_class)
|
103
|
-
end
|
104
|
-
|
105
|
-
def self.associated_ids_for(backend, klass, name, *these_ids)
|
106
|
-
these_ids.each_with_object({}) do |this_id, memo|
|
107
|
-
key = Zermelo::Records::Key.new(
|
108
|
-
:klass => klass,
|
109
|
-
:id => this_id,
|
110
|
-
:name => "#{name}_ids",
|
111
|
-
:type => :set,
|
112
|
-
:object => :association
|
113
|
-
)
|
114
|
-
memo[this_id] = backend.get(key)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
@@ -1,109 +0,0 @@
|
|
1
|
-
module Zermelo
|
2
|
-
module Associations
|
3
|
-
class HasOne
|
4
|
-
|
5
|
-
def initialize(parent, name)
|
6
|
-
@parent = parent
|
7
|
-
|
8
|
-
@backend = parent.send(:backend)
|
9
|
-
|
10
|
-
# TODO would be better as a 'has_one' hash, a bit like belongs_to
|
11
|
-
@record_id_key = Zermelo::Records::Key.new(
|
12
|
-
:klass => parent.class,
|
13
|
-
:id => parent.id,
|
14
|
-
:name => "#{name}_id",
|
15
|
-
:type => :string,
|
16
|
-
:object => :association
|
17
|
-
)
|
18
|
-
|
19
|
-
parent.class.send(:with_association_data, name.to_sym) do |data|
|
20
|
-
@associated_class = data.data_klass
|
21
|
-
@lock_klasses = [data.data_klass] + data.related_klasses
|
22
|
-
@inverse = data.inverse
|
23
|
-
@callbacks = data.callbacks
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def value
|
28
|
-
@parent.class.lock(@associated_class) do
|
29
|
-
if id = @backend.get(@record_id_key)
|
30
|
-
@associated_class.send(:load, id)
|
31
|
-
else
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def value=(record)
|
38
|
-
if record.nil?
|
39
|
-
@parent.class.lock(*@lock_klasses) do
|
40
|
-
id = @backend.get(@record_id_key)
|
41
|
-
unless id.nil?
|
42
|
-
r = @associated_class.send(:load, id)
|
43
|
-
unless r.nil?
|
44
|
-
bc = @callbacks[:before_clear]
|
45
|
-
if bc.nil? || !@parent.respond_to?(bc) || !@parent.send(bc, r).is_a?(FalseClass)
|
46
|
-
delete_without_lock(r)
|
47
|
-
ac = @callbacks[:after_clear]
|
48
|
-
@parent.send(ac, r) if !ac.nil? && @parent.respond_to?(ac)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
else
|
54
|
-
raise 'Invalid record class' unless record.is_a?(@associated_class)
|
55
|
-
raise 'Record must have been saved' unless record.persisted?
|
56
|
-
@parent.class.lock(*@lock_klasses) do
|
57
|
-
bs = @callbacks[:before_set]
|
58
|
-
if bs.nil? || !@parent.respond_to?(bs) || !@parent.send(bs, r).is_a?(FalseClass)
|
59
|
-
unless @inverse.nil?
|
60
|
-
@associated_class.send(:load, record.id).send("#{@inverse}=", @parent)
|
61
|
-
end
|
62
|
-
|
63
|
-
new_txn = @backend.begin_transaction
|
64
|
-
@backend.set(@record_id_key, record.id)
|
65
|
-
@backend.commit_transaction if new_txn
|
66
|
-
as = @callbacks[:after_set]
|
67
|
-
@parent.send(as, record) if !as.nil? && @parent.respond_to?(as)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def delete_without_lock(record)
|
76
|
-
unless @inverse.nil?
|
77
|
-
record.send("#{@inverse}=", nil)
|
78
|
-
end
|
79
|
-
new_txn = @backend.begin_transaction
|
80
|
-
@backend.clear(@record_id_key)
|
81
|
-
@backend.commit_transaction if new_txn
|
82
|
-
end
|
83
|
-
|
84
|
-
# associated will be a belongs_to; on_remove already runs inside lock and transaction
|
85
|
-
def on_remove
|
86
|
-
unless @inverse.nil?
|
87
|
-
if record_id = @backend.get(@record_id_key)
|
88
|
-
@associated_class.send(:load, record_id).send("#{@inverse}=", nil)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
@backend.clear(@record_id_key)
|
92
|
-
end
|
93
|
-
|
94
|
-
def self.associated_ids_for(backend, klass, name, *these_ids)
|
95
|
-
these_ids.each_with_object({}) do |this_id, memo|
|
96
|
-
key = Zermelo::Records::Key.new(
|
97
|
-
:klass => klass,
|
98
|
-
:id => this_id,
|
99
|
-
:name => "#{name}_id",
|
100
|
-
:type => :string,
|
101
|
-
:object => :association
|
102
|
-
)
|
103
|
-
memo[this_id] = backend.get(key)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
@@ -1,124 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
|
-
module Zermelo
|
4
|
-
module Associations
|
5
|
-
class HasSortedSet
|
6
|
-
|
7
|
-
extend Forwardable
|
8
|
-
|
9
|
-
def_delegators :filter, :intersect, :union, :diff, :sort,
|
10
|
-
:intersect_range, :union_range, :diff_range,
|
11
|
-
:find_by_id, :find_by_ids, :find_by_id!, :find_by_ids!,
|
12
|
-
:all, :each, :collect, :map,
|
13
|
-
:select, :find_all, :reject, :destroy_all,
|
14
|
-
:ids, :count, :empty?, :exists?,
|
15
|
-
:first, :last,
|
16
|
-
:associated_ids_for
|
17
|
-
|
18
|
-
def initialize(parent, name)
|
19
|
-
@parent = parent
|
20
|
-
|
21
|
-
@backend = parent.send(:backend)
|
22
|
-
|
23
|
-
@record_ids_key = Zermelo::Records::Key.new(
|
24
|
-
:klass => parent.class,
|
25
|
-
:id => parent.id,
|
26
|
-
:name => "#{name}_ids",
|
27
|
-
:type => :sorted_set,
|
28
|
-
:object => :association
|
29
|
-
)
|
30
|
-
|
31
|
-
parent.class.send(:with_association_data, name.to_sym) do |data|
|
32
|
-
@associated_class = data.data_klass
|
33
|
-
@lock_klasses = [data.data_klass] + data.related_klasses
|
34
|
-
@inverse = data.inverse
|
35
|
-
@sort_key = data.sort_key || backend.default_sort_key
|
36
|
-
@callbacks = data.callbacks
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def <<(record)
|
41
|
-
add(record)
|
42
|
-
self # for << 'a' << 'b'
|
43
|
-
end
|
44
|
-
|
45
|
-
# TODO collect all scores/ids and do a single zadd/single hmset
|
46
|
-
def add(*records)
|
47
|
-
raise 'No records to add' if records.empty?
|
48
|
-
raise 'Invalid record class' if records.any? {|r| !r.is_a?(@associated_class)}
|
49
|
-
raise 'Record(s) must have been saved' unless records.all? {|r| r.persisted?}
|
50
|
-
@parent.class.lock(*@lock_klasses) do
|
51
|
-
ba = @callbacks[:before_add]
|
52
|
-
if ba.nil? || !@parent.respond_to?(ba) || !@parent.send(ba, *records).is_a?(FalseClass)
|
53
|
-
unless @inverse.nil?
|
54
|
-
records.each do |record|
|
55
|
-
@associated_class.send(:load, record.id).send("#{@inverse}=", @parent)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
new_txn = @backend.begin_transaction
|
60
|
-
@backend.add(@record_ids_key, (records.map {|r| [r.send(@sort_key.to_sym).to_f, r.id]}.flatten))
|
61
|
-
@backend.commit_transaction if new_txn
|
62
|
-
aa = @callbacks[:after_add]
|
63
|
-
@parent.send(aa, *records) if !aa.nil? && @parent.respond_to?(aa)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def delete(*records)
|
69
|
-
raise 'No records to delete' if records.empty?
|
70
|
-
raise 'Invalid record class' if records.any? {|r| !r.is_a?(@associated_class)}
|
71
|
-
raise 'Record(s) must have been saved' unless records.all? {|r| r.persisted?}
|
72
|
-
@parent.class.lock(*@lock_klasses) do
|
73
|
-
br = @callbacks[:before_remove]
|
74
|
-
if br.nil? || !@parent.respond_to?(br) || !@parent.send(br, *records).is_a?(FalseClass)
|
75
|
-
unless @inverse.nil?
|
76
|
-
records.each do |record|
|
77
|
-
@associated_class.send(:load, record.id).send("#{@inverse}=", nil)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
new_txn = @backend.begin_transaction
|
82
|
-
@backend.delete(@record_ids_key, records.map(&:id))
|
83
|
-
@backend.commit_transaction if new_txn
|
84
|
-
ar = @callbacks[:after_remove]
|
85
|
-
@parent.send(ar, *records) if !ar.nil? && @parent.respond_to?(ar)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
# associated will be a belongs_to; on remove already runs inside a lock and transaction
|
93
|
-
def on_remove
|
94
|
-
unless @inverse.nil?
|
95
|
-
self.ids.each do |record_id|
|
96
|
-
# clear the belongs_to inverse value with this @parent.id
|
97
|
-
@associated_class.send(:load, record_id).send("#{@inverse}=", nil)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
@backend.clear(@record_ids_key)
|
101
|
-
end
|
102
|
-
|
103
|
-
# creates a new filter class each time it's called, to store the
|
104
|
-
# state for this particular filter chain
|
105
|
-
def filter
|
106
|
-
@backend.filter(@record_ids_key, @associated_class)
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.associated_ids_for(backend, klass, name, *these_ids)
|
110
|
-
these_ids.each_with_object({}) do |this_id, memo|
|
111
|
-
key = Zermelo::Records::Key.new(
|
112
|
-
:klass => klass,
|
113
|
-
:id => this_id,
|
114
|
-
:name => "#{name}_ids",
|
115
|
-
:type => :sorted_set,
|
116
|
-
:object => :association
|
117
|
-
)
|
118
|
-
memo[this_id] = backend.get(key)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
@@ -1,115 +0,0 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
|
3
|
-
require 'zermelo/locks/no_lock'
|
4
|
-
|
5
|
-
module Zermelo
|
6
|
-
|
7
|
-
module Backends
|
8
|
-
|
9
|
-
module Base
|
10
|
-
|
11
|
-
extend ActiveSupport::Concern
|
12
|
-
|
13
|
-
def escape_key_name(name)
|
14
|
-
name.gsub(/%/, '%%').gsub(/ /, '%20').gsub(/:/, '%3A')
|
15
|
-
end
|
16
|
-
|
17
|
-
def unescape_key_name(name)
|
18
|
-
name.gsub(/%3A/, ':').gsub(/%20/, ' ').gsub(/%%/, '%')
|
19
|
-
end
|
20
|
-
|
21
|
-
def index_keys(type, value)
|
22
|
-
return ["null", "null"] if value.nil?
|
23
|
-
|
24
|
-
case type
|
25
|
-
when :string
|
26
|
-
["string", escape_key_name(value)]
|
27
|
-
when :integer
|
28
|
-
["integer", escape_key_name(value.to_s)]
|
29
|
-
when :float
|
30
|
-
["float", escape_key_name(value.to_s)]
|
31
|
-
when :timestamp
|
32
|
-
case value
|
33
|
-
when Integer
|
34
|
-
["timestamp", escape_key_name(value.to_s)]
|
35
|
-
when Time, DateTime
|
36
|
-
["timestamp", escape_key_name(value.to_i.to_s)]
|
37
|
-
end
|
38
|
-
when :boolean
|
39
|
-
case value
|
40
|
-
when TrueClass
|
41
|
-
["boolean", "true"]
|
42
|
-
when FalseClass
|
43
|
-
["boolean", "false"]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# for hashes, lists, sets
|
49
|
-
def add(key, value)
|
50
|
-
change(:add, key, value)
|
51
|
-
end
|
52
|
-
|
53
|
-
def delete(key, value)
|
54
|
-
change(:delete, key, value)
|
55
|
-
end
|
56
|
-
|
57
|
-
def move(key, value, key_to)
|
58
|
-
change(:move, key, value, key_to)
|
59
|
-
end
|
60
|
-
|
61
|
-
def clear(key)
|
62
|
-
change(:clear, key)
|
63
|
-
end
|
64
|
-
|
65
|
-
# works for both simple and complex types (i.e. strings, numbers, booleans,
|
66
|
-
# hashes, lists, sets)
|
67
|
-
def set(key, value)
|
68
|
-
change(:set, key, value)
|
69
|
-
end
|
70
|
-
|
71
|
-
def purge(key)
|
72
|
-
change(:purge, key)
|
73
|
-
end
|
74
|
-
|
75
|
-
def get(attr_key)
|
76
|
-
get_multiple(attr_key)[attr_key.klass.send(:class_key)][attr_key.id][attr_key.name.to_s]
|
77
|
-
end
|
78
|
-
|
79
|
-
def lock(*klasses, &block)
|
80
|
-
ret = nil
|
81
|
-
# doesn't handle re-entrant case for influxdb, which has no locking yet
|
82
|
-
locking = Thread.current[:zermelo_locking]
|
83
|
-
if locking.nil?
|
84
|
-
lock_proc = proc do
|
85
|
-
begin
|
86
|
-
Thread.current[:zermelo_locking] = klasses
|
87
|
-
ret = block.call
|
88
|
-
ensure
|
89
|
-
Thread.current[:zermelo_locking] = nil
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
lock_klass = case self
|
94
|
-
when Zermelo::Backends::RedisBackend
|
95
|
-
Zermelo::Locks::RedisLock
|
96
|
-
else
|
97
|
-
Zermelo::Locks::NoLock
|
98
|
-
end
|
99
|
-
|
100
|
-
lock_klass.new.lock(*klasses, &lock_proc)
|
101
|
-
else
|
102
|
-
# accepts any subset of 'locking'
|
103
|
-
unless (klasses - locking).empty?
|
104
|
-
raise "Currently locking #{locking.map(&:name)}, cannot lock different set #{klasses.map(&:name)}"
|
105
|
-
end
|
106
|
-
ret = block.call
|
107
|
-
end
|
108
|
-
ret
|
109
|
-
end
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
data/lib/zermelo/filters/base.rb
DELETED
@@ -1,212 +0,0 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
|
3
|
-
require 'zermelo/records/errors'
|
4
|
-
|
5
|
-
require 'zermelo/filters/steps/list_step'
|
6
|
-
require 'zermelo/filters/steps/set_step'
|
7
|
-
require 'zermelo/filters/steps/sorted_set_step'
|
8
|
-
require 'zermelo/filters/steps/sort_step'
|
9
|
-
|
10
|
-
module Zermelo
|
11
|
-
|
12
|
-
module Filters
|
13
|
-
|
14
|
-
module Base
|
15
|
-
|
16
|
-
extend ActiveSupport::Concern
|
17
|
-
|
18
|
-
attr_reader :backend, :steps
|
19
|
-
|
20
|
-
# initial set a Zermelo::Record::Key object
|
21
|
-
# associated_class the class of the result record
|
22
|
-
def initialize(data_backend, initial_key, associated_class, ancestor = nil, step = nil)
|
23
|
-
@backend = data_backend
|
24
|
-
@initial_key = initial_key
|
25
|
-
@associated_class = associated_class
|
26
|
-
@steps = ancestor.nil? ? [] : ancestor.steps.dup
|
27
|
-
@steps << step unless step.nil?
|
28
|
-
end
|
29
|
-
|
30
|
-
def intersect(attrs = {})
|
31
|
-
self.class.new(@backend, @initial_key, @associated_class, self,
|
32
|
-
::Zermelo::Filters::Steps::SetStep.new({:op => :intersect}, attrs))
|
33
|
-
end
|
34
|
-
|
35
|
-
def union(attrs = {})
|
36
|
-
self.class.new(@backend, @initial_key, @associated_class, self,
|
37
|
-
::Zermelo::Filters::Steps::SetStep.new({:op => :union}, attrs))
|
38
|
-
end
|
39
|
-
|
40
|
-
def diff(attrs = {})
|
41
|
-
self.class.new(@backend, @initial_key, @associated_class, self,
|
42
|
-
::Zermelo::Filters::Steps::SetStep.new({:op => :diff}, attrs))
|
43
|
-
end
|
44
|
-
|
45
|
-
def sort(keys, opts = {})
|
46
|
-
self.class.new(@backend, @initial_key, @associated_class, self,
|
47
|
-
::Zermelo::Filters::Steps::SortStep.new({:keys => keys,
|
48
|
-
:desc => opts[:desc], :limit => opts[:limit],
|
49
|
-
:offset => opts[:offset]}, {})
|
50
|
-
)
|
51
|
-
end
|
52
|
-
|
53
|
-
def offset(amount, opts = {})
|
54
|
-
self.class.new(@backend, @initial_key, @associated_class, self,
|
55
|
-
::Zermelo::Filters::Steps::ListStep.new({:offset => amount,
|
56
|
-
:limit => opts[:limit]}, {}))
|
57
|
-
end
|
58
|
-
|
59
|
-
def page(num, opts = {})
|
60
|
-
per_page = opts[:per_page].to_i || 20
|
61
|
-
start = per_page * (num - 1)
|
62
|
-
finish = start + (per_page - 1)
|
63
|
-
self.class.new(@backend, @initial_key, @associated_class, self,
|
64
|
-
::Zermelo::Filters::Steps::ListStep.new({:offset => start,
|
65
|
-
:limit => per_page}, {}))
|
66
|
-
end
|
67
|
-
|
68
|
-
def intersect_range(start, finish, attrs_opts = {})
|
69
|
-
self.class.new(@backend, @initial_key, @associated_class, self,
|
70
|
-
::Zermelo::Filters::Steps::SortedSetStep.new(
|
71
|
-
{:op => :intersect_range, :start => start, :finish => finish,
|
72
|
-
:by_score => attrs_opts.delete(:by_score)},
|
73
|
-
attrs_opts)
|
74
|
-
)
|
75
|
-
end
|
76
|
-
|
77
|
-
def union_range(start, finish, attrs_opts = {})
|
78
|
-
self.class.new(@backend, @initial_key, @associated_class, self,
|
79
|
-
::Zermelo::Filters::Steps::SortedSetStep.new(
|
80
|
-
{:op => :union_range, :start => start, :finish => finish,
|
81
|
-
:by_score => attrs_opts.delete(:by_score)},
|
82
|
-
attrs_opts)
|
83
|
-
)
|
84
|
-
end
|
85
|
-
|
86
|
-
def diff_range(start, finish, attrs_opts = {})
|
87
|
-
self.class.new(@backend, @initial_key, @associated_class, self,
|
88
|
-
::Zermelo::Filters::Steps::SortedSetStep.new(
|
89
|
-
{:op => :diff_range, :start => start, :finish => finish,
|
90
|
-
:by_score => attrs_opts.delete(:by_score)},
|
91
|
-
attrs_opts)
|
92
|
-
)
|
93
|
-
end
|
94
|
-
|
95
|
-
# step users
|
96
|
-
def exists?(e_id)
|
97
|
-
lock(false) { _exists?(e_id) }
|
98
|
-
end
|
99
|
-
|
100
|
-
def find_by_id(f_id)
|
101
|
-
lock { _find_by_id(f_id) }
|
102
|
-
end
|
103
|
-
|
104
|
-
def find_by_id!(f_id)
|
105
|
-
ret = lock { _find_by_id(f_id) }
|
106
|
-
raise ::Zermelo::Records::Errors::RecordNotFound.new(@associated_class, f_id) if ret.nil?
|
107
|
-
ret
|
108
|
-
end
|
109
|
-
|
110
|
-
def find_by_ids(*f_ids)
|
111
|
-
lock { f_ids.collect {|f_id| _find_by_id(f_id) } }
|
112
|
-
end
|
113
|
-
|
114
|
-
def find_by_ids!(*f_ids)
|
115
|
-
ret = lock { f_ids.collect {|f_id| _find_by_id(f_id) } }
|
116
|
-
if ret.any? {|r| r.nil? }
|
117
|
-
raise ::Zermelo::Records::Errors::RecordsNotFound.new(@associated_class, f_ids - ret.compact.map(&:id))
|
118
|
-
end
|
119
|
-
ret
|
120
|
-
end
|
121
|
-
|
122
|
-
def ids
|
123
|
-
lock(false) { _ids }
|
124
|
-
end
|
125
|
-
|
126
|
-
def count
|
127
|
-
lock(false) { _count }
|
128
|
-
end
|
129
|
-
|
130
|
-
def empty?
|
131
|
-
lock(false) { _count == 0 }
|
132
|
-
end
|
133
|
-
|
134
|
-
def all
|
135
|
-
lock { _all }
|
136
|
-
end
|
137
|
-
|
138
|
-
def collect(&block)
|
139
|
-
lock { _ids.collect {|id| block.call(_load(id))} }
|
140
|
-
end
|
141
|
-
alias_method :map, :collect
|
142
|
-
|
143
|
-
def each(&block)
|
144
|
-
lock { _ids.each {|id| block.call(_load(id)) } }
|
145
|
-
end
|
146
|
-
|
147
|
-
def select(&block)
|
148
|
-
lock { _all.select {|obj| block.call(obj) } }
|
149
|
-
end
|
150
|
-
alias_method :find_all, :select
|
151
|
-
|
152
|
-
def reject(&block)
|
153
|
-
lock { _all.reject {|obj| block.call(obj)} }
|
154
|
-
end
|
155
|
-
|
156
|
-
def destroy_all
|
157
|
-
lock(*@associated_class.send(:associated_classes)) do
|
158
|
-
_all.each {|r| r.destroy }
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def associated_ids_for(name, options = {})
|
163
|
-
klass = @associated_class.send(:with_association_data, name.to_sym) do |data|
|
164
|
-
data.type_klass
|
165
|
-
end
|
166
|
-
|
167
|
-
lock {
|
168
|
-
case klass.name
|
169
|
-
when ::Zermelo::Associations::BelongsTo.name
|
170
|
-
klass.send(:associated_ids_for, @backend,
|
171
|
-
@associated_class, name,
|
172
|
-
options[:inversed].is_a?(TrueClass), *_ids)
|
173
|
-
else
|
174
|
-
klass.send(:associated_ids_for, @backend,
|
175
|
-
@associated_class, name, *_ids)
|
176
|
-
end
|
177
|
-
}
|
178
|
-
end
|
179
|
-
|
180
|
-
protected
|
181
|
-
|
182
|
-
def lock(when_steps_empty = true, *klasses, &block)
|
183
|
-
return(block.call) if !when_steps_empty && @steps.empty?
|
184
|
-
klasses += [@associated_class] if !klasses.include?(@associated_class)
|
185
|
-
@backend.lock(*klasses, &block)
|
186
|
-
end
|
187
|
-
|
188
|
-
private
|
189
|
-
|
190
|
-
def _find_by_id(id)
|
191
|
-
if !id.nil? && _exists?(id)
|
192
|
-
_load(id.to_s)
|
193
|
-
else
|
194
|
-
nil
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
def _load(id)
|
199
|
-
object = @associated_class.new
|
200
|
-
object.load(id)
|
201
|
-
object
|
202
|
-
end
|
203
|
-
|
204
|
-
def _all
|
205
|
-
_ids.map {|id| _load(id) }
|
206
|
-
end
|
207
|
-
|
208
|
-
end
|
209
|
-
|
210
|
-
end
|
211
|
-
|
212
|
-
end
|