pax_global_header00006660000000000000000000000064147446271370014531gustar00rootroot0000000000000052 comment=7b8c5a405fbea0cf78babf0401c07121daa46f8e copiousfreetime-hitimes-eccb77f/000077500000000000000000000000001474462713700171555ustar00rootroot00000000000000copiousfreetime-hitimes-eccb77f/.fossa.yml000077500000000000000000000004541474462713700210770ustar00rootroot00000000000000# Generated by FOSSA CLI (https://github.com/fossas/fossa-cli) # Visit https://fossa.io to learn more version: 1 cli: server: https://app.fossa.io fetcher: custom project: git@github.com:copiousfreetime/hitimes.git analyze: modules: - name: Gemfile type: gem target: . path: . copiousfreetime-hitimes-eccb77f/.gitignore000066400000000000000000000002301474462713700211400ustar00rootroot00000000000000*~ *.swp *.swo *.o *.a ext/hitimes/Makefile ext/hitimes/rbconfig.rb *.log a.out *.so *.bundle *.class *.jar coverage doc log pkg tags tmp .gem/ vendor/ copiousfreetime-hitimes-eccb77f/.rubocop.yml000066400000000000000000000026721474462713700214360ustar00rootroot00000000000000require: - rubocop-md - rubocop-minitest - rubocop-packaging - rubocop-performance - rubocop-rake - rubocop-thread_safety AllCops: NewCops: enable TargetRubyVersion: 3.0 Exclude: - 'coverage/**/*' - 'doc/**/*' - 'pkg/**/*' - 'tmp/**/*' - '*.gemspec' - 'vendor/bundle/**/*' - 'examples/*' Layout/ExtraSpacing: AllowBeforeTrailingComments: true Layout/SpaceInsideHashLiteralBraces: EnforcedStyle: space Lint/DuplicateBranch: IgnoreLiteralBranches: true IgnoreConstantBranches: true Lint/SuppressedException: Exclude: - 'spec/spec_helper.rb' Metrics/AbcSize: CountRepeatedAttributes: false Max: 25 Exclude: - 'tasks/**/*' Metrics/BlockLength: Exclude: - 'tasks/**/*' - 'spec/**/*' Metrics/ClassLength: Exclude: - 'tasks/**/*' Metrics/MethodLength: Max: 20 # keeps want to combine examples Style/CombinableLoops: Exclude: - README.md Style/Documentation: Exclude: - 'spec/**/*' Style/StringLiterals: Enabled: true EnforcedStyle: double_quotes ConsistentQuotesInMultiline: false Style/TernaryParentheses: EnforcedStyle: require_parentheses_when_complex Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: consistent_comma Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: consistent_comma ThreadSafety/ClassInstanceVariable: Enabled: false ThreadSafety/NewThread: Enabled: false ThreadSafety/DirChdir: Enabled: false copiousfreetime-hitimes-eccb77f/.ruby-version000066400000000000000000000000061474462713700216160ustar00rootroot000000000000003.4.1 copiousfreetime-hitimes-eccb77f/.semaphore/000077500000000000000000000000001474462713700212165ustar00rootroot00000000000000copiousfreetime-hitimes-eccb77f/.semaphore/ensure-bundle.sh000077500000000000000000000010611474462713700243230ustar00rootroot00000000000000# Ensure that the bundle is installed and cached # bundle config set --local deployment true bundle config set --local path vendor/bundle gem update --no-doc bundler gemfile_checksum=$(checksum Gemfile.lock) cache_key="${SEMAPHORE_AGENT_MACHINE_OS_IMAGE}-${RUBY_VERSION}-${gemfile_checksum}" if cache has_key "${cache_key}"; then echo "Bundle for ${RUBY_VERSION} and Gemfile.lock found in cache" cache restore "${cache_key}" else echo "Caching Bundle for ${RUBY_VERSION} and Gemfile.lock" bundle install cache store "${cache_key}" vendor/bundle fi copiousfreetime-hitimes-eccb77f/.semaphore/ensure-ruby-version.sh000077500000000000000000000006661474462713700255300ustar00rootroot00000000000000# Ensure the correct Ruby version is installed and cached # cache_key="${SEMAPHORE_AGENT_MACHINE_OS_IMAGE}-${RUBY_VERSION}" if cache has_key "${cache_key}"; then echo "Ruby ${RUBY_VERSION} found in cache" cache restore "${cache_key}" sem-version ruby "${RUBY_VERSION}" -f else echo "Installing Ruby $RUBY_VERSION" sem-version ruby "${RUBY_VERSION}" -f cache store "${cache_key}" "${HOME}/.rbenv/versions/${RUBY_VERSION}" fi copiousfreetime-hitimes-eccb77f/.semaphore/semaphore.yml000066400000000000000000000041131474462713700237230ustar00rootroot00000000000000version: v1.0 name: CI Pipeline agent: machine: type: e1-standard-2 os_image: ubuntu2004 auto_cancel: running: when: "branch != 'main'" blocks: - name: Run tests in Linux environment dependencies: [] task: prologue: commands: - checkout - sudo apt-get update && sudo apt-get install -y libyaml-dev - git -C ${HOME}/.rbenv/plugins/ruby-build pull epilogue: always: commands: - test-results publish --name ${RUBY_VERSION} ${TEST_RESULTS_FILE} jobs: - name: run tests matrix: - env_var: RUBY_VERSION values: - 3.1.6 - 3.2.6 - 3.3.7 - 3.4.1 - jruby-9.4.10.0 - truffleruby-24.1.1 commands: - source .semaphore/ensure-ruby-version.sh - source .semaphore/ensure-bundle.sh - mkdir -p tmp/test-results/ - bundle exec rake rubocop - export TEST_RESULTS_FILE=tmp/test-results/${RUBY_VERSION}.xml - A="--junit --junit-filename=${TEST_RESULTS_FILE}" bundle exec rake test - name: Run MacOS Tests dependencies: [] task: agent: machine: type: a1-standard-4 os_image: macos-xcode15 prologue: commands: - checkout - git -C ${HOME}/.rbenv/plugins/ruby-build pull jobs: - name: macos matrix test matrix: - env_var: RUBY_VERSION values: - 3.1.6 - 3.2.5 - 3.3.7 - 3.4.1 commands: - source .semaphore/ensure-ruby-version.sh - source .semaphore/ensure-bundle.sh - mkdir -p tmp/test-results/ - bundle exec rake rubocop - export TEST_RESULTS_FILE=tmp/test-results/${RUBY_VERSION}.xml - bundle exec rake test after_pipeline: task: jobs: - name: Publish Results commands: - test-results gen-pipeline-report copiousfreetime-hitimes-eccb77f/CONTRIBUTING.md000066400000000000000000000036611474462713700214140ustar00rootroot00000000000000# Hi there! I see you are interested in contributing. That is wonderful. I love contributions. I guarantee that there are bugs in this software. And I guarantee that there is a feature you want that is not in here yet. As such, any and all bugs reports are gratefully accepted, bugfixes even more so. Helping out with bugs is the easiest way to contribute. ## The Quick Version * Have a [GitHub Account][]. * Search the [GitHub Issues][] and see if your issue already present. If so add your comments, :thumbsup:, etc. * Issue not there? Not a problem, open up a [new issue][]. * **Bug reports** please be as detailed as possible. Include: * full ruby engine and version: `ruby -e 'puts RUBY_DESCRIPTION'` * operating system and version * version of hitimes `ruby -rubygems -e "require 'hitimes'; puts Hitimes::VERSION"` * as much detail about the bug as possible so I can replicated it. Feel free to link in a [gist][] * **New Feature** * What the new feature should do. * What benefit the new feature brings to the project. * Fork the [repo][]. * Create a new branch for your issue: `git checkout -b issue/my-issue` * Lovingly craft your contribution: * `bin/setup` to get started * `rake test` to run tests * Make sure that `rake test` passes. Its important, I said it twice. * Add yourself to the contributors section below. * Submit your [pull request][]. # Contributors * Jeremy Hinegardner * Wojciech Piekutowski [GitHub Account]: https://github.com/signup/free "GitHub Signup" [GitHub Issues]: https://github.com/copiousfreetime/hitimes/issues "Hitimes Issues" [new issue]: https://github.com/copiousfreetime/hitimes/issues/new "New Hitimes Issue" [gist]: https://gist.github.com/ "New Gist" [repo]: https://github.com/copiousfreetime/hitimes "hitimes Repo" [pull request]: https://help.github.com/articles/using-pull-requests "Using Pull Requests" copiousfreetime-hitimes-eccb77f/Gemfile000066400000000000000000000012551474462713700204530ustar00rootroot00000000000000# frozen_string_literal: true source "https://rubygems.org" gemspec # gem "debug", "~> 1.0", require: false gem "heel", require: false gem "minitest", "~> 5.11" gem "minitest-focus", "~> 1.2" gem "minitest-junit", "~> 1.0" gem "rake" gem "rdoc", "~> 6.3", require: false gem "reek", require: false gem "rubocop", "~> 1.63", require: false gem "rubocop-minitest", "~> 0.35", require: false gem "rubocop-packaging", "~> 0.5", require: false gem "rubocop-performance", "~> 1.21", require: false gem "rubocop-rake", "~> 0.6", require: false gem "rubocop-thread_safety", "~> 0.5", require: false gem "rubocop-md", "~> 1.2", require: false gem "simplecov", "~> 0.22", require: false copiousfreetime-hitimes-eccb77f/Gemfile.lock000066400000000000000000000233321474462713700214020ustar00rootroot00000000000000PATH remote: . specs: hitimes (3.1.0) GEM remote: https://rubygems.org/ specs: addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) ast (2.4.2) bigdecimal (3.1.9) bigdecimal (3.1.9-java) builder (3.3.0) concurrent-ruby (1.3.5) date (3.4.1) date (3.4.1-java) docile (1.4.1) dry-configurable (1.3.0) dry-core (~> 1.1) zeitwerk (~> 2.6) dry-core (1.1.0) concurrent-ruby (~> 1.0) logger zeitwerk (~> 2.6) dry-inflector (1.2.0) dry-initializer (3.2.0) dry-logic (1.6.0) bigdecimal concurrent-ruby (~> 1.0) dry-core (~> 1.1) zeitwerk (~> 2.6) dry-schema (1.13.4) concurrent-ruby (~> 1.0) dry-configurable (~> 1.0, >= 1.0.1) dry-core (~> 1.0, < 2) dry-initializer (~> 3.0) dry-logic (>= 1.4, < 2) dry-types (>= 1.7, < 2) zeitwerk (~> 2.6) dry-types (1.8.1) bigdecimal (~> 3.0) concurrent-ruby (~> 1.0) dry-core (~> 1.0) dry-inflector (~> 1.0) dry-logic (~> 1.4) zeitwerk (~> 2.6) heel (4.0.1) launchy (~> 2.5) mime-types (~> 3.4) puma (~> 6.0) rack (~> 3.0) rackup (~> 2.1) rouge (~> 4.0) jar-dependencies (0.5.3) json (2.9.1) json (2.9.1-java) language_server-protocol (3.17.0.3) launchy (2.5.2) addressable (~> 2.8) logger (1.6.5) mime-types (3.6.0) logger mime-types-data (~> 3.2015) mime-types-data (3.2025.0107) minitest (5.25.4) minitest-focus (1.4.0) minitest (>= 4, < 6) minitest-junit (1.1.0) builder (~> 3.2) minitest (~> 5.11) nio4r (2.7.4) nio4r (2.7.4-java) parallel (1.26.3) parser (3.3.7.0) ast (~> 2.4.1) racc psych (5.2.3) date stringio psych (5.2.3-java) date jar-dependencies (>= 0.1.7) public_suffix (6.0.1) puma (6.5.0) nio4r (~> 2.0) puma (6.5.0-java) nio4r (~> 2.0) racc (1.8.1) racc (1.8.1-java) rack (3.1.8) rackup (2.2.1) rack (>= 3) rainbow (3.1.1) rake (13.2.1) rdoc (6.11.0) psych (>= 4.0.0) reek (6.4.0) dry-schema (~> 1.13.0) logger (~> 1.6) parser (~> 3.3.0) rainbow (>= 2.0, < 4.0) rexml (~> 3.1) regexp_parser (2.10.0) rexml (3.4.0) rouge (4.5.1) rubocop (1.71.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) rubocop-ast (>= 1.36.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) rubocop-ast (1.37.0) parser (>= 3.3.1.0) rubocop-md (1.2.4) rubocop (>= 1.45) rubocop-minitest (0.36.0) rubocop (>= 1.61, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) rubocop-performance (1.23.1) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) rubocop-rake (0.6.0) rubocop (~> 1.0) rubocop-thread_safety (0.6.0) rubocop (>= 1.48.1) ruby-progressbar (1.13.0) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.13.1) simplecov_json_formatter (0.1.4) stringio (3.1.2) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) zeitwerk (2.6.18) PLATFORMS arm64-darwin java ruby DEPENDENCIES heel hitimes! minitest (~> 5.11) minitest-focus (~> 1.2) minitest-junit (~> 1.0) rake rdoc (~> 6.3) reek rubocop (~> 1.63) rubocop-md (~> 1.2) rubocop-minitest (~> 0.35) rubocop-packaging (~> 0.5) rubocop-performance (~> 1.21) rubocop-rake (~> 0.6) rubocop-thread_safety (~> 0.5) simplecov (~> 0.22) CHECKSUMS addressable (2.8.7) sha256=462986537cf3735ab5f3c0f557f14155d778f4b43ea4f485a9deb9c8f7c58232 ast (2.4.2) sha256=1e280232e6a33754cde542bc5ef85520b74db2aac73ec14acef453784447cc12 bigdecimal (3.1.9) sha256=2ffc742031521ad69c2dfc815a98e426a230a3d22aeac1995826a75dabfad8cc bigdecimal (3.1.9-java) sha256=dd9b8f7c870664cd9538a1325ce385ba57a6627969177258c4f0e661a7be4456 builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6 date (3.4.1) sha256=bf268e14ef7158009bfeaec40b5fa3c7271906e88b196d958a89d4b408abe64f date (3.4.1-java) sha256=74740d914c65a922a15657c25ff0e203c16f1d0f7aa910a9ebed712afe9819c4 docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e dry-configurable (1.3.0) sha256=882d862858567fc1210d2549d4c090f34370fc1bb7c5c1933de3fe792e18afa8 dry-core (1.1.0) sha256=0903821a9707649a7da545a2cd88e20f3a663ab1c5288abd7f914fa7751ab195 dry-inflector (1.2.0) sha256=22f5d0b50fd57074ae57e2ca17e3b300e57564c218269dcf82ff3e42d3f38f2e dry-initializer (3.2.0) sha256=37d59798f912dc0a1efe14a4db4a9306989007b302dcd5f25d0a2a20c166c4e3 dry-logic (1.6.0) sha256=da6fedbc0f90fc41f9b0cc7e6f05f5d529d1efaef6c8dcc8e0733f685745cea2 dry-schema (1.13.4) sha256=caeb644de0be412d347eb4a6d91c56ceef8ec22cfceb98e80d03d354954b1d2a dry-types (1.8.1) sha256=3fe395835763c64fb76f1076b564d718e0c2519afbfddb8ab5609a4724d70a95 heel (4.0.1) sha256=bd0bd0b20efec89a6bf2ab37958525386cfe84d0ba6159f49ac9cf684ec433d9 hitimes (3.0.0) jar-dependencies (0.5.3) sha256=f6a7a0bf3f917252461e2a87f9f54005424502f7dc9ef912d8b9540658dbd0bc json (2.9.1) sha256=d2bdef4644052fad91c1785d48263756fe32fcac08b96a20bb15840e96550d11 json (2.9.1-java) sha256=88de8c79b54fee6ae1b4854bc48b8d7089f524cbacaf4596df24f86b10896ee8 language_server-protocol (3.17.0.3) sha256=3d5c58c02f44a20d972957a9febe386d7e7468ab3900ce6bd2b563dd910c6b3f launchy (2.5.2) sha256=8aa0441655aec5514008e1d04892c2de3ba57bd337afb984568da091121a241b logger (1.6.5) sha256=c3cfe56d01656490ddd103d38b8993d73d86296adebc5f58cefc9ec03741e56b mime-types (3.6.0) sha256=6f71db957840ceae44211531eff3e2f7e0dd4645fefb5f535dbaeb6307ab6464 mime-types-data (3.2025.0107) sha256=232a497cc68cfca58982685d47fb26f10ef2eb009c4d1c53a7e159d572743dca minitest (5.25.4) sha256=9cf2cae25ac4dfc90c988ebc3b917f53c054978b673273da1bd20bcb0778f947 minitest-focus (1.4.0) sha256=4cf04bd2b5fe0649922db56f9f2ae30af91475a0f51e02c2baa33f5c5a47ff6c minitest-junit (1.1.0) sha256=036c7c29ed8d852424631c7240c5b9eaa76edbadff4a8b321c12cb77869e6032 nio4r (2.7.4) sha256=d95dee68e0bb251b8ff90ac3423a511e3b784124e5db7ff5f4813a220ae73ca9 nio4r (2.7.4-java) sha256=eaa37458dc182a075ea3ad48cddd27823f2e306498acceab17062069d0f387d3 parallel (1.26.3) sha256=d86babb7a2b814be9f4b81587bf0b6ce2da7d45969fab24d8ae4bf2bb4d4c7ef parser (3.3.7.0) sha256=7449011771e3e7881297859b849de26a6f4fccd515bece9520a87e7d2116119b psych (5.2.3) sha256=84a54bb952d14604fea22d99938348814678782f58b12648fcdfa4d2fce859ee psych (5.2.3-java) sha256=3e5425b9e8a2f41cc2707d5ef14fdc1ae908abbafb12fe45727bd63900056585 public_suffix (6.0.1) sha256=61d44e1cab5cbbbe5b31068481cf16976dd0dc1b6b07bd95617ef8c5e3e00c6f puma (6.5.0) sha256=94d1b75cab7f356d52e4f1b17b9040a090889b341dbeee6ee3703f441dc189f2 puma (6.5.0-java) sha256=a58eea585d291aa33796add9884208bc1591da5d8e61886f8ac74d080b298c40 racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f racc (1.8.1-java) sha256=54f2e6d1e1b91c154013277d986f52a90e5ececbe91465d29172e49342732b98 rack (3.1.8) sha256=d3fbcbca43dc2b43c9c6d7dfbac01667ae58643c42cea10013d0da970218a1b1 rackup (2.2.1) sha256=f737191fd5c5b348b7f0a4412a3b86383f88c43e13b8217b63d4c8d90b9e798d rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a rake (13.2.1) sha256=46cb38dae65d7d74b6020a4ac9d48afed8eb8149c040eccf0523bec91907059d rdoc (6.11.0) sha256=bec66fb9b019be64f7ba7d2cd2aecb283a3a01fef23a95b33e2349c6d1aa0040 reek (6.4.0) sha256=80f9a14979aa3ffaecfb2b8b10bdf87fcd8a0fca47c36823e2a4e1e62f1ddd47 regexp_parser (2.10.0) sha256=cb6f0ddde88772cd64bff1dbbf68df66d376043fe2e66a9ef77fcb1b0c548c61 rexml (3.4.0) sha256=efbea1efba7fa151158e0ee1e643525834da2d8eb4cf744aa68f6480bc9804b2 rouge (4.5.1) sha256=2ac81c6dee7019bbc6600d4c2d641d730d65c165941400ebd924259067e690dd rubocop (1.71.0) sha256=e19679efd447346ac476122313d3788ae23c38214790bcf660e984c747608bf0 rubocop-ast (1.37.0) sha256=9513ac88aaf113d04b52912533ffe46475de1362d4aa41141b51b2455827c080 rubocop-md (1.2.4) sha256=3e481bb08e2d7479eeba3d02359737074f58dd5694f7a57de4ad8d807fdaf6ff rubocop-minitest (0.36.0) sha256=1d15850849c685ff4b6d64dd801ec2d13eb2fe56b6f7ce9aab93d1b0508e7b9f rubocop-packaging (0.5.2) sha256=a36753777573161318ab627a380510c80bfdef2ef2a5b55ad313f923efd20fc7 rubocop-performance (1.23.1) sha256=f22f86a795f5e6a6180aac2c6fc172534b173a068d6ed3396d6460523e051b82 rubocop-rake (0.6.0) sha256=56b6f22189af4b33d4f4e490a555c09f1281b02f4d48c3a61f6e8fe5f401d8db rubocop-thread_safety (0.6.0) sha256=234857694d77a20498e4aae25d87d13e6be462f27d59c40b2a277d67442baea5 ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5 simplecov-html (0.13.1) sha256=5dab0b7ee612e60e9887ad57693832fdf4695b4c0c859eaea5f95c18791ef10b simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428 stringio (3.1.2) sha256=204f1828f85cdb39d57cac4abc6dc44b04505a223f131587f2e20ae3729ba131 unicode-display_width (3.1.4) sha256=8caf2af1c0f2f07ec89ef9e18c7d88c2790e217c482bfc78aaa65eadd5415ac1 unicode-emoji (4.0.4) sha256=2c2c4ef7f353e5809497126285a50b23056cc6e61b64433764a35eff6c36532a zeitwerk (2.6.18) sha256=bd2d213996ff7b3b364cd342a585fbee9797dbc1c0c6d868dc4150cc75739781 BUNDLED WITH 2.6.3 copiousfreetime-hitimes-eccb77f/HISTORY.md000066400000000000000000000114161474462713700206430ustar00rootroot00000000000000# Hitimes Changelog ## Versino 3.1.0 - 2025-01-24 * Update the supported ruby versions to be 3.1 and up ## Version 3.0.0 - 2024-05-01 * Migrated to SemaphoreCI for doing full test runs on all active ruby versions. * Remove the dependency on `Process.clock_getres` as it is unreliable. * This has the effect of deprecating some Hitimes constants that had been documented as public. These are now removed as this is a major version update - `Hitimes::CLOCK_RESOLUTION_NANOSECONDS` - `Hitimes::CLOCK_RESOLUTION_SECONDS` - `Hitimes::INSTANT_CONVERSION_FACTOR` - `Hitimes.clock_resolution_description` - `Hitimes.clock_description` * Added Rubocop for some coding consistency * Updated the supported ruby version to be 3.0 and up * Updated all dependencies * Changed how all the `assert_delta` style tests were done so they were not so flakey * Hitimes will now emit a `warn` message if it ends up using `CLOCK_REALTIME` * Hitimes will raise an exception if it cannot find a valid clock id. This is a bug and a message to file a report is in the exception ## Version 2.0.0 2019-09-23 * Remove the C and Java extensions as `Process.clock_gettime()` has the same resolution as what the extensions did. * Update all depedencies and resolve deprecations * Now usable on truffleruby ## Version 1.3.1 2019-01-18 * Update jruby extension to not use global runtime state (thanks @kares) (#70) * Update travis CI config * Update documentataion for Ruby 2.6 ## Version 1.3.0 2018-06-15 * Add api method `Hitimes.raw_instant` to expose raw OS instant value * Add api constant `Hitimes::INSTANT_CONVERSION_FACTOR` to expose raw OS instant conversion factor * other development cleanup tasks ## Version 1.2.6 2017-08-04 * Resolve version number issue (#61) (thanks @anthraxx) ## Version 1.2.5 2017-05-25 * Update dependencies * Add ruby 2.4 to windows fatbinary * Update docs to indicate windows ruby before 2.0 is no longer supported ## Version 1.2.4 2016-05-01 * Fix finding the extension on ruby 2.1.10 (thanks @wpiekutowski) * Add more readable load error (thanks @mbautin) * Update README with what versions of ruby are supported. ## Version 1.2.3 2015-09-13 * Release new fatbinary version for windows * Update README to indicate duration units * Provide a more friendly error message if the gem is not installed correctly ## Version 1.2.2 2014-07-09 * fix compilation issue with clock_gettime in libc (reported by eradman and virtualfunction) * Switch to minispec for tests ## Version 1.2.1 2013-03-12 * Update dependencies * Ruby 2.0 fixes * Switch to Markdown, Yeah RDoc 4.0! ## Version 1.2.0 2013-02-09 * Update dependencies * Documentation cleanup * Fix use of deprecated JRuby API in java extension * Fix use of deprecated OSX system calls in C extension * Make hitimes -w clean * Fix ambiguity of calling duration on non-started Interval * Use RbConfig instead of Config (eregon) * Added Hitimes.measure * Switch to using rake-compiler for cross compilation of gems ## Version 1.1.1 2010-09-04 * Remove the unnecessary dependencies that should be development dependencies ## Version 1.1.0 2010-07-28 * Add a pure java extension so hitimes may be used in jruby with the same API ## Version 1.0.5 2010-07-20 * Fix 'circular require considered harmful' warnings in 1.9.x (reported by Roger Pack) * Fix 'method redefined' warnings in 1.9.x (reported by Roger Pack) ## Version 1.0.4 2009-08-01 * Add in support for x86-mingw32 gem * Add version subdirectory for extension on all platforms ## Version 1.0.3 2009-06-28 * Fix bug with time.h on linode (reported by Roger Pack) * Fix potential garbage collection issue with Interval class * Windows gem is now a fat binary to support installing in 1.8 or 1.9 from the same gem ## Version 1.0.1 2009-06-12 * Fix examples * performance tuning, new Hitimes::Metric derived classes are faster than old Timer class ## Version 1.0.0 2009-06-12 * Major version bump with complete refactor of the metric collection API * 3 types of metrics now instead of just 1 Timer * Hitimes::ValueMetric * Hitimes::TimedMetric * Hitimes::TimedValueMetric * The ability to convert all metrics #to_hash * Updated documentation with examples using each metric type ## Version 0.4.1 2009-02-19 * change to ISC License * fix bug in compilation on gentoo ## Version 0.4.0 2008-12-20 * Added new stat 'rate' * Added new stat method to_hash * Added Hitimes::MutexedStats class for threadsafe stats collection - not needed when used in MRI 1.8.x * remove stale dependency on mkrf ## Version 0.3.0 * switched to extconf for building extensions * first release of windows binary gem * reverted back to normal rdoc ## Version 0.2.1 * added Timer#rate method * switched to darkfish rdoc ## Version 0.2.0 * Performance improvements * Added Hitimes::Stats class ## Version 0.1.0 * Initial completion copiousfreetime-hitimes-eccb77f/LICENSE.txt000066400000000000000000000014441474462713700210030ustar00rootroot00000000000000ISC LICENSE - http://opensource.org/licenses/isc-license.txt Copyright (c) 2008-2015 Jeremy Hinegardner Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. copiousfreetime-hitimes-eccb77f/Manifest.txt000066400000000000000000000005641474462713700214710ustar00rootroot00000000000000CONTRIBUTING.md HISTORY.md LICENSE.txt Manifest.txt README.md hitimes.gemspec lib/hitimes.rb lib/hitimes/initialize.rb lib/hitimes/instant.rb lib/hitimes/interval.rb lib/hitimes/metric.rb lib/hitimes/mutexed_stats.rb lib/hitimes/paths.rb lib/hitimes/stats.rb lib/hitimes/timed_metric.rb lib/hitimes/timed_value_metric.rb lib/hitimes/value_metric.rb lib/hitimes/version.rb copiousfreetime-hitimes-eccb77f/README.md000066400000000000000000000112551474462713700204400ustar00rootroot00000000000000# Hitimes [![Build Status](https://copiousfreetime.semaphoreci.com/badges/hitimes/branches/main.svg)](https://copiousfreetime.semaphoreci.com/projects/hitimes) * [Homepage](http://github.com/copiousfreetime/hitimes) * [Github project](http://github.com/copiousfreetime/hitimes) ## DESCRIPTION A fast, high resolution timer library for recording performance metrics. ## TABLE OF CONTENTS * [Requirements](#requirements) * [Usage](#usage) * [Contributing](#contributing) * [Support](#support) * [License](#license) ## REQUIREMENTS Hitimes requires the following to run: * Ruby ## USAGE Hitimes easiest to use when installed with `rubygems`: ```sh gem install hitimes ``` Or as part of your bundler `Gemfile`: ```ruby gem "hitimes" ``` You can load it with the standard ruby require statement. ```ruby require "hitimes" ``` ### Interval Use `Hitimes::Interval` to calculate only the duration of a block of code. Returns the time as seconds. ```ruby duration = Hitimes::Interval.measure do 1_000_000.times do |x| 2 + 2 end end puts duration # => 0.047414297 (seconds) ``` ### TimedMetric Use a `Hitimes::TimedMetric` to calculate statistics about an iterative operation ```ruby timed_metric = Hitimes::TimedMetric.new("operation on items") ``` Explicitly use `start` and `stop`: ```ruby collection.each do |item| timed_metric.start # .. do something with item timed_metric.stop end ``` Or use the block. In `TimedMetric` the return value of `measure` is the return value of the block. ```ruby collection.each do |item| result_of_do_something = timed_metric.measure { do_something(item) } # do something with result_of_do_something end ``` And then look at the stats ```ruby puts timed_metric.mean puts timed_metric.max puts timed_metric.min puts timed_metric.stddev puts timed_metric.rate ``` ### ValueMetric Use a `Hitimes::ValueMetric` to calculate statistics about measured samples. ``` ruby value_metric = Hitimes::ValueMetric.new("size of thing") loop do # ... do stuff changing sizes of 'thing' value_metric.measure(thing.size) # ... do other stuff that may change size of thing end puts value_metric.mean puts value_metric.max puts value_metric.min puts value_metric.stddev puts value_metric.rate ``` ### TimedValueMetric Use a `Hitimes::TimedValueMetric` to calculate statistics about batches of samples. ``` ruby timed_value_metric = Hitimes::TimedValueMetric.new("batch times") loop do batch = ... # get a batch of things timed_value_metric.start # .. do something with batch timed_value_metric.stop(batch.size) end puts timed_value_metric.rate puts timed_value_metric.timed_stats.mean puts timed_value_metric.timed_stats.max puts timed_value_metric.timed_stats.min puts timed_value_metric.timed_stats.stddev puts timed_value_metric.value_stats.mean puts timed_value_metric.value_stats.max puts timed_value_metric.value_stats.min puts timed_value_metric.value_stats.stddev ``` ### Implementation details Hitimes uses the internal ruby `Process::clock_gettime()` to get the highest granularity time increment possible. Generally this is nanosecond resolution, or whatever the hardware in the CPU supports. ## SUPPORT Hitimes is supported on whatever versions of ruby are currently supported. Hitimes also follows [semantic versioning](http://semver.org/). The current officially supported versions of Ruby are: * MRI Ruby (all platforms) 3.0 - current * JRuby 9.4.x.x * Truffleruby 24 Unofficially supported versions, any version of MRI from Ruby 2.1 and up. Since the C Extension has been removed Hitimes should work with any ruby that is 2.1 or greater as that is when `Process.clock_gettime()` was implemented. For versions of Ruby before 2.1 please use Hitimes 1.3, the extension code is still in there and they should still work. ## CONTRIBUTING Please read [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on development and bug reporting. ## Credits * [Bruce Williams](https://github.com/bruce) for suggesting the idea. * [Benoit Daloze](https://github.com/eregon) and [Thomas Hurst](https://github.com/Freaky) for conversations around clock_ids. ## License Hitimes is licensed under the [ISC](https://opensource.org/licenses/ISC) license. ## Related Works * [monotime](https://github.com/Freaky/monotime) - A sensible interface to Ruby's monotonic clock. * [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) - [Concurrent.monotonic_time](https://github.com/ruby-concurrency/concurrent-ruby) is a straight pass through to `Process.clock_gettime(Process::CLOCK_MONOTONIC,...)`. * [Instant](https://doc.rust-lang.org/src/std/time.rs.html) - The rust equivalent. * [time.Now](https://pkg.go.dev/time) - The go monotonic time interface is part of this package. copiousfreetime-hitimes-eccb77f/Rakefile000066400000000000000000000012521474462713700206220ustar00rootroot00000000000000# frozen_string_literal: true # vim: syntax=ruby load "tasks/this.rb" This.name = "hitimes" This.author = "Jeremy Hinegardner" This.email = "jeremy@copiousfreetime.org" This.homepage = "http://github.com/copiousfreetime/#{This.name}" This.ruby_gemspec do |spec| spec.metadata = { "bug_tracker_uri" => "https://github.com/copiousfreetime/#{This.name}/issues", "changelog_uri" => "https://github.com/copiousfreetime/#{This.name}/blob/master/HISTORY.md", "homepage_uri" => "https://github.com/copiousfreetime/#{This.name}", "source_code_uri" => "https://github.com/copiousfreetime/#{This.name}", } spec.license = "ISC" end load "tasks/default.rake" copiousfreetime-hitimes-eccb77f/bin/000077500000000000000000000000001474462713700177255ustar00rootroot00000000000000copiousfreetime-hitimes-eccb77f/bin/setup000077500000000000000000000003701474462713700210130ustar00rootroot00000000000000#!/usr/bin/env ruby # frozen_string_literal: true require "pathname" require "fileutils" PROJECT_ROOT = Pathname.new(__dir__).parent.expand_path FileUtils.chdir(PROJECT_ROOT) do puts "Installing dependencies..." system("bundle install") end copiousfreetime-hitimes-eccb77f/examples/000077500000000000000000000000001474462713700207735ustar00rootroot00000000000000copiousfreetime-hitimes-eccb77f/examples/benchmarks.rb000066400000000000000000000053521474462713700234420ustar00rootroot00000000000000require "benchmark" require "time" # # this is all here in case this example is run from the examples directory # begin require "hitimes" rescue LoadError => e ext_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "ext")) lib_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")) raise e if $:.include?(ext_path) $: << ext_path $: << lib_path retry end #---------------------------------------------------------------------- # test program to look at the performance sampling time durations using # different methods #---------------------------------------------------------------------- include Benchmark # # Normal apprach to Interval usage # def hitimes_duration_i1 i = Hitimes::Interval.new i.start i.stop end # # Use the easy access method to start stop an interval # def hitimes_duration_i2 Hitimes::Interval.now.stop end # # Use a new timer each time # def hitimes_duration_t1 Hitimes::TimedMetric.now("duration_t1").stop end # # reuse the same timer over and over # HT2 = Hitimes::TimedMetric.new("duration_t2") def hitimes_duration_t2 HT2.start HT2.stop end HT3 = Hitimes::TimedMetric.new("duration_t3") def hitimes_duration_t3 HT3.measure { nil } end # # Check out the speed of the TimedValueMetric too # def hitimes_duration_tv1 Hitimes::TimedValueMetric.now("duration_tv1").stop(42) end HTV2 = Hitimes::TimedValueMetric.new("duration_tv2") def hitimes_duration_tv2 HTV2.start HTV2.stop(42) end HTV3 = Hitimes::TimedValueMetric.new("duration_tv3") def hitimes_duration_tv3 HTV3.measure(42) { nil } end # # use the Struct::Tms values and return the difference in User time between 2 # successive calls # def process_duration t1 = Process.times.utime Process.times.utime - t1 end # # Take 2 times and subtract one from the other # def time_duration t1 = Time.now.to_f Time.now.to_f - t1 end puts "Testing time sampling 100,000 times" bm(30) do |x| x.report("Process") { 100_000.times { process_duration } } x.report("Time") { 100_000.times { time_duration } } x.report("Hitimes::TimedMetric 1") { 100_000.times { hitimes_duration_t1 } } x.report("Hitimes::TimedMetric 2") { 100_000.times { hitimes_duration_t2 } } x.report("Hitimes::TimedMetric 3") { 100_000.times { hitimes_duration_t3 } } x.report("Hitimes::Interval 1") { 100_000.times { hitimes_duration_i1 } } x.report("Hitimes::Interval 2") { 100_000.times { hitimes_duration_i2 } } x.report("Hitimes::TimedValueMetric 1") { 100_000.times { hitimes_duration_tv1 } } x.report("Hitimes::TimedValueMetric 2") { 100_000.times { hitimes_duration_tv2 } } x.report("Hitimes::TimedValueMetric 3") { 100_000.times { hitimes_duration_tv3 } } end copiousfreetime-hitimes-eccb77f/examples/clock_ids.rb000066400000000000000000000021321474462713700232500ustar00rootroot00000000000000def potential_clock_ids [].tap do |clock_ids| %i[ CLOCK_MONOTONIC_RAW CLOCK_UPTIME_RAW CLOCK_UPTIME CLOCK_MONOTONIC CLOCK_MONOTONIC_FAST CLOCK_BOOTTIME CLOCK_REALTIME ].each do |c| clock_ids << { name: c, value: Process.const_get(c)} if Process.const_defined?(c) end end end clock_ids = potential_clock_ids puts "Using the following clock ids: #{clock_ids.join(', ')}" def resolutions_of(clock_id) counts = Hash.new(0) 1_000_000.times do val = Process.clock_gettime(clock_id, :nanosecond) res = if (val % 1_000_000_000).zero? 1 elsif (val % 1_000_000).zero? 1e-3 elsif (val % 1_000).zero? 1e-6 else 1e-9 end counts[res] += 1 end counts end data = { platform: RUBY_PLATFORM, clock_ids: {} } clock_ids.each do |clock_info| name = clock_info[:name] clock_id = clock_info[:value] resolutions = resolutions_of(clock_id) data[:clock_ids][name] = resolutions.transform_keys(&:to_s) end require 'yaml' puts YAML.dump(data) copiousfreetime-hitimes-eccb77f/examples/results.yml000066400000000000000000000027101474462713700232170ustar00rootroot00000000000000:platform: x86_64-linux :clock_ids: :CLOCK_MONOTONIC_RAW: '1.0e-09': 999016 '1.0e-06': 984 :CLOCK_MONOTONIC: '1.0e-09': 999070 '1.0e-06': 929 '0.001': 1 :CLOCK_BOOTTIME: '1.0e-09': 999046 '1.0e-06': 954 :CLOCK_REALTIME: '1.0e-09': 998980 '1.0e-06': 1019 '0.001': 1 --- :platform: arm64-darwin23 :clock_ids: :CLOCK_MONOTONIC_RAW: '1.0e-09': 960487 '1.0e-06': 39469 '0.001': 44 :CLOCK_UPTIME_RAW: '1.0e-09': 961327 '1.0e-06': 38632 '0.001': 41 :CLOCK_MONOTONIC: '1.0e-06': 998928 '0.001': 1072 :CLOCK_REALTIME: '1.0e-06': 998963 '0.001': 1037 --- :platform: x86_64-darwin21 :clock_ids: :CLOCK_MONOTONIC_RAW: '1.0e-09': 999526 '1.0e-06': 473 '0.001': 1 :CLOCK_UPTIME_RAW: '1.0e-09': 999020 '1.0e-06': 978 '0.001': 2 :CLOCK_MONOTONIC: '1.0e-06': 998998 '0.001': 1000 '1': 2 :CLOCK_REALTIME: '1.0e-06': 998986 '0.001': 1014 --- :platform: x86_64-darwin21 :clock_ids: :CLOCK_MONOTONIC_RAW: '1.0e-09': 999048 '1.0e-06': 952 :CLOCK_UPTIME_RAW: '1.0e-09': 999371 '1.0e-06': 629 :CLOCK_MONOTONIC: '1.0e-06': 998999 '0.001': 1001 :CLOCK_REALTIME: '1.0e-06': 998998 '0.001': 1002 --- :platform: x86_64-openbsd :clock_ids: :CLOCK_MONOTONIC: 1.0e-09: 999023 1.0e-06: 977 :CLOCK_UPTIME: 1.0e-09: 998966 1.0e-06: 1034 :CLOCK_REALTIME: 1.0e-09: 998966 1.0e-06: 1032 0.001: 2 copiousfreetime-hitimes-eccb77f/examples/stats.rb000066400000000000000000000010321474462713700224520ustar00rootroot00000000000000# # this is all here in case this example is run from the examples directory # begin require "hitimes" rescue LoadError => e %w[ext lib].each do |p| path = File.expand_path(File.join(File.dirname(__FILE__), "..", p)) raise e if $:.include?(path) $: << path end retry end s = Hitimes::Stats.new dir = ARGV.shift || Dir.pwd Dir.entries(dir).each do |entry| fs = File.stat(entry) s.update(fs.size) if fs.file? end Hitimes::Stats::STATS.each do |m| puts "#{m.rjust(6)} : #{s.send(m)}" end puts s.to_hash.inspect copiousfreetime-hitimes-eccb77f/hitimes.gemspec000066400000000000000000000040461474462713700221700ustar00rootroot00000000000000# DO NOT EDIT - This file is automatically generated # Make changes to Manifest.txt and/or Rakefile and regenerate # -*- encoding: utf-8 -*- # stub: hitimes 3.1.0 ruby lib Gem::Specification.new do |s| s.name = "hitimes".freeze s.version = "3.1.0".freeze s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "bug_tracker_uri" => "https://github.com/copiousfreetime/hitimes/issues", "changelog_uri" => "https://github.com/copiousfreetime/hitimes/blob/master/HISTORY.md", "homepage_uri" => "https://github.com/copiousfreetime/hitimes", "source_code_uri" => "https://github.com/copiousfreetime/hitimes" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Jeremy Hinegardner".freeze] s.bindir = "exe".freeze s.date = "2025-01-24" s.description = "A fast, high resolution timer library for recording performance metrics.".freeze s.email = "jeremy@copiousfreetime.org".freeze s.extra_rdoc_files = ["CONTRIBUTING.md".freeze, "HISTORY.md".freeze, "LICENSE.txt".freeze, "Manifest.txt".freeze, "README.md".freeze] s.files = ["CONTRIBUTING.md".freeze, "HISTORY.md".freeze, "LICENSE.txt".freeze, "Manifest.txt".freeze, "README.md".freeze, "hitimes.gemspec".freeze, "lib/hitimes.rb".freeze, "lib/hitimes/initialize.rb".freeze, "lib/hitimes/instant.rb".freeze, "lib/hitimes/interval.rb".freeze, "lib/hitimes/metric.rb".freeze, "lib/hitimes/mutexed_stats.rb".freeze, "lib/hitimes/paths.rb".freeze, "lib/hitimes/stats.rb".freeze, "lib/hitimes/timed_metric.rb".freeze, "lib/hitimes/timed_value_metric.rb".freeze, "lib/hitimes/value_metric.rb".freeze, "lib/hitimes/version.rb".freeze] s.homepage = "http://github.com/copiousfreetime/hitimes".freeze s.licenses = ["ISC".freeze] s.rdoc_options = ["--main".freeze, "README.md".freeze, "--markup".freeze, "tomdoc".freeze] s.required_ruby_version = Gem::Requirement.new(">= 3.0.0".freeze) s.rubygems_version = "3.6.3".freeze s.summary = "A fast, high resolution timer library for recording performance metrics.".freeze end copiousfreetime-hitimes-eccb77f/lib/000077500000000000000000000000001474462713700177235ustar00rootroot00000000000000copiousfreetime-hitimes-eccb77f/lib/hitimes.rb000066400000000000000000000015141474462713700217130ustar00rootroot00000000000000# frozen_string_literal: true #-- # Copyright (c) 2008 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ # # The top level module containing the contents of the hitimes library # # use the library with: # # require 'hitimes' # module Hitimes # # Base class of all errors in Hitimes # class Error < ::StandardError; end # Hitimes.measure { } -> Float # # Times the execution of the block, returning the number of seconds it took def self.measure(&block) Hitimes::Interval.measure(&block) end end require "hitimes/paths" require "hitimes/version" require "hitimes/instant" require "hitimes/interval" require "hitimes/stats" require "hitimes/mutexed_stats" require "hitimes/metric" require "hitimes/value_metric" require "hitimes/timed_metric" require "hitimes/timed_value_metric" copiousfreetime-hitimes-eccb77f/lib/hitimes/000077500000000000000000000000001474462713700213655ustar00rootroot00000000000000copiousfreetime-hitimes-eccb77f/lib/hitimes/initialize.rb000066400000000000000000000102341474462713700240530ustar00rootroot00000000000000# frozen_string_literal: true module Hitimes # Internal: Internal setup that is done when the library is loaded # # We want to determine what clock to use for this machine. So we need to # introspect the ruby environment and then setup some initial constants. These # methods are used in lib/hitimes/instant.rb to help setup the CLOCK_ID # constant at load time. # module Initialize # # After a fair bit of experimentaiton, it seems that the only clock_ids that # are of any use are the following: # # POSIX: # # CLOCK_REALTIME A settable system-wide real-time clock. Measures # wall-clock time. Affected by system jumps in time, # adjtime(3), and NTP. # # CLOCK_MONOTONIC A nonsettable system-wide clock that represent # monotomic time since some unspecified point in the # past. Not affected by jumps in system time, but is # affectd by adjtime(3) and NTP. # # Darwin: # # CLOCK_MONOTONIC_RAW clock that increments monotonically, tracking the # time since an arbitrary point like CLOCK_MONOTONIC. # However, this clock is unaffected by frequency or # time adjustments. # # CLOCK_UPTIME_RAW clock that increments monotonically, in the same manner # as CLOCK_MONOTONIC_RAW, but that does not increment # while the system is asleep. The returned value is # identical to the result of mach_absolute_time() # after the appropriate mach_timebase conversion is applied. # # Linux: # # CLOCK_MONOTONIC_RAW Similar to CLOCK_MONOTONIC, but provides access to # a raw hardware-based time that is not subject to NTP # adjustments or the incremental adjustments performed # by adjtime(3) # # CLOCK_BOOTTIME Identical to CLOCK_MONOTONIC, except it also includes any # time that the system is suspended. # # *BSD: # # All the BSDs seem to have CLOCK_MONOTONIC and CLOCK_REALTIME although on # NetBSD CLOCK_MONOTONIC is not affected by adjtime(2). It is unclear if # they are affected by adjtime(2) on FreeBSD, OpenBSD, or DragonFlyBSD. - # at least according to the man pages. # # What this boils down to as that pretty much all systems have CLOCK_REALTIME # and CLOCK_MONOTONIC. The other clocks are system specific and may or may # not exist. We'll try to use the most accurate clock available. # # So we'll try to use the following clocks in order of preference: # # On Linux and Darwin # CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC, CLOCK_REALTIME # # Everyone else: # CLOCK_MONOTONIC, CLOCK_REALTIME # # So in reality, well just test for constants on all of the above and use the # first one that exists. # # If CLOCK_REALTIME is chose, we will dump a warning to the user. # And if we can't finde one, which is really, really odd, we'll raise an exception. POTENTIAL_CLOCK_IDS = %i[CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC CLOCK_REALTIME].freeze def determine_clock_id(potential_ids = POTENTIAL_CLOCK_IDS) sym = potential_ids.find { |id| Process.const_defined?(id) } unless sym raise Hitimes::Error, <<~ERROR Unable to find a high resolution clock at all. THIS IS A BUG!! RUBY_DESCRIPTION: #{RUBY_DESCRIPTION} Please report this bug to the hitimes issue tracker at https://github.com/copiousfreetime/hitimes/issues ERROR end if sym == :CLOCK_REALTIME warn <<~TXT Unable to find a high resolution clock. Using CLOCK_REALTIME for timing. RUBY_DESCRIPTION: #{RUBY_DESCRIPTION} Please report the above information to the hitimes issue tracker at https://github.com/copiousfreetime/hitimes/issues TXT end Process.const_get(sym) end module_function :determine_clock_id end end copiousfreetime-hitimes-eccb77f/lib/hitimes/instant.rb000066400000000000000000000016751474462713700234030ustar00rootroot00000000000000# frozen_string_literal: true require "hitimes/initialize" # Hitimes Constants and module methods # module Hitimes # Public: The clock_id to use in Process.clock_gettime CLOCK_ID = Initialize.determine_clock_id.freeze # Internal: The fraction of second of a nanosecond NANOSECONDS_PER_SECOND = 1e9 # Public: Get the raw instant # # Examples: # # Hitimes.raw_instant # # Returns the raw instant value def raw_instant Process.clock_gettime(::Hitimes::CLOCK_ID, :nanosecond) end module_function :raw_instant # Internal: The human readable clock name of the CLOCK_ID as a string # # Returns the clock name as a String def clock_name case CLOCK_ID when Symbol CLOCK_ID.to_s else const = Process.constants.grep(/CLOCK/).find do |id| Process.const_get(id) == CLOCK_ID end "Process::#{const}" end end module_function :clock_name end copiousfreetime-hitimes-eccb77f/lib/hitimes/interval.rb000066400000000000000000000114251474462713700235410ustar00rootroot00000000000000# frozen_string_literal: true # Copyright (c) 2008 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. # module Hitimes # This is the lowest level timing mechanism available. It allows for easy # measuring based upon a block: # # duration = Interval.measure { ... } # # Or measuring something specifically # # interval = Interval.new # interval.start # duration = interval.stop # # Allocating and starting an interval can be done in one method call with # # interval = Interval.now # # Interval is useful when you only need to track a single interval of time, or # if you do not want to track statistics about an operation. class Interval # Public: The integer representing the start instant of the Interval. This # valuea is not useful on its own. It is a platform dependent value. attr_reader :start_instant # Public: The integer representing the stop instant of the Interval. This # value is not useful on its own. It is a platform dependent value. attr_reader :stop_instant def initialize(start = nil, stop = nil) @start_instant = start @stop_instant = stop @duration = -Float::INFINITY end # call-seq: # Interval.now -> Interval # # Create an interval that has already started # def self.now Interval.new(Hitimes.raw_instant) end # call-seq: # Interval.measure { } -> Float # # Times the execution of the block returning the number of seconds it took def self.measure raise Error, "No block given to Interval.measure" unless block_given? interval = Interval.now yield interval.stop interval.duration end # call-seq: # interval.split -> Interval # # Immediately stop the current interval and start a new interval that has a # start_instant equivalent to the stop_interval of self. def split @stop_instant = ::Hitimes.raw_instant Interval.new(@stop_instant) end # call-seq: # interval.start -> boolean # # mark the start of the interval. Calling start on an already started # interval has no effect. An interval can only be started once. If the # interval is truely started +true+ is returned otherwise +false+. def start return false if started? @start_instant = ::Hitimes.raw_instant true end # call-seq: # interval.stop -> bool or Float # # mark the stop of the interval. Calling stop on an already stopped interval # has no effect. An interval can only be stopped once. If the interval is # truely stopped then the duration is returned, otherwise +false+. def stop raise Error, "Attempt to stop an interval that has not started" unless started? return false if stopped? @stop_instant = ::Hitimes.raw_instant duration end # call-seq: # interval.duration_so_far -> Float or false # # return how the duration so far. This will return the duration from the time # the Interval was started if the interval is running, otherwise it will return # false. def duration_so_far return false unless running? raw = Hitimes.raw_instant calculate_duration(@start_instant, raw) end # call-seq: # interval.started? -> boolean # # returns whether or not the interval has been started def started? !!@start_instant end # call-seq: # interval.stopped? -> boolean # # returns whether or not the interval has been stopped def stopped? !!@stop_instant end # call-seq: # interval.running? -> boolean # # returns whether or not the interval is running or not. This means that it # has started, but not stopped. # def running? started? && !stopped? end # call-seq: # interval.duration -> Float # interval.to_f -> Float # interval.to_seconds -> Float # interval.length -> Float # # Returns the Float value of the interval, the value is in seconds. If the # interval has not had stop called yet, it will report the number of seconds # in the interval up to the current point in time. # # Raises Error if duration is called on an interval that has not started yet. # def duration raise Error, "Attempt to report a duration on an interval that has not started" unless started? return duration_so_far unless stopped? @duration = calculate_duration(@start_instant, @stop_instant) if @duration.negative? @duration end alias to_f duration alias to_seconds duration alias length duration private def calculate_duration(start, stop) (stop - start) / ::Hitimes::NANOSECONDS_PER_SECOND end end end copiousfreetime-hitimes-eccb77f/lib/hitimes/metric.rb000066400000000000000000000063541474462713700232050ustar00rootroot00000000000000# frozen_string_literal: true #-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ module Hitimes # # Metric hold the common meta information for all derived metric classes # # All metrics hold the meta information of: # # * The name of the metric # * The time of day the first measurement is taken # * The time of day the last measurement is taken # * additional data # # Each derived class is assumed to set the sampling_start_time and # sampling_stop_time appropriately. # # Metric itself should generally not be used. Only use the derived classes. # class Metric # the number of seconds as a float since the sampling_start_time attr_reader :sampling_delta # An additional hash of data to associate with the metric attr_reader :additional_data # The 'name' to associate with the metric attr_reader :name # # :call-seq: # Metric.new( 'my_metric' ) -> Metric # Metric.new( 'my_metric', 'foo' => 'bar', 'this' => 42 ) -> Metric # # Create a new ValueMetric giving it a name and additional data. # # +additional_data+ may be anything that follows the +to_hash+ protocol. # +name+ may be anything that follows the +to_s+ protocol. # def initialize(name, additional_data = {}) @sampling_start_time = nil @sampling_start_interval = nil @sampling_delta = 0 @name = name.to_s @additional_data = additional_data.to_hash end # # :call-seq: # metric.sampling_start_time -> Float or nil # # The time at which the first sample was taken. # This is the number of microseconds since UNIX epoch UTC as a Float # # If the metric has not started measuring then the start time is nil. # def sampling_start_time return unless @sampling_start_interval @sampling_start_time ||= utc_microseconds end # # :call-seq: # metric.sampling_stop_time -> Float or nil # # The time at which the last sample was taken # This is the number of microseconds since UNIX epoch UTC as a Float # # If the metric has not completely measured at least one thing then # stop time is nil. # # Because accessing the actual 'time of day' is an expesive operation, we # only get the time of day at the beginning of the first measurement and we # keep track of the offset from that point in @sampling_delta. # # When sampling_stop_time is called, the actual time of day is caculated. # def sampling_stop_time return unless @sampling_delta.positive? (sampling_start_time + (@sampling_delta * 1_000_000)) end # # :call-seq: # metric.to_hash -> Hash # metric.to_hash # # Convert the metric to a Hash. # def to_hash { "sampling_start_time" => sampling_start_time, "sampling_stop_time" => sampling_stop_time, "additional_data" => additional_data, "name" => name, } end # # :call-seq: # metric.utc_microseconds -> Float # # The current time in microseconds from the UNIX Epoch in the UTC # def utc_microseconds Time.now.gmtime.to_f * 1_000_000 end end end copiousfreetime-hitimes-eccb77f/lib/hitimes/mutexed_stats.rb000066400000000000000000000005641474462713700246100ustar00rootroot00000000000000# frozen_string_literal: true #-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ module Hitimes # # MutexedStats is the start of a threadsafe Stats class. Currently, on MRI # Ruby the Stats object is already threadsafe, so there is no need to use # MutexedStats. # MutexedStats = Stats end copiousfreetime-hitimes-eccb77f/lib/hitimes/paths.rb000066400000000000000000000030751474462713700230360ustar00rootroot00000000000000# frozen_string_literal: true #-- # Copyright (c) 2008 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ # module Hitimes # # Access to various paths inside the project programatically # module Paths # # :call-seq: # Hitimes::Paths.root_dir -> String # # Returns The full expanded path of the parent directory of +lib+ # going up the path from the current file. A trailing File::SEPARATOR # is guaranteed. # def self.root_dir @root_dir ||= begin path_parts = ::File.expand_path(__FILE__).split(::File::SEPARATOR) lib_index = path_parts.rindex("lib") @root_dir = path_parts[0...lib_index].join(::File::SEPARATOR) + ::File::SEPARATOR end end # # :call-seq: # Hitimes::Paths.lib_path( *args ) -> String # # Returns The full expanded path of the +lib+ directory below # _root_dir_. All parameters passed in are joined onto the # result. A trailing File::SEPARATOR is guaranteed if # _args_ are *not* present. # def self.lib_path(*args) sub_path("lib", *args) end # # :call-seq: # Hitimes::Paths.sub_path( sub, *args ) -> String # # Returns the full expanded path of the +sub+ directory below _root_dir. All # _arg_ parameters passed in are joined onto the result. A trailing # File::SEPARATOR is guaranteed if _args_ are *not* present. # def self.sub_path(sub, *args) sp = ::File.join(root_dir, sub) + File::SEPARATOR ::File.join(sp, *args) if args end end end copiousfreetime-hitimes-eccb77f/lib/hitimes/stats.rb000066400000000000000000000104351474462713700230530ustar00rootroot00000000000000# frozen_string_literal: true #-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ require "stringio" module Hitimes # # The Stats class encapulsates capturing and reporting statistics. It is # modeled after the RFuzz::Sampler class, but implemented in C. For general use # you allocate a new Stats object, and then update it with new values. The # Stats object will keep track of the _min_, _max_, _count_, _sum_ and _sumsq_ # and when you want you may also retrieve the _mean_, _stddev_ and _rate_. # # this contrived example shows getting a list of all the files in a directory # and running stats on file sizes. # # s = Hitimes::Stats.new # dir = ARGV.shift || Dir.pwd # Dir.entries( dir ).each do |entry| # fs = File.stat( entry ) # if fs.file? then # s.update( fs.size ) # end # end # # %w[ count min max mean sum stddev rate ].each do |m| # puts "#{m.rjust(6)} : #{s.send( m ) }" # end # class Stats # A list of the available stats STATS = %w[count max mean min rate stddev sum sumsq].freeze attr_reader :min, :max, :count, :sum, :sumsq def initialize @mutex = Mutex.new @min = Float::INFINITY @max = -Float::INFINITY @count = 0 @sum = 0.0 @sumsq = 0.0 end # call-seq: # stat.update( val ) -> val # # Update the running stats with the new value. # Return the input value. def update(value) @mutex.synchronize do @min = (value < @min) ? value : @min @max = (value > @max) ? value : @max @count += 1 @sum += value @sumsq += (value * value) end value end # call-seq: # stat.mean -> Float # # Return the arithmetic mean of the values put into the Stats object. If no # values have passed through the stats object then 0.0 is returned; def mean return 0.0 if @count.zero? @sum / @count end # call-seq: # stat.rate -> Float # # Return the +count+ divided by +sum+. # # In many cases when Stats#update( _value_ ) is called, the _value_ is a unit # of time, typically seconds or microseconds. #rate is a convenience for those # times. In this case, where _value_ is a unit if time, then count divided by # sum is a useful value, i.e. +something per unit of time+. # # In the case where _value_ is a non-time related value, then the value # returned by _rate_ is not really useful. # def rate return 0.0 if @sum.zero? @count / @sum end # # call-seq: # stat.stddev -> Float # # Return the standard deviation of all the values that have passed through the # Stats object. The standard deviation has no meaning unless the count is > 1, # therefore if the current _stat.count_ is < 1 then 0.0 will be returned; # def stddev return 0.0 unless @count > 1 Math.sqrt((@sumsq - ((@sum * @sum) / @count)) / (@count - 1)) end # # call-seq: # stat.to_hash -> Hash # stat.to_hash( %w[ count max mean ]) -> Hash # # return a hash of the stats. By default this returns a hash of all stats # but passing in an array of items will limit the stats returned to only # those in the Array. # # If passed in an empty array or nil to to_hash then STATS is assumed to be # the list of stats to return in the hash. # def to_hash(*args) result = {} fields = [args].flatten fields = STATS if fields.empty? fields.each do |meth| result[meth] = send(meth) end result end # # call-seq: # stat.to_json -> String # stat.to_json( *args ) -> String # # return a json string of the stats. By default this returns a json string # of all the stats. If an array of items is passed in, those that match the # known stats will be all that is included in the json output. # def to_json(*args) stats = to_hash(*args) slugs = [] out = StringIO.new out.print "{ " stats.each_pair do |key, val| slugs << "\"#{key}\": #{val}" end out.print slugs.join(", ") out.print "}" out.string end end end copiousfreetime-hitimes-eccb77f/lib/hitimes/timed_metric.rb000066400000000000000000000111071474462713700243570ustar00rootroot00000000000000# frozen_string_literal: true #-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ require "forwardable" module Hitimes # # A TimedMetric holds the metrics on how long it takes to do something. For # example, measuring how long a method takes to operate. # # tm = TimedMetric.new( 'my-method' ) # # 200.times do # my_method_result = tm.measure do # my_method( ... ) # end # end # # puts "#{ tm.name } operated at a rate of #{ tm.rate } calls per second" # # Since TimedMetric is a child class of Metric make sure to look at the # Metric API also. # # A TimedMetric measures the execution time of an option with the Interval # class. # # A TimedMetric contains a Stats object, therefore TimedMetric has +count+, +max+, # +mean+, +min+, +rate+, +stddev+, +sum+, +sumsq+ methods that delegate to that Stats # object for convenience. # # class TimedMetric < Metric # holds all the statistics attr_reader :stats class << TimedMetric # # :call-seq: # TimedMetric.now -> TimedMetric # # Return a TimedMetric that has been started # def now(name, additional_data = {}) tm = TimedMetric.new(name, additional_data) tm.start tm end end # # :call-seq: # TimedMetric.new( 'name') -> TimedMetric # TimedMetric.new( 'name', 'other' => 'data') -> TimedMetric # # Create a new TimedMetric giving it a name and additional data. # +additional_data+ may be anything that follows the +to_hash+ protocol # def initialize(name, additional_data = {}) super @stats = Stats.new @current_interval = Interval.new end # # :call-seq: # timed_metric.running? -> true or false # # return whether or not the timer is currently running. # def running? @current_interval.running? end # # :call-seq: # timed_metric.start -> nil # # Start the current metric, if the current metric is already started, then # this is a noop. # def start unless @current_interval.running? @current_interval.start @sampling_start_time ||= utc_microseconds @sampling_start_interval ||= Interval.now end nil end # # :call-seq: # timed_metric.stop -> Float or nil # # Stop the current metric. This updates the stats and removes the current # interval. If the timer was stopped then the duration of the last Interval # is returned. If the timer was already stopped then false is returned and # no stats are updated. # def stop if @current_interval.running? duration = @current_interval.stop @stats.update(duration) @current_interval = Interval.new # update the length of time we have been sampling @sampling_delta = @sampling_start_interval.duration_so_far return duration end false end # # :call-seq: # timed_metric.measure { ... } -> Object # # Measure the execution of a block and add those stats to the running stats. # The return value is the return value of the block # def measure return_value = nil begin start return_value = yield ensure stop end return_value end # # :call-seq: # timed_metric.split -> Float # # Split the current TimedMetric. Essentially, mark a split time. This means # stop the current interval and create a new interval, but make sure # that the new interval lines up exactly, timewise, behind the previous # interval. # # If the timer is running, then split returns the duration of the previous # interval, i.e. the split-time. If the timer is not running, nothing # happens and false is returned. # def split if @current_interval.running? next_interval = @current_interval.split duration = @current_interval.duration @stats.update(duration) @current_interval = next_interval return duration end false end # # :call-seq: # metric.to_hash -> Hash # # Convert the metric to a hash # def to_hash result = super Stats::STATS.each do |stat| result[stat] = send(stat) end result end # forward appropriate calls directly to the stats object extend Forwardable def_delegators :@stats, :count, :max, :mean, :min, :rate, :stddev, :sum, :sumsq alias duration sum end end copiousfreetime-hitimes-eccb77f/lib/hitimes/timed_value_metric.rb000066400000000000000000000154151474462713700255610ustar00rootroot00000000000000# frozen_string_literal: true #-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ module Hitimes # # A TimedValueMetric holds the metrics on how long it takes to do a batch of something. # something. For measuring how long a method takes to operate on N items. # # tm = TimedValueMetric.new( 'my-batch-method' ) # # 42.times do # tm.start # number_of_items_processed = do_something # tm.stop( number_of_items_processed ) # end # # puts "#{ tm.name } operated at a rate of #{ tm.rate } calls per second" # # TimedValueMetric combines the usefulness of a ValueMetric and a TimedMetric. # The stats are available for both the time it took to do the operation and # the sizes of the batches that were run. # # A TimedValueMetric keeps track of both the time it took to do an operation # and the size of the batch that was operated on. These metrics are kept # separately as +timed_stats+ and +value_stats+ accessors. # class TimedValueMetric < Metric # holds all the Timed statistics attr_reader :timed_stats # holds all the Value statistics attr_reader :value_stats class << TimedValueMetric # # :call-seq: # TimedValueMetric.now( 'name' ) -> TimedValueMetric # # Return a TimedValueMetric that has been started # def now(name, additional_data = {}) tvm = TimedValueMetric.new(name, additional_data) tvm.start tvm end end # # :call-seq: # TimedValueMetric.new( 'name') -> TimedValueMetric # TimedValueMetric.new( 'name', 'other' => 'data') -> TimedValueMetric # # Create a new TimedValueMetric giving it a name and additional data. # +additional_data+ may be anything that follows the +to_hash+ protocol # def initialize(name, additional_data = {}) super @timed_stats = Stats.new @value_stats = Stats.new @current_interval = Interval.new end # # :call-seq: # timed_value_metric.running? -> true or false # # return whether or not the metric is currently timing something. # def running? @current_interval.running? end # # :call-seq: # timed_value_metric.start -> nil # # Start the current timer, if the current timer is already started, then # this is a noop. # def start unless @current_interval.running? @current_interval.start @sampling_start_time ||= utc_microseconds @sampling_start_interval ||= Interval.now end nil end # # :call-seq: # timed_value_metric.stop( count ) -> Float or nil # # Stop the current metric. The +count+ parameter must be a # value to update to the _value_ portion of the TimedValueMetric. Generally # this is probably the number of things that were operated upon since # +start+ was invoked. # # This updates both the +value_stats+ and +timed_stats+ stats and removes # the current interval. If the metric is stopped then the duration of the # last Interval is returned. If the metric was already stopped before this # call, then false is returned and no stats are updated. # # def stop(value) if @current_interval.running? duration = @current_interval.stop @timed_stats.update(duration) @current_interval = Interval.new @value_stats.update(value) # update the lenght of time we have been sampling @sampling_delta = @sampling_start_interval.duration_so_far return duration end false end # # :call-seq: # timed_value_metric.measure( value ) { ... } -> Object # # Measure the execution of a block and add those stats to the running stats. # The return value is the return value of the block. A value must be passed # into +measure+ to update the +value_stats+ portion of the TimedValueMetric. # def measure(value) return_value = nil begin start return_value = yield ensure stop(value) end return_value end # # :call-seq: # timed_value_metric.split( value ) -> Float # # Split the current metric. Essentially, mark a split time. This means # stop the current interval, with the givein +value+ and create a new # interval, but make sure that the new interval lines up exactly, timewise, # behind the previous interval. # # If the metric is running, then split returns the duration of the previous # interval, i.e. the split-time. If the metric is not running, nothing # happens, no stats are updated, and false is returned. # # def split(value) if @current_interval.running? next_interval = @current_interval.split duration = @current_interval.duration @timed_stats.update(duration) @value_stats.update(value) @current_interval = next_interval return duration end false end # # :call-seq: # timed_value_metric.duration -> Float # # The duration of measured time from the metric. # def duration @timed_stats.sum end # # :call-seq: # timed_value_metric.unit_count -> Float # # The sum of all values passed to +stop+ or +skip+ or +measure+ # def unit_count @value_stats.sum end # # :call-seq: # timed_value_metric.rate -> Float # # Rate in the context of the TimedValueMetric is different than the # TimedMetric. In the TimedValueMetric, each measurement of time is # associated with a quantity of things done during that unit of time. So # the +rate+ for a TimedValueMetric is the (sum of all quantities sampled) / # ( sum of all durations measured ) # # For example, say you were measuring, using a TimedValueMetric batch jobs # that had individual units of work. # # tvm = TimedValueMetric.new( 'some-batch' ) # tvm.start # # process a batch of 12 units # duration1 = tvm.stop( 12 ) # # tvm.start # # process a larger batch of 42 units # duration2 = tvm.stop( 42 ) # # At this point the rate of units per second is calculated as ( 12 + 42 ) / ( duration1 + duration2 ) # # some_batch_rate = tvm.rate # returns ( 34 / ( duration1+duration2 ) ) # def rate @value_stats.sum / @timed_stats.sum end # # :call-seq: # metric.to_hash -> Hash # # Convert the metric to a hash # def to_hash result = super result["timed_stats"] = @timed_stats.to_hash result["value_stats"] = @value_stats.to_hash(Stats::STATS - %w[rate]) result["rate"] = rate result["unit_count"] = unit_count result end end end copiousfreetime-hitimes-eccb77f/lib/hitimes/value_metric.rb000066400000000000000000000037161474462713700244000ustar00rootroot00000000000000# frozen_string_literal: true #-- # Copyright (c) 2008, 2009 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details. #++ require "forwardable" module Hitimes # # A ValueMetric holds the data from measuring a single value over a period of # time. In most cases this may be a single measurement at a single point in # time. # # A good example of a ValueMetric is measuring the number of items in a queue. # # A ValueMetric contains a Stats object, therefore ValueMetric has +count+, +max+, # +mean+, +min+, +stddev+, +sum+, +sumsq+ methods that delegate to that Stats # object for convenience. # class ValueMetric < Metric # holds all the statistics attr_reader :stats # # :call-seq: # ValueMetric.new( 'my_metric' ) -> ValueMetric # ValueMetric.new( 'my_metric', 'foo' => 'bar', 'this' => 42 ) -> ValueMetric # # Create a new ValueMetric giving it a name and additional data. # +additional_data+ may be anything that follows the +to_hash+ protocol. # def initialize(name, additional_data = {}) super @stats = Stats.new end # # :call-seq: # metric.measure( value ) -> Float # # Give the +value+ as the measurement to the metric. The value is returned # def measure(value) @sampling_start_time ||= utc_microseconds @sampling_start_interval ||= Interval.now @stats.update(value) # update the length of time we have been sampling @sampling_delta = @sampling_start_interval.duration_so_far end # # :call-seq: # metric.to_hash -> Hash # # Convert the metric to a hash # def to_hash result = super (Stats::STATS - %w[rate]).each do |stat| result[stat] = send(stat) end result end # forward appropriate calls directly to the stats object extend Forwardable def_delegators :@stats, :count, :max, :mean, :min, :stddev, :sum, :sumsq end end copiousfreetime-hitimes-eccb77f/lib/hitimes/version.rb000066400000000000000000000002661474462713700234030ustar00rootroot00000000000000# frozen_string_literal: true #-- # Copyright (c) 2008 Jeremy Hinegardner # All rights reserved. See LICENSE and/or COPYING for details #++ module Hitimes VERSION = "3.1.0" end copiousfreetime-hitimes-eccb77f/spec/000077500000000000000000000000001474462713700201075ustar00rootroot00000000000000copiousfreetime-hitimes-eccb77f/spec/hitimes_spec.rb000066400000000000000000000010071474462713700231060ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe Hitimes do it "can time a block of code" do d = Hitimes.measure { sleep 0.01 } _(d).must_be :>, 0 end it "raises an error if measure is called with no block" do _(-> { Hitimes.measure }).must_raise(Hitimes::Error) end it "has the raw instant value" do v = Hitimes.raw_instant _(v).must_be :>, 0 end it "has access to the number of nanosecond" do f = Hitimes::NANOSECONDS_PER_SECOND _(f).must_be :>, 0 end end copiousfreetime-hitimes-eccb77f/spec/initialize_spec.rb000066400000000000000000000011171474462713700236070ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe "Hitimes::Initialize" do it "should return a clock id" do val = Hitimes::Initialize.determine_clock_id _(val).wont_be_nil end it "should return :CLOCK_REALTIME as a last option" do val = Hitimes::Initialize.determine_clock_id([:CLOCK_REALTIME]) _(val).must_equal(Process::CLOCK_REALTIME) end it "should raise an error if no clock id is found" do _(lambda { Hitimes::Initialize.determine_clock_id([]) }).must_raise(Hitimes::Error, /Unable to find a high resolution clock/) end end copiousfreetime-hitimes-eccb77f/spec/interval_spec.rb000066400000000000000000000065211474462713700232760ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe Hitimes::Interval do it "raises an error if duration is called on a non-started interval" do i = Hitimes::Interval.new _(lambda { i.duration }).must_raise(Hitimes::Error, /\AAttempt to report a duration on an interval that has not started\Z/) end it "raises an error if stop is called on a non-started interval" do i = Hitimes::Interval.new _(-> { i.stop }).must_raise(Hitimes::Error, /\AAttempt to stop an interval that has not started\Z/) end it "knows if it has been started" do i = Hitimes::Interval.new _(i.started?).must_equal false i.start _(i.started?).must_equal true end it "knows if it has been stopped" do i = Hitimes::Interval.new i.start _(i.stopped?).must_equal false i.stop _(i.stopped?).must_equal true end it "knows if it is currently running" do i = Hitimes::Interval.new _(i.running?).must_equal false i.start _(i.running?).must_equal true i.stop _(i.running?).must_equal false end it "can time a block of code" do d = Hitimes::Interval.measure do sleep 0.01 end _(d).must_be :>, 0 end it "raises an error if measure is called with no block" do _(-> { Hitimes::Interval.measure }).must_raise(Hitimes::Error, /\ANo block given to Interval.measure\Z/) end it "creates an interval via #now" do i = Hitimes::Interval.now _(i.started?).must_equal true _(i.stopped?).must_equal false end it "calling duration multiple times returns successivly grater durations" do i = Hitimes::Interval.new i.start y = i.duration z = i.duration _(z).must_be :>, y end it "calling start multiple times on has no effect after the first call" do i = Hitimes::Interval.new _(i.start).must_equal true x = i.start_instant _(i.start_instant).must_be :>, 0 _(i.start).must_equal false _(x).must_equal i.start_instant end it "returns the duration on the first call to stop" do i = Hitimes::Interval.now d = i.stop _(d).must_be_instance_of(Float) end it "calling stop multiple times on has no effect after the first call" do i = Hitimes::Interval.new _(i.start).must_equal true i.stop x = i.stop_instant _(i.stop_instant).must_be :>, 0 _(i.stop).must_equal false _(x).must_equal i.stop_instant end it "duration does not change after stop is calledd" do i = Hitimes::Interval.new i.start x = i.stop y = i.duration _(i.stop).must_equal false z = i.duration _(x).must_equal y _(x).must_equal z _(y).must_equal z end it "can return how much time has elapsed from the start without stopping the interval" do i = Hitimes::Interval.new i.start x = i.duration_so_far _(i.running?).must_equal true y = i.duration_so_far i.stop _(x).must_be :<, y _(x).must_be :<, i.duration _(y).must_be :<, i.duration end describe "#split" do it "creates a new Interval object" do i = Hitimes::Interval.new i.start i2 = i.split _(i.object_id).wont_equal i2.object_id end it "with the stop instant equivialent to the previous Interval's start instant" do i = Hitimes::Interval.new i.start i2 = i.split _(i.stop_instant).must_equal i2.start_instant end end end copiousfreetime-hitimes-eccb77f/spec/metric_spec.rb000066400000000000000000000014131474462713700227300ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe Hitimes::Metric do before(:each) do @metric = Hitimes::Metric.new("testing") end it "has a name" do _(@metric.name).must_equal "testing" end it "has associated data from initialization" do m = Hitimes::Metric.new("more-data", "foo" => "bar", "this" => "that") _(m.additional_data["foo"]).must_equal "bar" _(m.additional_data["this"]).must_equal "that" m = Hitimes::Metric.new("more-data", { "foo" => "bar", "this" => "that" }) _(m.additional_data["foo"]).must_equal "bar" _(m.additional_data["this"]).must_equal "that" end it "initially has no sampling times" do _(@metric.sampling_start_time).must_be_nil _(@metric.sampling_stop_time).must_be_nil end end copiousfreetime-hitimes-eccb77f/spec/mutex_stats_spec.rb000066400000000000000000000013031474462713700240230ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe Hitimes::MutexedStats do before(:each) do @threads = 5 @iters = 10_000 @final_value = @threads * @iters end def run_with_scissors(stats, threads, iters) spool = [] threads.times do |_t| spool << Thread.new { iters.times { stats.update(1) } } end spool.each(&:join) stats end it "Hitimes::Stats is threadsafe" do stats = run_with_scissors(Hitimes::Stats.new, @threads, @iters) _(stats.count).must_equal @final_value end it "has a threadsafe update" do stats = run_with_scissors(Hitimes::MutexedStats.new, @threads, @iters) _(stats.count).must_equal @final_value end end copiousfreetime-hitimes-eccb77f/spec/paths_spec.rb000066400000000000000000000007741474462713700225750ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe Hitimes::Paths do it "can access the root dir of the project" do _(Hitimes::Paths.root_dir).must_equal File.expand_path(File.join(File.dirname(__FILE__), "..")) + File::SEPARATOR end it "can access the lib path of the project" do _(Hitimes::Paths.lib_path).must_equal File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")) + File::SEPARATOR end end copiousfreetime-hitimes-eccb77f/spec/spec_helper.rb000066400000000000000000000001431474462713700227230ustar00rootroot00000000000000# frozen_string_literal: true require "hitimes" require "minitest/focus" require "minitest/pride" copiousfreetime-hitimes-eccb77f/spec/stats_spec.rb000066400000000000000000000050021474462713700226010ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" require "json" describe Hitimes::Stats do before(:each) do @stats = Hitimes::Stats.new @full_stats = Hitimes::Stats.new [1, 2, 3].each { |i| @full_stats.update(i) } end it "is initialized with usable values" do _(@stats.count).must_equal 0 _(@stats.min).must_equal Float::INFINITY _(@stats.max).must_equal(-Float::INFINITY) _(@stats.sum).must_equal 0.0 _(@stats.rate).must_equal 0.0 end it "calculates the mean correctly" do _(@full_stats.mean).must_equal 2.0 end it "calculates the rate correctly" do _(@full_stats.rate).must_equal 0.5 end it "tracks the maximum value" do _(@full_stats.max).must_equal 3.0 end it "tracks the minimum value" do _(@full_stats.min).must_equal 1.0 end it "tracks the count" do _(@full_stats.count).must_equal 3 end it "tracks the sum" do _(@full_stats.sum).must_equal 6.0 end it "calculates the standard deviation" do _(@full_stats.stddev).must_equal 1.0 end it "calculates the sum of squares " do _(@full_stats.sumsq).must_equal 14.0 end describe "#to_hash " do it "converts to a Hash" do h = @full_stats.to_hash _(h.size).must_equal Hitimes::Stats::STATS.size _(h.keys.sort).must_equal Hitimes::Stats::STATS end it "converts to a limited Hash if given arguments" do h = @full_stats.to_hash("min", "max", "mean") _(h.size).must_equal 3 _(h.keys.sort).must_equal %w[max mean min] h = @full_stats.to_hash(%w[count rate]) _(h.size).must_equal 2 _(h.keys.sort).must_equal %w[count rate] end it "raises NoMethodError if an invalid stat is used" do _(-> { @full_stats.to_hash("wibble") }).must_raise(NoMethodError) end end describe "#to_json" do it "converts to a json string" do j = @full_stats.to_json h = JSON.parse(j) _(h.size).must_equal Hitimes::Stats::STATS.size _(h.keys.sort).must_equal Hitimes::Stats::STATS end it "converts to a limited Hash if given arguments" do j = @full_stats.to_json("min", "max", "mean") h = JSON.parse(j) _(h.size).must_equal 3 _(h.keys.sort).must_equal %w[max mean min] j = @full_stats.to_json(%w[count rate]) h = JSON.parse(j) _(h.size).must_equal 2 _(h.keys.sort).must_equal %w[count rate] end it "raises NoMethodError if an invalid stat is used" do _(-> { @full_stats.to_json("wibble") }).must_raise(NoMethodError) end end end copiousfreetime-hitimes-eccb77f/spec/timed_metric_spec.rb000066400000000000000000000101521474462713700241120ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe Hitimes::TimedMetric do before(:each) do @tm = Hitimes::TimedMetric.new("test-timed-metric") end it "knows if it is running or not" do _(@tm.running?).must_equal false @tm.start _(@tm.running?).must_equal true @tm.stop _(@tm.running?).must_equal false end it "#split returns the last duration and the timer is still running" do @tm.start d = @tm.split _(@tm.running?).must_equal true _(d).must_be :>, 0 _(@tm.count).must_equal 1 _(@tm.duration).must_equal d end it "#stop returns false if called more than once in a row" do @tm.start _(@tm.stop).must_be :>, 0 _(@tm.stop).must_equal false end it "does not count a currently running interval as an interval in calculations" do @tm.start _(@tm.count).must_equal 0 @tm.split _(@tm.count).must_equal 1 end it "#split called on a stopped timer does nothing" do @tm.start @tm.stop _(@tm.split).must_equal false end it "calculates the mean of the durations" do 2.times do @tm.start @tm.stop end _(@tm.mean).must_be :>, 0 end it "calculates the rate of the counts " do 5.times do @tm.start @tm.stop end _(@tm.rate).must_be :>, 0 end it "calculates the stddev of the durations" do 3.times do |_x| @tm.start @tm.stop end _(@tm.stddev).must_be :>, 0 end it "returns 0.0 for stddev if there is no data" do _(@tm.stddev).must_equal 0.0 end it "keeps track of the min value" do 2.times do @tm.start @tm.stop end _(@tm.min).must_be :>, 0 end it "keeps track of the max value" do 2.times do @tm.start @tm.stop end _(@tm.max).must_be :>, 0 end it "keeps track of the sum value" do 2.times do @tm.start @tm.stop end _(@tm.sum).must_be :>, 0 end it "keeps track of the sum of squars value" do 3.times do @tm.start @tm.stop end _(@tm.sumsq).must_be :>, 0 end it "keeps track of the minimum start time of all the intervals" do f1 = Time.now.gmtime.to_f * 1_000_000 5.times do @tm.start @tm.stop end f2 = Time.now.gmtime.to_f * 1_000_000 _(@tm.sampling_start_time).must_be :>=, f1 _(@tm.sampling_start_time).must_be :<, f2 end it "keeps track of the last stop time of all the intervals" do f1 = Time.now.gmtime.to_f * 1_000_000 5.times do @tm.start @tm.stop end f2 = Time.now.gmtime.to_f * 1_000_000 _(@tm.sampling_stop_time).must_be :>, f1 _(@tm.sampling_stop_time).must_be :<=, f2 # distance from now to max stop time time should be less than the distance # from the start to the max stop time _((f2 - @tm.sampling_stop_time)).must_be :<, (@tm.sampling_stop_time - f1) end it "can create an already running timer" do t = Hitimes::TimedMetric.now("already-running") _(t.running?).must_equal true end it "can measure a block of code from an instance" do t = Hitimes::TimedMetric.new("measure a block") 3.times { t.measure { sleep 0.01 } } _(t.duration).must_be :>, 0 _(t.count).must_equal 3 end it "returns the value of the block when measuring" do t = Hitimes::TimedMetric.new("measure a block") x = t.measure do sleep 0.01 42 end _(t.duration).must_be :>, 0 _(x).must_equal 42 end describe "#to_hash" do it "has name value" do h = @tm.to_hash _(h["name"]).must_equal "test-timed-metric" end it "has an empty hash for additional_data" do h = @tm.to_hash _(h["additional_data"]).must_equal({}) _(h["additional_data"].size).must_equal 0 end it "has the right sum" do 10.times { |x| @tm.measure { sleep 0.01 * x } } h = @tm.to_hash _(h["sum"]).must_be :>, 0 end fields = Hitimes::Stats::STATS.dup + %w[name additional_data sampling_start_time sampling_stop_time] fields.each do |f| it "has a value for #{f}" do @tm.measure { sleep 0.001 } h = @tm.to_hash _(h[f]).wont_be_nil end end end end copiousfreetime-hitimes-eccb77f/spec/timed_value_metric_spec.rb000066400000000000000000000114711474462713700253130ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe Hitimes::TimedValueMetric do before(:each) do @tm = Hitimes::TimedValueMetric.new("test-timed-value-metric") end it "knows if it is running or not" do _(@tm.running?).must_equal false @tm.start _(@tm.running?).must_equal true @tm.stop(1) _(@tm.running?).must_equal false end it "#split returns the last duration and the timer is still running" do @tm.start d = @tm.split(1) _(@tm.running?).must_equal true _(d).must_be :>, 0 _(@tm.value_stats.count).must_equal 1 _(@tm.timed_stats.count).must_equal 1 _(@tm.duration).must_equal d end it "#stop returns false if called more than once in a row" do @tm.start _(@tm.stop(1)).must_be :>, 0 _(@tm.stop(1)).must_equal false end it "does not count a currently running interval as an interval in calculations" do @tm.start _(@tm.value_stats.count).must_equal 0 _(@tm.timed_stats.count).must_equal 0 @tm.split(1) _(@tm.value_stats.count).must_equal 1 _(@tm.timed_stats.count).must_equal 1 end it "#split called on a stopped timer does nothing" do @tm.start @tm.stop(1) _(@tm.split(1)).must_equal false end it "calculates the mean of the durations" do 3.times do |x| @tm.start @tm.stop(x) end _(@tm.timed_stats.mean).must_be :>, 0 _(@tm.value_stats.mean).must_equal 1.00 end it "calculates the rate of the counts " do 5.times do |x| @tm.start @tm.stop(x) end _(@tm.rate).must_be :>, 0 end it "calculates the stddev of the durations" do 3.times do |x| @tm.start @tm.stop(x) end _(@tm.timed_stats.stddev).must_be :>, 0 _(@tm.value_stats.stddev).must_equal 1.0 end it "returns 0.0 for stddev if there is no data" do _(@tm.timed_stats.stddev).must_equal 0.0 _(@tm.value_stats.stddev).must_equal 0.0 end it "keeps track of the min value" do 3.times do |x| @tm.start @tm.stop(x) end _(@tm.timed_stats.min).must_be :>, 0 _(@tm.value_stats.min).must_equal 0 end it "keeps track of the max value" do 3.times do |x| @tm.start @tm.stop(x) end _(@tm.timed_stats.max).must_be :>, 0 _(@tm.value_stats.max).must_equal 2 end it "keeps track of the sum value" do 3.times do |x| @tm.start @tm.stop(x) end _(@tm.timed_stats.sum).must_be :>, 0 _(@tm.value_stats.sum).must_equal 3 end it "keeps track of the sum of squares value" do 3.times do |x| @tm.start @tm.stop(x) end _(@tm.timed_stats.sumsq).must_be :>, 0 _(@tm.value_stats.sumsq).must_equal 5 end it "keeps track of the minimum start time of all the intervals" do f1 = Time.now.gmtime.to_f * 1_000_000 5.times do @tm.start @tm.stop(1) end f2 = Time.now.gmtime.to_f * 1_000_000 _(@tm.sampling_start_time).must_be :>=, f1 _(@tm.sampling_start_time).must_be :<, f2 end it "keeps track of the last stop time of all the intervals" do f1 = Time.now.gmtime.to_f * 1_000_000 5.times do @tm.start @tm.stop(1) end f2 = Time.now.gmtime.to_f * 1_000_000 _(@tm.sampling_stop_time).must_be :>, f1 _(@tm.sampling_stop_time).must_be :<=, f2 end it "can create an already running timer" do t = Hitimes::TimedValueMetric.now("already-running") _(t.running?).must_equal true end it "can measure a block of code from an instance" do t = Hitimes::TimedValueMetric.new("measure a block") 3.times { t.measure(1) { sleep 0.001 } } _(t.duration).must_be :>, 0 _(t.timed_stats.count).must_equal 3 _(t.value_stats.count).must_equal 3 end it "returns the value of the block when measuring" do t = Hitimes::TimedValueMetric.new("measure a block") x = t.measure(42) do sleep 0.001 42 end _(t.duration).must_be :>, 0 _(x).must_equal 42 end describe "#to_hash" do it "has name value" do h = @tm.to_hash _(h["name"]).must_equal "test-timed-value-metric" end it "has an empty has for additional_data" do h = @tm.to_hash _(h["additional_data"]).must_equal({}) _(h["additional_data"].size).must_equal 0 end it "has a rate" do 5.times do |x| @tm.start @tm.stop(x) end h = @tm.to_hash _(h["rate"]).must_be :>, 0 end it "has a unit_count" do 5.times do |x| @tm.start @tm.stop(x) end h = @tm.to_hash _(h["unit_count"]).must_equal 10 end fields = %w[name additional_data sampling_start_time sampling_stop_time value_stats timed_stats rate unit_count] fields.each do |f| it "has a value for #{f}" do 3.times { |x| @tm.measure(x) { sleep 0.001 } } h = @tm.to_hash _(h[f]).wont_be_nil end end end end copiousfreetime-hitimes-eccb77f/spec/value_metric_spec.rb000066400000000000000000000054211474462713700241270ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe Hitimes::ValueMetric do before(:each) do @metric = Hitimes::ValueMetric.new("testing") 10.times { |x| @metric.measure(x) } end it "has a name" do _(@metric.name).must_equal "testing" end it "has associated data from initialization" do m = Hitimes::ValueMetric.new("more-data", "foo" => "bar", "this" => "that") _(m.additional_data["foo"]).must_equal "bar" _(m.additional_data["this"]).must_equal "that" m = Hitimes::ValueMetric.new("more-data", { "foo" => "bar", "this" => "that" }) _(m.additional_data["foo"]).must_equal "bar" _(m.additional_data["this"]).must_equal "that" end it "calculates the mean of the measurements" do _(@metric.mean).must_equal 4.5 end it "calculates the stddev of the measurements" do _(@metric.stddev).must_be :>, 0.0 end it "returns 0.0 for stddev if there is no data" do m = Hitimes::ValueMetric.new("0-data") _(m.stddev).must_equal 0.0 end it "keeps track of the sum of data" do _(@metric.sum).must_equal 45.0 end it "keeps track of the sum of squars of data" do _(@metric.sumsq).must_equal 285.0 end it "retuns 0.0 for mean if there is no data" do _(Hitimes::ValueMetric.new("0-data").mean).must_equal 0.0 end it "keeps track of the min value" do _(@metric.min).must_equal 0 end it "keeps track of the max value" do _(@metric.max).must_equal 9 end it "keeps track of the first start time of all the measurements" do m = Hitimes::ValueMetric.new("first-start-time") f1 = Time.now.gmtime.to_f * 1_000_000 10.times do |x| m.measure(x) end f2 = Time.now.gmtime.to_f * 1_000_000 _(m.sampling_start_time).must_be :>=, f1 _(m.sampling_start_time).must_be :<, f2 end it "keeps track of the last stop time of all the intervals" do m = Hitimes::ValueMetric.new("last-stop-time") f1 = Time.now.gmtime.to_f * 1_000_000 10.times do |x| m.measure(x) end f2 = Time.now.gmtime.to_f * 1_000_000 _(m.sampling_stop_time).must_be :>, f1 _(m.sampling_stop_time).must_be :<=, f2 end describe "#to_hash" do it "has name value" do h = @metric.to_hash _(h["name"]).must_equal "testing" end it "has an empty has for additional_data" do h = @metric.to_hash _(h["additional_data"]).must_equal({}) _(h["additional_data"].size).must_equal 0 end it "has the right sum" do h = @metric.to_hash _(h["sum"]).must_equal 45 end fields = Hitimes::Stats::STATS.dup + %w[name additional_data sampling_start_time sampling_stop_time] fields -= ["rate"] fields.each do |f| it "has a value for #{f}" do h = @metric.to_hash _(h[f]).wont_be_nil end end end end copiousfreetime-hitimes-eccb77f/spec/version_spec.rb000066400000000000000000000003001474462713700231240ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe "Hitimes::Version" do it "should be accessable as a constant" do _(Hitimes::VERSION).must_match(/\d+\.\d+\.\d+/) end end copiousfreetime-hitimes-eccb77f/tasks/000077500000000000000000000000001474462713700203025ustar00rootroot00000000000000copiousfreetime-hitimes-eccb77f/tasks/default.rake000066400000000000000000000203631474462713700225760ustar00rootroot00000000000000# frozen_string_literal: true # vim: syntax=ruby require "rake/clean" require "digest" #------------------------------------------------------------------------------ # Minitest - standard TestTask #------------------------------------------------------------------------------ begin require "minitest/test_task" Minitest::TestTask.create(:test) do |t| t.libs << "spec" t.warning = true t.test_globs = "{test,spec}/**/{test_*,*_spec}.rb" end desc "Run the requirements for the tests" task "test:requirements" desc "and the requirements" task test: "test:requirements" task default: :test rescue LoadError This.task_warning("test") end #------------------------------------------------------------------------------ # Coverage - integrated with minitest #------------------------------------------------------------------------------ begin require "simplecov" desc "Run tests with code coverage" Minitest::TestTask.create(:coverage) do |t| t.test_prelude = 'require "simplecov"; SimpleCov.start;' t.libs << "spec" t.warning = true t.test_globs = "{test,spec}/**/{test_*,*_spec}.rb" end CLOBBER << "coverage" if File.directory?("coverage") rescue LoadError This.task_warning("simplecov") end #------------------------------------------------------------------------------ # RDoc - standard rdoc rake task, although we must make sure to use a more # recent version of rdoc since it is the one that has 'tomdoc' markup #------------------------------------------------------------------------------ begin gem "rdoc" # otherwise we get the wrong task from stdlib require "rdoc/task" RDoc::Task.new do |t| t.markup = "tomdoc" t.rdoc_dir = "doc" t.main = "README.md" t.title = "#{This.name} #{This.version}" t.rdoc_files.include(FileList["*.{rdoc,md,txt}"], FileList["ext/**/*.c"], FileList["lib/**/*.rb"]) end rescue StandardError, LoadError This.task_warning("rdoc") end #------------------------------------------------------------------------------ # Reek - static code analysis #------------------------------------------------------------------------------ begin require "reek/rake/task" Reek::Rake::Task.new rescue LoadError This.task_warning("reek") end #------------------------------------------------------------------------------ # Rubocop - static code analysis #------------------------------------------------------------------------------ begin require "rubocop/rake_task" RuboCop::RakeTask.new rescue LoadError This.task_warning("rubocop") end def git_files IO.popen(%w[git ls-files -z], chdir: This.project_root, err: IO::NULL) do |ls| ls.readlines("\x0", chomp: true) end end #------------------------------------------------------------------------------ # Manifest - We want an explicit list of thos files that are to be packaged in # the gem. Most of this is from Hoe. #------------------------------------------------------------------------------ namespace "manifest" do desc "Check the manifest" task check: :clean do files = FileList["**/*", ".*"].to_a.sort files = files.select { |f| (f =~ This.include_in_manifest) && File.file?(f) } tmp = "Manifest.tmp" File.open(tmp, "w") do |f| f.puts files.join("\n") end begin sh "diff -du Manifest.txt #{tmp}" ensure rm tmp end puts "Manifest looks good" end desc "Generate the manifest" task generate: :clean do files = git_files.grep(This.include_in_manifest) files.sort! File.open("Manifest.txt", "w") do |f| f.puts files.join("\n") end end end #------------------------------------------------------------------------------ # Fixme - look for fixmes and report them #------------------------------------------------------------------------------ def fixme_project_root This.project_path("../fixme") end def fixme_project_path(subtree) fixme_project_root.join(subtree) end def local_fixme_files local_files = Dir.glob("tasks/**/*") local_files.concat(Dir.glob(".semaphore/*")) local_files.concat(Dir.glob(".rubocop.yml")) local_files.concat(Dir.glob("bin/*")) end def upstream_fixme_files fixme_project_root.glob("{tasks/**/*,.semaphore/*,.rubocop.yml,bin/*}") end def outdated_fixme_files local_fixme_files.select do |local| upstream = fixme_project_path(local) if upstream.exist? if File.exist?(local) (Digest::SHA256.file(local) != Digest::SHA256.file(upstream)) else true end end end end def fixme_up_to_date? outdated_fixme_files.empty? end namespace :fixme do task default: "manifest:check" do This.manifest.each do |file| next if file == __FILE__ next unless /(txt|rb|md|rdoc|css|html|xml|css)\Z/.match?(file) puts "FIXME: Rename #{file}" if /fixme/i.match?(file) File.readlines(file).each_with_index do |line, idx| prefix = "FIXME: #{file}:#{idx + 1}".ljust(42) puts "#{prefix} => #{line.strip}" if /fixme/i.match?(line) end end end desc "See the local fixme files" task :local_files do local_fixme_files.each do |f| puts f end end desc "See the upstream fixme files" task :upstream_files do upstream_fixme_files.each do |f| puts f end end desc "See if the fixme tools are outdated" task :outdated do if fixme_up_to_date? puts "Fixme files are up to date." else outdated_fixme_files.each do |f| puts "#{f} is outdated" end end end desc "Show the diff between the local and upstream fixme files" task :diff do outdated_fixme_files.each do |f| upstream = fixme_project_path(f) puts "===> Start Diff for #{f}" output = `diff -du #{upstream} #{f}` puts output unless output.empty? puts "===> End Diff for #{f}" puts end end desc "Update outdated fixme files" task :update do if fixme_up_to_date? puts "Fixme files are already up to date." else puts "Updating fixme files:" outdated_fixme_files.each do |local| upstream = fixme_project_path(local) puts " * #{local}" FileUtils.cp(upstream, local) end puts "Use your git commands as appropriate." end end end desc "Look for fixmes and report them" task fixme: "fixme:default" #------------------------------------------------------------------------------ # Gem Specification #------------------------------------------------------------------------------ # Really this is only here to support those who use bundler desc "Build the #{This.name}.gemspec file" task :gemspec do File.open(This.gemspec_file, "wb+") do |f| f.puts "# DO NOT EDIT - This file is automatically generated" f.puts "# Make changes to Manifest.txt and/or Rakefile and regenerate" f.write This.platform_gemspec.to_ruby end end # .rbc files from ruby 2.0 CLOBBER << "**/*.rbc" # The standard gem packaging task, everyone has it. require "rubygems/package_task" Gem::PackageTask.new(This.platform_gemspec) do # nothing end #------------------------------------------------------------------------------ # Release - the steps we go through to do a final release, this is pulled from # a compbination of mojombo's rakegem, hoe and hoe-git # # 1) make sure we are on the main branch # 2) make sure there are no uncommitted items # 3) check the manifest and make sure all looks good # 4) build the gem # 5) do an empty commit to have the commit message of the version # 6) tag that commit as the version # 7) push main # 8) push the tag # 7) pus the gem #------------------------------------------------------------------------------ desc "Check to make sure we are ready to release" task :release_check do abort "You must be on the main branch to release!" unless /^\* main/.match?(`git branch`) abort "Nope, sorry, you have unfinished business" unless /^nothing to commit/m.match?(`git status`) end desc "Create tag v#{This.version}, build and push #{This.platform_gemspec.full_name} to rubygems.org" task release: [:release_check, "manifest:check", :gem] do sh "git commit --allow-empty -a -m 'Release #{This.version}'" sh "git tag -a -m 'v#{This.version}' v#{This.version}" sh "git push origin main" sh "git push origin v#{This.version}" sh "gem push pkg/#{This.platform_gemspec.full_name}.gem" end copiousfreetime-hitimes-eccb77f/tasks/this.rb000066400000000000000000000133461474462713700216050ustar00rootroot00000000000000# frozen_string_literal: true require "pathname" # Public: A Class containing all the metadata and utilities needed to manage a # ruby project. class ThisProject # The name of this project attr_accessor :name # The author's name attr_accessor :author # The email address of the author(s) attr_accessor :email # The homepage of this project attr_accessor :homepage # The regex of files to include in the manifest attr_accessor :include_in_manifest # The hash of Gem::Specifications keyed' by platform attr_accessor :gemspecs # List of cross platforms to build the gem for attr_accessor :cross_platforms # Public: Initialize ThisProject # # Yields self def initialize @include_in_manifest = Regexp.union(/\Alib/, /\Aexe/, /\Aext/, %r{\A[^/]+\.(gemspec|txt|md|rdoc|adoc)\Z}) @gemspecs = {} yield self if block_given? end # Public: return the version of ThisProject # # Search the ruby files in the project looking for the one that has the # version string in it. This does not eval any code in the project, it parses # the source code looking for the string. # # Returns a String version def version ["lib/#{name}.rb", "lib/#{name}/version.rb"].each do |v| path = project_path(v) line = path.read[/^\s*VERSION\s*=\s*.*/] return line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] if line end end # Internal: Return a section of an RDoc file with the given section name # # path - the relative path in the project of the file to parse # section_name - the section out of the file from which to parse data # # Retuns the text of the section as an array of paragrphs. def section_of(file, section_name) re = /^[=#]+ (.*)$/ sectional = project_path(file) parts = sectional.read.split(re)[1..] parts.map!(&:strip) sections = {} Hash[*parts].each do |k, v| sections[k] = v.split("\n\n") end sections[section_name] end # Internal: print out a warning about the give task def task_warning(task) warn "WARNING: '#{task}' tasks are not defined. Please run 'bin/setup'" end # Internal: Return the full path to the file that is relative to the project # root. # # path - the relative path of the file from the project root # # Returns the Pathname of the file def project_path(*relative_path) project_root.join(*relative_path) end # Internal: The absolute path of this file # # Returns the Pathname of this file. def this_file_path Pathname.new(__FILE__).expand_path end # Internal: The root directory of this project # # This is defined as being the directory that is in the path of this project # that has the first Rakefile # # Returns the Pathname of the directory def project_root this_file_path.ascend do |p| rakefile = p.join("Rakefile") return p if rakefile.exist? end end # Internal: Returns the contents of the Manifest.txt file as an array # # Returns an Array of strings def manifest manifest_file = project_path("Manifest.txt") abort "You need a Manifest.txt" unless manifest_file.readable? manifest_file.readlines.map(&:strip) end # Internal: Return the files that define the extensions # # Returns an Array def extension_conf_files manifest.grep(/extconf.rb\Z/) end # Internal: Returns the gemspace associated with the current ruby platform def platform_gemspec gemspecs.fetch(platform) { This.ruby_gemspec } end def core_gemspec Gem::Specification.new do |spec| spec.name = name spec.version = version spec.author = author spec.email = email spec.homepage = homepage spec.summary = summary spec.description = description spec.license = license spec.files = manifest spec.bindir = "exe" spec.executables = spec.files.grep(/^exe/) { |f| File.basename(f) } spec.test_files = [] spec.extra_rdoc_files += spec.files.grep(/(txt|rdoc|md)$/) spec.rdoc_options = ["--main", "README.md", "--markup", "tomdoc",] spec.required_ruby_version = ">= 3.0.0" end end # Internal: Return the gemspec for the ruby platform def ruby_gemspec(core = core_gemspec, &block) yielding_gemspec("ruby", core, &block) end # Internal: Return the gemspec for the jruby platform def java_gemspec(core = core_gemspec, &block) yielding_gemspec("java", core, &block) end # Internal: give an initial spec and a key, create a new gemspec based off of # it. # # This will force the new gemspecs 'platform' to be that of the key, since the # only reason you would have multiple gemspecs at this point is to deal with # different platforms. def yielding_gemspec(key, core) spec = gemspecs[key] ||= core.dup spec.platform = key yield spec if block_given? spec end # Internal: Return the platform of ThisProject at the current moment in time. def platform (RUBY_PLATFORM == "java") ? "java" : Gem::Platform::RUBY end # Internal: Return the DESCRIPTION section of the README.rdoc file def description_section section_of("README.md", "DESCRIPTION") end # Internal: Return the summary text from the README def summary description_section.first end # Internal: Return the full description text from the README def description description_section.join(" ").tr("\n", " ").gsub(/[{}]/, "").gsub(/\[[^\]]+\]/, "") # strip rdoc end def license license_file = project_path("LICENSE.txt") line = license_file.readlines.first line.split(/\s+/).first end # Internal: The path to the gemspec file def gemspec_file project_path("#{name}.gemspec") end end This = ThisProject.new