influxer 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -2
- data/.travis.yml +3 -2
- data/Changelog.md +19 -0
- data/Gemfile +0 -13
- data/MIT-LICENSE +1 -1
- data/README.md +51 -6
- data/gemfiles/rails42.gemfile +7 -0
- data/influxer.gemspec +7 -5
- data/lib/influxer/client.rb +46 -0
- data/lib/influxer/config.rb +10 -0
- data/lib/influxer/metrics/fanout.rb +53 -0
- data/lib/influxer/metrics/metrics.rb +33 -5
- data/lib/influxer/metrics/relation/fanout_query.rb +33 -0
- data/lib/influxer/metrics/relation/time_query.rb +73 -0
- data/lib/influxer/metrics/relation.rb +177 -39
- data/lib/influxer/metrics/scoping/default.rb +28 -0
- data/lib/influxer/metrics/scoping/named.rb +18 -0
- data/lib/influxer/metrics/scoping.rb +56 -0
- data/lib/influxer/model.rb +3 -1
- data/lib/influxer/version.rb +1 -1
- data/spec/client_spec.rb +34 -0
- data/spec/dummy/app/metrics/testo_metrics.rb +1 -1
- data/spec/dummy/app/models/testo.rb +2 -0
- data/spec/dummy/config/application.rb +1 -0
- data/spec/dummy/config/environments/test.rb +0 -1
- data/spec/metrics/fanout_spec.rb +46 -0
- data/spec/metrics/metrics_spec.rb +42 -7
- data/spec/metrics/relation_spec.rb +199 -51
- data/spec/metrics/scoping_spec.rb +68 -0
- data/spec/model/testo_spec.rb +12 -3
- data/spec/spec_helper.rb +1 -1
- metadata +78 -26
@@ -1,12 +1,72 @@
|
|
1
|
+
require 'influxer/metrics/relation/time_query'
|
2
|
+
require 'influxer/metrics/relation/fanout_query'
|
3
|
+
|
1
4
|
module Influxer
|
2
5
|
class Relation
|
6
|
+
attr_reader :values
|
7
|
+
|
8
|
+
include Influxer::TimeQuery
|
9
|
+
include Influxer::FanoutQuery
|
3
10
|
|
11
|
+
MULTI_VALUE_METHODS = [:select, :where, :group]
|
12
|
+
|
13
|
+
MULTI_KEY_METHODS = [:fanout]
|
14
|
+
|
15
|
+
SINGLE_VALUE_METHODS = [:fill, :limit, :merge, :time]
|
16
|
+
|
17
|
+
MULTI_VALUE_SIMPLE_METHODS = [:select, :group]
|
18
|
+
|
19
|
+
SINGLE_VALUE_SIMPLE_METHODS = [:fill, :limit, :merge]
|
20
|
+
|
21
|
+
MULTI_VALUE_METHODS.each do |name|
|
22
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
23
|
+
def #{name}_values # def select_values
|
24
|
+
@values[:#{name}] ||= [] # @values[:select] || []
|
25
|
+
end # end
|
26
|
+
CODE
|
27
|
+
end
|
28
|
+
|
29
|
+
MULTI_KEY_METHODS.each do |name|
|
30
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
31
|
+
def #{name}_values # def fanout_values
|
32
|
+
@values[:#{name}] ||= {} # @values[:fanout] || {}
|
33
|
+
end # end
|
34
|
+
CODE
|
35
|
+
end
|
36
|
+
|
37
|
+
SINGLE_VALUE_METHODS.each do |name|
|
38
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
39
|
+
def #{name}_value # def limit_value
|
40
|
+
@values[:#{name}] # @values[:limit]
|
41
|
+
end # end
|
42
|
+
CODE
|
43
|
+
end
|
44
|
+
|
45
|
+
SINGLE_VALUE_SIMPLE_METHODS.each do |name|
|
46
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
47
|
+
def #{name}(val) # def limit(val)
|
48
|
+
@values[:#{name}] = val # @value[:limit] = val
|
49
|
+
self # self
|
50
|
+
end # end
|
51
|
+
CODE
|
52
|
+
end
|
53
|
+
|
54
|
+
MULTI_VALUE_SIMPLE_METHODS.each do |name|
|
55
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
56
|
+
def #{name}(*args) # def select(*args)
|
57
|
+
#{name}_values.concat args.map(&:to_s) # select_values.concat args.map(&:to_s)
|
58
|
+
self # self
|
59
|
+
end # end
|
60
|
+
CODE
|
61
|
+
end
|
62
|
+
|
4
63
|
# Initialize new Relation for 'klass' (Class) metrics.
|
5
64
|
#
|
6
65
|
# Available params:
|
7
66
|
# :attributes - hash of attributes to be included to new Metrics object and where clause of Relation
|
8
67
|
#
|
9
68
|
def initialize(klass, params = {})
|
69
|
+
@klass = klass
|
10
70
|
@instance = klass.new params[:attributes]
|
11
71
|
self.reset
|
12
72
|
self.where(params[:attributes]) if params[:attributes].present?
|
@@ -26,59 +86,46 @@ module Influxer
|
|
26
86
|
@instance
|
27
87
|
end
|
28
88
|
|
29
|
-
# accepts strings and symbols only
|
30
|
-
def select(*args)
|
31
|
-
return self if args.empty?
|
32
|
-
@select_values.concat args
|
33
|
-
self
|
34
|
-
end
|
35
|
-
|
36
89
|
# accepts hash or strings conditions
|
37
|
-
# TODO: add sanitization and array support
|
38
|
-
|
39
90
|
def where(*args,**hargs)
|
40
|
-
|
41
|
-
|
42
|
-
unless hargs.empty?
|
43
|
-
hargs.each do |key, val|
|
44
|
-
@where_values << "(#{key}=#{quoted(val)})"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
self
|
48
|
-
end
|
49
|
-
|
50
|
-
def group(*args)
|
51
|
-
return self if args.empty?
|
52
|
-
@group_values.concat args
|
91
|
+
build_where(args, hargs, false)
|
53
92
|
self
|
54
93
|
end
|
55
94
|
|
56
|
-
def
|
57
|
-
|
95
|
+
def not(*args, **hargs)
|
96
|
+
build_where(args, hargs, true)
|
58
97
|
self
|
59
98
|
end
|
60
99
|
|
61
100
|
def to_sql
|
62
101
|
sql = ["select"]
|
63
102
|
|
64
|
-
if
|
103
|
+
if select_values.empty?
|
65
104
|
sql << "*"
|
66
105
|
else
|
67
|
-
sql <<
|
106
|
+
sql << select_values.uniq.join(",")
|
68
107
|
end
|
69
108
|
|
70
|
-
sql << "from #{
|
109
|
+
sql << "from #{ build_series_name }"
|
110
|
+
|
111
|
+
unless merge_value.nil?
|
112
|
+
sql << "merge #{ @instance.quote_series(merge_value) }"
|
113
|
+
end
|
114
|
+
|
115
|
+
unless group_values.empty? and time_value.nil?
|
116
|
+
sql << "group by #{ (time_value.nil? ? [] : ['time('+@values[:time]+')']).concat(group_values).uniq.join(",") }"
|
117
|
+
end
|
71
118
|
|
72
|
-
unless
|
73
|
-
sql << "
|
119
|
+
unless fill_value.nil?
|
120
|
+
sql << "fill(#{ fill_value })"
|
74
121
|
end
|
75
122
|
|
76
|
-
unless
|
77
|
-
sql << "where #{
|
123
|
+
unless where_values.empty?
|
124
|
+
sql << "where #{ where_values.join(" and ") }"
|
78
125
|
end
|
79
126
|
|
80
|
-
unless
|
81
|
-
sql << "limit #{
|
127
|
+
unless limit_value.nil?
|
128
|
+
sql << "limit #{ limit_value }"
|
82
129
|
end
|
83
130
|
sql.join " "
|
84
131
|
end
|
@@ -101,12 +148,96 @@ module Influxer
|
|
101
148
|
end
|
102
149
|
|
103
150
|
def delete_all
|
151
|
+
sql = ["delete"]
|
152
|
+
|
153
|
+
sql << "from #{@instance.series}"
|
154
|
+
|
155
|
+
unless where_values.empty?
|
156
|
+
sql << "where #{where_values.join(" and ")}"
|
157
|
+
end
|
158
|
+
|
159
|
+
sql = sql.join " "
|
160
|
+
|
161
|
+
@instance.client.query sql
|
162
|
+
end
|
163
|
+
|
164
|
+
def scoping
|
165
|
+
previous, @klass.current_scope = @klass.current_scope, self
|
166
|
+
yield
|
167
|
+
ensure
|
168
|
+
@klass.current_scope = previous
|
169
|
+
end
|
170
|
+
|
171
|
+
def merge!(rel)
|
172
|
+
return self if rel.nil?
|
173
|
+
MULTI_VALUE_METHODS.each do |method|
|
174
|
+
(@values[method]||=[]).concat(rel.values[method]).uniq! unless rel.values[method].nil?
|
175
|
+
end
|
176
|
+
|
177
|
+
MULTI_KEY_METHODS.each do |method|
|
178
|
+
(@values[method]||={}).merge!(rel.values[method]) unless rel.values[method].nil?
|
179
|
+
end
|
180
|
+
|
181
|
+
SINGLE_VALUE_METHODS.each do |method|
|
182
|
+
@values[method] = rel.values[method] unless rel.values[method].nil?
|
183
|
+
end
|
104
184
|
|
185
|
+
self
|
105
186
|
end
|
106
187
|
|
107
188
|
protected
|
189
|
+
def build_where(args, hargs, negate)
|
190
|
+
case
|
191
|
+
when (args.present? and args[0].is_a?(String))
|
192
|
+
where_values.concat args.map{|str| "(#{str})"}
|
193
|
+
when hargs.present?
|
194
|
+
build_hash_where(hargs, negate)
|
195
|
+
else
|
196
|
+
false
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def build_hash_where(hargs, negate = false)
|
201
|
+
hargs.each do |key, val|
|
202
|
+
if @klass.fanout?(key)
|
203
|
+
build_fanout(key,val)
|
204
|
+
else
|
205
|
+
where_values << "(#{ build_eql(key,val,negate) })"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def build_eql(key,val,negate)
|
211
|
+
case val
|
212
|
+
when Regexp
|
213
|
+
"#{key}#{ negate ? '!~' : '=~'}#{val.inspect}"
|
214
|
+
when Array
|
215
|
+
build_in(key,val,negate)
|
216
|
+
when Range
|
217
|
+
build_range(key,val,negate)
|
218
|
+
else
|
219
|
+
"#{key}#{ negate ? '<>' : '='}#{quoted(val)}"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def build_in(key, arr, negate)
|
224
|
+
buf = []
|
225
|
+
arr.each do |val|
|
226
|
+
buf << build_eql(key,val,negate)
|
227
|
+
end
|
228
|
+
"#{ buf.join( negate ? ' and ' : ' or ') }"
|
229
|
+
end
|
230
|
+
|
231
|
+
def build_range(key,val,negate)
|
232
|
+
unless negate
|
233
|
+
"#{key}>#{quoted(val.begin)} and #{key}<#{quoted(val.end)}"
|
234
|
+
else
|
235
|
+
"#{key}<#{quoted(val.begin)} and #{key}>#{quoted(val.end)}"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
108
239
|
def load
|
109
|
-
@records = @instance.client.
|
240
|
+
@records = get_points(@instance.client.cached_query(to_sql))
|
110
241
|
@loaded = true
|
111
242
|
end
|
112
243
|
|
@@ -115,10 +246,7 @@ module Influxer
|
|
115
246
|
end
|
116
247
|
|
117
248
|
def reset
|
118
|
-
@
|
119
|
-
@select_values = []
|
120
|
-
@group_values = []
|
121
|
-
@where_values = []
|
249
|
+
@values = {}
|
122
250
|
@records = nil
|
123
251
|
@loaded = false
|
124
252
|
self
|
@@ -131,7 +259,7 @@ module Influxer
|
|
131
259
|
end
|
132
260
|
|
133
261
|
def quoted(val)
|
134
|
-
if val.is_a?(String)
|
262
|
+
if val.is_a?(String) or val.is_a?(Symbol)
|
135
263
|
"'#{val}'"
|
136
264
|
elsif val.kind_of?(Time) or val.kind_of?(DateTime)
|
137
265
|
"#{val.to_i}s"
|
@@ -139,5 +267,15 @@ module Influxer
|
|
139
267
|
val.to_s
|
140
268
|
end
|
141
269
|
end
|
270
|
+
|
271
|
+
def method_missing(method, *args, &block)
|
272
|
+
if @klass.respond_to?(method)
|
273
|
+
merge!(scoping { @klass.public_send(method, *args, &block) })
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def get_points(hash)
|
278
|
+
hash.values.reduce([],:+)
|
279
|
+
end
|
142
280
|
end
|
143
281
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Influxer
|
2
|
+
module Scoping
|
3
|
+
module Default
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :default_scopes
|
8
|
+
self.default_scopes = []
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def default_scope(scope)
|
13
|
+
self.default_scopes += [scope] unless scope.nil?
|
14
|
+
end
|
15
|
+
|
16
|
+
def unscoped
|
17
|
+
Relation.new self
|
18
|
+
end
|
19
|
+
|
20
|
+
def default_scoped
|
21
|
+
self.default_scopes.inject(Relation.new(self)) do |rel, scope|
|
22
|
+
rel.merge!(rel.scoping{ scope.call })
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Influxer
|
2
|
+
module Scoping
|
3
|
+
module Named
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def scope(name, scope)
|
8
|
+
raise Error.new("Scope not defined: #{name}") if scope.nil? or !scope.respond_to?(:call)
|
9
|
+
singleton_class.send(:define_method, name) do |*args|
|
10
|
+
rel = all
|
11
|
+
rel.merge!(rel.scoping { scope.call(*args) })
|
12
|
+
rel
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'active_support/per_thread_registry'
|
2
|
+
require 'influxer/metrics/scoping/default'
|
3
|
+
require 'influxer/metrics/scoping/named'
|
4
|
+
|
5
|
+
module Influxer
|
6
|
+
module Scoping
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class Error < StandardError; end;
|
10
|
+
|
11
|
+
included do
|
12
|
+
include Default
|
13
|
+
include Named
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def current_scope #:nodoc:
|
18
|
+
ScopeRegistry.value_for(:current_scope, name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def current_scope=(scope) #:nodoc:
|
22
|
+
ScopeRegistry.set_value_for(:current_scope, name, scope)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ScopeRegistry # :nodoc:
|
27
|
+
extend ActiveSupport::PerThreadRegistry
|
28
|
+
|
29
|
+
VALID_SCOPE_TYPES = [:current_scope]
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@registry = Hash.new { |hash, key| hash[key] = {} }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Obtains the value for a given +scope_name+ and +variable_name+.
|
36
|
+
def value_for(scope_type, variable_name)
|
37
|
+
raise_invalid_scope_type!(scope_type)
|
38
|
+
@registry[scope_type][variable_name]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sets the +value+ for a given +scope_type+ and +variable_name+.
|
42
|
+
def set_value_for(scope_type, variable_name, value)
|
43
|
+
raise_invalid_scope_type!(scope_type)
|
44
|
+
@registry[scope_type][variable_name] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def raise_invalid_scope_type!(scope_type)
|
50
|
+
if !VALID_SCOPE_TYPES.include?(scope_type)
|
51
|
+
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/influxer/model.rb
CHANGED
@@ -17,8 +17,10 @@ module Influxer
|
|
17
17
|
attrs = params[:inherits]
|
18
18
|
end
|
19
19
|
|
20
|
+
_foreign_key = params.key?(:foreign_key) ? params[:foreign_key] : self.to_s.foreign_key
|
21
|
+
|
20
22
|
define_method(metrics_name) do
|
21
|
-
rel_attrs = {
|
23
|
+
rel_attrs = _foreign_key ? {_foreign_key => self.id} : {}
|
22
24
|
|
23
25
|
unless attrs.nil?
|
24
26
|
attrs.each do |key|
|
data/lib/influxer/version.rb
CHANGED
data/spec/client_spec.rb
CHANGED
@@ -2,6 +2,10 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Influxer::Client do
|
4
4
|
|
5
|
+
after(:each) do
|
6
|
+
Rails.cache.clear
|
7
|
+
end
|
8
|
+
|
5
9
|
let(:conf) { Influxer.config }
|
6
10
|
let(:client) { Influxer.client }
|
7
11
|
|
@@ -11,4 +15,34 @@ describe Influxer::Client do
|
|
11
15
|
expect(client.database).to eq conf.database
|
12
16
|
end
|
13
17
|
|
18
|
+
describe "cache" do
|
19
|
+
before do
|
20
|
+
allow_any_instance_of(Influxer::Client).to receive(:query) do |_, sql|
|
21
|
+
sql
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:q) { "list series" }
|
26
|
+
|
27
|
+
after(:each) do
|
28
|
+
conf.cache = false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should write data to cache" do
|
32
|
+
conf.cache = {}
|
33
|
+
|
34
|
+
client.cached_query(q)
|
35
|
+
expect(Rails.cache.exist?("influxer:listseries")).to be_truthy
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should write data to cache with expiration" do
|
39
|
+
conf.cache = {expires_in: 1}
|
40
|
+
|
41
|
+
client.cached_query(q)
|
42
|
+
expect(Rails.cache.exist?("influxer:listseries")).to be_truthy
|
43
|
+
|
44
|
+
sleep 2
|
45
|
+
expect(Rails.cache.exist?("influxer:listseries")).to be_falsey
|
46
|
+
end
|
47
|
+
end
|
14
48
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
class Testo < ActiveRecord::Base
|
2
2
|
has_metrics
|
3
3
|
has_metrics :testo_metrics, class_name: "TestoMetrics", inherits: [:receipt_id]
|
4
|
+
has_metrics :testo2_metrics, class_name: "TestoMetrics", foreign_key: :testo
|
5
|
+
has_metrics :custom_metrics, class_name: "TestoMetrics", foreign_key: nil
|
4
6
|
end
|
@@ -7,6 +7,7 @@ require "influxer"
|
|
7
7
|
|
8
8
|
module Dummy
|
9
9
|
class Application < Rails::Application
|
10
|
+
config.cache_store = :memory_store
|
10
11
|
# Settings in config/environments/* take precedence over those specified here.
|
11
12
|
# Application configuration should go into files in config/initializers
|
12
13
|
# -- all .rb files in that directory are automatically loaded.
|
@@ -1,6 +1,5 @@
|
|
1
1
|
Dummy::Application.configure do
|
2
2
|
# Settings specified here will take precedence over those in config/application.rb.
|
3
|
-
|
4
3
|
# The test environment is used exclusively to run your application's
|
5
4
|
# test suite. You never need to work with it otherwise. Remember that
|
6
5
|
# your test database is "scratch space" for the test suite and is wiped
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Influxer::Metrics do
|
4
|
+
before do
|
5
|
+
allow_any_instance_of(Influxer::Client).to receive(:query) do |_, sql|
|
6
|
+
sql
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:dummy) do
|
11
|
+
Class.new(Influxer::Metrics) do
|
12
|
+
set_series 'dummy'
|
13
|
+
default_scope -> { time(:hour) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:doomy) do
|
18
|
+
Class.new(dummy) do
|
19
|
+
scope :by_user, -> (id) { where(user: id) if id.present? }
|
20
|
+
scope :hourly, -> { where(by: :hour).time(nil) }
|
21
|
+
scope :daily, -> { where(by: :day).time(nil) }
|
22
|
+
|
23
|
+
fanout :by, :user, :account, delimeter: "."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:dappy) do
|
28
|
+
Class.new(doomy) do
|
29
|
+
fanout :user, delimeter: "_"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "fanouts" do
|
34
|
+
it "should work with one fanout" do
|
35
|
+
expect(doomy.by_user(1).to_sql).to eq "select * from \"dummy.user.1\" group by time(1h)"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should work with several fanouts" do
|
39
|
+
expect(dappy.by_user(1).hourly.to_sql).to eq "select * from \"dummy_by_hour_user_1\""
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should work with regexp fanouts" do
|
43
|
+
expect(dappy.where(dummy_id: 100).by_user(/[1-3]/).daily.to_sql).to eq "select * from merge(/^dummy_by_day_user_[1-3]$/) where (dummy_id=100)"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Influxer::Metrics do
|
4
|
+
before do
|
5
|
+
allow_any_instance_of(Influxer::Client).to receive(:query) do |_, sql|
|
6
|
+
sql
|
7
|
+
end
|
8
|
+
end
|
4
9
|
|
5
10
|
let(:metrics) { Influxer::Metrics.new }
|
6
11
|
let(:metrics_class) { Influxer::Metrics }
|
@@ -16,7 +21,10 @@ describe Influxer::Metrics do
|
|
16
21
|
|
17
22
|
specify { expect(metrics_class).to respond_to :all}
|
18
23
|
specify { expect(metrics_class).to respond_to :where}
|
19
|
-
specify { expect(metrics_class).to respond_to :
|
24
|
+
specify { expect(metrics_class).to respond_to :merge}
|
25
|
+
specify { expect(metrics_class).to respond_to :time}
|
26
|
+
specify { expect(metrics_class).to respond_to :past}
|
27
|
+
specify { expect(metrics_class).to respond_to :since}
|
20
28
|
specify { expect(metrics_class).to respond_to :limit}
|
21
29
|
specify { expect(metrics_class).to respond_to :select}
|
22
30
|
specify { expect(metrics_class).to respond_to :delete_all}
|
@@ -86,12 +94,30 @@ describe Influxer::Metrics do
|
|
86
94
|
end
|
87
95
|
end
|
88
96
|
|
97
|
+
let(:dummy_metrics_2) do
|
98
|
+
Class.new(Influxer::Metrics) do
|
99
|
+
set_series "dummy \"A\""
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
let(:dummy_metrics_3) do
|
104
|
+
Class.new(Influxer::Metrics) do
|
105
|
+
set_series /^.*$/
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
89
109
|
let(:dummy_with_2_series) do
|
90
110
|
Class.new(Influxer::Metrics) do
|
91
111
|
set_series :events, :errors
|
92
112
|
end
|
93
113
|
end
|
94
114
|
|
115
|
+
let(:dummy_with_2_series_quoted) do
|
116
|
+
Class.new(Influxer::Metrics) do
|
117
|
+
set_series "dummy \"A\"", "dummy \"B\""
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
95
121
|
let(:dummy_with_proc_series) do
|
96
122
|
Class.new(Influxer::Metrics) do
|
97
123
|
attributes :user_id, :test_id
|
@@ -99,27 +125,36 @@ describe Influxer::Metrics do
|
|
99
125
|
end
|
100
126
|
end
|
101
127
|
|
102
|
-
|
103
|
-
|
104
128
|
describe "set_series" do
|
105
129
|
it "should set series name from class name by default" do
|
106
|
-
expect(DummyMetrics.series).to eq
|
130
|
+
expect(DummyMetrics.new.series).to eq "\"dummy\""
|
107
131
|
end
|
108
132
|
|
109
133
|
it "should set series from subclass" do
|
110
|
-
expect(dummy_metrics.series).to eq
|
134
|
+
expect(dummy_metrics.new.series).to eq "\"dummies\""
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should set series as regexp" do
|
138
|
+
expect(dummy_metrics_3.new.series).to eq '/^.*$/'
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should set series with quotes" do
|
142
|
+
expect(dummy_metrics_2.new.series).to eq "\"dummy \\\"A\\\"\""
|
111
143
|
end
|
112
144
|
|
113
145
|
it "should set several series" do
|
114
|
-
expect(dummy_with_2_series.series).to eq
|
146
|
+
expect(dummy_with_2_series.new.series).to eq "merge(\"events\",\"errors\")"
|
115
147
|
end
|
116
148
|
|
149
|
+
it "should set several series with quotes" do
|
150
|
+
expect(dummy_with_2_series_quoted.new.series).to eq "merge(\"dummy \\\"A\\\"\",\"dummy \\\"B\\\"\")"
|
151
|
+
end
|
117
152
|
|
118
153
|
it "should set series from proc" do
|
119
154
|
expect(dummy_with_proc_series.series).to be_an_instance_of Proc
|
120
155
|
|
121
156
|
m = dummy_with_proc_series.new user_id: 2, test_id:123
|
122
|
-
expect(
|
157
|
+
expect(m.series).to eq "\"test/123/user/2\""
|
123
158
|
end
|
124
159
|
end
|
125
160
|
|