nobrainer 0.43.0 → 0.44.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/lib/no_brainer/config.rb +29 -22
- data/lib/no_brainer/criteria/where.rb +47 -4
- data/lib/no_brainer/document/association/belongs_to.rb +23 -8
- data/lib/no_brainer/document/association.rb +5 -4
- data/lib/no_brainer/document/attributes.rb +11 -3
- data/lib/no_brainer/profiler/logger.rb +58 -41
- data/lib/no_brainer/profiler/slow_queries.rb +23 -0
- data/lib/no_brainer/query_runner/profiler.rb +8 -4
- data/lib/rails/generators/templates/nobrainer.rb +14 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '035182f731c727beff4562127d0ec36aaa7ad56871f11121940f05b0b72819c2'
|
4
|
+
data.tar.gz: aa14e68a3ff7fded92a16171842dff3a7329cb78c93946af9475255d25bba1ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9ab73b56b9df5df5098d7a769581fe367284fa7200b19b7cfd637982f8bf3afd36e071e3f12370504e87e1799bc0d7f1f5ee03ca62617f9f3bfbbb62c6e51df
|
7
|
+
data.tar.gz: 42a7831651d35f55bd39c68c6baaec3b20e845c82e6d5ce562d9e5c2b4184cfcd3daf754ab097bcc3bffe91c05ab873e2d083d37d21c5e44bbfc6afb81197f22
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.44.0] - 2023-07-31
|
10
|
+
### Added
|
11
|
+
- Slow Queries Logger feature logging slow queries in a dedicated log file
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
- `first_or_create` with a polymorphic association [#288](https://github.com/NoBrainerORM/nobrainer/issues/288)
|
9
15
|
|
10
16
|
## [0.43.0] - 2022-06-16
|
11
17
|
### Added
|
@@ -128,7 +134,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
128
134
|
- Locks: bug fix: allow small timeouts in lock()
|
129
135
|
- Fix reentrant lock counter on steals
|
130
136
|
|
131
|
-
[Unreleased]: https://github.com/nobrainerorm/nobrainer/compare/v0.
|
137
|
+
[Unreleased]: https://github.com/nobrainerorm/nobrainer/compare/v0.44.0...HEAD
|
138
|
+
[0.44.0]: https://github.com/nobrainerorm/nobrainer/compare/v0.43.0...v0.44.0
|
132
139
|
[0.43.0]: https://github.com/nobrainerorm/nobrainer/compare/v0.42.0...v0.43.0
|
133
140
|
[0.42.0]: https://github.com/nobrainerorm/nobrainer/compare/v0.41.1...v0.42.0
|
134
141
|
[0.41.1]: https://github.com/nobrainerorm/nobrainer/compare/v0.41.0...v0.41.1
|
data/lib/no_brainer/config.rb
CHANGED
@@ -1,29 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'logger'
|
2
4
|
|
3
5
|
module NoBrainer::Config
|
4
6
|
SETTINGS = {
|
5
|
-
:
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
|
7
|
+
app_name: { default: -> { default_app_name } },
|
8
|
+
colorize_logger: { default: -> { true }, valid_values: [true, false] },
|
9
|
+
criteria_cache_max_entries: { default: -> { 10_000 } },
|
10
|
+
db_timezone: { default: -> { :utc }, valid_values: %i[unchanged utc local] },
|
11
|
+
distributed_lock_class: { default: -> { 'NoBrainer::Lock' } },
|
12
|
+
driver: { default: -> { :regular }, valid_values: %i[regular em] },
|
13
|
+
durability: { default: -> {} }, # legacy
|
14
|
+
environment: { default: -> { default_environment } },
|
15
|
+
geo_options: { default: -> { { geo_system: 'WGS84', unit: 'm' } } },
|
16
|
+
lock_options: { default: -> { { expire: 60, timeout: 10 } }, valid_keys: %i[expire timeout] },
|
17
|
+
log_slow_queries: { default: -> { false } },
|
18
|
+
logger: { default: -> { default_logger } },
|
19
|
+
long_query_time: { default: -> { 10 } },
|
20
|
+
machine_id: { default: -> { default_machine_id } },
|
21
|
+
max_string_length: { default: -> { 255 } },
|
22
|
+
per_thread_connection: { default: -> { false }, valid_values: [true, false] },
|
23
|
+
rethinkdb_urls: { default: -> { [default_rethinkdb_url] } },
|
24
|
+
run_options: { default: -> { { durability: default_durability } } },
|
25
|
+
slow_query_log_file: { default: -> { File.join('/', 'var', 'log', 'rethinkdb', 'slow_queries.log') } },
|
26
|
+
ssl_options: { default: -> {} },
|
27
|
+
table_options: {
|
28
|
+
default: -> { { shards: 1, replicas: 1, write_acks: :majority } },
|
29
|
+
valid_keys: %i[durability shards replicas primary_replica_tag nonvoting_replica_tags write_acks]
|
30
|
+
},
|
31
|
+
user_timezone: { default: -> { :local }, valid_values: %i[unchanged utc local] },
|
32
|
+
warn_on_active_record: { default: -> { true }, valid_values: [true, false] }
|
33
|
+
}.freeze
|
27
34
|
|
28
35
|
class << self
|
29
36
|
attr_accessor(*SETTINGS.keys)
|
@@ -107,7 +107,22 @@ module NoBrainer::Criteria::Where
|
|
107
107
|
when :during then [key_modifier, op, [cast_value(value.first), cast_value(value.last)]]
|
108
108
|
else [key_modifier, op, cast_value(value)]
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
|
+
# When key_path relates to a polymorphic associatoin, the new_key_path is
|
112
|
+
# an Array containing the foreign_type and then the foreign_key.
|
113
|
+
if new_key_path.first.is_a?(Array)
|
114
|
+
foreign_type, foreign_key = new_key_path.first
|
115
|
+
|
116
|
+
MultiOperator.new(
|
117
|
+
:and,
|
118
|
+
[
|
119
|
+
BinaryOperator.new([foreign_type], new_key_modifier, new_op, value.class.to_s, model, true),
|
120
|
+
BinaryOperator.new([foreign_key], new_key_modifier, new_op, value.__send__(value.class.pk_name), model, true)
|
121
|
+
]
|
122
|
+
)
|
123
|
+
else
|
124
|
+
BinaryOperator.new(new_key_path, new_key_modifier, new_op, new_value, model, true)
|
125
|
+
end
|
111
126
|
end
|
112
127
|
|
113
128
|
def to_rql(doc)
|
@@ -185,7 +200,7 @@ module NoBrainer::Criteria::Where
|
|
185
200
|
box_value = key_modifier.in?([:any, :all]) || op == :include
|
186
201
|
value = [value] if box_value
|
187
202
|
k_v = key_path.reverse.reduce(value) { |v,k| {k => v} }.first
|
188
|
-
k_v = model.association_user_to_model_cast(*k_v)
|
203
|
+
k_v = model.association_user_to_model_cast(*k_v, value.class)
|
189
204
|
value = model.cast_user_to_db_for(*k_v)
|
190
205
|
value = key_path[1..-1].reduce(value) { |h,k| h[k] }
|
191
206
|
value = value.first if box_value
|
@@ -193,15 +208,43 @@ module NoBrainer::Criteria::Where
|
|
193
208
|
end
|
194
209
|
end
|
195
210
|
|
211
|
+
#
|
212
|
+
# This method is used in order to transform association from the passed
|
213
|
+
# `key_path` in to model's field(s).
|
214
|
+
#
|
215
|
+
# When `key_path` contains a field, this method just ensures the given field
|
216
|
+
# is well defined in the owner model.
|
217
|
+
# When `key_path` contains the name of an association, this method updates
|
218
|
+
# the `key_path` in order to replace the association name with the field(s)
|
219
|
+
# behind that association.
|
220
|
+
#
|
221
|
+
# In the case of :
|
222
|
+
# * a polymorphic association name passed as `key_path`, the association
|
223
|
+
# name will be replaced by the 2 fields representing it (foreign_key and
|
224
|
+
# foreign_type)
|
225
|
+
# * all other association the association name is replaced by
|
226
|
+
# the primary_key or the foreign_key depending on the association type
|
196
227
|
def cast_key_path(key_path)
|
197
228
|
return key_path if casted_values
|
198
229
|
|
230
|
+
# key_path is an Array of symbols representing the path to the key being
|
231
|
+
# queried.
|
232
|
+
#
|
233
|
+
# The Array size can be greater that 1 when quering from a field with,
|
234
|
+
# the type `Hash` and the query is targetting a nested key from the Hash.
|
235
|
+
#
|
236
|
+
# The first Array element is always the field name.
|
199
237
|
if key_path.size == 1
|
200
|
-
|
201
|
-
|
238
|
+
# With fields and non-polymorphic associations `keys` will be a symbol
|
239
|
+
# while with a polymorphic association it will be an Array of symbols
|
240
|
+
# being the two fields used by the association.
|
241
|
+
keys, _v = model.association_user_to_model_cast(key_path.first, nil, value.class)
|
242
|
+
key_path = [keys]
|
202
243
|
end
|
203
244
|
|
245
|
+
# Ensures fields exist on the model
|
204
246
|
model.ensure_valid_key!(key_path.first)
|
247
|
+
|
205
248
|
key_path
|
206
249
|
end
|
207
250
|
end
|
@@ -70,8 +70,16 @@ class NoBrainer::Document::Association::BelongsTo
|
|
70
70
|
raise 'You cannot set class_name on a polymorphic belongs_to'
|
71
71
|
end
|
72
72
|
|
73
|
-
|
74
|
-
|
73
|
+
if options[:polymorphic]
|
74
|
+
if options[:uniq] || options[:unique]
|
75
|
+
owner_model.field(foreign_type, uniq: { scope: foreign_key })
|
76
|
+
owner_model.index([foreign_type, foreign_key])
|
77
|
+
else
|
78
|
+
owner_model.field(foreign_type)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
owner_model.field(foreign_key, store_as: options[:foreign_key_store_as], index: options[:index])
|
75
83
|
|
76
84
|
unless options[:validates] == false
|
77
85
|
owner_model.validates(target_name, options[:validates]) if options[:validates]
|
@@ -98,13 +106,20 @@ class NoBrainer::Document::Association::BelongsTo
|
|
98
106
|
add_callback_for(:after_validation)
|
99
107
|
end
|
100
108
|
|
101
|
-
def cast_attr(
|
102
|
-
case
|
103
|
-
when target_model
|
104
|
-
|
109
|
+
def cast_attr(key, value, target_class = nil)
|
110
|
+
case value
|
111
|
+
when target_model(target_class)
|
112
|
+
[foreign_key, value.__send__(primary_key)]
|
113
|
+
when nil
|
114
|
+
if options[:polymorphic]
|
115
|
+
[[foreign_type, foreign_key], nil]
|
116
|
+
else
|
117
|
+
[foreign_key, nil]
|
118
|
+
end
|
105
119
|
else
|
106
|
-
|
107
|
-
|
120
|
+
raise NoBrainer::Error::InvalidType.new(
|
121
|
+
model: owner_model, attr_name: key, type: target_model, value: value
|
122
|
+
)
|
108
123
|
end
|
109
124
|
end
|
110
125
|
|
@@ -21,11 +21,12 @@ module NoBrainer::Document::Association
|
|
21
21
|
super
|
22
22
|
end
|
23
23
|
|
24
|
-
def association_user_to_model_cast(
|
25
|
-
association = association_metadata[
|
24
|
+
def association_user_to_model_cast(key, value, target_class = nil)
|
25
|
+
association = association_metadata[key]
|
26
26
|
case association
|
27
|
-
when NoBrainer::Document::Association::BelongsTo::Metadata
|
28
|
-
|
27
|
+
when NoBrainer::Document::Association::BelongsTo::Metadata
|
28
|
+
association.cast_attr(key, value, target_class)
|
29
|
+
else [key, value]
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
@@ -154,9 +154,17 @@ module NoBrainer::Document::Attributes
|
|
154
154
|
!!fields[attr.to_sym]
|
155
155
|
end
|
156
156
|
|
157
|
-
def ensure_valid_key!(
|
158
|
-
|
159
|
-
|
157
|
+
def ensure_valid_key!(keys)
|
158
|
+
missings = Array(keys).select do |key|
|
159
|
+
has_field?(key) == false && has_index?(key) == false
|
160
|
+
end
|
161
|
+
|
162
|
+
return if missings.empty?
|
163
|
+
|
164
|
+
raise NoBrainer::Error::UnknownAttribute,
|
165
|
+
"`#{missings.join('\', `')}' #{missings.size > 1 ? 'are' : 'is'} " \
|
166
|
+
"not #{'a' if missings.size == 1} valid attribute" \
|
167
|
+
"#{'s' if missings.size > 1} of #{self}"
|
160
168
|
end
|
161
169
|
end
|
162
170
|
end
|
@@ -1,45 +1,62 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
msg_duration = " " * [0, 6 - msg_duration.size].max + msg_duration
|
13
|
-
msg_duration = "[#{msg_duration}ms] "
|
14
|
-
|
15
|
-
env[:query_type] = NoBrainer::RQL.type_of(env[:query])
|
16
|
-
|
17
|
-
msg_db = "[#{env[:options][:db]}] " if env[:options][:db]
|
18
|
-
msg_query = env[:query].inspect.gsub(/\n/, '').gsub(/ +/, ' ')
|
19
|
-
|
20
|
-
msg_exception = "#{env[:exception].class} #{env[:exception].message.split("\n").first}" if env[:exception]
|
21
|
-
msg_exception ||= "perf: filtering without using an index" if not_indexed
|
22
|
-
|
23
|
-
msg_last = nil
|
24
|
-
|
25
|
-
if NoBrainer::Config.colorize_logger
|
26
|
-
query_color = case env[:query_type]
|
27
|
-
when :write then "\e[1;31m" # red
|
28
|
-
when :read then "\e[1;32m" # green
|
29
|
-
when :management then "\e[1;33m" # yellow
|
30
|
-
end
|
31
|
-
msg_duration = [query_color, msg_duration].join
|
32
|
-
msg_db = ["\e[0;34m", msg_db, query_color].join if msg_db
|
33
|
-
if msg_exception
|
34
|
-
exception_color = "\e[0;31m" if level == Logger::ERROR
|
35
|
-
msg_exception = ["\e[0;39m", " -- ", exception_color, msg_exception].compact.join
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NoBrainer
|
4
|
+
module Profiler
|
5
|
+
class Logger
|
6
|
+
def on_query(env)
|
7
|
+
level = ::Logger::ERROR if env[:exception]
|
8
|
+
level ||= not_indexed(env) ? ::Logger::INFO : ::Logger::DEBUG
|
9
|
+
return if NoBrainer.logger.level > level
|
10
|
+
|
11
|
+
NoBrainer.logger.add(level, build_message(env))
|
36
12
|
end
|
37
|
-
msg_last = "\e[0m"
|
38
|
-
end
|
39
13
|
|
40
|
-
|
41
|
-
|
42
|
-
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_message(env)
|
17
|
+
msg_duration = (env[:duration] * 1000.0).round(1).to_s
|
18
|
+
msg_duration = (' ' * [0, 6 - msg_duration.size].max) + msg_duration
|
19
|
+
msg_duration = "[#{msg_duration}ms] "
|
20
|
+
|
21
|
+
env[:query_type] = NoBrainer::RQL.type_of(env[:query])
|
22
|
+
|
23
|
+
msg_db = "[#{env[:options][:db]}] " if env[:options][:db]
|
24
|
+
msg_query = env[:query].inspect.gsub(/\n/, '').gsub(/ +/, ' ')
|
25
|
+
|
26
|
+
msg_exception = "#{env[:exception].class} #{env[:exception].message.split("\n").first}" if env[:exception]
|
27
|
+
msg_exception ||= 'perf: filtering without using an index' if not_indexed(env)
|
28
|
+
|
29
|
+
msg_last = nil
|
43
30
|
|
44
|
-
|
31
|
+
if NoBrainer::Config.colorize_logger
|
32
|
+
msg_duration = [query_color(env[:query_type]), msg_duration].join
|
33
|
+
msg_db = ["\e[0;34m", msg_db, query_color(env[:query_type])].join if msg_db
|
34
|
+
if msg_exception
|
35
|
+
exception_color = "\e[0;31m" if level == Logger::ERROR
|
36
|
+
msg_exception = ["\e[0;39m", ' -- ', exception_color, msg_exception].compact.join
|
37
|
+
end
|
38
|
+
msg_last = "\e[0m"
|
39
|
+
end
|
40
|
+
|
41
|
+
[msg_duration, msg_db, msg_query, msg_exception, msg_last].join
|
42
|
+
end
|
43
|
+
|
44
|
+
def not_indexed(env)
|
45
|
+
env[:criteria] &&
|
46
|
+
env[:criteria].where_present? &&
|
47
|
+
!env[:criteria].where_indexed? &&
|
48
|
+
!env[:criteria].model.try(:perf_warnings_disabled)
|
49
|
+
end
|
50
|
+
|
51
|
+
def query_color(query_type)
|
52
|
+
{
|
53
|
+
write: "\e[1;31m", # red
|
54
|
+
read: "\e[1;32m", # green
|
55
|
+
management: "\e[1;33m" # yellow
|
56
|
+
}[query_type]
|
57
|
+
end
|
58
|
+
|
59
|
+
NoBrainer::Profiler.register(new)
|
60
|
+
end
|
61
|
+
end
|
45
62
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NoBrainer
|
4
|
+
module Profiler
|
5
|
+
class SlowQueries < Logger
|
6
|
+
def on_query(env)
|
7
|
+
return unless NoBrainer::Config.log_slow_queries
|
8
|
+
|
9
|
+
query_duration = (env[:duration] * 1000.0).round(1)
|
10
|
+
|
11
|
+
return unless query_duration > NoBrainer::Config.long_query_time
|
12
|
+
|
13
|
+
File.write(
|
14
|
+
NoBrainer::Config.slow_query_log_file,
|
15
|
+
build_message(env),
|
16
|
+
mode: 'a'
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
NoBrainer::Profiler.register(new)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class NoBrainer::QueryRunner::Profiler < NoBrainer::QueryRunner::Middleware
|
2
4
|
def call(env)
|
3
5
|
profiler_start(env)
|
@@ -10,12 +12,13 @@ class NoBrainer::QueryRunner::Profiler < NoBrainer::QueryRunner::Middleware
|
|
10
12
|
private
|
11
13
|
|
12
14
|
require 'no_brainer/profiler/logger'
|
15
|
+
require 'no_brainer/profiler/slow_queries'
|
13
16
|
|
14
17
|
def profiler_start(env)
|
15
18
|
env[:start_time] = Time.now
|
16
19
|
end
|
17
20
|
|
18
|
-
def profiler_end(env, exception=nil)
|
21
|
+
def profiler_end(env, exception = nil)
|
19
22
|
return if handle_on_demand_exception?(env, exception)
|
20
23
|
|
21
24
|
env[:end_time] = Time.now
|
@@ -26,10 +29,11 @@ class NoBrainer::QueryRunner::Profiler < NoBrainer::QueryRunner::Middleware
|
|
26
29
|
env[:query_type] = NoBrainer::RQL.type_of(env[:query])
|
27
30
|
|
28
31
|
NoBrainer::Profiler.registered_profilers.each do |profiler|
|
29
|
-
begin
|
32
|
+
begin # rubocop:disable Style/RedundantBegin
|
30
33
|
profiler.on_query(env)
|
31
|
-
rescue
|
32
|
-
STDERR.puts "[NoBrainer]
|
34
|
+
rescue StandardError => error
|
35
|
+
STDERR.puts "[NoBrainer] #{profiler.class.name} profiler error: " \
|
36
|
+
"#{error.class} #{error.message}\n#{error.backtrace.join('\n')}"
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
NoBrainer.configure do |config|
|
2
4
|
# app_name is the name of your application in lowercase.
|
3
5
|
# When using Rails, the application name is automatically inferred.
|
@@ -94,4 +96,16 @@ NoBrainer.configure do |config|
|
|
94
96
|
# is cached. The per criteria cache is disabled if it grows too big to avoid
|
95
97
|
# out of memory issues.
|
96
98
|
# config.criteria_cache_max_entries = 10_000
|
99
|
+
|
100
|
+
# Write queries running longer than config.long_query_time seconds.
|
101
|
+
# The slow query log can be used to find queries that take a long time to
|
102
|
+
# execute and are therefore candidates for optimization.
|
103
|
+
# config.log_slow_queries = true
|
104
|
+
|
105
|
+
# Queries running longer than the bellow value will be logged in a log file if
|
106
|
+
# the above `config.log_slow_queries` is `true`.
|
107
|
+
# config.long_query_time = 10 # seconds
|
108
|
+
|
109
|
+
# Path of the slow queries log file
|
110
|
+
# config.slow_query_log_file = File.join('/', 'var', 'log', 'rethinkdb', 'slow_queries.log')
|
97
111
|
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.
|
4
|
+
version: 0.44.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicolas Viennot
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -205,6 +205,7 @@ files:
|
|
205
205
|
- lib/no_brainer/profiler.rb
|
206
206
|
- lib/no_brainer/profiler/controller_runtime.rb
|
207
207
|
- lib/no_brainer/profiler/logger.rb
|
208
|
+
- lib/no_brainer/profiler/slow_queries.rb
|
208
209
|
- lib/no_brainer/query_runner.rb
|
209
210
|
- lib/no_brainer/query_runner/connection_lock.rb
|
210
211
|
- lib/no_brainer/query_runner/database_on_demand.rb
|
@@ -247,7 +248,7 @@ metadata:
|
|
247
248
|
homepage_uri: http://nobrainer.io
|
248
249
|
source_code_uri: https://github.com/NoBrainerORM/nobrainer
|
249
250
|
changelog_uri: https://github.com/NoBrainerORM/nobrainer/blob/master/CHANGELOG.md
|
250
|
-
post_install_message:
|
251
|
+
post_install_message:
|
251
252
|
rdoc_options: []
|
252
253
|
require_paths:
|
253
254
|
- lib
|
@@ -263,7 +264,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
263
264
|
version: '0'
|
264
265
|
requirements: []
|
265
266
|
rubygems_version: 3.1.6
|
266
|
-
signing_key:
|
267
|
+
signing_key:
|
267
268
|
specification_version: 4
|
268
269
|
summary: A Ruby ORM for RethinkDB
|
269
270
|
test_files: []
|