nobrainer 0.26.0 → 0.27.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: 22202d66d5f511c228b92b0a93df97628dd2ad6d
4
- data.tar.gz: 28b596c4b83b83300add95b5f9971e4a3c9ceaf8
3
+ metadata.gz: cc50cb3ae605543bb7ed3df448d0ff32d3b984d4
4
+ data.tar.gz: b8ae438c48a0d64408f4966f92002be17f6cdff0
5
5
  SHA512:
6
- metadata.gz: df558fdce2a60deee0e500766a7963769ea6514fb1f97d6129c7d49eb66579f3b45990d9dca0b2506e0e8bb55940a826d1acf33e97233b8162cd4b53ec377862
7
- data.tar.gz: 59ef60554676a4eff6919a3168d91803a6c11897cfa7073d52da262c4047640d7de6879a062c086994ea0a6f7fec4e56804ce6b46f444a86777c8151bbc667f5
6
+ metadata.gz: 118724ce72e1c29432aa208b062f9e107b56dc0401d921f5194708a2c3f2b7a95160572cf2cd83cb22f322cad0fb7cfe3939fdccf8eb6ef401da99f975df275b
7
+ data.tar.gz: 2d5737b1035583abd7f888f61341b0922d85e654b919aee2c282be2fcd07bb144f3654ef76ad2d016289b5692766d72d7d0e7620f8d13a6923d054acab49de91
@@ -14,7 +14,7 @@ module NoBrainer::Autoload
14
14
  end
15
15
 
16
16
  def autoload_and_include(*constants)
17
- constants.each { |constant| autoload constant }
17
+ autoload(*constants)
18
18
  constants.each { |constant| include const_get(constant) }
19
19
  end
20
20
  end
@@ -14,7 +14,7 @@ module NoBrainer::Config
14
14
  :user_timezone => { :default => ->{ :local }, :valid_values => [:unchanged, :utc, :local] },
15
15
  :db_timezone => { :default => ->{ :utc }, :valid_values => [:unchanged, :utc, :local] },
16
16
  :geo_options => { :default => ->{ {:geo_system => 'WGS84', :unit => 'm'} } },
17
- :distributed_lock_class => { :default => ->{ NoBrainer::Lock } },
17
+ :distributed_lock_class => { :default => ->{ "NoBrainer::Lock" } },
18
18
  :lock_options => { :default => ->{ { :expire => 60, :timeout => 10 } } },
19
19
  :per_thread_connection => { :default => ->{ false }, :valid_values => [true, false] },
20
20
  :machine_id => { :default => ->{ default_machine_id } },
@@ -58,7 +58,7 @@ module NoBrainer::Config
58
58
  assert_valid_options
59
59
  @configured = true
60
60
 
61
- NoBrainer::ConnectionManager.disconnect_if_url_changed
61
+ NoBrainer::ConnectionManager.notify_url_change
62
62
  end
63
63
 
64
64
  def configured?
@@ -107,18 +107,23 @@ module NoBrainer::Config
107
107
  dev_mode? ? 1 : 15
108
108
  end
109
109
 
110
+ # XXX Not referencing NoBrainer::Document::PrimaryKey::Generator::MACHINE_ID_MASK
111
+ # because we don't want to load all the document code to speedup boot time.
112
+ MACHINE_ID_BITS = 24
113
+ MACHINE_ID_MASK = (1 << MACHINE_ID_BITS)-1
114
+
110
115
  def default_machine_id
116
+ return ENV['MACHINE_ID'] if ENV['MACHINE_ID']
117
+
111
118
  require 'socket'
112
119
  require 'digest/md5'
113
120
 
114
- return ENV['MACHINE_ID'] if ENV['MACHINE_ID']
115
-
116
121
  host = Socket.gethostname
117
122
  if host.in? %w(127.0.0.1 localhost)
118
123
  raise "Please configure NoBrainer::Config.machine_id due to lack of appropriate hostname (Socket.gethostname = #{host})"
119
124
  end
120
125
 
121
- Digest::MD5.digest(host).unpack("N")[0] & NoBrainer::Document::PrimaryKey::Generator::MACHINE_ID_MASK
126
+ Digest::MD5.digest(host).unpack("N")[0] & MACHINE_ID_MASK
122
127
  end
123
128
 
124
129
  def machine_id=(machine_id)
@@ -127,9 +132,16 @@ module NoBrainer::Config
127
132
  when /^[0-9]+$/ then machine_id.to_i
128
133
  else raise "Invalid machine_id"
129
134
  end
130
- max_id = NoBrainer::Document::PrimaryKey::Generator::MACHINE_ID_MASK
135
+ max_id = MACHINE_ID_MASK
131
136
  raise "Invalid machine_id (must be between 0 and #{max_id})" unless machine_id.in?(0..max_id)
132
137
  @machine_id = machine_id
133
138
  end
139
+
140
+ def distributed_lock_class
141
+ if @distributed_lock_class.is_a?(String)
142
+ @distributed_lock_class = @distributed_lock_class.constantize
143
+ end
144
+ @distributed_lock_class
145
+ end
134
146
  end
135
147
  end
@@ -71,7 +71,7 @@ module NoBrainer::ConnectionManager
71
71
  synchronize { _disconnect }
72
72
  end
73
73
 
74
- def disconnect_if_url_changed
74
+ def notify_url_change
75
75
  synchronize do
76
76
  c = current_connection
77
77
  _disconnect if c && c.uri != NoBrainer::Config.rethinkdb_url
@@ -1,12 +1,4 @@
1
1
  module NoBrainer::Criteria::Where
2
- NON_CHAINABLE_OPERATORS = %w(in eq gt ge gte lt le lte defined near intersects).map(&:to_sym)
3
- CHAINABLE_OPERATORS = %w(not any all).map(&:to_sym)
4
- OPERATORS = CHAINABLE_OPERATORS + NON_CHAINABLE_OPERATORS
5
-
6
- require 'symbol_decoration'
7
- Symbol::Decoration.register(*NON_CHAINABLE_OPERATORS)
8
- Symbol::Decoration.register(*CHAINABLE_OPERATORS, :chainable => true)
9
-
10
2
  extend ActiveSupport::Concern
11
3
  include ActiveModel::ForbiddenAttributesProtection
12
4
 
@@ -28,11 +28,11 @@ module NoBrainer::Document::Association
28
28
  raise "Cannot change the :through option" unless r.options[:through] == options[:through]
29
29
  r.options.merge!(options)
30
30
  else
31
- model_name = (options[:through] ? "#{association}_through" : association.to_s).camelize
32
- metadata_model = NoBrainer::Document::Association.const_get(model_name).const_get(:Metadata)
33
- r = metadata_model.new(self, target, options)
34
- ([self] + descendants).each do |model|
35
- model.association_metadata[target] = r
31
+ class_name = (options[:through] ? "#{association}_through" : association.to_s).camelize
32
+ metadata_class = NoBrainer::Document::Association.const_get(class_name).const_get(:Metadata)
33
+ r = metadata_class.new(self, target, options)
34
+ subclass_tree.each do |subclass|
35
+ subclass.association_metadata[target] = r
36
36
  end
37
37
  end
38
38
  r.hook
@@ -11,7 +11,22 @@ class NoBrainer::Document::Association::BelongsTo
11
11
  end
12
12
 
13
13
  def primary_key
14
- options[:primary_key].try(:to_sym) || target_model.pk_name
14
+ # We default the primary_key to `:id' and not `target_model.pk_name',
15
+ # because we don't want to require the target_model to be already loaded.
16
+ # (We want the ability to load models in any order).
17
+ # Using target_model.pk_name and allowing lazy loading of models is
18
+ # difficult due to the inexistant API to remove validations if the
19
+ # foreign_key name was to be changed as the pk_name gets renamed.
20
+ return options[:primary_key].to_sym if options[:primary_key]
21
+
22
+ NoBrainer::Document::PrimaryKey::DEFAULT_PK_NAME.tap do |default_pk_name|
23
+ # We'll try to give a warning when we see a different target pk name (best effort).
24
+ real_pk_name = target_model.pk_name rescue nil
25
+ if real_pk_name && real_pk_name != default_pk_name
26
+ raise "Please specify the primary_key name on the following belongs_to association as such:\n" +
27
+ " belongs_to :#{target_name}, :primary_key => :#{real_pk_name}"
28
+ end
29
+ end
15
30
  end
16
31
 
17
32
  def target_model
@@ -24,11 +39,6 @@ class NoBrainer::Document::Association::BelongsTo
24
39
 
25
40
  def hook
26
41
  super
27
- # XXX We are loading the target_model unless the primary_key is specified.
28
- # This may eager load a part of the application Oh well.
29
-
30
- # TODO if the primary key of the target_model changes, we need to revisit
31
- # our default foreign_key/primary_key value
32
42
 
33
43
  # TODO set the type of the foreign key to be the same as the target's primary key
34
44
  if owner_model.association_metadata.values.any? { |assoc|
@@ -74,7 +84,7 @@ class NoBrainer::Document::Association::BelongsTo
74
84
  return target if loaded?
75
85
 
76
86
  if fk = owner.read_attribute(foreign_key)
77
- preload(target_model.unscoped.where(primary_key => fk).first)
87
+ preload(base_criteria.where(primary_key => fk).first)
78
88
  end
79
89
  end
80
90
 
@@ -33,7 +33,6 @@ class NoBrainer::Document::Association::EagerLoader
33
33
  docs = docs.compact
34
34
  return [] if docs.empty?
35
35
  meta = docs.first.root_class.association_metadata
36
- # TODO test the singularize thingy.
37
36
  association = meta[association_name.to_sym] || meta[association_name.to_s.singularize.to_sym]
38
37
  raise "Unknown association #{association_name}" unless association
39
38
  association.eager_load(docs, criteria)
@@ -1,8 +1,9 @@
1
1
  module NoBrainer::Document::Attributes
2
2
  VALID_FIELD_OPTIONS = [:index, :default, :type, :readonly, :primary_key, :lazy_fetch, :store_as,
3
3
  :validates, :required, :unique, :uniq, :format, :in, :length, :min_length, :max_length]
4
- RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations, :pk_value] \
5
- + NoBrainer::Criteria::Where::OPERATORS
4
+ RESERVED_FIELD_NAMES = [:index, :default, :and, :or, :selector, :associations, :pk_value] +
5
+ NoBrainer::SymbolDecoration::OPERATORS
6
+
6
7
  extend ActiveSupport::Concern
7
8
  include ActiveModel::ForbiddenAttributesProtection
8
9
 
@@ -129,9 +130,9 @@ module NoBrainer::Document::Attributes
129
130
  raise "Cannot use a reserved field attr: #{attr}"
130
131
  end
131
132
 
132
- ([self] + descendants).each do |model|
133
- model.fields[attr] ||= {}
134
- model.fields[attr].deep_merge!(options)
133
+ subclass_tree.each do |subclass|
134
+ subclass.fields[attr] ||= {}
135
+ subclass.fields[attr].deep_merge!(options)
135
136
  end
136
137
 
137
138
  _field(attr, self.fields[attr])
@@ -149,8 +150,8 @@ module NoBrainer::Document::Attributes
149
150
 
150
151
  _remove_field(attr, options)
151
152
 
152
- ([self] + descendants).each do |model|
153
- model.fields.delete(attr)
153
+ subclass_tree.each do |subclass|
154
+ subclass.fields.delete(attr)
154
155
  end
155
156
  end
156
157
 
@@ -51,8 +51,8 @@ module NoBrainer::Document::Criteria
51
51
  criteria_proc = block || (criteria.is_a?(Proc) ? criteria : proc { criteria })
52
52
  raise "default_scope only accepts a criteria or a proc that returns criteria" unless criteria_proc.is_a?(Proc)
53
53
 
54
- ([self] + self.descendants).each do |model|
55
- model.default_scopes << criteria_proc
54
+ subclass_tree.each do |subclass|
55
+ subclass.default_scopes << criteria_proc
56
56
  end
57
57
  end
58
58
 
@@ -34,9 +34,9 @@ module NoBrainer::Document::LazyFetch
34
34
  model = self
35
35
  inject_in_layer :lazy_fetch do
36
36
  if options[:lazy_fetch]
37
- model.for_each_subclass { |_model| _model.fields_to_lazy_fetch << attr }
37
+ model.subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch << attr }
38
38
  else
39
- model.for_each_subclass { |_model| _model.fields_to_lazy_fetch.delete(attr) }
39
+ model.subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch.delete(attr) }
40
40
  end
41
41
 
42
42
  # Lazy loading can also specified through criteria.
@@ -57,7 +57,7 @@ module NoBrainer::Document::LazyFetch
57
57
 
58
58
  def _remove_field(attr, options={})
59
59
  super
60
- for_each_subclass { |model| model.fields_to_lazy_fetch.delete(attr) }
60
+ subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch.delete(attr) }
61
61
  inject_in_layer :lazy_fetch do
62
62
  remove_method("#{attr}") if method_defined?("#{attr}")
63
63
  end
@@ -28,12 +28,12 @@ module NoBrainer::Document::Polymorphic
28
28
  self == root_class
29
29
  end
30
30
 
31
- def for_each_subclass(&block)
32
- ([self] + self.descendants).each(&block)
31
+ def subclass_tree
32
+ [self] + self.descendants
33
33
  end
34
34
 
35
35
  def descendants_type_values
36
- for_each_subclass.map(&:type_value)
36
+ subclass_tree.map(&:type_value)
37
37
  end
38
38
 
39
39
  def model_from_attrs(attrs)
@@ -23,6 +23,10 @@ module NoBrainer::Document::PrimaryKey
23
23
 
24
24
  delegate :hash, :to => :pk_value
25
25
 
26
+ def cache_key
27
+ "#{self.class.table_name}/#{pk_value}"
28
+ end
29
+
26
30
  module ClassMethods
27
31
  def define_default_pk
28
32
  class_variable_set(:@@pk_name, nil)
@@ -30,6 +34,7 @@ module NoBrainer::Document::PrimaryKey
30
34
  end
31
35
 
32
36
  def define_pk(attr)
37
+ return if pk_name == attr
33
38
  if fields[pk_name].try(:[], :primary_key) == :default
34
39
  remove_field(pk_name, :set_default_pk => false)
35
40
  end
@@ -50,9 +55,13 @@ module NoBrainer::Document::PrimaryKey
50
55
  options = options.merge(:readonly => true) if options[:readonly].nil?
51
56
  options = options.merge(:index => true)
52
57
 
53
- if options[:type].in?([String, nil]) && options[:default].nil?
54
- options[:type] = String
55
- options[:default] = ->{ NoBrainer::Document::PrimaryKey::Generator.generate }
58
+ if options[:default].nil?
59
+ # TODO Maybe we should let the user configure the pk generator
60
+ default_pk_generator = NoBrainer::Document::PrimaryKey::Generator
61
+ if options[:type].in?([default_pk_generator.field_type, nil])
62
+ options[:type] = default_pk_generator.field_type
63
+ options[:default] = ->{ default_pk_generator.generate }
64
+ end
56
65
  end
57
66
  end
58
67
  super
@@ -20,6 +20,7 @@ module NoBrainer::Document::PrimaryKey::Generator
20
20
  # 1% of chance to have a collision with ~580 servers.
21
21
  # When using more than 500 machines, it's therefore a good
22
22
  # idea to set the machine_id manually to avoid collisions.
23
+ # XXX This is referenced in nobrainer/config.rb#default_machine_id
23
24
  MACHINE_ID_BITS = 24
24
25
 
25
26
  # 15 bits for the current pid. We wouldn't need it if the sequence number was
@@ -27,7 +28,7 @@ module NoBrainer::Document::PrimaryKey::Generator
27
28
  PID_BITS = 15
28
29
 
29
30
  # Total: 83 bits
30
- # We need at most 14 digits in [A-Za-z0-9] to represent 83 bits:
31
+ # With 14 digits in [A-Za-z0-9], we can represent 83 bits:
31
32
  # Math.log(62**14)/Math.log(2) = 83.35
32
33
  ID_STR_LENGTH = 14
33
34
 
@@ -80,4 +81,8 @@ module NoBrainer::Document::PrimaryKey::Generator
80
81
  def self.generate
81
82
  convert_to_alphanum(@lock.synchronize { _generate })
82
83
  end
84
+
85
+ def self.field_type
86
+ String
87
+ end
83
88
  end
@@ -4,8 +4,6 @@ module NoBrainer::Document::Serialization
4
4
  include ActiveModel::Serialization
5
5
  include ActiveModel::Serializers::JSON
6
6
 
7
- included { self.include_root_in_json = false }
8
-
9
7
  def read_attribute_for_serialization(*a, &b)
10
8
  read_attribute(*a, &b)
11
9
  end
@@ -17,4 +17,8 @@ module NoBrainer::Document::Timestamps
17
17
  self.updated_at = Time.now unless updated_at_changed?
18
18
  super(attrs.merge('updated_at' => @_attributes['updated_at']))
19
19
  end
20
+
21
+ def cache_key
22
+ "#{super}#{updated_at.try(:strftime, "-%s%L")}"
23
+ end
20
24
  end
@@ -60,7 +60,8 @@ module NoBrainer::Document::Validation::Uniqueness
60
60
  super
61
61
  self.model = options[:class]
62
62
  self.scope = [*options[:scope]].map(&:to_sym)
63
- ([model] + model.descendants).each do |subclass|
63
+
64
+ model.subclass_tree.each do |subclass|
64
65
  subclass.unique_validators << self
65
66
  end
66
67
  end
@@ -9,6 +9,8 @@ class NoBrainer::QueryRunner::Profiler < NoBrainer::QueryRunner::Middleware
9
9
 
10
10
  private
11
11
 
12
+ require 'no_brainer/profiler/logger'
13
+
12
14
  def profiler_start(env)
13
15
  env[:start_time] = Time.now
14
16
  end
@@ -0,0 +1,11 @@
1
+ module NoBrainer::SymbolDecoration
2
+ NON_CHAINABLE_OPERATORS = %w(in eq gt ge gte lt le lte defined near intersects).map(&:to_sym)
3
+ CHAINABLE_OPERATORS = %w(not any all).map(&:to_sym)
4
+ OPERATORS = CHAINABLE_OPERATORS + NON_CHAINABLE_OPERATORS
5
+
6
+ def self.hook
7
+ require 'symbol_decoration'
8
+ Symbol::Decoration.register(*NON_CHAINABLE_OPERATORS)
9
+ Symbol::Decoration.register(*CHAINABLE_OPERATORS, :chainable => true)
10
+ end
11
+ end
data/lib/nobrainer.rb CHANGED
@@ -14,9 +14,9 @@ module NoBrainer
14
14
 
15
15
  # We eager load things that could be loaded when handling the first web request.
16
16
  # Code that is loaded through the DSL of NoBrainer should not be eager loaded.
17
- autoload :Document, :IndexManager, :Loader, :Fork, :Geo, :Lock, :Profiler
18
- eager_autoload :Config, :Connection, :ConnectionManager, :Error,
19
- :QueryRunner, :Criteria, :RQL
17
+ autoload :Document, :IndexManager, :Loader, :Fork, :Geo, :SymbolDecoration
18
+ eager_autoload :Config, :Connection, :ConnectionManager, :Error,
19
+ :QueryRunner, :Criteria, :RQL, :Lock, :Profiler
20
20
 
21
21
  class << self
22
22
  delegate :connection, :disconnect, :to => 'NoBrainer::ConnectionManager'
@@ -38,11 +38,11 @@ module NoBrainer
38
38
  end
39
39
 
40
40
  Fork.hook unless jruby?
41
+ SymbolDecoration.hook
41
42
  end
42
43
 
43
44
  ActiveSupport.on_load(:i18n) do
44
45
  I18n.load_path << File.dirname(__FILE__) + '/no_brainer/locale/en.yml'
45
46
  end
46
47
 
47
- require 'no_brainer/profiler/logger'
48
48
  require 'no_brainer/railtie' if defined?(Rails)
@@ -6,7 +6,7 @@ module NoBrainer::Generators
6
6
  extend NoBrainer::Generators::NamespaceFix
7
7
  source_root File.expand_path("../../templates", __FILE__)
8
8
 
9
- desc "Disable ActiveRecord and generates ./config/initializer/nobrainer.rb"
9
+ desc "Disable ActiveRecord and generates ./config/initializers/nobrainer.rb"
10
10
 
11
11
  class RequireProxy
12
12
  attr_accessor :required_paths
@@ -42,7 +42,7 @@ module NoBrainer::Generators
42
42
 
43
43
 
44
44
  def copy_initializer
45
- template('nobrainer.rb', 'config/initializer/nobrainer.rb')
45
+ template('nobrainer.rb', 'config/initializers/nobrainer.rb')
46
46
  end
47
47
  end
48
48
  end
@@ -65,7 +65,7 @@ NoBrainer.configure do |config|
65
65
 
66
66
  # Configures which mechanism to use in order to perform non-racy uniqueness
67
67
  # validations. More about this behavior in the Distributed Locks section.
68
- # config.distributed_lock_class = NoBrainer::Lock
68
+ # config.distributed_lock_class = "NoBrainer::Lock"
69
69
 
70
70
  # Configures the default timing lock options.
71
71
  # config.lock_options = { :expire => 60, :timeout => 10 }
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.26.0
4
+ version: 0.27.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-20 00:00:00.000000000 Z
11
+ date: 2015-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rethinkdb
@@ -191,6 +191,7 @@ files:
191
191
  - lib/no_brainer/railtie.rb
192
192
  - lib/no_brainer/railtie/database.rake
193
193
  - lib/no_brainer/rql.rb
194
+ - lib/no_brainer/symbol_decoration.rb
194
195
  - lib/nobrainer.rb
195
196
  - lib/rails/generators/nobrainer/install_generator.rb
196
197
  - lib/rails/generators/nobrainer/model_generator.rb