statsd-instrument 3.0.0.pre1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +22 -0
  3. data/.github/workflows/tests.yml +31 -0
  4. data/.rubocop.yml +3 -13
  5. data/CHANGELOG.md +50 -0
  6. data/Gemfile +8 -2
  7. data/README.md +6 -3
  8. data/Rakefile +7 -7
  9. data/benchmark/send-metrics-to-dev-null-log +12 -11
  10. data/benchmark/send-metrics-to-local-udp-receiver +16 -15
  11. data/bin/rake +29 -0
  12. data/bin/rubocop +29 -0
  13. data/lib/statsd-instrument.rb +1 -1
  14. data/lib/statsd/instrument.rb +112 -145
  15. data/lib/statsd/instrument/assertions.rb +200 -208
  16. data/lib/statsd/instrument/batched_udp_sink.rb +154 -0
  17. data/lib/statsd/instrument/capture_sink.rb +23 -19
  18. data/lib/statsd/instrument/client.rb +410 -306
  19. data/lib/statsd/instrument/datagram.rb +69 -65
  20. data/lib/statsd/instrument/datagram_builder.rb +81 -77
  21. data/lib/statsd/instrument/dogstatsd_datagram.rb +76 -72
  22. data/lib/statsd/instrument/dogstatsd_datagram_builder.rb +68 -64
  23. data/lib/statsd/instrument/environment.rb +88 -77
  24. data/lib/statsd/instrument/expectation.rb +96 -96
  25. data/lib/statsd/instrument/helpers.rb +11 -7
  26. data/lib/statsd/instrument/log_sink.rb +20 -16
  27. data/lib/statsd/instrument/matchers.rb +93 -74
  28. data/lib/statsd/instrument/null_sink.rb +12 -8
  29. data/lib/statsd/instrument/railtie.rb +11 -7
  30. data/lib/statsd/instrument/rubocop.rb +8 -8
  31. data/lib/statsd/instrument/rubocop/measure_as_dist_argument.rb +1 -1
  32. data/lib/statsd/instrument/rubocop/metaprogramming_positional_arguments.rb +2 -2
  33. data/lib/statsd/instrument/rubocop/metric_prefix_argument.rb +1 -1
  34. data/lib/statsd/instrument/rubocop/metric_return_value.rb +2 -2
  35. data/lib/statsd/instrument/rubocop/metric_value_keyword_argument.rb +1 -1
  36. data/lib/statsd/instrument/rubocop/positional_arguments.rb +4 -4
  37. data/lib/statsd/instrument/rubocop/singleton_configuration.rb +1 -1
  38. data/lib/statsd/instrument/rubocop/splat_arguments.rb +2 -2
  39. data/lib/statsd/instrument/statsd_datagram_builder.rb +12 -8
  40. data/lib/statsd/instrument/strict.rb +1 -6
  41. data/lib/statsd/instrument/udp_sink.rb +49 -47
  42. data/lib/statsd/instrument/version.rb +1 -1
  43. data/statsd-instrument.gemspec +4 -8
  44. data/test/assertions_test.rb +205 -161
  45. data/test/benchmark/clock_gettime.rb +1 -1
  46. data/test/benchmark/default_tags.rb +9 -9
  47. data/test/benchmark/metrics.rb +8 -8
  48. data/test/benchmark/tags.rb +4 -4
  49. data/test/capture_sink_test.rb +14 -14
  50. data/test/client_test.rb +96 -96
  51. data/test/datagram_builder_test.rb +55 -55
  52. data/test/datagram_test.rb +5 -5
  53. data/test/dogstatsd_datagram_builder_test.rb +37 -37
  54. data/test/environment_test.rb +30 -21
  55. data/test/helpers/rubocop_helper.rb +12 -9
  56. data/test/helpers_test.rb +15 -15
  57. data/test/integration_test.rb +7 -7
  58. data/test/log_sink_test.rb +4 -4
  59. data/test/matchers_test.rb +54 -54
  60. data/test/null_sink_test.rb +4 -4
  61. data/test/rubocop/measure_as_dist_argument_test.rb +2 -2
  62. data/test/rubocop/metaprogramming_positional_arguments_test.rb +2 -2
  63. data/test/rubocop/metric_prefix_argument_test.rb +2 -2
  64. data/test/rubocop/metric_return_value_test.rb +6 -6
  65. data/test/rubocop/metric_value_keyword_argument_test.rb +3 -3
  66. data/test/rubocop/positional_arguments_test.rb +12 -12
  67. data/test/rubocop/singleton_configuration_test.rb +8 -8
  68. data/test/rubocop/splat_arguments_test.rb +2 -2
  69. data/test/statsd_datagram_builder_test.rb +6 -6
  70. data/test/statsd_instrumentation_test.rb +122 -122
  71. data/test/statsd_test.rb +69 -67
  72. data/test/test_helper.rb +19 -10
  73. data/test/udp_sink_test.rb +122 -50
  74. metadata +12 -92
  75. data/.github/workflows/ci.yml +0 -56
  76. data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +0 -1027
@@ -1,75 +1,79 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # @note This class is part of the new Client implementation that is intended
4
- # to become the new default in the next major release of this library.
5
- class StatsD::Instrument::DogStatsDDatagramBuilder < StatsD::Instrument::DatagramBuilder
6
- unsupported_datagram_types :kv
3
+ module StatsD
4
+ module Instrument
5
+ # @note This class is part of the new Client implementation that is intended
6
+ # to become the new default in the next major release of this library.
7
+ class DogStatsDDatagramBuilder < StatsD::Instrument::DatagramBuilder
8
+ unsupported_datagram_types :kv
7
9
 
8
- def self.datagram_class
9
- StatsD::Instrument::DogStatsDDatagram
10
- end
10
+ def self.datagram_class
11
+ StatsD::Instrument::DogStatsDDatagram
12
+ end
11
13
 
12
- def latency_metric_type
13
- :d
14
- end
14
+ def latency_metric_type
15
+ :d
16
+ end
15
17
 
16
- # Constricts an event datagram.
17
- #
18
- # @param [String] title Event title.
19
- # @param [String] text Event description. Newlines are allowed.
20
- # @param [Time] timestamp The of the event. If not provided,
21
- # Datadog will interpret it as the current timestamp.
22
- # @param [String] hostname A hostname to associate with the event.
23
- # @param [String] aggregation_key An aggregation key to group events with the same key.
24
- # @param [String] priority Priority of the event. Either "normal" (default) or "low".
25
- # @param [String] source_type_name The source type of the event.
26
- # @param [String] alert_type Either "error", "warning", "info" (default) or "success".
27
- # @param [Array, Hash] tags Tags to associate with the event.
28
- # @return [String] The correctly formatted service check datagram
29
- #
30
- # @see https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/#events
31
- def _e(title, text, timestamp: nil, hostname: nil, aggregation_key: nil, priority: nil,
32
- source_type_name: nil, alert_type: nil, tags: nil)
18
+ # Constricts an event datagram.
19
+ #
20
+ # @param [String] title Event title.
21
+ # @param [String] text Event description. Newlines are allowed.
22
+ # @param [Time] timestamp The of the event. If not provided,
23
+ # Datadog will interpret it as the current timestamp.
24
+ # @param [String] hostname A hostname to associate with the event.
25
+ # @param [String] aggregation_key An aggregation key to group events with the same key.
26
+ # @param [String] priority Priority of the event. Either "normal" (default) or "low".
27
+ # @param [String] source_type_name The source type of the event.
28
+ # @param [String] alert_type Either "error", "warning", "info" (default) or "success".
29
+ # @param [Array, Hash] tags Tags to associate with the event.
30
+ # @return [String] The correctly formatted service check datagram
31
+ #
32
+ # @see https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/#events
33
+ def _e(title, text, timestamp: nil, hostname: nil, aggregation_key: nil, priority: nil,
34
+ source_type_name: nil, alert_type: nil, tags: nil)
33
35
 
34
- escaped_title = "#{@prefix}#{title}".gsub("\n", '\n')
35
- escaped_text = text.gsub("\n", '\n')
36
- tags = normalize_tags(tags) + default_tags
36
+ escaped_title = "#{@prefix}#{title}".gsub("\n", '\n')
37
+ escaped_text = text.gsub("\n", '\n')
38
+ tags = normalize_tags(tags) + default_tags
37
39
 
38
- datagram = +"_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}"
39
- datagram << "|h:#{hostname}" if hostname
40
- datagram << "|d:#{timestamp.to_i}" if timestamp
41
- datagram << "|k:#{aggregation_key}" if aggregation_key
42
- datagram << "|p:#{priority}" if priority
43
- datagram << "|s:#{source_type_name}" if source_type_name
44
- datagram << "|t:#{alert_type}" if alert_type
45
- datagram << "|##{tags.join(',')}" unless tags.empty?
46
- datagram
47
- end
40
+ datagram = +"_e{#{escaped_title.length},#{escaped_text.length}}:#{escaped_title}|#{escaped_text}"
41
+ datagram << "|h:#{hostname}" if hostname
42
+ datagram << "|d:#{timestamp.to_i}" if timestamp
43
+ datagram << "|k:#{aggregation_key}" if aggregation_key
44
+ datagram << "|p:#{priority}" if priority
45
+ datagram << "|s:#{source_type_name}" if source_type_name
46
+ datagram << "|t:#{alert_type}" if alert_type
47
+ datagram << "|##{tags.join(",")}" unless tags.empty?
48
+ datagram
49
+ end
48
50
 
49
- # Constricts a service check datagram.
50
- #
51
- # @param [String] name Name of the service
52
- # @param [Symbol] status Either `:ok`, `:warning`, `:critical` or `:unknown`
53
- # @param [Time] timestamp The moment when the service was checked. If not provided,
54
- # Datadog will interpret it as the current timestamp.
55
- # @param [String] hostname A hostname to associate with the check.
56
- # @param [Array, Hash] tags Tags to associate with the check.
57
- # @param [String] message A message describing the current state of the service check.
58
- # @return [String] The correctly formatted service check datagram
59
- #
60
- # @see https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/#service-checks
61
- def _sc(name, status, timestamp: nil, hostname: nil, tags: nil, message: nil)
62
- status_number = status.is_a?(Integer) ? status : SERVICE_CHECK_STATUS_VALUES.fetch(status.to_sym)
63
- tags = normalize_tags(tags) + default_tags
51
+ # Constricts a service check datagram.
52
+ #
53
+ # @param [String] name Name of the service
54
+ # @param [Symbol] status Either `:ok`, `:warning`, `:critical` or `:unknown`
55
+ # @param [Time] timestamp The moment when the service was checked. If not provided,
56
+ # Datadog will interpret it as the current timestamp.
57
+ # @param [String] hostname A hostname to associate with the check.
58
+ # @param [Array, Hash] tags Tags to associate with the check.
59
+ # @param [String] message A message describing the current state of the service check.
60
+ # @return [String] The correctly formatted service check datagram
61
+ #
62
+ # @see https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/#service-checks
63
+ def _sc(name, status, timestamp: nil, hostname: nil, tags: nil, message: nil)
64
+ status_number = status.is_a?(Integer) ? status : SERVICE_CHECK_STATUS_VALUES.fetch(status.to_sym)
65
+ tags = normalize_tags(tags) + default_tags
64
66
 
65
- datagram = +"_sc|#{@prefix}#{normalize_name(name)}|#{status_number}"
66
- datagram << "|h:#{hostname}" if hostname
67
- datagram << "|d:#{timestamp.to_i}" if timestamp
68
- datagram << "|##{tags.join(',')}" unless tags.empty?
69
- datagram << "|m:#{normalize_name(message)}" if message
70
- datagram
71
- end
67
+ datagram = +"_sc|#{@prefix}#{normalize_name(name)}|#{status_number}"
68
+ datagram << "|h:#{hostname}" if hostname
69
+ datagram << "|d:#{timestamp.to_i}" if timestamp
70
+ datagram << "|##{tags.join(",")}" unless tags.empty?
71
+ datagram << "|m:#{normalize_name(message)}" if message
72
+ datagram
73
+ end
72
74
 
73
- SERVICE_CHECK_STATUS_VALUES = { ok: 0, warning: 1, critical: 2, unknown: 3 }.freeze
74
- private_constant :SERVICE_CHECK_STATUS_VALUES
75
+ SERVICE_CHECK_STATUS_VALUES = { ok: 0, warning: 1, critical: 2, unknown: 3 }.freeze
76
+ private_constant :SERVICE_CHECK_STATUS_VALUES
77
+ end
78
+ end
75
79
  end
@@ -1,94 +1,105 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The environment module is used to detect, and initialize the environment in
4
- # which this library is active. It will use different default values based on the environment.
5
- class StatsD::Instrument::Environment
6
- class << self
7
- def current
8
- @current ||= StatsD::Instrument::Environment.new(ENV)
9
- end
3
+ module StatsD
4
+ module Instrument
5
+ # The environment module is used to detect, and initialize the environment in
6
+ # which this library is active. It will use different default values based on the environment.
7
+ class Environment
8
+ class << self
9
+ def current
10
+ @current ||= StatsD::Instrument::Environment.new(ENV)
11
+ end
10
12
 
11
- # Detects the current environment, either by asking Rails, or by inspecting environment variables.
12
- #
13
- # - Within a Rails application, <tt>Rails.env</tt> is used.
14
- # - It will check the following environment variables in order: <tt>RAILS_ENV</tt>, <tt>RACK_ENV</tt>, <tt>ENV</tt>.
15
- # - If none of these are set, it will return <tt>development</tt>
16
- #
17
- # @return [String] The detected environment.
18
- def environment
19
- current.environment
20
- end
13
+ # @deprecated For backwards compatibility only. Use {StatsD::Instrument::Environment#environment}
14
+ # through {StatsD::Instrument::Environment.current} instead.
15
+ def environment
16
+ current.environment
17
+ end
21
18
 
22
- # Sets default values for sample rate and logger.
23
- #
24
- # - Default sample rate is set to the value in the STATSD_SAMPLE_RATE environment variable,
25
- # or 1.0 otherwise. See {StatsD#default_sample_rate}
26
- # - {StatsD#logger} is set to a logger that send output to stderr.
27
- #
28
- # If you are including this library inside a Rails environment, additional initialization will
29
- # be done as part of the {StatsD::Instrument::Railtie}.
30
- #
31
- # @return [void]
32
- def setup
33
- StatsD.logger = Logger.new($stderr)
34
- end
35
- end
19
+ # Sets default values for sample rate and logger.
20
+ #
21
+ # - Default sample rate is set to the value in the STATSD_SAMPLE_RATE environment variable,
22
+ # or 1.0 otherwise. See {StatsD#default_sample_rate}
23
+ # - {StatsD#logger} is set to a logger that send output to stderr.
24
+ #
25
+ # If you are including this library inside a Rails environment, additional initialization will
26
+ # be done as part of the {StatsD::Instrument::Railtie}.
27
+ #
28
+ # @return [void]
29
+ def setup
30
+ StatsD.logger = Logger.new($stderr)
31
+ end
32
+ end
36
33
 
37
- attr_reader :env
34
+ attr_reader :env
38
35
 
39
- def initialize(env)
40
- @env = env
41
- end
36
+ def initialize(env)
37
+ @env = env
38
+ end
42
39
 
43
- # Detects the current environment, either by asking Rails, or by inspecting environment variables.
44
- #
45
- # - Within a Rails application, <tt>Rails.env</tt> is used.
46
- # - It will check the following environment variables in order: <tt>RAILS_ENV</tt>, <tt>RACK_ENV</tt>, <tt>ENV</tt>.
47
- # - If none of these are set, it will return <tt>development</tt>
48
- #
49
- # @return [String] The detected environment.
50
- def environment
51
- if env['STATSD_ENV']
52
- env['STATSD_ENV']
53
- elsif defined?(Rails) && Rails.respond_to?(:env)
54
- Rails.env.to_s
55
- else
56
- env['RAILS_ENV'] || env['RACK_ENV'] || env['ENV'] || 'development'
57
- end
58
- end
40
+ # Detects the current environment, either by asking Rails, or by inspecting environment variables.
41
+ #
42
+ # - It will prefer the value set in <tt>ENV['STATSD_ENV']</tt>
43
+ # - Within a Rails application, <tt>Rails.env</tt> is used.
44
+ # - It will check the following environment variables in order:
45
+ # - <tt>RAILS_ENV</tt>,
46
+ # - <tt>RACK_ENV</tt>
47
+ # - <tt>ENV</tt>.
48
+ # - If none of these are set, it will return <tt>development</tt>
49
+ #
50
+ # @return [String] The detected environment.
51
+ def environment
52
+ if env["STATSD_ENV"]
53
+ env["STATSD_ENV"]
54
+ elsif defined?(Rails) && Rails.respond_to?(:env)
55
+ Rails.env.to_s
56
+ else
57
+ env["RAILS_ENV"] || env["RACK_ENV"] || env["ENV"] || "development"
58
+ end
59
+ end
59
60
 
60
- def statsd_implementation
61
- env.fetch('STATSD_IMPLEMENTATION', 'datadog')
62
- end
61
+ def statsd_implementation
62
+ env.fetch("STATSD_IMPLEMENTATION", "datadog")
63
+ end
63
64
 
64
- def statsd_sample_rate
65
- env.fetch('STATSD_SAMPLE_RATE', 1.0).to_f
66
- end
65
+ def statsd_sample_rate
66
+ env.fetch("STATSD_SAMPLE_RATE", 1.0).to_f
67
+ end
67
68
 
68
- def statsd_prefix
69
- env.fetch('STATSD_PREFIX', nil)
70
- end
69
+ def statsd_prefix
70
+ env.fetch("STATSD_PREFIX", nil)
71
+ end
71
72
 
72
- def statsd_addr
73
- env.fetch('STATSD_ADDR', 'localhost:8125')
74
- end
73
+ def statsd_addr
74
+ env.fetch("STATSD_ADDR", "localhost:8125")
75
+ end
75
76
 
76
- def statsd_default_tags
77
- env.key?('STATSD_DEFAULT_TAGS') ? env.fetch('STATSD_DEFAULT_TAGS').split(',') : nil
78
- end
77
+ def statsd_default_tags
78
+ env.key?("STATSD_DEFAULT_TAGS") ? env.fetch("STATSD_DEFAULT_TAGS").split(",") : nil
79
+ end
79
80
 
80
- def client
81
- StatsD::Instrument::Client.from_env(self)
82
- end
81
+ def statsd_flush_interval
82
+ Float(env.fetch("STATSD_FLUSH_INTERVAL", 1.0))
83
+ end
84
+
85
+ def client
86
+ StatsD::Instrument::Client.from_env(self)
87
+ end
83
88
 
84
- def default_sink_for_environment
85
- case environment
86
- when 'production', 'staging'
87
- StatsD::Instrument::UDPSink.for_addr(statsd_addr)
88
- when 'test'
89
- StatsD::Instrument::NullSink.new
90
- else
91
- StatsD::Instrument::LogSink.new(StatsD.logger)
89
+ def default_sink_for_environment
90
+ case environment
91
+ when "production", "staging"
92
+ if statsd_flush_interval > 0.0
93
+ StatsD::Instrument::BatchedUDPSink.for_addr(statsd_addr, flush_interval: statsd_flush_interval)
94
+ else
95
+ StatsD::Instrument::UDPSink.for_addr(statsd_addr)
96
+ end
97
+ when "test"
98
+ StatsD::Instrument::NullSink.new
99
+ else
100
+ StatsD::Instrument::LogSink.new(StatsD.logger)
101
+ end
102
+ end
92
103
  end
93
104
  end
94
105
  end
@@ -1,119 +1,119 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # @private
4
- class StatsD::Instrument::Expectation
5
- class << self
6
- def increment(name, **options)
7
- new(type: :c, name: name, **options)
8
- end
9
-
10
- def measure(name, **options)
11
- new(type: :ms, name: name, **options)
12
- end
3
+ module StatsD
4
+ module Instrument
5
+ # @private
6
+ class Expectation
7
+ class << self
8
+ def increment(name, value = nil, **options)
9
+ new(type: :c, name: name, value: value, **options)
10
+ end
13
11
 
14
- def gauge(name, **options)
15
- new(type: :g, name: name, **options)
16
- end
12
+ def measure(name, value = nil, **options)
13
+ new(type: :ms, name: name, value: value, **options)
14
+ end
17
15
 
18
- def set(name, **options)
19
- new(type: :s, name: name, **options)
20
- end
16
+ def gauge(name, value = nil, **options)
17
+ new(type: :g, name: name, value: value, **options)
18
+ end
21
19
 
22
- def key_value(name, **options)
23
- new(type: :kv, name: name, **options)
24
- end
20
+ def set(name, value = nil, **options)
21
+ new(type: :s, name: name, value: value, **options)
22
+ end
25
23
 
26
- def distribution(name, **options)
27
- new(type: :d, name: name, **options)
28
- end
24
+ def distribution(name, value = nil, **options)
25
+ new(type: :d, name: name, value: value, **options)
26
+ end
29
27
 
30
- def histogram(name, **options)
31
- new(type: :h, name: name, **options)
32
- end
33
- end
28
+ def histogram(name, value = nil, **options)
29
+ new(type: :h, name: name, value: value, **options)
30
+ end
31
+ end
34
32
 
35
- attr_accessor :times, :type, :name, :value, :sample_rate, :tags
33
+ attr_accessor :times, :type, :name, :value, :sample_rate, :tags
36
34
 
37
- def initialize(client: StatsD.singleton_client, type:, name:, value: nil,
38
- sample_rate: nil, tags: nil, no_prefix: false, times: 1)
35
+ def initialize(client: StatsD.singleton_client, type:, name:, value: nil,
36
+ sample_rate: nil, tags: nil, no_prefix: false, times: 1)
39
37
 
40
- @type = type
41
- @name = client.prefix ? "#{client.prefix}.#{name}" : name unless no_prefix
42
- @value = normalized_value_for_type(type, value) if value
43
- @sample_rate = sample_rate
44
- @tags = normalize_tags(tags)
45
- @times = times
46
- end
38
+ @type = type
39
+ @name = no_prefix || !client.prefix ? name : "#{client.prefix}.#{name}"
40
+ @value = normalized_value_for_type(type, value) if value
41
+ @sample_rate = sample_rate
42
+ @tags = normalize_tags(tags)
43
+ @times = times
44
+ end
47
45
 
48
- def normalized_value_for_type(type, value)
49
- case type
50
- when :c then Integer(value)
51
- when :g, :h, :d, :kv, :ms then Float(value)
52
- when :s then String(value)
53
- else value
54
- end
55
- end
46
+ def normalized_value_for_type(type, value)
47
+ case type
48
+ when :c then Integer(value)
49
+ when :g, :h, :d, :kv, :ms then Float(value)
50
+ when :s then String(value)
51
+ else value
52
+ end
53
+ end
56
54
 
57
- def matches(actual_metric)
58
- return false if sample_rate && sample_rate != actual_metric.sample_rate
59
- return false if value && value != normalized_value_for_type(actual_metric.type, actual_metric.value)
55
+ def matches(actual_metric)
56
+ return false if sample_rate && sample_rate != actual_metric.sample_rate
57
+ return false if value && value != normalized_value_for_type(actual_metric.type, actual_metric.value)
60
58
 
61
- if tags
62
- expected_tags = Set.new(tags)
63
- actual_tags = Set.new(actual_metric.tags)
64
- return expected_tags.subset?(actual_tags)
65
- end
66
- true
67
- end
59
+ if tags
60
+ expected_tags = Set.new(tags)
61
+ actual_tags = Set.new(actual_metric.tags)
62
+ return expected_tags.subset?(actual_tags)
63
+ end
64
+ true
65
+ end
68
66
 
69
- def to_s
70
- str = +"#{name}:#{value || '<anything>'}|#{type}"
71
- str << "|@#{sample_rate}" if sample_rate
72
- str << "|#" << tags.join(',') if tags
73
- str << " (expected #{times} times)" if times > 1
74
- str
75
- end
67
+ def to_s
68
+ str = +"#{name}:#{value || "<anything>"}|#{type}"
69
+ str << "|@#{sample_rate}" if sample_rate
70
+ str << "|#" << tags.join(",") if tags
71
+ str << " (expected #{times} times)" if times > 1
72
+ str
73
+ end
76
74
 
77
- def inspect
78
- "#<StatsD::Instrument::Expectation:\"#{self}\">"
79
- end
75
+ def inspect
76
+ "#<StatsD::Instrument::Expectation:\"#{self}\">"
77
+ end
80
78
 
81
- private
79
+ private
82
80
 
83
- # Needed for normalize_tags
84
- unless Regexp.method_defined?(:match?) # for ruby 2.3
85
- module RubyBackports
86
- refine Regexp do
87
- def match?(str)
88
- (self =~ str) != nil
81
+ # Needed for normalize_tags
82
+ unless Regexp.method_defined?(:match?) # for ruby 2.3
83
+ module RubyBackports
84
+ refine Regexp do
85
+ def match?(str)
86
+ (self =~ str) != nil
87
+ end
88
+ end
89
89
  end
90
+
91
+ using(RubyBackports)
90
92
  end
91
- end
92
93
 
93
- using RubyBackports
94
- end
94
+ # @private
95
+ #
96
+ # Utility function to convert tags to the canonical form.
97
+ #
98
+ # - Tags specified as key value pairs will be converted into an array
99
+ # - Tags are normalized to remove unsupported characters
100
+ #
101
+ # @param tags [Array<String>, Hash<String, String>, nil] Tags specified in any form.
102
+ # @return [Array<String>, nil] the list of tags in canonical form.
103
+ #
104
+ # @todo We should delegate this to thje datagram builder of the current client,
105
+ # to ensure that this logic matches the logic of the active datagram builder.
106
+ def normalize_tags(tags)
107
+ return [] unless tags
108
+ tags = tags.map { |k, v| "#{k}:#{v}" } if tags.is_a?(Hash)
109
+
110
+ # Fast path when no string replacement is needed
111
+ return tags unless tags.any? { |tag| /[|,]/.match?(tag) }
112
+ tags.map { |tag| tag.tr("|,", "") }
113
+ end
114
+ end
95
115
 
96
- # @private
97
- #
98
- # Utility function to convert tags to the canonical form.
99
- #
100
- # - Tags specified as key value pairs will be converted into an array
101
- # - Tags are normalized to remove unsupported characters
102
- #
103
- # @param tags [Array<String>, Hash<String, String>, nil] Tags specified in any form.
104
- # @return [Array<String>, nil] the list of tags in canonical form.
105
- #
106
- # @todo We should delegate this to thje datagram builder of the current client,
107
- # to ensure that this logic matches the logic of the active datagram builder.
108
- def normalize_tags(tags)
109
- return [] unless tags
110
- tags = tags.map { |k, v| "#{k}:#{v}" } if tags.is_a?(Hash)
111
-
112
- # Fast path when no string replacement is needed
113
- return tags unless tags.any? { |tag| /[|,]/.match?(tag) }
114
- tags.map { |tag| tag.tr('|,', '') }
116
+ # For backwards compatibility
117
+ MetricExpectation = Expectation
115
118
  end
116
119
  end
117
-
118
- # For backwards compatibility
119
- StatsD::Instrument::MetricExpectation = StatsD::Instrument::Expectation