nobrainer 0.26.0 → 0.27.0

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