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