rarbg 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 875767e85f80c778779be897867dffad4731f9f8a4a7dca68cd76b47be386b5a
4
- data.tar.gz: 1a340ef687b05465ff2290fc8feec5324102d90514c657641b47411149cfd7eb
3
+ metadata.gz: 6ae0fc9cbd49bc0297737148b66e9c08164e1ebb4e0e1e7237d0e0c3385d4165
4
+ data.tar.gz: 76ae543821aa6204da813588d4020bfaf8d9f0462f88cea1c7e8299ee9b84fac
5
5
  SHA512:
6
- metadata.gz: 9a8fca5afbe015a0d4fdb33a2ff3fcd77090b680e519c064d9686c291f5aa3366aa9ea456ae770042c79b5bc8bb7ba7477dfa945c85ee8327befa2b1052adeb2
7
- data.tar.gz: 1d64bf8971f55b9bebaa8cd19cc45d0667f77b53fbba7fa896ad81d8099f40ffb649e5d32994fdd20cca0c11f8fdf849bfdb2661982d54a5f4d4f25d4918ca99
6
+ metadata.gz: c486d18f7621cfa1aa6305145dda201e60c5a05a4c53637efe68908efcb296adb7094af575ab77253c6286098269a1daaa5561ad140d9274a40786de9ebe2c50
7
+ data.tar.gz: f1974c700b3f66ec529e123a92ae8619fca9c9f014f9a7b7d6d3381b0aa95890e05f70706f3e20a62bacaa677a0b758cab3885a634e300a24fdc634310640f95
@@ -0,0 +1,4 @@
1
+ # These owners will be the default owners for everything in the repo.
2
+ # Unless a later match takes precedence, they will be requested for review
3
+ # when someone opens a pull request.
4
+ * @epistrephein
@@ -1,8 +1,12 @@
1
- ## Bug description
2
- A clear and concise description of what the bug is.
1
+ ---
2
+ name: Bug Report
3
+ about: Report something that isn't working as expected.
4
+
5
+ ---
3
6
 
4
- ## Expected behavior
5
- A clear and concise description of what you expected to happen.
7
+ ## Bug description
8
+ A clear and concise description of what the bug is, what you expected to happen
9
+ and what happened instead.
6
10
 
7
11
  ## Steps to reproduce
8
12
  1. Instantiate object...
@@ -10,9 +14,9 @@ A clear and concise description of what you expected to happen.
10
14
  3. Error is raised...
11
15
 
12
16
  ## Environment
13
- - Gem version:
14
- - Ruby version:
15
- - OS and version:
17
+ - Gem version:
18
+ - Ruby version:
19
+ - OS and version:
16
20
 
17
21
  ## Logs
18
22
  If applicable, add logs or screenshots to help explain your problem.
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project.
4
+
5
+ ---
6
+
7
+ ## Feature description
8
+ A clear and concise description of what problem you're trying to solve and what
9
+ you'd want to happen.
10
+
11
+ ## Possible implementations
12
+ If possible, suggest a way to add this feature.
13
+
14
+ ## Alternatives considered
15
+ A description of any alternative solutions or features you've considered.
16
+
17
+ ## Additional context
18
+ Add any other context about the feature here.
@@ -0,0 +1,14 @@
1
+ ## Pull Request description
2
+ A clear and concise explanation of what changes your PR introduces and why this
3
+ was needed.
4
+
5
+ ## Pull Request checklist
6
+
7
+ - [ ] My code is a **bug fix** (non-breaking change which fixes an issue)
8
+ - [ ] My code is a **new feature** (non-breaking change which adds functionality)
9
+ - [ ] My code introduces a **breaking change** (fix or feature that would cause existing functionality to change)
10
+ - [ ] My code follows the code style of this project
11
+ - [ ] My change requires a change to the documentation
12
+ - [ ] I have updated the documentation accordingly
13
+ - [ ] I have added tests to cover my changes
14
+ - [ ] All new and existing tests passed
data/.gitignore CHANGED
@@ -32,6 +32,10 @@ Gemfile.lock
32
32
  *.log
33
33
  *.log.[0-9]*
34
34
 
35
+ # Pry
36
+ .pryrc
37
+ .pry_history
38
+
35
39
  # RVM
36
40
  .rvmrc
37
41
 
data/.rspec CHANGED
@@ -1,4 +1,4 @@
1
+ --color
1
2
  --format documentation
2
3
  --order random
3
- --color
4
4
  --require spec_helper
@@ -0,0 +1,27 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ DisplayCopNames: true
4
+ DisplayStyleGuide: true
5
+
6
+ Gemspec/RequiredRubyVersion:
7
+ Enabled: false
8
+
9
+ Layout/AlignHash:
10
+ EnforcedHashRocketStyle: table
11
+ EnforcedColonStyle: table
12
+
13
+ Layout/FirstParameterIndentation:
14
+ EnforcedStyle: consistent
15
+
16
+ Metrics/BlockLength:
17
+ Exclude:
18
+ - '*.gemspec'
19
+ ExcludedMethods:
20
+ - 'describe'
21
+ - 'context'
22
+
23
+ Metrics/LineLength:
24
+ Max: 90
25
+
26
+ Style/BracesAroundHashParameters:
27
+ EnforcedStyle: context_dependent
@@ -3,6 +3,7 @@ language: ruby
3
3
  cache: bundler
4
4
  script: bundle exec rspec
5
5
  rvm:
6
+ - 2.6.0
6
7
  - 2.5.3
7
8
  - 2.4.5
8
9
  - 2.3.8
@@ -4,8 +4,23 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
- ## Unreleased
8
- [Diff](https://github.com/epistrephein/rarbg/compare/v1.1.1...master)
7
+ ## 1.2.0 - 2019-01-02
8
+ [RubyGems](https://rubygems.org/gems/rarbg/versions/1.2.0) |
9
+ [Release](https://github.com/epistrephein/rarbg/releases/tag/v1.2.0) |
10
+ [Diff](https://github.com/epistrephein/rarbg/compare/v1.1.1...v1.2.0)
11
+
12
+ #### Fixed
13
+ - Fixed gemspec to not include HTML documentation files.
14
+ - Fixed code style and typos.
15
+
16
+ #### Added
17
+ - Added rubocop as development dependency to enforce code style.
18
+ - Added GitHub templates for issues and pull requests.
19
+ - Added CONTRIBUTING document with contribution guidelines.
20
+
21
+ #### Changed
22
+ - Bumped all dependencies to stricter minor version constraint.
23
+ - Rate limit is now stubbed to 0.1 seconds in RSpec to speed up tests.
9
24
 
10
25
 
11
26
  ## 1.1.1 - 2018-10-19
@@ -13,7 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
13
28
  [Release](https://github.com/epistrephein/rarbg/releases/tag/v1.1.1) |
14
29
  [Diff](https://github.com/epistrephein/rarbg/compare/v1.1.0...v1.1.1)
15
30
 
16
- ### Changed
31
+ #### Changed
17
32
  - Self-host documentation on GitHub pages.
18
33
 
19
34
 
@@ -33,13 +48,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
33
48
  [Release](https://github.com/epistrephein/rarbg/releases/tag/v1.0.1) |
34
49
  [Diff](https://github.com/epistrephein/rarbg/compare/v1.0.0...v1.0.1)
35
50
 
36
- #### Added
37
- - Faraday now logs requests to stdout if in verbose mode (`-w` or `-v`).
38
-
39
51
  #### Fixed
40
52
  - Fixed code styling and some documentation errors.
41
53
  - Fixed IMDb id autocorrect in `search`.
42
54
 
55
+ #### Added
56
+ - Faraday now logs requests to stdout if in verbose mode (`-w` or `-v`).
57
+
43
58
 
44
59
  ## 1.0.0 – 2018-04-02
45
60
  [RubyGems](https://rubygems.org/gems/rarbg/versions/1.0.0) |
@@ -95,9 +110,11 @@ search type must be specified as a keyword argument upon `search`.
95
110
  [Diff](https://github.com/epistrephein/rarbg/compare/v0.1.0...v0.1.1)
96
111
 
97
112
  #### Fixed
98
- - Fix invalid gemspec
113
+ - Fix invalid gemspec.
99
114
 
100
115
 
101
116
  ## 0.1.0 – 2016-12-21
117
+ [RubyGems](https://rubygems.org/gems/rarbg/versions/0.1.0) |
118
+ [Release](https://github.com/epistrephein/rarbg/releases/tag/v0.1.0)
102
119
 
103
- Initial release. Yanked from [RubyGems](https://rubygems.org).
120
+ Initial release. Yanked from RubyGems.
@@ -0,0 +1,92 @@
1
+ # Contributing
2
+
3
+ First off, thank you for considering contributing to this project.
4
+
5
+ As part of the open source community, this project thrives thanks to the help
6
+ and the contributions of its users. There are many ways to contribute - from
7
+ submitting bug reports and feature requests, to improving the documentation, or
8
+ writing code which can be incorporated into the codebase.
9
+
10
+ In these guidelines, you will find the most common scenarios related to
11
+ contributing and information on how to proceed to make sure that your submission
12
+ can be evaluated swiftly and properly.
13
+
14
+ This project is intended to be a safe, welcoming space for collaboration,
15
+ and contributors are expected to adhere to the
16
+ [Code of Conduct](https://github.com/epistrephein/rarbg/blob/master/CODE_OF_CONDUCT.md).
17
+
18
+ ## Opening an issue
19
+
20
+ Opening an issue on GitHub is the fastest way to get in touch with the maintainers
21
+ of the project and start a discussion regarding a problem you're experiencing or
22
+ a missing feature you would like to suggest.
23
+
24
+ For this reason, upon opening a new issue, you'll be able to choose between
25
+ different templates for your submission, with some pre-allocated sections to fill.
26
+ Please try to follow the existing structure of the issue and to provide as much
27
+ information as possible regarding your situation. This will help the maintainers
28
+ to reproduce the bug or effectively consider the feature you'd like to see
29
+ implemented.
30
+
31
+ If you think your issue doesn't fit into any of the existing templates, or if you
32
+ just have a question regarding the existing functionalities of the project,
33
+ simply open a regular issue and describe the problem in the clearest way possible.
34
+
35
+ ## Contributing with code
36
+
37
+ If you would like to contribute to the project by writing code, you can open a
38
+ Pull Request on GitHub with your changes and ask a maintainer for a review.
39
+
40
+ As an open source Ruby gem, this projects uses the most common development tools
41
+ to test, validate, and document the code.
42
+
43
+ ### Tools and integrations
44
+
45
+ We use [RSpec](http://rspec.info/) as testing framework.
46
+ The test suite lives in the `spec` directory, and you can run it to test your
47
+ changes with `rake spec`.
48
+
49
+ We also use [RuboCop](https://docs.rubocop.org/en/latest/) to enforce code style.
50
+ The configuration file used for this project lives in `.rubocop.yml`. You can check
51
+ the code style of your changes with `rake rubocop`.
52
+
53
+ The default Rake task, runnable using `rake`, performs code linting via Rubocop
54
+ and then runs the RSpec tests.
55
+
56
+ Documentation is written as [YARD](https://yardoc.org/) docblocks in the Ruby code.
57
+ This is rendered as self-hosted Web pages on [GitHub pages](https://epistrephein.github.io/rarbg/).
58
+ Sources are stored in the repository under the `docs` folder and can be automatically
59
+ generated and updated via `rake yard`.
60
+ The completeness of the documentation is then measured via
61
+ [Inch CI](https://inch-ci.org/github/epistrephein/rarbg).
62
+
63
+ Continuous integration and automated tests are run on
64
+ [Travis CI](https://travis-ci.org/epistrephein/rarbg) and integrated with the
65
+ GitHub Pull Request flow.
66
+
67
+ Code quality and test coverage are then scored via
68
+ [CodeClimate](https://codeclimate.com/github/epistrephein/rarbg).
69
+
70
+ Finally, dependencies are kept up-to-date thanks to
71
+ [Depfu](https://depfu.com/github/epistrephein/rarbg) integration.
72
+
73
+ ### Submitting a PR
74
+
75
+ This project follows the [GitHub flow](https://guides.github.com/introduction/flow/)
76
+ for Pull Request submissions.
77
+
78
+ To submit a PR with your proposed changes, follow these steps:
79
+
80
+ 1. [Fork the repo](https://github.com/epistrephein/rarbg/fork) in your GitHub
81
+ userspace and clone it locally
82
+ 2. Install the dependencies with bundler (`bin/setup`)
83
+ 3. Create a feature branch (`git checkout -b my-new-feature`)
84
+ 4. Add your code to the branch and then commit the changes (`git commit -am 'Add some feature'`)
85
+ 5. Run the test suite (`rake spec`) and make sure that existing and newly introduced
86
+ tests pass. You can also run Rubocop linting with `rake rubocop`, or just use `rake`
87
+ to run both Rubocop and RSpec tests
88
+ 6. Push to your remote branch on GitHub (`git push origin my-new-feature`)
89
+ 7. Create a [new pull request](https://github.com/epistrephein/rarbg/pulls)
90
+
91
+ As for issues, when opening a new Pull Request, please fill out the template with
92
+ all the relevant information in order to reduce the review effort of the maintainers.
data/Gemfile CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  source 'https://rubygems.org'
4
-
5
4
  gemspec
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2016-2018 Tommaso Barbato
3
+ Copyright (c) 2016-2019 Tommaso Barbato
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -20,7 +20,7 @@ $ gem install rarbg
20
20
  Or add it to your Gemfile and execute `bundle install`
21
21
 
22
22
  ```ruby
23
- gem 'rarbg', '~> 1.0'
23
+ gem 'rarbg', '~> 1.2'
24
24
  ```
25
25
 
26
26
  ## Usage
@@ -67,7 +67,6 @@ One search type parameter among `string`, `imdb`, `themoviedb` and `tvdb` is req
67
67
  rarbg.search(string: 'Force Awakens')
68
68
 
69
69
  # Search by IMDB id, in `Movies/x264/1080` and `Movies/x264/720`.
70
- # Note that 'tt' can be omitted when passing an IMDB id.
71
70
  rarbg.search(imdb: 'tt2488496', category: [44, 45])
72
71
 
73
72
  # Search unranked torrents by TheMovieDB id, sorted by last.
@@ -115,7 +114,14 @@ rarbg.list('a string instead of an hash')
115
114
  # => ArgumentError: Expected params hash
116
115
 
117
116
  rarbg.search(limit: 50)
118
- # => ArgumentError: At least one parameter required among string, imdb, tvdb, themoviedb for search mode.
117
+ # => ArgumentError: One search parameter required among: string, imdb, tvdb, themoviedb
118
+ ```
119
+
120
+ Lower level connection errors will raise `Faraday::Error` subclasses exceptions.
121
+
122
+ ```ruby
123
+ rarbg.search(string: 'a timeout error')
124
+ # => Faraday::ConnectionFailed: execution expired
119
125
  ```
120
126
 
121
127
  ## Contributing
@@ -135,6 +141,8 @@ You can contribute changes by forking the project and submitting a pull request.
135
141
  6. Push to the branch (`git push origin my-new-feature`)
136
142
  7. Create a new pull request
137
143
 
144
+ For more information about contributing to this project, check out [CONTRIBUTING](https://github.com/epistrephein/rarbg/blob/master/CONTRIBUTING.md).
145
+
138
146
  ## License
139
147
 
140
148
  This gem is released as open source under the terms of the [MIT License](https://github.com/epistrephein/rarbg/blob/master/LICENSE).
data/Rakefile CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
5
6
  require 'yard'
6
7
 
7
8
  RSpec::Core::RakeTask.new(:spec)
9
+ RuboCop::RakeTask.new(:rubocop)
8
10
  YARD::Rake::YardocTask.new(:yard)
9
11
 
10
- task default: :spec
12
+ task default: %i[rubocop spec]
@@ -4,13 +4,13 @@ require 'rarbg/version'
4
4
  require 'rarbg/categories'
5
5
  require 'rarbg/api'
6
6
 
7
- # Module shortcut methods
7
+ # Module namespace shortcut methods.
8
8
  module RARBG
9
9
  class << self
10
10
  %i[list search].each do |m|
11
- define_method(m) do |*arg|
11
+ define_method(m) do |*args|
12
12
  @rarbg ||= RARBG::API.new
13
- @rarbg.send(m, *arg)
13
+ @rarbg.send(m, *args)
14
14
  end
15
15
  end
16
16
  end
@@ -3,7 +3,7 @@
3
3
  require 'faraday'
4
4
  require 'faraday_middleware'
5
5
 
6
- # Main namespace for RARBG
6
+ # Main namespace for RARBG.
7
7
  module RARBG
8
8
  # Default error class for the module.
9
9
  class APIError < StandardError; end
@@ -16,10 +16,10 @@ module RARBG
16
16
  # App name identifier.
17
17
  APP_ID = 'rarbg-rubygem'
18
18
 
19
- # Default token expiration time.
19
+ # Default token expiration time (seconds).
20
20
  TOKEN_EXPIRATION = 800
21
21
 
22
- # Default API rate limit.
22
+ # Default API rate limit (seconds).
23
23
  RATE_LIMIT = 2.1
24
24
 
25
25
  # @return [Faraday::Connection] the Faraday connection object.
@@ -34,7 +34,11 @@ module RARBG
34
34
  # @return [Integer] the monotonic timestamp of the last request performed.
35
35
  attr_reader :last_request
36
36
 
37
- # Initialize a new istance of `RARBG::API`.
37
+ # Supported search parameters.
38
+ SEARCH_KEYS = %w[string imdb tvdb themoviedb].freeze
39
+ private_constant :SEARCH_KEYS
40
+
41
+ # Initialize a new instance of `RARBG::API`.
38
42
  #
39
43
  # @example
40
44
  # rarbg = RARBG::API.new
@@ -72,9 +76,9 @@ module RARBG
72
76
  # @return [Array<Hash>] Return torrents that match the specified parameters.
73
77
  #
74
78
  # @raise [ArgumentError] Exception raised if `params` is not an `Hash`.
75
- #
76
79
  # @raise [RARBG::APIError] Exception raised when request fails or endpoint
77
80
  # responds with an error.
81
+ # @raise [Faraday::Error] Exception raised on low-level connection errors.
78
82
  #
79
83
  # @example List last 100 ranked torrents in `Movies/x264/1080`
80
84
  # rarbg = RARBG::API.new
@@ -87,8 +91,8 @@ module RARBG
87
91
  raise ArgumentError, 'Expected params hash' unless params.is_a?(Hash)
88
92
 
89
93
  params.update(
90
- mode: 'list',
91
- token: token?
94
+ mode: 'list',
95
+ token: token?
92
96
  )
93
97
  call(params)
94
98
  end
@@ -103,7 +107,7 @@ module RARBG
103
107
  # @option params [String] :themoviedb Search by The Movie DB id.
104
108
  # @option params [Array<Integer>] :category Filter results by category.
105
109
  # @option params [Symbol] :format Format results.
106
- # Valid values: `:json`, `:json_extended`. Default: `:json`
110
+ # Valid values: `:json`, `:json_extended`. Default: `:json`.
107
111
  # @option params [Integer] :limit Limit results number.
108
112
  # Valid values: `25`, `50`, `100`. Default: `25`.
109
113
  # @option params [Integer] :min_seeders Filter results by minimum seeders.
@@ -116,16 +120,15 @@ module RARBG
116
120
  # @return [Array<Hash>] Return torrents that match the specified parameters.
117
121
  #
118
122
  # @raise [ArgumentError] Exception raised if `params` is not an `Hash`.
119
- #
120
123
  # @raise [ArgumentError] Exception raised if no search type param is passed
121
124
  # (among `string`, `imdb`, `tvdb`, `themoviedb`).
122
- #
123
125
  # @raise [RARBG::APIError] Exception raised when request fails or endpoint
124
126
  # responds with an error.
127
+ # @raise [Faraday::Error] Exception raised on low-level connection errors.
125
128
  #
126
129
  # @example Search by IMDb ID, sorted by leechers and in extended format.
127
130
  # rarbg = RARBG::API.new
128
- # rarbg.search(imdb: 'tt012831', sort: :leechers, format: :json_extended)
131
+ # rarbg.search(imdb: 'tt2488496', sort: :leechers, format: :json_extended)
129
132
  #
130
133
  # @example Search unranked torrents by string, with at least 2 seeders.
131
134
  # rarbg = RARBG::API.new
@@ -134,20 +137,21 @@ module RARBG
134
137
  raise ArgumentError, 'Expected params hash' unless params.is_a?(Hash)
135
138
 
136
139
  params.update(
137
- mode: 'search',
138
- token: token?
140
+ mode: 'search',
141
+ token: token?
139
142
  )
140
143
  call(params)
141
144
  end
142
145
 
143
146
  private
144
147
 
145
- # Wrap request for error handling.
148
+ # Wrap requests for error handling.
146
149
  def call(params)
147
150
  response = request(validate(params))
148
151
 
149
152
  return [] if response['error'] == 'No results found'
150
153
  raise APIError, response['error'] if response.key?('error')
154
+
151
155
  response.fetch('torrent_results', [])
152
156
  end
153
157
 
@@ -167,23 +171,20 @@ module RARBG
167
171
  Hash[params.reject { |_k, v| v.nil? }.map { |k, v| [k.to_s, v] }]
168
172
  end
169
173
 
170
- # Validate search type parameter.
174
+ # Validate search type parameters.
171
175
  def validate_search!(params)
172
- search_keys = %w[string imdb tvdb themoviedb]
173
-
174
- raise(
175
- ArgumentError,
176
- "At least one parameter required among #{search_keys.join(', ')} " \
177
- 'for search mode.'
178
- ) if (params.keys & search_keys).none?
176
+ if (params.keys & SEARCH_KEYS).none?
177
+ raise(ArgumentError,
178
+ "One search parameter required among: #{SEARCH_KEYS.join(', ')}")
179
+ end
179
180
 
180
- search_keys.each do |k|
181
+ SEARCH_KEYS.each do |k|
181
182
  params["search_#{k}"] = params.delete(k) if params.key?(k)
182
183
  end
183
184
  params
184
185
  end
185
186
 
186
- # Convert ruby sugar to expected value style.
187
+ # Convert ruby syntax to expected value format.
187
188
  def normalize
188
189
  {
189
190
  'category' => (->(v) { v.join(';') }),
@@ -194,28 +195,28 @@ module RARBG
194
195
 
195
196
  # Return or renew auth token.
196
197
  def token?
197
- if @token.nil? || time >= (@token_time + TOKEN_EXPIRATION)
198
+ if token.nil? || time >= (token_time.to_f + TOKEN_EXPIRATION)
198
199
  response = request(get_token: 'get_token')
199
200
  @token = response.fetch('token')
200
201
  @token_time = time
201
202
  end
202
- @token
203
+ token
203
204
  end
204
205
 
205
206
  # Perform API request.
206
207
  def request(params)
207
208
  rate_limit!(RATE_LIMIT)
208
-
209
- response = @conn.get(nil, params)
209
+ response = conn.get(nil, params)
210
210
  @last_request = time
211
211
 
212
212
  return response.body if response.success?
213
+
213
214
  raise APIError, "#{response.reason_phrase} (#{response.status})"
214
215
  end
215
216
 
216
217
  # Rate-limit requests to comply with endpoint limits.
217
218
  def rate_limit!(seconds)
218
- sleep(0.3) until time >= ((@last_request || 0) + seconds)
219
+ sleep(0.1) until time >= (last_request.to_f + seconds)
219
220
  end
220
221
 
221
222
  # Monotonic clock for elapsed time calculations.