nobrainer 0.30.0 → 0.34.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +129 -0
  3. data/README.md +2 -0
  4. data/lib/no_brainer/config.rb +16 -6
  5. data/lib/no_brainer/connection.rb +5 -3
  6. data/lib/no_brainer/criteria/first_or_create.rb +4 -1
  7. data/lib/no_brainer/criteria/join.rb +2 -1
  8. data/lib/no_brainer/criteria/order_by.rb +2 -0
  9. data/lib/no_brainer/criteria/update.rb +1 -1
  10. data/lib/no_brainer/criteria/virtual_attributes.rb +8 -1
  11. data/lib/no_brainer/criteria/where.rb +46 -53
  12. data/lib/no_brainer/document/association.rb +8 -0
  13. data/lib/no_brainer/document/association/belongs_to.rb +21 -5
  14. data/lib/no_brainer/document/association/core.rb +2 -1
  15. data/lib/no_brainer/document/association/has_many.rb +8 -5
  16. data/lib/no_brainer/document/attributes.rb +1 -0
  17. data/lib/no_brainer/document/callbacks.rb +9 -2
  18. data/lib/no_brainer/document/core.rb +10 -1
  19. data/lib/no_brainer/document/criteria.rb +1 -1
  20. data/lib/no_brainer/document/dirty.rb +5 -0
  21. data/lib/no_brainer/document/index/meta_store.rb +1 -1
  22. data/lib/no_brainer/document/lazy_fetch.rb +1 -0
  23. data/lib/no_brainer/document/persistance.rb +1 -0
  24. data/lib/no_brainer/document/primary_key/generator.rb +1 -1
  25. data/lib/no_brainer/document/table_config.rb +2 -2
  26. data/lib/no_brainer/document/table_config/synchronizer.rb +1 -6
  27. data/lib/no_brainer/document/types.rb +3 -2
  28. data/lib/no_brainer/document/types/geo.rb +1 -1
  29. data/lib/no_brainer/document/validation/core.rb +2 -3
  30. data/lib/no_brainer/lock.rb +2 -1
  31. data/lib/no_brainer/query_runner/reconnect.rb +2 -1
  32. data/lib/no_brainer/query_runner/run_options.rb +17 -12
  33. data/lib/no_brainer/query_runner/table_on_demand.rb +10 -0
  34. data/lib/no_brainer/railtie.rb +4 -2
  35. data/lib/no_brainer/system/table_config.rb +1 -0
  36. data/lib/nobrainer.rb +8 -0
  37. data/lib/rails/generators/nobrainer/install_generator.rb +8 -3
  38. data/lib/rails/generators/templates/nobrainer.rb +12 -12
  39. metadata +20 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ebe188d50e06d6ef907cdf043560143e3bd7d703
4
- data.tar.gz: 15b1580f27a66b6670386cc117b4767f930e67c7
2
+ SHA256:
3
+ metadata.gz: ad996bff8cfeced8ef2da26c7b989d24138bb5e4c9a7aa53738cd2cbdfdd907c
4
+ data.tar.gz: a24979230bc5d9deeb4dfbee4f353de22a61037dddff0e97521a8d72380db9af
5
5
  SHA512:
6
- metadata.gz: ff9b387f2045109c33efb88d36273325edf34573acb134bbb94d3d2e04c1efd6fdf65b761739eac61c185309cfa9deba5a07987fdf56becf3d78308b300838b9
7
- data.tar.gz: acb0f33b44d27ac25599352b0d568dd563e13a81da91b0dfef6cf72596a1500026e739f2cc64c63e22edc72ddb57ee3b61a70acf72c139cd502f0b1d95e78c9b
6
+ metadata.gz: c3fbe68aa20b186aaca5b5981c476754a9f3cb7e238d09db17340a348f983d48518e34927064cd124640b5dd47564406893613b2a39487b90712e7e02a5bcd58
7
+ data.tar.gz: 34106b6e8c1be3260dc8e6356c0658deaabff758818def9aafead00445c2ffcb8b93f8c6b261b4d1d6555d0e5dae0f8c97aad1e123c583933b79eda283849791
data/CHANGELOG.md ADDED
@@ -0,0 +1,129 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.34.1] - 2021-02-18
10
+ ### Fixed
11
+ - Defining attributes at class level (Rails 6.1 compatibity)
12
+ - Ruby 2.7 support
13
+
14
+ ## [0.34.0] - 2019-10-15
15
+ ### Added
16
+ - Rails 6 support
17
+ - Support for the nonvoting_replica_tags table option
18
+
19
+ ## [0.33.0] - 2016-11-27
20
+ ### Added
21
+ - Allow a run_options to be configured globally
22
+ - Support for username and password authentication
23
+
24
+ ### Changed
25
+ - Use URI.decode() for user and password in the RethinkDB URL
26
+ - Also comment active_record lines in the config/initializers/*.rb on gem installation
27
+ - Removed .unscoped when fetching belongs_to associations
28
+ - update_all() operates without ordering
29
+ - has_many dependent refactoring
30
+ - Default logger should be STDERR if the rails logger is not initialized
31
+ - Locking: changes lock key type from string to text in order to support more than 255 characters keys by default, the key type needs to be Text.
32
+ - field type for rql_function to :text
33
+
34
+ ### Fixed
35
+ - Model reloading with Rails5
36
+ - Rails 5 Strong Parameters: Strong parameters are not a Hash anymore in Rails 5, but support transforming into a hash
37
+
38
+ ## [0.32.0] - 2016-06-05
39
+ ### Added
40
+ - Compatiblity with Rails5
41
+ - order() as an alias of order_by()
42
+ - Provide attribute_will_change!() for compatibility
43
+
44
+ ### Changed
45
+ - Comments more active_record related configs during install
46
+
47
+ ### Fixed
48
+ - Rails 5 deprecation: Using ActiveSupport::Reloader instead of ActionDispatch::Reloader to be ready when Rails 5 hits
49
+ - config syntax
50
+
51
+ ### Removed
52
+ - JRuby and Rbx in travis-ci
53
+
54
+ ## [0.31.0] - 2016-02-07
55
+ ### Added
56
+ - Allow belongs_to association names to be used in upsert params
57
+ - `uniq` shorthand for the belongs_to association
58
+ - Allow self-referential belongs_to
59
+ - Prevent double loading of the same model
60
+ - Use index for where(XXX.defined => true)
61
+ - Guard against bad virtual attribute expressions
62
+ - Allow non existing attributes to be lazy fetched
63
+ - Added back update_attributes()
64
+
65
+ ### Fixed
66
+ - Fix join on keys that are undefined
67
+
68
+ ### Changed
69
+ - where() leverages the association translation abstraction
70
+ - Primary keys time offset should operate on fixed timezone
71
+
72
+ ### Removed
73
+ - Removing locking around sync_table_config
74
+
75
+ ## [0.30.0] - 2015-10-03
76
+ ### Added
77
+ - Add rbx-2 to .travis.yml
78
+ - Add the virtual attribute feature
79
+ - Lock: Allow find() to get locks by key
80
+ - Locks: Allow default expire/timeout values to be passed in new()
81
+ - NoBrainer::ReentrantLock implementation
82
+ - Allow compound indexes to be declared with an implicit name
83
+ - Allow polymorphic queries with first_or_create under certain conditions
84
+
85
+ ### Changed
86
+ - Prevent first_or_create() to accept block with arguments
87
+
88
+ ### Fixed
89
+ - Fix rails issue with tests and profiler
90
+ - Fix where() .include modifier with type checking
91
+ - Virtual attribute option fix
92
+ - Discard documents when join() encounter a nil join key
93
+ - Locks: bug fix: allow small timeouts in lock()
94
+ - Fix reentrant lock counter on steals
95
+
96
+ [Unreleased]: https://github.com/nobrainerorm/nobrainer/compare/v0.34.1...HEAD
97
+ [0.34.1]: https://github.com/nobrainerorm/nobrainer/compare/v0.34.0...v0.34.1
98
+ [0.34.0]: https://github.com/nobrainerorm/nobrainer/compare/v0.33.0...v0.34.0
99
+ [0.33.0]: https://github.com/nobrainerorm/nobrainer/compare/v0.32.0...v0.33.0
100
+ [0.32.0]: https://github.com/nobrainerorm/nobrainer/compare/v0.31.0...v0.32.0
101
+ [0.31.0]: https://github.com/nobrainerorm/nobrainer/compare/v0.30.0...v0.31.0
102
+ [0.30.0]: https://github.com/nobrainerorm/nobrainer/compare/v0.29.0...v0.30.0
103
+ [0.29.0]: https://github.com/nobrainerorm/nobrainer/compare/0.28.0...0.29.0
104
+ [0.28.0]: https://github.com/nobrainerorm/nobrainer/compare/0.27.0...0.28.0
105
+ [0.27.0]: https://github.com/nobrainerorm/nobrainer/compare/0.26.0...0.27.0
106
+ [0.26.0]: https://github.com/nobrainerorm/nobrainer/compare/0.25.1...0.26.0
107
+ [0.25.1]: https://github.com/nobrainerorm/nobrainer/compare/0.25.0...0.25.1
108
+ [0.25.0]: https://github.com/nobrainerorm/nobrainer/compare/0.24.0...0.25.0
109
+ [0.24.0]: https://github.com/nobrainerorm/nobrainer/compare/0.23.0...0.24.0
110
+ [0.23.0]: https://github.com/nobrainerorm/nobrainer/compare/0.22.0...0.23.0
111
+ [0.22.0]: https://github.com/nobrainerorm/nobrainer/compare/0.21.0...0.22.0
112
+ [0.21.0]: https://github.com/nobrainerorm/nobrainer/compare/0.20.0...0.21.0
113
+ [0.20.0]: https://github.com/nobrainerorm/nobrainer/compare/0.19.0...0.20.0
114
+ [0.19.0]: https://github.com/nobrainerorm/nobrainer/compare/0.18.1...0.19.0
115
+ [0.18.1]: https://github.com/nobrainerorm/nobrainer/compare/0.18.0...0.18.1
116
+ [0.18.0]: https://github.com/nobrainerorm/nobrainer/compare/0.17.0...0.18.0
117
+ [0.17.0]: https://github.com/nobrainerorm/nobrainer/compare/0.16.0...0.17.0
118
+ [0.16.0]: https://github.com/nobrainerorm/nobrainer/compare/0.15.0...0.16.0
119
+ [0.15.0]: https://github.com/nobrainerorm/nobrainer/compare/0.14.0...0.15.0
120
+ [0.15.0]: https://github.com/nobrainerorm/nobrainer/compare/0.14.0...0.15.0
121
+ [0.14.0]: https://github.com/nobrainerorm/nobrainer/compare/0.13.1...0.14.0
122
+ [0.13.1]: https://github.com/nobrainerorm/nobrainer/compare/0.13.0...0.13.1
123
+ [0.13.0]: https://github.com/nobrainerorm/nobrainer/compare/0.12.0...0.13.0
124
+ [0.12.0]: https://github.com/nobrainerorm/nobrainer/compare/0.11.0...0.12.0
125
+ [0.11.0]: https://github.com/nobrainerorm/nobrainer/compare/0.10.0...0.11.0
126
+ [0.10.0]: https://github.com/nobrainerorm/nobrainer/compare/0.9.1...0.10.0
127
+ [0.9.1]: https://github.com/nobrainerorm/nobrainer/compare/0.9.0...0.9.1
128
+ [0.9.0]: https://github.com/nobrainerorm/nobrainer/compare/0.8.0...0.9.0
129
+ [0.8.0]: https://github.com/nobrainerorm/nobrainer/releases/tag/0.8.0
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  NoBrainer
2
2
  ===========
3
3
 
4
+ [![Join the chat at https://gitter.im/nviennot/nobrainer](https://badges.gitter.im/nviennot/nobrainer.svg)](https://gitter.im/nviennot/nobrainer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5
+
4
6
  NoBrainer is a Ruby ORM for [RethinkDB](http://www.rethinkdb.com/).
5
7
 
6
8
  The documentation can be found on [http://nobrainer.io](http://nobrainer.io).
@@ -10,9 +10,10 @@ module NoBrainer::Config
10
10
  :logger => { :default => ->{ default_logger } },
11
11
  :colorize_logger => { :default => ->{ true }, :valid_values => [true, false] },
12
12
  :warn_on_active_record => { :default => ->{ true }, :valid_values => [true, false] },
13
- :durability => { :default => ->{ default_durability }, :valid_values => [:hard, :soft] },
13
+ :durability => { :default => ->{ nil } }, # legacy
14
14
  :table_options => { :default => ->{ {:shards => 1, :replicas => 1, :write_acks => :majority} },
15
- :valid_keys => [:shards, :replicas, :primary_replica_tag, :write_acks, :durability] },
15
+ :valid_keys => [:durability, :shards, :replicas, :primary_replica_tag, :nonvoting_replica_tags, :write_acks] },
16
+ :run_options => { :default => ->{ {:durability => default_durability} } },
16
17
  :max_string_length => { :default => ->{ 255 } },
17
18
  :user_timezone => { :default => ->{ :local }, :valid_values => [:unchanged, :utc, :local] },
18
19
  :db_timezone => { :default => ->{ :utc }, :valid_values => [:unchanged, :utc, :local] },
@@ -51,6 +52,10 @@ module NoBrainer::Config
51
52
  @geo_options = value.try(:symbolize_keys)
52
53
  end
53
54
 
55
+ def run_options=(value)
56
+ @run_options = value.try(:symbolize_keys)
57
+ end
58
+
54
59
  def assert_valid_options
55
60
  SETTINGS.each do |k,v|
56
61
  assert_value_in(k, v[:valid_values]) if v[:valid_values]
@@ -102,7 +107,10 @@ module NoBrainer::Config
102
107
  end
103
108
 
104
109
  def default_app_name
105
- defined?(Rails) ? Rails.application.class.parent_name.underscore.presence : nil rescue nil
110
+ return unless defined?(Rails)
111
+ NoBrainer.rails6? ?
112
+ Rails.application.class.module_parent_name.underscore.presence :
113
+ Rails.application.class.parent_name.underscore.presence rescue nil
106
114
  end
107
115
 
108
116
  def default_environment
@@ -119,9 +127,10 @@ module NoBrainer::Config
119
127
  db ||= "#{self.app_name}_#{self.environment}" if self.app_name && self.environment
120
128
  host = ENV['RETHINKDB_HOST'] || ENV['RDB_HOST'] || 'localhost'
121
129
  port = ENV['RETHINKDB_PORT'] || ENV['RDB_PORT']
122
- auth = ENV['RETHINKDB_AUTH'] || ENV['RDB_AUTH']
130
+ user = ENV['RETHINKDB_USER'] || ENV['RDB_USER']
131
+ pass = ENV['RETHINKDB_PASSWORD'] || ENV['RDB_PASSWORD'] || ENV['RETHINKDB_AUTH'] || ENV['RDB_AUTH']
123
132
  url = ENV['RETHINKDB_URL'] || ENV['RDB_URL']
124
- url ||= "rethinkdb://#{":#{auth}@" if auth}#{host}#{":#{port}" if port}/#{db}" if db
133
+ url ||= "rethinkdb://#{"#{user}:#{pass}@" if (user || pass)}#{host}#{":#{port}" if port}/#{db}" if db
125
134
  url
126
135
  end
127
136
 
@@ -133,7 +142,8 @@ module NoBrainer::Config
133
142
  end
134
143
 
135
144
  def default_logger
136
- defined?(Rails.logger) ? Rails.logger : Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
145
+ (Rails.logger if defined?(Rails.logger)) ||
146
+ Logger.new(STDERR).tap { |l| l.level = Logger::WARN }
137
147
  end
138
148
 
139
149
  def default_durability
@@ -18,7 +18,9 @@ class NoBrainer::Connection
18
18
  "Invalid URI. Expecting something like rethinkdb://host:port/database. Got #{uri}"
19
19
  end
20
20
 
21
- { :auth_key => uri.password,
21
+ {
22
+ :user => uri.user && CGI.unescape(uri.user),
23
+ :password => uri.password && CGI.unescape(uri.password),
22
24
  :host => uri.host,
23
25
  :port => uri.port || 28015,
24
26
  :db => uri.path.gsub(/^\//, ''),
@@ -27,7 +29,7 @@ class NoBrainer::Connection
27
29
  end
28
30
 
29
31
  def uri
30
- "rethinkdb://#{'****@' if parsed_uri[:auth_key]}#{parsed_uri[:host]}:#{parsed_uri[:port]}/#{parsed_uri[:db]}"
32
+ "rethinkdb://#{'****@' if parsed_uri[:password]}#{parsed_uri[:host]}:#{parsed_uri[:port]}/#{parsed_uri[:db]}"
31
33
  end
32
34
 
33
35
  def raw
@@ -45,6 +47,6 @@ class NoBrainer::Connection
45
47
  end
46
48
 
47
49
  def current_db
48
- NoBrainer.current_run_options.try(:[], :db) || default_db
50
+ NoBrainer.current_run_options[:db] || default_db
49
51
  end
50
52
  end
@@ -20,7 +20,10 @@ module NoBrainer::Criteria::FirstOrCreate
20
20
  private
21
21
 
22
22
  def _upsert(attrs, save_options)
23
- attrs = attrs.symbolize_keys
23
+ # Note: we don't do a full cast of user_to_cast because where() takes care
24
+ # of it. We just need to locate a suitable uniqueness validator, for which
25
+ # we just need to translate keys.
26
+ attrs = Hash[attrs.map { |k,v| model.association_user_to_model_cast(k.to_sym, v) }]
24
27
  unique_keys = get_model_unique_fields.detect { |keys| keys & attrs.keys == keys }
25
28
  return where(attrs.slice(*unique_keys)).__send__(:_first_or_create, attrs, save_options) if unique_keys
26
29
 
@@ -36,6 +36,7 @@ module NoBrainer::Criteria::Join
36
36
 
37
37
  def _instantiate_model(attrs, options={})
38
38
  return super unless @options[:join] && !raw?
39
+ return super if attrs.nil?
39
40
 
40
41
  associated_instances = join_ast.map do |association, criteria|
41
42
  [association, criteria.send(:_instantiate_model, attrs.delete(association.target_name.to_s))]
@@ -53,7 +54,7 @@ module NoBrainer::Criteria::Join
53
54
  join_ast.reduce(super) do |rql, (association, criteria)|
54
55
  rql.concat_map do |doc|
55
56
  key = doc[association.eager_load_owner_key]
56
- RethinkDB::RQL.new.branch(key.eq(nil), [],
57
+ RethinkDB::RQL.new.branch(key.default(nil).eq(nil), [],
57
58
  criteria.where(association.eager_load_target_key => key).to_rql.map do |assoc_doc|
58
59
  doc.merge(association.target_name => assoc_doc)
59
60
  end)
@@ -24,6 +24,8 @@ module NoBrainer::Criteria::OrderBy
24
24
  :reversed_ordering => false)
25
25
  end
26
26
 
27
+ alias_method :order, :order_by
28
+
27
29
  def without_ordering
28
30
  chain(:ordering_mode => :disabled)
29
31
  end
@@ -16,6 +16,6 @@ module NoBrainer::Criteria::Update
16
16
  # can't use without_distinct when passed a block as the operation may be
17
17
  # performed many times, which might not be idempotent.
18
18
  clause = block ? self : without_distinct
19
- run { clause.without_plucking.to_rql.__send__(type, *args, &block) }
19
+ run { clause.without_plucking.without_ordering.to_rql.__send__(type, *args, &block) }
20
20
  end
21
21
  end
@@ -8,7 +8,14 @@ module NoBrainer::Criteria::VirtualAttributes
8
8
  rql = rql.map do |_doc|
9
9
  model.virtual_fields.reduce(_doc) do |doc, field|
10
10
  field_rql = model.fields[field][:virtual].call(doc, RethinkDB::RQL.new)
11
- field_rql.nil? ? doc : doc.merge(field => field_rql)
11
+ if field_rql.nil?
12
+ doc
13
+ else
14
+ unless field_rql.is_a?(RethinkDB::RQL)
15
+ raise "The virtual attribute `#{model}.#{field}' should return a RQL expression"
16
+ end
17
+ doc.merge(field => field_rql)
18
+ end
12
19
  end
13
20
  end
14
21
  end
@@ -150,69 +150,57 @@ module NoBrainer::Criteria::Where
150
150
  raise "Incorrect use of `#{op}' and `#{key_modifier}'" if key_modifier != :scalar
151
151
  end
152
152
 
153
- def association
154
- return nil if key_path.size > 1
155
- @association ||= [model.association_metadata[key_path.first]]
156
- @association.first
157
- end
158
-
159
153
  def cast_value(value)
160
154
  return value if casted_values
161
155
 
162
- case association
163
- when NoBrainer::Document::Association::BelongsTo::Metadata
164
- target_model = association.target_model
165
- unless value.is_a?(target_model)
166
- opts = { :model => model, :attr_name => key_path.first, :type => target_model, :value => value }
167
- raise NoBrainer::Error::InvalidType.new(opts)
168
- end
169
- value.pk_value
170
- else
171
- case op
172
- when :defined, :undefined then NoBrainer::Boolean.nobrainer_cast_user_to_model(value)
173
- when :intersects
174
- raise "Use a geo object with `intersects`" unless value.is_a?(NoBrainer::Geo::Base)
175
- value
176
- when :near
177
- # TODO enforce key is a geo type
178
- case value
179
- when Hash
180
- options = NoBrainer::Geo::Base.normalize_geo_options(value)
181
-
182
- options[:radius] = options.delete(:max_distance) if options[:max_distance]
183
- options[:radius] = options.delete(:max_dist) if options[:max_dist]
184
- options[:center] = options.delete(:point) if options[:point]
185
-
186
- unless options[:circle]
187
- unless options[:center] && options[:radius]
188
- raise "`near' takes something like {:center => P, :radius => d}"
189
- end
190
- { :circle => NoBrainer::Geo::Circle.new(options), :max_results => options[:max_results] }
156
+ case op
157
+ when :defined, :undefined then NoBrainer::Boolean.nobrainer_cast_user_to_model(value)
158
+ when :intersects
159
+ raise "Use a geo object with `intersects`" unless value.is_a?(NoBrainer::Geo::Base)
160
+ value
161
+ when :near
162
+ # TODO enforce key is a geo type
163
+ case value
164
+ when Hash
165
+ options = NoBrainer::Geo::Base.normalize_geo_options(value)
166
+
167
+ options[:radius] = options.delete(:max_distance) if options[:max_distance]
168
+ options[:radius] = options.delete(:max_dist) if options[:max_dist]
169
+ options[:center] = options.delete(:point) if options[:point]
170
+
171
+ unless options[:circle]
172
+ unless options[:center] && options[:radius]
173
+ raise "`near' takes something like {:center => P, :radius => d}"
191
174
  end
192
- when NoBrainer::Geo::Circle then { :circle => value }
193
- else raise "Incorrect use of `near': rvalue must be a hash or a circle"
175
+ { :circle => NoBrainer::Geo::Circle.new(options), :max_results => options[:max_results] }
194
176
  end
195
- else
196
- # 1) Box value in array if we have an any/all modifier
197
- # 2) Box value in hash if we have a nested query.
198
- box_value = key_modifier.in?([:any, :all]) || op == :include
199
- value = [value] if box_value
200
- value_hash = key_path.reverse.reduce(value) { |v,k| {k => v} }
201
- value = model.cast_user_to_db_for(*value_hash.first)
202
- value = key_path[1..-1].reduce(value) { |h,k| h[k] }
203
- value = value.first if box_value
204
- value
177
+ when NoBrainer::Geo::Circle then { :circle => value }
178
+ else raise "Incorrect use of `near': rvalue must be a hash or a circle"
205
179
  end
180
+ else
181
+ # 1) Box value in array if we have an any/all modifier
182
+ # 2) Box value in hash if we have a nested query.
183
+ box_value = key_modifier.in?([:any, :all]) || op == :include
184
+ value = [value] if box_value
185
+ k_v = key_path.reverse.reduce(value) { |v,k| {k => v} }.first
186
+ k_v = model.association_user_to_model_cast(*k_v)
187
+ value = model.cast_user_to_db_for(*k_v)
188
+ value = key_path[1..-1].reduce(value) { |h,k| h[k] }
189
+ value = value.first if box_value
190
+ value
206
191
  end
207
192
  end
208
193
 
209
- def cast_key_path(key)
210
- return key if casted_values
194
+ def cast_key_path(key_path)
195
+ return key_path if casted_values
211
196
 
212
- case association
213
- when NoBrainer::Document::Association::BelongsTo::Metadata then [association.foreign_key]
214
- else model.ensure_valid_key!(key_path.first); key_path
197
+ if key_path.size == 1
198
+ k, _v = model.association_user_to_model_cast(key_path.first, nil)
199
+ key_path = [k]
215
200
  end
201
+
202
+ model.ensure_valid_key!(key_path.first)
203
+ key_path
216
204
  end
217
205
  end
218
206
 
@@ -365,7 +353,7 @@ module NoBrainer::Criteria::Where
365
353
  end
366
354
 
367
355
  def find_strategy_canonical
368
- clauses = get_candidate_clauses(:eq, :in, :between, :near, :intersects)
356
+ clauses = get_candidate_clauses(:eq, :in, :between, :near, :intersects, :defined)
369
357
  return nil unless clauses.present?
370
358
 
371
359
  usable_indexes = Hash[get_usable_indexes.map { |i| [[i.name], i] }]
@@ -384,6 +372,11 @@ module NoBrainer::Criteria::Where
384
372
  [:get_nearest, circle.center.to_rql, circle.options.merge(options)]
385
373
  when :eq then [:get_all, [clause.value]]
386
374
  when :in then [:get_all, clause.value]
375
+ when :defined then
376
+ next unless clause.value == true
377
+ next unless clause.key_modifier == :scalar && index.multi == false
378
+ [:between, [RethinkDB::RQL.new.minval, RethinkDB::RQL.new.maxval],
379
+ :left_bound => :open, :right_bound => :open]
387
380
  when :between then [:between, [clause.value.min, clause.value.max],
388
381
  :left_bound => :closed, :right_bound => :closed]
389
382
  end
@@ -21,6 +21,14 @@ module NoBrainer::Document::Association
21
21
  super
22
22
  end
23
23
 
24
+ def association_user_to_model_cast(k,v)
25
+ association = association_metadata[k]
26
+ case association
27
+ when NoBrainer::Document::Association::BelongsTo::Metadata then association.cast_attr(k,v)
28
+ else [k,v]
29
+ end
30
+ end
31
+
24
32
  METHODS.each do |association|
25
33
  define_method(association) do |target, options={}|
26
34
  target = target.to_sym
@@ -2,7 +2,8 @@ class NoBrainer::Document::Association::BelongsTo
2
2
  include NoBrainer::Document::Association::Core
3
3
 
4
4
  class Metadata
5
- VALID_OPTIONS = [:primary_key, :foreign_key, :class_name, :foreign_key_store_as, :index, :validates, :required]
5
+ VALID_OPTIONS = [:primary_key, :foreign_key, :class_name, :foreign_key_store_as,
6
+ :index, :validates, :required, :uniq, :unique]
6
7
  include NoBrainer::Document::Association::Core::Metadata
7
8
  include NoBrainer::Document::Association::EagerLoader::Generic
8
9
 
@@ -34,7 +35,7 @@ class NoBrainer::Document::Association::BelongsTo
34
35
  end
35
36
 
36
37
  def base_criteria
37
- target_model.without_ordering.unscoped
38
+ target_model.without_ordering
38
39
  end
39
40
 
40
41
  def hook
@@ -51,12 +52,17 @@ class NoBrainer::Document::Association::BelongsTo
51
52
  unless options[:validates] == false
52
53
  owner_model.validates(target_name, options[:validates]) if options[:validates]
53
54
 
55
+ uniq = options[:uniq] || options[:unique]
56
+ if uniq
57
+ owner_model.validates(foreign_key, :uniqueness => uniq)
58
+ end
59
+
54
60
  if options[:required]
55
61
  owner_model.validates(target_name, :presence => options[:required])
56
62
  else
57
- # Always validate the validity of the foreign_key if not nil.
63
+ # Always validate the foreign_key if not nil.
58
64
  owner_model.validates_each(foreign_key) do |doc, attr, value|
59
- if !value.nil? && doc.read_attribute_for_validation(target_name).nil?
65
+ if !value.nil? && value != doc.pk_value && doc.read_attribute_for_validation(target_name).nil?
60
66
  doc.errors.add(attr, :invalid_foreign_key, :target_model => target_model, :primary_key => primary_key)
61
67
  end
62
68
  end
@@ -68,6 +74,16 @@ class NoBrainer::Document::Association::BelongsTo
68
74
  add_callback_for(:after_validation)
69
75
  end
70
76
 
77
+ def cast_attr(k, v)
78
+ case v
79
+ when target_model then [foreign_key, v.__send__(primary_key)]
80
+ when nil then [foreign_key, nil]
81
+ else
82
+ opts = { :model => owner_model, :attr_name => k, :type => target_model, :value => v }
83
+ raise NoBrainer::Error::InvalidType.new(opts)
84
+ end
85
+ end
86
+
71
87
  def eager_load_owner_key; foreign_key; end
72
88
  def eager_load_target_key; primary_key; end
73
89
  end
@@ -109,7 +125,7 @@ class NoBrainer::Document::Association::BelongsTo
109
125
  end
110
126
 
111
127
  def after_validation_callback
112
- if loaded? && target && !target.persisted?
128
+ if loaded? && target && !target.persisted? && target != owner
113
129
  raise NoBrainer::Error::AssociationNotPersisted.new("#{target_name} must be saved first")
114
130
  end
115
131
  end
@@ -51,7 +51,8 @@ module NoBrainer::Document::Association::Core
51
51
  return model_name if model_name.is_a?(Module)
52
52
 
53
53
  model_name = model_name.to_s
54
- current_module = @owner_model.parent
54
+ current_module = NoBrainer.rails6? ? @owner_model.module_parent : @owner_model.parent
55
+
55
56
  return model_name.constantize if current_module == Object
56
57
  return model_name.constantize if model_name =~ /^::/
57
58
  return model_name.constantize if !current_module.const_defined?(model_name)
@@ -97,13 +97,16 @@ class NoBrainer::Document::Association::HasMany
97
97
  ->(target){ set_inverses_of([target]) if target.is_a?(NoBrainer::Document) }
98
98
  end
99
99
 
100
+ def dependent_criteria
101
+ target_criteria.unscoped
102
+ end
103
+
100
104
  def before_destroy_callback
101
- criteria = target_criteria.unscoped.without_cache
102
105
  case metadata.options[:dependent]
103
- when :destroy then criteria.destroy_all
104
- when :delete then criteria.delete_all
105
- when :nullify then criteria.update_all(foreign_key => nil)
106
- when :restrict then raise NoBrainer::Error::ChildrenExist unless criteria.count.zero?
106
+ when :destroy then dependent_criteria.destroy_all
107
+ when :delete then dependent_criteria.delete_all
108
+ when :nullify then dependent_criteria.update_all(foreign_key => nil)
109
+ when :restrict then raise NoBrainer::Error::ChildrenExist unless dependent_criteria.empty?
107
110
  end
108
111
  end
109
112
  end
@@ -70,6 +70,7 @@ module NoBrainer::Document::Attributes
70
70
  end
71
71
 
72
72
  def assign_attributes(attrs, options={})
73
+ attrs = attrs.to_h if !attrs.is_a?(Hash) && attrs.respond_to?(:to_h)
73
74
  raise ArgumentError, "To assign attributes, please pass a hash instead of `#{attrs.class}'" unless attrs.is_a?(Hash)
74
75
 
75
76
  if options[:pristine]
@@ -1,11 +1,18 @@
1
1
  module NoBrainer::Document::Callbacks
2
2
  extend ActiveSupport::Concern
3
3
 
4
+ def self.define_callbacks_options(options={})
5
+ if ActiveSupport::Callbacks.respond_to?(:halt_and_display_warning_on_return_false)
6
+ ActiveSupport::Callbacks.halt_and_display_warning_on_return_false = false
7
+ end
8
+ NoBrainer.rails5? ? options : options.merge(:terminator => proc { false })
9
+ end
10
+
4
11
  included do
5
12
  extend ActiveModel::Callbacks
6
13
 
7
- define_model_callbacks :initialize, :create, :update, :save, :destroy, :terminator => proc { false }
8
- define_model_callbacks :find, :only => [:after], :terminator => proc { false }
14
+ define_model_callbacks :initialize, :create, :update, :save, :destroy, NoBrainer::Document::Callbacks.define_callbacks_options
15
+ define_model_callbacks :find, NoBrainer::Document::Callbacks.define_callbacks_options(:only => [:after])
9
16
  end
10
17
 
11
18
  def initialize(*args, &block)
@@ -9,7 +9,16 @@ module NoBrainer::Document::Core
9
9
  extend ActiveModel::Naming
10
10
  extend ActiveModel::Translation
11
11
 
12
- NoBrainer::Document::Core._all << self unless name =~ /^NoBrainer::/
12
+ unless name =~ /^NoBrainer::/
13
+ if NoBrainer::Document::Core._all.map(&:name).include?(name)
14
+ raise "Fatal: The model `#{name}' is already registered and partially loaded.\n" +
15
+ "This may happen when an exception occured while loading the model definitions\n" +
16
+ "(e.g. calling a missing class method on another model, having circular dependencies).\n" +
17
+ "In this situation, ActiveSupport autoloader may retry loading the model.\n" +
18
+ "Try moving all class methods declaration at the top of the model."
19
+ end
20
+ NoBrainer::Document::Core._all << self
21
+ end
13
22
  end
14
23
 
15
24
  def self.all(options={})
@@ -16,7 +16,7 @@ module NoBrainer::Document::Criteria
16
16
  delegate :to_rql, # Core
17
17
  :raw, # Raw
18
18
  :limit, :offset, :skip, # Limit
19
- :order_by, :reverse_order, :without_ordering, :order_by_indexed?, :order_by_index_name, # OrderBy
19
+ :order_by, :order, :reverse_order, :without_ordering, :order_by_indexed?, :order_by_index_name, # OrderBy
20
20
  :unscoped, # Scope
21
21
  :_where, :where, :where_indexed?, :where_index_name, :where_index_type, # Where
22
22
  :with_index, :without_index, :used_index, # Index
@@ -61,6 +61,11 @@ module NoBrainer::Document::Dirty
61
61
  end
62
62
  end
63
63
 
64
+ def attribute_will_change!(*)
65
+ # Provided for comatibility. See issue #190
66
+ :not_implemented_in_no_brainer_see_issue_190
67
+ end
68
+
64
69
  def _read_attribute(name)
65
70
  super.tap do |value|
66
71
  # This take care of string/arrays/hashes that could change without going
@@ -10,7 +10,7 @@ class NoBrainer::Document::Index::MetaStore
10
10
 
11
11
  field :table_name, :type => String, :required => true
12
12
  field :index_name, :type => String, :required => true
13
- field :rql_function, :type => String, :required => true
13
+ field :rql_function, :type => Text, :required => true
14
14
 
15
15
  def rql_function=(value)
16
16
  super(JSON.dump(value))
@@ -49,6 +49,7 @@ module NoBrainer::Document::LazyFetch
49
49
  raise e unless attr.in?(@lazy_fetch)
50
50
  reload(:pluck => attr, :keep_ivars => true)
51
51
  @lazy_fetch.delete(attr)
52
+ clear_missing_field(attr)
52
53
  retry
53
54
  end
54
55
  end
@@ -112,6 +112,7 @@ module NoBrainer::Document::Persistance
112
112
  def update(*args)
113
113
  update?(*args)
114
114
  end
115
+ alias_method :update_attributes, :update # for API compat like devise
115
116
 
116
117
  def delete
117
118
  unless @destroyed
@@ -3,7 +3,7 @@ module NoBrainer::Document::PrimaryKey::Generator
3
3
 
4
4
  BASE_TABLE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".freeze
5
5
 
6
- TIME_OFFSET = Time.parse('2014-01-01').to_i
6
+ TIME_OFFSET = Time.utc(2014, 01, 01).to_i
7
7
 
8
8
  # 30 bits timestamp with 1s resolution -> We overflow in year 2048. Good enough.
9
9
  # Math.log(Time.parse('2048-01-01').to_f - TIME_OFFSET)/Math.log(2) = 29.999
@@ -6,7 +6,7 @@ module NoBrainer::Document::TableConfig
6
6
 
7
7
  autoload :Synchronizer
8
8
 
9
- VALID_TABLE_CONFIG_OPTIONS = [:name, :durability, :shards, :replicas, :primary_replica_tag, :write_acks]
9
+ VALID_TABLE_CONFIG_OPTIONS = [:name, :durability, :shards, :replicas, :primary_replica_tag, :nonvoting_replica_tags, :write_acks]
10
10
 
11
11
  included do
12
12
  cattr_accessor :table_config_options, :instance_accessor => false
@@ -78,7 +78,7 @@ module NoBrainer::Document::TableConfig
78
78
  def sync_table_config(options={})
79
79
  c = table_create_options
80
80
  table_config.update!(c.slice(:durability, :primary_key, :write_acks))
81
- NoBrainer.run { rql_table.reconfigure(c.slice(:shards, :replicas, :primary_replica_tag)) }
81
+ NoBrainer.run { rql_table.reconfigure(c.slice(:shards, :replicas, :primary_replica_tag, :nonvoting_replica_tags)) }
82
82
  true
83
83
  end
84
84
 
@@ -4,12 +4,7 @@ class NoBrainer::Document::TableConfig::Synchronizer
4
4
  end
5
5
 
6
6
  def sync_table_config(options={})
7
- # XXX A bit funny since we might touch the lock table...
8
- lock = NoBrainer::Lock.new('nobrainer:sync_table_config')
9
-
10
- lock.synchronize do
11
- @models.each(&:sync_table_config)
12
- end
7
+ @models.each(&:sync_table_config)
13
8
 
14
9
  unless options[:wait] == false
15
10
  # Waiting on all models due to possible races
@@ -1,6 +1,9 @@
1
1
  module NoBrainer::Document::Types
2
2
  extend ActiveSupport::Concern
3
3
 
4
+ mattr_accessor :loaded_extensions
5
+ self.loaded_extensions = Set.new
6
+
4
7
  included { before_validation :add_type_errors }
5
8
 
6
9
  def add_type_errors
@@ -111,8 +114,6 @@ module NoBrainer::Document::Types
111
114
  end
112
115
 
113
116
  class << self
114
- mattr_accessor :loaded_extensions
115
- self.loaded_extensions = Set.new
116
117
  def load_type_extensions(model)
117
118
  unless loaded_extensions.include?(model)
118
119
  begin
@@ -1 +1 @@
1
- # Look in lib/no_brainer/geo.rb instead
1
+ # Look in lib/no_brainer/geo/*.rb instead
@@ -4,9 +4,8 @@ module NoBrainer::Document::Validation::Core
4
4
  include ActiveModel::Validations::Callbacks
5
5
 
6
6
  included do
7
- # We don't want before_validation returning false to halt the chain.
8
- define_callbacks :validation, :skip_after_callbacks_if_terminated => true,
9
- :scope => [:kind, :name], :terminator => proc { false }
7
+ define_callbacks :validation, NoBrainer::Document::Callbacks.define_callbacks_options(
8
+ :skip_after_callbacks_if_terminated => true, :scope => [:kind, :name])
10
9
  end
11
10
 
12
11
  def valid?(context=nil, options={})
@@ -8,7 +8,7 @@ class NoBrainer::Lock
8
8
  # Since PKs are limited to 127 characters, we can't use the user's key as a PK
9
9
  # as it could be arbitrarily long.
10
10
  field :key_hash, :type => String, :primary_key => true, :default => ->{ Digest::SHA1.base64digest(key.to_s) }
11
- field :key, :type => String
11
+ field :key, :type => Text
12
12
  field :instance_token, :type => String, :default => ->{ get_new_instance_token }
13
13
  field :expires_at, :type => Time
14
14
 
@@ -27,6 +27,7 @@ class NoBrainer::Lock
27
27
  @default_options = options.slice(:expire, :timeout)
28
28
  options.delete(:expire); options.delete(:timeout);
29
29
 
30
+ key = key.to_s if key.is_a?(Symbol)
30
31
  super(options.merge(:key => key))
31
32
  raise ArgumentError unless valid?
32
33
  end
@@ -16,7 +16,8 @@ class NoBrainer::QueryRunner::Reconnect < NoBrainer::QueryRunner::Middleware
16
16
  when RethinkDB::RqlError
17
17
  e.message =~ /lost contact/ ||
18
18
  e.message =~ /(P|p)rimary .* not available/||
19
- e.message =~ /Connection.*closed/
19
+ e.message =~ /Connection.*closed/ ||
20
+ e.message =~ /Connection.*refused/
20
21
  else
21
22
  false
22
23
  end
@@ -10,7 +10,10 @@ class NoBrainer::QueryRunner::RunOptions < NoBrainer::QueryRunner::Middleware
10
10
  end
11
11
 
12
12
  def self.current_run_options
13
- Thread.current[:nobrainer_run_with] || {}
13
+ options = NoBrainer::Config.run_options
14
+ options = options.merge(:durability => NoBrainer::Config.durability) if NoBrainer::Config.durability
15
+ options = options.merge(Thread.current[:nobrainer_run_with]) if Thread.current[:nobrainer_run_with]
16
+ options
14
17
  end
15
18
 
16
19
  def self.run_with(options={}, &block)
@@ -29,17 +32,9 @@ class NoBrainer::QueryRunner::RunOptions < NoBrainer::QueryRunner::Middleware
29
32
  end
30
33
 
31
34
  def call(env)
32
- options = env[:options].symbolize_keys
33
- options = self.class.current_run_options.merge(options)
34
-
35
- if NoBrainer::Config.durability.to_s != 'hard'
36
- options[:durability] ||= NoBrainer::Config.durability
37
- end
38
-
39
- options[:db] = options[:db].to_s if options[:db]
40
- if options[:db].blank? || options[:db] == NoBrainer.default_db
41
- options.delete(:db)
42
- end
35
+ options = self.class.current_run_options
36
+ options = options.merge(env[:options].symbolize_keys)
37
+ options = prune_default_run_options(options)
43
38
 
44
39
  env[:criteria] = options.delete(:criteria)
45
40
 
@@ -52,4 +47,14 @@ class NoBrainer::QueryRunner::RunOptions < NoBrainer::QueryRunner::Middleware
52
47
  env[:options] = options
53
48
  @runner.call(env)
54
49
  end
50
+
51
+ def prune_default_run_options(options)
52
+ options = options.dup
53
+ options.delete(:durability) if options[:durability].to_s == 'hard'
54
+
55
+ options[:db] = options[:db].to_s if options[:db]
56
+ options.delete(:db) if options[:db].blank? || options[:db] == NoBrainer.default_db
57
+
58
+ options
59
+ end
55
60
  end
@@ -34,6 +34,16 @@ class NoBrainer::QueryRunner::TableOnDemand < NoBrainer::QueryRunner::Middleware
34
34
  r.table_create(table_name, create_options.reject { |k,_| k.in? [:name, :write_acks] })
35
35
  end
36
36
 
37
+ # Prevent duplicate table errors on a cluster.
38
+ # Workaround from https://github.com/rethinkdb/rethinkdb/issues/4898#issuecomment-270267740
39
+ NoBrainer.run(:db => 'rethinkdb') do |r|
40
+ r.table('table_config')
41
+ .filter({db: db_name, name: table_name})
42
+ .order_by('id')
43
+ .slice(1)
44
+ .delete
45
+ end
46
+
37
47
  if create_options[:write_acks] && create_options[:write_acks] != 'single'
38
48
  NoBrainer.run(:db => db_name) do |r|
39
49
  r.table(table_name).config().update(:write_acks => create_options[:write_acks])
@@ -26,8 +26,10 @@ class NoBrainer::Railtie < Rails::Railtie
26
26
  config.after_initialize do
27
27
  NoBrainer::Config.configure unless NoBrainer::Config.configured?
28
28
 
29
- ActionDispatch::Reloader.to_prepare do
30
- NoBrainer::Loader.cleanup
29
+ if NoBrainer.rails5?
30
+ ActiveSupport::Reloader.before_class_unload { NoBrainer::Loader.cleanup }
31
+ else
32
+ ActionDispatch::Reloader.to_prepare { NoBrainer::Loader.cleanup }
31
33
  end
32
34
  end
33
35
 
@@ -7,4 +7,5 @@ class NoBrainer::System::TableConfig
7
7
  field :primary_key
8
8
  field :shards
9
9
  field :write_acks
10
+ field :indexes
10
11
  end
data/lib/nobrainer.rb CHANGED
@@ -37,6 +37,14 @@ module NoBrainer
37
37
  RUBY_PLATFORM == 'java'
38
38
  end
39
39
 
40
+ def rails5?
41
+ Gem.loaded_specs['activesupport'].version >= Gem::Version.new('5.0.0.beta')
42
+ end
43
+
44
+ def rails6?
45
+ Gem.loaded_specs['activesupport'].version >= Gem::Version.new('6.0.0')
46
+ end
47
+
40
48
  def eager_load!
41
49
  # XXX This forces all the NoBrainer code to be loaded in memory.
42
50
  # Not to be confused with eager_load() that operates on documents.
@@ -34,12 +34,17 @@ module NoBrainer::Generators
34
34
  end
35
35
 
36
36
  def remove_active_record
37
- (Dir['config/environments/*'] + ['config/application.rb']).each do |config_file|
37
+ (Dir['config/environments/*'] +
38
+ Dir['config/initializers/*'] +
39
+ ['config/application.rb']).each do |config_file|
38
40
  comment_lines(config_file, /active_record/)
39
41
  end
40
- remove_file('config/database.yml')
41
- end
42
42
 
43
+ (Dir['config/**/*active_record*.rb'] +
44
+ Dir['app/models/application_record.rb'] +
45
+ ['config/database.yml'])
46
+ .each { |f| remove_file(f) }
47
+ end
43
48
 
44
49
  def copy_initializer
45
50
  template('nobrainer.rb', 'config/initializers/nobrainer.rb')
@@ -13,9 +13,8 @@ NoBrainer.configure do |config|
13
13
  # The default is to use localhost, with a database name matching the
14
14
  # application name and the environment.
15
15
  # NoBrainer also reads environment variables when defined:
16
- # * RETHINKDB_URL, RDB_URL
17
- # * RETHINKDB_HOST, RETHINKDB_PORT, RETHINKDB_DB, RETHINKDB_AUTH
18
- # * RDB_HOST, RDB_PORT, RDB_DB, RDB_AUTH
16
+ # * RDB_URL, RDB_USER, RDB_PASSWORD, RDB_HOST, RDB_PORT, RDB_DB
17
+ # * All the above, but with RETHINKDB instead of RDB
19
18
  # config.rethinkdb_urls = [config.default_rethinkdb_url]
20
19
 
21
20
  # ssl_options may be set to {:ca_certs => '/path/to/ca.crt'} to establish
@@ -43,9 +42,10 @@ NoBrainer.configure do |config|
43
42
  # You can turn off the warning if you want to use both.
44
43
  # config.warn_on_active_record = true
45
44
 
46
- # Configures the durability for database writes.
47
- # The default is :soft for development or test environment, otherwise :hard.
48
- # config.durability = config.default_durability
45
+ # Configures the run options passed to r.run() when executing queries.
46
+ # The options are listed in the RethinkDB run() documentation.
47
+ # The default durability is :soft for development or test environments, otherwise :hard.
48
+ # config.run_options = { :durability => config.default_durability }
49
49
 
50
50
  # Configures the default table configuration options. These values are
51
51
  # reflected to the database when running `rake nobrainer:sync_schema'.
@@ -56,17 +56,17 @@ NoBrainer.configure do |config|
56
56
  # config.max_string_length = 255
57
57
 
58
58
  # user_timezone can be configured with :utc, :local, or :unchanged.
59
- # When reading an attribute from a model which type is Time, the timezone
60
- # of that time is translated according to this setting.
59
+ # When reading a Time attribute from a model, the timezone of that attribute
60
+ # is set according to the following setting.
61
61
  # config.user_timezone = :local
62
62
 
63
63
  # db_timezone can be configured with :utc, :local, or :unchanged.
64
- # When writting to the database, the timezone of Time attributes are
65
- # translated according to this setting.
64
+ # When writing a Time attribute into the database, the timezone of that
65
+ # attribute is set according to the following setting.
66
66
  # config.db_timezone = :utc
67
67
 
68
68
  # Default options used when compiling geo queries.
69
- # config.geo_options => { :geo_system => 'WGS84', :unit => 'm' }
69
+ # config.geo_options = { :geo_system => 'WGS84', :unit => 'm' }
70
70
 
71
71
  # Configures which mechanism to use in order to perform non-racy uniqueness
72
72
  # validations. More about this behavior in the Distributed Locks section.
@@ -90,7 +90,7 @@ NoBrainer.configure do |config|
90
90
  # generated without conflicts.
91
91
  # config.machine_id = config.default_machine_id
92
92
 
93
- # Criteria cache elements. For example, the result of a has_many association
93
+ # Criteria cache documents. For example, the result of a has_many association
94
94
  # is cached. The per criteria cache is disabled if it grows too big to avoid
95
95
  # out of memory issues.
96
96
  # config.criteria_cache_max_entries = 10_000
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nobrainer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 0.34.1
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-10-03 00:00:00.000000000 Z
11
+ date: 2021-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rethinkdb
14
+ name: activemodel
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 2.1.0
19
+ version: 4.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.1.0
26
+ version: 4.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -39,33 +39,33 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: 4.1.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: activemodel
42
+ name: middleware
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 4.1.0
47
+ version: 0.1.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 4.1.0
54
+ version: 0.1.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: middleware
56
+ name: rethinkdb
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 0.1.0
61
+ version: 2.3.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 0.1.0
68
+ version: 2.3.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: symbol_decoration
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,13 +80,15 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.1'
83
- description: ORM for RethinkDB
83
+ description: The goal of NoBrainer is to provide a similar interface compared to ActiveRecord
84
+ and Mongoid to build data models on top of RethinkDB while providing precise semantics.
84
85
  email:
85
86
  - nicolas@viennot.biz
86
87
  executables: []
87
88
  extensions: []
88
89
  extra_rdoc_files: []
89
90
  files:
91
+ - CHANGELOG.md
90
92
  - LICENSE
91
93
  - README.md
92
94
  - lib/no_brainer/autoload.rb
@@ -236,10 +238,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
236
238
  - !ruby/object:Gem::Version
237
239
  version: '0'
238
240
  requirements: []
239
- rubyforge_project:
240
- rubygems_version: 2.4.6
241
+ rubygems_version: 3.1.4
241
242
  signing_key:
242
243
  specification_version: 4
243
- summary: ORM for RethinkDB
244
+ summary: A Ruby ORM for RethinkDB
244
245
  test_files: []
245
- has_rdoc: false