measurometer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ec73a73f9d9ffac70cebe59196acea5fe394003d
4
+ data.tar.gz: c75bd18585251af9ce93b54046c950b35d5202a4
5
+ SHA512:
6
+ metadata.gz: 8a2cafb15f8d6328b91f615c5662147798baaa20f08c7148708aa4a8c6599caf8d58a9f89cf075d51e5962e0fdea6b4081a771626be558fae17d336b530874e7
7
+ data.tar.gz: 0b946d3fcfb239b93e873a0da293f5febb1d3f8e3fa0dbd700352a4efd830594859eb38d226d8c65fe037251f25203442e86d6a54009b2bfe6af38f9d64270ef
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ inherit_gem:
2
+ wetransfer_style: ruby/default.yml
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.3
5
+ before_install: gem install bundler -v 1.14.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 1.0.0
2
+
3
+ Initial release
@@ -0,0 +1,46 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ ## Our Standards
8
+
9
+ Examples of behavior that contributes to creating a positive environment include:
10
+
11
+ * Using welcoming and inclusive language
12
+ * Being respectful of differing viewpoints and experiences
13
+ * Gracefully accepting constructive criticism
14
+ * Focusing on what is best for the community
15
+ * Showing empathy towards other community members
16
+
17
+ Examples of unacceptable behavior by participants include:
18
+
19
+ * The use of sexualized language or imagery and unwelcome sexual attention or advances
20
+ * Trolling, insulting/derogatory comments, and personal or political attacks
21
+ * Public or private harassment
22
+ * Publishing others' private information, such as a physical or electronic address, without explicit permission
23
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
24
+
25
+ ## Our Responsibilities
26
+
27
+ Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28
+
29
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30
+
31
+ ## Scope
32
+
33
+ This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34
+
35
+ ## Enforcement
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at julik@wetransfer.com and/or noah@wetransfer.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38
+
39
+ Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40
+
41
+ ## Attribution
42
+
43
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44
+
45
+ [homepage]: http://contributor-covenant.org
46
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in measurometer.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2018- WeTransfer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # Measurometer
2
+
3
+ Minimum viable API for instrumentation _in libraries._ A lightweight "hub" module that
4
+ will source simple instrumentation measurements/block contexts to different destinations. To illustrate the usefulness,
5
+ imagine you are using a library called `promulgator` in your code, as well as a library called `profligator`
6
+
7
+ You start measuring how long tasks in your application take, and you notice that an action suddenly takes 30 seconds.
8
+ You for a fact know that this action consists of the following code:
9
+
10
+ ```
11
+ heavy_model = ModelTree.find(user_id)
12
+ prpfligation_outcome = Profilgator.profligate(heavy_model)
13
+ Promulgator.promulgate(profligation_outcome)
14
+ ```
15
+
16
+ You can see how long `ModelTree.find` takes, because your metrics and error tracing solution latches onto the ActiveSupport
17
+ instrumentation hooks and records how long things take. But the Promulgator and the Profligator... who knows
18
+ what they do and for how long? Writing of benchmark scripts ensues.
19
+
20
+ Imagine, on the other hand, that you had the `Meaasurometer` supported in both the Promulgator and the Profligator.
21
+ Then you could say, for example, if you use [Appsignal](https://appsignal.com)
22
+
23
+ ```
24
+ Measurometer.drivers << Appsignal
25
+ ```
26
+
27
+ and metrics from both the `promulgator` gem and the `profligator` gem calls would be sourced to Appsignal automatically.
28
+ The good part of it is that neither the `promulgator` or the `profligator` would have to depend on something
29
+ heavy (or Rails version dependent!) as `ActiveSupport::Notifications`
30
+
31
+ ## Visualising the benefit
32
+
33
+ This is an action from one of our applications, where we parse the image format and then perform image processing.
34
+ Both tasks (format detection and image transformations) are handled by separate libraries - `format_parser` and
35
+ `image_vise` respectively. Even though the graph comes from Appsignal, neither of the libraries has knowledge
36
+ of Appsignal's existence in the system.
37
+
38
+ ![Appsignal action with Measurometer sources](measurometer_in_practice.png)
39
+
40
+ ## Usage with statsd-ruby
41
+
42
+ For [statsd-ruby](https://github.com/reinh/statsd) we provide a builtin adapter. Pass it your client object:
43
+
44
+ ```
45
+ $statsd = Statsd.new 'localhost', 8125
46
+ Measurometer.drivers << Measurometer::StatsdDriver.new($statsd)
47
+ ```
48
+
49
+ ## Installation for libraries
50
+
51
+ If you want to supply Measurometer metrics, add this line to your **library's** Gemfile:
52
+
53
+ ```ruby
54
+ s.add_dependency 'measurometer', '~> 1'
55
+ ```
56
+
57
+ That's right, we _promise_ to guarantee version 1 compatibility for as long as possible, for as long
58
+ as practical, and if there are to be breaking changes to the API semantic versioning will be followed.
59
+
60
+ Should it so happen that you do not trust us that API version compatibility will be maintained,
61
+ you can **copy** the entire Measurometer into your library, just let the user know that the driver
62
+ will have to be installed for your library separately. **Seriously though:** – we do realize
63
+ that Measurometer is meant to be a diamond dependency and will therefore commit to maintaining semver.
64
+
65
+ ## Usage in libraries
66
+
67
+ When executing a semantically meaningful block of code, name the block using your library's name as a prefix,
68
+ and run the block wrapped with `Measurometer.instrument()`
69
+
70
+ ```
71
+ Measurometer.instrument('profligator.ideate') do
72
+ options_offered_to_client = Thinkfluencer.ideate(the_creative)
73
+ Measurometer.increment_counter('profligator.num_ideations', 1)x
74
+ Measurometer.add_distribution_value('profligator.options_offered_to_client_per_ideation', options_offered_to_client.length)
75
+ end
76
+ ```
77
+
78
+ Note that it is prudent to make your library provide **either** Measurometer instrumentation **or** ActiveSupport::Notifications,
79
+ but **not both** - unless your metrics collection driver/system can deal with that cleanly.
80
+
81
+ ## Usage in applications
82
+
83
+ If one or more of the libraries you are using are Measurometer-instrumented, all you have to do is
84
+ connect a driver. The driver must respond to the same methods as the Measurometer module, and has to
85
+ be explicitly added to the set of Measurometer drivers to source instrumentation, like so:
86
+
87
+ ```
88
+ Measurometer.drivers << Appsignal
89
+ ```
90
+
91
+ Appsignal, since Measurometer's API copies it's instrumentaiton API, can be used as-is,
92
+ without any adapters.
93
+
94
+ ## Development
95
+
96
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
97
+
98
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
99
+
100
+ ## Contributing
101
+
102
+ Bug reports and pull requests are welcome on GitHub at https://github.com/WeTransfer/measurometer.
103
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'measurometer'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,38 @@
1
+ module Measurometer
2
+ class StatsdDriver
3
+ attr_accessor :statsd_client
4
+
5
+ MONOTONIC_AVAILABLE = defined?(Process::CLOCK_MONOTONIC)
6
+
7
+ def initialize(ruby_statsd_client)
8
+ @statsd_client = ruby_statsd_client
9
+ end
10
+
11
+ def instrument(action_name)
12
+ s = gettime
13
+ yield.tap do
14
+ delta_fractional_s = gettime - s
15
+ millis = (delta_fractional_s * 1000).to_i
16
+ @statsd_client.timing(action_name, millis)
17
+ end
18
+ end
19
+
20
+ def increment_counter(counter_name, by)
21
+ @statsd_client.increment(counter_name, by)
22
+ end
23
+
24
+ def add_distribution_value(key_path, value)
25
+ @statsd_client.count(key_path, value)
26
+ end
27
+
28
+ def set_gauge(gauge_name, value)
29
+ @statsd_client.gauge(gauge_name, value)
30
+ end
31
+
32
+ private
33
+
34
+ def gettime
35
+ MONOTONIC_AVAILABLE ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : Time.now.to_f
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Measurometer
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,89 @@
1
+ require 'measurometer/version'
2
+ require 'set'
3
+
4
+ module Measurometer
5
+ @drivers = Set.new
6
+ autoload :StatsdDriver, 'measurometer/statsd_driver'
7
+
8
+ class << self
9
+ # Permits adding instrumentation drivers. Measurometer is 1-1 API
10
+ # compatible with Appsignal, which we use a lot. So to magically
11
+ # obtain all Appsignal instrumentation, add the Appsignal module
12
+ # as a driver.
13
+ #
14
+ # Measurometer.drivers << Appsignal
15
+ #
16
+ # A driver must be reentrant and thread-safe - it should be possible
17
+ # to have multiple `instrument` calls open from different threads at the
18
+ # same time.
19
+ #
20
+ # The driver must support the same interface as the Measurometer class
21
+ # itself, minus the `drivers` method.
22
+ #
23
+ # Note that this method does not return a copy of the drivers, it returns
24
+ # the mutable Set itself
25
+ #
26
+ # @return [Set]
27
+ def drivers
28
+ @drivers
29
+ end
30
+
31
+ # Runs a given block within a cascade of `instrument` blocks of all the
32
+ # added drivers.
33
+ #
34
+ # Measurometer.instrument('do_foo') { compute! }
35
+ #
36
+ # unfolds to
37
+ #
38
+ # Appsignal.instrument('do_foo') do
39
+ # StatsdDriver#instrument('do_foo') do
40
+ # compute!
41
+ # end
42
+ # end
43
+ #
44
+ # @param block_name[String] under which path to push the metric
45
+ # @param blk[#call] the block to instrument
46
+ # @return [Object] the return value of &blk
47
+ def instrument(block_name, &blk)
48
+ return yield if @drivers.empty? # The block wrapping business is not free
49
+ blk_return_value = nil
50
+ blk_with_capture = -> { blk_return_value = blk.call }
51
+ @drivers.inject(blk_with_capture) { |outer_block, driver|
52
+ -> {
53
+ driver.instrument(block_name, &outer_block)
54
+ }
55
+ }.call
56
+ blk_return_value
57
+ end
58
+
59
+ # Adds a distribution value (sample) under a given path
60
+ #
61
+ # @param value_path[String] under which path to push the metric
62
+ # @param value[Numeric] distribution value
63
+ # @return nil
64
+ def add_distribution_value(value_path, value)
65
+ @drivers.each { |d| d.add_distribution_value(value_path, value) }
66
+ nil
67
+ end
68
+
69
+ # Increment a named counter under a given path
70
+ #
71
+ # @param counter_path[String] under which path to push the metric
72
+ # @param by[Integer] the counter increment to apply
73
+ # @return nil
74
+ def increment_counter(counter_path, by)
75
+ @drivers.each { |d| d.increment_counter(counter_path, by) }
76
+ nil
77
+ end
78
+
79
+ # Set a global single named value (gauge)
80
+ #
81
+ # @param gauge_name[String] under which path to push the metric
82
+ # @param value[Integer] the absolute value of the gauge
83
+ # @return nil
84
+ def set_gauge(gauge_name, value)
85
+ @drivers.each { |d| d.set_gauge(gauge_name, value) }
86
+ nil
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,27 @@
1
+
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'measurometer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'measurometer'
8
+ spec.version = Measurometer::VERSION
9
+ spec.authors = ['Julik Tarkhanov']
10
+ spec.email = ['me@julik.nl']
11
+
12
+ spec.summary = 'Minimum viable API for instrumentation in libraries'
13
+ spec.description = 'Minimum viable API for instrumentation in libraries. Source metrics from your libraries to Measurometer, pick them up on the other end in the application, centrally.'
14
+ spec.homepage = 'https://github.com/WeTransfer/measurometer'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(/\.png$/)
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.14'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec', '~> 3.0'
26
+ spec.add_development_dependency 'wetransfer_style', '0.5.0'
27
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Measurometer::StatsdDriver' do
4
+ after(:each) do
5
+ Measurometer.drivers.clear
6
+ end
7
+
8
+ it 'passes metrics to the contained Statsd client' do
9
+ statsd = spy('Statsd')
10
+ Measurometer.drivers << Measurometer::StatsdDriver.new(statsd)
11
+
12
+ Measurometer.instrument('some_block.x') do
13
+ sleep 0.21
14
+ end
15
+
16
+ Measurometer.set_gauge('app.some_gauge', 42)
17
+ Measurometer.increment_counter('app.some_counter', 2)
18
+ Measurometer.add_distribution_value('app.some_sample', 42)
19
+
20
+ expect(statsd).to have_received(:timing) {|block_name, timing_millis|
21
+ expect(block_name).to eq('some_block.x')
22
+ expect(timing_millis).to be_within(20).of(200)
23
+ }
24
+
25
+ expect(statsd).to have_received(:increment).with('app.some_counter', 2)
26
+ expect(statsd).to have_received(:count).with('app.some_sample', 42)
27
+ end
28
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Measurometer do
4
+ RSpec::Matchers.define :include_counter_or_measurement_named do |named|
5
+ match do |actual|
6
+ actual.any? do |e|
7
+ e[0] == named && e[1] > 0
8
+ end
9
+ end
10
+ end
11
+
12
+ it 'has a version number' do
13
+ expect(Measurometer::VERSION).not_to be nil
14
+ end
15
+
16
+ describe '.drivers' do
17
+ before(:each) { Measurometer.drivers.clear }
18
+ after(:each) { Measurometer.drivers.clear }
19
+ let(:driver) { Object.new }
20
+
21
+ it 'allows adding and removing a driver' do
22
+ expect(Measurometer.drivers).not_to include(driver)
23
+
24
+ Measurometer.drivers << driver
25
+ expect(Measurometer.drivers).to include(driver)
26
+
27
+ Measurometer.drivers.delete(driver)
28
+ expect(Measurometer.drivers).not_to include(driver)
29
+ end
30
+
31
+ it 'does not add the same driver twice' do
32
+ Measurometer.drivers.clear
33
+ 3.times { Measurometer.drivers << driver }
34
+ expect(Measurometer.drivers.length).to eq(1)
35
+ end
36
+ end
37
+
38
+ describe '.instrument' do
39
+ it 'preserves the return value of the block even if one of the drivers swallows it' do
40
+ bad_driver = Object.new
41
+ def bad_driver.instrument(_blk)
42
+ yield
43
+ nil # Be nasty
44
+ end
45
+
46
+ Measurometer.drivers << bad_driver
47
+ instrument_result = Measurometer.instrument('foo') do
48
+ :block_result
49
+ end
50
+ Measurometer.drivers.delete(bad_driver)
51
+
52
+ expect(instrument_result).to eq(:block_result)
53
+ end
54
+ end
55
+
56
+ it 'sources instrumentation to a driver' do
57
+ driver_class = Class.new do
58
+ attr_accessor :timings, :counters, :distributions, :gauges
59
+ def instrument(block_name)
60
+ s = Process.clock_gettime(Process::CLOCK_MONOTONIC)
61
+ yield.tap do
62
+ delta = Process.clock_gettime(Process::CLOCK_MONOTONIC) - s
63
+ @timings ||= []
64
+ @timings << [block_name, delta * 1000]
65
+ end
66
+ end
67
+
68
+ def add_distribution_value(value_path, value)
69
+ @distributions ||= []
70
+ @distributions << [value_path, value]
71
+ end
72
+
73
+ def increment_counter(value_path, value)
74
+ @counters ||= []
75
+ @counters << [value_path, value]
76
+ end
77
+
78
+ def set_gauge(value_path, value)
79
+ @gauges ||= []
80
+ @gauges << [value_path, value]
81
+ end
82
+ end
83
+
84
+ instrumenter = driver_class.new
85
+ Measurometer.drivers << instrumenter
86
+
87
+ Measurometer.instrument('something_amazing.foo') do
88
+ sleep(rand / 4)
89
+ Measurometer.instrument('something_amazing.subtask') do
90
+ sleep(rand / 9)
91
+ Measurometer.increment_counter('something_amazing.subtasks_performed', 1)
92
+ end
93
+ Measurometer.instrument('something_amazing.another_subtask') do
94
+ sd = rand / 9
95
+ sleep(sd)
96
+ Measurometer.add_distribution_value('something_amazing.another_subtask.sleep_durations', sd)
97
+ end
98
+ Measurometer.set_gauge('some.gauge', 42)
99
+ :task_finished
100
+ end
101
+
102
+ Measurometer.drivers.delete(instrumenter)
103
+
104
+ expect(instrumenter.counters).to include_counter_or_measurement_named('something_amazing.subtasks_performed')
105
+ expect(instrumenter.distributions).to include_counter_or_measurement_named('something_amazing.another_subtask.sleep_durations')
106
+ expect(instrumenter.timings).to include_counter_or_measurement_named('something_amazing.subtask')
107
+ expect(instrumenter.timings).to include_counter_or_measurement_named('something_amazing.another_subtask')
108
+ expect(instrumenter.timings).to include_counter_or_measurement_named('something_amazing.foo')
109
+ expect(instrumenter.gauges).to include_counter_or_measurement_named('some.gauge')
110
+ end
111
+ end
@@ -0,0 +1,11 @@
1
+ require 'bundler/setup'
2
+ require 'measurometer'
3
+
4
+ RSpec.configure do |config|
5
+ # Enable flags like --only-failures and --next-failure
6
+ config.example_status_persistence_file_path = '.rspec_status'
7
+
8
+ config.expect_with :rspec do |c|
9
+ c.syntax = :expect
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: measurometer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Julik Tarkhanov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-04-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: wetransfer_style
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.5.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.5.0
69
+ description: Minimum viable API for instrumentation in libraries. Source metrics from
70
+ your libraries to Measurometer, pick them up on the other end in the application,
71
+ centrally.
72
+ email:
73
+ - me@julik.nl
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - ".rspec"
80
+ - ".rubocop.yml"
81
+ - ".travis.yml"
82
+ - CHANGELOG.md
83
+ - CODE_OF_CONDUCT.md
84
+ - Gemfile
85
+ - LICENSE.txt
86
+ - README.md
87
+ - Rakefile
88
+ - bin/console
89
+ - bin/setup
90
+ - lib/measurometer.rb
91
+ - lib/measurometer/statsd_driver.rb
92
+ - lib/measurometer/version.rb
93
+ - measurometer.gemspec
94
+ - spec/measurometer/statsd_driver_spec.rb
95
+ - spec/measurometer_spec.rb
96
+ - spec/spec_helper.rb
97
+ homepage: https://github.com/WeTransfer/measurometer
98
+ licenses: []
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.5.2
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Minimum viable API for instrumentation in libraries
120
+ test_files: []