nobrainer 0.12.0 → 0.13.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 +1 -1
- data/lib/no_brainer/criteria.rb +1 -2
- data/lib/no_brainer/criteria/core.rb +1 -1
- data/lib/no_brainer/criteria/delete.rb +1 -1
- data/lib/no_brainer/criteria/update.rb +4 -6
- data/lib/no_brainer/document.rb +1 -1
- data/lib/no_brainer/document/association/belongs_to.rb +2 -1
- data/lib/no_brainer/document/attributes.rb +24 -24
- data/lib/no_brainer/document/callbacks.rb +5 -13
- data/lib/no_brainer/document/criteria.rb +0 -1
- data/lib/no_brainer/document/dirty.rb +36 -33
- data/lib/no_brainer/document/id.rb +1 -1
- data/lib/no_brainer/document/index.rb +7 -9
- data/lib/no_brainer/document/injection_layer.rb +2 -3
- data/lib/no_brainer/document/persistance.rb +13 -27
- data/lib/no_brainer/document/readonly.rb +19 -0
- data/lib/no_brainer/document/timestamps.rb +6 -7
- data/lib/no_brainer/document/types.rb +18 -16
- data/lib/no_brainer/document/validation.rb +3 -2
- data/lib/no_brainer/error.rb +12 -1
- data/lib/no_brainer/query_runner/connection.rb +36 -9
- data/lib/no_brainer/query_runner/run_options.rb +4 -2
- data/lib/nobrainer.rb +2 -6
- metadata +37 -30
- data/lib/no_brainer/criteria/inc.rb +0 -14
- data/lib/no_brainer/document_with_timestamps.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd6046706e63442640e96473b737f37ad5a4fea7
|
4
|
+
data.tar.gz: e1634aaf6acfb3c3e5e4287c57151d54cce30ee1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac735666befc4a025536e5beb72a8c4d4d79259875a8ad956ae16e4399c17cf5c2dcc7eecf6bcfa4351d56aec14b1a83a7d68cb751723d5beade516011990915
|
7
|
+
data.tar.gz: b6f69cd4dfbd953d9acc06a1c06aa9a27851baa51b1e1a2d0fce0ff320a32131c49724e5ea6aaa8af3a2aaf74bdb661379eb0a4e97abdb98e8084e5fae0516c5
|
data/lib/no_brainer/autoload.rb
CHANGED
data/lib/no_brainer/criteria.rb
CHANGED
@@ -3,6 +3,5 @@ require 'rethinkdb'
|
|
3
3
|
class NoBrainer::Criteria
|
4
4
|
extend NoBrainer::Autoload
|
5
5
|
autoload_and_include :Core, :Scope, :Raw, :AfterFind, :Where, :OrderBy, :Limit,
|
6
|
-
:Count, :Delete, :Enumerable, :First, :Preload, :
|
7
|
-
:Update, :Cache
|
6
|
+
:Count, :Delete, :Enumerable, :First, :Preload, :Update, :Cache
|
8
7
|
end
|
@@ -1,13 +1,11 @@
|
|
1
1
|
module NoBrainer::Criteria::Update
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
-
def update_all(
|
5
|
-
block
|
6
|
-
run(to_rql.update(&block))['replaced']
|
4
|
+
def update_all(*args, &block)
|
5
|
+
run(to_rql.update(*args, &block))
|
7
6
|
end
|
8
7
|
|
9
|
-
def replace_all(
|
10
|
-
block
|
11
|
-
run(to_rql.replace(&block))['replaced']
|
8
|
+
def replace_all(*args, &block)
|
9
|
+
run(to_rql.replace(*args, &block))
|
12
10
|
end
|
13
11
|
end
|
data/lib/no_brainer/document.rb
CHANGED
@@ -4,7 +4,7 @@ module NoBrainer::Document
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
extend NoBrainer::Autoload
|
6
6
|
|
7
|
-
autoload_and_include :Core, :StoreIn, :InjectionLayer, :Attributes, :Validation, :Types,
|
7
|
+
autoload_and_include :Core, :StoreIn, :InjectionLayer, :Attributes, :Readonly, :Validation, :Types,
|
8
8
|
:Persistance, :Callbacks, :Dirty, :Id, :Association, :Serialization,
|
9
9
|
:Criteria, :Polymorphic, :Index
|
10
10
|
|
@@ -2,7 +2,7 @@ class NoBrainer::Document::Association::BelongsTo
|
|
2
2
|
include NoBrainer::Document::Association::Core
|
3
3
|
|
4
4
|
class Metadata
|
5
|
-
VALID_OPTIONS = [:foreign_key, :class_name, :index, :validates]
|
5
|
+
VALID_OPTIONS = [:foreign_key, :class_name, :index, :validates, :required]
|
6
6
|
include NoBrainer::Document::Association::Core::Metadata
|
7
7
|
extend NoBrainer::Document::Association::EagerLoader::Generic
|
8
8
|
|
@@ -25,6 +25,7 @@ class NoBrainer::Document::Association::BelongsTo
|
|
25
25
|
# are likely to be related to each other. So we don't know the type
|
26
26
|
# of the primary key of the target.
|
27
27
|
owner_klass.field(foreign_key, :index => options[:index])
|
28
|
+
owner_klass.validates(target_name, { :presence => true }) if options[:required]
|
28
29
|
owner_klass.validates(target_name, options[:validates]) if options[:validates]
|
29
30
|
|
30
31
|
delegate("#{foreign_key}=", :assign_foreign_key, :call_super => true)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module NoBrainer::Document::Attributes
|
2
|
-
VALID_FIELD_OPTIONS = [:index, :default, :type, :type_cast_method, :validates]
|
2
|
+
VALID_FIELD_OPTIONS = [:index, :default, :type, :type_cast_method, :validates, :required, :readonly]
|
3
3
|
RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations] + NoBrainer::DecoratedSymbol::MODIFIERS.keys
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
@@ -39,13 +39,13 @@ module NoBrainer::Document::Attributes
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def _assign_attributes(attrs, options={})
|
43
|
-
attrs.each { |k,v| self.write_attribute(k,v) }
|
44
|
-
end
|
45
|
-
|
46
42
|
def assign_attributes(attrs, options={})
|
47
43
|
@_attributes.clear if options[:pristine]
|
48
|
-
|
44
|
+
if options[:from_db]
|
45
|
+
@_attributes.merge!(attrs)
|
46
|
+
else
|
47
|
+
attrs.each { |k,v| self.write_attribute(k,v) }
|
48
|
+
end
|
49
49
|
assign_defaults if options[:pristine]
|
50
50
|
self
|
51
51
|
end
|
@@ -71,33 +71,33 @@ module NoBrainer::Document::Attributes
|
|
71
71
|
subclass.fields = self.fields.dup
|
72
72
|
end
|
73
73
|
|
74
|
-
def
|
75
|
-
|
74
|
+
def _field(attr, options={})
|
75
|
+
# Using a layer so the user can use super when overriding these methods
|
76
|
+
attr = attr.to_s
|
77
|
+
inject_in_layer :attributes do
|
78
|
+
define_method("#{attr}=") { |value| @_attributes[attr] = value }
|
79
|
+
define_method("#{attr}") { @_attributes[attr] }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def field(attr, options={})
|
84
|
+
attr = attr.to_sym
|
76
85
|
|
77
86
|
options.assert_valid_keys(*VALID_FIELD_OPTIONS)
|
78
|
-
if
|
79
|
-
raise "Cannot use a reserved field
|
87
|
+
if attr.in?(RESERVED_FIELD_NAMES)
|
88
|
+
raise "Cannot use a reserved field attr: #{attr}"
|
80
89
|
end
|
81
90
|
|
82
91
|
([self] + descendants).each do |klass|
|
83
|
-
klass.fields[
|
84
|
-
klass.fields[
|
92
|
+
klass.fields[attr] ||= {}
|
93
|
+
klass.fields[attr].deep_merge!(options)
|
85
94
|
end
|
86
95
|
|
87
|
-
|
88
|
-
inject_in_layer :attributes, <<-RUBY, __FILE__, __LINE__ + 1
|
89
|
-
def #{name}=(value)
|
90
|
-
@_attributes['#{name}'] = value
|
91
|
-
end
|
92
|
-
|
93
|
-
def #{name}
|
94
|
-
@_attributes['#{name}']
|
95
|
-
end
|
96
|
-
RUBY
|
96
|
+
_field(attr, self.fields[attr])
|
97
97
|
end
|
98
98
|
|
99
|
-
def has_field?(
|
100
|
-
!!fields[
|
99
|
+
def has_field?(attr)
|
100
|
+
!!fields[attr.to_sym]
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
@@ -7,31 +7,23 @@ module NoBrainer::Document::Callbacks
|
|
7
7
|
define_model_callbacks :find, :only => [:after], :terminator => 'false'
|
8
8
|
end
|
9
9
|
|
10
|
-
def initialize(*args)
|
10
|
+
def initialize(*args, &block)
|
11
11
|
run_callbacks(:initialize) { _initialize(*args); true }
|
12
12
|
end
|
13
13
|
|
14
|
-
def _create(*args)
|
14
|
+
def _create(*args, &block)
|
15
15
|
run_callbacks(:create) { super }
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
18
|
+
def _update_only_changed_attrs(*args, &block)
|
19
19
|
run_callbacks(:update) { super }
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
run_callbacks(:update) { super }
|
24
|
-
end
|
25
|
-
|
26
|
-
def _update_changed(*args)
|
27
|
-
run_callbacks(:update) { super }
|
28
|
-
end
|
29
|
-
|
30
|
-
def save(*args)
|
22
|
+
def save(*args, &block)
|
31
23
|
run_callbacks(:save) { super }
|
32
24
|
end
|
33
25
|
|
34
|
-
def destroy(*args)
|
26
|
+
def destroy(*args, &block)
|
35
27
|
run_callbacks(:destroy) { super }
|
36
28
|
end
|
37
29
|
end
|
@@ -1,25 +1,27 @@
|
|
1
1
|
module NoBrainer::Document::Dirty
|
2
2
|
extend ActiveSupport::Concern
|
3
|
+
# We need to save the changes as seen through read_attribute because
|
4
|
+
# the user sees attributes through the read_attribute getters.
|
5
|
+
# But we want to detect changes based on @_attributes to track
|
6
|
+
# things like undefined -> nil. Going through the getters will
|
7
|
+
# not give us that.
|
3
8
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# Also it doesn't work properly with array and hashes
|
9
|
-
|
10
|
-
included { after_save { clear_dirtiness } }
|
9
|
+
def assign_attributes(attrs, options={})
|
10
|
+
clear_dirtiness if options[:pristine]
|
11
|
+
super
|
12
|
+
end
|
11
13
|
|
12
|
-
def
|
13
|
-
|
14
|
+
def _create(*args)
|
15
|
+
super.tap { clear_dirtiness }
|
14
16
|
end
|
15
17
|
|
16
|
-
def
|
17
|
-
|
18
|
+
def _update(*args)
|
19
|
+
super.tap { clear_dirtiness }
|
18
20
|
end
|
19
21
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
22
|
+
def clear_dirtiness
|
23
|
+
@_old_attributes = {}.with_indifferent_access
|
24
|
+
@_old_attributes_keys = @_attributes.keys # to track undefined -> nil changes
|
23
25
|
end
|
24
26
|
|
25
27
|
def changed?
|
@@ -32,51 +34,52 @@ module NoBrainer::Document::Dirty
|
|
32
34
|
|
33
35
|
def changes
|
34
36
|
result = {}.with_indifferent_access
|
35
|
-
|
37
|
+
@_old_attributes.each do |attr, old_value|
|
36
38
|
current_value = read_attribute(attr)
|
37
|
-
|
39
|
+
if current_value != old_value || !@_old_attributes_keys.include?(attr)
|
40
|
+
result[attr] = [old_value, current_value]
|
41
|
+
end
|
38
42
|
end
|
39
43
|
result
|
40
44
|
end
|
41
45
|
|
42
46
|
def attribute_may_change(attr, current_value)
|
43
|
-
unless
|
44
|
-
|
47
|
+
unless @_old_attributes.has_key?(attr)
|
48
|
+
@_old_attributes[attr] = current_value.deep_dup
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
48
52
|
module ClassMethods
|
49
|
-
def
|
53
|
+
def _field(attr, options={})
|
50
54
|
super
|
55
|
+
attr = attr.to_s
|
51
56
|
|
52
57
|
inject_in_layer :dirty_tracking do
|
53
|
-
define_method("#{
|
54
|
-
if
|
55
|
-
result = [
|
56
|
-
result
|
57
|
-
result
|
58
|
+
define_method("#{attr}_change") do
|
59
|
+
if @_old_attributes.has_key?(attr)
|
60
|
+
result = [@_old_attributes[attr], read_attribute(attr)]
|
61
|
+
result if result.first != result.last || !@_old_attributes_keys.include?(attr)
|
58
62
|
end
|
59
63
|
end
|
60
64
|
|
61
|
-
define_method("#{
|
62
|
-
!!__send__("#{
|
65
|
+
define_method("#{attr}_changed?") do
|
66
|
+
!!__send__("#{attr}_change")
|
63
67
|
end
|
64
68
|
|
65
|
-
define_method("#{
|
66
|
-
|
67
|
-
old_attributes_values[name] : read_attribute(name)
|
69
|
+
define_method("#{attr}_was") do
|
70
|
+
@_old_attributes.has_key?(attr) ? @_old_attributes[attr] : read_attribute(attr)
|
68
71
|
end
|
69
72
|
|
70
|
-
define_method("#{
|
73
|
+
define_method("#{attr}") do
|
71
74
|
super().tap do |value|
|
72
75
|
# This take care of string/arrays/hashes that could change without going
|
73
76
|
# through the setter.
|
74
|
-
attribute_may_change(
|
77
|
+
attribute_may_change(attr, value) if value.respond_to?(:size)
|
75
78
|
end
|
76
79
|
end
|
77
80
|
|
78
|
-
define_method("#{
|
79
|
-
attribute_may_change(
|
81
|
+
define_method("#{attr}=") do |value|
|
82
|
+
attribute_may_change(attr, read_attribute(attr))
|
80
83
|
super(value)
|
81
84
|
end
|
82
85
|
end
|
@@ -6,7 +6,7 @@ module NoBrainer::Document::Id
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
self.field :id, :type => String, :default => ->{ NoBrainer::Document::Id.generate }
|
9
|
+
self.field :id, :type => String, :default => ->{ NoBrainer::Document::Id.generate }, :readonly => true
|
10
10
|
end
|
11
11
|
|
12
12
|
def ==(other)
|
@@ -41,21 +41,19 @@ module NoBrainer::Document::Index
|
|
41
41
|
!!indexes[name.to_sym]
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
if has_index?(name) && indexes[name][:kind] != :single
|
48
|
-
raise "Cannot reuse index name #{name}"
|
44
|
+
def _field(attr, options={})
|
45
|
+
if has_index?(attr) && indexes[attr][:kind] != :single
|
46
|
+
raise "Cannot reuse index attr #{attr}"
|
49
47
|
end
|
50
48
|
|
51
49
|
super
|
52
50
|
|
53
51
|
case options[:index]
|
54
52
|
when nil then
|
55
|
-
when Hash then index(
|
56
|
-
when Symbol then index(
|
57
|
-
when true then index(
|
58
|
-
when false then remove_index(
|
53
|
+
when Hash then index(attr, options[:index])
|
54
|
+
when Symbol then index(attr, options[:index] => true)
|
55
|
+
when true then index(attr)
|
56
|
+
when false then remove_index(attr)
|
59
57
|
end
|
60
58
|
end
|
61
59
|
|
@@ -2,10 +2,9 @@ module NoBrainer::Document::InjectionLayer
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
module ClassMethods
|
5
|
-
def inject_in_layer(name,
|
5
|
+
def inject_in_layer(name, &block)
|
6
6
|
mod = class_eval "module NoBrainerLayer; module #{name.to_s.camelize}; self; end; end"
|
7
|
-
mod.
|
8
|
-
mod.module_exec(&block) if block
|
7
|
+
mod.module_exec(&block)
|
9
8
|
include mod
|
10
9
|
end
|
11
10
|
end
|
@@ -7,8 +7,8 @@ module NoBrainer::Document::Persistance
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def _initialize(attrs={}, options={})
|
10
|
-
super
|
11
10
|
@new_record = !options[:from_db]
|
11
|
+
super
|
12
12
|
end
|
13
13
|
|
14
14
|
def new_record?
|
@@ -38,43 +38,29 @@ module NoBrainer::Document::Persistance
|
|
38
38
|
true
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
42
|
-
|
43
|
-
selector.update_all(&block)
|
44
|
-
true
|
45
|
-
end
|
46
|
-
|
47
|
-
def replace(options={}, &block)
|
48
|
-
return false if options[:validate] && !valid?
|
49
|
-
selector.replace_all(&block)
|
50
|
-
true
|
51
|
-
end
|
52
|
-
|
53
|
-
def _update_changed_attributes(changed_attrs)
|
54
|
-
# If we have a hash to save, we replace the entire document
|
55
|
-
# instead of doing some smart update. This is because RethinkDB
|
56
|
-
# will merge the existing hash with the given hash. If the
|
57
|
-
# user has deleted some keys, we won't remove them.
|
58
|
-
if changed_attrs.values.any? { |v| v.is_a?(Hash) }
|
59
|
-
selector.replace_all { @_attributes }
|
60
|
-
else
|
61
|
-
selector.update_all { changed_attrs }
|
62
|
-
end
|
41
|
+
def _update(attrs)
|
42
|
+
selector.update_all(attrs)
|
63
43
|
end
|
64
44
|
|
65
|
-
def
|
45
|
+
def _update_only_changed_attrs(options={})
|
66
46
|
return false if options[:validate] && !valid?
|
67
47
|
|
68
48
|
# We won't be using the `changes` values, because they went through
|
69
49
|
# read_attribute(), and we want the raw values.
|
70
|
-
|
71
|
-
|
50
|
+
attrs = Hash[self.changed.map do |k|
|
51
|
+
attr = @_attributes[k]
|
52
|
+
# If we have a hash to save, we need to specify r.literal(),
|
53
|
+
# otherwise, the hash would just get merged with the existing one.
|
54
|
+
attr = RethinkDB::RQL.new.literal(attr) if attr.is_a?(Hash)
|
55
|
+
[k, attr]
|
56
|
+
end]
|
57
|
+
_update(attrs) if attrs.present?
|
72
58
|
true
|
73
59
|
end
|
74
60
|
|
75
61
|
def save(options={})
|
76
62
|
options = options.reverse_merge(:validate => true)
|
77
|
-
new_record? ? _create(options) :
|
63
|
+
new_record? ? _create(options) : _update_only_changed_attrs(options)
|
78
64
|
end
|
79
65
|
|
80
66
|
def save!(*args)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module NoBrainer::Document::Readonly
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
def _field(attr, options={})
|
6
|
+
super
|
7
|
+
inject_in_layer :readonly do
|
8
|
+
if options[:readonly]
|
9
|
+
define_method("#{attr}=") do |value|
|
10
|
+
raise NoBrainer::Error::ReadonlyField.new("#{attr} is readonly") unless new_record?
|
11
|
+
super(value)
|
12
|
+
end
|
13
|
+
else
|
14
|
+
remove_method("#{attr}=") if method_defined?("#{attr}=")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -4,16 +4,15 @@ module NoBrainer::Document::Timestamps
|
|
4
4
|
included do
|
5
5
|
field :created_at, :type => Time
|
6
6
|
field :updated_at, :type => Time
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# call if nothing has changed.
|
9
|
+
def _create(options={})
|
10
|
+
self.created_at = self.updated_at = Time.now
|
11
|
+
super
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
14
|
+
def _update(attrs)
|
15
15
|
self.updated_at = Time.now
|
16
|
-
|
17
|
-
super
|
16
|
+
super(attrs.merge('updated_at' => @_attributes['updated_at']))
|
18
17
|
end
|
19
18
|
end
|
@@ -92,41 +92,43 @@ module NoBrainer::Document::Types
|
|
92
92
|
end
|
93
93
|
|
94
94
|
module ClassMethods
|
95
|
-
def cast_value_for(
|
96
|
-
|
97
|
-
field_def = fields[
|
95
|
+
def cast_value_for(attr, value)
|
96
|
+
attr = attr.to_sym
|
97
|
+
field_def = fields[attr]
|
98
98
|
return value unless field_def && field_def[:type]
|
99
99
|
NoBrainer::Document::Types::CastingRules.cast(value, field_def[:type], field_def[:type_cast_method])
|
100
100
|
rescue NoBrainer::Error::InvalidType => error
|
101
101
|
error.type = field_def[:type]
|
102
102
|
error.value = value
|
103
|
-
error.attr_name =
|
103
|
+
error.attr_name = attr
|
104
104
|
raise error
|
105
105
|
end
|
106
106
|
|
107
|
-
def
|
108
|
-
return super unless options.has_key?(:type)
|
109
|
-
|
110
|
-
name = name.to_sym
|
111
|
-
type = options[:type]
|
112
|
-
options[:type_cast_method] = NoBrainer::Document::Types::CastingRules.lookup(type)
|
113
|
-
|
107
|
+
def _field(attr, options={})
|
114
108
|
super
|
115
109
|
|
116
110
|
inject_in_layer :types do
|
117
|
-
define_method("#{
|
111
|
+
define_method("#{attr}=") do |value|
|
118
112
|
begin
|
119
|
-
value = self.class.cast_value_for(
|
120
|
-
@pending_type_errors.try(:delete,
|
113
|
+
value = self.class.cast_value_for(attr, value)
|
114
|
+
@pending_type_errors.try(:delete, attr)
|
121
115
|
rescue NoBrainer::Error::InvalidType => error
|
122
116
|
@pending_type_errors ||= {}
|
123
|
-
@pending_type_errors[
|
117
|
+
@pending_type_errors[attr] = error
|
124
118
|
end
|
125
119
|
super(value)
|
126
120
|
end
|
127
121
|
|
128
|
-
define_method("#{
|
122
|
+
define_method("#{attr}?") { !!read_attribute(attr) } if options[:type] == Boolean
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def field(attr, options={})
|
127
|
+
if options[:type]
|
128
|
+
type_cast_method = NoBrainer::Document::Types::CastingRules.lookup(options[:type])
|
129
|
+
options = options.merge(:type_cast_method => type_cast_method)
|
129
130
|
end
|
131
|
+
super
|
130
132
|
end
|
131
133
|
end
|
132
134
|
end
|
@@ -8,9 +8,10 @@ module NoBrainer::Document::Validation
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module ClassMethods
|
11
|
-
def
|
11
|
+
def _field(attr, options={})
|
12
12
|
super
|
13
|
-
validates(
|
13
|
+
validates(attr, { :presence => true }) if options[:required]
|
14
|
+
validates(attr, options[:validates]) if options[:validates]
|
14
15
|
end
|
15
16
|
|
16
17
|
def validates_uniqueness_of(*attr_names)
|
data/lib/no_brainer/error.rb
CHANGED
@@ -1,13 +1,24 @@
|
|
1
1
|
module NoBrainer::Error
|
2
2
|
class Connection < RuntimeError; end
|
3
3
|
class DocumentNotFound < RuntimeError; end
|
4
|
-
class DocumentInvalid < RuntimeError; end
|
5
4
|
class DocumentNotSaved < RuntimeError; end
|
6
5
|
class ChildrenExist < RuntimeError; end
|
7
6
|
class CannotUseIndex < RuntimeError; end
|
8
7
|
class MissingIndex < RuntimeError; end
|
9
8
|
class InvalidType < RuntimeError; end
|
10
9
|
class AssociationNotSaved < RuntimeError; end
|
10
|
+
class ReadonlyField < RuntimeError; end
|
11
|
+
|
12
|
+
class DocumentInvalid < RuntimeError
|
13
|
+
attr_accessor :instance
|
14
|
+
def initialize(instance)
|
15
|
+
@instance = instance
|
16
|
+
end
|
17
|
+
|
18
|
+
def message
|
19
|
+
"#{instance} is invalid: #{instance.errors.full_messages.join(", ")}"
|
20
|
+
end
|
21
|
+
end
|
11
22
|
|
12
23
|
class InvalidType < RuntimeError
|
13
24
|
attr_accessor :attr_name, :value, :type
|
@@ -1,17 +1,44 @@
|
|
1
1
|
class NoBrainer::QueryRunner::Connection < NoBrainer::QueryRunner::Middleware
|
2
2
|
def call(env)
|
3
3
|
@runner.call(env)
|
4
|
-
rescue
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
rescue StandardError => e
|
5
|
+
# TODO test that thing
|
6
|
+
if is_connection_error_exception?(e)
|
7
|
+
retry if reconnect
|
8
|
+
end
|
9
|
+
raise
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
8
13
|
|
9
|
-
|
10
|
-
|
14
|
+
def reconnect
|
15
|
+
# FIXME thread safety? perhaps we need to use a connection pool
|
16
|
+
# XXX Possibly dangerous, as we could reexecute a non idempotent operation
|
17
|
+
# Check the semantics of the db
|
18
|
+
NoBrainer::Config.max_reconnection_tries.times do
|
19
|
+
begin
|
20
|
+
NoBrainer.logger.try(:warn, "Lost connection to #{NoBrainer::Config.rethinkdb_url}, retrying...")
|
21
|
+
sleep 1
|
22
|
+
NoBrainer.connection.reconnect(:noreply_wait => false)
|
23
|
+
return true
|
24
|
+
rescue StandardError => e
|
25
|
+
retry if is_connection_error_exception?(e)
|
26
|
+
raise
|
27
|
+
end
|
28
|
+
end
|
29
|
+
false
|
30
|
+
end
|
11
31
|
|
12
|
-
|
13
|
-
|
32
|
+
def is_connection_error_exception?(e)
|
33
|
+
case e
|
34
|
+
when Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::EPIPE,
|
35
|
+
Errno::ECONNRESET, Errno::ETIMEDOUT, IOError
|
36
|
+
true
|
37
|
+
when RethinkDB::RqlRuntimeError
|
38
|
+
e.message =~ /cannot perform (read|write): No master available/ ||
|
39
|
+
e.message =~ /Error: Connection Closed/
|
40
|
+
else
|
41
|
+
false
|
14
42
|
end
|
15
|
-
raise e
|
16
43
|
end
|
17
44
|
end
|
@@ -16,11 +16,11 @@ class NoBrainer::QueryRunner::RunOptions < NoBrainer::QueryRunner::Middleware
|
|
16
16
|
def call(env)
|
17
17
|
env[:options].symbolize_keys!
|
18
18
|
if Thread.current[:nobrainer_options]
|
19
|
-
env[:options]
|
19
|
+
env[:options].reverse_merge!(Thread.current[:nobrainer_options])
|
20
20
|
end
|
21
21
|
|
22
22
|
if NoBrainer::Config.durability.to_s != 'hard'
|
23
|
-
env[:options]
|
23
|
+
env[:options].reverse_merge!(:durability => NoBrainer::Config.durability)
|
24
24
|
end
|
25
25
|
|
26
26
|
if env[:options][:db] && !env[:options][:db].is_a?(RethinkDB::RQL)
|
@@ -28,6 +28,8 @@ class NoBrainer::QueryRunner::RunOptions < NoBrainer::QueryRunner::Middleware
|
|
28
28
|
env[:options][:db] = RethinkDB::RQL.new.db(env[:db_name])
|
29
29
|
end
|
30
30
|
|
31
|
+
env[:criteria] = env[:options].delete(:criteria)
|
32
|
+
|
31
33
|
@runner.call(env)
|
32
34
|
end
|
33
35
|
end
|
data/lib/nobrainer.rb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('1.9')
|
2
|
-
raise 'Please use Ruby 1.9 or later'
|
3
|
-
end
|
4
|
-
|
5
1
|
require 'active_support'
|
6
2
|
%w(module/delegation module/attribute_accessors class/attribute object/blank object/inclusion object/deep_dup
|
7
3
|
object/try hash/keys hash/indifferent_access hash/reverse_merge hash/deep_merge array/extract_options)
|
@@ -12,7 +8,7 @@ module NoBrainer
|
|
12
8
|
extend NoBrainer::Autoload
|
13
9
|
|
14
10
|
# We eager load things that could be loaded for the first time during the web request
|
15
|
-
autoload :Document, :
|
11
|
+
autoload :Document, :IndexManager, :Loader, :Fork, :DecoratedSymbol
|
16
12
|
eager_autoload :Config, :Connection, :Error, :QueryRunner, :Criteria, :Util
|
17
13
|
|
18
14
|
class << self
|
@@ -28,7 +24,7 @@ module NoBrainer
|
|
28
24
|
end
|
29
25
|
|
30
26
|
def disconnect
|
31
|
-
@connection.try(:disconnect)
|
27
|
+
@connection.try(:disconnect, :noreply_wait => true)
|
32
28
|
@connection = nil
|
33
29
|
end
|
34
30
|
|
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.13.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: 2014-01-
|
11
|
+
date: 2014-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rethinkdb
|
@@ -25,25 +25,33 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.11.0.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
- - <
|
35
|
-
- !ruby/object:Gem::Version
|
36
|
-
version: '5'
|
33
|
+
version: 4.0.0
|
37
34
|
type: :runtime
|
38
35
|
prerelease: false
|
39
36
|
version_requirements: !ruby/object:Gem::Requirement
|
40
37
|
requirements:
|
41
38
|
- - '>='
|
42
39
|
- !ruby/object:Gem::Version
|
43
|
-
version:
|
44
|
-
|
40
|
+
version: 4.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activemodel
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 4.0.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
45
53
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
54
|
+
version: 4.0.0
|
47
55
|
- !ruby/object:Gem::Dependency
|
48
56
|
name: middleware
|
49
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,33 +78,34 @@ files:
|
|
70
78
|
- lib/no_brainer/document/association/has_many_through.rb
|
71
79
|
- lib/no_brainer/document/association/has_many.rb
|
72
80
|
- lib/no_brainer/document/association/eager_loader.rb
|
73
|
-
- lib/no_brainer/document/association/belongs_to.rb
|
74
81
|
- lib/no_brainer/document/association/core.rb
|
82
|
+
- lib/no_brainer/document/association/belongs_to.rb
|
75
83
|
- lib/no_brainer/document/polymorphic.rb
|
76
84
|
- lib/no_brainer/document/store_in.rb
|
77
|
-
- lib/no_brainer/document/injection_layer.rb
|
78
85
|
- lib/no_brainer/document/core.rb
|
79
|
-
- lib/no_brainer/document/criteria.rb
|
80
|
-
- lib/no_brainer/document/dynamic_attributes.rb
|
81
86
|
- lib/no_brainer/document/serialization.rb
|
82
87
|
- lib/no_brainer/document/association.rb
|
83
|
-
- lib/no_brainer/document/index.rb
|
84
|
-
- lib/no_brainer/document/validation.rb
|
85
88
|
- lib/no_brainer/document/callbacks.rb
|
86
|
-
- lib/no_brainer/document/
|
87
|
-
- lib/no_brainer/document/persistance.rb
|
89
|
+
- lib/no_brainer/document/dynamic_attributes.rb
|
88
90
|
- lib/no_brainer/document/timestamps.rb
|
91
|
+
- lib/no_brainer/document/dirty.rb
|
92
|
+
- lib/no_brainer/document/index.rb
|
93
|
+
- lib/no_brainer/document/validation.rb
|
89
94
|
- lib/no_brainer/document/attributes.rb
|
90
95
|
- lib/no_brainer/document/id.rb
|
96
|
+
- lib/no_brainer/document/injection_layer.rb
|
97
|
+
- lib/no_brainer/document/persistance.rb
|
98
|
+
- lib/no_brainer/document/readonly.rb
|
91
99
|
- lib/no_brainer/document/types.rb
|
92
|
-
- lib/no_brainer/
|
100
|
+
- lib/no_brainer/document/criteria.rb
|
93
101
|
- lib/no_brainer/query_runner/database_on_demand.rb
|
94
102
|
- lib/no_brainer/query_runner/table_on_demand.rb
|
95
103
|
- lib/no_brainer/query_runner/write_error.rb
|
96
104
|
- lib/no_brainer/query_runner/driver.rb
|
97
|
-
- lib/no_brainer/query_runner/run_options.rb
|
98
105
|
- lib/no_brainer/query_runner/logger.rb
|
99
106
|
- lib/no_brainer/query_runner/missing_index.rb
|
107
|
+
- lib/no_brainer/query_runner/run_options.rb
|
108
|
+
- lib/no_brainer/query_runner/connection.rb
|
100
109
|
- lib/no_brainer/railtie/database.rake
|
101
110
|
- lib/no_brainer/index_manager.rb
|
102
111
|
- lib/no_brainer/loader.rb
|
@@ -104,30 +113,28 @@ files:
|
|
104
113
|
- lib/no_brainer/fork.rb
|
105
114
|
- lib/no_brainer/connection.rb
|
106
115
|
- lib/no_brainer/query_runner.rb
|
107
|
-
- lib/no_brainer/config.rb
|
108
|
-
- lib/no_brainer/autoload.rb
|
109
116
|
- lib/no_brainer/util.rb
|
110
|
-
- lib/no_brainer/document.rb
|
111
|
-
- lib/no_brainer/document_with_timestamps.rb
|
112
117
|
- lib/no_brainer/decorated_symbol.rb
|
113
|
-
- lib/no_brainer/criteria/update.rb
|
114
118
|
- lib/no_brainer/criteria/scope.rb
|
115
119
|
- lib/no_brainer/criteria/raw.rb
|
116
120
|
- lib/no_brainer/criteria/preload.rb
|
117
121
|
- lib/no_brainer/criteria/order_by.rb
|
118
122
|
- lib/no_brainer/criteria/limit.rb
|
119
|
-
- lib/no_brainer/criteria/inc.rb
|
120
123
|
- lib/no_brainer/criteria/first.rb
|
121
124
|
- lib/no_brainer/criteria/enumerable.rb
|
122
|
-
- lib/no_brainer/criteria/delete.rb
|
123
125
|
- lib/no_brainer/criteria/count.rb
|
124
|
-
- lib/no_brainer/criteria/core.rb
|
125
126
|
- lib/no_brainer/criteria/cache.rb
|
126
127
|
- lib/no_brainer/criteria/after_find.rb
|
127
128
|
- lib/no_brainer/criteria/where.rb
|
129
|
+
- lib/no_brainer/criteria/update.rb
|
130
|
+
- lib/no_brainer/criteria/delete.rb
|
131
|
+
- lib/no_brainer/criteria/core.rb
|
132
|
+
- lib/no_brainer/railtie.rb
|
133
|
+
- lib/no_brainer/config.rb
|
134
|
+
- lib/no_brainer/document.rb
|
128
135
|
- lib/no_brainer/criteria.rb
|
129
136
|
- lib/no_brainer/error.rb
|
130
|
-
- lib/no_brainer/
|
137
|
+
- lib/no_brainer/autoload.rb
|
131
138
|
- lib/rails/generators/nobrainer.rb
|
132
139
|
- lib/rails/generators/nobrainer/model/model_generator.rb
|
133
140
|
- lib/rails/generators/nobrainer/model/templates/model.rb.tt
|
@@ -146,7 +153,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
146
153
|
requirements:
|
147
154
|
- - '>='
|
148
155
|
- !ruby/object:Gem::Version
|
149
|
-
version:
|
156
|
+
version: 1.9.0
|
150
157
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
158
|
requirements:
|
152
159
|
- - '>='
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module NoBrainer::Criteria::Inc
|
2
|
-
extend ActiveSupport::Concern
|
3
|
-
|
4
|
-
def inc_all(field, value=1)
|
5
|
-
# TODO The useful inc() is on a model instance.
|
6
|
-
# But then do we want to postpone the inc() to the next save?
|
7
|
-
# It might make sense (because we don't have transactions).
|
8
|
-
update_all { |doc| { field => doc[field] + value } }
|
9
|
-
end
|
10
|
-
|
11
|
-
def dec_all(field, value=1)
|
12
|
-
inc_all(field, -value)
|
13
|
-
end
|
14
|
-
end
|