nobrainer 0.43.0 → 0.44.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 +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: []
|