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 +4 -4
- data/lib/no_brainer/autoload.rb +1 -1
- data/lib/no_brainer/config.rb +18 -6
- data/lib/no_brainer/connection_manager.rb +1 -1
- data/lib/no_brainer/criteria/where.rb +0 -8
- data/lib/no_brainer/document/association.rb +5 -5
- data/lib/no_brainer/document/association/belongs_to.rb +17 -7
- data/lib/no_brainer/document/association/eager_loader.rb +0 -1
- data/lib/no_brainer/document/attributes.rb +8 -7
- data/lib/no_brainer/document/criteria.rb +2 -2
- data/lib/no_brainer/document/lazy_fetch.rb +3 -3
- data/lib/no_brainer/document/polymorphic.rb +3 -3
- data/lib/no_brainer/document/primary_key.rb +12 -3
- data/lib/no_brainer/document/primary_key/generator.rb +6 -1
- data/lib/no_brainer/document/serialization.rb +0 -2
- data/lib/no_brainer/document/timestamps.rb +4 -0
- data/lib/no_brainer/document/validation/uniqueness.rb +2 -1
- data/lib/no_brainer/query_runner/profiler.rb +2 -0
- data/lib/no_brainer/symbol_decoration.rb +11 -0
- data/lib/nobrainer.rb +4 -4
- data/lib/rails/generators/nobrainer/install_generator.rb +2 -2
- data/lib/rails/generators/templates/nobrainer.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc50cb3ae605543bb7ed3df448d0ff32d3b984d4
|
4
|
+
data.tar.gz: b8ae438c48a0d64408f4966f92002be17f6cdff0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 118724ce72e1c29432aa208b062f9e107b56dc0401d921f5194708a2c3f2b7a95160572cf2cd83cb22f322cad0fb7cfe3939fdccf8eb6ef401da99f975df275b
|
7
|
+
data.tar.gz: 2d5737b1035583abd7f888f61341b0922d85e654b919aee2c282be2fcd07bb144f3654ef76ad2d016289b5692766d72d7d0e7620f8d13a6923d054acab49de91
|
data/lib/no_brainer/autoload.rb
CHANGED
data/lib/no_brainer/config.rb
CHANGED
@@ -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.
|
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] &
|
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 =
|
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
|
@@ -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
|
-
|
32
|
-
|
33
|
-
r =
|
34
|
-
|
35
|
-
|
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
|
-
|
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(
|
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
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
153
|
-
|
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
|
-
|
55
|
-
|
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.
|
37
|
+
model.subclass_tree.each { |subclass| subclass.fields_to_lazy_fetch << attr }
|
38
38
|
else
|
39
|
-
model.
|
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
|
-
|
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
|
32
|
-
|
31
|
+
def subclass_tree
|
32
|
+
[self] + self.descendants
|
33
33
|
end
|
34
34
|
|
35
35
|
def descendants_type_values
|
36
|
-
|
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[:
|
54
|
-
|
55
|
-
|
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
|
-
#
|
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
|
@@ -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
|
-
|
63
|
+
|
64
|
+
model.subclass_tree.each do |subclass|
|
64
65
|
subclass.unique_validators << self
|
65
66
|
end
|
66
67
|
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, :
|
18
|
-
eager_autoload :Config, :Connection, :ConnectionManager,
|
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/
|
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/
|
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.
|
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-
|
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
|