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