influxer 1.1.3 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +92 -0
  3. data/{MIT-LICENSE → LICENSE.txt} +1 -1
  4. data/README.md +106 -47
  5. data/lib/influxer.rb +10 -9
  6. data/lib/influxer/client.rb +1 -1
  7. data/lib/influxer/config.rb +5 -4
  8. data/lib/influxer/engine.rb +1 -1
  9. data/lib/influxer/metrics/active_model3/model.rb +2 -4
  10. data/lib/influxer/metrics/metrics.rb +10 -13
  11. data/lib/influxer/metrics/quoting/timestamp.rb +52 -9
  12. data/lib/influxer/metrics/relation.rb +33 -19
  13. data/lib/influxer/metrics/relation/calculations.rb +1 -1
  14. data/lib/influxer/metrics/relation/time_query.rb +15 -13
  15. data/lib/influxer/metrics/relation/where_clause.rb +19 -11
  16. data/lib/influxer/metrics/scoping.rb +4 -4
  17. data/lib/influxer/metrics/scoping/current_scope.rb +2 -1
  18. data/lib/influxer/metrics/scoping/default.rb +1 -1
  19. data/lib/influxer/metrics/scoping/named.rb +2 -1
  20. data/lib/influxer/model.rb +4 -9
  21. data/lib/influxer/rails/client.rb +2 -2
  22. data/lib/influxer/version.rb +1 -1
  23. metadata +30 -95
  24. data/.gitignore +0 -37
  25. data/.rspec +0 -2
  26. data/.rubocop.yml +0 -77
  27. data/.travis.yml +0 -10
  28. data/Changelog.md +0 -103
  29. data/Gemfile +0 -10
  30. data/Rakefile +0 -13
  31. data/gemfiles/rails32.gemfile +0 -7
  32. data/gemfiles/rails42.gemfile +0 -7
  33. data/gemfiles/rails5.gemfile +0 -7
  34. data/influxer.gemspec +0 -33
  35. data/spec/cases/points_spec.rb +0 -36
  36. data/spec/cases/write_points_spec.rb +0 -85
  37. data/spec/client_spec.rb +0 -46
  38. data/spec/fixtures/empty_result.json +0 -21
  39. data/spec/fixtures/single_series.json +0 -29
  40. data/spec/metrics/metrics_spec.rb +0 -283
  41. data/spec/metrics/relation_spec.rb +0 -425
  42. data/spec/metrics/scoping_spec.rb +0 -66
  43. data/spec/model/user_spec.rb +0 -46
  44. data/spec/spec_helper.rb +0 -64
  45. data/spec/support/metrics/action_metrics.rb +0 -5
  46. data/spec/support/metrics/custom_metrics.rb +0 -6
  47. data/spec/support/metrics/dummy_metrics.rb +0 -12
  48. data/spec/support/metrics/user_metrics.rb +0 -6
  49. data/spec/support/metrics/visits_metrics.rb +0 -8
  50. data/spec/support/shared_contexts/shared_query.rb +0 -16
  51. data/spec/support/user.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 36dc815f87b5baba491093838e46cf60537af9eb
4
- data.tar.gz: 3149c0e7950e1986bfefeb78cec5bf8e3b334596
2
+ SHA256:
3
+ metadata.gz: 5fa8f3e80a1c44d03d2217577611586d49e346fef552cc7545a73b39d6824a95
4
+ data.tar.gz: 990ebe156e20a28189949ad6d428188bc498cd70dd53614e777c28317609ed7e
5
5
  SHA512:
6
- metadata.gz: 9555072584dbcb205fafdf5f82615430d729a8e7b56962bda588c1b8319b3c5e2b6a07c238705bb1bec8737b39d85852e63d8dd3bdbd0a7483ba441bb512e806
7
- data.tar.gz: b2b26a45612366170dba61278e5fb3fbeb0ae56b802bf652b8db90fcf4b63372eb3a33386d791fb605c3668a27b3e399001a608df4a156e4755fb15dbfb36f96
6
+ metadata.gz: fa4793fba2f7a21533ef25a18d44ea2e525878a0d5d23cdb94b87287c001b4b776e01c2aa2bd5a8ecc35fb18ec2e1841172f94bb3071f61ea00b1a5e820612d4
7
+ data.tar.gz: '09d22a2c32a82775c39503545197becfd5eda67bdd18251274770dd9e6681d06e6efd13aa84a62e08a406c8fa51b95398efdf74fb5cd2720f40751a404730258'
@@ -0,0 +1,92 @@
1
+ # Change log
2
+
3
+ ## master (unreleased)
4
+
5
+ ## 1.2.1 (2020-07-09)
6
+
7
+ - Support for setting timezone in queries to configure influx time calculations, e.g., time epoch aggregation ([@jklimke][])
8
+
9
+ [PR](https://github.com/palkan/influxer/pull/46)
10
+
11
+ ## 1.2.0 (2019-05-20)
12
+
13
+ - **Require Ruby 2.4+**
14
+
15
+ ## 1.1.6
16
+
17
+ - [Fixes [#41](https://github.com/palkan/influxer/issues/41)] Fix query building with empty arrays in `where` clause ([@dimiii][])
18
+
19
+ [PR](https://github.com/palkan/influxer/pull/44)
20
+
21
+ **BREAKING:** `where.not` now returns non-empty result for an empty array.
22
+
23
+ - Support of year alias in queries ([@dimiii][])
24
+
25
+ - [Fixes [#40](https://github.com/palkan/influxer/issues/40)] Avoid adding precision suffix to write queries. ([@palkan][])
26
+
27
+ ## 1.1.5
28
+
29
+ - [Fixes [#37](https://github.com/palkan/influxer/issues/37)] Timestamp ranges are quoted again. ([@jklimke][])
30
+
31
+ ## 1.1.4
32
+
33
+ - [Fixes [#35](https://github.com/palkan/influxer/issues/35)] Support time duration suffix and handle `'s'` and `'ms'` precision. ([@palkan][])
34
+
35
+ [PR](https://github.com/palkan/influxer/pull/36)
36
+
37
+ **BREAKING:** `Time`-like value are only type-casted for `time` key.
38
+
39
+ ## 1.1.2
40
+
41
+ - Support exclusive ranges as `where` arguments. ([@MPursche][])
42
+
43
+ ```ruby
44
+ # range including the end
45
+ where(a: 1..4)
46
+ #=> ... WHERE a >= 1 AND a <= 4
47
+
48
+ #range excluding the end
49
+ where(a: 1...4)
50
+ #=> ... WHERE a >= 1 AND a < 4
51
+ ```
52
+
53
+ ## 1.1.1
54
+
55
+ - [Fixes [#31](https://github.com/palkan/influxer/issues/31)] Fix bug with empty arrays in `where` clause
56
+
57
+ - Introduce `Relation#none` method
58
+
59
+ ## 1.1.0
60
+
61
+ ### Features
62
+
63
+ - Add ability to specify per-metrics retention-policy, precision and database
64
+
65
+ Now you can override default configuration for a specific metrics class:
66
+
67
+ ```ruby
68
+ class CustomMetrics < Influxer::Metrics
69
+ set_database "custom_db"
70
+ set_retention_policy :yearly
71
+ set_precision "ms"
72
+ end
73
+ ```
74
+
75
+ ### Fixes
76
+
77
+ - [Fixes [#30](https://github.com/palkan/influxer/issues/30)] Fix writing points with custom retention policy
78
+
79
+ ### Misc
80
+
81
+ - Update Rubocop configuration and add Rubocop Rake task to defaults
82
+
83
+ ## 1.0.1
84
+
85
+ - Fix missing `#delegate` in ActiveRecord 3.2
86
+
87
+ See [changelog](https://github.com/palkan/influxer/blob/1.0.0/Changelog.md) for earlier versions.
88
+
89
+ [@palkan]: https://github.com/palkan
90
+ [@MPursche]: https://github.com/MPursche
91
+ [@jklimke]: https://github.com/jklimke
92
+ [@dimiii]: https://github.com/dimiii
@@ -1,4 +1,4 @@
1
- Copyright 2014 palkan
1
+ Copyright 2014-2020 Vladimir Dementyev
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,50 +1,109 @@
1
- [![Gem Version](https://badge.fury.io/rb/influxer.svg)](https://rubygems.org/gems/influxer) [![Build Status](https://travis-ci.org/palkan/influxer.svg?branch=master)](https://travis-ci.org/palkan/influxer) [![Dependency Status](https://dependencyci.com/github/palkan/influxer/badge)](https://dependencyci.com/github/palkan/influxer)
2
- ## Influxer
1
+ ![Build](https://github.com/palkan/influxer/workflows/Build/badge.svg)
3
2
 
4
- **NOTE**: Version 0.3.x supports InfluxDB >= 0.9.0. For InfluxDB 0.8.x use [version 0.2.5](https://github.com/palkan/influxer/tree/0.2.5).
5
-
6
- **NOTE**: Influxer is Rails 3, 4, 5 compatible!
3
+ # Influxer
7
4
 
8
5
  Influxer provides an ActiveRecord-style way to work with [InfluxDB](https://influxdb.com/) with many useful features, such as:
9
- - Familar query language (use `select`, `where`, `not`, `group` etc).
10
- - Support for Regex conditions: `where(page_id: /^home\/.*/) #=> select * ... where page_id=~/^home\/.*/`.
11
- - Special query methods for InfluxDB:
12
- - `time` - group by time (e.g. `Metrics.time(:hour) => # select * ... group by time(1h)`);
13
- - `past` - get only points for last hour/minute/whatever (e.g. `Metrics.past(:day) => # select * ... where time > now() - 1d`);
14
- - `since` - get only points since date (e.g. `Metrics.since(Time.utc(2014,12,31)) => # select * ... where time > 1419984000s`);
15
- - `merge` - merge series.
16
- - Scopes support
17
- ```ruby
18
- class Metrics < Influxer::Metrics
19
- default_scope -> { time(:hour).limit(1000) }
20
- tags :account_id
21
- attributes :value
22
- scope :unlimited, -> { limit(nil) }
23
- scope :by_account, -> (id) { where(account_id: id) if id.present? }
24
- end
25
-
26
- Metrics.by_account(1)
27
- # => select * from "metrics" group by time(1h) where account_id=1 limit 1000
28
-
29
- Metrics.unlimited.by_account(1).time(:week)
30
- # => select * from "metrics" group by time(1w) where account_id=1
31
-
32
- ```
33
- - Integrate with your model:
34
- ```ruby
35
- class UserVisits < Influxer::Metrics
36
- end
37
-
38
- class User < ActiveRecord::Base
39
- has_metrics :visits
40
- end
41
-
42
- user = User.find(1)
43
- user.visits.write(page_id: 'home')
44
- #=> < creates point {user_id: 1, page_id: 'home'} in 'user_visits' series >
45
-
46
- user.visits.where(page_id: 'home')
47
- #=> select * from user_visits where page_id='home'
48
- ```
49
-
50
- Find more on [Wiki](https://github.com/palkan/influxer/wiki).
6
+
7
+ ## Installation
8
+
9
+ Adding to a gem:
10
+
11
+ ```ruby
12
+ # my-cool-gem.gemspec
13
+ Gem::Specification.new do |spec|
14
+ # ...
15
+ spec.add_dependency "influxer", ">= 1.2.0"
16
+ # ...
17
+ end
18
+ ```
19
+
20
+ Or adding to your project:
21
+
22
+ ```ruby
23
+ # Gemfile
24
+ gem "influxer", "~> 1.2"
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Metrics classes
30
+
31
+ To query InfluxDB or write to it, you should define a metrics class first. Each metrics class represents a measurement/series (or multiple related measurements):
32
+
33
+ ```ruby
34
+ class VisitsMetrics < Influxer::Metrics
35
+ # Define tags...
36
+ tags :account_id, :page_id
37
+ # ...and attributes
38
+ attributes :user_id, :browser
39
+ end
40
+ ```
41
+
42
+ ### Querying
43
+
44
+ Now you can use your metrics classes in a similar way to Active Record models to build queries. For example:
45
+
46
+ ```ruby
47
+ VisitsMetrics.select(:account_id, :user_id).where(page_id: /^home\/.*/)
48
+ ```
49
+
50
+ Influxer provides special query methods for dealing with time series:
51
+
52
+ - Group by time: `Metrics.time(:hour) => # select * ... group by time(1h)`.
53
+ - Select only points for the last hour/minute/whatever: `Metrics.past(:day) => # select * ... where time > now() - 1d`.
54
+ - Select only points since the specified time: `Metrics.since(Time.utc(2014,12,31)) => # select * ... where time > 1419984000s`.
55
+ - and more.
56
+
57
+ See [our Wiki](https://github.com/palkan/influxer/wiki/Query-methods) for more.
58
+
59
+ ### Scopes support
60
+
61
+ You can define scopes to re-use query conditions:
62
+
63
+ ```ruby
64
+ class Metrics < Influxer::Metrics
65
+ tags :account_id
66
+ attributes :value
67
+
68
+ default_scope -> { time(:hour).limit(1000) }
69
+
70
+ scope :unlimited, -> { limit(nil) }
71
+ scope :by_account, ->(id) { where(account_id: id) if id.present? }
72
+ end
73
+
74
+ Metrics.by_account(1)
75
+ # => select * from "metrics" group by time(1h) where account_id=1 limit 1000
76
+
77
+ Metrics.unlimited.by_account(1).time(:week)
78
+ # => select * from "metrics" group by time(1w) where account_id=1
79
+ ```
80
+
81
+ ### Active Record integration
82
+
83
+ You can association metrics with Active Record models:
84
+
85
+ ```ruby
86
+ class UserVisits < Influxer::Metrics
87
+ end
88
+
89
+ class User < ActiveRecord::Base
90
+ has_metrics :visits
91
+ end
92
+
93
+ user = User.find(1)
94
+ user.visits.write(page_id: "home")
95
+ #=> < creates point {user_id: 1, page_id: 'home'} in 'user_visits' series >
96
+
97
+ user.visits.where(page_id: "home")
98
+ #=> select * from user_visits where page_id='home'
99
+ ```
100
+
101
+ Find more on [Wiki](https://github.com/palkan/influxer/wiki/ActiveRecord-integration).
102
+
103
+ ## Contributing
104
+
105
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/palkan/influxer](https://github.com/palkan/influxer).
106
+
107
+ ## License
108
+
109
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'influxer/version'
4
- require 'active_model'
3
+ require "influxer/version"
4
+ require "active_model"
5
+ require "active_support/core_ext"
5
6
 
6
7
  # Rails client for InfluxDB
7
8
  module Influxer
@@ -9,16 +10,16 @@ module Influxer
9
10
  ActiveModel::VERSION::MAJOR == 3
10
11
  end
11
12
 
12
- require 'influxer/config'
13
- require 'influxer/client'
14
- require 'influxer/metrics/metrics'
13
+ require "influxer/config"
14
+ require "influxer/client"
15
+ require "influxer/metrics/metrics"
15
16
 
16
17
  module Model # :nodoc:
17
- require 'influxer/model'
18
+ require "influxer/model"
18
19
  end
19
20
 
20
- require 'influxer/rails/client' if defined?(Rails)
21
- require 'influxer/engine' if defined?(Rails)
21
+ require "influxer/rails/client" if defined?(Rails)
22
+ require "influxer/engine" if defined?(Rails)
22
23
 
23
24
  def self.config
24
25
  @config ||= Config.new
@@ -33,7 +34,7 @@ module Influxer
33
34
  end
34
35
 
35
36
  def self.reset!
36
- @client.stop! unless @client.nil?
37
+ @client&.stop!
37
38
  @config = nil
38
39
  @client = nil
39
40
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'influxdb'
3
+ require "influxdb"
4
4
 
5
5
  module Influxer
6
6
  # InfluxDB API client
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'anyway_config'
3
+ require "anyway_config"
4
4
 
5
5
  module Influxer
6
6
  # Influxer configuration
@@ -28,9 +28,10 @@ module Influxer
28
28
  :denormalize,
29
29
  :udp,
30
30
  :async,
31
- database: 'db',
32
- time_precision: 'ns',
33
- cache: false
31
+ database: "db",
32
+ time_precision: "ns",
33
+ cache: false,
34
+ time_duration_suffix_enabled: false
34
35
 
35
36
  def load(*)
36
37
  super
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'influxer'
3
+ require "influxer"
4
4
 
5
5
  module Influxer
6
6
  class Engine < Rails::Engine # :nodoc:
@@ -5,10 +5,8 @@ module Influxer
5
5
  # Replacement of ActiveModel::Model for ActiveModel 3
6
6
  module Model
7
7
  def initialize(attributes = {})
8
- if attributes
9
- attributes.each do |attr, value|
10
- public_send("#{attr}=", value)
11
- end
8
+ attributes&.each do |attr, value|
9
+ public_send("#{attr}=", value)
12
10
  end
13
11
 
14
12
  super()
@@ -1,16 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'influxer/metrics/relation'
4
- require 'influxer/metrics/scoping'
5
- require 'influxer/metrics/quoting/timestamp'
6
- require 'influxer/metrics/active_model3/model'
3
+ require "influxer/metrics/relation"
4
+ require "influxer/metrics/scoping"
5
+ require "influxer/metrics/quoting/timestamp"
6
+ require "influxer/metrics/active_model3/model"
7
7
 
8
8
  module Influxer
9
9
  class MetricsError < StandardError; end
10
10
  class MetricsInvalid < MetricsError; end
11
11
 
12
12
  # Base class for InfluxDB querying and writing
13
- # rubocop:disable Metrics/ClassLength
14
13
  class Metrics
15
14
  TIME_FACTOR = 1_000_000_000
16
15
  if Influxer.active_model3?
@@ -18,7 +17,6 @@ module Influxer
18
17
  else
19
18
  include ActiveModel::Model
20
19
  end
21
- # rubocop:enable Style/MixinUsage
22
20
 
23
21
  include ActiveModel::Validations
24
22
  extend ActiveModel::Callbacks
@@ -35,7 +33,7 @@ module Influxer
35
33
  [
36
34
  :write, :write!, :select, :where,
37
35
  :group, :time, :past, :since,
38
- :limit, :offset, :fill, :delete_all, :epoch
36
+ :limit, :offset, :fill, :delete_all, :epoch, :timezone
39
37
  ] + Influxer::Calculations::CALCULATION_METHODS
40
38
  ),
41
39
  to: :all
@@ -125,7 +123,7 @@ module Influxer
125
123
  quoted_series(val.call(instance), write: write)
126
124
  when Array
127
125
  if val.length > 1
128
- "merge(#{val.map { |s| quoted_series(s, write: write) }.join(',')})"
126
+ "merge(#{val.map { |s| quoted_series(s, write: write) }.join(",")})"
129
127
  else
130
128
  quoted_series(val.first, write: write)
131
129
  end
@@ -133,7 +131,7 @@ module Influxer
133
131
  # Only add retention policy when selecting points
134
132
  # and not writing
135
133
  if !write && retention_policy.present?
136
- [quote(@retention_policy), quote(val)].join('.')
134
+ [quote(@retention_policy), quote(val)].join(".")
137
135
  else
138
136
  quote(val)
139
137
  end
@@ -169,6 +167,7 @@ module Influxer
169
167
 
170
168
  def write!
171
169
  raise MetricsInvalid if invalid?
170
+
172
171
  write
173
172
  end
174
173
 
@@ -225,13 +224,11 @@ module Influxer
225
224
  end
226
225
 
227
226
  def parsed_timestamp
228
- quote_timestamp @timestamp, client
227
+ quote_timestamp_for_write @timestamp, client
229
228
  end
230
- # rubocop:enable Metrics/MethodLength
231
- # rubocop:enable Metrics/AbcSize
232
229
 
233
230
  def unquote(name)
234
- name.gsub(/(\A['"]|['"]\z)/, '')
231
+ name.gsub(/(\A['"]|['"]\z)/, "")
235
232
  end
236
233
  end
237
234
  end
@@ -2,24 +2,67 @@
2
2
 
3
3
  module Influxer
4
4
  module TimestampQuoting #:nodoc:
5
- TIME_FACTOR = 1_000_000_000
5
+ TIME_FACTORS = {
6
+ "ns" => 1_000_000_000,
7
+ "ms" => 1_000,
8
+ "s" => 1
9
+ }.freeze
6
10
 
7
- # Quote timestamp as ns
8
- # rubocop: disable Metrics/AbcSize, Metrics/MethodLength
11
+ DEFAULT_PRECISION = "ns"
12
+
13
+ # Quote timestamp
14
+ # rubocop: disable Metrics/MethodLength, Metrics/AbcSize
9
15
  def quote_timestamp(val, client)
10
- return val unless client.time_precision == 'ns'
16
+ if Influxer.config.time_duration_suffix_enabled
17
+ precision = if TIME_FACTORS.key?(client.time_precision)
18
+ client.time_precision
19
+ else
20
+ DEFAULT_PRECISION
21
+ end
22
+ return quote_timestamp_with_suffix(val, precision)
23
+ end
24
+
25
+ if !TIME_FACTORS.key?(client.time_precision) &&
26
+ !val.is_a?(Numeric)
27
+ warn(
28
+ "Influxer doesn't automatically cast Time and String values " \
29
+ "to '#{client.time_precision}' precision. " \
30
+ "Please, convert to numeric value yourself"
31
+ )
32
+ return val
33
+ end
34
+
35
+ factorize_timestamp(val, TIME_FACTORS.fetch(client.time_precision))
36
+ end
37
+ # rubocop: enable Metrics/MethodLength, Metrics/AbcSize
38
+
39
+ def quote_timestamp_with_suffix(val, precision)
40
+ "#{factorize_timestamp(val, TIME_FACTORS.fetch(precision))}#{precision}"
41
+ end
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
11
54
 
55
+ def factorize_timestamp(val, factor)
12
56
  case val
13
57
  when Numeric
14
- val.to_i.to_s.ljust(19, '0').to_i
58
+ (val.to_f * factor).to_i
15
59
  when String
16
- (Time.parse(val).to_r * TIME_FACTOR).to_i
60
+ (Time.parse(val).to_r * factor).to_i
17
61
  when Date, DateTime
18
- (val.to_time.to_r * TIME_FACTOR).to_i
62
+ (val.to_time.to_r * factor).to_i
19
63
  when Time
20
- (val.to_r * TIME_FACTOR).to_i
64
+ (val.to_r * factor).to_i
21
65
  end
22
66
  end
23
- # rubocop: enable Metrics/AbcSize, Metrics/MethodLength
24
67
  end
25
68
  end