nobrainer 0.25.1 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 71e9e6216a8508d483d0c0769b6d8f5137b5f2c2
4
- data.tar.gz: 91cc415c1d35b3120fc94edfc322bdc30652aa30
3
+ metadata.gz: 22202d66d5f511c228b92b0a93df97628dd2ad6d
4
+ data.tar.gz: 28b596c4b83b83300add95b5f9971e4a3c9ceaf8
5
5
  SHA512:
6
- metadata.gz: b4a4656ab19c63e12e194b3c90dd7f25ffc806cfefee029a352e22deccca5908029bf06f14af097d8ebd069ef5eb0a923550d7411dc520fdb2fa8ef2e34378d6
7
- data.tar.gz: 37f77aef1ca56aeda705659162bab344f4c1e6495ea0c9fe5682ac7a3276eaee642b9313153ecbb80caf839054a499db19c3dd20eb144820ea0a9a97cf1e7f50
6
+ metadata.gz: df558fdce2a60deee0e500766a7963769ea6514fb1f97d6129c7d49eb66579f3b45990d9dca0b2506e0e8bb55940a826d1acf33e97233b8162cd4b53ec377862
7
+ data.tar.gz: 59ef60554676a4eff6919a3168d91803a6c11897cfa7073d52da262c4047640d7de6879a062c086994ea0a6f7fec4e56804ce6b46f444a86777c8151bbc667f5
@@ -11,9 +11,29 @@ module NoBrainer::ConnectionManager
11
11
  end
12
12
  end
13
13
 
14
+ def warn_for_other_orms
15
+ if defined?(ActiveRecord) && NoBrainer::Config.warn_on_active_record
16
+ STDERR.puts "[NoBrainer] ActiveRecord is loaded which is probably not what you want."
17
+ STDERR.puts "[NoBrainer] Follow the instructions on http://nobrainer.io/docs/configuration/#removing_activerecord"
18
+ STDERR.puts "[NoBrainer] Configure NoBrainer with 'config.warn_on_active_record = false' to disable with warning."
19
+ end
20
+
21
+ if defined?(Mongoid)
22
+ STDERR.puts "[NoBrainer] WARNING: Mongoid is loaded, and we conflict on the symbol decorations"
23
+ STDERR.puts "[NoBrainer] They are used in queries such as Model.where(:tags.in => ['fun', 'stuff'])"
24
+ STDERR.puts "[NoBrainer] This is a problem!"
25
+ end
26
+ end
27
+
14
28
  def get_new_connection
15
29
  url = NoBrainer::Config.rethinkdb_url
16
30
  raise "Please specify a database connection to RethinkDB" unless url
31
+
32
+ # We don't want to warn on "rails g nobrainer:install", but because it's
33
+ # hard to check when the generator is running because of spring as it wipes
34
+ # ARGV. So we check for other ORMs during the connection instantiation.
35
+ warn_for_other_orms
36
+
17
37
  NoBrainer::Connection.new(url)
18
38
  end
19
39
 
@@ -56,14 +56,20 @@ module NoBrainer::Document::AtomicOps
56
56
 
57
57
  def compile_rql_value(rql_doc)
58
58
  field = @instance.class.lookup_field_alias(@field)
59
- value = @is_user_value ? RethinkDB::RQL.new.expr(@value) : rql_doc[field]
59
+ if @is_user_value
60
+ casted_value = @instance.class.cast_model_to_db_for(@field, @value)
61
+ value = RethinkDB::RQL.new.expr(casted_value)
62
+ else
63
+ value = rql_doc[field]
64
+ end
60
65
  value = value.default(default_value) if default_value
61
66
  @ops.reduce(value) { |v, (method, a, b)| v.__send__(method, *a, &b) }
62
67
  end
63
68
 
64
69
  def modify_source!
65
- unless @instance._is_attribute_touched?(@field)
66
- @instance.write_attribute(@field, self)
70
+ if (@is_user_value && @instance.instance_eval { @_attributes[@field].equal?(@value) }) ||
71
+ !@instance._is_attribute_touched?(@field)
72
+ @instance._write_attribute(@field, self)
67
73
  end
68
74
  end
69
75
  end
@@ -1,10 +1,11 @@
1
1
  module NoBrainer::Document::Dirty
2
2
  extend ActiveSupport::Concern
3
- # We need to save the changes as seen through read_attribute because
4
- # the user sees attributes through the read_attribute getters.
5
- # But we want to detect changes based on @_attributes to track
6
- # things like undefined -> nil. Going through the getters will
7
- # not give us that.
3
+ # 1) We should save the changes as seen through read_attribute, because the
4
+ # user sees attributes through the read_attribute getters, but it's near
5
+ # impossible because we would need to wrap the user defined getters, so we'll
6
+ # go through _read_attribute.
7
+ # 2) We want to detect changes based on @_attributes to track things like
8
+ # undefined -> nil. Going through the getters will not give us that.
8
9
 
9
10
  def _create(*args)
10
11
  super.tap { clear_dirtiness }
@@ -36,7 +37,7 @@ module NoBrainer::Document::Dirty
36
37
  def changes
37
38
  result = {}.with_indifferent_access
38
39
  @_old_attributes.each do |attr, old_value|
39
- current_value = read_attribute(attr)
40
+ current_value = _read_attribute(attr)
40
41
  if current_value != old_value || !@_old_attributes_keys.include?(attr)
41
42
  result[attr] = [old_value, current_value]
42
43
  end
@@ -49,7 +50,7 @@ module NoBrainer::Document::Dirty
49
50
  if current_value == None
50
51
  current_value = begin
51
52
  assert_access_field(attr)
52
- read_attribute(attr)
53
+ _read_attribute(attr)
53
54
  rescue NoBrainer::Error::MissingAttribute => e
54
55
  e
55
56
  end
@@ -81,7 +82,7 @@ module NoBrainer::Document::Dirty
81
82
  inject_in_layer :dirty_tracking do
82
83
  define_method("#{attr}_change") do
83
84
  if @_old_attributes.has_key?(attr)
84
- result = [@_old_attributes[attr], read_attribute(attr)]
85
+ result = [@_old_attributes[attr], _read_attribute(attr)]
85
86
  result if result.first != result.last || !@_old_attributes_keys.include?(attr)
86
87
  end
87
88
  end
@@ -91,7 +92,7 @@ module NoBrainer::Document::Dirty
91
92
  end
92
93
 
93
94
  define_method("#{attr}_was") do
94
- @_old_attributes.has_key?(attr) ? @_old_attributes[attr] : read_attribute(attr)
95
+ @_old_attributes.has_key?(attr) ? @_old_attributes[attr] : _read_attribute(attr)
95
96
  end
96
97
  end
97
98
  end
@@ -48,12 +48,22 @@ class NoBrainer::Document::Index::Synchronizer
48
48
  end
49
49
 
50
50
  def sync_indexes(options={})
51
- plan = generate_plan
52
- plan.each { |op| op.run(options) }
51
+ lock = NoBrainer::Lock.new('nobrainer:sync_indexes')
52
+
53
+ lock.synchronize do
54
+ plan = generate_plan
55
+ plan.each { |op| lock.refresh; op.run(options) }
56
+ end
57
+
53
58
  unless options[:wait] == false
54
- models = plan.map(&:index).map(&:model).uniq
55
- models.each { |model| NoBrainer.run(model.rql_table.index_wait()) }
59
+ # Waiting on all models due to possible races when performing concurrent
60
+ # sync_indexes()
61
+ @models_indexes_map.each_key do |model|
62
+ NoBrainer.run(model.rql_table.index_wait())
63
+ end
56
64
  end
65
+
66
+ true
57
67
  end
58
68
 
59
69
  class << self
@@ -1,6 +1,9 @@
1
1
  module NoBrainer::Document::Validation::Uniqueness
2
2
  extend ActiveSupport::Concern
3
3
 
4
+ # XXX we don't use read_attribute_for_validation, which goes through the user
5
+ # getters, but read internal attributes instead. It makes more sense.
6
+
4
7
  def save?(options={})
5
8
  lock_unique_fields
6
9
  super
@@ -22,7 +25,7 @@ module NoBrainer::Document::Validation::Uniqueness
22
25
  self.class.unique_validators
23
26
  .flat_map { |validator| validator.attributes.map { |attr| [attr, validator] } }
24
27
  .select { |f, validator| validator.should_validate_field?(self, f) }
25
- .map { |f, validator| [f, *validator.scope].map { |k| [k, read_attribute(k)] } }
28
+ .map { |f, validator| [f, *validator.scope].map { |k| [k, _read_attribute(k)] } }
26
29
  .map { |params| self.class._uniqueness_key_name_from_params(params) }
27
30
  .sort.each { |key| _lock_for_uniqueness_once(key) }
28
31
  end
@@ -79,7 +82,7 @@ module NoBrainer::Document::Validation::Uniqueness
79
82
  end
80
83
 
81
84
  def apply_scopes(criteria, doc)
82
- criteria.where(scope.map { |k| {k => doc.read_attribute(k)} })
85
+ criteria.where(scope.map { |k| {k => doc._read_attribute(k)} })
83
86
  end
84
87
 
85
88
  def exclude_doc(criteria, doc)
@@ -35,7 +35,7 @@ class NoBrainer::Lock
35
35
  begin
36
36
  block.call
37
37
  ensure
38
- unlock
38
+ unlock if @locked
39
39
  end
40
40
  end
41
41
 
@@ -36,9 +36,8 @@ class NoBrainer::QueryRunner::Reconnect < NoBrainer::QueryRunner::Middleware
36
36
  Errno::ECONNRESET, Errno::ETIMEDOUT, IOError
37
37
  true
38
38
  when RethinkDB::RqlRuntimeError
39
- e.message =~ /No master available/ ||
40
- e.message =~ /Master .* not available/ ||
41
- e.message =~ /Error: Connection Closed/
39
+ e.message =~ /Primary .* not available/ ||
40
+ e.message =~ /Connection.*closed/
42
41
  else
43
42
  false
44
43
  end
@@ -26,18 +26,6 @@ class NoBrainer::Railtie < Rails::Railtie
26
26
  config.after_initialize do
27
27
  NoBrainer::Config.configure unless NoBrainer::Config.configured?
28
28
 
29
- if defined?(ActiveRecord) && NoBrainer::Config.warn_on_active_record
30
- STDERR.puts "[NoBrainer] ActiveRecord is loaded which is probably not what you want."
31
- STDERR.puts "[NoBrainer] Follow the instructions on http://nobrainer.io/docs/configuration/#removing_activerecord"
32
- STDERR.puts "[NoBrainer] Configure NoBrainer with 'config.warn_on_active_record = false' to disable with warning."
33
- end
34
-
35
- if defined?(Mongoid)
36
- STDERR.puts "[NoBrainer] WARNING: Mongoid is loaded, and we conflict on the symbol decorations"
37
- STDERR.puts "[NoBrainer] They are used in queries such as Model.where(:tags.in => ['fun', 'stuff'])"
38
- STDERR.puts "[NoBrainer] This is a problem!"
39
- end
40
-
41
29
  ActionDispatch::Reloader.to_prepare do
42
30
  NoBrainer::Loader.cleanup
43
31
  end
@@ -13,19 +13,19 @@ module NoBrainer::RQL
13
13
  RethinkDB::RQL.new.new_func(&block)))
14
14
  end
15
15
 
16
- def is_write_query?(rql_query)
17
- type_of(rql_query) == :write
16
+ def is_write_query?(rql)
17
+ type_of(rql) == :write
18
18
  end
19
19
 
20
- def type_of(rql_query)
21
- case rql_query.body.first
20
+ def type_of(rql)
21
+ case rql.is_a?(RethinkDB::RQL) && rql.body.is_a?(Array) && rql.body.first
22
22
  when UPDATE, DELETE, REPLACE, INSERT
23
23
  :write
24
24
  when DB_CREATE, DB_DROP, DB_LIST, TABLE_CREATE, TABLE_DROP, TABLE_LIST, SYNC,
25
25
  INDEX_CREATE, INDEX_DROP, INDEX_LIST, INDEX_STATUS, INDEX_WAIT
26
26
  :management
27
27
  else
28
- # XXX Not sure if that's correct, but we'll be happy for logging colors.
28
+ # XXX Not necessarily correct, but we'll be happy for logging colors.
29
29
  :read
30
30
  end
31
31
  end
@@ -0,0 +1,48 @@
1
+ require "rails/generators/nobrainer/namespace_fix"
2
+ require 'rails/generators/base'
3
+
4
+ module NoBrainer::Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ extend NoBrainer::Generators::NamespaceFix
7
+ source_root File.expand_path("../../templates", __FILE__)
8
+
9
+ desc "Disable ActiveRecord and generates ./config/initializer/nobrainer.rb"
10
+
11
+ class RequireProxy
12
+ attr_accessor :required_paths
13
+ def initialize
14
+ self.required_paths = []
15
+ end
16
+
17
+ def require(path)
18
+ self.required_paths << path
19
+ end
20
+
21
+ def resolve_require_path(path)
22
+ $:.map { |dir| File.join(dir, path) }.detect { |f| File.exists?(f) }
23
+ end
24
+ end
25
+
26
+ def expand_require_rails_all
27
+ require_proxy = RequireProxy.new
28
+ rails_all_file = require_proxy.resolve_require_path('rails/all.rb')
29
+ require_proxy.instance_eval(File.read(rails_all_file))
30
+
31
+ gsub_file('config/application.rb', %r(^require 'rails/all'$)) do
32
+ require_proxy.required_paths.map { |f| "require '#{f}'" }.join("\n")
33
+ end
34
+ end
35
+
36
+ def remove_active_record
37
+ (Dir['config/environments/*'] + ['config/application.rb']).each do |config_file|
38
+ comment_lines(config_file, /active_record/)
39
+ end
40
+ remove_file('config/database.yml')
41
+ end
42
+
43
+
44
+ def copy_initializer
45
+ template('nobrainer.rb', 'config/initializer/nobrainer.rb')
46
+ end
47
+ end
48
+ end
@@ -1,7 +1,11 @@
1
- require "rails/generators/nobrainer"
1
+ require "rails/generators/nobrainer/namespace_fix"
2
+ require 'rails/generators/named_base'
2
3
 
3
4
  module NoBrainer::Generators
4
- class ModelGenerator < Base
5
+ class ModelGenerator < Rails::Generators::NamedBase
6
+ extend NoBrainer::Generators::NamespaceFix
7
+ source_root File.expand_path("../../templates", __FILE__)
8
+
5
9
  argument(:attributes, :type => :array, default: [],
6
10
  banner: "field[:type][:index] ... field[:type][:index]")
7
11
 
@@ -10,7 +14,7 @@ module NoBrainer::Generators
10
14
  class_option :parent, :type => :string, :desc => "The parent class for the generated model"
11
15
 
12
16
  def create_model_file
13
- template "model.rb.tt", File.join("app/models", class_path, "#{file_name}.rb")
17
+ template "model.rb", File.join("app", "models", class_path, "#{file_name}.rb")
14
18
  end
15
19
 
16
20
  hook_for :test_framework
@@ -0,0 +1,15 @@
1
+ module NoBrainer::Generators
2
+ module NamespaceFix
3
+ def base_name
4
+ 'nobrainer'
5
+ end
6
+
7
+ def base_root
8
+ File.dirname(__FILE__)
9
+ end
10
+
11
+ def namespace
12
+ super.gsub(/no_brainer/, 'nobrainer')
13
+ end
14
+ end
15
+ end
@@ -7,7 +7,7 @@ class <%= class_name %><%= " < #{options[:parent].classify}" if options[:parent]
7
7
  <% end -%>
8
8
  <% attributes.reject(&:reference?).each do |attribute| -%>
9
9
  field :<%= attribute.name -%>
10
- <%= puts attribute; ", :type => #{attribute.type.to_s.classify}" if attribute.type != :object -%>
10
+ <%= ", :type => #{attribute.type.to_s.classify}" if attribute.type != :object -%>
11
11
  <%= ", :index => true" if attribute.has_index? %>
12
12
  <% end -%>
13
13
  <% attributes.select(&:reference?).each do |attribute| -%>
@@ -0,0 +1,92 @@
1
+ NoBrainer.configure do |config|
2
+ # app_name is the name of your application in lowercase.
3
+ # When using Rails, the application name is automatically inferred.
4
+ # config.app_name = config.default_app_name
5
+
6
+ # environment defaults to Rails.env for Rails apps or to the environment
7
+ # variables RUBY_ENV, RAILS_ENV, RACK_ENV, or :production.
8
+ # config.environment = config.default_environment
9
+
10
+ # The rethinkdb_url specifies the RethinkDB database connection url.
11
+ # When left unspecified, NoBrainer picks a database connection by default.
12
+ # The default is to use localhost, with a database name matching the
13
+ # application name and the environment.
14
+ # NoBrainer also reads environment variables when defined:
15
+ # * RETHINKDB_URL, RDB_URL
16
+ # * RETHINKDB_HOST, RETHINKDB_PORT, RETHINKDB_DB, RETHINKDB_AUTH
17
+ # * RDB_HOST, RDB_PORT, RDB_DB, RDB_AUTH
18
+ # config.rethinkdb_url = config.default_rethinkdb_url
19
+
20
+ # NoBrainer uses logger to emit debugging information.
21
+ # The default logger is the Rails logger if run with Rails,
22
+ # otherwise Logger.new(STDERR) with a WARN level.
23
+ # If the logger is configured with a DEBUG level,
24
+ # then each database query is emitted.
25
+ # config.logger = config.default_logger
26
+
27
+ # NoBrainer will colorize the queries if colorize_logger is true.
28
+ # Specifically, NoBrainer will colorize management RQL queries in yellow,
29
+ # write queries in red and read queries in green.
30
+ # config.colorize_logger = true
31
+
32
+ # You probably do not want to use both NoBrainer and ActiveRecord in your
33
+ # application. NoBrainer will emit a warning if you do so.
34
+ # You can turn off the warning if you want to use both.
35
+ # config.warn_on_active_record = true
36
+
37
+ # When the network connection is lost, NoBrainer can retry running a given
38
+ # query a few times before giving up. Note that this can be a problem with
39
+ # non idempotent write queries such as increments.
40
+ # Setting it to 0 disable retries during reconnections.
41
+ # The default is 1 for development or test environment, otherwise 15.
42
+ # config.max_retries_on_connection_failure = \
43
+ # config.default_max_retries_on_connection_failure
44
+
45
+ # Configures the durability for database writes.
46
+ # The default is :soft for development or test environment, otherwise :hard.
47
+ # config.durability = config.default_durability
48
+
49
+ # Persisted Strings have a configurable maximum length. To get rid of the
50
+ # length validation, you may use the Text type instead.
51
+ # config.max_string_length = 255
52
+
53
+ # user_timezone can be configured with :utc, :local, or :unchanged.
54
+ # When reading an attribute from a model which type is Time, the timezone
55
+ # of that time is translated according to this setting.
56
+ # config.user_timezone = :local
57
+
58
+ # db_timezone can be configured with :utc, :local, or :unchanged.
59
+ # When writting to the database, the timezone of Time attributes are
60
+ # translated according to this setting.
61
+ # config.db_timezone = :utc
62
+
63
+ # Default options used when compiling geo queries.
64
+ # config.geo_options => { :geo_system => 'WGS84', :unit => 'm' }
65
+
66
+ # Configures which mechanism to use in order to perform non-racy uniqueness
67
+ # validations. More about this behavior in the Distributed Locks section.
68
+ # config.distributed_lock_class = NoBrainer::Lock
69
+
70
+ # Configures the default timing lock options.
71
+ # config.lock_options = { :expire => 60, :timeout => 10 }
72
+
73
+ # Instead of using a single connection to the database, You can tell
74
+ # NoBrainer to spin up a new connection for each thread. This is
75
+ # useful for multi-threading usage such as Sidekiq.
76
+ # Call NoBrainer.disconnect before a thread exits, otherwise you will have
77
+ # a resource leak, and you will run out of connections.
78
+ # Note that this is solution is temporary, until we get a connection pool.
79
+ # config.per_thread_connection = false
80
+
81
+ # The machine id is used to generate primary keys. The default one is seeded
82
+ # with the machine IP with Socket.gethostname.
83
+ # The env variable MACHINE_ID can also be used to set the machine id.
84
+ # When using distinct machine_id, then primary keys are guaranteed to be
85
+ # generated without conflicts.
86
+ # config.machine_id = config.default_machine_id
87
+
88
+ # Criteria cache elements. For example, the result of a has_many association
89
+ # is cached. The per criteria cache is disabled if it grows too big to avoid
90
+ # out of memory issues.
91
+ # config.criteria_cache_max_entries = 10_000
92
+ end
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.25.1
4
+ version: 0.26.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-06-01 00:00:00.000000000 Z
11
+ date: 2015-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rethinkdb
@@ -192,9 +192,11 @@ files:
192
192
  - lib/no_brainer/railtie/database.rake
193
193
  - lib/no_brainer/rql.rb
194
194
  - lib/nobrainer.rb
195
- - lib/rails/generators/nobrainer.rb
196
- - lib/rails/generators/nobrainer/model/model_generator.rb
197
- - lib/rails/generators/nobrainer/model/templates/model.rb.tt
195
+ - lib/rails/generators/nobrainer/install_generator.rb
196
+ - lib/rails/generators/nobrainer/model_generator.rb
197
+ - lib/rails/generators/nobrainer/namespace_fix.rb
198
+ - lib/rails/generators/templates/model.rb
199
+ - lib/rails/generators/templates/nobrainer.rb
198
200
  homepage: http://nobrainer.io
199
201
  licenses:
200
202
  - LGPLv3
@@ -1,18 +0,0 @@
1
- require 'rails/generators/named_base'
2
- require 'nobrainer'
3
-
4
- module NoBrainer::Generators
5
- class Base < Rails::Generators::NamedBase
6
- def self.base_name
7
- 'nobrainer'
8
- end
9
-
10
- def self.base_root
11
- File.dirname(__FILE__)
12
- end
13
-
14
- def self.namespace
15
- super.gsub(/no_brainer/, 'nobrainer')
16
- end
17
- end
18
- end