influxer 1.1.4 → 1.2.2
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 +5 -5
- data/CHANGELOG.md +98 -0
- data/{MIT-LICENSE → LICENSE.txt} +1 -1
- data/README.md +106 -47
- data/lib/influxer.rb +10 -9
- data/lib/influxer/client.rb +1 -1
- data/lib/influxer/config.rb +19 -6
- data/lib/influxer/engine.rb +1 -1
- data/lib/influxer/metrics/active_model3/model.rb +2 -4
- data/lib/influxer/metrics/metrics.rb +10 -13
- data/lib/influxer/metrics/quoting/timestamp.rb +23 -11
- data/lib/influxer/metrics/relation.rb +33 -17
- data/lib/influxer/metrics/relation/calculations.rb +1 -1
- data/lib/influxer/metrics/relation/time_query.rb +15 -13
- data/lib/influxer/metrics/relation/where_clause.rb +19 -11
- data/lib/influxer/metrics/scoping.rb +4 -4
- data/lib/influxer/metrics/scoping/current_scope.rb +2 -1
- data/lib/influxer/metrics/scoping/default.rb +1 -1
- data/lib/influxer/metrics/scoping/named.rb +2 -1
- data/lib/influxer/model.rb +4 -9
- data/lib/influxer/rails/client.rb +6 -6
- data/lib/influxer/version.rb +1 -1
- metadata +30 -95
- data/.gitignore +0 -37
- data/.rspec +0 -2
- data/.rubocop.yml +0 -77
- data/.travis.yml +0 -10
- data/Changelog.md +0 -111
- data/Gemfile +0 -10
- data/Rakefile +0 -13
- data/gemfiles/rails32.gemfile +0 -7
- data/gemfiles/rails42.gemfile +0 -7
- data/gemfiles/rails5.gemfile +0 -7
- data/influxer.gemspec +0 -33
- data/spec/cases/points_spec.rb +0 -36
- data/spec/cases/write_points_spec.rb +0 -85
- data/spec/client_spec.rb +0 -46
- data/spec/fixtures/empty_result.json +0 -21
- data/spec/fixtures/single_series.json +0 -29
- data/spec/metrics/metrics_spec.rb +0 -283
- data/spec/metrics/relation_spec.rb +0 -477
- data/spec/metrics/scoping_spec.rb +0 -66
- data/spec/model/user_spec.rb +0 -46
- data/spec/spec_helper.rb +0 -64
- data/spec/support/metrics/action_metrics.rb +0 -5
- data/spec/support/metrics/custom_metrics.rb +0 -6
- data/spec/support/metrics/dummy_metrics.rb +0 -12
- data/spec/support/metrics/user_metrics.rb +0 -6
- data/spec/support/metrics/visits_metrics.rb +0 -8
- data/spec/support/shared_contexts/shared_query.rb +0 -16
- data/spec/support/user.rb +0 -16
@@ -3,27 +3,27 @@
|
|
3
3
|
module Influxer
|
4
4
|
module TimestampQuoting #:nodoc:
|
5
5
|
TIME_FACTORS = {
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
"ns" => 1_000_000_000,
|
7
|
+
"ms" => 1_000,
|
8
|
+
"s" => 1
|
9
9
|
}.freeze
|
10
10
|
|
11
|
-
DEFAULT_PRECISION =
|
11
|
+
DEFAULT_PRECISION = "ns"
|
12
12
|
|
13
13
|
# Quote timestamp
|
14
14
|
# rubocop: disable Metrics/MethodLength, Metrics/AbcSize
|
15
15
|
def quote_timestamp(val, client)
|
16
16
|
if Influxer.config.time_duration_suffix_enabled
|
17
|
-
precision = if TIME_FACTORS.
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
precision = if TIME_FACTORS.key?(client.time_precision)
|
18
|
+
client.time_precision
|
19
|
+
else
|
20
|
+
DEFAULT_PRECISION
|
21
|
+
end
|
22
22
|
return quote_timestamp_with_suffix(val, precision)
|
23
23
|
end
|
24
24
|
|
25
|
-
if !TIME_FACTORS.
|
26
|
-
|
25
|
+
if !TIME_FACTORS.key?(client.time_precision) &&
|
26
|
+
!val.is_a?(Numeric)
|
27
27
|
warn(
|
28
28
|
"Influxer doesn't automatically cast Time and String values " \
|
29
29
|
"to '#{client.time_precision}' precision. " \
|
@@ -40,6 +40,18 @@ module Influxer
|
|
40
40
|
"#{factorize_timestamp(val, TIME_FACTORS.fetch(precision))}#{precision}"
|
41
41
|
end
|
42
42
|
|
43
|
+
def quote_timestamp_for_write(val, client)
|
44
|
+
if !TIME_FACTORS.key?(client.time_precision) &&
|
45
|
+
!val.is_a?(Numeric)
|
46
|
+
raise ArgumentError,
|
47
|
+
"Influxer doesn't support quoting #{val} " \
|
48
|
+
" with '#{client.time_precision}' precision. " \
|
49
|
+
"Please, convert to numeric value yourself"
|
50
|
+
end
|
51
|
+
|
52
|
+
factorize_timestamp(val, TIME_FACTORS.fetch(client.time_precision))
|
53
|
+
end
|
54
|
+
|
43
55
|
def factorize_timestamp(val, factor)
|
44
56
|
case val
|
45
57
|
when Numeric
|
@@ -1,14 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
|
+
require "influxer/metrics/relation/time_query"
|
5
|
+
require "influxer/metrics/relation/calculations"
|
6
|
+
require "influxer/metrics/relation/where_clause"
|
7
|
+
require "influxer/metrics/quoting/timestamp"
|
8
8
|
|
9
9
|
module Influxer
|
10
10
|
# Relation is used to build queries
|
11
|
-
# rubocop:disable Metrics/ClassLength
|
12
11
|
class Relation
|
13
12
|
include Influxer::TimeQuery
|
14
13
|
include Influxer::Calculations
|
@@ -23,11 +22,11 @@ module Influxer
|
|
23
22
|
|
24
23
|
MULTI_KEY_METHODS = %i[fanout].freeze
|
25
24
|
|
26
|
-
SINGLE_VALUE_METHODS = %i[fill time limit offset slimit soffset from normalized].freeze
|
25
|
+
SINGLE_VALUE_METHODS = %i[fill time limit offset slimit soffset from normalized timezone].freeze
|
27
26
|
|
28
27
|
MULTI_VALUE_SIMPLE_METHODS = %i[select group].freeze
|
29
28
|
|
30
|
-
SINGLE_VALUE_SIMPLE_METHODS = %i[fill limit offset slimit soffset from].freeze
|
29
|
+
SINGLE_VALUE_SIMPLE_METHODS = %i[fill limit offset slimit soffset from timezone].freeze
|
31
30
|
|
32
31
|
MULTI_VALUE_METHODS.each do |name|
|
33
32
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
@@ -122,6 +121,13 @@ module Influxer
|
|
122
121
|
self
|
123
122
|
end
|
124
123
|
|
124
|
+
def timezone(val)
|
125
|
+
return self if val.blank?
|
126
|
+
|
127
|
+
@values[:timezone] = val
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
125
131
|
def order(val)
|
126
132
|
case val
|
127
133
|
when Hash
|
@@ -144,22 +150,23 @@ module Influxer
|
|
144
150
|
|
145
151
|
sql << "from #{build_series_name}"
|
146
152
|
|
147
|
-
sql << "where #{where_values.join(
|
153
|
+
sql << "where #{where_values.join(" and ")}" unless where_values.empty?
|
148
154
|
|
149
155
|
unless group_values.empty? && time_value.nil?
|
150
|
-
group_fields = (time_value.nil? ? [] : [
|
156
|
+
group_fields = (time_value.nil? ? [] : ["time(" + @values[:time] + ")"]) + group_values
|
151
157
|
group_fields.uniq!
|
152
|
-
sql << "group by #{group_fields.join(
|
158
|
+
sql << "group by #{group_fields.join(", ")}"
|
153
159
|
end
|
154
160
|
|
155
161
|
sql << "fill(#{fill_value})" unless fill_value.nil?
|
156
162
|
|
157
|
-
sql << "order by #{order_values.uniq.join(
|
163
|
+
sql << "order by #{order_values.uniq.join(",")}" unless order_values.empty?
|
158
164
|
|
159
165
|
sql << "limit #{limit_value}" unless limit_value.nil?
|
160
166
|
sql << "offset #{offset_value}" unless offset_value.nil?
|
161
167
|
sql << "slimit #{slimit_value}" unless slimit_value.nil?
|
162
168
|
sql << "soffset #{soffset_value}" unless soffset_value.nil?
|
169
|
+
sql << "TZ('#{timezone_value}')" unless timezone_value.blank?
|
163
170
|
sql.join " "
|
164
171
|
end
|
165
172
|
# rubocop:enable Metrics/AbcSize
|
@@ -169,14 +176,15 @@ module Influxer
|
|
169
176
|
|
170
177
|
def to_a
|
171
178
|
return @records if loaded?
|
179
|
+
|
172
180
|
load
|
173
181
|
end
|
174
182
|
|
175
183
|
def inspect
|
176
184
|
entries = to_a.take(11).map!(&:inspect)
|
177
|
-
entries[10] =
|
185
|
+
entries[10] = "..." if entries.size == 11
|
178
186
|
|
179
|
-
"#<#{self.class.name} [#{entries.join(
|
187
|
+
"#<#{self.class.name} [#{entries.join(", ")}]>"
|
180
188
|
end
|
181
189
|
|
182
190
|
def empty?
|
@@ -205,11 +213,15 @@ module Influxer
|
|
205
213
|
end
|
206
214
|
|
207
215
|
def delete_all
|
208
|
-
sql =
|
216
|
+
sql = if where_contains_time?
|
217
|
+
["delete"]
|
218
|
+
else
|
219
|
+
["drop series"]
|
220
|
+
end
|
209
221
|
|
210
|
-
sql << "from #{@instance.series}"
|
222
|
+
sql << "from #{@instance.series(write: true)}"
|
211
223
|
|
212
|
-
sql << "where #{where_values.join(
|
224
|
+
sql << "where #{where_values.join(" and ")}" unless where_values.empty?
|
213
225
|
|
214
226
|
sql = sql.join " "
|
215
227
|
|
@@ -228,6 +240,7 @@ module Influxer
|
|
228
240
|
# rubocop:disable Metrics/MethodLength
|
229
241
|
def merge!(rel)
|
230
242
|
return self if rel.nil?
|
243
|
+
|
231
244
|
MULTI_VALUE_METHODS.each do |method|
|
232
245
|
(@values[method] ||= []).concat(rel.values[method]).uniq! unless rel.values[method].nil?
|
233
246
|
end
|
@@ -280,6 +293,7 @@ module Influxer
|
|
280
293
|
|
281
294
|
def get_points(list)
|
282
295
|
return list if normalized?
|
296
|
+
|
283
297
|
list.reduce([]) do |a, e|
|
284
298
|
a + e.fetch("values", []).map { |v| inject_tags(v, e["tags"] || {}) }
|
285
299
|
end
|
@@ -291,11 +305,13 @@ module Influxer
|
|
291
305
|
|
292
306
|
def method_missing(method, *args, &block)
|
293
307
|
return super unless @klass.respond_to?(method)
|
308
|
+
|
294
309
|
merge!(scoping { @klass.public_send(method, *args, &block) })
|
295
310
|
end
|
296
311
|
|
297
312
|
def respond_to_missing?(method, *args)
|
298
313
|
return true if @klass.respond_to?(method)
|
314
|
+
|
299
315
|
super
|
300
316
|
end
|
301
317
|
end
|
@@ -21,7 +21,7 @@ module Influxer
|
|
21
21
|
|
22
22
|
def percentile(name, val, alias_name = nil)
|
23
23
|
@values[:has_calculations] = true
|
24
|
-
select_values << "percentile(#{name}, #{val})#{alias_name ?
|
24
|
+
select_values << "percentile(#{name}, #{val})#{alias_name ? " as " + alias_name.to_s : ""}"
|
25
25
|
self
|
26
26
|
end
|
27
27
|
end
|
@@ -3,14 +3,15 @@
|
|
3
3
|
module Influxer
|
4
4
|
module TimeQuery #:nodoc:
|
5
5
|
TIME_ALIASES = {
|
6
|
-
hour:
|
7
|
-
minute:
|
8
|
-
second:
|
9
|
-
ms:
|
10
|
-
u:
|
11
|
-
week:
|
12
|
-
day:
|
13
|
-
month:
|
6
|
+
hour: "1h",
|
7
|
+
minute: "1m",
|
8
|
+
second: "1s",
|
9
|
+
ms: "1ms",
|
10
|
+
u: "1u",
|
11
|
+
week: "1w",
|
12
|
+
day: "1d",
|
13
|
+
month: "30d",
|
14
|
+
year: "365d"
|
14
15
|
}.freeze
|
15
16
|
|
16
17
|
FILL_RESERVED = %i[null previous none].freeze
|
@@ -27,10 +28,10 @@ module Influxer
|
|
27
28
|
# # select * from metrics group by time(4d) fill(0)
|
28
29
|
def time(val, options = {})
|
29
30
|
@values[:time] = if val.is_a?(Symbol)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
TIME_ALIASES[val] || "1" + val.to_s
|
32
|
+
else
|
33
|
+
val
|
34
|
+
end
|
34
35
|
|
35
36
|
build_fill(options[:fill])
|
36
37
|
self
|
@@ -50,7 +51,7 @@ module Influxer
|
|
50
51
|
def past(val)
|
51
52
|
case val
|
52
53
|
when Symbol
|
53
|
-
where("time > now() - #{TIME_ALIASES[val] || (
|
54
|
+
where("time > now() - #{TIME_ALIASES[val] || ("1" + val.to_s)}")
|
54
55
|
when String
|
55
56
|
where("time > now() - #{val}")
|
56
57
|
else
|
@@ -75,6 +76,7 @@ module Influxer
|
|
75
76
|
|
76
77
|
def build_fill(val)
|
77
78
|
return if val.nil?
|
79
|
+
|
78
80
|
fill(FILL_RESERVED.include?(val) ? val.to_s : val.to_i)
|
79
81
|
end
|
80
82
|
end
|
@@ -29,6 +29,7 @@ module Influxer
|
|
29
29
|
|
30
30
|
def load
|
31
31
|
return if @null_relation
|
32
|
+
|
32
33
|
super
|
33
34
|
end
|
34
35
|
|
@@ -57,14 +58,15 @@ module Influxer
|
|
57
58
|
when NilClass
|
58
59
|
build_eql(key, /.*/, !negate)
|
59
60
|
when Regexp
|
60
|
-
"#{key}#{negate ?
|
61
|
+
"#{key}#{negate ? " !~ " : " =~ "}#{val.inspect}"
|
61
62
|
when Array
|
62
|
-
return build_none if val.empty?
|
63
|
+
return build_none(negate) if val.empty?
|
64
|
+
|
63
65
|
build_in(key, val, negate)
|
64
66
|
when Range
|
65
67
|
build_range(key, val, negate)
|
66
68
|
else
|
67
|
-
"#{key}#{negate ?
|
69
|
+
"#{key}#{negate ? " <> " : " = "}#{quoted(val, key)}"
|
68
70
|
end
|
69
71
|
end
|
70
72
|
# rubocop:enable Metrics/CyclomaticComplexity
|
@@ -75,7 +77,7 @@ module Influxer
|
|
75
77
|
arr.each do |val|
|
76
78
|
buf << build_eql(key, val, negate)
|
77
79
|
end
|
78
|
-
buf.join(negate ?
|
80
|
+
buf.join(negate ? " and " : " or ").to_s
|
79
81
|
end
|
80
82
|
|
81
83
|
# rubocop: disable Metrics/AbcSize, Metrics/MethodLength, Style/IfInsideElse
|
@@ -83,24 +85,30 @@ module Influxer
|
|
83
85
|
if val.exclude_end?
|
84
86
|
# begin...end range
|
85
87
|
if negate
|
86
|
-
"#{key} < #{quoted(val.begin)} or #{key} >= #{quoted(val.end)}"
|
88
|
+
"#{key} < #{quoted(val.begin, key)} or #{key} >= #{quoted(val.end, key)}"
|
87
89
|
else
|
88
|
-
"#{key} >= #{quoted(val.begin)} and #{key} < #{quoted(val.end)}"
|
90
|
+
"#{key} >= #{quoted(val.begin, key)} and #{key} < #{quoted(val.end, key)}"
|
89
91
|
end
|
90
92
|
else
|
91
93
|
# begin..end range
|
92
94
|
if negate
|
93
|
-
"#{key} < #{quoted(val.begin)} or #{key} > #{quoted(val.end)}"
|
95
|
+
"#{key} < #{quoted(val.begin, key)} or #{key} > #{quoted(val.end, key)}"
|
94
96
|
else
|
95
|
-
"#{key} >= #{quoted(val.begin)} and #{key} <= #{quoted(val.end)}"
|
97
|
+
"#{key} >= #{quoted(val.begin, key)} and #{key} <= #{quoted(val.end, key)}"
|
96
98
|
end
|
97
99
|
end
|
98
100
|
end
|
99
101
|
# rubocop: enable Metrics/AbcSize, Metrics/MethodLength, Style/IfInsideElse
|
100
102
|
|
101
|
-
def build_none
|
102
|
-
@null_relation =
|
103
|
-
|
103
|
+
def build_none(negate = false)
|
104
|
+
@null_relation = !negate
|
105
|
+
negate ? "time >= 0" : "time < 0"
|
106
|
+
end
|
107
|
+
|
108
|
+
def where_contains_time?
|
109
|
+
where_values.any? do |where_clause|
|
110
|
+
/time( )/ === where_clause
|
111
|
+
end
|
104
112
|
end
|
105
113
|
end
|
106
114
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "influxer/metrics/scoping/default"
|
4
|
+
require "influxer/metrics/scoping/named"
|
5
5
|
|
6
6
|
if Influxer.active_model3?
|
7
|
-
require
|
7
|
+
require "influxer/metrics/scoping/old_current_scope"
|
8
8
|
else
|
9
|
-
require
|
9
|
+
require "influxer/metrics/scoping/current_scope"
|
10
10
|
end
|
11
11
|
|
12
12
|
module Influxer
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "active_support/per_thread_registry"
|
4
4
|
|
5
5
|
module Influxer
|
6
6
|
module Scoping
|
@@ -39,6 +39,7 @@ module Influxer
|
|
39
39
|
|
40
40
|
def raise_invalid_scope_type!(scope_type)
|
41
41
|
return if VALID_SCOPE_TYPES.include?(scope_type)
|
42
|
+
|
42
43
|
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. \
|
43
44
|
Scope types must be included in VALID_SCOPE_TYPES"
|
44
45
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "active_support/concern"
|
4
4
|
|
5
5
|
module Influxer
|
6
6
|
module Scoping
|
@@ -10,6 +10,7 @@ module Influxer
|
|
10
10
|
module ClassMethods
|
11
11
|
def scope(name, scope)
|
12
12
|
raise "Scope not defined: #{name}" if scope.nil? || !scope.respond_to?(:call)
|
13
|
+
|
13
14
|
singleton_class.send(:define_method, name) do |*args|
|
14
15
|
rel = all
|
15
16
|
rel.merge!(rel.scoping { scope.call(*args) })
|
data/lib/influxer/model.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "active_support"
|
4
4
|
|
5
5
|
module Influxer
|
6
6
|
# Add `has_metrics` method to AR::Base
|
@@ -8,9 +8,6 @@ module Influxer
|
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
module ClassMethods # :nodoc:
|
11
|
-
# rubocop:disable Naming/PredicateName
|
12
|
-
# rubocop:disable Metrics/MethodLength
|
13
|
-
# rubocop:disable Metrics/AbcSize
|
14
11
|
def has_metrics(*args, **params)
|
15
12
|
metrics_name = args.empty? ? "metrics" : args.first.to_s
|
16
13
|
|
@@ -22,12 +19,10 @@ module Influxer
|
|
22
19
|
foreign_key = params.fetch(:foreign_key, to_s.foreign_key)
|
23
20
|
|
24
21
|
define_method(metrics_name) do
|
25
|
-
rel_attrs = foreign_key ? {
|
22
|
+
rel_attrs = foreign_key ? {foreign_key => id} : {}
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
rel_attrs[key] = send(key)
|
30
|
-
end
|
24
|
+
attrs&.each do |key|
|
25
|
+
rel_attrs[key] = send(key)
|
31
26
|
end
|
32
27
|
Relation.new klass, attributes: rel_attrs
|
33
28
|
end
|
@@ -6,10 +6,10 @@ module Influxer
|
|
6
6
|
class Client
|
7
7
|
def query(sql, options = {})
|
8
8
|
log_sql(sql) do
|
9
|
-
if !options.fetch(:cache, true)
|
10
|
-
super(sql, options)
|
9
|
+
if !(options.fetch(:cache, true) && Influxer.config.cache_enabled)
|
10
|
+
super(sql, **options)
|
11
11
|
else
|
12
|
-
Rails.cache.fetch(normalized_cache_key(sql), cache_options(sql)) { super(sql, options) }
|
12
|
+
Rails.cache.fetch(normalized_cache_key(sql), **cache_options(sql)) { super(sql, **options) }
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -33,13 +33,13 @@ module Influxer
|
|
33
33
|
# of config.cache if defined
|
34
34
|
def cache_options(sql = nil)
|
35
35
|
options = Influxer.config.cache.dup
|
36
|
-
options[:expires_in] = (options[:cache_now_for] || 60) if
|
37
|
-
options
|
36
|
+
options[:expires_in] = (options[:cache_now_for] || 60) if /\snow\(\)/.match?(sql)
|
37
|
+
options.symbolize_keys
|
38
38
|
end
|
39
39
|
|
40
40
|
# add prefix; remove whitespaces
|
41
41
|
def normalized_cache_key(sql)
|
42
|
-
"influxer:#{sql.gsub(/\s*/,
|
42
|
+
"influxer:#{sql.gsub(/\s*/, "")}"
|
43
43
|
end
|
44
44
|
|
45
45
|
def logger
|