nobrainer 0.28.0 → 0.29.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/no_brainer/autoload.rb +7 -1
  3. data/lib/no_brainer/config.rb +12 -2
  4. data/lib/no_brainer/connection.rb +2 -15
  5. data/lib/no_brainer/connection_manager.rb +4 -3
  6. data/lib/no_brainer/criteria/enumerable.rb +10 -1
  7. data/lib/no_brainer/criteria/first_or_create.rb +24 -7
  8. data/lib/no_brainer/document/aliases.rb +2 -2
  9. data/lib/no_brainer/document/association.rb +3 -0
  10. data/lib/no_brainer/document/attributes.rb +14 -23
  11. data/lib/no_brainer/document/dirty.rb +2 -2
  12. data/lib/no_brainer/document/index.rb +9 -4
  13. data/lib/no_brainer/document/lazy_fetch.rb +13 -13
  14. data/lib/no_brainer/document/primary_key.rb +17 -29
  15. data/lib/no_brainer/document/readonly.rb +10 -6
  16. data/lib/no_brainer/document/table_config.rb +17 -3
  17. data/lib/no_brainer/document/types.rb +19 -12
  18. data/lib/no_brainer/document/types/boolean.rb +12 -0
  19. data/lib/no_brainer/document/types/enum.rb +63 -0
  20. data/lib/no_brainer/document/types/time.rb +2 -1
  21. data/lib/no_brainer/document/validation/core.rb +1 -1
  22. data/lib/no_brainer/query_runner.rb +35 -17
  23. data/lib/no_brainer/query_runner/database_on_demand.rb +4 -1
  24. data/lib/no_brainer/query_runner/em_driver.rb +128 -0
  25. data/lib/no_brainer/query_runner/missing_index.rb +3 -5
  26. data/lib/no_brainer/query_runner/reconnect.rb +2 -49
  27. data/lib/no_brainer/query_runner/table_on_demand.rb +2 -1
  28. data/lib/no_brainer/railtie/database.rake +10 -13
  29. data/lib/no_brainer/rql.rb +9 -1
  30. data/lib/nobrainer.rb +11 -3
  31. data/lib/rails/generators/templates/nobrainer.rb +4 -8
  32. metadata +6 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8406e0c14de6dc8379b7047aa6d7254675e1b1fb
4
- data.tar.gz: 0d9906ef55e4f74e5317a6d2377d4c1f19ac9799
3
+ metadata.gz: 9fbf539d3fd47eeec38c4ae88843297a7462efb5
4
+ data.tar.gz: e02bde6490ffc0eb21bf3fde637dc6a4dea0350d
5
5
  SHA512:
6
- metadata.gz: fdcbf5021c15bad13546e7b346eb73fb7f6d0b62ab861578110b0ab81aafda8c364a4b6fe9b282d0f8f8c6d732ebd8dcc3f5be9aada980b79d34d93b0fed6fea
7
- data.tar.gz: afa546845837919bb06b01c7ba4168626b74263244986d4d9d3a7428b9b68b24faa968572f110546fb5afb9c4b57dbb236d5c48556b9ae691d271deea62ff816
6
+ metadata.gz: 586f87afabedec2bc9716949298255c6456b7cbf9846a6b9e3090fe009cf481d68fd3eb852368a2658f1e4b59e432388f690cd2fce24606460229dbad1daf633
7
+ data.tar.gz: d55889b76d3a1e6dd48338fca51eb1ad636baa00bc20c21a992cf8e5e6d02a4f958f535dc95398a89c3695ceab87cf5ededbd915ac3b0f07f041dda16148e913
@@ -13,8 +13,14 @@ module NoBrainer::Autoload
13
13
  super() { autoload(*constants) }
14
14
  end
15
15
 
16
+ def eager_load!
17
+ super
18
+ @_autoloads.keys.map { |c| const_get(c) }
19
+ .each { |c| c.eager_load! if c.respond_to?(:eager_load!) }
20
+ end
21
+
16
22
  def autoload_and_include(*constants)
17
- autoload(*constants)
23
+ eager_autoload(*constants)
18
24
  constants.each { |constant| include const_get(constant) }
19
25
  end
20
26
  end
@@ -6,10 +6,10 @@ module NoBrainer::Config
6
6
  :environment => { :default => ->{ default_environment } },
7
7
  :rethinkdb_urls => { :default => ->{ [default_rethinkdb_url] } },
8
8
  :ssl_options => { :default => ->{ nil } },
9
+ :driver => { :default => ->{ :regular }, :valid_values => [:regular, :em] },
9
10
  :logger => { :default => ->{ default_logger } },
10
11
  :colorize_logger => { :default => ->{ true }, :valid_values => [true, false] },
11
12
  :warn_on_active_record => { :default => ->{ true }, :valid_values => [true, false] },
12
- :max_retries_on_connection_failure => { :default => ->{ default_max_retries_on_connection_failure } },
13
13
  :durability => { :default => ->{ default_durability }, :valid_values => [:hard, :soft] },
14
14
  :table_options => { :default => ->{ {:shards => 1, :replicas => 1, :write_acks => :majority} },
15
15
  :valid_keys => [:shards, :replicas, :primary_replica_tag, :write_acks, :durability] },
@@ -37,6 +37,11 @@ module NoBrainer::Config
37
37
  STDERR.puts "[NoBrainer] The current behavior is now to always auto create tables"
38
38
  end
39
39
 
40
+ def max_retries_on_connection_failure=(value)
41
+ STDERR.puts "[NoBrainer] config.max_retries_on_connection_failure has been removed."
42
+ STDERR.puts "[NoBrainer] Queries are no longer retried upon failures"
43
+ end
44
+
40
45
  def apply_defaults
41
46
  @applied_defaults_for = SETTINGS.keys.reject { |k| instance_variable_defined?("@#{k}") }
42
47
  @applied_defaults_for.each { |k| __send__("#{k}=", SETTINGS[k][:default].call) }
@@ -53,6 +58,10 @@ module NoBrainer::Config
53
58
  end
54
59
 
55
60
  validate_urls
61
+
62
+ if driver == :em && per_thread_connection
63
+ raise "To use EventMachine, disable per_thread_connection"
64
+ end
56
65
  end
57
66
 
58
67
  def reset!
@@ -119,7 +128,7 @@ module NoBrainer::Config
119
128
  def validate_urls
120
129
  # This is not connecting, just validating the format.
121
130
  dbs = rethinkdb_urls.compact.map { |url| NoBrainer::Connection.new(url).parsed_uri[:db] }.uniq
122
- raise "Please specify at least one rethinkdb_url" if dbs.size == 0
131
+ raise "Please specify the app_name and the environment, or a rethinkdb_url" if dbs.size == 0
123
132
  raise "All the rethinkdb_urls must specify the same db name (instead of #{dbs.inspect})" if dbs.size != 1
124
133
  end
125
134
 
@@ -132,6 +141,7 @@ module NoBrainer::Config
132
141
  end
133
142
 
134
143
  def default_max_retries_on_connection_failure
144
+ # TODO remove
135
145
  dev_mode? ? 1 : 15
136
146
  end
137
147
 
@@ -2,9 +2,10 @@ require 'rethinkdb'
2
2
  require 'uri'
3
3
 
4
4
  class NoBrainer::Connection
5
- attr_accessor :parsed_uri
5
+ attr_accessor :parsed_uri, :orig_uri
6
6
 
7
7
  def initialize(uri)
8
+ @orig_uri = uri
8
9
  parse_uri(uri)
9
10
  end
10
11
 
@@ -46,18 +47,4 @@ class NoBrainer::Connection
46
47
  def current_db
47
48
  NoBrainer.current_run_options.try(:[], :db) || default_db
48
49
  end
49
-
50
- def drop!
51
- NoBrainer.run { |r| r.db_drop(current_db) }
52
- end
53
-
54
- # Note that truncating each table (purge!) is much faster than dropping the database (drop!)
55
- def purge!
56
- NoBrainer.run { |r| r.table_list }.each do |table_name|
57
- # keeping the index meta store because indexes are not going away when purging
58
- next if table_name == NoBrainer::Document::Index::MetaStore.table_name
59
- NoBrainer.run { |r| r.table(table_name).delete }
60
- end
61
- true
62
- end
63
50
  end
@@ -62,11 +62,12 @@ module NoBrainer::ConnectionManager
62
62
  end
63
63
 
64
64
  def _disconnect
65
- self.current_connection.try(:close, :noreply_wait => false) rescue nil
66
- self.current_connection = nil
65
+ c, self.current_connection = self.current_connection, nil
66
+ c.try(:close, :noreply_wait => false) rescue nil
67
67
  end
68
68
 
69
69
  def disconnect
70
+ return unless self.current_connection
70
71
  synchronize { _disconnect }
71
72
  end
72
73
 
@@ -74,7 +75,7 @@ module NoBrainer::ConnectionManager
74
75
  synchronize do
75
76
  @urls = nil
76
77
  c = current_connection
77
- _disconnect if c && !NoBrainer::Config.rethinkdb_urls.include?(c.uri)
78
+ _disconnect if c && !NoBrainer::Config.rethinkdb_urls.include?(c.orig_uri)
78
79
  end
79
80
  end
80
81
  end
@@ -3,7 +3,16 @@ module NoBrainer::Criteria::Enumerable
3
3
 
4
4
  def each(options={}, &block)
5
5
  return enum_for(:each, options) unless block
6
- run.each { |attrs| block.call(instantiate_doc(attrs)) }
6
+ run.tap { |cursor| @cursor = cursor }.each do |attrs|
7
+ return close if @close_cursor
8
+ block.call(instantiate_doc(attrs))
9
+ end
10
+ self
11
+ end
12
+
13
+ def close
14
+ @close_cursor = true
15
+ @cursor.close if NoBrainer::Config.driver == :em
7
16
  self
8
17
  end
9
18
 
@@ -10,11 +10,11 @@ module NoBrainer::Criteria::FirstOrCreate
10
10
  end
11
11
 
12
12
  def upsert(attrs, save_options={})
13
- _upsert(attrs, save_options.merge(:save_method => :save?))
13
+ _upsert(attrs, save_options.merge(:save_method => :save?, :update => true))
14
14
  end
15
15
 
16
16
  def upsert!(attrs, save_options={})
17
- _upsert(attrs, save_options.merge(:save_method => :save!))
17
+ _upsert(attrs, save_options.merge(:save_method => :save!, :update => true))
18
18
  end
19
19
 
20
20
  private
@@ -22,15 +22,27 @@ module NoBrainer::Criteria::FirstOrCreate
22
22
  def _upsert(attrs, save_options)
23
23
  attrs = attrs.symbolize_keys
24
24
  unique_keys = get_model_unique_fields.detect { |keys| keys & attrs.keys == keys }
25
- raise "Could not find a uniqueness validator within `#{attrs.keys.inspect}'.\n" +
26
- "Please add a corresponding uniqueness validator" unless unique_keys
27
- where(attrs.slice(*unique_keys)).__send__(:_first_or_create, attrs, save_options)
25
+ return where(attrs.slice(*unique_keys)).__send__(:_first_or_create, attrs, save_options) if unique_keys
26
+
27
+ # We can't do an upsert :( Let see if we can fail on a validator first...
28
+ instance = model.new(attrs)
29
+ unless instance.valid?
30
+ case save_options[:save_method]
31
+ when :save! then raise NoBrainer::Error::DocumentInvalid, instance
32
+ when :save? then return instance
33
+ end
34
+ end
35
+
36
+ raise "Could not find a uniqueness validator for the following keys: `#{attrs.keys.inspect}'."
28
37
  end
29
38
 
30
39
  def _first_or_create(create_params, save_options, &block)
31
40
  raise "Cannot use .raw() with .first_or_create()" if raw?
32
41
  raise "Use first_or_create() on the root class `#{model.root_class}'" unless model.is_root_class?
33
42
 
43
+ save_method = save_options.delete(:save_method)
44
+ should_update = save_options.delete(:update)
45
+
34
46
  where_params = extract_where_params()
35
47
 
36
48
  # Note that we are not matching a subset of the keys on the uniqueness
@@ -58,7 +70,13 @@ module NoBrainer::Criteria::FirstOrCreate
58
70
  new_instance._lock_for_uniqueness_once(lock_key_name)
59
71
 
60
72
  old_instance = self.first
61
- return old_instance if old_instance
73
+ if old_instance
74
+ if should_update
75
+ old_instance.assign_attributes(create_params)
76
+ old_instance.__send__(save_method, save_options)
77
+ end
78
+ return old_instance
79
+ end
62
80
 
63
81
  create_params = block.call if block
64
82
  create_params = create_params.symbolize_keys
@@ -80,7 +98,6 @@ module NoBrainer::Criteria::FirstOrCreate
80
98
  end
81
99
 
82
100
  new_instance.assign_attributes(create_params)
83
- save_method = save_options.delete(:save_method)
84
101
  new_instance.__send__(save_method, save_options)
85
102
  return new_instance
86
103
  ensure
@@ -11,7 +11,7 @@ module NoBrainer::Document::Aliases
11
11
  end
12
12
 
13
13
  module ClassMethods
14
- def _field(attr, options={})
14
+ def field(attr, options={})
15
15
  if options[:store_as]
16
16
  self.alias_map[attr.to_s] = options[:store_as].to_s
17
17
  self.alias_reverse_map[options[:store_as].to_s] = attr.to_s
@@ -19,7 +19,7 @@ module NoBrainer::Document::Aliases
19
19
  super
20
20
  end
21
21
 
22
- def _remove_field(attr, options={})
22
+ def remove_field(attr, options={})
23
23
  super
24
24
 
25
25
  self.alias_map.delete(attr.to_s)
@@ -1,6 +1,7 @@
1
1
  module NoBrainer::Document::Association
2
2
  extend NoBrainer::Autoload
3
3
  autoload :Core, :BelongsTo, :HasMany, :HasManyThrough, :HasOne, :HasOneThrough, :EagerLoader
4
+ eager_autoload :EagerLoader
4
5
  METHODS = [:belongs_to, :has_many, :has_one]
5
6
 
6
7
  extend ActiveSupport::Concern
@@ -24,6 +25,8 @@ module NoBrainer::Document::Association
24
25
  define_method(association) do |target, options={}|
25
26
  target = target.to_sym
26
27
 
28
+ options[:class_name] = options.delete(:class) if options[:class]
29
+
27
30
  if r = self.association_metadata[target]
28
31
  raise "Cannot change the :through option" unless r.options[:through] == options[:through]
29
32
  r.options.merge!(options)
@@ -1,6 +1,8 @@
1
1
  module NoBrainer::Document::Attributes
2
- VALID_FIELD_OPTIONS = [:index, :default, :type, :readonly, :primary_key, :lazy_fetch, :store_as,
3
- :validates, :required, :unique, :uniq, :format, :in, :length, :min_length, :max_length]
2
+ VALID_FIELD_OPTIONS = [:index, :default, :type, :readonly, :primary_key,
3
+ :lazy_fetch, :store_as, :validates, :required, :unique,
4
+ :uniq, :format, :in, :length, :min_length, :max_length,
5
+ :prefix, :suffix]
4
6
  RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations, :pk_value] +
5
7
  NoBrainer::SymbolDecoration::OPERATORS
6
8
 
@@ -115,43 +117,32 @@ module NoBrainer::Document::Attributes
115
117
  super
116
118
  end
117
119
 
118
- # The different between _field and field is that field can set other options
119
- # (c.f. primary key module). _field always receive an immutable options list.
120
- def _field(attr, options={})
120
+ def field(attr, options={})
121
121
  options.assert_valid_keys(*VALID_FIELD_OPTIONS)
122
+ unless attr.is_a?(Symbol)
123
+ raise "The field `#{attr}' must be declared with a Symbol" # we're just being lazy here...
124
+ end
122
125
  if attr.in?(RESERVED_FIELD_NAMES)
123
126
  raise "The field name `:#{attr}' is reserved. Please use another one."
124
127
  end
125
128
 
126
- attr = attr.to_s
127
- inject_in_layer :attributes do
128
- define_method("#{attr}=") { |value| _write_attribute(attr, value) }
129
- define_method("#{attr}") { _read_attribute(attr) }
130
- end
131
- end
132
-
133
- def field(attr, options={})
134
- attr = attr.to_sym
135
-
136
129
  subclass_tree.each do |subclass|
137
130
  subclass.fields[attr] ||= {}
138
131
  subclass.fields[attr].deep_merge!(options)
139
132
  end
140
133
 
141
- _field(attr, self.fields[attr])
134
+ attr = attr.to_s
135
+ inject_in_layer :attributes do
136
+ define_method("#{attr}=") { |value| _write_attribute(attr, value) }
137
+ define_method("#{attr}") { _read_attribute(attr) }
138
+ end
142
139
  end
143
140
 
144
- def _remove_field(attr, options={})
141
+ def remove_field(attr, options={})
145
142
  inject_in_layer :attributes do
146
143
  remove_method("#{attr}=")
147
144
  remove_method("#{attr}")
148
145
  end
149
- end
150
-
151
- def remove_field(attr, options={})
152
- attr = attr.to_sym
153
-
154
- _remove_field(attr, options)
155
146
 
156
147
  subclass_tree.each do |subclass|
157
148
  subclass.fields.delete(attr)
@@ -75,7 +75,7 @@ module NoBrainer::Document::Dirty
75
75
  end
76
76
 
77
77
  module ClassMethods
78
- def _field(attr, options={})
78
+ def field(attr, options={})
79
79
  super
80
80
  attr = attr.to_s
81
81
 
@@ -97,7 +97,7 @@ module NoBrainer::Document::Dirty
97
97
  end
98
98
  end
99
99
 
100
- def _remove_field(attr, options={})
100
+ def remove_field(attr, options={})
101
101
  super
102
102
  inject_in_layer :dirty_tracking do
103
103
  remove_method("#{attr}_change")
@@ -12,7 +12,12 @@ module NoBrainer::Document::Index
12
12
 
13
13
  module ClassMethods
14
14
  def index(name, *args)
15
- name = name.to_sym
15
+ name = case name
16
+ when String then name.to_sym
17
+ when Symbol then name
18
+ else raise ArgumentError, "Incorrect index specification"
19
+ end
20
+
16
21
  options = args.extract_options!
17
22
  options.assert_valid_keys(*VALID_INDEX_OPTIONS)
18
23
 
@@ -54,14 +59,14 @@ module NoBrainer::Document::Index
54
59
  !!indexes[name.to_sym]
55
60
  end
56
61
 
57
- def _field(attr, options={})
62
+ def field(attr, options={})
58
63
  if has_index?(attr) && indexes[attr].kind != :single
59
64
  raise "The index `#{attr}' is already declared. Please remove its definition first."
60
65
  end
61
66
 
62
67
  super
63
68
 
64
- store_as = {:store_as => options[:store_as]}
69
+ store_as = {:store_as => fields[attr][:store_as]}
65
70
  case options[:index]
66
71
  when nil then
67
72
  when Hash then index(attr, store_as.merge(options[:index]))
@@ -71,7 +76,7 @@ module NoBrainer::Document::Index
71
76
  end
72
77
  end
73
78
 
74
- def _remove_field(attr, options={})
79
+ def remove_field(attr, options={})
75
80
  remove_index(attr) if fields[attr][:index]
76
81
  super
77
82
  end
@@ -28,18 +28,18 @@ module NoBrainer::Document::LazyFetch
28
28
  super
29
29
  end
30
30
 
31
- def _field(attr, options={})
31
+ def field(attr, options={})
32
32
  super
33
33
  attr = attr.to_s
34
- model = self
35
- inject_in_layer :lazy_fetch do
36
- if options[:lazy_fetch]
37
- model.subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch << attr }
38
- else
39
- model.subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch.delete(attr) }
40
- end
41
34
 
42
- # Lazy loading can also specified through criteria.
35
+ case options[:lazy_fetch]
36
+ when true then subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch << attr }
37
+ when false then subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch.delete(attr) }
38
+ end
39
+
40
+ inject_in_layer :lazy_fetch do
41
+ # Lazy loading can also specified through criteria, we have to define
42
+ # this method regardless of the provided options.
43
43
  define_method("#{attr}") do
44
44
  return super() unless @lazy_fetch
45
45
 
@@ -55,12 +55,12 @@ module NoBrainer::Document::LazyFetch
55
55
  end
56
56
  end
57
57
 
58
- def _remove_field(attr, options={})
59
- super
60
- subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch.delete(attr) }
58
+ def remove_field(attr, options={})
59
+ subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch.delete(attr.to_s) }
61
60
  inject_in_layer :lazy_fetch do
62
- remove_method("#{attr}") if method_defined?("#{attr}")
61
+ remove_method("#{attr}")
63
62
  end
63
+ super
64
64
  end
65
65
 
66
66
  def all
@@ -1,6 +1,6 @@
1
1
  module NoBrainer::Document::PrimaryKey
2
2
  extend NoBrainer::Autoload
3
- autoload :Generator
3
+ eager_autoload :Generator
4
4
 
5
5
  extend ActiveSupport::Concern
6
6
  include ActiveModel::Conversion
@@ -34,50 +34,38 @@ module NoBrainer::Document::PrimaryKey
34
34
  end
35
35
 
36
36
  module ClassMethods
37
- def define_default_pk
38
- class_variable_set(:@@pk_name, nil)
39
- field NoBrainer::Document::PrimaryKey::DEFAULT_PK_NAME, :primary_key => :default
40
- end
41
-
42
- def define_pk(attr)
43
- return if pk_name == attr
44
- if fields[pk_name].try(:[], :primary_key) == :default
45
- remove_field(pk_name, :set_default_pk => false)
46
- end
47
- class_variable_set(:@@pk_name, attr)
48
- end
49
-
50
37
  def pk_name
51
38
  class_variable_get(:@@pk_name)
52
39
  end
53
40
 
54
- def _field(attr, options={})
55
- super
56
- define_pk(attr) if options[:primary_key]
41
+ def define_default_pk
42
+ class_variable_set(:@@pk_name, nil)
43
+
44
+ # TODO Maybe we should let the user configure the pk generator
45
+ pk_generator = NoBrainer::Document::PrimaryKey::Generator
46
+
47
+ field NoBrainer::Document::PrimaryKey::DEFAULT_PK_NAME, :primary_key => true,
48
+ :type => pk_generator.field_type, :default => ->{ pk_generator.generate }
57
49
  end
58
50
 
59
51
  def field(attr, options={})
60
- if attr.to_sym == pk_name || options[:primary_key]
61
- options = options.merge(:readonly => true) if options[:readonly].nil?
62
- options = options.merge(:index => true)
52
+ return super unless options[:primary_key]
63
53
 
64
- # TODO Maybe we should let the user configure the pk generator
65
- pk_generator = NoBrainer::Document::PrimaryKey::Generator
66
-
67
- if options[:type].in?([pk_generator.field_type, nil]) && !options.key?(:default)
68
- options[:type] = pk_generator.field_type
69
- options[:default] = ->{ pk_generator.generate }
70
- end
54
+ if attr != pk_name
55
+ remove_field(pk_name, :set_default_pk => false) if pk_name
56
+ class_variable_set(:@@pk_name, attr)
71
57
  end
72
58
 
59
+ options[:index] = true
60
+ options[:readonly] = true
73
61
  super
74
62
  end
75
63
 
76
- def _remove_field(attr, options={})
77
- super
64
+ def remove_field(attr, options={})
78
65
  if fields[attr][:primary_key] && options[:set_default_pk] != false
79
66
  define_default_pk
80
67
  end
68
+ super
81
69
  end
82
70
  end
83
71
  end
@@ -2,21 +2,25 @@ module NoBrainer::Document::Readonly
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  module ClassMethods
5
- def _field(attr, options={})
5
+ def field(attr, options={})
6
6
  super
7
7
  inject_in_layer :readonly do
8
- if options[:readonly]
8
+ case options[:readonly]
9
+ when true
9
10
  define_method("#{attr}=") do |value|
10
- raise NoBrainer::Error::ReadonlyField.new("#{attr} is readonly") unless new_record?
11
+ unless new_record?
12
+ if read_attribute(attr) != value
13
+ raise NoBrainer::Error::ReadonlyField.new("#{attr} is readonly")
14
+ end
15
+ end
11
16
  super(value)
12
17
  end
13
- else
14
- remove_method("#{attr}=") if method_defined?("#{attr}=")
18
+ when false then remove_method("#{attr}=") if method_defined?("#{attr}=")
15
19
  end
16
20
  end
17
21
  end
18
22
 
19
- def _remove_field(attr, options={})
23
+ def remove_field(attr, options={})
20
24
  super
21
25
  inject_in_layer :readonly do
22
26
  remove_method("#{attr}=") if method_defined?("#{attr}=")
@@ -99,7 +99,7 @@ module NoBrainer::Document::TableConfig
99
99
  end
100
100
 
101
101
  def sync_indexes(options={})
102
- # nobrainer models don't have indexes
102
+ # NoBrainer internal models don't have indexes.
103
103
  models = NoBrainer::Document.all(:types => [:user])
104
104
  NoBrainer::Document::Index::Synchronizer.new(models).sync_indexes(options)
105
105
  end
@@ -110,8 +110,22 @@ module NoBrainer::Document::TableConfig
110
110
  end
111
111
 
112
112
  def rebalance(options={})
113
- models = NoBrainer::Document.all(:types => [:user])
114
- models.each(&:rebalance)
113
+ NoBrainer.run { |r| r.table_list }.each do |table_name|
114
+ NoBrainer.run { |r| r.table(table_name).rebalance }
115
+ end
116
+ true
117
+ end
118
+
119
+ def drop!
120
+ NoBrainer.run { |r| r.db_drop(NoBrainer.current_db) }
121
+ end
122
+
123
+ def purge!
124
+ NoBrainer.run { |r| r.table_list }.each do |table_name|
125
+ # keeping the index meta store because indexes are not going away when purging
126
+ next if table_name == NoBrainer::Document::Index::MetaStore.table_name
127
+ NoBrainer.run { |r| r.table(table_name).delete }
128
+ end
115
129
  true
116
130
  end
117
131
  end
@@ -58,19 +58,20 @@ module NoBrainer::Document::Types
58
58
  cast_model_to_db_for(k, super)
59
59
  end
60
60
 
61
- def _field(attr, options={})
61
+ def field(attr, options={})
62
62
  super
63
63
 
64
- return unless options[:type]
64
+ type = options[:type]
65
+ return unless type
65
66
 
66
- raise "Please use a class for the type option" unless options[:type].is_a?(Class)
67
- case options[:type].to_s
67
+ raise "Please use a class for the type option" unless type.is_a?(Class)
68
+ case type.to_s
68
69
  when "NoBrainer::Geo::Circle" then raise "Cannot store circles :("
69
70
  when "NoBrainer::Geo::Polygon", "NoBrainer::Geo::LineString"
70
71
  raise "Make a request on github if you'd like to store polygons/linestrings"
71
72
  end
72
73
 
73
- NoBrainer::Document::Types.load_type_extensions(options[:type]) if options[:type]
74
+ NoBrainer::Document::Types.load_type_extensions(type) if type
74
75
 
75
76
  inject_in_layer :types do
76
77
  define_method("#{attr}=") do |value|
@@ -83,22 +84,28 @@ module NoBrainer::Document::Types
83
84
  end
84
85
  super(value)
85
86
  end
87
+ end
86
88
 
87
- define_method("#{attr}?") { !!read_attribute(attr) } if options[:type] == Boolean
89
+ if type.respond_to?(:nobrainer_field_defined)
90
+ type.nobrainer_field_defined(self, attr, options)
88
91
  end
89
92
  end
90
93
 
91
- def _remove_field(attr, options={})
92
- super
94
+ def remove_field(attr, options={})
95
+ if type = fields[attr][:type]
96
+ inject_in_layer :types do
97
+ remove_method("#{attr}=")
98
+ end
93
99
 
94
- inject_in_layer :types do
95
- remove_method("#{attr}=")
96
- remove_method("#{attr}?") if method_defined?("#{attr}?")
100
+ if type.respond_to?(:nobrainer_field_undefined)
101
+ type.nobrainer_field_undefined(self, attr, options)
102
+ end
97
103
  end
104
+ super
98
105
  end
99
106
  end
100
107
 
101
- %w(binary boolean text geo).each do |type|
108
+ %w(binary boolean text geo enum).each do |type|
102
109
  require File.join(File.dirname(__FILE__), 'types', type)
103
110
  const_set(type.camelize, NoBrainer.const_get(type.camelize))
104
111
  end
@@ -19,6 +19,18 @@ class NoBrainer::Boolean
19
19
  else raise InvalidType
20
20
  end
21
21
  end
22
+
23
+ def nobrainer_field_defined(model, attr, options={})
24
+ model.inject_in_layer :types do
25
+ define_method("#{attr}?") { !!read_attribute(attr) }
26
+ end
27
+ end
28
+
29
+ def nobrainer_field_undefined(model, attr, options={})
30
+ model.inject_in_layer :types do
31
+ remove_method("#{attr}?")
32
+ end
33
+ end
22
34
  end
23
35
  extend NoBrainerExtensions
24
36
  end
@@ -0,0 +1,63 @@
1
+ class NoBrainer::Enum
2
+ def initialize; raise; end
3
+ def self.inspect; 'Enum'; end
4
+ def self.to_s; inspect; end
5
+ def self.name; inspect; end
6
+
7
+ module NoBrainerExtensions
8
+ InvalidType = NoBrainer::Error::InvalidType
9
+
10
+ def nobrainer_cast_user_to_model(value)
11
+ Symbol.nobrainer_cast_user_to_model(value)
12
+ end
13
+
14
+ def nobrainer_cast_db_to_model(value)
15
+ Symbol.nobrainer_cast_db_to_model(value)
16
+ end
17
+
18
+ def nobrainer_field_defined(model, attr, options={})
19
+ NoBrainer::Document::Types.load_type_extensions(Symbol)
20
+
21
+ unless options[:in].present?
22
+ raise "When using Enum on `#{model}.#{attr}', you must provide the `:in` option to specify values"
23
+ end
24
+ unless options[:in].all? { |v| v.is_a?(Symbol) }
25
+ raise "The `:in` option must specify symbol values"
26
+ end
27
+
28
+ model.inject_in_layer :enum do
29
+ extend ActiveSupport::Concern
30
+
31
+ const_set(:ClassMethods, Module.new) unless const_defined?(:ClassMethods)
32
+
33
+ options[:in].each do |value|
34
+ method = NoBrainer::Enum::NoBrainerExtensions.method_name(value, attr, options)
35
+ if method_defined?("#{method}?")
36
+ raise "The method `#{method}' is already taken. You may specify a :prefix or :suffix option"
37
+ end
38
+
39
+ define_method("#{method}?") { read_attribute(attr) == value }
40
+ define_method("#{method}!") { write_attribute(attr, value) }
41
+ const_get(:ClassMethods).__send__(:define_method, "#{method}") { where(attr => value) }
42
+ end
43
+ end
44
+ end
45
+
46
+ def nobrainer_field_undefined(model, attr, options={})
47
+ model.inject_in_layer :enum do
48
+ model.fields[attr][:in].each do |value|
49
+ method = NoBrainer::Enum::NoBrainerExtensions.method_name(value, attr, model.fields[attr])
50
+ remove_method("#{method}?")
51
+ remove_method("#{method}!")
52
+ const_get(:ClassMethods).__send__(:remove_method, "#{method}")
53
+ end
54
+ end
55
+ end
56
+
57
+ def self.method_name(value, attr, options)
58
+ [options[:prefix] == true ? attr : options[:prefix], value,
59
+ options[:suffix] == true ? attr : options[:suffix]].compact.join("_")
60
+ end
61
+ end
62
+ extend NoBrainerExtensions
63
+ end
@@ -8,7 +8,8 @@ class Time
8
8
  case value
9
9
  when Time then time = value
10
10
  when String
11
- value = value.strip.sub(/Z$/, '+00:00')
11
+ value = value.strip
12
+ value = value.sub(/Z$/, '+00:00') unless NoBrainer.jruby?
12
13
  # Using DateTime to preserve the timezone offset
13
14
  dt = DateTime.parse(value) rescue (raise InvalidType)
14
15
  time = Time.new(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.zone)
@@ -27,7 +27,7 @@ module NoBrainer::Document::Validation::Core
27
27
  :uniq => :uniqueness, :unique => :uniqueness, :in => :inclusion }
28
28
 
29
29
  module ClassMethods
30
- def _field(attr, options={})
30
+ def field(attr, options={})
31
31
  super
32
32
 
33
33
  shorthands = SHORTHANDS
@@ -10,31 +10,49 @@ module NoBrainer::QueryRunner
10
10
  end
11
11
  end
12
12
 
13
- autoload :Driver, :DatabaseOnDemand, :TableOnDemand, :WriteError,
14
- :Reconnect, :Selection, :RunOptions, :Profiler, :MissingIndex,
15
- :ConnectionLock
13
+ autoload :EMDriver, :Driver, :DatabaseOnDemand, :TableOnDemand, :WriteError,
14
+ :Reconnect, :RunOptions, :Profiler, :MissingIndex, :ConnectionLock
16
15
 
17
16
  class << self
18
- attr_accessor :stack
19
-
20
17
  def run(*args, &block)
21
18
  options = args.extract_options!
22
19
  raise ArgumentError unless args.size == 1 || block
23
20
  query = args.first || block.call(RethinkDB::RQL.new)
24
21
  stack.call(:query => query, :options => options)
25
22
  end
26
- end
27
23
 
28
- # thread-safe, since require() is ran with a mutex.
29
- self.stack = ::Middleware::Builder.new do
30
- use RunOptions
31
- use MissingIndex
32
- use DatabaseOnDemand
33
- use TableOnDemand
34
- use Profiler
35
- use WriteError
36
- use ConnectionLock
37
- use Reconnect
38
- use Driver
24
+ def stack
25
+ case NoBrainer::Config.driver
26
+ when :regular then normal_stack
27
+ when :em then em_stack
28
+ end
29
+ end
30
+
31
+ def normal_stack
32
+ @normal_stack ||= ::Middleware::Builder.new do
33
+ use RunOptions
34
+ use MissingIndex
35
+ use DatabaseOnDemand
36
+ use TableOnDemand
37
+ use Profiler
38
+ use WriteError
39
+ use ConnectionLock
40
+ use Reconnect
41
+ use Driver
42
+ end
43
+ end
44
+
45
+ def em_stack
46
+ @em_stack ||= ::Middleware::Builder.new do
47
+ use RunOptions
48
+ use MissingIndex
49
+ use DatabaseOnDemand
50
+ use TableOnDemand
51
+ use Profiler
52
+ use WriteError
53
+ use Reconnect
54
+ use EMDriver
55
+ end
56
+ end
39
57
  end
40
58
  end
@@ -3,6 +3,9 @@ class NoBrainer::QueryRunner::DatabaseOnDemand < NoBrainer::QueryRunner::Middlew
3
3
  @runner.call(env)
4
4
  rescue RuntimeError => e
5
5
  if db_name = handle_database_on_demand_exception?(env, e)
6
+ # Don't auto create on db_drop.
7
+ return {} if NoBrainer::RQL.db_drop?(env[:query])
8
+
6
9
  auto_create_database(env, db_name)
7
10
  retry
8
11
  end
@@ -10,7 +13,7 @@ class NoBrainer::QueryRunner::DatabaseOnDemand < NoBrainer::QueryRunner::Middlew
10
13
  end
11
14
 
12
15
  def handle_database_on_demand_exception?(env, e)
13
- e.message =~ /^Database `(.+)` does not exist\.$/ && $1
16
+ /^Database `(.+)` does not exist\.$/.match(e.message).try(:[], 1)
14
17
  end
15
18
 
16
19
  private
@@ -0,0 +1,128 @@
1
+ require 'eventmachine'
2
+ require 'fiber'
3
+
4
+ class NoBrainer::QueryRunner::EMDriver < NoBrainer::QueryRunner::Middleware
5
+ def call(env)
6
+ options = env[:options]
7
+ options = options.merge(:db => RethinkDB::RQL.new.db(options[:db])) if options[:db]
8
+
9
+ handler = ResponseHandler.new
10
+ query_handler = env[:query].em_run(NoBrainer.connection.raw, handler, options)
11
+ handler.on_dispatch(query_handler)
12
+ handler.value
13
+ end
14
+
15
+ def self.sync(&block)
16
+ # Similar to em-synchrony's sync.
17
+ f = Fiber.current
18
+ block.call(proc do |val|
19
+ if f == Fiber.current
20
+ return val
21
+ else
22
+ f.resume(val)
23
+ end
24
+ end)
25
+ Fiber.yield
26
+ end
27
+
28
+ class ResponseHandler < RethinkDB::Handler
29
+ def initialize
30
+ @ready = EventMachine::DefaultDeferrable.new
31
+ end
32
+
33
+ def close_query_handle
34
+ @query_handle.close
35
+ end
36
+
37
+ def on_dispatch(caller)
38
+ @query_handle = caller
39
+ end
40
+
41
+ def on_open(caller)
42
+ @has_data = true
43
+ end
44
+
45
+ def on_close(caller)
46
+ return if @has_atom
47
+ return on_error(RethinkDB::RqlRuntimeError.new("NoBrainer EM driver: No data received"), caller) unless @has_data
48
+ @queue ? push(:close) : set_atom([])
49
+ end
50
+
51
+ def on_error(err, caller)
52
+ @error = err
53
+ push(err)
54
+ end
55
+
56
+ def on_atom(val, caller)
57
+ set_atom(val)
58
+ end
59
+
60
+ def on_array(arr, caller)
61
+ set_atom(arr)
62
+ end
63
+
64
+ def on_stream_val(val, caller)
65
+ push([val])
66
+ end
67
+
68
+ def on_unhandled_change(val, caller)
69
+ push([val])
70
+ end
71
+
72
+ def push(v)
73
+ raise "internal error: unexpected stream" if @has_atom
74
+ @queue ||= EventMachine::Queue.new
75
+ @queue.push(v)
76
+ response_ready!
77
+ end
78
+
79
+ def set_atom(v)
80
+ raise "internal error: unexpected atom" if @queue
81
+ @has_atom = true
82
+ @value = v
83
+ response_ready!
84
+ end
85
+
86
+ def response_ready!
87
+ @ready.succeed(nil) if @ready
88
+ end
89
+
90
+ def wait_for_response
91
+ NoBrainer::QueryRunner::EMDriver.sync { |w| @ready.callback(&w) } if @ready
92
+ @ready = nil
93
+ end
94
+
95
+ def value
96
+ wait_for_response
97
+ raise @error if @error
98
+ @has_atom ? @value : Cursor.new(self, @queue)
99
+ end
100
+
101
+ class Cursor
102
+ include Enumerable
103
+ def initialize(handler, queue)
104
+ @handler = handler
105
+ @queue = queue
106
+ end
107
+
108
+ def close
109
+ @handler.close_query_handle
110
+ end
111
+
112
+ def each(&block)
113
+ return enum_for(:each) unless block
114
+
115
+ raise "Can only iterate over a cursor once." if @iterated
116
+ @iterated = true
117
+
118
+ loop do
119
+ case result = NoBrainer::QueryRunner::EMDriver.sync { |w| @queue.pop(&w) }
120
+ when :close then return self
121
+ when Exception then raise result
122
+ else result.each { |v| block.call(v) }
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -1,11 +1,9 @@
1
1
  class NoBrainer::QueryRunner::MissingIndex < NoBrainer::QueryRunner::Middleware
2
2
  def call(env)
3
3
  @runner.call(env)
4
- rescue RethinkDB::RqlRuntimeError => e
5
- if e.message =~ /^Index `(.+)` was not found on table `(.+)\.(.+)`\.$/
6
- index_name = $1
7
- db_name = $2
8
- table_name = $3
4
+ rescue RuntimeError => e
5
+ if match_data = /^Index `(.+)` was not found on table `(.+)\.(.+)`\.$/.match(e.message)
6
+ _, index_name, db_name, table_name = *match_data
9
7
 
10
8
  model = NoBrainer::Document.all.select { |m| m.table_name == table_name }.first
11
9
  index = model.indexes.values.select { |i| i.aliased_name == index_name.to_sym }.first if model
@@ -2,56 +2,18 @@ class NoBrainer::QueryRunner::Reconnect < NoBrainer::QueryRunner::Middleware
2
2
  def call(env)
3
3
  @runner.call(env)
4
4
  rescue StandardError => e
5
- if is_connection_error_exception?(e)
6
- context ||= {}
7
- # XXX Possibly dangerous, as we could reexecute a non idempotent operation
8
- retry if reconnect(e, context)
9
- end
5
+ NoBrainer.disconnect if is_connection_error_exception?(e)
10
6
  raise
11
7
  end
12
8
 
13
9
  private
14
10
 
15
- def max_tries
16
- NoBrainer::Config.max_retries_on_connection_failure
17
- end
18
-
19
- def reconnect(e, context)
20
- context[:connection_retries] ||= max_tries
21
- context[:previous_connection] ||= NoBrainer.connection
22
- NoBrainer.disconnect
23
-
24
- unless context[:lost_connection_logged]
25
- context[:lost_connection_logged] = true
26
-
27
- msg = server_not_ready?(e) ? "Server %s not ready: %s" : "Connection issue with %s: %s"
28
- NoBrainer.logger.warn(msg % [context[:previous_connection].try(:uri), exception_msg(e)])
29
- end
30
-
31
- if context[:connection_retries].zero?
32
- NoBrainer.logger.info("Retry limit exceeded (#{max_tries}). Giving up.")
33
- return false
34
- end
35
- context[:connection_retries] -= 1
36
-
37
- sleep 1
38
-
39
- c = NoBrainer.connection
40
- NoBrainer.logger.info("Connecting to #{c.uri}... (last error: #{exception_msg(e)})")
41
- c.connect
42
-
43
- true
44
- rescue StandardError => e
45
- retry if is_connection_error_exception?(e)
46
- raise
47
- end
48
-
49
11
  def is_connection_error_exception?(e)
50
12
  case e
51
13
  when Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::EPIPE,
52
14
  Errno::ECONNRESET, Errno::ETIMEDOUT, IOError
53
15
  true
54
- when RethinkDB::RqlRuntimeError
16
+ when RethinkDB::RqlError
55
17
  e.message =~ /lost contact/ ||
56
18
  e.message =~ /(P|p)rimary .* not available/||
57
19
  e.message =~ /Connection.*closed/
@@ -59,13 +21,4 @@ class NoBrainer::QueryRunner::Reconnect < NoBrainer::QueryRunner::Middleware
59
21
  false
60
22
  end
61
23
  end
62
-
63
- def exception_msg(e)
64
- e.is_a?(RethinkDB::RqlRuntimeError) ? e.message.split("\n").first : e.to_s
65
- end
66
-
67
- def server_not_ready?(e)
68
- e.message =~ /lost contact/ ||
69
- e.message =~ /(P|p)rimary .* not available/
70
- end
71
24
  end
@@ -1,6 +1,7 @@
1
1
  class NoBrainer::QueryRunner::TableOnDemand < NoBrainer::QueryRunner::Middleware
2
2
  def call(env)
3
3
  @runner.call(env)
4
+ # Not matching in RqlRuntimeError because we can get a DocumentNotPersisted
4
5
  rescue RuntimeError => e
5
6
  if table_info = handle_table_on_demand_exception?(env, e)
6
7
  auto_create_table(env, *table_info)
@@ -10,7 +11,7 @@ class NoBrainer::QueryRunner::TableOnDemand < NoBrainer::QueryRunner::Middleware
10
11
  end
11
12
 
12
13
  def handle_table_on_demand_exception?(env, e)
13
- e.message =~ /^Table `(.+)\.(.+)` does not exist\.$/ && [$1, $2]
14
+ /^Table `(.+)\.(.+)` does not exist\.$/.match(e.message).try(:[], 1..2)
14
15
  end
15
16
 
16
17
  private
@@ -4,17 +4,20 @@ namespace :nobrainer do
4
4
  NoBrainer.drop!
5
5
  end
6
6
 
7
- desc 'Synchronize index definitions'
7
+ desc 'Rebalance all tables'
8
+ task :rebalance => :environment do
9
+ NoBrainer.rebalance(:verbose => true)
10
+ end
11
+
8
12
  task :sync_indexes => :environment do
9
13
  NoBrainer.sync_indexes(:verbose => true)
10
14
  end
11
15
 
12
- desc 'Synchronize table configuration'
13
16
  task :sync_table_config => :environment do
14
17
  NoBrainer.sync_table_config(:verbose => true)
15
18
  end
16
19
 
17
- desc 'Synchronize indexes and table configuration'
20
+ desc 'Synchronize schema'
18
21
  task :sync_schema => :environment do
19
22
  NoBrainer.sync_schema(:verbose => true)
20
23
  end
@@ -28,17 +31,11 @@ namespace :nobrainer do
28
31
  Rails.application.load_seed
29
32
  end
30
33
 
31
- desc 'Equivalent to :sync_schema_quiet + :seed'
32
34
  task :setup => [:sync_schema_quiet, :seed]
33
35
 
34
- desc 'Equivalent to :drop + :setup'
35
- task :reset => [:drop, :setup]
36
+ desc 'Equivalent to :drop + :sync_schema + :seed'
37
+ task :reset => [:drop, :sync_schema_quiet, :seed]
36
38
 
37
- task :create => :environment do
38
- # noop
39
- end
40
-
41
- task :migrate => :environment do
42
- # noop
43
- end
39
+ task :create => [:sync_schema]
40
+ task :migrate => [:sync_schema]
44
41
  end
@@ -17,8 +17,16 @@ module NoBrainer::RQL
17
17
  type_of(rql) == :write
18
18
  end
19
19
 
20
+ def get_rql_statement(rql)
21
+ rql.is_a?(RethinkDB::RQL) && rql.body.is_a?(Array) && rql.body.first
22
+ end
23
+
24
+ def db_drop?(rql)
25
+ get_rql_statement(rql) == DB_DROP
26
+ end
27
+
20
28
  def type_of(rql)
21
- case rql.is_a?(RethinkDB::RQL) && rql.body.is_a?(Array) && rql.body.first
29
+ case get_rql_statement(rql)
22
30
  when UPDATE, DELETE, REPLACE, INSERT
23
31
  :write
24
32
  when DB_CREATE, DB_DROP, DB_LIST, TABLE_CREATE, TABLE_DROP, TABLE_LIST,
@@ -20,8 +20,7 @@ module NoBrainer
20
20
 
21
21
  class << self
22
22
  delegate :connection, :disconnect, :to => 'NoBrainer::ConnectionManager'
23
-
24
- delegate :drop!, :purge!, :default_db, :current_db, :to => :connection
23
+ delegate :default_db, :current_db, :to => :connection
25
24
 
26
25
  delegate :configure, :logger, :to => 'NoBrainer::Config'
27
26
  delegate :run, :to => 'NoBrainer::QueryRunner'
@@ -29,13 +28,22 @@ module NoBrainer
29
28
 
30
29
  delegate :with, :with_database, :to => 'NoBrainer::QueryRunner::RunOptions' # deprecated
31
30
 
32
- delegate :sync_indexes, :sync_table_config, :sync_schema, :rebalance, :to => 'NoBrainer::Document::TableConfig'
31
+ delegate :sync_indexes, :sync_table_config, :sync_schema, :rebalance,
32
+ :drop!, :purge!, :to => 'NoBrainer::Document::TableConfig'
33
33
 
34
34
  delegate :eager_load, :to => 'NoBrainer::Document::Association::EagerLoader'
35
35
 
36
36
  def jruby?
37
37
  RUBY_PLATFORM == 'java'
38
38
  end
39
+
40
+ def eager_load!
41
+ # XXX This forces all the NoBrainer code to be loaded in memory.
42
+ # Not to be confused with eager_load() that operates on documents.
43
+ # We assume that NoBrainer is already configured at this point.
44
+ super
45
+ NoBrainer::QueryRunner.stack # load the code for the current stack
46
+ end
39
47
  end
40
48
 
41
49
  Fork.hook unless jruby?
@@ -22,6 +22,10 @@ NoBrainer.configure do |config|
22
22
  # an SSL connection to the RethinkDB servers.
23
23
  # config.ssl_options = nil
24
24
 
25
+ # driver specifies which driver to use. You may use :regular or :em.
26
+ # Use :em if you use EventMachine with em-synchrony.
27
+ # config.driver = :regular
28
+
25
29
  # NoBrainer uses logger to emit debugging information.
26
30
  # The default logger is the Rails logger if run with Rails,
27
31
  # otherwise Logger.new(STDERR) with a WARN level.
@@ -39,14 +43,6 @@ NoBrainer.configure do |config|
39
43
  # You can turn off the warning if you want to use both.
40
44
  # config.warn_on_active_record = true
41
45
 
42
- # When the network connection is lost, NoBrainer can retry running a given
43
- # query a few times before giving up. Note that this can be a problem with
44
- # non idempotent write queries such as increments.
45
- # Setting it to 0 disable retries during reconnections.
46
- # The default is 1 for development or test environment, otherwise 15.
47
- # config.max_retries_on_connection_failure = \
48
- # config.default_max_retries_on_connection_failure
49
-
50
46
  # Configures the durability for database writes.
51
47
  # The default is :soft for development or test environment, otherwise :hard.
52
48
  # config.durability = config.default_durability
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.28.0
4
+ version: 0.29.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-08-02 00:00:00.000000000 Z
11
+ date: 2015-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rethinkdb
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.0.0
19
+ version: 2.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.0.0
26
+ version: 2.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -154,6 +154,7 @@ files:
154
154
  - lib/no_brainer/document/types/binary.rb
155
155
  - lib/no_brainer/document/types/boolean.rb
156
156
  - lib/no_brainer/document/types/date.rb
157
+ - lib/no_brainer/document/types/enum.rb
157
158
  - lib/no_brainer/document/types/float.rb
158
159
  - lib/no_brainer/document/types/geo.rb
159
160
  - lib/no_brainer/document/types/integer.rb
@@ -184,6 +185,7 @@ files:
184
185
  - lib/no_brainer/query_runner/connection_lock.rb
185
186
  - lib/no_brainer/query_runner/database_on_demand.rb
186
187
  - lib/no_brainer/query_runner/driver.rb
188
+ - lib/no_brainer/query_runner/em_driver.rb
187
189
  - lib/no_brainer/query_runner/missing_index.rb
188
190
  - lib/no_brainer/query_runner/profiler.rb
189
191
  - lib/no_brainer/query_runner/reconnect.rb