nobrainer 0.18.0 → 0.22.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/autoload.rb +0 -5
- data/lib/no_brainer/config.rb +73 -39
- data/lib/no_brainer/connection.rb +2 -4
- data/lib/no_brainer/criteria/after_find.rb +3 -11
- data/lib/no_brainer/criteria/aggregate.rb +2 -2
- data/lib/no_brainer/criteria/cache.rb +15 -10
- data/lib/no_brainer/criteria/core.rb +46 -11
- data/lib/no_brainer/criteria/delete.rb +2 -2
- data/lib/no_brainer/criteria/eager_load.rb +51 -0
- data/lib/no_brainer/criteria/extend.rb +4 -16
- data/lib/no_brainer/criteria/find.rb +27 -0
- data/lib/no_brainer/criteria/index.rb +7 -13
- data/lib/no_brainer/criteria/limit.rb +5 -12
- data/lib/no_brainer/criteria/order_by.rb +20 -36
- data/lib/no_brainer/criteria/pluck.rb +16 -22
- data/lib/no_brainer/criteria/raw.rb +4 -10
- data/lib/no_brainer/criteria/scope.rb +6 -19
- data/lib/no_brainer/criteria/update.rb +8 -6
- data/lib/no_brainer/criteria/where.rb +252 -138
- data/lib/no_brainer/criteria.rb +3 -2
- data/lib/no_brainer/document/aliases.rb +3 -3
- data/lib/no_brainer/document/association/belongs_to.rb +9 -5
- data/lib/no_brainer/document/association/core.rb +6 -5
- data/lib/no_brainer/document/association/eager_loader.rb +9 -9
- data/lib/no_brainer/document/association/has_many.rb +23 -9
- data/lib/no_brainer/document/association/has_many_through.rb +12 -3
- data/lib/no_brainer/document/atomic_ops.rb +79 -78
- data/lib/no_brainer/document/attributes.rb +24 -20
- data/lib/no_brainer/document/callbacks.rb +1 -1
- data/lib/no_brainer/document/core.rb +5 -2
- data/lib/no_brainer/document/criteria.rb +14 -19
- data/lib/no_brainer/document/dirty.rb +11 -16
- data/lib/no_brainer/document/index/index.rb +2 -1
- data/lib/no_brainer/document/index/meta_store.rb +1 -1
- data/lib/no_brainer/document/index.rb +14 -10
- data/lib/no_brainer/document/persistance.rb +24 -13
- data/lib/no_brainer/document/primary_key/generator.rb +83 -0
- data/lib/no_brainer/document/{id.rb → primary_key.rb} +9 -36
- data/lib/no_brainer/document/store_in.rb +2 -2
- data/lib/no_brainer/document/timestamps.rb +4 -2
- data/lib/no_brainer/document/types/binary.rb +2 -7
- data/lib/no_brainer/document/types/boolean.rb +2 -4
- data/lib/no_brainer/document/types/date.rb +2 -2
- data/lib/no_brainer/document/types/float.rb +2 -2
- data/lib/no_brainer/document/types/geo.rb +1 -0
- data/lib/no_brainer/document/types/integer.rb +2 -2
- data/lib/no_brainer/document/types/set.rb +2 -2
- data/lib/no_brainer/document/types/string.rb +5 -2
- data/lib/no_brainer/document/types/symbol.rb +2 -2
- data/lib/no_brainer/document/types/text.rb +18 -0
- data/lib/no_brainer/document/types/time.rb +2 -2
- data/lib/no_brainer/document/types.rb +17 -18
- data/lib/no_brainer/document/validation/not_null.rb +15 -0
- data/lib/no_brainer/document/{uniqueness.rb → validation/uniqueness.rb} +11 -11
- data/lib/no_brainer/document/validation.rb +35 -6
- data/lib/no_brainer/document.rb +1 -1
- data/lib/no_brainer/error.rb +21 -19
- data/lib/no_brainer/geo/base.rb +16 -0
- data/lib/no_brainer/geo/circle.rb +25 -0
- data/lib/no_brainer/geo/line_string.rb +11 -0
- data/lib/no_brainer/geo/point.rb +49 -0
- data/lib/no_brainer/geo/polygon.rb +11 -0
- data/lib/no_brainer/geo.rb +4 -0
- data/lib/no_brainer/locale/en.yml +1 -0
- data/lib/no_brainer/lock.rb +114 -0
- data/lib/no_brainer/query_runner/connection_lock.rb +1 -1
- data/lib/no_brainer/query_runner/database_on_demand.rb +0 -1
- data/lib/no_brainer/query_runner/missing_index.rb +1 -1
- data/lib/no_brainer/query_runner/reconnect.rb +9 -11
- data/lib/no_brainer/query_runner/run_options.rb +0 -3
- data/lib/no_brainer/query_runner/table_on_demand.rb +3 -4
- data/lib/no_brainer/railtie/database.rake +2 -2
- data/lib/no_brainer/rql.rb +1 -5
- data/lib/nobrainer.rb +2 -6
- data/lib/rails/generators/nobrainer.rb +1 -1
- metadata +34 -9
- data/lib/no_brainer/criteria/preload.rb +0 -50
- data/lib/no_brainer/decorated_symbol.rb +0 -17
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
module NoBrainer::Document::Validation
|
|
2
|
+
extend NoBrainer::Autoload
|
|
2
3
|
extend ActiveSupport::Concern
|
|
3
4
|
include ActiveModel::Validations
|
|
4
5
|
include ActiveModel::Validations::Callbacks
|
|
5
6
|
|
|
7
|
+
autoload_and_include :Uniqueness, :NotNull
|
|
8
|
+
|
|
6
9
|
included do
|
|
7
10
|
# We don't want before_validation returning false to halt the chain.
|
|
8
11
|
define_callbacks :validation, :skip_after_callbacks_if_terminated => true,
|
|
@@ -12,7 +15,7 @@ module NoBrainer::Document::Validation
|
|
|
12
15
|
def valid?(context=nil, options={})
|
|
13
16
|
context ||= new_record? ? :create : :update
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
# XXX Monkey Patching, because we need to have control on errors.clear
|
|
16
19
|
current_context, self.validation_context = validation_context, context
|
|
17
20
|
errors.clear unless options[:clear_errors] == false
|
|
18
21
|
run_validations!
|
|
@@ -20,15 +23,41 @@ module NoBrainer::Document::Validation
|
|
|
20
23
|
self.validation_context = current_context
|
|
21
24
|
end
|
|
22
25
|
|
|
26
|
+
SHORTHANDS = { :format => :format, :length => :length, :required => :presence,
|
|
27
|
+
:uniq => :uniqueness, :unique => :uniqueness, :in => :inclusion }
|
|
28
|
+
|
|
23
29
|
module ClassMethods
|
|
24
30
|
def _field(attr, options={})
|
|
25
31
|
super
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
validates(attr,
|
|
30
|
-
|
|
32
|
+
|
|
33
|
+
shorthands = SHORTHANDS
|
|
34
|
+
shorthands = shorthands.merge(:required => :not_null) if options[:type] == NoBrainer::Boolean
|
|
35
|
+
shorthands.each { |k,v| validates(attr, v => options[k]) if options.has_key?(k) }
|
|
36
|
+
|
|
31
37
|
validates(attr, options[:validates]) if options[:validates]
|
|
38
|
+
validates(attr, :length => { :minimum => options[:min_length] }) if options[:min_length]
|
|
39
|
+
validates(attr, :length => { :maximum => options[:max_length] }) if options[:max_length]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class ActiveModel::EachValidator
|
|
45
|
+
def should_validate_field?(record, attribute)
|
|
46
|
+
return true unless record.is_a?(NoBrainer::Document)
|
|
47
|
+
return true if record.new_record?
|
|
48
|
+
|
|
49
|
+
attr_changed = "#{attribute}_changed?"
|
|
50
|
+
return record.respond_to?(attr_changed) ? record.__send__(attr_changed) : true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# XXX Monkey Patching :(
|
|
54
|
+
def validate(record)
|
|
55
|
+
attributes.each do |attribute|
|
|
56
|
+
next unless should_validate_field?(record, attribute) # <--- Added
|
|
57
|
+
value = record.read_attribute_for_validation(attribute)
|
|
58
|
+
next if value.is_a?(NoBrainer::Document::AtomicOps::PendingAtomic) # <--- Added
|
|
59
|
+
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
|
|
60
|
+
validate_each(record, attribute, value)
|
|
32
61
|
end
|
|
33
62
|
end
|
|
34
63
|
end
|
data/lib/no_brainer/document.rb
CHANGED
|
@@ -5,7 +5,7 @@ module NoBrainer::Document
|
|
|
5
5
|
extend NoBrainer::Autoload
|
|
6
6
|
|
|
7
7
|
autoload_and_include :Core, :StoreIn, :InjectionLayer, :Attributes, :Readonly,
|
|
8
|
-
:
|
|
8
|
+
:Persistance, :Validation, :Types, :Callbacks, :Dirty, :PrimaryKey,
|
|
9
9
|
:Association, :Serialization, :Criteria, :Polymorphic, :Index, :Aliases,
|
|
10
10
|
:MissingAttributes, :LazyFetch, :AtomicOps
|
|
11
11
|
|
data/lib/no_brainer/error.rb
CHANGED
|
@@ -5,24 +5,13 @@ module NoBrainer::Error
|
|
|
5
5
|
class ChildrenExist < RuntimeError; end
|
|
6
6
|
class CannotUseIndex < RuntimeError; end
|
|
7
7
|
class MissingIndex < RuntimeError; end
|
|
8
|
-
class InvalidType < RuntimeError; end
|
|
9
8
|
class AssociationNotPersisted < RuntimeError; end
|
|
10
9
|
class ReadonlyField < RuntimeError; end
|
|
11
10
|
class MissingAttribute < RuntimeError; end
|
|
11
|
+
class UnknownAttribute < RuntimeError; end
|
|
12
12
|
class AtomicBlock < RuntimeError; end
|
|
13
|
-
|
|
14
|
-
class
|
|
15
|
-
attr_accessor :instance, :field, :value
|
|
16
|
-
def initialize(instance, field, value)
|
|
17
|
-
@instance = instance
|
|
18
|
-
@field = field
|
|
19
|
-
@value = value
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def message
|
|
23
|
-
"Cannot read #{field}, atomic operations are pending"
|
|
24
|
-
end
|
|
25
|
-
end
|
|
13
|
+
class LostLock < RuntimeError; end
|
|
14
|
+
class LockUnavailable < RuntimeError; end
|
|
26
15
|
|
|
27
16
|
class DocumentInvalid < RuntimeError
|
|
28
17
|
attr_accessor :instance
|
|
@@ -36,19 +25,32 @@ module NoBrainer::Error
|
|
|
36
25
|
end
|
|
37
26
|
|
|
38
27
|
class InvalidType < RuntimeError
|
|
39
|
-
attr_accessor :attr_name, :value, :type
|
|
28
|
+
attr_accessor :model, :attr_name, :value, :type, :error
|
|
40
29
|
def initialize(options={})
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
update(options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def update(options={})
|
|
34
|
+
options.assert_valid_keys(:model, :attr_name, :type, :value, :error)
|
|
35
|
+
options.each { |k,v| instance_variable_set("@#{k}", v) }
|
|
44
36
|
end
|
|
45
37
|
|
|
46
38
|
def human_type_name
|
|
47
39
|
type.to_s.underscore.humanize.downcase
|
|
48
40
|
end
|
|
49
41
|
|
|
42
|
+
def error
|
|
43
|
+
# dup because errors.add eventually .delete() on our option.
|
|
44
|
+
@error.nil? ? (type && { :type => human_type_name }) : @error.dup
|
|
45
|
+
end
|
|
46
|
+
|
|
50
47
|
def message
|
|
51
|
-
|
|
48
|
+
return super unless model && attr_name && error
|
|
49
|
+
value = self.value
|
|
50
|
+
mock = model.allocate
|
|
51
|
+
mock.singleton_class.send(:define_method, :read_attribute_for_validation) { |_| value }
|
|
52
|
+
mock.errors.add(attr_name, :invalid_type, error)
|
|
53
|
+
mock.errors.full_messages.first
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module NoBrainer::Geo::Base
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
def self.normalize_geo_options(options)
|
|
5
|
+
options = options.symbolize_keys
|
|
6
|
+
|
|
7
|
+
geo_system = options.delete(:geo_system) || NoBrainer::Config.geo_options[:geo_system]
|
|
8
|
+
unit = options.delete(:unit) || NoBrainer::Config.geo_options[:unit]
|
|
9
|
+
|
|
10
|
+
options[:unit] = unit if unit && unit.to_s != 'm'
|
|
11
|
+
options[:geo_system] = geo_system if geo_system && geo_system.to_s != 'WGS84'
|
|
12
|
+
options[:max_dist] = options.delete(:max_distance) if options[:max_distance]
|
|
13
|
+
|
|
14
|
+
options
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class NoBrainer::Geo::Circle < Struct.new(:center, :radius, :options)
|
|
2
|
+
include NoBrainer::Geo::Base
|
|
3
|
+
|
|
4
|
+
def initialize(*args)
|
|
5
|
+
options = args.extract_options!
|
|
6
|
+
options = NoBrainer::Geo::Base.normalize_geo_options(options)
|
|
7
|
+
|
|
8
|
+
raise NoBrainer::Error::InvalidType if args.size > 2
|
|
9
|
+
center = args[0] || options.delete(:center)
|
|
10
|
+
radius = args[1] || options.delete(:radius)
|
|
11
|
+
|
|
12
|
+
center = NoBrainer::Geo::Point.nobrainer_cast_user_to_model(center)
|
|
13
|
+
radius = Float.nobrainer_cast_user_to_model(radius)
|
|
14
|
+
|
|
15
|
+
self.center = center
|
|
16
|
+
self.radius = radius
|
|
17
|
+
self.options = options
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_rql
|
|
21
|
+
RethinkDB::RQL.new.circle(center.to_rql, radius, options)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# No DB serialization, can't store circles.
|
|
25
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class NoBrainer::Geo::LineString < Struct.new(:points)
|
|
2
|
+
include NoBrainer::Geo::Base
|
|
3
|
+
|
|
4
|
+
def initialize(*points)
|
|
5
|
+
self.points = points.map { |p| NoBrainer::Geo::Point.nobrainer_cast_user_to_model(p) }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def to_rql
|
|
9
|
+
RethinkDB::RQL.new.line(points.map(&:to_rql))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'no_brainer/document/types/float'
|
|
2
|
+
|
|
3
|
+
class NoBrainer::Geo::Point < Struct.new(:longitude, :latitude)
|
|
4
|
+
include NoBrainer::Geo::Base
|
|
5
|
+
|
|
6
|
+
def initialize(*args)
|
|
7
|
+
if args.size == 2
|
|
8
|
+
longitude, latitude = args
|
|
9
|
+
elsif args.size == 1 && args.first.is_a?(self.class)
|
|
10
|
+
longitude, latitude = args.first.longitude, args.first.latitude
|
|
11
|
+
elsif args.size == 1 && args.first.is_a?(Hash)
|
|
12
|
+
opt = args.first.symbolize_keys
|
|
13
|
+
longitude, latitude = opt[:longitude] || opt[:long], opt[:latitude] || opt[:lat]
|
|
14
|
+
else
|
|
15
|
+
raise NoBrainer::Error::InvalidType
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
longitude = Float.nobrainer_cast_user_to_model(longitude)
|
|
19
|
+
latitude = Float.nobrainer_cast_user_to_model(latitude)
|
|
20
|
+
|
|
21
|
+
raise NoBrainer::Error::InvalidType unless (-180..180).include?(longitude)
|
|
22
|
+
raise NoBrainer::Error::InvalidType unless (-90..90).include?(latitude)
|
|
23
|
+
|
|
24
|
+
self.longitude = longitude
|
|
25
|
+
self.latitude = latitude
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_rql
|
|
29
|
+
RethinkDB::RQL.new.point(longitude, latitude)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_s
|
|
33
|
+
[longitude, latitude].inspect
|
|
34
|
+
end
|
|
35
|
+
alias_method :inspect, :to_s
|
|
36
|
+
|
|
37
|
+
def self.nobrainer_cast_user_to_model(value)
|
|
38
|
+
value.is_a?(Array) ? new(*value) : new(value)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.nobrainer_cast_db_to_model(value)
|
|
42
|
+
return value unless value.is_a?(Hash) && value['coordinates'].is_a?(Array) && value['coordinates'].size == 2
|
|
43
|
+
new(value['coordinates'][0], value['coordinates'][1])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.nobrainer_cast_model_to_db(value)
|
|
47
|
+
value.is_a?(self) ? value.to_rql : value
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class NoBrainer::Geo::Polygon < Struct.new(:points)
|
|
2
|
+
include NoBrainer::Geo::Base
|
|
3
|
+
|
|
4
|
+
def initialize(*points)
|
|
5
|
+
self.points = points.map { |p| NoBrainer::Geo::Point.nobrainer_cast_user_to_model(p) }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def to_rql
|
|
9
|
+
RethinkDB::RQL.new.polygon(points.map(&:to_rql))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require 'digest/sha1'
|
|
2
|
+
|
|
3
|
+
class NoBrainer::Lock
|
|
4
|
+
include NoBrainer::Document
|
|
5
|
+
|
|
6
|
+
store_in :table => 'nobrainer_locks'
|
|
7
|
+
|
|
8
|
+
# Since PKs are limited to 127 characters, we can't use the user's key as a PK
|
|
9
|
+
# as it could be arbitrarily long.
|
|
10
|
+
field :key_hash, :type => String, :primary_key => true, :default => ->{ Digest::SHA1.base64digest(key) }
|
|
11
|
+
field :key, :type => String
|
|
12
|
+
field :token, :type => String
|
|
13
|
+
field :expires_at, :type => Time
|
|
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 }
|
|
18
|
+
|
|
19
|
+
scope :expired, where(:expires_at.lt(RethinkDB::RQL.new.now))
|
|
20
|
+
|
|
21
|
+
def initialize(key, options={})
|
|
22
|
+
return super if options[:from_db]
|
|
23
|
+
|
|
24
|
+
key = case key
|
|
25
|
+
when Symbol then key.to_s
|
|
26
|
+
when String then key
|
|
27
|
+
else raise ArgumentError
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
super(options.merge(:key => key))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def lock(options={}, &block)
|
|
34
|
+
if block
|
|
35
|
+
lock(options)
|
|
36
|
+
return block.call.tap { unlock }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
options.assert_valid_keys(:expire, :timeout)
|
|
40
|
+
timeout = NoBrainer::Config.lock_options.merge(options)[:timeout]
|
|
41
|
+
sleep_amount = 0.1
|
|
42
|
+
|
|
43
|
+
start_at = Time.now
|
|
44
|
+
while Time.now - start_at < timeout
|
|
45
|
+
return if try_lock(options.select { |k,_| k == :expire })
|
|
46
|
+
sleep(sleep_amount)
|
|
47
|
+
sleep_amount = [1, sleep_amount * 2].min
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
raise NoBrainer::Error::LockUnavailable.new("Lock on `#{key}' unavailable")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def try_lock(options={})
|
|
54
|
+
options.assert_valid_keys(:expire)
|
|
55
|
+
raise "Lock instance `#{key}' already locked" if @locked
|
|
56
|
+
|
|
57
|
+
set_expiration(options)
|
|
58
|
+
|
|
59
|
+
result = NoBrainer.run do |r|
|
|
60
|
+
selector.replace do |doc|
|
|
61
|
+
r.branch(doc.eq(nil).or(doc[:expires_at] < r.now),
|
|
62
|
+
self.attributes, doc)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
return @locked = (result['inserted'] + result['replaced']) == 1
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def unlock
|
|
70
|
+
raise "Lock instance `#{key}' not locked" unless @locked
|
|
71
|
+
|
|
72
|
+
result = NoBrainer.run do |r|
|
|
73
|
+
selector.replace do |doc|
|
|
74
|
+
r.branch(doc[:token].eq(self.token),
|
|
75
|
+
nil, doc)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
@locked = false
|
|
80
|
+
raise NoBrainer::Error::LostLock.new("Lost lock on `#{key}'") unless result['deleted'] == 1
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def refresh(options={})
|
|
84
|
+
options.assert_valid_keys(:expire)
|
|
85
|
+
raise "Lock instance `#{key}' not locked" unless @locked
|
|
86
|
+
|
|
87
|
+
set_expiration(options)
|
|
88
|
+
|
|
89
|
+
result = NoBrainer.run do |r|
|
|
90
|
+
selector.update do |doc|
|
|
91
|
+
r.branch(doc[:token].eq(self.token),
|
|
92
|
+
{ :expires_at => self.expires_at }, nil)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Note: If we are too quick, expires_at may not change, and the returned
|
|
97
|
+
# 'replaced' won't be 1. We'll generate a spurious error. This is very
|
|
98
|
+
# unlikely to happen and should not harmful.
|
|
99
|
+
unless result['replaced'] == 1
|
|
100
|
+
@locked = false
|
|
101
|
+
raise NoBrainer::Error::LostLock.new("Lost lock on `#{key}'")
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
def set_expiration(options)
|
|
108
|
+
expire = NoBrainer::Config.lock_options.merge(options)[:expire]
|
|
109
|
+
self.expires_at = RethinkDB::RQL.new.now + expire
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def save?; raise; end
|
|
113
|
+
def delete; raise; end
|
|
114
|
+
end
|
|
@@ -10,7 +10,6 @@ class NoBrainer::QueryRunner::DatabaseOnDemand < NoBrainer::QueryRunner::Middlew
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def handle_database_on_demand_exception?(env, e)
|
|
13
|
-
(NoBrainer::Config.auto_create_databases || env[:auto_create_databases]) &&
|
|
14
13
|
e.message =~ /^Database `(.+)` does not exist\.$/ && $1
|
|
15
14
|
end
|
|
16
15
|
|
|
@@ -11,7 +11,7 @@ class NoBrainer::QueryRunner::MissingIndex < NoBrainer::QueryRunner::Middleware
|
|
|
11
11
|
index = model.indexes.values.select { |i| i.aliased_name == index_name.to_sym }.first if model
|
|
12
12
|
index_name = index.name if index
|
|
13
13
|
|
|
14
|
-
if model.try(:pk_name).try(:to_s) == index_name
|
|
14
|
+
if model.try(:pk_name).try(:to_s) == index_name.to_s
|
|
15
15
|
err_msg = "Please update the primary key `#{index_name}` in the table `#{database_name}.#{table_name}`."
|
|
16
16
|
else
|
|
17
17
|
err_msg = "Please run `NoBrainer.sync_indexes' or `rake nobrainer:sync_indexes' to create the index `#{index_name}`"
|
|
@@ -18,18 +18,16 @@ class NoBrainer::QueryRunner::Reconnect < NoBrainer::QueryRunner::Middleware
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
20
|
def reconnect(e, context)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
context[:retries] -= 1
|
|
21
|
+
return false if context[:retries].zero?
|
|
22
|
+
context[:retries] -= 1
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
end
|
|
24
|
+
warn_reconnect(e)
|
|
25
|
+
sleep 1
|
|
26
|
+
NoBrainer.connection.reconnect(:noreply_wait => false)
|
|
27
|
+
true
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
retry if is_connection_error_exception?(e)
|
|
30
|
+
raise
|
|
33
31
|
end
|
|
34
32
|
|
|
35
33
|
def is_connection_error_exception?(e)
|
|
@@ -30,9 +30,6 @@ class NoBrainer::QueryRunner::RunOptions < NoBrainer::QueryRunner::Middleware
|
|
|
30
30
|
|
|
31
31
|
env[:criteria] = env[:options].delete(:criteria)
|
|
32
32
|
|
|
33
|
-
env[:auto_create_tables] = env[:options].delete(:auto_create_tables)
|
|
34
|
-
env[:auto_create_databases] = env[:options].delete(:auto_create_databases)
|
|
35
|
-
|
|
36
33
|
@runner.call(env)
|
|
37
34
|
end
|
|
38
35
|
end
|
|
@@ -10,15 +10,14 @@ class NoBrainer::QueryRunner::TableOnDemand < NoBrainer::QueryRunner::Middleware
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def handle_table_on_demand_exception?(env, e)
|
|
13
|
-
(NoBrainer::Config.auto_create_tables || env[:auto_create_tables]) &&
|
|
14
13
|
e.message =~ /^Table `(.+)\.(.+)` does not exist\.$/ && [$1, $2]
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
private
|
|
18
17
|
|
|
19
18
|
def auto_create_table(env, database_name, table_name)
|
|
20
|
-
model
|
|
21
|
-
model ||= NoBrainer::Document.
|
|
19
|
+
model ||= NoBrainer::Document::Core._all.select { |m| m.table_name == table_name }.first
|
|
20
|
+
model ||= NoBrainer::Document::Core._all_nobrainer.select { |m| m.table_name == table_name }.first
|
|
22
21
|
|
|
23
22
|
if model.nil?
|
|
24
23
|
raise "Auto table creation is not working for `#{database_name}.#{table_name}` -- Can't find the corresponding model."
|
|
@@ -30,7 +29,7 @@ class NoBrainer::QueryRunner::TableOnDemand < NoBrainer::QueryRunner::Middleware
|
|
|
30
29
|
env[:last_auto_create_table] = [database_name, table_name]
|
|
31
30
|
|
|
32
31
|
NoBrainer.with_database(database_name) do
|
|
33
|
-
NoBrainer.table_create(table_name, :primary_key => model.pk_name)
|
|
32
|
+
NoBrainer.table_create(table_name, :primary_key => model.lookup_field_alias(model.pk_name))
|
|
34
33
|
end
|
|
35
34
|
rescue RuntimeError => e
|
|
36
35
|
# We might have raced with another table create
|
|
@@ -19,10 +19,10 @@ namespace :nobrainer do
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
desc 'Equivalent to :sync_indexes_quiet + :seed'
|
|
22
|
-
task :setup => [
|
|
22
|
+
task :setup => [:sync_indexes_quiet, :seed]
|
|
23
23
|
|
|
24
24
|
desc 'Equivalent to :drop + :setup'
|
|
25
|
-
task :reset => [
|
|
25
|
+
task :reset => [:drop, :setup]
|
|
26
26
|
|
|
27
27
|
task :create => :environment do
|
|
28
28
|
# noop
|
data/lib/no_brainer/rql.rb
CHANGED
|
@@ -19,7 +19,7 @@ module NoBrainer::RQL
|
|
|
19
19
|
case rql_query.body.first
|
|
20
20
|
when UPDATE, DELETE, REPLACE, INSERT
|
|
21
21
|
:write
|
|
22
|
-
when DB_CREATE,DB_DROP, DB_LIST, TABLE_CREATE, TABLE_DROP, TABLE_LIST, SYNC,
|
|
22
|
+
when DB_CREATE, DB_DROP, DB_LIST, TABLE_CREATE, TABLE_DROP, TABLE_LIST, SYNC,
|
|
23
23
|
INDEX_CREATE, INDEX_DROP, INDEX_LIST, INDEX_STATUS, INDEX_WAIT
|
|
24
24
|
:management
|
|
25
25
|
else
|
|
@@ -27,8 +27,4 @@ module NoBrainer::RQL
|
|
|
27
27
|
:read
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
|
-
|
|
31
|
-
def is_table?(rql)
|
|
32
|
-
rql.body.first == TABLE
|
|
33
|
-
end
|
|
34
30
|
end
|
data/lib/nobrainer.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'set'
|
|
2
2
|
require 'active_support'
|
|
3
|
+
require 'thread'
|
|
3
4
|
%w(module/delegation module/attribute_accessors module/introspection
|
|
4
5
|
class/attribute object/blank object/inclusion object/deep_dup
|
|
5
6
|
object/try hash/keys hash/indifferent_access hash/reverse_merge
|
|
@@ -12,7 +13,7 @@ module NoBrainer
|
|
|
12
13
|
|
|
13
14
|
# We eager load things that could be loaded when handling the first web request.
|
|
14
15
|
# Code that is loaded through the DSL of NoBrainer should not be eager loaded.
|
|
15
|
-
autoload :Document, :IndexManager, :Loader, :Fork, :
|
|
16
|
+
autoload :Document, :IndexManager, :Loader, :Fork, :Geo, :Lock
|
|
16
17
|
eager_autoload :Config, :Connection, :ConnectionManager, :Error,
|
|
17
18
|
:QueryRunner, :Criteria, :RQL
|
|
18
19
|
|
|
@@ -31,13 +32,8 @@ module NoBrainer
|
|
|
31
32
|
def jruby?
|
|
32
33
|
RUBY_PLATFORM == 'java'
|
|
33
34
|
end
|
|
34
|
-
|
|
35
|
-
def user_caller
|
|
36
|
-
caller.reject { |s| s =~ /\/no_brainer\// }.first
|
|
37
|
-
end
|
|
38
35
|
end
|
|
39
36
|
|
|
40
|
-
DecoratedSymbol.hook
|
|
41
37
|
Fork.hook unless jruby?
|
|
42
38
|
end
|
|
43
39
|
|