benchin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 56b56dc6c976815e1fdc736932b8918879c30b8eb925eb7f6834998a802aa0b0
4
+ data.tar.gz: c54eb4f9d6a5f3eae5287242acc36931eeeea68b7e2005fee42c874a3c13fef2
5
+ SHA512:
6
+ metadata.gz: 4aaace72b22276acb2afa8981f0256785afc558a9edaced10a1e6e1f99f72b8b844c5b231a95d8b0f610cd8e272377f8e6bc51972cd7d6c678de192da433098c
7
+ data.tar.gz: 5ca4be7447beea555217ad15916c8791cf6d69b71f7bfa91603dbad50a456c6aece5fbc6e441dc5c678e916cfd4151e5930c0426e7dfef2b9d8a59971c02dae9
@@ -0,0 +1,47 @@
1
+ name: Build
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ branches:
9
+ - master
10
+ schedule:
11
+ - cron: 0 2 * * 1-5
12
+
13
+ jobs:
14
+ build:
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ ruby:
20
+ - 2.5.x
21
+ - 2.6.x
22
+ steps:
23
+ - uses: actions/checkout@v1
24
+ - name: Set up Ruby
25
+ uses: actions/setup-ruby@v1
26
+ with:
27
+ ruby-version: ${{ matrix.ruby }}
28
+ - name: Install Bundler
29
+ run: gem install bundler
30
+ - name: Install Hunspell
31
+ run: sudo apt-get install hunspell
32
+ - name: Install Deps
33
+ run: bundle install --jobs 4 --retry 3
34
+ - name: Rubocop
35
+ run: bundle exec rake rubocop
36
+ - name: Reek
37
+ run: bundle exec rake reek
38
+ - name: RSpec
39
+ env:
40
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
41
+ run: bundle exec rake spec
42
+ - name: Markdown Linter (docs)
43
+ run: bundle exec rake mdl
44
+ - name: Spelling Check (docs & code)
45
+ run: bundle exec rake spellcheck
46
+ - name: Documentation Check
47
+ run: bundle exec rake inch
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.mdlrc ADDED
@@ -0,0 +1 @@
1
+ rules "~MD013", "~MD033"
@@ -0,0 +1,11 @@
1
+ ---
2
+ detectors:
3
+ TooManyInstanceVariables:
4
+ exclude:
5
+ - 'Benchin::Wrap::Report::Node'
6
+ TooManyStatements:
7
+ exclude:
8
+ - 'Benchin::Wrap#call'
9
+ DuplicateMethodCall:
10
+ allow_calls:
11
+ - 'Process.clock_gettime'
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,40 @@
1
+ require:
2
+ - rubocop-rspec
3
+ - rubocop-performance
4
+ - rubocop-md
5
+
6
+ AllCops:
7
+ TargetRubyVersion: 2.6
8
+
9
+ Metrics/LineLength:
10
+ Max: 120
11
+
12
+ Metrics/BlockLength:
13
+ Exclude:
14
+ - 'spec/**/*'
15
+ - '*.gemspec'
16
+
17
+ Style/FrozenStringLiteralComment:
18
+ EnforcedStyle: never
19
+
20
+ Metrics/ParameterLists:
21
+ CountKeywordArgs: false
22
+
23
+ RSpec/MessageSpies:
24
+ EnforcedStyle: receive
25
+
26
+ Naming/UncommunicativeMethodParamName:
27
+ Exclude:
28
+ - '**/*.md'
29
+
30
+ Style/MixinUsage:
31
+ Exclude:
32
+ - '**/*.md'
33
+
34
+ Lint/UnusedMethodArgument:
35
+ Exclude:
36
+ - '**/*.md'
37
+
38
+ Lint/UnusedBlockArgument:
39
+ Exclude:
40
+ - '**/*.md'
@@ -0,0 +1 @@
1
+ 2.6.5
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at roman.kolesnev@vineti.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [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 benchin.gemspec
4
+ gemspec
@@ -0,0 +1,166 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ benchin (0.1.0)
5
+ rainbow (~> 3.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.0)
11
+ axiom-types (0.1.1)
12
+ descendants_tracker (~> 0.0.4)
13
+ ice_nine (~> 0.11.0)
14
+ thread_safe (~> 0.3, >= 0.3.1)
15
+ backports (3.15.0)
16
+ codeclimate-engine-rb (0.4.1)
17
+ virtus (~> 1.0)
18
+ codecov (0.1.15)
19
+ json
20
+ simplecov
21
+ url
22
+ coderay (1.1.2)
23
+ coercible (1.0.0)
24
+ descendants_tracker (~> 0.0.1)
25
+ crass (1.0.5)
26
+ descendants_tracker (0.0.4)
27
+ thread_safe (~> 0.3, >= 0.3.1)
28
+ diff-lcs (1.3)
29
+ docile (1.3.2)
30
+ equalizer (0.0.11)
31
+ equatable (0.6.1)
32
+ ffi (1.11.1)
33
+ ffi-hunspell (0.5.0)
34
+ ffi (~> 1.0)
35
+ forspell (0.0.8)
36
+ backports (~> 3.0)
37
+ ffi-hunspell
38
+ highline
39
+ kramdown (~> 2.0)
40
+ kramdown-parser-gfm (~> 1.0)
41
+ parser
42
+ pastel
43
+ ruby-progressbar
44
+ sanitize (~> 5.0)
45
+ slop (~> 4.6)
46
+ yard
47
+ highline (2.0.3)
48
+ ice_nine (0.11.2)
49
+ inch (0.8.0)
50
+ pry
51
+ sparkr (>= 0.2.0)
52
+ term-ansicolor
53
+ yard (~> 0.9.12)
54
+ jaro_winkler (1.5.4)
55
+ json (2.2.0)
56
+ kramdown (2.1.0)
57
+ kramdown-parser-gfm (1.1.0)
58
+ kramdown (~> 2.0)
59
+ kwalify (0.7.2)
60
+ mdl (0.7.0)
61
+ kramdown (~> 2.0)
62
+ kramdown-parser-gfm (~> 1.0)
63
+ mixlib-cli (~> 2.1, >= 2.1.1)
64
+ mixlib-config (>= 2.2.1, < 4)
65
+ method_source (0.9.2)
66
+ mini_portile2 (2.4.0)
67
+ mixlib-cli (2.1.1)
68
+ mixlib-config (3.0.1)
69
+ tomlrb
70
+ nokogiri (1.10.4)
71
+ mini_portile2 (~> 2.4.0)
72
+ nokogumbo (2.0.1)
73
+ nokogiri (~> 1.8, >= 1.8.4)
74
+ parallel (1.18.0)
75
+ parser (2.6.5.0)
76
+ ast (~> 2.4.0)
77
+ pastel (0.7.3)
78
+ equatable (~> 0.6)
79
+ tty-color (~> 0.5)
80
+ pry (0.12.2)
81
+ coderay (~> 1.1.0)
82
+ method_source (~> 0.9.0)
83
+ psych (3.1.0)
84
+ rainbow (3.0.0)
85
+ rake (10.5.0)
86
+ reek (5.4.0)
87
+ codeclimate-engine-rb (~> 0.4.0)
88
+ kwalify (~> 0.7.0)
89
+ parser (>= 2.5.0.0, < 2.7, != 2.5.1.1)
90
+ psych (~> 3.1.0)
91
+ rainbow (>= 2.0, < 4.0)
92
+ rspec (3.9.0)
93
+ rspec-core (~> 3.9.0)
94
+ rspec-expectations (~> 3.9.0)
95
+ rspec-mocks (~> 3.9.0)
96
+ rspec-core (3.9.0)
97
+ rspec-support (~> 3.9.0)
98
+ rspec-expectations (3.9.0)
99
+ diff-lcs (>= 1.2.0, < 2.0)
100
+ rspec-support (~> 3.9.0)
101
+ rspec-mocks (3.9.0)
102
+ diff-lcs (>= 1.2.0, < 2.0)
103
+ rspec-support (~> 3.9.0)
104
+ rspec-support (3.9.0)
105
+ rubocop (0.76.0)
106
+ jaro_winkler (~> 1.5.1)
107
+ parallel (~> 1.10)
108
+ parser (>= 2.6)
109
+ rainbow (>= 2.2.2, < 4.0)
110
+ ruby-progressbar (~> 1.7)
111
+ unicode-display_width (>= 1.4.0, < 1.7)
112
+ rubocop-md (0.3.0)
113
+ rubocop (~> 0.60)
114
+ rubocop-performance (1.5.0)
115
+ rubocop (>= 0.71.0)
116
+ rubocop-rspec (1.36.0)
117
+ rubocop (>= 0.68.1)
118
+ ruby-progressbar (1.10.1)
119
+ sanitize (5.1.0)
120
+ crass (~> 1.0.2)
121
+ nokogiri (>= 1.8.0)
122
+ nokogumbo (~> 2.0)
123
+ simplecov (0.17.1)
124
+ docile (~> 1.1)
125
+ json (>= 1.8, < 3)
126
+ simplecov-html (~> 0.10.0)
127
+ simplecov-html (0.10.2)
128
+ slop (4.7.0)
129
+ sparkr (0.4.1)
130
+ term-ansicolor (1.7.1)
131
+ tins (~> 1.0)
132
+ thread_safe (0.3.6)
133
+ tins (1.22.0)
134
+ tomlrb (1.2.8)
135
+ tty-color (0.5.0)
136
+ unicode-display_width (1.6.0)
137
+ url (0.3.2)
138
+ virtus (1.0.5)
139
+ axiom-types (~> 0.1)
140
+ coercible (~> 1.0)
141
+ descendants_tracker (~> 0.0, >= 0.0.3)
142
+ equalizer (~> 0.0, >= 0.0.9)
143
+ yard (0.9.20)
144
+
145
+ PLATFORMS
146
+ ruby
147
+
148
+ DEPENDENCIES
149
+ benchin!
150
+ bundler (~> 2.0)
151
+ codecov
152
+ forspell (~> 0.0.8)
153
+ inch
154
+ mdl
155
+ rake (~> 10.0)
156
+ reek
157
+ rspec (~> 3.0)
158
+ rubocop
159
+ rubocop-md
160
+ rubocop-performance
161
+ rubocop-rspec
162
+ simplecov
163
+ yard
164
+
165
+ BUNDLED WITH
166
+ 2.0.2
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Roman Kolesnev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ # Benchin
2
+
3
+ [![Build Status](https://github.com/ffloyd/benchin/workflows/Build/badge.svg)](https://github.com/ffloyd/benchin/actions)
4
+ [![codecov](https://codecov.io/gh/ffloyd/benchin/branch/master/graph/badge.svg)](https://codecov.io/gh/ffloyd/benchin)
5
+ [![Gem Version](https://badge.fury.io/rb/benchin.svg)](https://badge.fury.io/rb/benchin)
6
+
7
+ Toolset for performance investigations in Ruby.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'benchin'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```sh
20
+ bundle
21
+ ```
22
+
23
+ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ ```sh
28
+ gem install benchin
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ Check out [documentation](https://rubydoc.info/github/ffloyd/benchin/master) for usage examples.
34
+
35
+ ## Development
36
+
37
+ 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.
38
+
39
+ 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).
40
+
41
+ ## Contributing
42
+
43
+ Bug reports and pull requests are welcome on GitHub at [ffloyd/benchin](https://github.com/ffloyd/benchin). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
44
+
45
+ ## License
46
+
47
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
48
+
49
+ ## Code of Conduct
50
+
51
+ Everyone interacting in the Benchin project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/ffloyd/benchin/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,30 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ require 'rubocop/rake_task'
5
+ require 'reek/rake/task'
6
+ require 'forspell/cli'
7
+ require 'mdl'
8
+ require 'inch/rake'
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+ RuboCop::RakeTask.new(:rubocop)
12
+ Reek::Rake::Task.new
13
+ Inch::Rake::Suggest.new
14
+
15
+ PATHS_TO_SPELLCHECK = ['.'].freeze
16
+ PATHS_FOR_MDL = ['README.md'].freeze
17
+
18
+ desc 'Run self spellchecking'
19
+ task :spellcheck do |_task|
20
+ Forspell::CLI.new(PATHS_TO_SPELLCHECK).call
21
+ end
22
+
23
+ desc 'Run markdown linter'
24
+ task :mdl do |_task|
25
+ MarkdownLint.run(PATHS_FOR_MDL)
26
+ end
27
+
28
+ # TODO: spellcheck and mdl tasks stop execution even with successful result
29
+ # should be fixed someday
30
+ task default: %i[rubocop reek inch spec spellcheck mdl]
@@ -0,0 +1,55 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'benchin/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'benchin'
7
+ spec.version = Benchin::VERSION
8
+ spec.authors = ['Roman Kolesnev']
9
+ spec.email = ['rvkolesnev@gmail.com']
10
+
11
+ spec.summary = 'Benchmarking toolset for performance investigations.'
12
+ spec.homepage = 'https://github.com/ffloyd/benchin'
13
+ spec.license = 'MIT'
14
+
15
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/ffloyd/benchin'
19
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ end
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_dependency 'rainbow', '~> 3.0'
31
+
32
+ # Essential dev deps
33
+ spec.add_development_dependency 'bundler', '~> 2.0'
34
+ spec.add_development_dependency 'rake', '~> 10.0'
35
+
36
+ # Testing
37
+ spec.add_development_dependency 'rspec', '~> 3.0'
38
+
39
+ # Test Coverage
40
+ spec.add_development_dependency 'codecov'
41
+ spec.add_development_dependency 'simplecov'
42
+
43
+ # Documentation
44
+ spec.add_development_dependency 'yard'
45
+
46
+ # Code & Documentation Quality
47
+ spec.add_development_dependency 'forspell', '~> 0.0.8'
48
+ spec.add_development_dependency 'inch'
49
+ spec.add_development_dependency 'mdl'
50
+ spec.add_development_dependency 'reek'
51
+ spec.add_development_dependency 'rubocop'
52
+ spec.add_development_dependency 'rubocop-md'
53
+ spec.add_development_dependency 'rubocop-performance'
54
+ spec.add_development_dependency 'rubocop-rspec'
55
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'benchin'
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__)
@@ -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,6 @@
1
+ # Format: one word per line. Empty lines and #-comments are supported too.
2
+ # If you want to add word with its forms, you can write 'word: example' (without quotes) on the line,
3
+ # where 'example' is existing word with the same possible forms (endings) as your word.
4
+ # Example: deduplicate: duplicate
5
+ Benchin
6
+ toolset
@@ -0,0 +1,18 @@
1
+ pre-commit:
2
+ parallel: true
3
+ commands:
4
+ rubocop:
5
+ glob: "{*.rb,*.md,*.gemspec,Gemfile,Rakefile}"
6
+ run: bundle exec rubocop {staged_files}
7
+ reek:
8
+ glob: "*.rb"
9
+ run: bundle exec reek {staged_files}
10
+ markdownlinter:
11
+ glob: "*.md"
12
+ run: bundle exec mdl {staged_files}
13
+ forspell:
14
+ glob: "{*.md,*.rb}"
15
+ run: bundle exec forspell {staged_files}
16
+ inch:
17
+ glob: "{*.md,*.rb}"
18
+ run: bundle exec rake inch
@@ -0,0 +1,34 @@
1
+ require 'benchin/version'
2
+ require 'benchin/wrap'
3
+
4
+ # Benchmarking toolset.
5
+ #
6
+ # @example Using {Wrap} global instance
7
+ # Benchin.wrap.reset
8
+ # Benchin.wrap.call('Expesive Code') do
9
+ # expesive_logic
10
+ # 10.times do
11
+ # Benchin.wrap.call('Nested Hot Operation') { do_something }
12
+ # end
13
+ # end
14
+ #
15
+ # puts Benchin.wrap
16
+ #
17
+ # @see Wrap
18
+ module Benchin
19
+ # Base error class
20
+ class Error < StandardError; end
21
+
22
+ class << self
23
+ # Returns global instance of {Wrap}.
24
+ #
25
+ # It can be used to simplify usage when you have
26
+ # to wrap code in many different places in your project.
27
+ #
28
+ # @see Wrap
29
+ # @return [Wrap]
30
+ def wrap
31
+ @wrap ||= Wrap.new('GLOBAL')
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module Benchin
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,148 @@
1
+ require_relative './wrap/report'
2
+
3
+ module Benchin
4
+ # Benchmark tool for high-level nested measurement. Check out {Report#to_h} for an example of
5
+ # the report structure.
6
+ #
7
+ # It's designed to use when:
8
+ #
9
+ # - you have some slow code (even one execution takes significant time)
10
+ # - you know which blocks of code you have to measure
11
+ # - you have to measure:
12
+ # - total time spend in each block
13
+ # - time spend in blocks executed inside a concrete block (child time)
14
+ # - time spend in a concrete block minus child block's time (self time)
15
+ #
16
+ # By using this information you can discover which block of slow code is the slowest one.
17
+ #
18
+ # Measurement uses WALL time.
19
+ #
20
+ # One of the most common possible examples of usage is to investigate slow requests in legacy and
21
+ # big codebases. In this case more classic instruments like stack profiling can be too
22
+ # focused on particular methods instead of logic chunks in your code.
23
+ #
24
+ # It can be inconvenient to 'drill' {Wrap} instance to all the places where we have to wrap some code.
25
+ # To address this issue we have helper {Benchin.wrap} which uses global {Wrap} instance.
26
+ #
27
+ # @example Measure request timings in controller
28
+ # class SomeDirtyController < SomeWebFramework::Controller
29
+ # def create(params)
30
+ # @bench = Benchin::Wrap.new('CREATE REQUEST')
31
+ #
32
+ # data = @bench.call('Create Operation') do
33
+ # do_create(params)
34
+ # end
35
+ #
36
+ # @bench.call('Rendering') do
37
+ # render data
38
+ # end
39
+ #
40
+ # File.write('report.txt', @bench.to_s)
41
+ # end
42
+ #
43
+ # private
44
+ #
45
+ # def do_create(params)
46
+ # do_something(params)
47
+ # @bench.call('Event Logging') do
48
+ # log_events
49
+ # end
50
+ # end
51
+ # end
52
+ # # Report saved to a file will look like:
53
+ # #
54
+ # # CREATE REQUEST ->
55
+ # # | time(all): 22.0
56
+ # # | Create Operation ->
57
+ # # | | calls: 1
58
+ # # | | time(all): 15.0
59
+ # # | | time(self): 5.0
60
+ # # | | time(childs): 10.0
61
+ # # | | Event Logging ->
62
+ # # | | | calls: 1
63
+ # # | | | time(all): 10.0
64
+ # # | | | time(self): 10.0
65
+ # # | | | time(childs): 0.0
66
+ # # | Rendering ->
67
+ # # | | calls: 1
68
+ # # | | time(all): 7.0
69
+ # # | | time(self): 7.0
70
+ # # | | time(childs): 0.0
71
+ class Wrap
72
+ # @return [Report] collected measurement data.
73
+ attr_reader :report
74
+
75
+ # @param name [String] report name
76
+ def initialize(name)
77
+ @name = name
78
+
79
+ @report = Report.new(@name)
80
+ @current_path = []
81
+ end
82
+
83
+ # Resets the {#report} to an empty state.
84
+ #
85
+ # @return [Wrap] self
86
+ def reset
87
+ @report = Report.new(@name)
88
+ @current_path = []
89
+
90
+ self
91
+ end
92
+
93
+ # Wraps code block with WALL time measurement and returns the block result.
94
+ #
95
+ # Can be used in a nested way.
96
+ #
97
+ # @example Simple measurement
98
+ # wrap = Benchin::Wrap.new
99
+ # wrap.call('Sum of strings') do
100
+ # ['aa', 'bb', 'cc'].sum
101
+ # end
102
+ # #=> 'aabbcc'
103
+ #
104
+ # @example Nested measurement
105
+ # wrap = Benchin::Wrap.new
106
+ # wrap.call('Big calcultation') do
107
+ # array = some_expensive_code
108
+ # processed = array.map do |data|
109
+ # wrap.call('Nested calcultation') do
110
+ # some_tranformation(data)
111
+ # end
112
+ # end
113
+ # send_somewhere(processed)
114
+ # end
115
+ #
116
+ # @param name [String] name for code block in reporting.
117
+ # @yield code block to execute.
118
+ # @return result of provided block execution.
119
+ def call(name)
120
+ @current_path.push name
121
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
122
+ result = yield
123
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
124
+ elapsed = ending - starting
125
+
126
+ report.add_time(@current_path, elapsed)
127
+ @current_path.pop
128
+
129
+ result
130
+ end
131
+
132
+ # Shortcut for `report.to_s`.
133
+ #
134
+ # @see {Report.to_s}
135
+ # @return [String]
136
+ def to_s
137
+ report.to_s
138
+ end
139
+
140
+ # Shortcut for `report.to_h`.
141
+ #
142
+ # @see {Report.to_s}
143
+ # @return [Hash]
144
+ def to_h
145
+ report.to_s
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,87 @@
1
+ require_relative './report/node'
2
+ require_relative './report/node_printer'
3
+
4
+ module Benchin
5
+ class Wrap
6
+ # Represents tree-like time measurement data produced by {Wrap}.
7
+ #
8
+ # See {#to_h} method for more concrete example.
9
+ class Report
10
+ # @param name [String] report name
11
+ def initialize(name)
12
+ @root = Node::Virtual.new(name)
13
+ end
14
+
15
+ # Adds seconds to a {path} in a tree and increases calls count by 1.
16
+ #
17
+ # @param path [Array<String>] path in a report tree.
18
+ # @param seconds [Float] amount of total seconds spent in this call (including child calls).
19
+ #
20
+ # @return [Report] self
21
+ def add_time(path, seconds)
22
+ target_node = path.reduce(@root) do |node, name|
23
+ node.nested[name] ||= Node.new(name)
24
+ end
25
+
26
+ target_node.add_call(seconds)
27
+
28
+ self
29
+ end
30
+
31
+ # Transforms report to a basic ruby types: arrays and hashes.
32
+ #
33
+ # @example
34
+ # report = Report.new('Report name')
35
+ # report
36
+ # .add_time(%w[TOP NESTED], 10.0)
37
+ # .add_time(%w[TOP], 10.0)
38
+ # .add_time(%w[TOP], 5.0)
39
+ # .add_time(%w[NESTED], 7.0)
40
+ # .to_h
41
+ # # will produce following structure
42
+ # {
43
+ # name: 'Report name',
44
+ # total_seconds: 22.0,
45
+ # nested: [
46
+ # {
47
+ # name: 'TOP',
48
+ # total_seconds: 15.0,
49
+ # self_seconds: 5.0,
50
+ # child_seconds: 10.0,
51
+ # calls: 2,
52
+ # nested: [
53
+ # {
54
+ # name: 'NESTED',
55
+ # total_seconds: 10.0,
56
+ # self_seconds: 10.0,
57
+ # child_seconds: 0.0,
58
+ # calls: 1,
59
+ # nested: []
60
+ # }
61
+ # ]
62
+ # },
63
+ # {
64
+ # name: 'NESTED',
65
+ # total_seconds: 7.0,
66
+ # self_seconds: 7.0,
67
+ # child_seconds: 0.0,
68
+ # calls: 1,
69
+ # nested: []
70
+ # }
71
+ # ]
72
+ # }
73
+ #
74
+ # @return [Hash] report represented using {Array}s and {Hash}es. It's safe to modify it.
75
+ def to_h
76
+ @root.to_h
77
+ end
78
+
79
+ # Renders human readable report.
80
+ #
81
+ # @return [String] human readable report with TTY colors
82
+ def to_s
83
+ NodePrinter.new(@root).to_s
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,75 @@
1
+ module Benchin
2
+ class Wrap
3
+ class Report
4
+ # @api private
5
+ class Node
6
+ attr_reader :name
7
+ attr_reader :calls
8
+ attr_reader :total_seconds
9
+ attr_reader :nested
10
+
11
+ def initialize(name)
12
+ @name = name
13
+
14
+ @calls = 0
15
+ @total_seconds = 0.0
16
+ @nested = {}
17
+ end
18
+
19
+ def add_call(seconds)
20
+ @child_seconds = nil
21
+ @calls += 1
22
+ @total_seconds += seconds
23
+ end
24
+
25
+ def to_h
26
+ {
27
+ name: name,
28
+ calls: calls,
29
+ total_seconds: total_seconds,
30
+ self_seconds: self_seconds,
31
+ child_seconds: child_seconds,
32
+ nested: nested.values.map(&:to_h)
33
+ }
34
+ end
35
+
36
+ def self_seconds
37
+ total_seconds - child_seconds
38
+ end
39
+
40
+ def child_seconds
41
+ child_nodes = nested.values
42
+
43
+ @child_seconds ||= child_nodes.map(&:total_seconds).sum.to_f
44
+ end
45
+
46
+ # Virtual Node is a node without ability to track time in.
47
+ #
48
+ # It is designed to be used as container for nested nodes.
49
+ #
50
+ # @api private
51
+ class Virtual < Node
52
+ def total_seconds
53
+ child_seconds
54
+ end
55
+
56
+ def self_seconds
57
+ 0.0
58
+ end
59
+
60
+ def add_time
61
+ raise 'Cannot add time to a virtual node'
62
+ end
63
+
64
+ def to_h
65
+ {
66
+ name: name,
67
+ total_seconds: total_seconds,
68
+ nested: nested.values.map(&:to_h)
69
+ }
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,103 @@
1
+ require 'rainbow'
2
+
3
+ module Benchin
4
+ class Wrap
5
+ class Report
6
+ # Human readable {Node} printing.
7
+ #
8
+ # With TTY colors.
9
+ #
10
+ # @api private
11
+ class NodePrinter
12
+ FIELD_SPACE = 14
13
+ VALUE_SPACE = 7
14
+ PRECISION = 3
15
+
16
+ # @param node [Node] node to print
17
+ # @param level [Integer] defines current level of nesting
18
+ def initialize(node, level: 0)
19
+ @node = node
20
+ @level = level
21
+ end
22
+
23
+ # @return [String] rendered report
24
+ def to_s
25
+ [
26
+ (@node.is_a?(Node::Virtual) ? virtual_body : body),
27
+ @node.nested.values.map do |child_node|
28
+ self.class.new(child_node, level: @level + 1).to_s
29
+ end
30
+ ].flatten.join("\n")
31
+ end
32
+
33
+ private
34
+
35
+ def virtual_body
36
+ nav = '| '
37
+ prefix = nav * @level
38
+
39
+ [
40
+ title,
41
+ nav + time_all
42
+ ].map { |line| prefix + line }.join("\n")
43
+ end
44
+
45
+ def body
46
+ nav = '| '
47
+ prefix = nav * @level
48
+
49
+ [
50
+ title,
51
+ nav + calls,
52
+ nav + time_all,
53
+ nav + time_self,
54
+ nav + time_childs
55
+ ].map { |line| prefix + line }.join("\n")
56
+ end
57
+
58
+ def title
59
+ "#{Rainbow(@node.name).bright} ->"
60
+ end
61
+
62
+ def calls
63
+ 'calls:'.ljust(FIELD_SPACE) +
64
+ Rainbow(
65
+ @node.calls
66
+ .to_s
67
+ .rjust(VALUE_SPACE)
68
+ ).blue
69
+ end
70
+
71
+ def time_all
72
+ 'time(all):'.ljust(FIELD_SPACE) +
73
+ Rainbow(
74
+ @node.total_seconds
75
+ .truncate(PRECISION)
76
+ .to_s
77
+ .rjust(VALUE_SPACE)
78
+ ).green
79
+ end
80
+
81
+ def time_self
82
+ 'time(self):'.ljust(FIELD_SPACE) +
83
+ Rainbow(
84
+ @node.self_seconds
85
+ .truncate(PRECISION)
86
+ .to_s
87
+ .rjust(VALUE_SPACE)
88
+ ).green.bright
89
+ end
90
+
91
+ def time_childs
92
+ 'time(childs):'.ljust(FIELD_SPACE) +
93
+ Rainbow(
94
+ @node.child_seconds
95
+ .truncate(PRECISION)
96
+ .to_s
97
+ .rjust(VALUE_SPACE)
98
+ ).green
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
metadata ADDED
@@ -0,0 +1,279 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: benchin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Roman Kolesnev
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-11-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rainbow
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: codecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: forspell
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.0.8
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.0.8
125
+ - !ruby/object:Gem::Dependency
126
+ name: inch
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: mdl
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: reek
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rubocop-md
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: rubocop-performance
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: rubocop-rspec
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ description:
224
+ email:
225
+ - rvkolesnev@gmail.com
226
+ executables: []
227
+ extensions: []
228
+ extra_rdoc_files: []
229
+ files:
230
+ - ".github/workflows/build.yml"
231
+ - ".gitignore"
232
+ - ".mdlrc"
233
+ - ".reek.yml"
234
+ - ".rspec"
235
+ - ".rubocop.yml"
236
+ - ".ruby-version"
237
+ - CODE_OF_CONDUCT.md
238
+ - Gemfile
239
+ - Gemfile.lock
240
+ - LICENSE.txt
241
+ - README.md
242
+ - Rakefile
243
+ - benchin.gemspec
244
+ - bin/console
245
+ - bin/setup
246
+ - forspell.dict
247
+ - lefthook.yml
248
+ - lib/benchin.rb
249
+ - lib/benchin/version.rb
250
+ - lib/benchin/wrap.rb
251
+ - lib/benchin/wrap/report.rb
252
+ - lib/benchin/wrap/report/node.rb
253
+ - lib/benchin/wrap/report/node_printer.rb
254
+ homepage: https://github.com/ffloyd/benchin
255
+ licenses:
256
+ - MIT
257
+ metadata:
258
+ homepage_uri: https://github.com/ffloyd/benchin
259
+ source_code_uri: https://github.com/ffloyd/benchin
260
+ post_install_message:
261
+ rdoc_options: []
262
+ require_paths:
263
+ - lib
264
+ required_ruby_version: !ruby/object:Gem::Requirement
265
+ requirements:
266
+ - - ">="
267
+ - !ruby/object:Gem::Version
268
+ version: '0'
269
+ required_rubygems_version: !ruby/object:Gem::Requirement
270
+ requirements:
271
+ - - ">="
272
+ - !ruby/object:Gem::Version
273
+ version: '0'
274
+ requirements: []
275
+ rubygems_version: 3.0.3
276
+ signing_key:
277
+ specification_version: 4
278
+ summary: Benchmarking toolset for performance investigations.
279
+ test_files: []