nobrainer 0.29.0 → 0.30.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/lib/no_brainer/criteria.rb +5 -4
- data/lib/no_brainer/criteria/first_or_create.rb +34 -7
- data/lib/no_brainer/criteria/join.rb +4 -3
- data/lib/no_brainer/criteria/virtual_attributes.rb +18 -0
- data/lib/no_brainer/criteria/where.rb +3 -2
- data/lib/no_brainer/document.rb +1 -1
- data/lib/no_brainer/document/attributes.rb +1 -1
- data/lib/no_brainer/document/index.rb +1 -0
- data/lib/no_brainer/document/persistance.rb +7 -12
- data/lib/no_brainer/document/virtual_attributes.rb +52 -0
- data/lib/no_brainer/error.rb +1 -0
- data/lib/no_brainer/lock.rb +56 -29
- data/lib/no_brainer/profiler/controller_runtime.rb +2 -0
- data/lib/no_brainer/reentrant_lock.rb +36 -0
- data/lib/nobrainer.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebe188d50e06d6ef907cdf043560143e3bd7d703
|
4
|
+
data.tar.gz: 15b1580f27a66b6670386cc117b4767f930e67c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff9b387f2045109c33efb88d36273325edf34573acb134bbb94d3d2e04c1efd6fdf65b761739eac61c185309cfa9deba5a07987fdf56becf3d78308b300838b9
|
7
|
+
data.tar.gz: acb0f33b44d27ac25599352b0d568dd563e13a81da91b0dfef6cf72596a1500026e739f2cc64c63e22edc72ddb57ee3b61a70acf72c139cd502f0b1d95e78c9b
|
data/lib/no_brainer/criteria.rb
CHANGED
@@ -2,8 +2,9 @@ require 'rethinkdb'
|
|
2
2
|
|
3
3
|
class NoBrainer::Criteria
|
4
4
|
extend NoBrainer::Autoload
|
5
|
-
autoload_and_include :Core, :Run, :Raw, :
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
5
|
+
autoload_and_include :Core, :Run, :Raw, :VirtualAttributes, :Scope,
|
6
|
+
:AfterFind, :Where, :OrderBy, :Limit, :Pluck, :Count,
|
7
|
+
:Delete, :Enumerable, :Find, :First, :FirstOrCreate,
|
8
|
+
:Changes, :Aggregate, :EagerLoad, :Update, :Cache,
|
9
|
+
:Index, :Extend, :Join
|
9
10
|
end
|
@@ -38,7 +38,11 @@ module NoBrainer::Criteria::FirstOrCreate
|
|
38
38
|
|
39
39
|
def _first_or_create(create_params, save_options, &block)
|
40
40
|
raise "Cannot use .raw() with .first_or_create()" if raw?
|
41
|
-
|
41
|
+
|
42
|
+
if block && block.arity == 1
|
43
|
+
raise "When passing a block to first_or_create(), you must pass a block with no arguments.\n" +
|
44
|
+
"The passed block must return a hash of additional attributes for create()"
|
45
|
+
end
|
42
46
|
|
43
47
|
save_method = save_options.delete(:save_method)
|
44
48
|
should_update = save_options.delete(:update)
|
@@ -62,6 +66,25 @@ module NoBrainer::Criteria::FirstOrCreate
|
|
62
66
|
"\nend"
|
63
67
|
end
|
64
68
|
|
69
|
+
unless model.is_root_class? || (model.superclass.fields.keys & keys).empty?
|
70
|
+
# We can't allow the parent to share the keys we are matching on.
|
71
|
+
# Consider this case:
|
72
|
+
# - Base has the field :name, :uniq => true declared
|
73
|
+
# - A < Base
|
74
|
+
# - B < Base
|
75
|
+
# - A.create(:name => 'x'),
|
76
|
+
# - B.where(:name => 'x').first_or_create
|
77
|
+
# We are forced to return nil, or raise.
|
78
|
+
parent = model
|
79
|
+
parent = parent.superclass while parent.superclass < NoBrainer::Document &&
|
80
|
+
!(parent.superclass.fields.keys & keys).empty?
|
81
|
+
raise "A polymorphic problem has been detected: The fields `#{keys.inspect}' are defined on `#{parent}'.\n" +
|
82
|
+
"This is problematic as first_or_create() could return nil in some cases.\n" +
|
83
|
+
"Either 1) Only define `#{keys.inspect}' on `#{model}', \n" +
|
84
|
+
"or 2) Query the superclass, and pass :_type in first_or_create() as such:\n" +
|
85
|
+
" `#{parent}.where(...).first_or_create(:_type => \"#{model}\")'."
|
86
|
+
end
|
87
|
+
|
65
88
|
# We don't want to access create_params yet, because invoking the block
|
66
89
|
# might be costly (the user might be doing some API call or w/e), and
|
67
90
|
# so we want to invoke the block only if necessary.
|
@@ -108,18 +131,22 @@ module NoBrainer::Criteria::FirstOrCreate
|
|
108
131
|
where_clauses = finalized_criteria.options[:where_ast]
|
109
132
|
|
110
133
|
unless where_clauses.is_a?(NoBrainer::Criteria::Where::MultiOperator) &&
|
111
|
-
where_clauses.op == :and
|
112
|
-
where_clauses.clauses.all? do |c|
|
113
|
-
c.is_a?(NoBrainer::Criteria::Where::BinaryOperator) &&
|
114
|
-
c.op == :eq && c.key_modifier == :scalar
|
115
|
-
end
|
134
|
+
where_clauses.op == :and
|
116
135
|
raise "Please use a query of the form `.where(...).first_or_create(...)'"
|
117
136
|
end
|
118
137
|
|
119
138
|
Hash[where_clauses.clauses.map do |c|
|
139
|
+
unless c.is_a?(NoBrainer::Criteria::Where::BinaryOperator) &&
|
140
|
+
c.op == :eq && c.key_modifier == :scalar
|
141
|
+
# Ignore params on the subclass type, we are handling this case directly
|
142
|
+
# in _first_or_create()
|
143
|
+
next if c.key_path == [:_type]
|
144
|
+
raise "Please only use equal constraints in your where() query when using first_or_create()"
|
145
|
+
end
|
146
|
+
|
120
147
|
raise "You may not use nested hash queries with first_or.create()" if c.key_path.size > 1
|
121
148
|
[c.key_path.first.to_sym, c.value]
|
122
|
-
end]
|
149
|
+
end.compact].tap { |h| raise "Missing where() clauses for first_or_create()" if h.empty? }
|
123
150
|
end
|
124
151
|
|
125
152
|
def get_model_unique_fields
|
@@ -53,9 +53,10 @@ module NoBrainer::Criteria::Join
|
|
53
53
|
join_ast.reduce(super) do |rql, (association, criteria)|
|
54
54
|
rql.concat_map do |doc|
|
55
55
|
key = doc[association.eager_load_owner_key]
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
RethinkDB::RQL.new.branch(key.eq(nil), [],
|
57
|
+
criteria.where(association.eager_load_target_key => key).to_rql.map do |assoc_doc|
|
58
|
+
doc.merge(association.target_name => assoc_doc)
|
59
|
+
end)
|
59
60
|
end
|
60
61
|
end
|
61
62
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module NoBrainer::Criteria::VirtualAttributes
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
def compile_rql_pass2
|
5
|
+
rql = super
|
6
|
+
|
7
|
+
if model.virtual_fields
|
8
|
+
rql = rql.map do |_doc|
|
9
|
+
model.virtual_fields.reduce(_doc) do |doc, field|
|
10
|
+
field_rql = model.fields[field][:virtual].call(doc, RethinkDB::RQL.new)
|
11
|
+
field_rql.nil? ? doc : doc.merge(field => field_rql)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
rql
|
17
|
+
end
|
18
|
+
end
|
@@ -195,11 +195,12 @@ module NoBrainer::Criteria::Where
|
|
195
195
|
else
|
196
196
|
# 1) Box value in array if we have an any/all modifier
|
197
197
|
# 2) Box value in hash if we have a nested query.
|
198
|
-
|
198
|
+
box_value = key_modifier.in?([:any, :all]) || op == :include
|
199
|
+
value = [value] if box_value
|
199
200
|
value_hash = key_path.reverse.reduce(value) { |v,k| {k => v} }
|
200
201
|
value = model.cast_user_to_db_for(*value_hash.first)
|
201
202
|
value = key_path[1..-1].reduce(value) { |h,k| h[k] }
|
202
|
-
value = value.first if
|
203
|
+
value = value.first if box_value
|
203
204
|
value
|
204
205
|
end
|
205
206
|
end
|
data/lib/no_brainer/document.rb
CHANGED
@@ -7,7 +7,7 @@ module NoBrainer::Document
|
|
7
7
|
autoload_and_include :Core, :TableConfig, :InjectionLayer, :Attributes, :Readonly,
|
8
8
|
:Persistance, :Callbacks, :Validation, :Types, :Dirty, :PrimaryKey,
|
9
9
|
:Association, :Serialization, :Criteria, :Polymorphic, :Index, :Aliases,
|
10
|
-
:MissingAttributes, :LazyFetch, :AtomicOps
|
10
|
+
:MissingAttributes, :LazyFetch, :AtomicOps, :VirtualAttributes
|
11
11
|
|
12
12
|
autoload :DynamicAttributes, :Timestamps
|
13
13
|
|
@@ -2,7 +2,7 @@ module NoBrainer::Document::Attributes
|
|
2
2
|
VALID_FIELD_OPTIONS = [:index, :default, :type, :readonly, :primary_key,
|
3
3
|
:lazy_fetch, :store_as, :validates, :required, :unique,
|
4
4
|
:uniq, :format, :in, :length, :min_length, :max_length,
|
5
|
-
:prefix, :suffix]
|
5
|
+
:prefix, :suffix, :virtual]
|
6
6
|
RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations, :pk_value] +
|
7
7
|
NoBrainer::SymbolDecoration::OPERATORS
|
8
8
|
|
@@ -18,18 +18,13 @@ module NoBrainer::Document::Persistance
|
|
18
18
|
!new_record? && !destroyed?
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
|
21
|
+
def _reload(options={})
|
22
|
+
criteria = root_class.raw
|
23
23
|
if opt = options[:missing_attributes]
|
24
|
-
|
25
|
-
|
24
|
+
criteria = criteria.pluck(opt[:pluck]) if opt[:pluck]
|
25
|
+
criteria = criteria.without(opt[:without]) if opt[:without]
|
26
26
|
end
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
def _reload(options={})
|
31
|
-
attrs = NoBrainer.run { _reload_selector(options) }
|
32
|
-
raise NoBrainer::Error::DocumentNotFound, "#{self.class} :#{self.class.pk_name}=>\"#{pk_value}\" not found" unless attrs
|
27
|
+
attrs = criteria.find(pk_value)
|
33
28
|
|
34
29
|
options = options.merge(:pristine => true, :from_db => true)
|
35
30
|
|
@@ -60,7 +55,7 @@ module NoBrainer::Document::Persistance
|
|
60
55
|
end
|
61
56
|
|
62
57
|
def _create(options={})
|
63
|
-
attrs = self.class.persistable_attributes(@_attributes
|
58
|
+
attrs = self.class.persistable_attributes(@_attributes)
|
64
59
|
result = NoBrainer.run(self.class.rql_table.insert(attrs))
|
65
60
|
self.pk_value ||= result['generated_keys'].to_a.first
|
66
61
|
@new_record = false
|
@@ -69,7 +64,7 @@ module NoBrainer::Document::Persistance
|
|
69
64
|
end
|
70
65
|
|
71
66
|
def _update(attrs)
|
72
|
-
rql = ->(doc){ self.class.persistable_attributes(attrs, :
|
67
|
+
rql = ->(doc){ self.class.persistable_attributes(attrs, :rql_doc => doc) }
|
73
68
|
NoBrainer.run { selector.update(&rql) }
|
74
69
|
end
|
75
70
|
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module NoBrainer::Document::VirtualAttributes
|
2
|
+
extend NoBrainer::Autoload
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
VALID_VIRTUAL_FIELD_OPTIONS = [:type, :lazy_fetch, :virtual]
|
6
|
+
|
7
|
+
included do
|
8
|
+
cattr_accessor :virtual_fields, :instance_accessor => false
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def virtual_field(attr, rql=nil, options={}, &block)
|
13
|
+
rql, options = nil, rql if rql.is_a?(Hash)
|
14
|
+
rql ||= block
|
15
|
+
rql_proc = rql.is_a?(Proc) ? rql : proc { rql }
|
16
|
+
field(attr, options.merge(:virtual => rql_proc))
|
17
|
+
end
|
18
|
+
|
19
|
+
def field(attr, options={})
|
20
|
+
return super unless options.key?(:virtual)
|
21
|
+
|
22
|
+
raise "virtual attributes are limited to the root class `#{self.root_class}' for the moment.\n" +
|
23
|
+
"Ask on GitHub for polymorphic support." unless is_root_class?
|
24
|
+
|
25
|
+
raise "You cannot index a virtual attribute. Use an index with a lambda expression instead" if options[:index]
|
26
|
+
options.assert_valid_keys(*VALID_VIRTUAL_FIELD_OPTIONS)
|
27
|
+
|
28
|
+
self.virtual_fields ||= Set.new
|
29
|
+
virtual_fields << attr
|
30
|
+
|
31
|
+
inject_in_layer :virtual_attributes do
|
32
|
+
define_method("#{attr}=") do |value|
|
33
|
+
raise NoBrainer::Error::ReadonlyField.new("#{attr} is a virtual attribute and thus readonly.")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove_field(attr, options={})
|
41
|
+
super
|
42
|
+
|
43
|
+
if fields[:virtual]
|
44
|
+
virtual_fields.try(:delete, attr)
|
45
|
+
|
46
|
+
inject_in_layer :virtual_attributes do
|
47
|
+
remove_method("#{attr}=")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/no_brainer/error.rb
CHANGED
@@ -11,6 +11,7 @@ module NoBrainer::Error
|
|
11
11
|
class UnknownAttribute < RuntimeError; end
|
12
12
|
class AtomicBlock < RuntimeError; end
|
13
13
|
class LostLock < RuntimeError; end
|
14
|
+
class LockInvalidOp < RuntimeError; end
|
14
15
|
class LockUnavailable < RuntimeError; end
|
15
16
|
class InvalidPolymorphicType < RuntimeError; end
|
16
17
|
|
data/lib/no_brainer/lock.rb
CHANGED
@@ -7,27 +7,33 @@ class NoBrainer::Lock
|
|
7
7
|
|
8
8
|
# Since PKs are limited to 127 characters, we can't use the user's key as a PK
|
9
9
|
# as it could be arbitrarily long.
|
10
|
-
field :key_hash,
|
11
|
-
field :key,
|
12
|
-
field :
|
13
|
-
field :expires_at,
|
14
|
-
|
15
|
-
# We always use a new token, even when reading from the DB, because that's
|
16
|
-
# what represent our instance.
|
17
|
-
after_initialize { self.token = NoBrainer::Document::PrimaryKey::Generator.generate }
|
10
|
+
field :key_hash, :type => String, :primary_key => true, :default => ->{ Digest::SHA1.base64digest(key.to_s) }
|
11
|
+
field :key, :type => String
|
12
|
+
field :instance_token, :type => String, :default => ->{ get_new_instance_token }
|
13
|
+
field :expires_at, :type => Time
|
18
14
|
|
19
15
|
scope :expired, where(:expires_at.lt(RethinkDB::RQL.new.now))
|
20
16
|
|
21
|
-
def
|
22
|
-
|
17
|
+
def self.find(key)
|
18
|
+
super(Digest::SHA1.base64digest(key.to_s))
|
19
|
+
end
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
def initialize(key, options={})
|
22
|
+
if options[:from_db]
|
23
|
+
super
|
24
|
+
# We reset our instance_token to allow recoveries.
|
25
|
+
self.instance_token = get_new_instance_token
|
26
|
+
else
|
27
|
+
@default_options = options.slice(:expire, :timeout)
|
28
|
+
options.delete(:expire); options.delete(:timeout);
|
29
|
+
|
30
|
+
super(options.merge(:key => key))
|
31
|
+
raise ArgumentError unless valid?
|
28
32
|
end
|
33
|
+
end
|
29
34
|
|
30
|
-
|
35
|
+
def get_new_instance_token
|
36
|
+
NoBrainer::Document::PrimaryKey::Generator.generate
|
31
37
|
end
|
32
38
|
|
33
39
|
def synchronize(options={}, &block)
|
@@ -41,22 +47,21 @@ class NoBrainer::Lock
|
|
41
47
|
|
42
48
|
def lock(options={})
|
43
49
|
options.assert_valid_keys(:expire, :timeout)
|
44
|
-
timeout =
|
50
|
+
timeout = get_option_value(options, :timeout)
|
45
51
|
sleep_amount = 0.1
|
46
52
|
|
47
53
|
start_at = Time.now
|
48
|
-
|
49
|
-
return if try_lock(options.
|
54
|
+
loop do
|
55
|
+
return if try_lock(options.slice(:expire))
|
56
|
+
raise_lock_unavailable! if Time.now - start_at + sleep_amount > timeout
|
50
57
|
sleep(sleep_amount)
|
51
58
|
sleep_amount = [1, sleep_amount * 2].min
|
52
59
|
end
|
53
|
-
|
54
|
-
raise NoBrainer::Error::LockUnavailable.new("Lock on `#{key}' unavailable")
|
55
60
|
end
|
56
61
|
|
57
62
|
def try_lock(options={})
|
58
63
|
options.assert_valid_keys(:expire)
|
59
|
-
|
64
|
+
raise_if_locked!
|
60
65
|
|
61
66
|
set_expiration(options)
|
62
67
|
|
@@ -71,28 +76,28 @@ class NoBrainer::Lock
|
|
71
76
|
end
|
72
77
|
|
73
78
|
def unlock
|
74
|
-
|
79
|
+
raise_unless_locked!
|
75
80
|
|
76
81
|
result = NoBrainer.run do |r|
|
77
82
|
selector.replace do |doc|
|
78
|
-
r.branch(doc[:
|
83
|
+
r.branch(doc[:instance_token].default(nil).eq(self.instance_token),
|
79
84
|
nil, doc)
|
80
85
|
end
|
81
86
|
end
|
82
87
|
|
83
88
|
@locked = false
|
84
|
-
|
89
|
+
raise_lost_lock! unless result['deleted'] == 1
|
85
90
|
end
|
86
91
|
|
87
92
|
def refresh(options={})
|
88
93
|
options.assert_valid_keys(:expire)
|
89
|
-
|
94
|
+
raise_unless_locked!
|
90
95
|
|
91
|
-
set_expiration(options)
|
96
|
+
set_expiration(options.merge(:use_previous_expire => true))
|
92
97
|
|
93
98
|
result = NoBrainer.run do |r|
|
94
99
|
selector.update do |doc|
|
95
|
-
r.branch(doc[:
|
100
|
+
r.branch(doc[:instance_token].eq(self.instance_token),
|
96
101
|
{ :expires_at => self.expires_at }, nil)
|
97
102
|
end
|
98
103
|
end
|
@@ -102,7 +107,7 @@ class NoBrainer::Lock
|
|
102
107
|
# unlikely to happen and should not harmful.
|
103
108
|
unless result['replaced'] == 1
|
104
109
|
@locked = false
|
105
|
-
|
110
|
+
raise_lost_lock!
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|
@@ -112,7 +117,29 @@ class NoBrainer::Lock
|
|
112
117
|
private
|
113
118
|
|
114
119
|
def set_expiration(options)
|
115
|
-
expire =
|
120
|
+
expire = @previous_expire if options[:use_previous_expire] && !options[:expire]
|
121
|
+
expire ||= get_option_value(options, :expire)
|
122
|
+
@previous_expire = expire
|
116
123
|
self.expires_at = RethinkDB::RQL.new.now + expire
|
117
124
|
end
|
125
|
+
|
126
|
+
def get_option_value(options, key)
|
127
|
+
NoBrainer::Config.lock_options.merge(@default_options || {}).merge(options)[key]
|
128
|
+
end
|
129
|
+
|
130
|
+
def raise_if_locked!
|
131
|
+
raise NoBrainer::Error::LockInvalidOp.new("Lock instance `#{key}' already locked") if @locked
|
132
|
+
end
|
133
|
+
|
134
|
+
def raise_unless_locked!
|
135
|
+
raise NoBrainer::Error::LockInvalidOp.new("Lock instance `#{key}' not locked") unless @locked
|
136
|
+
end
|
137
|
+
|
138
|
+
def raise_lost_lock!
|
139
|
+
raise NoBrainer::Error::LostLock.new("Lost lock on `#{key}'")
|
140
|
+
end
|
141
|
+
|
142
|
+
def raise_lock_unavailable!
|
143
|
+
raise NoBrainer::Error::LockUnavailable.new("Lock on `#{key}' unavailable")
|
144
|
+
end
|
118
145
|
end
|
@@ -47,6 +47,8 @@ module NoBrainer::Profiler::ControllerRuntime
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def cleanup_view_runtime
|
50
|
+
return super unless Profiler.current
|
51
|
+
|
50
52
|
time_spent_in_db_before_views = Profiler.current.total_duration
|
51
53
|
runtime = super
|
52
54
|
time_spent_in_db_after_views = Profiler.current.total_duration
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class NoBrainer::ReentrantLock < NoBrainer::Lock
|
2
|
+
field :lock_count, :type => Integer
|
3
|
+
|
4
|
+
def try_lock(options={})
|
5
|
+
options.assert_valid_keys(:expire)
|
6
|
+
set_expiration(options)
|
7
|
+
|
8
|
+
result = NoBrainer.run do |r|
|
9
|
+
selector.replace do |doc|
|
10
|
+
r.branch(doc[:instance_token].default(nil).eq(self.instance_token),
|
11
|
+
doc.merge(:expires_at => self.expires_at,
|
12
|
+
:lock_count => doc[:lock_count] + 1),
|
13
|
+
r.branch(doc.eq(nil).or(doc[:expires_at] < r.now),
|
14
|
+
self.attributes.merge(:lock_count => 1), doc))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
@locked = true # to make refresh() and synchronize() happy, somewhat hacky
|
19
|
+
return (result['inserted'] + result['replaced']) == 1
|
20
|
+
end
|
21
|
+
|
22
|
+
def unlock
|
23
|
+
set_expiration(:use_previous_expire => true)
|
24
|
+
|
25
|
+
result = NoBrainer.run do |r|
|
26
|
+
selector.replace do |doc|
|
27
|
+
r.branch(doc[:instance_token].default(nil).eq(self.instance_token),
|
28
|
+
r.branch(doc[:lock_count] > 1,
|
29
|
+
doc.merge(:expires_at => self.expires_at,
|
30
|
+
:lock_count => doc[:lock_count] - 1), nil), doc)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
raise_lost_lock! unless (result['deleted'] + result['replaced']) == 1
|
35
|
+
end
|
36
|
+
end
|
data/lib/nobrainer.rb
CHANGED
@@ -16,7 +16,7 @@ module NoBrainer
|
|
16
16
|
# Code that is loaded through the DSL of NoBrainer should not be eager loaded.
|
17
17
|
autoload :Document, :IndexManager, :Loader, :Fork, :Geo, :SymbolDecoration
|
18
18
|
eager_autoload :Config, :Connection, :ConnectionManager, :Error,
|
19
|
-
:QueryRunner, :Criteria, :RQL, :Lock, :Profiler, :System
|
19
|
+
:QueryRunner, :Criteria, :RQL, :Lock, :ReentrantLock, :Profiler, :System
|
20
20
|
|
21
21
|
class << self
|
22
22
|
delegate :connection, :disconnect, :to => 'NoBrainer::ConnectionManager'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nobrainer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.30.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicolas Viennot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rethinkdb
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/no_brainer/criteria/run.rb
|
117
117
|
- lib/no_brainer/criteria/scope.rb
|
118
118
|
- lib/no_brainer/criteria/update.rb
|
119
|
+
- lib/no_brainer/criteria/virtual_attributes.rb
|
119
120
|
- lib/no_brainer/criteria/where.rb
|
120
121
|
- lib/no_brainer/document.rb
|
121
122
|
- lib/no_brainer/document/aliases.rb
|
@@ -167,6 +168,7 @@ files:
|
|
167
168
|
- lib/no_brainer/document/validation/core.rb
|
168
169
|
- lib/no_brainer/document/validation/not_null.rb
|
169
170
|
- lib/no_brainer/document/validation/uniqueness.rb
|
171
|
+
- lib/no_brainer/document/virtual_attributes.rb
|
170
172
|
- lib/no_brainer/error.rb
|
171
173
|
- lib/no_brainer/fork.rb
|
172
174
|
- lib/no_brainer/geo.rb
|
@@ -194,6 +196,7 @@ files:
|
|
194
196
|
- lib/no_brainer/query_runner/write_error.rb
|
195
197
|
- lib/no_brainer/railtie.rb
|
196
198
|
- lib/no_brainer/railtie/database.rake
|
199
|
+
- lib/no_brainer/reentrant_lock.rb
|
197
200
|
- lib/no_brainer/rql.rb
|
198
201
|
- lib/no_brainer/symbol_decoration.rb
|
199
202
|
- lib/no_brainer/system.rb
|