nobrainer 0.28.0 → 0.29.0

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