From 019aa1fbc2ca9f9aa76b38016e7fdf8b609a1e42 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 04:06:35 -0600 Subject: [PATCH 01/15] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20IDE?= =?UTF-8?q?=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 8 + .idea/GitLink.xml | 6 + .idea/developer-tools.xml | 6 + .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/misc.xml | 15 ++ .idea/modules.xml | 8 + .idea/oauth.iml | 149 +++++++++++++++++++ .idea/vcs.xml | 13 ++ 8 files changed, 211 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/GitLink.xml create mode 100644 .idea/developer-tools.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/oauth.iml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/GitLink.xml b/.idea/GitLink.xml new file mode 100644 index 00000000..000fa355 --- /dev/null +++ b/.idea/GitLink.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/developer-tools.xml b/.idea/developer-tools.xml new file mode 100644 index 00000000..bccc5cc1 --- /dev/null +++ b/.idea/developer-tools.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..b0db9b0f --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..0dbdd623 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..3c539724 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/oauth.iml b/.idea/oauth.iml new file mode 100644 index 00000000..da14c3e2 --- /dev/null +++ b/.idea/oauth.iml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..4ba488d6 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file From 64796e501c40d1dc7cbafe82dd7d51c936e41f1e Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 04:08:44 -0600 Subject: [PATCH 02/15] =?UTF-8?q?=F0=9F=93=9D=20CoC=20v2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CODE_OF_CONDUCT.md | 108 +++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 29 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index c4880b6d..7ad4c15e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,83 +2,133 @@ ## Our Pledge -We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. -We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. ## Our Standards -Examples of behavior that contributes to a positive environment for our community include: +Examples of behavior that contributes to a positive environment for our +community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall community +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or - advances of any kind +* The use of sexualized language or imagery, and sexual attention or advances of + any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission +* Publishing others' private information, such as a physical or email address, + without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities -Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. -Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. ## Scope -This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at peter.boling@gmail.com. All complaints will be reviewed and investigated promptly and fairly. +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact]. +All complaints will be reviewed and investigated promptly and fairly. -All community leaders are obligated to respect the privacy and security of the reporter of any incident. +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. ## Enforcement Guidelines -Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction -**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. -**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. ### 2. Warning -**Community Impact**: A violation through a single incident or series of actions. +**Community Impact**: A violation through a single incident or series of +actions. -**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. ### 3. Temporary Ban -**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. -**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. ### 4. Permanent Ban -**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. -**Consequence**: A permanent ban from any sort of public interaction within the community. +**Consequence**: A permanent ban from any sort of public interaction within the +community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, -available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. -Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations +[🚂maint-contact]: http://www.railsbling.com/contact +[🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red From 46a0ee6cebd8fdf7c4b17c938a50ffd3fa6b1115 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 04:10:24 -0600 Subject: [PATCH 03/15] =?UTF-8?q?=F0=9F=93=9D=20LICENSE.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE.txt | 2 +- README.md | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 0e4ed44f..efc8054d 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,7 @@ The MIT License (MIT) +Copyright (c) 2020-2025 Peter H. Boling, of Galtzo.com, and oauth contributors Copyright (c) 2007-2012, 2016-2017 Blaine Cook, Larry Halff, Pelle Braendgaard -Copyright (c) 2020-2022 oauth-xx organization, https://gitlab.com/oauth-xx Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 65e7ae78..14a6e5d4 100644 --- a/README.md +++ b/README.md @@ -365,13 +365,41 @@ For example: spec.add_dependency "oauth", "~> 1.1" ``` -## License +
+📌 Is "Platform Support" part of the public API? More details inside. -The gem is available as open source under the terms of -the [MIT License][license] [![License: MIT][🖇src-license-img]][🖇src-license]. -See [LICENSE.txt][license] for the [Copyright Notice][copyright-notice-explainer]. +SemVer should, IMO, but doesn't explicitly, say that dropping support for specific Platforms +is a *breaking change* to an API. +It is obvious to many, but not all, and since the spec is silent, the bike shedding is endless. + +To get a better understanding of how SemVer is intended to work over a project's lifetime, +read this article from the creator of SemVer: -## Contact +- ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred] + +
+ +See [CHANGELOG.md][📌changelog] for a list of releases. + +## 📄 License + +The gem is available as open source under the terms of +the [MIT License][📄license] [![License: MIT][📄license-img]][📄license-ref]. +See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright-notice-explainer]. + +### © Copyright + + OAuth Ruby has been created and maintained by a large number of talented individuals. The current maintainer is Peter Boling (@pboling). Please From 11820fcf330c8b0085c934a7ec9db07bfda3e80c Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 04:11:09 -0600 Subject: [PATCH 04/15] =?UTF-8?q?=F0=9F=8E=A8=20Template=20bootstrap=20by?= =?UTF-8?q?=20kettle-dev-setup=20v1.1.18?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .aiignore | 19 + .devcontainer/devcontainer.json | 26 + .env.local.example | 27 + .envrc | 46 ++ .git-hooks/commit-msg | 48 ++ .git-hooks/commit-subjects-goalie.txt | 8 + .git-hooks/footer-template.erb.txt | 16 + .git-hooks/prepare-commit-msg | 19 + .github/FUNDING.yml | 4 +- .github/dependabot.yml | 9 +- .github/workflows/ancient.yml | 81 ++ .github/workflows/auto-assign.yml | 21 + .github/workflows/codeql-analysis.yml | 12 +- .github/workflows/coverage.yml | 142 ++-- .github/workflows/current.yml | 91 +++ .github/workflows/dep-heads.yml | 105 +++ .github/workflows/dependency-review.yml | 20 + .github/workflows/discord-notifier.yml | 39 + .github/workflows/heads.yml | 98 ++- .github/workflows/jruby.yml | 72 ++ .github/workflows/legacy.yml | 76 ++ .github/workflows/locked_deps.yml | 85 +++ .github/workflows/macos.yml | 2 +- .github/workflows/opencollective.yml | 40 + .github/workflows/style.yml | 65 +- .github/workflows/supported.yml | 81 +- .github/workflows/truffle.yml | 93 +++ .github/workflows/unlocked_deps.yml | 84 +++ .github/workflows/unsupported.yml | 76 ++ .github/workflows/windows.yml | 2 +- .gitignore | 38 +- .gitlab-ci.yml | 136 ++++ .idea/oauth.iml | 2 + .junie/guidelines-rbs.md | 49 ++ .junie/guidelines.md | 139 ++++ .opencollective.yml | 3 + .qlty/qlty.toml | 79 ++ .rspec | 9 + .rubocop.yml | 46 +- .rubocop_gradual.lock | 131 ++++ .rubocop_rspec.yml | 30 + .rubocop_todo.yml | 114 --- .simplecov | 42 +- .tool-versions | 1 + .yard_gfm_support.rb | 22 + .yardopts | 11 + Appraisal.root.gemfile | 12 + Appraisals | 105 +++ CHANGELOG.md | 168 +++-- CITATION.cff | 20 + CONTRIBUTING.md | 249 +++++-- FUNDING.md | 77 ++ Gemfile | 59 +- Gemfile.lock | 386 +++++++--- README.md | 704 +++++++++++------- RUBOCOP.md | 71 ++ Rakefile | 69 +- SECURITY.md | 29 +- bin/appraisal | 16 + bin/bundle | 118 --- bin/bundle-audit | 16 + bin/bundler-audit | 16 + bin/code_climate_reek | 16 + bin/coderay | 16 + bin/erb | 16 + bin/gem_checksums | 16 + bin/htmldiff | 16 + bin/irb | 16 + bin/kettle-changelog | 16 + bin/kettle-commit-msg | 16 + bin/kettle-dev-setup | 16 + bin/kettle-dvcs | 16 + bin/kettle-pre-release | 16 + bin/kettle-readme-backers | 16 + bin/kettle-release | 16 + bin/kramdown | 16 + bin/ldiff | 16 + bin/nokogiri | 16 + bin/oauth | 16 + bin/pry | 16 + bin/racc | 16 + bin/rackup | 16 + bin/rails | 16 + bin/rake | 13 +- bin/rbs | 16 + bin/rdbg | 16 + bin/rdoc | 16 + bin/reek | 16 + bin/restclient | 16 + bin/ri | 16 + bin/rspec | 16 + bin/rubocop | 11 - bin/rubocop-gradual | 16 + bin/ruby-parse | 16 + bin/ruby-rewrite | 16 + bin/setup | 8 + bin/standardrb | 16 + bin/thor | 16 + bin/yard | 16 + bin/yard-junk | 16 + bin/yardoc | 16 + bin/yri | 16 + examples/twitter.rb | 5 +- examples/yql.rb | 5 +- gemfiles/README.md | 32 - gemfiles/a6.gemfile | 9 - gemfiles/a7.gemfile | 9 - gemfiles/audit.gemfile | 7 + gemfiles/coverage.gemfile | 11 + gemfiles/current.gemfile | 7 + gemfiles/dep_heads.gemfile | 9 + gemfiles/head.gemfile | 9 + gemfiles/modular/a5.gemfile | 2 + gemfiles/modular/a6.0.gemfile | 2 + gemfiles/modular/a6.1.gemfile | 2 + gemfiles/modular/a7.gemfile | 2 + gemfiles/modular/a8.gemfile | 2 + gemfiles/modular/coverage.gemfile | 6 + gemfiles/modular/debug.gemfile | 13 + gemfiles/modular/documentation.gemfile | 11 + gemfiles/modular/erb/r2.3/default.gemfile | 6 + gemfiles/modular/erb/r2.6/v2.2.gemfile | 3 + gemfiles/modular/erb/r2/v3.0.gemfile | 1 + gemfiles/modular/erb/r3.1/v4.0.gemfile | 2 + gemfiles/modular/erb/r3/v5.0.gemfile | 1 + gemfiles/modular/erb/vHEAD.gemfile | 2 + gemfiles/modular/injected.gemfile | 60 ++ gemfiles/modular/mutex_m/r2.4/v0.1.gemfile | 3 + gemfiles/modular/mutex_m/r2/v0.3.gemfile | 2 + gemfiles/modular/mutex_m/r3/v0.3.gemfile | 2 + gemfiles/modular/mutex_m/vHEAD.gemfile | 2 + gemfiles/modular/optional.gemfile | 1 + gemfiles/modular/runtime_heads.gemfile | 8 + gemfiles/modular/stringio/r2.4/v0.0.2.gemfile | 4 + gemfiles/modular/stringio/r2/v3.0.gemfile | 5 + gemfiles/modular/stringio/r3/v3.0.gemfile | 5 + gemfiles/modular/stringio/vHEAD.gemfile | 2 + gemfiles/modular/style.gemfile | 25 + gemfiles/modular/x_std_libs.gemfile | 2 + gemfiles/modular/x_std_libs/r2.3/libs.gemfile | 3 + gemfiles/modular/x_std_libs/r2.4/libs.gemfile | 3 + gemfiles/modular/x_std_libs/r2.6/libs.gemfile | 3 + gemfiles/modular/x_std_libs/r2/libs.gemfile | 3 + gemfiles/modular/x_std_libs/r3.1/libs.gemfile | 3 + gemfiles/modular/x_std_libs/r3/libs.gemfile | 3 + gemfiles/modular/x_std_libs/vHEAD.gemfile | 3 + gemfiles/ruby_2_3.gemfile | 9 + gemfiles/ruby_2_4.gemfile | 9 + gemfiles/ruby_2_5.gemfile | 9 + gemfiles/ruby_2_6.gemfile | 9 + gemfiles/ruby_2_7.gemfile | 7 + gemfiles/ruby_3_0.gemfile | 7 + gemfiles/ruby_3_1.gemfile | 7 + gemfiles/ruby_3_2.gemfile | 7 + gemfiles/ruby_3_3.gemfile | 7 + gemfiles/style.gemfile | 9 + gemfiles/unlocked_deps.gemfile | 15 + lib/oauth/client/action_controller_request.rb | 23 +- lib/oauth/client/em_http.rb | 50 +- lib/oauth/client/helper.rb | 26 +- lib/oauth/client/net_http.rb | 52 +- lib/oauth/consumer.rb | 116 +-- lib/oauth/errors/problem.rb | 2 +- lib/oauth/helper.rb | 6 +- lib/oauth/oauth.rb | 15 +- .../action_controller_request.rb | 10 +- lib/oauth/request_proxy/base.rb | 24 +- lib/oauth/request_proxy/em_http_request.rb | 8 +- lib/oauth/request_proxy/jabber_request.rb | 11 +- lib/oauth/request_proxy/net_http.rb | 2 +- .../request_proxy/rest_client_request.rb | 7 +- lib/oauth/server.rb | 20 +- lib/oauth/signature/base.rb | 2 +- lib/oauth/signature/rsa/sha1.rb | 15 +- lib/oauth/tokens/access_token.rb | 2 +- lib/oauth/tokens/consumer_token.rb | 4 +- lib/oauth/tokens/request_token.rb | 13 +- oauth.gemspec | 213 ++++-- spec/config/debug.rb | 4 + spec/config/rspec/rack_test.rb | 5 + spec/config/rspec/rspec_block_is_expected.rb | 2 + spec/config/rspec/rspec_core.rb | 11 + spec/config/rspec/rspec_pending_for.rb | 6 + spec/config/rspec/version_gem.rb | 1 + spec/config/vcr.rb | 12 + spec/config/warnings.rb | 24 + spec/oauth/backwards_compatibility_spec.rb | 8 + spec/oauth/consumer_integration_spec.rb | 230 ++++++ spec/oauth/consumer_spec.rb | 219 ++++++ spec/oauth/em_http_client_spec.rb | 19 + spec/oauth/helper_spec.rb | 97 +++ spec/oauth/net_http_client_spec.rb | 41 + .../request_proxy/action_controller_spec.rb | 30 + .../request_proxy/action_dispatch_spec.rb | 29 + spec/oauth/request_proxy/curb_spec.rb | 20 + spec/oauth/request_proxy/em_http_spec.rb | 19 + spec/oauth/request_proxy/net_http_spec.rb | 28 + spec/oauth/request_proxy/rack_spec.rb | 44 ++ spec/oauth/request_proxy/rest_client_spec.rb | 22 + spec/oauth/request_proxy/typhoeus_spec.rb | 22 + spec/oauth/server_spec.rb | 47 ++ spec/oauth/server_token_spec.rb | 28 + spec/oauth/signature/base_spec.rb | 31 + spec/oauth/signature/hmac_sha1_spec.rb | 44 ++ spec/oauth/signature/hmac_sha256_spec.rb | 44 ++ spec/oauth/signature/plaintext_spec.rb | 45 ++ spec/oauth/signature/rsa_sha1_spec.rb | 189 +++++ spec/oauth/token_spec.rb | 13 + spec/oauth/tokens/access_token_spec.rb | 25 + spec/oauth/tokens/request_token_spec.rb | 72 ++ spec/oauth/tty/cli_spec.rb | 298 ++++++++ spec/spec_helper.rb | 40 + {test => spec/support/fixtures}/keys/rsa.cert | 0 {test => spec/support/fixtures}/keys/rsa.pem | 0 ...en_dance_and_call_a_protected_resource.yml | 42 ++ .../1_0-final/construct_request_url_test.rb | 60 -- .../normalize_request_parameters_test.rb | 86 --- .../1_0-final/parameter_encodings_test.rb | 89 --- .../1_0-final/signature_base_strings_test.rb | 77 -- test/support/minitest_helpers.rb | 29 - test/support/oauth_case.rb | 21 - test/test_helper.rb | 62 -- test/units/access_token_test.rb | 28 - .../action_controller_request_proxy_test.rb | 146 ---- .../action_dispatch_request_proxy_test.rb | 41 - test/units/cli_test.rb | 307 -------- test/units/client_helper_test.rb | 149 ---- test/units/consumer_integration_test.rb | 322 -------- test/units/consumer_test.rb | 365 --------- test/units/curb_request_proxy_test.rb | 77 -- test/units/em_http_client_test.rb | 77 -- test/units/em_http_request_proxy_test.rb | 118 --- test/units/hmac_sha1_test.rb | 22 - test/units/hmac_sha256_test.rb | 23 - test/units/net_http_client_test.rb | 340 --------- test/units/net_http_request_proxy_test.rb | 74 -- test/units/oauth_helper_test.rb | 112 --- test/units/rack_request_proxy_test.rb | 41 - test/units/request_token_test.rb | 82 -- test/units/rest_client_request_proxy_test.rb | 90 --- test/units/rsa_sha1_test.rb | 81 -- test/units/server_test.rb | 43 -- test/units/signature_base_test.rb | 33 - test/units/signature_hmac_sha1_test.rb | 43 -- test/units/signature_hmac_sha256_test.rb | 43 -- test/units/signature_plain_text_test.rb | 31 - test/units/signature_test.rb | 14 - test/units/token_test.rb | 15 - test/units/typhoeus_request_proxy_test.rb | 102 --- 249 files changed, 6603 insertions(+), 4565 deletions(-) create mode 100644 .aiignore create mode 100644 .devcontainer/devcontainer.json create mode 100644 .env.local.example create mode 100755 .envrc create mode 100755 .git-hooks/commit-msg create mode 100644 .git-hooks/commit-subjects-goalie.txt create mode 100644 .git-hooks/footer-template.erb.txt create mode 100755 .git-hooks/prepare-commit-msg create mode 100644 .github/workflows/ancient.yml create mode 100644 .github/workflows/auto-assign.yml create mode 100644 .github/workflows/current.yml create mode 100644 .github/workflows/dep-heads.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/discord-notifier.yml create mode 100644 .github/workflows/jruby.yml create mode 100644 .github/workflows/legacy.yml create mode 100644 .github/workflows/locked_deps.yml create mode 100644 .github/workflows/opencollective.yml create mode 100644 .github/workflows/truffle.yml create mode 100644 .github/workflows/unlocked_deps.yml create mode 100644 .github/workflows/unsupported.yml create mode 100644 .gitlab-ci.yml create mode 100644 .junie/guidelines-rbs.md create mode 100644 .junie/guidelines.md create mode 100644 .opencollective.yml create mode 100644 .qlty/qlty.toml create mode 100644 .rspec create mode 100644 .rubocop_gradual.lock create mode 100644 .rubocop_rspec.yml delete mode 100644 .rubocop_todo.yml create mode 100755 .tool-versions create mode 100644 .yard_gfm_support.rb create mode 100644 .yardopts create mode 100644 Appraisal.root.gemfile create mode 100644 Appraisals create mode 100644 CITATION.cff create mode 100644 FUNDING.md create mode 100644 RUBOCOP.md create mode 100755 bin/appraisal delete mode 100755 bin/bundle create mode 100755 bin/bundle-audit create mode 100755 bin/bundler-audit create mode 100755 bin/code_climate_reek create mode 100755 bin/coderay create mode 100755 bin/erb create mode 100755 bin/gem_checksums create mode 100755 bin/htmldiff create mode 100755 bin/irb create mode 100755 bin/kettle-changelog create mode 100755 bin/kettle-commit-msg create mode 100755 bin/kettle-dev-setup create mode 100755 bin/kettle-dvcs create mode 100755 bin/kettle-pre-release create mode 100755 bin/kettle-readme-backers create mode 100755 bin/kettle-release create mode 100755 bin/kramdown create mode 100755 bin/ldiff create mode 100755 bin/nokogiri create mode 100755 bin/oauth create mode 100755 bin/pry create mode 100755 bin/racc create mode 100755 bin/rackup create mode 100755 bin/rails create mode 100755 bin/rbs create mode 100755 bin/rdbg create mode 100755 bin/rdoc create mode 100755 bin/reek create mode 100755 bin/restclient create mode 100755 bin/ri create mode 100755 bin/rspec create mode 100755 bin/rubocop-gradual create mode 100755 bin/ruby-parse create mode 100755 bin/ruby-rewrite create mode 100755 bin/setup create mode 100755 bin/standardrb create mode 100755 bin/thor create mode 100755 bin/yard create mode 100755 bin/yard-junk create mode 100755 bin/yardoc create mode 100755 bin/yri delete mode 100644 gemfiles/README.md delete mode 100644 gemfiles/a6.gemfile delete mode 100644 gemfiles/a7.gemfile create mode 100644 gemfiles/audit.gemfile create mode 100644 gemfiles/coverage.gemfile create mode 100644 gemfiles/current.gemfile create mode 100644 gemfiles/dep_heads.gemfile create mode 100644 gemfiles/head.gemfile create mode 100644 gemfiles/modular/a5.gemfile create mode 100644 gemfiles/modular/a6.0.gemfile create mode 100644 gemfiles/modular/a6.1.gemfile create mode 100644 gemfiles/modular/a7.gemfile create mode 100644 gemfiles/modular/a8.gemfile create mode 100644 gemfiles/modular/coverage.gemfile create mode 100644 gemfiles/modular/debug.gemfile create mode 100644 gemfiles/modular/documentation.gemfile create mode 100644 gemfiles/modular/erb/r2.3/default.gemfile create mode 100644 gemfiles/modular/erb/r2.6/v2.2.gemfile create mode 100644 gemfiles/modular/erb/r2/v3.0.gemfile create mode 100644 gemfiles/modular/erb/r3.1/v4.0.gemfile create mode 100644 gemfiles/modular/erb/r3/v5.0.gemfile create mode 100644 gemfiles/modular/erb/vHEAD.gemfile create mode 100644 gemfiles/modular/injected.gemfile create mode 100644 gemfiles/modular/mutex_m/r2.4/v0.1.gemfile create mode 100644 gemfiles/modular/mutex_m/r2/v0.3.gemfile create mode 100644 gemfiles/modular/mutex_m/r3/v0.3.gemfile create mode 100644 gemfiles/modular/mutex_m/vHEAD.gemfile create mode 100644 gemfiles/modular/optional.gemfile create mode 100644 gemfiles/modular/runtime_heads.gemfile create mode 100644 gemfiles/modular/stringio/r2.4/v0.0.2.gemfile create mode 100644 gemfiles/modular/stringio/r2/v3.0.gemfile create mode 100644 gemfiles/modular/stringio/r3/v3.0.gemfile create mode 100644 gemfiles/modular/stringio/vHEAD.gemfile create mode 100644 gemfiles/modular/style.gemfile create mode 100644 gemfiles/modular/x_std_libs.gemfile create mode 100644 gemfiles/modular/x_std_libs/r2.3/libs.gemfile create mode 100644 gemfiles/modular/x_std_libs/r2.4/libs.gemfile create mode 100644 gemfiles/modular/x_std_libs/r2.6/libs.gemfile create mode 100644 gemfiles/modular/x_std_libs/r2/libs.gemfile create mode 100644 gemfiles/modular/x_std_libs/r3.1/libs.gemfile create mode 100644 gemfiles/modular/x_std_libs/r3/libs.gemfile create mode 100644 gemfiles/modular/x_std_libs/vHEAD.gemfile create mode 100644 gemfiles/ruby_2_3.gemfile create mode 100644 gemfiles/ruby_2_4.gemfile create mode 100644 gemfiles/ruby_2_5.gemfile create mode 100644 gemfiles/ruby_2_6.gemfile create mode 100644 gemfiles/ruby_2_7.gemfile create mode 100644 gemfiles/ruby_3_0.gemfile create mode 100644 gemfiles/ruby_3_1.gemfile create mode 100644 gemfiles/ruby_3_2.gemfile create mode 100644 gemfiles/ruby_3_3.gemfile create mode 100644 gemfiles/style.gemfile create mode 100644 gemfiles/unlocked_deps.gemfile create mode 100644 spec/config/debug.rb create mode 100644 spec/config/rspec/rack_test.rb create mode 100644 spec/config/rspec/rspec_block_is_expected.rb create mode 100644 spec/config/rspec/rspec_core.rb create mode 100644 spec/config/rspec/rspec_pending_for.rb create mode 100644 spec/config/rspec/version_gem.rb create mode 100644 spec/config/vcr.rb create mode 100644 spec/config/warnings.rb create mode 100644 spec/oauth/backwards_compatibility_spec.rb create mode 100644 spec/oauth/consumer_integration_spec.rb create mode 100644 spec/oauth/consumer_spec.rb create mode 100644 spec/oauth/em_http_client_spec.rb create mode 100644 spec/oauth/helper_spec.rb create mode 100644 spec/oauth/net_http_client_spec.rb create mode 100644 spec/oauth/request_proxy/action_controller_spec.rb create mode 100644 spec/oauth/request_proxy/action_dispatch_spec.rb create mode 100644 spec/oauth/request_proxy/curb_spec.rb create mode 100644 spec/oauth/request_proxy/em_http_spec.rb create mode 100644 spec/oauth/request_proxy/net_http_spec.rb create mode 100644 spec/oauth/request_proxy/rack_spec.rb create mode 100644 spec/oauth/request_proxy/rest_client_spec.rb create mode 100644 spec/oauth/request_proxy/typhoeus_spec.rb create mode 100644 spec/oauth/server_spec.rb create mode 100644 spec/oauth/server_token_spec.rb create mode 100644 spec/oauth/signature/base_spec.rb create mode 100644 spec/oauth/signature/hmac_sha1_spec.rb create mode 100644 spec/oauth/signature/hmac_sha256_spec.rb create mode 100644 spec/oauth/signature/plaintext_spec.rb create mode 100644 spec/oauth/signature/rsa_sha1_spec.rb create mode 100644 spec/oauth/token_spec.rb create mode 100644 spec/oauth/tokens/access_token_spec.rb create mode 100644 spec/oauth/tokens/request_token_spec.rb create mode 100644 spec/oauth/tty/cli_spec.rb create mode 100644 spec/spec_helper.rb rename {test => spec/support/fixtures}/keys/rsa.cert (100%) rename {test => spec/support/fixtures}/keys/rsa.pem (100%) create mode 100644 spec/support/fixtures/vcr_cassettes/OAuth_Consumer_integration/integration_against_term_ie_example_endpoints/can_perform_a_full_token_dance_and_call_a_protected_resource.yml delete mode 100644 test/cases/spec/1_0-final/construct_request_url_test.rb delete mode 100644 test/cases/spec/1_0-final/normalize_request_parameters_test.rb delete mode 100644 test/cases/spec/1_0-final/parameter_encodings_test.rb delete mode 100644 test/cases/spec/1_0-final/signature_base_strings_test.rb delete mode 100644 test/support/minitest_helpers.rb delete mode 100644 test/support/oauth_case.rb delete mode 100644 test/test_helper.rb delete mode 100644 test/units/access_token_test.rb delete mode 100644 test/units/action_controller_request_proxy_test.rb delete mode 100644 test/units/action_dispatch_request_proxy_test.rb delete mode 100644 test/units/cli_test.rb delete mode 100644 test/units/client_helper_test.rb delete mode 100644 test/units/consumer_integration_test.rb delete mode 100644 test/units/consumer_test.rb delete mode 100644 test/units/curb_request_proxy_test.rb delete mode 100644 test/units/em_http_client_test.rb delete mode 100644 test/units/em_http_request_proxy_test.rb delete mode 100644 test/units/hmac_sha1_test.rb delete mode 100644 test/units/hmac_sha256_test.rb delete mode 100644 test/units/net_http_client_test.rb delete mode 100644 test/units/net_http_request_proxy_test.rb delete mode 100644 test/units/oauth_helper_test.rb delete mode 100644 test/units/rack_request_proxy_test.rb delete mode 100644 test/units/request_token_test.rb delete mode 100644 test/units/rest_client_request_proxy_test.rb delete mode 100644 test/units/rsa_sha1_test.rb delete mode 100644 test/units/server_test.rb delete mode 100644 test/units/signature_base_test.rb delete mode 100644 test/units/signature_hmac_sha1_test.rb delete mode 100644 test/units/signature_hmac_sha256_test.rb delete mode 100644 test/units/signature_plain_text_test.rb delete mode 100644 test/units/signature_test.rb delete mode 100644 test/units/token_test.rb delete mode 100644 test/units/typhoeus_request_proxy_test.rb diff --git a/.aiignore b/.aiignore new file mode 100644 index 00000000..df6bd8b7 --- /dev/null +++ b/.aiignore @@ -0,0 +1,19 @@ +# An .aiignore file follows the same syntax as a .gitignore file. +# .gitignore documentation: https://git-scm.com/docs/gitignore + +# you can ignore files +.DS_Store +*.log +*.tmp + +# or folders +.devcontainer/ +.qlty/ +.yardoc/ +dist/ +build/ +out/ +coverage/ +docs/ +pkg/ +results/ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..c5fee1cc --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,26 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/ruby +{ + "name": "Ruby", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/ruby:1-3-bookworm", + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "ruby --version", + + // Configure tool-specific properties. + "customizations" : { + "jetbrains" : { + "backend" : "RubyMine" + } + }, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 00000000..7729745d --- /dev/null +++ b/.env.local.example @@ -0,0 +1,27 @@ +# +# DO NOT EDIT THIS FILE +# +# COPT THIS FILE TO .env.local +# +# That file is ignored by .gitignore. This file is not. +# +export DEBUG=false # do not allow byebug statements (override in .env.local) +export FLOSS_FUNDING_DEBUG=false # extra logging to help diagnose issues (override in .env.local) +export AUTOGEN_FIXTURE_CLEANUP=false # autogenerated gem fixture cleanup after every RSpec run +export GIT_HOOK_FOOTER_APPEND=false +export GIT_HOOK_FOOTER_APPEND_DEBUG=false +export GIT_HOOK_FOOTER_SENTINEL="⚡️ A message from a fellow meat-based-AI" + +# Tokens used by ci:act and CI helpers for reading workflow/pipeline status via APIs +# GitHub (either GITHUB_TOKEN or GH_TOKEN will be used; fine-grained recommended) +# - Scope/permissions: For fine-grained tokens, grant repository access (Read) and Actions: Read +# - For classic tokens, public repos need no scopes; private repos typically require repo +export GITHUB_TOKEN= +# Alternatively: +# export GH_TOKEN= + +# GitLab (either GITLAB_TOKEN or GL_TOKEN will be used) +# - Scope: read_api is sufficient to read pipelines +export GITLAB_TOKEN= +# Alternatively: +# export GL_TOKEN= diff --git a/.envrc b/.envrc new file mode 100755 index 00000000..ea38cf68 --- /dev/null +++ b/.envrc @@ -0,0 +1,46 @@ +# Run any command in this library's bin/ without the bin/ prefix! +# Prefer exe version over binstub +PATH_add exe +PATH_add bin + +# Only add things to this file that should be shared with the team. + +# **dotenv** (See end of file for .env.local integration) +# .env would override anything in this file, if enabled. +# .env is a DOCKER standard, and if we use it, it would be in deployed, or DOCKER, environments. +# Override and customize anything below in your own .env.local +# If you are using dotenv and not direnv, +# copy the following `export` statements to your own .env file. + +### General Ruby ### +# Turn off Ruby Warnings about deprecated code +# export RUBYOPT="-W0" + +### External Testing Controls +export K_SOUP_COV_DO=true # Means you want code coverage +export K_SOUP_COV_COMMAND_NAME="Test Coverage" +# Available formats are html, xml, rcov, lcov, json, tty +export K_SOUP_COV_FORMATTERS="html,xml,rcov,lcov,json,tty" +export K_SOUP_COV_MIN_BRANCH=52 # Means you want to enforce X% branch coverage +export K_SOUP_COV_MIN_LINE=82 # Means you want to enforce X% line coverage +export K_SOUP_COV_MIN_HARD=true # Means you want the build to fail if the coverage thresholds are not met +export K_SOUP_COV_MULTI_FORMATTERS=true +export K_SOUP_COV_OPEN_BIN= # Means don't try to open coverage results in browser +export MAX_ROWS=1 # Setting for simplecov-console gem for tty output, limits to the worst N rows of bad coverage +export KETTLE_TEST_SILENT=true + +# Internal Debugging Controls +export DEBUG=false # do not allow byebug statements (override in .env.local) +export FLOSS_CFG_FUND_DEBUG=false # extra logging to help diagnose issues (override in .env.local) +export FLOSS_CFG_FUND_LOGFILE=tmp/log/debug.log + +# Concurrently developing the rubocop-lts suite? +export RUBOCOP_LTS_LOCAL=false + +# .env would override anything in this file, if `dotenv` is uncommented below. +# .env is a DOCKER standard, and if we use it, it would be in deployed, or DOCKER, environments, +# and that is why we generally want to leave it commented out. +# dotenv + +# .env.local will override anything in this file. +dotenv_if_exists .env.local diff --git a/.git-hooks/commit-msg b/.git-hooks/commit-msg new file mode 100755 index 00000000..750c5bb1 --- /dev/null +++ b/.git-hooks/commit-msg @@ -0,0 +1,48 @@ +#!/usr/bin/env ruby +# vim: set syntax=ruby + +# Do not rely on Bundler; allow running outside a Bundler context +begin + require "rubygems" +rescue LoadError + # continue +end + +begin + # External gems + require "gitmoji/regex" + + full_text = File.read(ARGV[0]) + # Is the first character a GitMoji? + gitmoji_index = full_text =~ Gitmoji::Regex::REGEX + if gitmoji_index == 0 + exit 0 + else + denied = < e + warn("gitmoji-regex gem not found: #{e.class}: #{e.message}.\n\tSkipping gitmoji check and allowing commit to proceed.\n\tRecommendation: add 'gitmoji-regex' to your development dependencies to enable this check.") + exit 0 +end diff --git a/.git-hooks/commit-subjects-goalie.txt b/.git-hooks/commit-subjects-goalie.txt new file mode 100644 index 00000000..54b905aa --- /dev/null +++ b/.git-hooks/commit-subjects-goalie.txt @@ -0,0 +1,8 @@ +🔖 Prepare release v +🔒️ Checksums for v + +# Lines beginning with # are ignored. +# This file is read by .git-hooks/prepare-commit-msg in each project. +# Each line of this file will be matched against the commit subject using `starts_with?`. +# If any `starts_with?` match the project script bin/prepare-commit-msg will run. +# 🔒️ Checksums for v is the standard commit message by stone_checksums. diff --git a/.git-hooks/footer-template.erb.txt b/.git-hooks/footer-template.erb.txt new file mode 100644 index 00000000..d732d699 --- /dev/null +++ b/.git-hooks/footer-template.erb.txt @@ -0,0 +1,16 @@ +⚡️ A message from a fellow meat-based-AI ⚡️ +- [❤️] Finely-crafted open-source tools like <%= @gem_name %> (& many more) are a full-time endeavor. +- [❤️] Though I adore my work, it lacks financial sustainability. +- [❤️] Please, help me continue enhancing your tools by becoming a sponsor: + - [💲] https://liberapay.com/pboling/donate + - [💲] https://github.com/sponsors/pboling + +<% if ENV["GIT_HOOK_FOOTER_APPEND_DEBUG"] == "true" %> + @pwd = <%= @pwd %> + @gemspecs = <%= @gemspecs %> + @spec = <%= @spec %> + @gemspec_path = <%= @gemspec_path %> + @gem_name <%= @gem_name %> + @spec_name <%= @spec_name %> + @content <%= @content %> +<% end %> diff --git a/.git-hooks/prepare-commit-msg b/.git-hooks/prepare-commit-msg new file mode 100755 index 00000000..c6a15570 --- /dev/null +++ b/.git-hooks/prepare-commit-msg @@ -0,0 +1,19 @@ +#!/bin/sh + +# Fail on error and unset variables +set -eu + +# Determine project root as the parent directory of this hook script +PROJECT_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")"/.. && pwd)" + +# Run the Ruby hook within the direnv context (if available), +# so ENV from .envrc/.env.local at project root is loaded. +# One of the things .envrc needs to do is add $PROJECT_ROOT/bin/ to the path. +# You should have this line at the top of .envrc +# PATH_add bin +# NOTE: If this project ships exe scripts it should also add that. +if command -v direnv >/dev/null 2>&1; then + exec direnv exec "$PROJECT_ROOT" "kettle-commit-msg" "$@" +else + raise "direnv not found. Local development of this project ($PROJECT_ROOT) with tools from the kettle-dev gem may not work properly. Please run 'brew install direnv'." +fi diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 998b7a20..697c6583 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -6,8 +6,8 @@ github: [pboling] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., issuehunt: pboling # Replace with a single IssueHunt username ko_fi: pboling # Replace with a single Ko-fi username liberapay: pboling # Replace with a single Liberapay username -open_collective: ruby-oauth # Replace with a single Open Collective username +open_collective: ruby-oauth patreon: galtzo # Replace with a single Patreon username polar: pboling thanks_dev: u/gh/pboling -tidelift: rubygems/oauth # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +tidelift: rubygems/oauth diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c3240fb6..956aa5a3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,8 +3,11 @@ updates: - package-ecosystem: bundler directory: "/" schedule: - interval: daily - time: "04:30" - open-pull-requests-limit: 10 + interval: "weekly" + open-pull-requests-limit: 5 ignore: - dependency-name: "rubocop-lts" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ancient.yml b/.github/workflows/ancient.yml new file mode 100644 index 00000000..3cc6e37e --- /dev/null +++ b/.github/workflows/ancient.yml @@ -0,0 +1,81 @@ +name: MRI 2.3, 2.4, 2.5 (EOL) + +permissions: + contents: read + +on: + push: + branches: + - 'main' + - '*-stable' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + runs-on: ubuntu-22.04 + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + fail-fast: false + matrix: + include: + # Ruby 2.3 + - ruby: "ruby-2.3" + appraisal: "ruby-2-3" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: "3.3.27" + bundler: "2.3.27" + + # Ruby 2.4 + - ruby: "ruby-2.4" + appraisal: "ruby-2-4" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: "3.3.27" + bundler: "2.3.27" + + # Ruby 2.5 + - ruby: "ruby-2.5" + appraisal: "ruby-2-5" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: "3.3.27" + bundler: "2.3.27" + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + run: bundle + - name: Appraisal for ${{ matrix.appraisal }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml new file mode 100644 index 00000000..96975f22 --- /dev/null +++ b/.github/workflows/auto-assign.yml @@ -0,0 +1,21 @@ +name: Auto Assign +on: + issues: + types: [opened] + pull_request: + types: [opened] +jobs: + run: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: 'Auto-assign issue' + uses: pozil/auto-assign-issue@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + assignees: pboling + abortIfPreviousAssignees: true + allowSelfAssign: true + numOfAssignee: 1 \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d3f85059..45a8ec2c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,10 +13,10 @@ name: "CodeQL" on: push: - branches: [ main, "*-maintenance" ] + branches: [ main, '*-stable' ] pull_request: # The branches below must be a subset of the branches above - branches: [ main, "*-maintenance" ] + branches: [ main, '*-stable' ] schedule: - cron: '35 1 * * 5' @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v5 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -67,4 +67,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e2bb595d..ba438c26 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,15 +1,23 @@ -name: Code Coverage +name: Test Coverage + +permissions: + contents: read + pull-requests: write + id-token: write env: - CI_CODECOV: true - COVER_ALL: true + K_SOUP_COV_MIN_BRANCH: 52 + K_SOUP_COV_MIN_LINE: 82 + K_SOUP_COV_MIN_HARD: true + K_SOUP_COV_FORMATTERS: "xml,rcov,lcov,tty" + K_SOUP_COV_DO: true + K_SOUP_COV_MULTI_FORMATTERS: true + K_SOUP_COV_COMMAND_NAME: "Test Coverage" on: push: branches: - 'main' - - '*-maintenance' - - '*-dev' - '*-stable' tags: - '!*' # Do not execute on tags @@ -22,102 +30,98 @@ on: # Cancels all previous workflow runs for the same branch that have not yet completed. concurrency: # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true jobs: - test: - name: Specs with Coverage - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile + coverage: if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Code Coverage on ${{ matrix.ruby }}@current + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile strategy: fail-fast: false matrix: - experimental: [false] - gemfile: - - a7 - rubygems: - - latest - bundler: - - latest - ruby: - - "2.7" + include: + # Coverage + - ruby: "ruby" + appraisal: "coverage" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} steps: - name: Checkout - uses: actions/checkout@v3 - - - name: Install cURL Headers - run: | - sudo apt-get update - sudo apt-get -y --fix-missing install libcurl4-openssl-dev + uses: actions/checkout@v5 - - uses: amancevice/setup-code-climate@v0 - name: CodeClimate Install - if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() - with: - cc_test_reporter_id: ${{ secrets.CC_TEST_REPORTER_ID }} - - - name: Setup Ruby & Bundle + - name: Setup Ruby & RubyGems uses: ruby/setup-ruby@v1 with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true + ruby-version: "${{ matrix.ruby }}" + rubygems: "${{ matrix.rubygems }}" + bundler: "${{ matrix.bundler }}" + bundler-cache: false - - name: CodeClimate Pre-build Notification - run: cc-test-reporter before-build - if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() - continue-on-error: ${{ matrix.experimental != 'false' }} + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + run: bundle + - name: Appraisal for ${{ matrix.appraisal }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Tests for ${{ matrix.ruby }}@current via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} - - name: Run tests - run: bundle exec rake test + # Do SaaS coverage uploads first + - name: Upload coverage to Coveralls + if: ${{ !env.ACT }} + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: ${{ matrix.experimental != 'false' }} - - name: CodeClimate Post-build Notification - run: cc-test-reporter after-build - if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() + - name: Upload coverage to QLTY + if: ${{ !env.ACT }} + uses: qltysh/qlty-action/coverage@main + with: + token: ${{secrets.QLTY_COVERAGE_TOKEN}} + files: coverage/.resultset.json continue-on-error: ${{ matrix.experimental != 'false' }} + # Build will fail here if coverage upload fails + # which will hopefully be noticed for the lack of code coverage comments + - name: Upload coverage to CodeCov + if: ${{ !env.ACT }} + uses: codecov/codecov-action@v5 + with: + use_oidc: true + fail_ci_if_error: false # optional (default = false) + files: coverage/lcov.info,coverage/coverage.xml + verbose: true # optional (default = false) + + # Then PR comments - name: Code Coverage Summary Report - uses: irongut/CodeCoverageSummary@v1.2.0 + if: ${{ !env.ACT && github.event_name == 'pull_request' }} + uses: irongut/CodeCoverageSummary@v1.3.0 with: filename: ./coverage/coverage.xml badge: true fail_below_min: true format: markdown - hide_branch_rate: true + hide_branch_rate: false hide_complexity: true indicators: true output: both - thresholds: '78 40' + thresholds: '82 52' continue-on-error: ${{ matrix.experimental != 'false' }} - name: Add Coverage PR Comment uses: marocchino/sticky-pull-request-comment@v2 - if: matrix.ruby == '2.7' && always() + if: ${{ !env.ACT && github.event_name == 'pull_request' }} with: recreate: true path: code-coverage-results.md continue-on-error: ${{ matrix.experimental != 'false' }} - - - name: Coveralls - uses: coverallsapp/github-action@master - if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - continue-on-error: ${{ matrix.experimental != 'false' }} - -# Using the codecov gem instead. -# - name: CodeCov -# uses: codecov/codecov-action@v2 -# if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() -# with: -# files: ./coverage/coverage.xml -# flags: unittests -# name: codecov-upload -# fail_ci_if_error: true -# continue-on-error: ${{ matrix.experimental != 'false' }} diff --git a/.github/workflows/current.yml b/.github/workflows/current.yml new file mode 100644 index 00000000..72e44fe1 --- /dev/null +++ b/.github/workflows/current.yml @@ -0,0 +1,91 @@ +# Targets the evergreen latest release of ruby, truffleruby, and jruby +name: Current + +permissions: + contents: read + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + - '*-stable' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Specs ${{ matrix.ruby }}@${{ matrix.appraisal }} + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + matrix: + include: + # Ruby 3.4 + - ruby: "ruby" + appraisal: "current" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + # truffleruby-24.1 + # (according to documentation: targets Ruby 3.3 compatibility) + # (according to runtime: targets Ruby 3.2 compatibility) + - ruby: "truffleruby" + appraisal: "current" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: default + bundler: default + + # jruby-10.0 (targets Ruby 3.4 compatibility) + - ruby: "jruby" + appraisal: "current" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: default + bundler: default + + steps: + - name: Checkout + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + run: bundle + - name: Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }} + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/dep-heads.yml b/.github/workflows/dep-heads.yml new file mode 100644 index 00000000..f6915689 --- /dev/null +++ b/.github/workflows/dep-heads.yml @@ -0,0 +1,105 @@ +# Targets the evergreen latest release of ruby, truffleruby, and jruby +# and tests against the HEAD of runtime dependencies +name: Runtime Deps @ HEAD + +permissions: + contents: read + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + - '*-stable' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Specs ${{ matrix.ruby }}@${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + fail-fast: true + matrix: + include: + # Ruby 3.4 + - ruby: "ruby" + appraisal: "dep-heads" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + # truffleruby-24.1 + # (according to documentation: targets Ruby 3.3 compatibility) + # (according to runtime: targets Ruby 3.2 compatibility) + - ruby: "truffleruby" + appraisal: "dep-heads" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: default + bundler: default + + # jruby-10.0 (targets Ruby 3.4 compatibility) + - ruby: "jruby" + appraisal: "dep-heads" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: default + bundler: default + + steps: + - name: Checkout + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: "Install Root Appraisal" + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + run: bundle + + - name: "[Attempt 1] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + id: bundleAttempt1 + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + # Continue to the next step on failure + continue-on-error: true + + # Effectively an automatic retry of the previous step. + - name: "[Attempt 2] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" + # If bundleAttempt1 failed, try again here; Otherwise skip. + if: ${{ steps.bundleAttempt1.outcome == 'failure' && !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + id: bundleAttempt2 + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + + - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000..046e9c88 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,20 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v5 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v4 diff --git a/.github/workflows/discord-notifier.yml b/.github/workflows/discord-notifier.yml new file mode 100644 index 00000000..ad98367a --- /dev/null +++ b/.github/workflows/discord-notifier.yml @@ -0,0 +1,39 @@ +name: Discord Notify + +on: + check_run: + types: [completed] + discussion: + types: [ created ] + discussion_comment: + types: [ created ] + fork: + gollum: + issues: + types: [ opened ] + issue_comment: + types: [ created ] + pull_request: + types: [ opened, reopened, closed ] + release: + types: [ published ] + watch: + types: [ started ] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + notify: + if: false + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + - name: Actions Status Discord + uses: sarisia/actions-status-discord@v1 + if: always() + with: + webhook: ${{ secrets.DISCORD_WEBHOOK }} + status: ${{ job.status }} + username: GitHub Actions diff --git a/.github/workflows/heads.yml b/.github/workflows/heads.yml index 268ceb6e..f8c92d16 100644 --- a/.github/workflows/heads.yml +++ b/.github/workflows/heads.yml @@ -1,11 +1,15 @@ name: Heads +permissions: + contents: read + +env: + K_SOUP_COV_DO: false + on: push: branches: - 'main' - - '*-maintenance' - - '*-dev' - '*-stable' tags: - '!*' # Do not execute on tags @@ -18,45 +22,81 @@ on: # Cancels all previous workflow runs for the same branch that have not yet completed. concurrency: # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true jobs: test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Specs ${{ matrix.ruby }}@${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile strategy: - fail-fast: false + fail-fast: true matrix: - experimental: [true] - gemfile: - - a7 - rubygems: - - latest - bundler: - - latest - ruby: - - truffleruby+graalvm-head - - truffleruby-head - - ruby-head + include: + # NOTE: Heads use default rubygems / bundler; their defaults are custom, unreleased, and from the future! + # ruby-head + - ruby: "ruby-head" + appraisal: "head" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: default + bundler: default + + # truffleruby-head + - ruby: "truffleruby-head" + appraisal: "head" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: default + bundler: default + + # jruby-head + - ruby: "jruby-head" + appraisal: "head" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: default + bundler: default - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} steps: - name: Checkout - uses: actions/checkout@v3 - - name: Install cURL Headers - run: | - sudo apt-get update - sudo apt-get -y --fix-missing install libcurl4-openssl-dev - - name: Setup Ruby & Bundle + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} rubygems: ${{ matrix.rubygems }} bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: "Install Root Appraisal" + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + run: bundle + + - name: "[Attempt 1] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + id: bundleAttempt1 + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + # Continue to the next step on failure + continue-on-error: true + + # Effectively an automatic retry of the previous step. + - name: "[Attempt 2] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" + # If bundleAttempt1 failed, try again here; Otherwise skip. + if: ${{ steps.bundleAttempt1.outcome == 'failure' && !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + id: bundleAttempt2 + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + + - name: Tests for ${{ matrix.ruby }}@${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} + if: ${{ !(env.ACT && startsWith(matrix.ruby, 'jruby')) }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/jruby.yml b/.github/workflows/jruby.yml new file mode 100644 index 00000000..26cc8f3e --- /dev/null +++ b/.github/workflows/jruby.yml @@ -0,0 +1,72 @@ +name: JRuby + +permissions: + contents: read + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + - '*-stable' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + runs-on: ubuntu-22.04 + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + matrix: + include: + # jruby-9.4 (targets Ruby 3.1 compatibility) + - ruby: "jruby-9.4" + appraisal: "ruby-3-1" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: default + bundler: default + + steps: + - name: Checkout + if: ${{ !env.ACT }} + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + if: ${{ !env.ACT }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + if: ${{ !env.ACT }} + run: bundle + - name: Appraisal for ${{ matrix.appraisal }} + if: ${{ !env.ACT }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} + if: ${{ !env.ACT }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/legacy.yml b/.github/workflows/legacy.yml new file mode 100644 index 00000000..7f1fc299 --- /dev/null +++ b/.github/workflows/legacy.yml @@ -0,0 +1,76 @@ +name: MRI 3.0, 3.1 (EOL) + +permissions: + contents: read + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + - '*-stable' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + runs-on: ubuntu-22.04 + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + fail-fast: false + matrix: + include: + # Ruby 3.0 + - ruby: "ruby-3.0" + appraisal: "ruby-3-0" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: '3.5.23' + bundler: '2.5.23' + + # Ruby 3.1 + - ruby: "ruby-3.1" + appraisal: "ruby-3-1" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + run: bundle + - name: Appraisal for ${{ matrix.appraisal }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/locked_deps.yml b/.github/workflows/locked_deps.yml new file mode 100644 index 00000000..38e380a7 --- /dev/null +++ b/.github/workflows/locked_deps.yml @@ -0,0 +1,85 @@ +--- +# Lock/Unlock Deps Pattern +# +# Two often conflicting goals resolved! +# +# - unlocked_deps.yml +# - All runtime & dev dependencies, but does not have a `gemfiles/*.gemfile.lock` committed +# - Uses an Appraisal2 "unlocked_deps" gemfile, and the current MRI Ruby release +# - Know when new dependency releases will break local dev with unlocked dependencies +# - Broken workflow indicates that new releases of dependencies may not work +# +# - locked_deps.yml +# - All runtime & dev dependencies, and has a `Gemfile.lock` committed +# - Uses the project's main Gemfile, and the current MRI Ruby release +# - Matches what contributors and maintainers use locally for development +# - Broken workflow indicates that a new contributor will have a bad time +# +name: Deps Locked + +permissions: + contents: read + +env: + # Running coverage, but not validating minimum coverage, + # because it would be redundant with the coverage workflow. + # Also we can validate all output formats without breaking CodeCov, + # since we aren't submitting these reports anywhere. + K_SOUP_COV_MIN_BRANCH: 56 + K_SOUP_COV_MIN_LINE: 84 + K_SOUP_COV_MIN_HARD: false + K_SOUP_COV_FORMATTERS: "html,xml,rcov,lcov,json,tty" + K_SOUP_COV_DO: true + K_SOUP_COV_MULTI_FORMATTERS: true + K_SOUP_COV_COMMAND_NAME: "Test Coverage" + +on: + push: + branches: + - 'main' + - '*-stable' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Default rake task w/ main Gemfile.lock ${{ matrix.name_extra || '' }} + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + fail-fast: false + matrix: + include: + # Ruby + - ruby: "ruby" + exec_cmd: "rake" + rubygems: latest + bundler: latest + experimental: false + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: true + + - name: Checks the kitchen sink via ${{ matrix.exec_cmd }} + run: bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 04d64aa8..98cd82c2 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -32,7 +32,7 @@ jobs: matrix: experimental: [true] gemfile: - - a7 + - current rubygems: - latest bundler: diff --git a/.github/workflows/opencollective.yml b/.github/workflows/opencollective.yml new file mode 100644 index 00000000..6122df47 --- /dev/null +++ b/.github/workflows/opencollective.yml @@ -0,0 +1,40 @@ +name: Open Collective Backers + +on: + schedule: + # Run once a week on Sunday at 12:00 AM UTC + - cron: '0 0 * * 0' + workflow_dispatch: + +permissions: + contents: write + +jobs: + update-backers: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + persist-credentials: false + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ruby + rubygems: default + bundler: default + bundler-cache: true + + - name: README Update + env: + # Keep GITHUB_TOKEN for any tools/scripts expecting it, mapped to the same secret + GITHUB_TOKEN: ${{ secrets.README_UPDATER_TOKEN }} + README_UPDATER_TOKEN: ${{ secrets.README_UPDATER_TOKEN }} + REPO: ${{ github.repository }} + run: | + git config user.name 'autobolt' + git config user.email 'autobots@9thbit.net' + # Use the configured token for authenticated pushes + git remote set-url origin "https://x-access-token:${README_UPDATER_TOKEN}@github.com/${REPO}.git" + bin/kettle-readme-backers + # Push back to the same branch/ref that triggered the workflow (default branch for schedule) + git push origin HEAD diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 954363b7..2fe1e03c 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -1,50 +1,65 @@ -name: Code Style Checks +name: Style + +permissions: + contents: read on: push: branches: - 'main' - - '*-maintenance' - - '*-dev' - '*-stable' tags: - '!*' # Do not execute on tags pull_request: branches: - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true jobs: rubocop: - name: Rubocop - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Style on ${{ matrix.ruby }}@current + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile strategy: fail-fast: false matrix: - experimental: [false] - gemfile: - - a7 - rubygems: - - latest - bundler: - - latest - ruby: - - "2.7" - runs-on: ubuntu-latest + include: + # Style + - ruby: "ruby" + appraisal: "style" + exec_cmd: "rake rubocop_gradual:check" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + steps: - name: Checkout - uses: actions/checkout@v3 - - name: Install cURL Headers - run: | - sudo apt-get update - sudo apt-get -y --fix-missing install libcurl4-openssl-dev - - name: Setup Ruby & Bundle + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} rubygems: ${{ matrix.rubygems }} bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run Rubocop - run: bundle exec rubocop -DESP + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + run: bundle + - name: Appraisal for ${{ matrix.appraisal }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Run ${{ matrix.appraisal }} checks via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/supported.yml b/.github/workflows/supported.yml index 4108907c..887034b0 100644 --- a/.github/workflows/supported.yml +++ b/.github/workflows/supported.yml @@ -1,11 +1,15 @@ -name: Official Support +name: MRI Non-EOL + +permissions: + contents: read + +env: + K_SOUP_COV_DO: false on: push: branches: - 'main' - - '*-maintenance' - - '*-dev' - '*-stable' tags: - '!*' # Do not execute on tags @@ -18,53 +22,54 @@ on: # Cancels all previous workflow runs for the same branch that have not yet completed. concurrency: # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true jobs: test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile strategy: - fail-fast: false matrix: - experimental: [false] - rubygems: - - latest - bundler: - - latest - gemfile: - - a6 - - a7 - ruby: - - "2.7" - - "3.0" - - "3.1" - exclude: - - ruby: "3.0" - gemfile: "a7" - - ruby: "3.1" - gemfile: "a5" - - ruby: "3.1" - gemfile: "a6" + include: + # Ruby 3.2 + - ruby: "ruby-3.2" + appraisal: "ruby-3-2" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + # Ruby 3.3 + - ruby: "ruby-3.3" + appraisal: "ruby-3-3" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} steps: - name: Checkout - uses: actions/checkout@v3 - - name: Install cURL Headers - run: | - sudo apt-get update - sudo apt-get -y --fix-missing install libcurl4-openssl-dev - - name: Setup Ruby & Bundle + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} rubygems: ${{ matrix.rubygems }} bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + run: bundle + - name: Appraisal for ${{ matrix.ruby }} ${{ matrix.appraisal }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Tests for ${{ matrix.ruby }} ${{ matrix.appraisal }} via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/truffle.yml b/.github/workflows/truffle.yml new file mode 100644 index 00000000..db651885 --- /dev/null +++ b/.github/workflows/truffle.yml @@ -0,0 +1,93 @@ +name: Truffle + +permissions: + contents: read + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + - '*-stable' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + runs-on: ubuntu-22.04 + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + matrix: + include: + # NOTE: truffleruby does not support upgrading rubygems. + # truffleruby-23.1 (targets Ruby 3.2 compatibility) + - ruby: "truffleruby-23.1" + appraisal: "ruby-3-2" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: default + bundler: default + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + run: bundle + + - name: "[Attempt 1] Install Root Appraisal" + id: bundleAttempt1 + run: bundle + # Continue to the next step on failure + continue-on-error: true + + # Effectively an automatic retry of the previous step. + - name: "[Attempt 2] Install Root Appraisal" + id: bundleAttempt2 + # If bundleAttempt1 failed, try again here; Otherwise skip. + if: steps.bundleAttempt1.outcome == 'failure' + run: bundle + + - name: "[Attempt 1] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" + id: bundleAppraisalAttempt1 + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + # Continue to the next step on failure + continue-on-error: true + + # Effectively an automatic retry of the previous step. + - name: "[Attempt 2] Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal }}" + id: bundleAppraisalAttempt2 + # If bundleAttempt1 failed, try again here; Otherwise skip. + if: steps.bundleAppraisalAttempt1.outcome == 'failure' + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + + - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/unlocked_deps.yml b/.github/workflows/unlocked_deps.yml new file mode 100644 index 00000000..7faffa13 --- /dev/null +++ b/.github/workflows/unlocked_deps.yml @@ -0,0 +1,84 @@ +--- +# Lock/Unlock Deps Pattern +# +# Two often conflicting goals resolved! +# +# - unlocked_deps.yml +# - All runtime & dev dependencies, but does not have a `gemfiles/*.gemfile.lock` committed +# - Uses an Appraisal2 "unlocked_deps" gemfile, and the current MRI Ruby release +# - Know when new dependency releases will break local dev with unlocked dependencies +# - Broken workflow indicates that new releases of dependencies may not work +# +# - locked_deps.yml +# - All runtime & dev dependencies, and has a `Gemfile.lock` committed +# - Uses the project's main Gemfile, and the current MRI Ruby release +# - Matches what contributors and maintainers use locally for development +# - Broken workflow indicates that a new contributor will have a bad time +# +name: Deps Unlocked + +permissions: + contents: read + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + - '*-stable' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Default rake task w/ unlocked deps ${{ matrix.name_extra || '' }} + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + matrix: + include: + # Ruby + - ruby: "ruby" + appraisal_name: "unlocked_deps" + exec_cmd: "rake" + gemfile: "Appraisal.root" + rubygems: latest + bundler: latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + run: bundle + - name: Appraisal for ${{ matrix.ruby }}@${{ matrix.appraisal_name }} + run: bundle exec appraisal ${{ matrix.appraisal_name }} bundle + - name: Run ${{ matrix.exec_cmd }} on ${{ matrix.ruby }}@${{ matrix.appraisal_name }} + run: bundle exec appraisal ${{ matrix.appraisal_name }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/unsupported.yml b/.github/workflows/unsupported.yml new file mode 100644 index 00000000..98336994 --- /dev/null +++ b/.github/workflows/unsupported.yml @@ -0,0 +1,76 @@ +name: MRI 2.6 & 2.7 (EOL) + +permissions: + contents: read + +env: + K_SOUP_COV_DO: false + +on: + push: + branches: + - 'main' + - '*-stable' + tags: + - '!*' # Do not execute on tags + pull_request: + branches: + - '*' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + test: + if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" + name: Specs ${{ matrix.ruby }} ${{ matrix.appraisal }}${{ matrix.name_extra || '' }} + runs-on: ubuntu-22.04 + continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} + env: # $BUNDLE_GEMFILE must be set at job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}.gemfile + strategy: + fail-fast: false + matrix: + include: + # Ruby 2.6 + - ruby: "ruby-2.6" + appraisal: "ruby-2-6" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: '3.4.22' + bundler: '2.4.22' + + # Ruby 2.7 + - ruby: "ruby-2.7" + appraisal: "ruby-2-7" + exec_cmd: "rake test" + gemfile: "Appraisal.root" + rubygems: '3.4.22' + bundler: '2.4.22' + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Ruby & RubyGems + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: ${{ matrix.rubygems }} + bundler: ${{ matrix.bundler }} + bundler-cache: false + + # Raw `bundle` will use the BUNDLE_GEMFILE set to matrix.gemfile (i.e. Appraisal.root) + # We need to do this first to get appraisal installed. + # NOTE: This does not use the primary Gemfile at all. + - name: Install Root Appraisal + run: bundle + - name: Appraisal for ${{ matrix.appraisal }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle + - name: Tests for ${{ matrix.ruby }} via ${{ matrix.exec_cmd }} + run: bundle exec appraisal ${{ matrix.appraisal }} bundle exec ${{ matrix.exec_cmd }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1ba89b53..22db7bdf 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -32,7 +32,7 @@ jobs: matrix: experimental: [true] gemfile: - - a7 + - current rubygems: - latest bundler: diff --git a/.gitignore b/.gitignore index c0dbca29..5fb6e270 100644 --- a/.gitignore +++ b/.gitignore @@ -9,27 +9,41 @@ /gemfiles/.bundle/ /gemfiles/.bundle/config /gemfiles/vendor/ +Appraisal.*.gemfile.lock # Specs +.rspec_status /coverage/ -/test/reports/ +/spec/reports/ +/results/ +.output.txt # Documentation -/.yardoc +/.yardoc/ /_yardoc/ -/doc/ /rdoc/ +/doc/ -# Editors -.idea -*~ +# Ruby Version Managers (RVM, rbenv, etc) +# Ignored because we currently use .tool-versions +.rvmrc +.ruby-version +.ruby-gemset -# Other +# Benchmarking /measurement/ -/.byebug_history + +# Debugger detritus +.byebug_history + +# direnv - brew install direnv +.env.local + +# OS Detritus .DS_Store -# Version Managers -.rvmrc -.ruby-version -.tool-versions +# Editors +*~ + +# Sentinels +.floss_funding.*.lock diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..3a196555 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,136 @@ +# You can override the included template(s) by including variable overrides +# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings +# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings +# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings +# Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings +# Note that environment variables can be set in several places +# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence +#stages: +# - test +#sast: +# stage: test +#include: +# - template: Security/SAST.gitlab-ci.yml + +default: + image: ruby + +variables: + BUNDLE_INSTALL_FLAGS: "--quiet --jobs=$(nproc) --retry=3" + BUNDLE_FROZEN: "false" # No lockfile! + BUNDLE_GEMFILE: Appraisal.root.gemfile + K_SOUP_COV_DEBUG: true + K_SOUP_COV_DO: true + K_SOUP_COV_HARD: true + K_SOUP_COV_MIN_BRANCH: 52 + K_SOUP_COV_MIN_LINE: 82 + K_SOUP_COV_VERBOSE: true + K_SOUP_COV_FORMATTERS: "tty" + K_SOUP_COV_MULTI_FORMATTERS: true + K_SOUP_COV_COMMAND_NAME: "RSpec Coverage" + +workflow: + rules: + # For merge requests, create a pipeline. + - if: '$CI_MERGE_REQUEST_IID' + # For the ` main ` branch, create a pipeline (this includes on schedules, pushes, merges, etc.). + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + # For tags, create a pipeline. + - if: '$CI_COMMIT_TAG' + +.test_template-current: &test_definition-current + image: ruby:${RUBY_VERSION} + stage: test + script: + # || true so we don't fail here, because it'll probably work even if the gem update fails + - gem update --silent --system > /dev/null 2>&1 || true + - mkdir -p vendor/bundle + - bundle config set path 'vendor/bundle' + - chmod +t -R vendor/bundle + - chmod o-w -R vendor/bundle + # Setup appraisal2 + - bundle install + # Bundle a specific appraisal + - bundle exec appraisal unlocked_deps bundle install + # Light smoke test + - bundle exec appraisal unlocked_deps bin/rake --tasks + # Run tests, skipping those that won't work in CI + - > + bundle exec appraisal unlocked_deps \ + bin/rspec spec \ + --tag \~ci_skip \ + --format progress \ + --format RspecJunitFormatter + cache: + key: ${CI_JOB_IMAGE} + paths: + - vendor/ruby + +.test_template-legacy: &test_definition-legacy + image: ruby:${RUBY_VERSION} + stage: test + script: + # RUBYGEMS_VERSION because we support EOL Ruby still... + # || true so we don't fail here, because it'll probably work even if the gem update fails + - gem install rubygems-update -v ${RUBYGEMS_VERSION} || true + # Actually updates both RubyGems and Bundler! + - update_rubygems + - mkdir -p vendor/bundle + - bundle config set path 'vendor/bundle' + - chmod +t -R vendor/bundle + - chmod o-w -R vendor/bundle + # Setup appraisal2 + - bundle install + # Bundle a specific appraisal + - bundle exec appraisal ${APPRAISAL} bundle install + # Light smoke test + - bundle exec appraisal ${APPRAISAL} bin/rake --tasks + # Run tests, skipping those that won't work in CI + - > + bundle exec appraisal unlocked_deps \ + bin/rspec spec \ + --tag \~ci_skip \ + --format progress \ + --format RspecJunitFormatter + cache: + key: ${CI_JOB_IMAGE} + paths: + - vendor/ruby + +ruby-current: + variables: + K_SOUP_COV_DO: true + <<: *test_definition-current + parallel: + matrix: + - RUBY_VERSION: ["3.2", "3.3", "3.4"] + +ruby-ruby3_1: + variables: + RUBYGEMS_VERSION: "3.6.9" + APPRAISAL: ruby_3_1 + K_SOUP_COV_DO: false + <<: *test_definition-legacy + parallel: + matrix: + - RUBY_VERSION: ["3.1"] + +ruby-ruby3_0: + variables: + RUBYGEMS_VERSION: "3.5.23" + APPRAISAL: ruby_3_0 + K_SOUP_COV_DO: false + <<: *test_definition-legacy + parallel: + matrix: + - RUBY_VERSION: ["3.0"] + +ruby-ruby2_7: + variables: + RUBYGEMS_VERSION: "3.4.22" + APPRAISAL: ruby_2_7 + K_SOUP_COV_DO: false + <<: *test_definition-legacy + parallel: + matrix: + - RUBY_VERSION: ["2.7"] diff --git a/.idea/oauth.iml b/.idea/oauth.iml index da14c3e2..ed2346ed 100644 --- a/.idea/oauth.iml +++ b/.idea/oauth.iml @@ -23,6 +23,7 @@ + @@ -45,6 +46,7 @@ + diff --git a/.junie/guidelines-rbs.md b/.junie/guidelines-rbs.md new file mode 100644 index 00000000..9de89732 --- /dev/null +++ b/.junie/guidelines-rbs.md @@ -0,0 +1,49 @@ +# Junie Project Guidelines Addendum: RBS Documentation + +This repository ships RBS type signatures under `sig/` which are included in the published gem and referenced by documentation tooling. + +RBS files must contain only valid RBS syntax. Do not embed Ruby code or YARD-style Ruby documentation constructs in `.rbs` files. + +Requirements for RBS documentation and signatures: + +- Use RBS comment style (`# ...`) for notes and documentation inside `.rbs` files. +- Do not use Ruby heredocs (`<<-DOC`, `<<~RUBY`, etc.) or any Ruby code constructs in `.rbs` files. +- Do not use Ruby metaprogramming notation like `class << self` in `.rbs`. For singleton methods, use: + - `def self.method_name: ...` +- Do not use `extend self` or `module self` in `.rbs`. Declare singleton methods explicitly with `def self.method_name: ...`. +- Keep type aliases, interfaces, and method signatures in proper RBS form only (e.g., `def foo: (String) -> Integer`). +- If you need to document parameters or returns, place brief comments above the signature lines using `#` and keep them RBS-friendly (no `@param` / `@return` tags from YARD). + +Examples: + +Valid (RBS): + +``` +module Foo + # Runs tasks + def self.run: () -> void +end +``` + +Invalid (not allowed in .rbs): + +``` +# Ruby syntax – not RBS +class << self + def run: () -> void +end + +# Not supported across RBS versions; avoid in this project +module self + def run: () -> void +end + +# Heredocs or any Ruby bodies are not allowed in .rbs +def self.run: () -> void + <<~DOC +DOC +end +``` + +Enforcement: +- CI and local builds may parse `.rbs` files during gem install or doc generation. Any non-RBS syntax can cause installation to fail. Keep `.rbs` clean to avoid such failures. diff --git a/.junie/guidelines.md b/.junie/guidelines.md new file mode 100644 index 00000000..a1082ac8 --- /dev/null +++ b/.junie/guidelines.md @@ -0,0 +1,139 @@ +Project: oauth — Development Guidelines (for advanced contributors) + +This document captures project-specific knowledge to streamline setup, testing, and ongoing development. + +1. Build and configuration +- ENV is controlled by `direnv`. + - Two files are loaded: + - .envrc — environment variables for local development, committed to source control + - .env.local — environment variables that are not committed to source control. These setting override .envrc. + - Run `direnv allow` after making changes to .envrc or .env.local. + - See .envrc for details. + - See .env.local.example for an example of what to put in .env.local. + - See CONTRIBUTING.md for details on how to set up your local environment. +- Ruby and Bundler + - Runtime supports very old Rubies (>= 1.9.2) but development tooling targets >= 2.3 because of CI/setup-ruby and dev dependencies. + - Use a recent Ruby (>= 3.4 recommended) for fastest setup and to exercise modern coverage behavior. + - Install dependencies via Bundler in project root: + - bundle install +- Rake tasks (preferred entry points) + - The Rakefile wires common workflows. Useful targets: + - rake spec — run RSpec suite (also aliased via rake test) + - rake coverage — run specs with coverage locally and open a report (requires kettle-soup-cover) + - rake rubocop_gradual:autocorrect — RuboCop-LTS Gradual, with autocorrect as default task + - rake reek and rake reek:update — code smell checks and persisted snapshots in REEK + - rake yard — generate YARD docs for lib and selected extra files + - rake bundle:audit and rake bundle:audit:update — dependency vulnerability checks + - rake build / rake release — gem build/release helper tasks (Bundler + stone_checksums) + - The default rake target runs a curated set of tasks; this varies for CI vs local (see CI env var logic in Rakefile). + - Always run the default rake task prior commits, and after making changes to lib/ code, or *.md files, to allow the linter to autocorrect, and to generate updated documentation. +- Coverage orchestration + - Coverage is controlled by kettle-soup-cover and .simplecov. Thresholds (line and branch) are enforced and can fail the process. + - Thresholds are primarily controlled by environment variables (see .simplecov and comments therein) typically loaded via direnv (.envrc) and CI workflow (.github/workflows/coverage.yml). When running only a test subset, thresholds may fail; see Testing below. +- Gem signing (for releases) + - Signing is enabled unless SKIP_GEM_SIGNING is set. If enabled and certificates are present (certs/.pem), gem build will attempt to sign using ~/.ssh/gem-private_key.pem. + - See CONTRIBUTING.md for releasing details; use SKIP_GEM_SIGNING when building in environments without the private key. + - Important for local testing (to avoid hanging prompts): ALWAYS skip signing when building locally to test the packaging or install process. Without the private key password, the build will wait indefinitely at a signing prompt. + - One-off commands (recommended): + - SKIP_GEM_SIGNING=true gem build oauth.gemspec + - SKIP_GEM_SIGNING=true bundle exec rake build + - SKIP_GEM_SIGNING=true bundle exec rake release # only to test workflow; do not actually push + - direnv option (optional, not recommended globally): add `export SKIP_GEM_SIGNING=true` to your .env.local when you know you won’t be signing in this environment. + - Remove or unset SKIP_GEM_SIGNING when performing a real, signed release in the environment that has the private key. + +2. Testing +- Framework and helpers + - RSpec 3.13 with custom spec/spec_helper.rb configuration: + - silent_stream: STDOUT is silenced by default for examples to keep logs clean. + - To explicitly test console output, tag the example or group with :check_output. + - Global state hygiene: Around each example, FlossFunding.namespaces and FlossFunding.silenced are snapshotted and restored to prevent cross-test pollution. + - DEBUG toggle: Set DEBUG=true to require 'debug' and avoid silencing output during your run. + - ENV seeding: The suite sets ENV["FLOSS_FUNDING_FLOSS_FUNDING"] = "Free-as-in-beer" so that the library’s own namespace is considered activated (avoids noisy warnings). + - Coverage: kettle-soup-cover integrates SimpleCov; .simplecov is invoked from spec_helper when enabled by Kettle::Soup::Cover::DO_COV, which is controlled by K_SOUP_COV_DO being set to true / false. + - RSpec.describe usage: + - Use `describe "#"` to contain a block of specs that test instance method behavior. + - Use `describe "::"` to contain a block of specs that test class method behavior. + - Do not use `describe "."` because the dot is ambiguous w.r.t instance vs. class methods. + - When adding new code or modifying existing code always add tests to cover the updated behavior, including branches, and different types of expected and unexpected inputs. + - Additional test utilities: + - rspec-stubbed_env: Use stub_env to control ENV safely within examples. + - timecop-rspec: Time manipulation is available, and is setup by kettle-test. + - To freeze time use `freeze: Time.new(*args)` tag on an example or group +- Running tests (verified) + - Full suite (recommended to satisfy coverage thresholds): + - bin/rspec + - or: bundle exec rspec + - or: bundle exec rake spec + - Progress format (less verbose): + - bundle exec rspec --format progress + - Focused runs + - You can run a single file or example, but note: coverage thresholds need to be disabled with K_SOUP_COV_MIN_HARD=false + - Example: K_SOUP_COV_MIN_HARD=false bin/rspec spec/oauth/class_spec.rb:42 + - Output visibility + - To see STDOUT from the code under test, use the :check_output tag on the example or group. + Example: + RSpec.describe "output", :check_output do + it "prints" do + puts "This output should be visible" + expect(true).to be true + end + end + - Alternatively, run with DEBUG=true to disable silencing for the entire run. + - During a spec run, the presence of output about missing activation keys is often expected, since it is literally what this library is for. It only indicates a failure if the spec expected all activation keys to be present, and not all specs do. +- Adding new tests (guidelines) + - Organize specs by class/module. Do not create per-task umbrella spec files; add examples to the existing spec for the class/module under test, or create a new spec file for that class/module if one does not exist. Only create a standalone scenario spec when it intentionally spans multiple classes for an integration/benchmark scenario (e.g., bench_integration_spec), and name it accordingly. + - Spec file names must map to a real class or module under lib/ (mirror the path). Do not introduce specs for non-existent classes or ad-hoc names (e.g., avoid template_helpers_replacements_spec.rb when testing Oauth::TemplateHelpers; add those examples to template_helpers_spec.rb). + - REQUIRED: Provide unit tests for every class, module, constant, and public method. Place them in spec/ mirroring the path under lib/. When a file under lib/ is added or changed, ensure a corresponding spec file exists/updated for it. + - Add tests for all public methods and add contexts for variations of their arguments, and arity. + - This repository targets near-100% coverage of its public API; when you add new public methods, rake tasks to a rakelib, or config behavior, add or update specs accordingly. + - Place new specs under spec/ mirroring lib/ structure where possible. Do not require "spec_helper" at the top of spec files, as it is automatically loaded by .rspec. + - If your code relies on environment variables that drive activation (see "Activation env vars" below), prefer using rspec-stubbed_env: + - it does not support stubbing with blocks, but it does automatically clean up after itself. + - outside the example: + include_context 'with stubbed env' + - in a before hook, or in an example: + stub_env("FLOSS_FUNDING_MY_NS" => "Free-as-in-beer") + # example code continues + - If your spec needs to assert on console output, tag it with :check_output. By default, STDOUT is silenced. + - Use Timecop for deterministic time-sensitive behavior as needed (require config/timecop is already done by spec_helper). + +- Types and documentation + - REQUIRED: All public APIs must have RBS type signatures checked into sig/ under the corresponding path. When you add a new public method or change a signature, update the matching .rbs file. + - REQUIRED: All public methods must include inline YARD docs with @param/@return (and @yield/@option where applicable). Generate docs with `bundle exec rake yard` to verify formatting. + +3. Additional development information +- Code style and static analysis + - RuboCop-LTS (Gradual) is integrated. Use: + - bundle exec rake rubocop_gradual:autocorrect + - bundle exec rake rubocop_gradual:force_update # only run if there are still linting violations the default rake task, which includes autocorrect locally, or a standalone autocorrect task, has run, and failed, and the violations won't be fixed + - Reek is configured to scan {lib,spec,tests}/**/*.rb. Use: + - bundle exec rake reek + - bundle exec rake reek:update # writes current output to REEK, fails on smells + - Keep REEK file updated with intentional smells snapshot when appropriate (e.g., after refactors). + - Locally, the default rake task includes reek:update. +- Documentation + - Generate YARD docs with: bundle exec rake yard. It includes lib/**/*.rb and extra docs like README.md, CHANGELOG.md, RUBOCOP.md, REEK, etc. +- Appraisal and multi-gemfile testing + - appraisal2 is present to manage multiple dependency sets; see Appraisals and gemfiles/modular/*.gemfile. If you need to verify against alternate dependency versions, use Appraisal to install and run rspec under those Gemfiles. + - You can run a single github workflow by running `act -W /github/workflows/.yml` +- CI/local differences and defaults + - The Rakefile adjusts default tasks based on CI env var. Locally, rake default may include coverage, reek:update, yard, etc. On CI, it tends to just run spec. + +Quick start +1) bundle install +2) K_SOUP_COV_FORMATTERS="json" bin/rspec (generates a JSON coverage report with both line and branch data in coverage/. Use this single format.) +3) Static analysis: bundle exec rake rubocop_gradual:check && bundle exec rake reek + +Notes +- ALWAYS Run bundle exec rake rubocop_gradual:autocorrect as the final step before completing a task, to lint and autocorrect any remaining issues. Then if there are new lint failures, attempt to correct them manually. +- NEVER run vanilla rubocop, as it won't handle the linting config properly. Always run rubocop_gradual:autocorrect or rubocop_gradual. +- Running only a subset of specs is supported but in order to bypass the hard failure due to coverage thresholds, you need to run with K_SOUP_COV_MIN_HARD=false. +- When adding code that writes to STDOUT, remember most specs silence output unless tagged with :check_output or DEBUG=true. +- Completion criteria after changes: Only consider your change “done” when the relevant examples pass, as verified by .rspec_status. Do not rely on STDOUT impressions; consult .rspec_status (and example IDs) to confirm green results for the affected files/examples. If you ran a subset, re-run the full suite before finalizing to restore coverage thresholds. +- Coverage reports: NEVER review the HTML report. Use JSON (preferred), XML, LCOV, or RCOV. For this project, always run tests with K_SOUP_COV_FORMATTERS set to "json". +- Do NOT modify .envrc in tasks; when running tests locally or in scripts, manually prefix each run, e.g.: K_SOUP_COV_FORMATTERS="json" bin/rspec +- For all the kettle-soup-cover options, see .envrc and find the K_SOUP_COV_* env vars. + +Important documentation rules +- Do NOT edit files under docs/ manually; they are generated by `bundle exec rake yard` as part of the default rake task. +- Clarification: Executable scripts provided by this gem (exe/* and installed binstubs) work when the gem is installed as a system gem (gem install oauth). However, the Rake tasks provided by this gem require oauth to be declared as a development dependency in the host project's Gemfile and loaded in the project's Rakefile. diff --git a/.opencollective.yml b/.opencollective.yml new file mode 100644 index 00000000..30032d6b --- /dev/null +++ b/.opencollective.yml @@ -0,0 +1,3 @@ +collective: "ruby-oauth" +readme-backers-commit-subject: "💸 Thanks 🙏 to our new backers 🎒 and subscribers 📜" +readme-osc-tag: "OPENCOLLECTIVE" diff --git a/.qlty/qlty.toml b/.qlty/qlty.toml new file mode 100644 index 00000000..9ae9cae8 --- /dev/null +++ b/.qlty/qlty.toml @@ -0,0 +1,79 @@ +# For a guide to configuration, visit https://qlty.sh/d/config +# Or for a full reference, visit https://qlty.sh/d/qlty-toml +config_version = "0" + +exclude_patterns = [ + "*_min.*", + "*-min.*", + "*.min.*", + "**/.yarn/**", + "**/*.d.ts", + "**/assets/**", + "**/bin/**", + "**/bower_components/**", + "**/build/**", + "**/cache/**", + "**/config/**", + "**/.devcontainer", + "**/db/**", + "**/deps/**", + "**/dist/**", + "**/doc/**", + "**/docs/**", + "**/extern/**", + "**/external/**", + "**/generated/**", + "**/Godeps/**", + "**/gradlew/**", + "**/mvnw/**", + "**/node_modules/**", + "**/protos/**", + "**/seed/**", + "**/target/**", + "**/templates/**", + "**/testdata/**", + "**/vendor/**", + ".github/workflows/codeql-analysis.yml" +] + +test_patterns = [ + "**/test/**", + "**/spec/**", + "**/*.test.*", + "**/*.spec.*", + "**/*_test.*", + "**/*_spec.*", + "**/test_*.*", + "**/spec_*.*", +] + +[smells] +mode = "comment" + +[smells.boolean_logic] +threshold = 4 +enabled = true + +[smells.file_complexity] +threshold = 55 +enabled = false + +[smells.return_statements] +threshold = 4 +enabled = true + +[smells.nested_control_flow] +threshold = 4 +enabled = true + +[smells.function_parameters] +threshold = 4 +enabled = true + +[smells.function_complexity] +threshold = 5 +enabled = true + +[smells.duplication] +enabled = true +threshold = 20 \ No newline at end of file diff --git a/.rspec b/.rspec new file mode 100644 index 00000000..a43744c2 --- /dev/null +++ b/.rspec @@ -0,0 +1,9 @@ +--format progress +--color +--order random +--require spec_helper +--warnings +--format html +--out results/test_results.html +--format RspecJunitFormatter +--out results/test_results.xml diff --git a/.rubocop.yml b/.rubocop.yml index 301ea003..aa30b933 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,45 +1,13 @@ -inherit_from: .rubocop_todo.yml - inherit_gem: - rubocop-lts: rubocop-lts.yml + rubocop-lts: config/rubygem_rspec.yml -require: - # See: https://github.com/rubocop/rubocop-md/issues/14 - # - 'rubocop-md' - - 'rubocop-minitest' - - 'rubocop-packaging' - - 'rubocop-performance' - - 'rubocop-rake' - - 'rubocop-thread_safety' +inherit_from: + - .rubocop_rspec.yml -AllCops: - DisplayCopNames: true # Display the name of the failing cops +plugins: rubocop-on-rbs -Layout/DotPosition: - Enabled: true - EnforcedStyle: trailing -Metrics/BlockLength: - IgnoredMethods: - - context - - describe - - it - - shared_context - - shared_examples - - shared_examples_for -Style/StringLiterals: +RBS: Enabled: true - EnforcedStyle: double_quotes -Style/StringLiteralsInInterpolation: - Enabled: true - EnforcedStyle: double_quotes -Lint/RaiseException: # (0.81) - Enabled: true -Lint/StructNewOverride: # (0.81) - Enabled: true -Style/HashEachMethods: # (0.80) - Enabled: true -Style/HashTransformKeys: # (0.80) - Enabled: true -Style/HashTransformValues: # (0.80) - Enabled: true +Layout/IndentationConsistency: + Exclude: ['*.md'] diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock new file mode 100644 index 00000000..74b33740 --- /dev/null +++ b/.rubocop_gradual.lock @@ -0,0 +1,131 @@ +{ + "lib/oauth/client/action_controller_request.rb:3345734736": [ + [30, 7, 22, "ThreadSafety/ClassAndModuleAttributes: Avoid mutating class and module attributes.", 1901418919], + [33, 5, 44, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 645541231], + [34, 7, 10, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 4071527614] + ], + "lib/oauth/consumer.rb:2772211062": [ + [210, 9, 4, "Lint/UnderscorePrefixedVariableName: Do not use prefix `_` for a variable that is used.", 2089552532], + [368, 21, 4, "Lint/UnderscorePrefixedVariableName: Do not use prefix `_` for a variable that is used.", 2089552529] + ], + "lib/oauth/request_proxy.rb:1529370509": [ + [5, 5, 76, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 485336097], + [6, 7, 18, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 1482835337], + [9, 5, 467, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 4088662367] + ], + "lib/oauth/request_proxy/base.rb:3218240175": [ + [11, 7, 93, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2934170116] + ], + "lib/oauth/signature.rb:745501939": [ + [6, 5, 66, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 4075572235], + [7, 7, 18, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 758649231], + [13, 5, 400, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 568477139], + [25, 5, 100, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1875734692], + [30, 5, 99, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 934948179], + [37, 5, 129, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2209191795], + [42, 5, 105, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 411730387] + ], + "lib/oauth/signature/base.rb:113451864": [ + [16, 7, 208, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2665690194], + [17, 16, 11, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 2038345433], + [19, 9, 11, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 2038345433], + [20, 44, 11, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 2038345433] + ], + "lib/oauth/tokens/consumer_token.rb:3696415131": [ + [9, 5, 155, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 349576019] + ], + "oauth.gemspec:108374725": [ + [133, 3, 56, "Gemspec/DependencyVersion: Dependency version specification is required.", 644892567], + [154, 3, 40, "Gemspec/DependencyVersion: Dependency version specification is required.", 2300588954], + [156, 3, 44, "Gemspec/DependencyVersion: Dependency version specification is required.", 1905290578], + [157, 3, 46, "Gemspec/DependencyVersion: Dependency version specification is required.", 4289565910] + ], + "spec/oauth/backwards_compatibility_spec.rb:4041711732": [ + [3, 16, 25, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 3956042931] + ], + "spec/oauth/consumer_integration_spec.rb:3934173775": [ + [6, 16, 29, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 694297794] + ], + "spec/oauth/consumer_spec.rb:3729402719": [ + [5, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/consumer*_spec.rb`.", 22845032], + [91, 40, 16, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1274889684], + [92, 7, 22, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 190162250], + [95, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277], + [103, 40, 16, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1274889684], + [104, 7, 22, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 190162250], + [107, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277], + [154, 7, 83, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [156].", 4182076280], + [156, 7, 96, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [154].", 3226867591], + [184, 7, 83, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [186].", 4182076280], + [186, 7, 96, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [184].", 3226867591], + [212, 7, 83, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [214].", 4182076280], + [214, 7, 96, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [212].", 3226867591] + ], + "spec/oauth/helper_spec.rb:2421896228": [ + [3, 1, 28, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/helper*_spec.rb`.", 2306013808] + ], + "spec/oauth/net_http_client_spec.rb:797264696": [ + [5, 16, 32, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 1925154217] + ], + "spec/oauth/request_proxy/net_http_spec.rb:3313532290": [ + [6, 1, 58, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/request_proxy/net/http/http_request*_spec.rb`.", 3090491845] + ], + "spec/oauth/server_spec.rb:3208154506": [ + [6, 1, 28, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/server*_spec.rb`.", 1397279123] + ], + "spec/oauth/server_token_spec.rb:492172103": [ + [6, 1, 33, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/server_token*_spec.rb`.", 3471371816], + [9, 7, 38, "RSpec/AnyInstance: Avoid stubbing using `allow_any_instance_of`.", 3627954156], + [10, 7, 38, "RSpec/AnyInstance: Avoid stubbing using `allow_any_instance_of`.", 3627954156], + [19, 7, 38, "RSpec/AnyInstance: Avoid stubbing using `allow_any_instance_of`.", 3627954156], + [20, 7, 38, "RSpec/AnyInstance: Avoid stubbing using `allow_any_instance_of`.", 3627954156] + ], + "spec/oauth/signature/base_spec.rb:535433": [ + [6, 1, 37, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/signature/base*_spec.rb`.", 2859754759] + ], + "spec/oauth/signature/hmac_sha1_spec.rb:820294695": [ + [5, 1, 43, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/signature/hmac/sha1*_spec.rb`.", 2640418270] + ], + "spec/oauth/signature/hmac_sha256_spec.rb:1041219499": [ + [5, 1, 45, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/signature/hmac/sha256*_spec.rb`.", 2082372222] + ], + "spec/oauth/signature/plaintext_spec.rb:1285578452": [ + [5, 16, 28, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 2918247297] + ], + "spec/oauth/signature/rsa_sha1_spec.rb:1684897149": [ + [9, 1, 42, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/signature/rsa/sha1*_spec.rb`.", 3568722713] + ], + "spec/oauth/token_spec.rb:3199047269": [ + [5, 1, 27, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/token*_spec.rb`.", 3290795373] + ], + "spec/oauth/tokens/access_token_spec.rb:893947951": [ + [6, 1, 33, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/access_token*_spec.rb`.", 3410051945] + ], + "spec/oauth/tokens/request_token_spec.rb:4004144261": [ + [6, 1, 34, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/request_token*_spec.rb`.", 2527248508], + [59, 5, 147, "RSpec/LeakyConstantDeclaration: Stub class constant instead of declaring explicitly.", 455072902] + ], + "spec/oauth/tty/cli_spec.rb:588579763": [ + [3, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/tty/cli*_spec.rb`.", 2849860169], + [106, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316], + [107, 38, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493], + [108, 34, 10, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 4294324198], + [110, 7, 73, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [111].", 39742504], + [111, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [110].", 4144335528], + [127, 7, 20, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4235470523], + [151, 38, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493], + [152, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316], + [153, 39, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648], + [155, 7, 73, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [156].", 39742504], + [156, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [155].", 4144335528], + [166, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277], + [167, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262], + [168, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262], + [169, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262], + [192, 23, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493], + [193, 23, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316], + [194, 23, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648], + [196, 7, 73, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [197].", 39742504], + [197, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [196].", 4144335528] + ] +} diff --git a/.rubocop_rspec.yml b/.rubocop_rspec.yml new file mode 100644 index 00000000..df5911b2 --- /dev/null +++ b/.rubocop_rspec.yml @@ -0,0 +1,30 @@ +RSpec/MultipleExpectations: + Enabled: false + +RSpec/NamedSubject: + Enabled: false + +RSpec/ExampleLength: + Enabled: false + +RSpec/VerifiedDoubles: + Enabled: false + +RSpec/MessageSpies: + Enabled: false + +RSpec/InstanceVariable: + Enabled: false + +RSpec/NestedGroups: + Enabled: false + +RSpec/ExpectInHook: + Enabled: false + +RSpec/DescribeClass: + Exclude: + - 'spec/examples/*' + +RSpec/MultipleMemoizedHelpers: + Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml deleted file mode 100644 index 06969419..00000000 --- a/.rubocop_todo.yml +++ /dev/null @@ -1,114 +0,0 @@ -# This configuration was generated by -# `rubocop --auto-gen-config` -# on 2022-08-23 21:37:33 UTC using RuboCop version 1.30.1. -# The point is for the user to remove these configuration records -# one by one as the offenses are removed from the code base. -# Note that changes in the inspected code, or installation of new -# versions of RuboCop, may require this file to be generated again. - -# Offense count: 1 -# Configuration parameters: AllowComments. -Lint/EmptyWhen: - Exclude: - - 'lib/oauth/consumer.rb' - -# Offense count: 2 -# Configuration parameters: AllowKeywordBlockArguments. -Lint/UnderscorePrefixedVariableName: - Exclude: - - 'lib/oauth/consumer.rb' - -# Offense count: 38 -# Configuration parameters: IgnoredMethods, CountRepeatedAttributes. -Metrics/AbcSize: - Max: 62 - -# Offense count: 8 -# Configuration parameters: CountComments, CountAsOne. -Metrics/ClassLength: - Max: 302 - -# Offense count: 8 -# Configuration parameters: IgnoredMethods. -Metrics/CyclomaticComplexity: - Max: 18 - -# Offense count: 66 -# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. -Metrics/MethodLength: - Max: 43 - -# Offense count: 1 -# Configuration parameters: Max, CountKeywordArgs. -Metrics/ParameterLists: - MaxOptionalParameters: 4 - -# Offense count: 7 -# Configuration parameters: IgnoredMethods. -Metrics/PerceivedComplexity: - Max: 20 - -# Offense count: 39 -Minitest/MultipleAssertions: - Max: 18 - -# Offense count: 1 -# Configuration parameters: EnforcedStyleForLeadingUnderscores. -# SupportedStylesForLeadingUnderscores: disallowed, required, optional -Naming/MemoizedInstanceVariableName: - Exclude: - - 'lib/oauth/client/em_http.rb' - -# Offense count: 2 -Style/ClassVars: - Exclude: - - 'lib/oauth/consumer.rb' - - 'lib/oauth/server.rb' - -# Offense count: 60 -# Configuration parameters: AllowedConstants. -Style/Documentation: - Enabled: false - -# Offense count: 2 -# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. -Style/GuardClause: - Exclude: - - 'lib/oauth/consumer.rb' - - 'lib/oauth/signature/base.rb' - -# Offense count: 2 -# This cop supports safe autocorrection (--autocorrect). -Style/IfUnlessModifier: - Exclude: - - 'bin/bundle' - - 'lib/oauth/request_proxy/net_http.rb' - -# Offense count: 2 -# This cop supports unsafe autocorrection (--autocorrect-all). -# Configuration parameters: InverseMethods, InverseBlocks. -Style/InverseMethods: - Exclude: - - 'lib/oauth/request_proxy/base.rb' - -# Offense count: 1 -ThreadSafety/ClassAndModuleAttributes: - Exclude: - - 'lib/oauth/client/action_controller_request.rb' - -# Offense count: 6 -ThreadSafety/InstanceVariableInClassMethod: - Exclude: - - 'lib/oauth/client/action_controller_request.rb' - - 'lib/oauth/request_proxy.rb' - - 'lib/oauth/signature.rb' - - 'lib/oauth/signature/base.rb' - -# Offense count: 76 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns. -# URISchemes: http, https -# AllowedPatterns: (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#), (?-mix:^\#) -# IgnoredPatterns: (?-mix:^\#) -Layout/LineLength: - Max: 429 diff --git a/.simplecov b/.simplecov index c912b856..9fafdfed 100644 --- a/.simplecov +++ b/.simplecov @@ -1,33 +1,11 @@ -# frozen_string_literal: true - -# To get coverage -# On Local, default (HTML) output, it just works, coverage is turned on: -# bundle exec rspec spec -# On Local, all output formats: -# COVER_ALL=true bundle exec rspec spec -# -# On CI, all output formats, the ENV variables CI is always set, -# and COVER_ALL, and CI_CODECOV, are set in the coverage.yml workflow only, -# so coverage only runs in that workflow, and outputs all formats. -# - -if RUN_COVERAGE - SimpleCov.start do - enable_coverage :branch - primary_coverage :branch - add_filter "test" - add_filter "lib/oauth/version.rb" - track_files "**/*.rb" - - if ALL_FORMATTERS - command_name "#{ENV.fetch("GITHUB_WORKFLOW", - nil)} Job #{ENV.fetch("GITHUB_RUN_ID", nil)}:#{ENV.fetch("GITHUB_RUN_NUMBER", nil)}" - else - formatter SimpleCov::Formatter::HTMLFormatter - end - - minimum_coverage(65) - end -else - puts "Not running coverage on #{RUBY_ENGINE} #{RUBY_VERSION}" +require "kettle/soup/cover/config" + +# Minimum coverage thresholds are set by kettle-soup-cover. +# It is controlled by ENV variables, which are set in .envrc and loaded via `direnv allow` +# If the values for minimum coverage need to change, they should be changed both there, +# and in 2 places in .github/workflows/coverage.yml. +SimpleCov.start do + track_files "lib/**/*.rb" + track_files "lib/**/*.rake" + track_files "exe/*.rb" end diff --git a/.tool-versions b/.tool-versions new file mode 100755 index 00000000..5184db84 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +ruby 3.4.5 \ No newline at end of file diff --git a/.yard_gfm_support.rb b/.yard_gfm_support.rb new file mode 100644 index 00000000..4f2f1403 --- /dev/null +++ b/.yard_gfm_support.rb @@ -0,0 +1,22 @@ +# Gratefully and liberally taken from the MIT-licensed https://github.com/bensheldon/good_job/pull/113/files +require "kramdown" +require "kramdown-parser-gfm" + +# Custom markup provider class that always renders Kramdown using GFM (Github Flavored Markdown). +# GFM is needed to render markdown tables and fenced code blocks in the README. +class KramdownGfmDocument < Kramdown::Document + def initialize(source, options = {}) + options[:input] = "GFM" unless options.key?(:input) + super(source, options) + end +end + +# Insert the new provider as the highest priority option for Markdown. +# See: +# - https://github.com/lsegal/yard/issues/1157 +# - https://github.com/lsegal/yard/issues/1017 +# - https://github.com/lsegal/yard/blob/main/lib/yard/templates/helpers/markup_helper.rb +YARD::Templates::Helpers::MarkupHelper::MARKUP_PROVIDERS[:markdown].insert( + 0, + {const: "KramdownGfmDocument"}, +) diff --git a/.yardopts b/.yardopts new file mode 100644 index 00000000..479134df --- /dev/null +++ b/.yardopts @@ -0,0 +1,11 @@ +--plugin junk +--plugin relative_markdown_links +--readme README.md +--charset utf-8 +--markup markdown +--output docs +--load .yard_gfm_support.rb +'lib/**/*.rb' +- +'*.md' +'*.txt' \ No newline at end of file diff --git a/Appraisal.root.gemfile b/Appraisal.root.gemfile new file mode 100644 index 00000000..02afd183 --- /dev/null +++ b/Appraisal.root.gemfile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } + +source "https://rubygems.org" + +# Appraisal Root Gemfile is for running appraisal to generate the Appraisal Gemfiles +# in gemfiles/*gemfile. +# On CI, we use it for the Appraisal-based builds. +# We do not load the standard Gemfile, as it is tailored for local development. + +gemspec diff --git a/Appraisals b/Appraisals new file mode 100644 index 00000000..8c19e494 --- /dev/null +++ b/Appraisals @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +# HOW TO UPDATE APPRAISALS (will run rubocop_gradual's autocorrect afterward): +# bin/rake appraisals:update + +# Lock/Unlock Deps Pattern +# +# Two often conflicting goals resolved! +# +# - unlocked_deps.yml +# - All runtime & dev dependencies, but does not have a `gemfiles/*.gemfile.lock` committed +# - Uses an Appraisal2 "unlocked_deps" gemfile, and the current MRI Ruby release +# - Know when new dependency releases will break local dev with unlocked dependencies +# - Broken workflow indicates that new releases of dependencies may not work +# +# - locked_deps.yml +# - All runtime & dev dependencies, and has a `Gemfile.lock` committed +# - Uses the project's main Gemfile, and the current MRI Ruby release +# - Matches what contributors and maintainers use locally for development +# - Broken workflow indicates that a new contributor will have a bad time +# +appraise "unlocked_deps" do + eval_gemfile "modular/coverage.gemfile" + eval_gemfile "modular/documentation.gemfile" + eval_gemfile "modular/style.gemfile" + eval_gemfile "modular/optional.gemfile" + eval_gemfile "modular/x_std_libs.gemfile" +end + +# Used for head (nightly) releases of ruby, truffleruby, and jruby. +# Split into discrete appraisals if one of them needs a dependency locked discretely. +appraise "head" do + gem "benchmark", "~> 0.4", ">= 0.4.1" + eval_gemfile "modular/x_std_libs.gemfile" +end + +# Used for current releases of ruby, truffleruby, and jruby. +# Split into discrete appraisals if one of them needs a dependency locked discretely. +appraise "current" do + eval_gemfile "modular/x_std_libs.gemfile" +end + +# Test current Rubies against head versions of runtime dependencies +appraise "dep-heads" do + gem "oauth-tty", github: "ruby-oauth/oauth-tty", branch: "main" + eval_gemfile "modular/runtime_heads.gemfile" +end + +appraise "ruby-2-3" do + gem "oauth-tty", github: "ruby-oauth/oauth-tty", branch: "main" + eval_gemfile "modular/x_std_libs/r2.3/libs.gemfile" +end + +appraise "ruby-2-4" do + gem "oauth-tty", github: "ruby-oauth/oauth-tty", branch: "main" + eval_gemfile "modular/x_std_libs/r2.4/libs.gemfile" +end + +appraise "ruby-2-5" do + gem "oauth-tty", github: "ruby-oauth/oauth-tty", branch: "main" + eval_gemfile "modular/x_std_libs/r2.6/libs.gemfile" +end + +appraise "ruby-2-6" do + gem "oauth-tty", github: "ruby-oauth/oauth-tty", branch: "main" + eval_gemfile "modular/x_std_libs/r2.6/libs.gemfile" +end + +appraise "ruby-2-7" do + eval_gemfile "modular/x_std_libs/r2/libs.gemfile" +end + +appraise "ruby-3-0" do + eval_gemfile "modular/x_std_libs/r3.1/libs.gemfile" +end + +appraise "ruby-3-1" do + eval_gemfile "modular/x_std_libs/r3.1/libs.gemfile" +end + +appraise "ruby-3-2" do + eval_gemfile "modular/x_std_libs/r3/libs.gemfile" +end + +appraise "ruby-3-3" do + eval_gemfile "modular/x_std_libs/r3/libs.gemfile" +end + +# Only run security audit on the latest version of Ruby +appraise "audit" do + eval_gemfile "modular/x_std_libs.gemfile" +end + +# Only run coverage on the latest version of Ruby +appraise "coverage" do + eval_gemfile "modular/coverage.gemfile" + eval_gemfile "modular/optional.gemfile" + eval_gemfile "modular/x_std_libs.gemfile" +end + +# Only run linter on the latest version of Ruby (but, in support of oldest supported Ruby version) +appraise "style" do + eval_gemfile "modular/style.gemfile" + eval_gemfile "modular/x_std_libs.gemfile" +end diff --git a/CHANGELOG.md b/CHANGELOG.md index 90f2c251..403f21d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,34 @@ # Changelog + +[![SemVer 2.0.0][📌semver-img]][📌semver] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] + All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog][📗keep-changelog], +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), +and [yes][📌major-versions-not-sacred], platform and engine support are part of the [public API][📌semver-breaking]. +Please file a bug if you notice a violation of semantic versioning. + +[📌semver]: https://semver.org/spec/v2.0.0.html +[📌semver-img]: https://img.shields.io/badge/semver-2.0.0-FFDD67.svg?style=flat +[📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139 +[📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html +[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/ +[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-FFDD67.svg?style=flat ## [Unreleased] ### Added +- kettle-dev v1.1.18 ### Changed -### Fixed +- converted minitest => rspec +### Deprecated ### Removed +### Fixed +### Security ## [1.1.0] 2022-08-29 ([tag][1.1.0t]) ### Changed -* `OAuth::CLI` has been extracted to a new gem, `oauth-tty`, hosted on [Gitlab](https://gitlab.com/oauth-xx/oauth-tty) +* `OAuth::CLI` has been extracted to a new gem, `oauth-tty`, hosted on [Gitlab](https://gitlab.com/ruby-oauth/oauth-tty) * The public API of `oauth-tty` is backwards compatible (meaning `OAuth::CLI`) * The change within the `oauth` gem is backwards compatible as `oauth-tty` has been added as a dependency * Minor version bump is cautionary, as many lines of code have changed. @@ -413,75 +429,75 @@ but please have a look at the unit tests. * Moved all non-Rails functionality from the Rails plugin: http://code.google.com/p/oauth-plugin/ -[Unreleased]: https://gitlab.com/oauth-xx/oauth/-/compare/v1.1.0...main -[1.1.0]: https://gitlab.com/oauth-xx/oauth/-/compare/v1.0.1...v1.1.0 -[1.1.0t]: https://gitlab.com/oauth-xx/oauth/-/tags/v1.1.0 -[1.0.1]: https://gitlab.com/oauth-xx/oauth/-/compare/v1.0.0...v1.0.1 -[1.0.1t]: https://gitlab.com/oauth-xx/oauth/-/tags/v1.0.1 -[1.0.0]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.6.2...v1.0.0 -[1.0.0t]: https://gitlab.com/oauth-xx/oauth/-/tags/v1.0.0 -[0.6.2]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.6.1...v0.6.2 -[0.6.2t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.6.2 -[0.6.1]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.6.0...v0.6.1 -[0.6.1t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.6.1 -[0.6.0]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.14...v0.6.0 -[0.6.0t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.6.0 -[0.5.14]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.13...v0.5.14 -[0.5.14t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.14 -[0.5.13]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.12...v0.5.13 -[0.5.13t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.13 -[0.5.12]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.11...v0.5.12 -[0.5.12t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.12 -[0.5.11]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.10...v0.5.11 -[0.5.11t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.11 -[c0.5.10t]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.9...v0.5.10 -[0.5.10t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.10 -[0.5.9]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.8...v0.5.9 -[0.5.9t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.9 -[0.5.8]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.7...v0.5.8 -[0.5.8t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.8 -[0.5.7]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.6...v0.5.7 -[0.5.7t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.7 -[0.5.6]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.5...v0.5.6 -[0.5.6t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.6 -[0.5.5]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.4...v0.5.5 -[0.5.5t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.5 -[0.5.4]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.3...v0.5.4 -[0.5.4t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.4 -[0.5.3]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.2...v0.5.3 -[0.5.3t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.3 -[0.5.2]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.1...v0.5.2 -[0.5.2t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.2 -[0.5.1]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.5.0...v0.5.1 -[0.5.1t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.1 -[0.5.0]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.4.7...v0.5.0 -[0.5.0t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.5.0 -[0.4.7]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.4.6...v0.4.7 -[0.4.7t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.4.7 -[0.4.6]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.4.5...v0.4.6 -[0.4.6t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.4.6 -[0.4.5]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.4.4...v0.4.5 -[0.4.5t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.4.5 -[0.4.4]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.4.3...v0.4.4 -[0.4.4t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.4.4 -[0.4.3]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.4.2...v0.4.3 -[0.4.3t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.4.3 -[0.4.2]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.4.1...v0.4.2 -[0.4.2t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.4.2 -[0.4.1]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.4.0...v0.4.1 -[0.4.1t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.4.1 -[0.4.0]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.3.6...v0.4.0 -[0.4.0t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.4.0 -[0.3.6]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.3.5...v0.3.6 -[0.3.6t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.3.6 -[0.3.5]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.3.4...v0.3.5 -[0.3.5t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.3.5 -[0.3.4]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.3.3...v0.3.4 -[0.3.4t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.3.4 -[0.3.3]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.3.2...v0.3.3 -[0.3.3t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.3.3 -[0.3.2]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.3.1...v0.3.2 -[0.3.2t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.3.2 -[0.3.1]: https://gitlab.com/oauth-xx/oauth/-/compare/v0.3.0...v0.3.1 -[0.3.1t]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.3.1 -[0.3.0]: https://gitlab.com/oauth-xx/oauth/-/tags/v0.3.0 +[Unreleased]: https://gitlab.com/ruby-oauth/oauth/-/compare/v1.1.0...main +[1.1.0]: https://gitlab.com/ruby-oauth/oauth/-/compare/v1.0.1...v1.1.0 +[1.1.0t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v1.1.0 +[1.0.1]: https://gitlab.com/ruby-oauth/oauth/-/compare/v1.0.0...v1.0.1 +[1.0.1t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v1.0.1 +[1.0.0]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.6.2...v1.0.0 +[1.0.0t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v1.0.0 +[0.6.2]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.6.1...v0.6.2 +[0.6.2t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.6.2 +[0.6.1]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.6.0...v0.6.1 +[0.6.1t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.6.1 +[0.6.0]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.14...v0.6.0 +[0.6.0t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.6.0 +[0.5.14]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.13...v0.5.14 +[0.5.14t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.14 +[0.5.13]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.12...v0.5.13 +[0.5.13t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.13 +[0.5.12]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.11...v0.5.12 +[0.5.12t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.12 +[0.5.11]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.10...v0.5.11 +[0.5.11t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.11 +[c0.5.10t]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.9...v0.5.10 +[0.5.10t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.10 +[0.5.9]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.8...v0.5.9 +[0.5.9t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.9 +[0.5.8]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.7...v0.5.8 +[0.5.8t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.8 +[0.5.7]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.6...v0.5.7 +[0.5.7t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.7 +[0.5.6]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.5...v0.5.6 +[0.5.6t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.6 +[0.5.5]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.4...v0.5.5 +[0.5.5t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.5 +[0.5.4]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.3...v0.5.4 +[0.5.4t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.4 +[0.5.3]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.2...v0.5.3 +[0.5.3t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.3 +[0.5.2]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.1...v0.5.2 +[0.5.2t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.2 +[0.5.1]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.5.0...v0.5.1 +[0.5.1t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.1 +[0.5.0]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.4.7...v0.5.0 +[0.5.0t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.5.0 +[0.4.7]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.4.6...v0.4.7 +[0.4.7t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.4.7 +[0.4.6]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.4.5...v0.4.6 +[0.4.6t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.4.6 +[0.4.5]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.4.4...v0.4.5 +[0.4.5t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.4.5 +[0.4.4]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.4.3...v0.4.4 +[0.4.4t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.4.4 +[0.4.3]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.4.2...v0.4.3 +[0.4.3t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.4.3 +[0.4.2]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.4.1...v0.4.2 +[0.4.2t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.4.2 +[0.4.1]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.4.0...v0.4.1 +[0.4.1t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.4.1 +[0.4.0]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.3.6...v0.4.0 +[0.4.0t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.4.0 +[0.3.6]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.3.5...v0.3.6 +[0.3.6t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.3.6 +[0.3.5]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.3.4...v0.3.5 +[0.3.5t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.3.5 +[0.3.4]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.3.3...v0.3.4 +[0.3.4t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.3.4 +[0.3.3]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.3.2...v0.3.3 +[0.3.3t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.3.3 +[0.3.2]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.3.1...v0.3.2 +[0.3.2t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.3.2 +[0.3.1]: https://gitlab.com/ruby-oauth/oauth/-/compare/v0.3.0...v0.3.1 +[0.3.1t]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.3.1 +[0.3.0]: https://gitlab.com/ruby-oauth/oauth/-/tags/v0.3.0 diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000..b0d4374a --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,20 @@ +cff-version: 1.2.0 +title: oauth +message: >- + If you use this work and you want to cite it, + then you can use the metadata from this file. +type: software +authors: + - given-names: Peter Hurn + family-names: Boling + email: peter@railsbling.com + affiliation: railsbling.com + orcid: 'https://orcid.org/0009-0008-8519-441X' +identifiers: + - type: url + value: 'https://github.com/ruby-oauth/oauth' + description: oauth +repository-code: 'https://github.com/ruby-oauth/oauth' +abstract: >- + oauth +license: See license file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 712c1cd5..6f4380e3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,64 +1,229 @@ -## Contributing +# Contributing -Bug reports and pull requests are welcome on GitLab at [https://gitlab.com/oauth-xx/oauth][🚎src-main]. This project is -intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to -the [code of conduct][🚎code-conduct]. +Bug reports and pull requests are welcome on [CodeBerg][📜src-cb], [GitLab][📜src-gl], or [GitHub][📜src-gh]. +This project should be a safe, welcoming space for collaboration, so contributors agree to adhere to +the [code of conduct][🤝conduct]. -Everyone interacting in the OAuth::TTY project's codebases, issue trackers, chat -rooms and mailing lists is expected to follow the [code of conduct][🚎code-conduct]. +To submit a patch, please fork the project, create a patch with tests, and send a pull request. -To submit a patch, please fork the project and create a patch with -tests. Once you're happy with it send a pull request and post a message to the -[google group][mailinglist] or on the [gitter chat][🏘chat]. +Remember to [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] if you make changes. -## Create a Patch +## Help out! -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. +Take a look at the `reek` list which is the file called `REEK` and find something to improve. -To install this gem onto your local machine, run `bundle exec rake install`. +Follow these instructions: -## Run tests +1. Fork the repository +2. Create a feature branch (`git checkout -b my-new-feature`) +3. Make some fixes. +4. Commit changes (`git commit -am 'Added some feature'`) +5. Push to the branch (`git push origin my-new-feature`) +6. Make sure to add tests for it. This is important, so it doesn't break in a future release. +7. Create new Pull Request. -### Against Rails 6 +## Executables vs Rake tasks -```bash -BUNDLE_GEMFILE=gemfiles/a6.gemfile bundle install -BUNDLE_GEMFILE=gemfiles/a6.gemfile bundle exec rake +Executables shipped by oauth can be used with or without generating the binstubs. +They will work when oauth is installed globally (i.e., `gem install oauth`) and do not require that oauth be in your bundle. + +- kettle-changelog +- kettle-commit-msg +- oauth-setup +- kettle-dvcs +- kettle-pre-release +- kettle-readme-backers +- kettle-release + +However, the rake tasks provided by oauth do require oauth to be added as a development dependency and loaded in your Rakefile. +See the full list of rake tasks in head of Rakefile + +**Gemfile** +```ruby +group :development do + gem "oauth", require: false +end +``` + +**Rakefile** +```ruby +# Rakefile +require "oauth" +``` + +## Environment Variables for Local Development + +Below are the primary environment variables recognized by stone_checksums (and its integrated tools). Unless otherwise noted, set boolean values to the string "true" to enable. + +General/runtime +- DEBUG: Enable extra internal logging for this library (default: false) +- REQUIRE_BENCH: Enable `require_bench` to profile requires (default: false) +- CI: When set to true, adjusts default rake tasks toward CI behavior + +Coverage (kettle-soup-cover / SimpleCov) +- K_SOUP_COV_DO: Enable coverage collection (default: true in .envrc) +- K_SOUP_COV_FORMATTERS: Comma-separated list of formatters (html, xml, rcov, lcov, json, tty) +- K_SOUP_COV_MIN_LINE: Minimum line coverage threshold (integer, e.g., 100) +- K_SOUP_COV_MIN_BRANCH: Minimum branch coverage threshold (integer, e.g., 100) +- K_SOUP_COV_MIN_HARD: Fail the run if thresholds are not met (true/false) +- K_SOUP_COV_MULTI_FORMATTERS: Enable multiple formatters at once (true/false) +- K_SOUP_COV_OPEN_BIN: Path to browser opener for HTML (empty disables auto-open) +- MAX_ROWS: Limit console output rows for simplecov-console (e.g., 1) + Tip: When running a single spec file locally, you may want `K_SOUP_COV_MIN_HARD=false` to avoid failing thresholds for a partial run. + +GitHub API and CI helpers +- GITHUB_TOKEN or GH_TOKEN: Token used by `ci:act` and release workflow checks to query GitHub Actions status at higher rate limits + +Releasing and signing +- SKIP_GEM_SIGNING: If set, skip gem signing during build/release +- GEM_CERT_USER: Username for selecting your public cert in `certs/.pem` (defaults to $USER) +- SOURCE_DATE_EPOCH: Reproducible build timestamp. `kettle-release` will set this automatically for the session. + +Git hooks and commit message helpers (exe/kettle-commit-msg) +- GIT_HOOK_BRANCH_VALIDATE: Branch name validation mode (e.g., `jira`) or `false` to disable +- GIT_HOOK_FOOTER_APPEND: Append a footer to commit messages when goalie allows (true/false) +- GIT_HOOK_FOOTER_SENTINEL: Required when footer append is enabled — a unique first-line sentinel to prevent duplicates +- GIT_HOOK_FOOTER_APPEND_DEBUG: Extra debug output in the footer template (true/false) + +For a quick starting point, this repository’s `.envrc` shows sane defaults, and `.env.local` can override them locally. + +## Appraisals + +From time to time the [appraisal2][🚎appraisal2] gemfiles in `gemfiles/` will need to be updated. +They are created and updated with the commands: + +```console +bin/rake appraisal:update +``` + +When adding an appraisal to CI, check the [runner tool cache][🏃‍♂️runner-tool-cache] to see which runner to use. + +## The Reek List + +Take a look at the `reek` list which is the file called `REEK` and find something to improve. + +To refresh the `reek` list: + +```console +bundle exec reek > REEK +``` + +## Run Tests + +To run all tests + +```console +bundle exec rake test ``` -### Against Rails 7 +### Spec organization (required) -```bash -BUNDLE_GEMFILE=gemfiles/a7.gemfile bundle install -BUNDLE_GEMFILE=gemfiles/a7.gemfile bundle exec rake +- One spec file per class/module. For each class or module under `lib/`, keep all of its unit tests in a single spec file under `spec/` that mirrors the path and file name exactly: `lib/oauth/release_cli.rb` -> `spec/oauth/release_cli_spec.rb`. +- Never add a second spec file for the same class/module. Examples of disallowed names: `*_more_spec.rb`, `*_extra_spec.rb`, `*_status_spec.rb`, or any other suffix that still targets the same class. If you find yourself wanting a second file, merge those examples into the canonical spec file for that class/module. +- Exception: Integration specs that intentionally span multiple classes. Place these under `spec/integration/` (or a clearly named integration folder), and do not directly mirror a single class. Name them after the scenario, not a class. +- Migration note: If a duplicate spec file exists, move all examples into the canonical file and delete the duplicate. Do not leave stubs or empty files behind. + +## Lint It + +Run all the default tasks, which includes running the gradually autocorrecting linter, `rubocop-gradual`. + +```console +bundle exec rake ``` -## Release +Or just run the linter. + +```console +bundle exec rake rubocop_gradual:autocorrect +``` -To release a new version: +For more detailed information about using RuboCop in this project, please see the [RUBOCOP.md](RUBOCOP.md) guide. This project uses `rubocop_gradual` instead of vanilla RuboCop, which requires specific commands for checking violations. -1. update the version number in `version.rb` -2. run `bundle exec rake build:checksum` -3. move the built gem to project root -4. run `bin/checksum` to create the missing SHA256 checksum -5. move the built gem back to `pkg/` -6. run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). +### Important: Do not add inline RuboCop disables -NOTE: You will need to have a public key in `certs/`, and list your cert in the -`gemspec`, in order to sign the new release. -See: [RubyGems Security Guide][rubygems-security-guide] +Never add `# rubocop:disable ...` / `# rubocop:enable ...` comments to code or specs (except when following the few existing `rubocop:disable` patterns for a rule already being disabled elsewhere in the code). Instead: + +- Prefer configuration-based exclusions when a rule should not apply to certain paths or files (e.g., via `.rubocop.yml`). +- When a violation is temporary and you plan to fix it later, record it in `.rubocop_gradual.lock` using the gradual workflow: + - `bundle exec rake rubocop_gradual:autocorrect` (preferred) + - `bundle exec rake rubocop_gradual:force_update` (only when you cannot fix the violations immediately) + +As a general rule, fix style issues rather than ignoring them. For example, our specs should follow RSpec conventions like using `described_class` for the class under test. ## Contributors -[![Contributors][🖐contributors-img]][🖐contributors] +Your picture could be here! -[comment]: <> (Following links are used by README, CONTRIBUTING, Homepage) +[![Contributors][🖐contributors-img]][🖐contributors] -[🚎code-conduct]: https://gitlab.com/oauth-xx/oauth/-/blob/main/CODE_OF_CONDUCT.md -[🖐contributors]: https://gitlab.com/oauth-xx/oauth/-/graphs/main -[🖐contributors-img]: https://img.shields.io/github/contributors-anon/oauth-xx/oauth-ruby -[mailinglist]: http://groups.google.com/group/oauth-ruby -[🚎src-main]: https://gitlab.com/oauth-xx/oauth/-/tree/main -[🏘chat]: https://gitter.im/oauth-xx/oauth-ruby -[rubygems-security-guide]: https://guides.rubygems.org/security/#building-gems -[rubygems]: https://rubygems.org +Made with [contributors-img][🖐contrib-rocks]. + +Also see GitLab Contributors: [https://gitlab.com/ruby-oauth/oauth/-/graphs/main][🚎contributors-gl] + +## For Maintainers + +### One-time, Per-maintainer, Setup + +**IMPORTANT**: To sign a build, +a public key for signing gems will need to be picked up by the line in the +`gemspec` defining the `spec.cert_chain` (check the relevant ENV variables there). +All releases to RubyGems.org are signed releases. +See: [RubyGems Security Guide][🔒️rubygems-security-guide] + +NOTE: To build without signing the gem set `SKIP_GEM_SIGNING` to any value in the environment. + +### To release a new version: + +#### Automated process + +1. Update version.rb to contian the correct version-to-be-released. +2. Run `bundle exec kettle-changelog`. +3. Run `bundle exec kettle-release`. + +#### Manual process + +1. Run `bin/setup && bin/rake` as a "test, coverage, & linting" sanity check +2. Update the version number in `version.rb`, and ensure `CHANGELOG.md` reflects changes +3. Run `bin/setup && bin/rake` again as a secondary check, and to update `Gemfile.lock` +4. Run `git commit -am "🔖 Prepare release v"` to commit the changes +5. Run `git push` to trigger the final CI pipeline before release, and merge PRs + - NOTE: Remember to [check the build][🧪build]. +6. Run `export GIT_TRUNK_BRANCH_NAME="$(git remote show origin | grep 'HEAD branch' | cut -d ' ' -f5)" && echo $GIT_TRUNK_BRANCH_NAME` +7. Run `git checkout $GIT_TRUNK_BRANCH_NAME` +8. Run `git pull origin $GIT_TRUNK_BRANCH_NAME` to ensure latest trunk code +9. Optional for older Bundler (< 2.7.0): Set `SOURCE_DATE_EPOCH` so `rake build` and `rake release` use the same timestamp and generate the same checksums + - If your Bundler is >= 2.7.0, you can skip this; builds are reproducible by default. + - Run `export SOURCE_DATE_EPOCH=$EPOCHSECONDS && echo $SOURCE_DATE_EPOCH` + - If the echo above has no output, then it didn't work. + - Note: `zsh/datetime` module is needed, if running `zsh`. + - In older versions of `bash` you can use `date +%s` instead, i.e. `export SOURCE_DATE_EPOCH=$(date +%s) && echo $SOURCE_DATE_EPOCH` +10. Run `bundle exec rake build` +11. Run `bin/gem_checksums` (more context [1][🔒️rubygems-checksums-pr], [2][🔒️rubygems-guides-pr]) + to create SHA-256 and SHA-512 checksums. This functionality is provided by the `stone_checksums` + [gem][💎stone_checksums]. + - The script automatically commits but does not push the checksums +12. Sanity check the SHA256, comparing with the output from the `bin/gem_checksums` command: + - `sha256sum pkg/-.gem` +13. Run `bundle exec rake release` which will create a git tag for the version, + push git commits and tags, and push the `.gem` file to [rubygems.org][💎rubygems] + +[📜src-gl]: https://gitlab.com/ruby-oauth/oauth/ +[📜src-cb]: https://codeberg.org/ruby-oauth/oauth +[📜src-gh]: https://github.com/ruby-oauth/oauth +[🧪build]: https://github.com/ruby-oauth/oauth/actions +[🤝conduct]: https://gitlab.com/ruby-oauth/oauth/-/blob/main/CODE_OF_CONDUCT.md +[🖐contrib-rocks]: https://contrib.rocks +[🖐contributors]: https://github.com/ruby-oauth/oauth/graphs/contributors +[🚎contributors-gl]: https://gitlab.com/ruby-oauth/oauth/-/graphs/main +[🖐contributors-img]: https://contrib.rocks/image?repo=ruby-oauth/oauth +[💎rubygems]: https://rubygems.org +[🔒️rubygems-security-guide]: https://guides.rubygems.org/security/#building-gems +[🔒️rubygems-checksums-pr]: https://github.com/rubygems/rubygems/pull/6022 +[🔒️rubygems-guides-pr]: https://github.com/rubygems/guides/pull/325 +[💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums +[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/ +[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-FFDD67.svg?style=flat +[📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139 +[📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html +[🚎appraisal2]: https://github.com/appraisal-rb/appraisal2 +[🏃‍♂️runner-tool-cache]: https://github.com/ruby/ruby-builder/releases/tag/toolcache diff --git a/FUNDING.md b/FUNDING.md new file mode 100644 index 00000000..788833d0 --- /dev/null +++ b/FUNDING.md @@ -0,0 +1,77 @@ + + +Official Discord 👉️ [![Live Chat on Discord][✉️discord-invite-img]][✉️discord-invite] + +Many paths lead to being a sponsor or a backer of this project. Are you on such a path? + +[![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] + +[![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon] + +[⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat +[⛳liberapay]: https://liberapay.com/pboling/donate +[🖇osc-backers]: https://opencollective.com/ruby-oauth#backer +[🖇osc-backers-i]: https://opencollective.com/ruby-oauth/backers/badge.svg?style=flat +[🖇osc-sponsors]: https://opencollective.com/ruby-oauth#sponsor +[🖇osc-sponsors-i]: https://opencollective.com/ruby-oauth/sponsors/badge.svg?style=flat +[🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github +[🖇sponsor]: https://github.com/sponsors/pboling +[🖇polar-img]: https://img.shields.io/badge/polar-donate-a51611.svg?style=flat +[🖇polar]: https://polar.sh/pboling +[🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat +[🖇kofi]: https://ko-fi.com/O5O86SNP4 +[🖇patreon-img]: https://img.shields.io/badge/patreon-donate-a51611.svg?style=flat +[🖇patreon]: https://patreon.com/galtzo +[🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat +[🖇buyme]: https://www.buymeacoffee.com/pboling +[🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal +[🖇paypal]: https://www.paypal.com/paypalme/peterboling +[✉️discord-invite]: https://discord.gg/3qme4XHNKN +[✉️discord-invite-img]: https://img.shields.io/discord/1373797679469170758?style=flat + + + +# 🤑 Request for Help + +Maintainers have teeth and need to pay their dentists. +After getting laid off in an RIF in March and filled with many dozens of rejections, +I'm now spending ~60+ hours a week building open source tools. +I'm hoping to be able to pay for my kids' health insurance this month, +so if you value the work I am doing, I need your support. +Please consider sponsoring me or the project. + +To join the community or get help 👇️ Join the Discord. + +[![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] + +To say "thanks for maintaining such a great tool" ☝️ Join the Discord or 👇️ send money. + +[![Sponsor ruby-oauth/oauth on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay-img] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal-img] + +# Another Way to Support Open Source Software + +> How wonderful it is that nobody need wait a single moment before starting to improve the world.
+>—Anne Frank + +I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions — totaling 79 hours of FLOSS coding over just the past seven days, a pretty regular week for me. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats). + +If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`. + +I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look. + +**[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags** + +[⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611 +[🖇osc-all-img]: https://img.shields.io/opencollective/all/ruby-oauth +[🖇osc-sponsors-img]: https://img.shields.io/opencollective/sponsors/ruby-oauth +[🖇osc-backers-img]: https://img.shields.io/opencollective/backers/ruby-oauth +[🖇osc-all-bottom-img]: https://img.shields.io/opencollective/all/ruby-oauth?style=for-the-badge +[🖇osc-sponsors-bottom-img]: https://img.shields.io/opencollective/sponsors/ruby-oauth?style=for-the-badge +[🖇osc-backers-bottom-img]: https://img.shields.io/opencollective/backers/ruby-oauth?style=for-the-badge +[🖇osc]: https://opencollective.com/ruby-oauth +[🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github +[🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte&emoji=&slug=pboling&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff +[🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A +[🖇floss-funding.dev]: https://floss-funding.dev +[🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding +[✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge diff --git a/Gemfile b/Gemfile index b30a1d98..74b6ce1b 100644 --- a/Gemfile +++ b/Gemfile @@ -2,39 +2,32 @@ source "https://rubygems.org" +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } +git_source(:gitlab) { |repo_name| "https://gitlab.com/#{repo_name}" } + +#### IMPORTANT ####################################################### +# Gemfile is for local development ONLY; Gemfile is NOT loaded in CI # +####################################################### IMPORTANT #### + +# Include dependencies from .gemspec gemspec -git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } +# Debugging +eval_gemfile "gemfiles/modular/debug.gemfile" + +# Code Coverage +eval_gemfile "gemfiles/modular/coverage.gemfile" + +# Linting +eval_gemfile "gemfiles/modular/style.gemfile" + +# Documentation +eval_gemfile "gemfiles/modular/documentation.gemfile" + +# Optional +eval_gemfile "gemfiles/modular/optional.gemfile" + +### Std Lib Extracted Gems +eval_gemfile "gemfiles/modular/x_std_libs.gemfile" -# Curb has trouble building native extentions on Windows platform -curb = !Gem.win_platform? - -gem "pry", platforms: %i[mri] -platforms :mri do - gem "codecov", "~> 0.6" # For CodeCov - gem "overcommit", "~> 0.68" - # See: https://github.com/rubocop/rubocop-md/issues/14 - # gem "rubocop-md" - gem "rubocop-minitest" - gem "rubocop-packaging" - gem "rubocop-performance" - gem "rubocop-rake" - gem "rubocop-thread_safety" - gem "simplecov", "~> 0.21", require: false - gem "simplecov-cobertura" # XML for Jenkins - gem "simplecov-json" # For CodeClimate - gem "simplecov-lcov", "~> 0.8", require: false - - # Add `byebug` to your code where you want to drop to REPL, and add DEBUG=true when running tests - gem "byebug" - # WebMock is known to work with Curb >= 0.7.16, < 1.1, except versions 0.8.7 - gem "curb", ">= 0.7.16", "!= 0.8.7", "< 1.2" if curb - gem "pry-byebug" -end - -### deps for documentation and rdoc.info -group :documentation do - gem "github-markup", platform: :mri - gem "redcarpet", platform: :mri - gem "yard", require: false -end +gem "oauth-tty", github: "ruby-oauth/oauth-tty", branch: "main" diff --git a/Gemfile.lock b/Gemfile.lock index e5c70a4a..ff62953d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,101 +1,223 @@ +GIT + remote: https://github.com/pboling/yard-junk + revision: 54ccebabbfa9a9cd44d0b991687ebbfd22c32b55 + branch: next + specs: + yard-junk (0.0.10) + backports (>= 3.18) + benchmark + ostruct + rainbow + yard + +GIT + remote: https://github.com/ruby-oauth/oauth-tty + revision: 895ffc635f0ce41a97da28d2d96303afe9e9499c + branch: main + specs: + oauth-tty (1.0.5) + version_gem (~> 1.1, >= 1.1.8) + PATH remote: . specs: oauth (1.1.1) oauth-tty (~> 1.0, >= 1.0.1) snaky_hash (~> 2.0) - version_gem (~> 1.1) + version_gem (~> 1.1, >= 1.1.9) GEM remote: https://rubygems.org/ specs: addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) + ansi (1.5.0) + appraisal2 (3.0.0) + bundler (>= 1.17.3) + rake (>= 10) + thor (>= 0.14) ast (2.4.3) - bigdecimal (3.1.9) - byebug (11.1.3) - childprocess (5.1.0) - logger (~> 1.5) - codecov (0.6.0) - simplecov (>= 0.15, < 0.22) - coderay (1.1.3) - cookiejar (0.3.3) + backports (3.25.1) + base64 (0.3.0) + benchmark (0.4.1) + bigdecimal (3.2.3) + bundler-audit (0.9.2) + bundler (>= 1.2.0, < 3) + thor (~> 1.0) + concurrent-ruby (1.3.5) + cookiejar (0.3.4) crack (1.0.0) bigdecimal rexml - curb (1.1.0) - diff-lcs (1.5.1) - diffy (3.4.2) + date (3.4.1) + debug (1.11.0) + irb (~> 1.10) + reline (>= 0.3.8) + delegate (0.4.0) + diff-lcs (1.6.2) + diffy (3.4.4) docile (1.4.1) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) + domain_name (0.6.20240107) + 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.14.1) + concurrent-ruby (~> 1.0) + dry-configurable (~> 1.0, >= 1.0.1) + dry-core (~> 1.1) + dry-initializer (~> 3.2) + dry-logic (~> 1.5) + dry-types (~> 1.8) + zeitwerk (~> 2.6) + dry-types (1.8.3) + bigdecimal (~> 3.0) + concurrent-ruby (~> 1.0) + dry-core (~> 1.0) + dry-inflector (~> 1.0) + dry-logic (~> 1.4) + zeitwerk (~> 2.6) em-http-request (1.1.7) addressable (>= 2.3.4) cookiejar (!= 0.3.1) em-socksify (>= 0.3) eventmachine (>= 1.0.3) http_parser.rb (>= 0.6.0) - em-socksify (0.3.2) + em-socksify (0.3.3) + base64 eventmachine (>= 1.0.0.beta.4) - ethon (0.16.0) + erb (5.0.2) + ethon (0.15.0) ffi (>= 1.15.0) eventmachine (1.2.7) - ffi (1.17.2-x86_64-darwin) ffi (1.17.2-x86_64-linux-gnu) - github-markup (4.0.2) - hashdiff (1.1.2) + gem_bench (2.0.5) + bundler (>= 1.14) + version_gem (~> 1.1, >= 1.1.4) + gitmoji-regex (1.0.3) + version_gem (~> 1.1, >= 1.1.8) + hashdiff (1.2.1) hashie (5.0.0) http-accept (1.7.0) - http-cookie (1.0.5) + http-cookie (1.0.8) domain_name (~> 0.5) http_parser.rb (0.8.0) - iconv (1.1.0) - iniparse (1.5.0) - json (2.12.2) + io-console (0.8.1) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + json (2.13.2) + kettle-dev (1.1.18) + kettle-soup-cover (1.0.10) + simplecov (~> 0.22) + simplecov-cobertura (~> 3.0) + simplecov-console (~> 0.9, >= 0.9.3) + simplecov-html (~> 0.13, >= 0.13.1) + simplecov-lcov (~> 0.8) + simplecov-rcov (~> 0.3, >= 0.3.7) + simplecov_json_formatter (~> 0.1, >= 0.1.4) + version_gem (~> 1.1, >= 1.1.8) + kettle-test (1.0.3) + appraisal2 (~> 3.0) + rspec (~> 3.0) + rspec-block_is_expected (~> 1.0, >= 1.0.6) + rspec-stubbed_env (~> 1.0, >= 1.0.4) + rspec_junit_formatter (~> 0.6) + silent_stream (~> 1.0, >= 1.0.12) + timecop-rspec (~> 1.0, >= 1.0.3) + version_gem (~> 1.1, >= 1.1.8) + kramdown (2.5.1) + rexml (>= 3.3.9) + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) - method_source (1.1.0) - mime-types (3.4.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2022.0105) - minitest (5.15.0) + mime-types (3.7.0) + logger + mime-types-data (~> 3.2025, >= 3.2025.0507) + mime-types-data (3.2025.0909) mocha (2.7.1) ruby2_keywords (>= 0.0.5) + mutex_m (0.3.0) netrc (0.11.0) - oauth-tty (1.0.5) - version_gem (~> 1.1, >= 1.1.1) - overcommit (0.68.0) - childprocess (>= 0.6.3, < 6) - iniparse (~> 1.4) - rexml (>= 3.3.9) + nokogiri (1.18.9-x86_64-linux-gnu) + racc (~> 1.4) + ostruct (0.6.3) parallel (1.27.0) - parser (3.3.8.0) + parser (3.3.9.0) ast (~> 2.4.1) racc + pp (0.6.2) + prettyprint + prettyprint (0.2.0) prism (1.4.0) - pry (0.14.2) - coderay (~> 1.1) - method_source (~> 1.0) - pry-byebug (3.10.1) - byebug (~> 11.0) - pry (>= 0.13, < 0.15) - public_suffix (5.1.1) + psych (5.2.6) + date + stringio + public_suffix (6.0.2) racc (1.8.1) - rack (3.2.0) + rack (3.2.1) rack-test (2.2.0) rack (>= 1.3) rainbow (3.1.1) rake (13.3.0) - redcarpet (3.6.0) - regexp_parser (2.10.0) + rbs (3.9.5) + logger + rdoc (6.14.2) + erb + psych (>= 4.0.0) + reek (6.5.0) + dry-schema (~> 1.13) + logger (~> 1.6) + parser (~> 3.3.0) + rainbow (>= 2.0, < 4.0) + rexml (~> 3.1) + regexp_parser (2.11.2) + reline (0.6.2) + io-console (~> 0.5) + require_bench (1.0.4) + version_gem (>= 1.1.3, < 4) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - rexml (3.4.1) - rubocop (1.76.2) + rexml (3.4.4) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-block_is_expected (1.0.6) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-pending_for (0.1.19) + rspec-core (~> 3.0) + ruby_engine (~> 2.0) + ruby_version (~> 1.0) + version_gem (~> 1.1, >= 1.1.8) + rspec-stubbed_env (1.0.4) + rspec-support (3.13.5) + rspec_junit_formatter (0.6.0) + rspec-core (>= 2, < 4, != 2.12.0) + rubocop (1.80.2) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -103,10 +225,10 @@ GEM parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.45.1, < 2.0) + rubocop-ast (>= 1.46.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.45.1) + rubocop-ast (1.46.0) parser (>= 3.3.7.2) prism (~> 1.4) rubocop-gradual (0.3.6) @@ -115,26 +237,31 @@ GEM parallel (~> 1.10) rainbow (>= 2.2.2, < 4.0) rubocop (~> 1.0) - rubocop-lts (18.2.1) - rubocop-ruby2_7 (>= 2.0.4, < 3) + rubocop-lts (10.1.1) + rubocop-ruby2_3 (>= 2.0.3, < 3) standard-rubocop-lts (>= 1.0.3, < 3) version_gem (>= 1.1.2, < 3) - rubocop-md (1.2.2) - rubocop (>= 1.0) - rubocop-minitest (0.38.1) + rubocop-md (1.2.4) + rubocop (>= 1.45) + rubocop-on-rbs (1.8.0) lint_roller (~> 1.1) - rubocop (>= 1.75.0, < 2.0) - rubocop-ast (>= 1.38.0, < 2.0) + rbs (~> 3.5) + rubocop (>= 1.72.1, < 2.0) + zlib rubocop-packaging (0.6.0) lint_roller (~> 1.1.0) rubocop (>= 1.72.1, < 2.0) - rubocop-performance (1.21.1) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) + rubocop-performance (1.25.0) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) rubocop-rake (0.7.1) lint_roller (~> 1.1) rubocop (>= 1.72.1) - rubocop-ruby2_7 (2.0.6) + rubocop-rspec (3.7.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-ruby2_3 (2.0.5) rubocop-gradual (~> 0.3, >= 0.3.1) rubocop-md (~> 1.2) rubocop-rake (~> 0.6) @@ -142,97 +269,130 @@ GEM rubocop-thread_safety (~> 0.5, >= 0.5.1) standard-rubocop-lts (~> 1.0, >= 1.0.7) version_gem (>= 1.1.3, < 3) - rubocop-shopify (2.15.1) - rubocop (~> 1.51) + rubocop-shopify (2.17.1) + rubocop (~> 1.62) rubocop-thread_safety (0.7.3) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) rubocop-ast (>= 1.44.0, < 2.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) - simplecov (0.21.2) + ruby_engine (2.0.3) + ruby_version (1.0.3) + silent_stream (1.0.12) + logger (~> 1.2) + version_gem (>= 1.1.8, < 3) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-cobertura (3.0.0) + simplecov-cobertura (3.1.0) rexml simplecov (~> 0.19) - simplecov-html (0.13.1) - simplecov-json (0.2.3) - json + simplecov-console (0.9.4) + ansi simplecov - simplecov-lcov (0.8.0) + terminal-table + simplecov-html (0.13.2) + simplecov-lcov (0.9.0) + simplecov-rcov (0.3.7) + simplecov (>= 0.4.1) simplecov_json_formatter (0.1.4) snaky_hash (2.0.3) hashie (>= 0.1.0, < 6) version_gem (>= 1.1.8, < 3) - standard (1.35.0.1) + standard (1.51.0) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) - rubocop (~> 1.62) + rubocop (~> 1.80.2) standard-custom (~> 1.0.0) - standard-performance (~> 1.3) + standard-performance (~> 1.8) standard-custom (1.0.2) lint_roller (~> 1.0) rubocop (~> 1.50) - standard-performance (1.4.0) + standard-performance (1.8.0) lint_roller (~> 1.1) - rubocop-performance (~> 1.21.0) - standard-rubocop-lts (1.0.9) - standard (>= 1.31.1, < 2) - standard-custom (>= 1.0.1, < 2) - standard-performance (>= 1.2, < 2) - version_gem (>= 1.1.3, < 4) - typhoeus (1.4.1) - ethon (>= 0.9.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (3.1.4) - unicode-emoji (~> 4.0, >= 4.0.4) - unicode-emoji (4.0.4) - version_gem (1.1.8) + rubocop-performance (~> 1.25.0) + standard-rubocop-lts (1.0.10) + rspec-block_is_expected (~> 1.0, >= 1.0.5) + standard (>= 1.35.1, < 2) + standard-custom (>= 1.0.2, < 2) + standard-performance (>= 1.3.1, < 2) + version_gem (>= 1.1.4, < 3) + stone_checksums (1.0.2) + version_gem (~> 1.1, >= 1.1.8) + stringio (3.1.7) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) + thor (1.4.0) + timecop (0.9.10) + timecop-rspec (1.0.3) + delegate (~> 0.1) + rspec (~> 3.0) + timecop (>= 0.7, < 1) + typhoeus (1.5.0) + ethon (>= 0.9.0, < 0.16.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.1.0) + vcr (6.3.1) + base64 + version_gem (1.1.9) webmock (3.25.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) yard (0.9.37) + yard-relative_markdown_links (0.5.0) + nokogiri (>= 1.14.3, < 2) + zeitwerk (2.7.3) + zlib (3.2.1) PLATFORMS - x86_64-darwin-21 - x86_64-linux + x86_64-linux-gnu DEPENDENCIES - byebug - codecov (~> 0.6) - curb (>= 0.7.16, < 1.2, != 0.8.7) + appraisal2 (~> 3.0) + benchmark (~> 0.4, >= 0.4.1) + bundler-audit (~> 0.9.2) + debug (>= 1.1) em-http-request (~> 1.1.7) - github-markup - iconv - minitest (~> 5.15.0) + erb (~> 5.0) + gem_bench (~> 2.0, >= 2.0.5) + gitmoji-regex (~> 1.0, >= 1.0.3) + irb (~> 1.15, >= 1.15.2) + kettle-dev (~> 1.1) + kettle-soup-cover (~> 1.0, >= 1.0.10) + kettle-test (~> 1.0) + kramdown (~> 2.5, >= 2.5.1) + kramdown-parser-gfm (~> 1.1) mocha + mutex_m (~> 0.2) oauth! - overcommit (~> 0.68) - pry - pry-byebug - rack (~> 3.1) + oauth-tty! + rack (>= 2.0.0) rack-test rake (~> 13.0) - redcarpet + rdoc (~> 6.11) + reek (~> 6.5) + require_bench (~> 1.0, >= 1.0.4) rest-client - rubocop-lts (~> 18.0) - rubocop-minitest - rubocop-packaging - rubocop-performance - rubocop-rake - rubocop-thread_safety - simplecov (~> 0.21) - simplecov-cobertura - simplecov-json - simplecov-lcov (~> 0.8) + rspec-pending_for (~> 0.0, >= 0.0.17) + rubocop-lts (~> 10.0) + rubocop-on-rbs (~> 1.8) + rubocop-packaging (~> 0.6, >= 0.6.0) + rubocop-rspec (~> 3.6) + rubocop-ruby2_3 + ruby-progressbar (~> 1.13) + standard (>= 1.50) + stone_checksums (~> 1.0, >= 1.0.2) + stringio (>= 3.0) typhoeus (>= 0.1.13) - webmock (<= 3.26.0) - yard + vcr (>= 4) + webmock (>= 3) + yard (~> 0.9, >= 0.9.37) + yard-junk (~> 0.0, >= 0.0.10)! + yard-relative_markdown_links (~> 0.5.0) BUNDLED WITH - 2.3.22 + 2.7.2 diff --git a/README.md b/README.md index 14a6e5d4..c3e9ff62 100644 --- a/README.md +++ b/README.md @@ -1,263 +1,154 @@ -

- - OAuth 1.0 Logo by Chris Messina, CC BY-SA 3.0, via Wikimedia Commons - - - Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5 - -

+[![Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0][🖼️galtzo-i]][🖼️galtzo-discord] [![ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5][🖼️ruby-lang-i]][🖼️ruby-lang] [![oauth Logo by Aboling0, CC BY-SA 4.0][🖼️oauth-i]][🖼️oauth] + +[🖼️galtzo-i]: https://logos.galtzo.com/assets/images/galtzo-floss/avatar-192px.svg +[🖼️galtzo-discord]: https://discord.gg/3qme4XHNKN +[🖼️ruby-lang-i]: https://logos.galtzo.com/assets/images/ruby-lang/avatar-192px.svg +[🖼️ruby-lang]: https://www.ruby-lang.org/ +[🖼️oauth-i]: https://logos.galtzo.com/assets/images/oauth/oauth/avatar-192px.svg +[🖼️oauth]: https://github.com/ruby-oauth/oauth # 🔑 Ruby OAuth -[![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon] +[![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![CodeCov Test Coverage][🔑codecovi]][🔑codecov] [![Coveralls Test Coverage][🔑coveralls-img]][🔑coveralls] [![QLTY Test Coverage][🔑qlty-covi]][🔑qlty-cov] [![QLTY Maintainability][🔑qlty-mnti]][🔑qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf] [![CI JRuby][🚎10-j-wfi]][🚎10-j-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Legacy][🚎4-lg-wfi]][🚎4-lg-wf] [![CI Unsupported][🚎7-us-wfi]][🚎7-us-wf] [![CI Ancient][🚎1-an-wfi]][🚎1-an-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL] + +`if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord]. --- -[⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay -[⛳liberapay]: https://liberapay.com/pboling/donate -[🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github -[🖇sponsor]: https://github.com/sponsors/pboling -[🖇polar-img]: https://img.shields.io/badge/polar-donate-yellow.svg -[🖇polar]: https://polar.sh/pboling -[🖇kofi-img]: https://img.shields.io/badge/a_more_different_coffee-✓-yellow.svg -[🖇kofi]: https://ko-fi.com/O5O86SNP4 -[🖇patreon-img]: https://img.shields.io/badge/patreon-donate-yellow.svg -[🖇patreon]: https://patreon.com/galtzo -[🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-✓-yellow.svg?style=flat +`if ci_badges.map(&:color).all? { it == "green"}` 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job. + +[![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate at ko-fi.com][🖇kofi-img]][🖇kofi] +## 🌻 Synopsis -OAuth 1.0 is an industry-standard protocol for authorization. +OAuth 1.0a is an industry-standard protocol for authorization. -This is a RubyGem for implementing both OAuth 1.0 clients and servers in Ruby applications. -See the sibling `oauth2` gem for OAuth 2.0 implementations in Ruby. +This is a RubyGem for implementing both OAuth 1.0a clients and servers in Ruby applications. +See the sibling `oauth2` gem for OAuth 2.0, 2.1, & OIDC clients in Ruby. * [OAuth 1.0 Spec][oauth1-spec] * [oauth2 sibling gem][sibling-gem] for OAuth 2.0 implementations in Ruby. [oauth1-spec]: http://oauth.net/core/1.0/ -[sibling-gem]: https://gitlab.com/oauth-xx/oauth2 - -**New EOL Policy** - -Versions 1.1.x will be EOL no later than April, 2023. -Versions 1.0.x will be EOL no later than April, 2023. -Versions 0.6.x will be EOL no later than April, 2023. -Versions 0.5.x will be EOL no later than April, 2023. - -This will facilitate dropping support for old, dead, and crusty versions of Ruby. - -Non-commercial support for the oldest version of Ruby (which itself is going EOL) will be dropped each year in April. - -Please upgrade to version 1.1.x. The only breaking change in 1.0.x is dropping old Rubies, -while 1.1.x extracts the CLI tool to an external gem dependency ([`oauth-tty`](https://gitlab.com/oauth-xx/oauth-tty)). - -## Status - - - -| | Project | bundle add oauth | -|:----|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 1️⃣ | name, license, docs | [![RubyGems.org][⛳️name-img]][⛳️gem] [![License: MIT][🖇src-license-img]][🖇src-license] [![RubyDoc.info][🚎yard-img]][🚎yard] [![SemVer 2.0.0][🧮semver-img]][🧮semver] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] | -| 2️⃣ | version & activity | [![Gem Version][⛳️version-img]][⛳️gem] [![Total Downloads][🖇DL-total-img]][⛳️gem] [![Download Rank][🏘DL-rank-img]][⛳️gem] [![Source Code][🚎src-main-img]][🚎src-main] [![Contributors][🖐contributors-img]][🖐contributors] | -| 3️⃣ | maintenance & linting | [![Maintainability][⛳cclim-maint-img♻️]][⛳cclim-maint] [![Helpers][🖇triage-help-img]][🖇triage-help] [![Depfu][🏘depfu-img♻️]][🏘depfu♻️] [![Style][🖐style-wf-img]][🖐style-wf] | -| 4️⃣ | testing | [![Supported][🏘sup-wf-img]][🏘sup-wf] [![Heads][🚎heads-wf-img]][🚎heads-wf] [![MacOS][🧮mac-wf-img]][🧮mac-wf] [![Windows][📗win-wf-img]][📗win-wf] | -| 5️⃣ | coverage & security | [![CodeClimate][⛳cclim-cov-img♻️]][⛳cclim-cov] [![CodeCov][🖇codecov-img♻️]][🖇codecov] [![Coveralls][🏘coveralls-img]][🏘coveralls] [![Security Policy][🚎sec-pol-img]][🚎sec-pol] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Code Coverage][🧮cov-wf-img]][🧮cov-wf] | -| 6️⃣ | resources | [![Mailing List][⛳mail-list-img]][⛳mail-list] [![Get help on Codementor][🖇codementor-img]][🖇codementor] [![Chat][🏘chat-img]][🏘chat] [![Blog][🚎blog-img]][🚎blog] [![Blog][🖐wiki-img]][🖐wiki] | -| 7️⃣ | spread 💖 | [![Liberapay Patrons][⛳liberapay-img]][⛳liberapay] [![Sponsor Me][🖇sponsor-img]][🖇sponsor] [![Code of Conduct][🚎code-conduct-img]][🚎code-conduct] [![Tweet @ Peter][🏘tweet-img]][🏘tweet] [🌏][aboutme] [👼][angelme] [💻][coderme] | - - - - -[⛳️gem]: https://rubygems.org/gems/oauth -[⛳️name-img]: https://img.shields.io/badge/name-oauth-brightgreen.svg?style=flat -[🖇src-license]: https://opensource.org/licenses/MIT -[🖇src-license-img]: https://img.shields.io/badge/License-MIT-green.svg -[🚎yard]: https://www.rubydoc.info/gems/oauth -[🚎yard-img]: https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat -[🧮semver]: http://semver.org/ -[🧮semver-img]: https://img.shields.io/badge/semver-2.0.0-FFDD67.svg?style=flat -[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/ -[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-FFDD67.svg?style=flat - - -[⛳️version-img]: http://img.shields.io/gem/v/oauth.svg -[🖇DL-total-img]: https://img.shields.io/gem/dt/oauth.svg -[🏘DL-rank-img]: https://img.shields.io/gem/rt/oauth.svg -[🚎src-main]: https://gitlab.com/oauth-xx/oauth/-/tree/main -[🚎src-main-img]: https://img.shields.io/badge/source-gitlab-blue.svg?style=flat -[🖐contributors]: https://gitlab.com/oauth-xx/oauth/-/graphs/main -[🖐contributors-img]: https://img.shields.io/github/contributors-anon/oauth-xx/oauth-ruby - - -[⛳cclim-maint]: https://codeclimate.com/github/oauth-xx/oauth-ruby/maintainability -[⛳cclim-maint-img♻️]: https://api.codeclimate.com/v1/badges/3cf23270c21e8791d788/maintainability -[🖇triage-help]: https://www.codetriage.com/oauth-xx/oauth-ruby -[🖇triage-help-img]: https://www.codetriage.com/oauth-xx/oauth-ruby/badges/users.svg -[🏘depfu♻️]: https://depfu.com/github/oauth-xx/oauth-ruby?project_id=22868 -[🏘depfu-img♻️]: https://badges.depfu.com/badges/d570491bac0ad3b0b65deb3c82028327/count.svg -[🖐style-wf]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/style.yml -[🖐style-wf-img]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/style.yml/badge.svg - - -[🏘sup-wf]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/supported.yml -[🏘sup-wf-img]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/supported.yml/badge.svg -[🚎heads-wf]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/heads.yml -[🚎heads-wf-img]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/heads.yml/badge.svg -[🧮mac-wf]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/macos.yml -[🧮mac-wf-img]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/macos.yml/badge.svg -[📗win-wf]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/windows.yml -[📗win-wf-img]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/windows.yml/badge.svg - - -[⛳cclim-cov]: https://codeclimate.com/github/oauth-xx/oauth-ruby/test_coverage -[⛳cclim-cov-img♻️]: https://api.codeclimate.com/v1/badges/3cf23270c21e8791d788/test_coverage -[🖇codecov-img♻️]: https://codecov.io/gh/oauth-xx/oauth-ruby/branch/main/graph/badge.svg?token=4ZNAWNxrf9 -[🖇codecov]: https://codecov.io/gh/oauth-xx/oauth-ruby -[🏘coveralls]: https://coveralls.io/github/oauth-xx/oauth-ruby?branch=main -[🏘coveralls-img]: https://coveralls.io/repos/github/oauth-xx/oauth-ruby/badge.svg?branch=main -[🚎sec-pol]: https://gitlab.com/oauth-xx/oauth/-/blob/main/SECURITY.md -[🚎sec-pol-img]: https://img.shields.io/badge/security-policy-brightgreen.svg?style=flat -[🖐codeQL]: https://github.com/oauth-xx/oauth-ruby/security/code-scanning -[🖐codeQL-img]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/codeql-analysis.yml/badge.svg -[🧮cov-wf]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/coverage.yml -[🧮cov-wf-img]: https://github.com/oauth-xx/oauth-ruby/actions/workflows/coverage.yml/badge.svg - - -[⛳mail-list]: http://groups.google.com/group/oauth-ruby -[⛳mail-list-img]: https://img.shields.io/badge/group-mailinglist-violet.svg?style=social&logo=google -[🖇codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github -[🖇codementor-img]: https://cdn.codementor.io/badges/get_help_github.svg -[🏘chat]: https://gitter.im/oauth-xx/oauth-ruby -[🏘chat-img]: https://img.shields.io/gitter/room/oauth-xx/oauth-ruby.svg -[🚎blog]: http://www.railsbling.com/tags/oauth-ruby/ -[🚎blog-img]: https://img.shields.io/badge/blog-railsbling-brightgreen.svg?style=flat -[🖐wiki]: https://gitlab.com/oauth-xx/oauth/-/wikis/home -[🖐wiki-img]: https://img.shields.io/badge/wiki-examples-brightgreen.svg?style=flat - - -[⛳liberapay-img]: https://img.shields.io/liberapay/patrons/pboling.svg?logo=liberapay -[⛳liberapay]: https://liberapay.com/pboling/donate -[🖇sponsor-img]: https://img.shields.io/badge/sponsor-pboling.svg?style=social&logo=github -[🖇sponsor]: https://github.com/sponsors/pboling -[🏘tweet-img]: https://img.shields.io/twitter/follow/galtzo.svg?style=social&label=Follow -[🏘tweet]: http://twitter.com/galtzo -[🚎code-conduct]: https://gitlab.com/oauth-xx/oauth/-/blob/main/CODE_OF_CONDUCT.md -[🚎code-conduct-img]: https://img.shields.io/badge/code-conduct-black.svg?style=flat - - -[aboutme]: https://about.me/peter.boling -[angelme]: https://angel.co/peter-boling -[coderme]:http://coderwall.com/pboling -[followme-img]: https://img.shields.io/twitter/follow/galtzo.svg?style=social&label=Follow -[gh_sponsors]: https://github.com/sponsors/pboling -[liberapay_donate]: https://liberapay.com/pboling/donate -[peterboling]: http://www.peterboling.com -[railsbling]: http://www.railsbling.com - -## Installation +[sibling-gem]: https://gitlab.com/ruby-oauth/oauth2 -Install the gem and add to the application's Gemfile by executing: - - $ bundle add oauth +OAuth Ruby has been maintained by a large number of talented +individuals over the years. +The primary maintainer since 2020 is Peter Boling (@pboling). -If bundler is not being used to manage dependencies, install the gem by executing: +## 💡 Info you can shake a stick at - $ gem install oauth +| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] | +|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Works with JRuby | ![JRuby 9.1 Compat][💎jruby-9.1i] ![JRuby 9.2 Compat][💎jruby-9.2i] ![JRuby 9.3 Compat][💎jruby-9.3i]
[![JRuby 9.4 Compat][💎jruby-9.4i]][🚎10-j-wf] [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] | +| Works with Truffle Ruby | ![Truffle Ruby 22.3 Compat][💎truby-22.3i] ![Truffle Ruby 23.0 Compat][💎truby-23.0i]
[![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] | +| Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎4-lg-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎6-s-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] | +| Works with MRI Ruby 2 | [![Ruby 2.3 Compat][💎ruby-2.3i]][🚎1-an-wf] [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎1-an-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎1-an-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎7-us-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎7-us-wf] | +| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] | +| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] | +| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![Wiki][📜wiki-img]][📜wiki] | +| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] | +| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] | +| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] | +| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] | -## OAuth for Enterprise +### Compatibility -Available as part of the Tidelift Subscription. +Compatible with MRI Ruby 2.3+, and concordant releases of JRuby, and TruffleRuby. -The maintainers of OAuth2 and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.](https://tidelift.com/subscription/pkg/rubygems-oauth?utm_source=rubygems-oauth&utm_medium=referral&utm_campaign=enterprise) +| 🚚 _Amazing_ test matrix was brought to you by | 🔎 appraisal2 🔎 and the color 💚 green 💚 | +|------------------------------------------------|--------------------------------------------------------| +| 👟 Check it out! | ✨ [github.com/appraisal-rb/appraisal2][💎appraisal2] ✨ | -## Security contact information [![Security Policy][🚎sec-pol-img]][🚎sec-pol] +### Federated DVCS -To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure. +
+ Find this repo on other forges (Coming soon!) -For more see [SECURITY.md][🚎sec-pol]. +| Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions | +|-------------------------------------------------|-----------------------------------------------------------------------|---------------------------|--------------------------|---------------------------|--------------------------|------------------------------| +| 🧪 [ruby-oauth/oauth on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜wiki] | 🏀 Tiny Matrix | ➖ | +| 🧊 [ruby-oauth/oauth on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ | +| 🐙 [ruby-oauth/oauth on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | ➖ | 💯 Full Matrix | [💚][gh-discussions] | +| 🎮️ [Discord Server][✉️discord-invite] | [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] | [Let's][✉️discord-invite] | [talk][✉️discord-invite] | [about][✉️discord-invite] | [this][✉️discord-invite] | [library!][✉️discord-invite] | -## Compatibility +
-Targeted ruby compatibility is non-EOL versions of Ruby, currently 2.7, 3.0, and -3.1. Ruby is limited to 2.7+ in the gemspec, and this will change with major version bumps, -in accordance with the SemVer spec. +[gh-discussions]: https://github.com/ruby-oauth/oauth/discussions -The `main` branch now targets 1.1.x releases, for Ruby >= 2.7. -See `v1.0-maintenance` (EOL April, 2023) branch for Ruby >= 2.7. -See `v0.6-maintenance` (EOL April, 2023) branch for Ruby >= 2.4. -See `v0.5-maintenance` (EOL April, 2023) branch for Ruby >= 2.0. +### Enterprise Support [![Tidelift](https://tidelift.com/badges/package/rubygems/oauth)](https://tidelift.com/subscription/pkg/rubygems-oauth?utm_source=rubygems-oauth&utm_medium=referral&utm_campaign=readme) -NOTE: No further releases of versions < 1.0.x are anticipated. +Available as part of the Tidelift Subscription.
- Ruby Engine Compatibility Policy - -This gem is tested against MRI, and to a lesser extent, against JRuby, and Truffleruby. -Each of those has varying versions that target a specific version of MRI Ruby. -This gem should work in the just-listed Ruby engines according to the targeted MRI compatibility in the table below. -If you would like to add support for additional engines, -first make sure Github Actions supports the engine, -then submit a PR to the correct maintenance branch as according to the table below. + Need enterprise-level guarantees? + +The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. + +[![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift] + +- 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies +- 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar] +- 💡Tidelift pays maintainers to maintain the software you depend on!
📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers + +Alternatively: + +- [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] +- [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] +- [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] +
+## ✨ Installation + +Install the gem and add to the application's Gemfile by executing: + +```console +bundle add oauth +``` + +If bundler is not being used to manage dependencies, install the gem by executing: + +```console +gem install oauth +``` + +### 🔒 Secure Installation +
- Ruby Version Compatibility Policy + For Medium or High Security Installations -If something doesn't work on one of these interpreters, it's a bug. +This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by +[stone_checksums][💎stone_checksums]. Be sure the gem you install hasn’t been tampered with +by following the instructions below. -This library may inadvertently work (or seem to work) on other Ruby -implementations, however support will only be provided for the versions listed -above. +Add my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate: -If you would like this library to support another Ruby version, you may -volunteer to be a maintainer. Being a maintainer entails making sure all tests -run and pass on that implementation. When something breaks on your -implementation, you will be responsible for providing patches in a timely -fashion. If critical issues for a particular implementation exist at the time -of a major release, support for that Ruby version may be dropped. -
+```console +gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem) +``` + +You only need to do that once. Then proceed to install with: + +```console +gem install oauth -P HighSecurity +``` -| | Ruby OAuth Version | Maintenance Branch | EOL | 🚂 Compatibility | Official 💎 | Unofficial 💎 | Incidental 💎 | -|:----|--------------------|--------------------|---------|------------------------|---------------|------------------------------|---------------| -| 1️⃣ | 1.1.x | `main` | 04/2023 | Rails 6, 7 | 2.7, 3.0, 3.1 | none | none | -| 2️⃣ | 1.0.x | `v1.0-maintenance` | 04/2023 | Rails 6, 7 | 2.7, 3.0, 3.1 | none | none | -| 3️⃣ | 0.6.x | `v0.6-maintenance` | 04/2023 | Rails 5, 6, 7 | 2.7, 3.0, 3.1 | 2.5, 2.6 | 2.4 | -| 4️⃣ | 0.5.x | `v0.5-maintenance` | 04/2023 | Rails 2, 3, 4, 5, 6, 7 | 2.7, 3.0, 3.1 | 2.1, 2.2, 2.3, 2.4, 2.5, 2.6 | 2.0 | -| 5️⃣ | older | ⛔ | ⛔ | ⛔ | ⛔ | ⛔ | ⛔ | +The `HighSecurity` trust profile will verify signed gems, and not allow the installation of unsigned dependencies. -See [SECURITY.md][🚎sec-pol] +If you want to up your security game full-time: -🚂 NOTE: See notes on Rails in next section. +```console +bundle config set --global trust-policy MediumSecurity +``` + +`MediumSecurity` instead of `HighSecurity` is necessary if not all the gems you use are signed. + +NOTE: Be prepared to track down certs for signed gems and add them the same way you added mine. + + -## Basics +## ⚙️ Configuration This is a ruby library which is intended to be used in creating Ruby Consumer and Service Provider applications. It is NOT a Rails plugin, but could easily @@ -270,11 +161,13 @@ Unfortunately, this gem does have some Rails related bits that are **optional** to load. You don't need Rails! The Rails bits may be pulled out into a separate gem with the 1.x minor updates of this gem. -## Extensions +## 🔧 Basic Usage -* [oauth-tty (on Gitlab)](https://gitlab.com/oauth-xx/oauth-tty) ([rubygems.org](https://rubygems.org/gems/oauth-tty)) +### Extensions -## Usage +* [oauth-tty (on Gitlab)](https://gitlab.com/ruby-oauth/oauth-tty) ([rubygems.org](https://rubygems.org/gems/oauth-tty)) + +### Examples We need to specify the `oauth_callback` url explicitly, otherwise it defaults to "oob" (Out of Band) @@ -302,7 +195,7 @@ redirect_to request_token.authorize_url(oauth_callback: callback_url) When user returns create an access_token ```ruby -hash = { oauth_token: session[:token], oauth_token_secret: session[:token_secret] } +hash = {oauth_token: session[:token], oauth_token_secret: session[:token_secret]} request_token = OAuth::RequestToken.from_hash(oauth_consumer, hash) access_token = request_token.get_access_token # For 3-legged authorization, flow oauth_verifier is passed as param in callback @@ -316,7 +209,7 @@ OAuth provider if you choose. ```ruby require "typhoeus" require "oauth/request_proxy/typhoeus_request" -oauth_params = { consumer: oauth_consumer, token: access_token } +oauth_params = {consumer: oauth_consumer, token: access_token} hydra = Typhoeus::Hydra.new req = Typhoeus::Request.new(uri, options) # :method needs to be specified in options oauth_helper = OAuth::Client::Helper.new(req, oauth_params.merge(request_uri: uri)) @@ -326,43 +219,139 @@ hydra.run @response = req.response ``` -## More Information +### More Information * RubyDoc Documentation: [![RubyDoc.info][🚎yard-img]][🚎yard] * Mailing List/Google Group: [![Mailing List][⛳mail-list-img]][⛳mail-list] -* Live Chat on Gitter: [![Join the chat at https://gitter.im/oauth-xx/oauth-ruby][🏘chat-img]][🏘chat] +* Live Chat on Gitter: [![Join the chat at https://gitter.im/ruby-oauth/oauth-ruby][🏘chat-img]][🏘chat] * Maintainer's Blog: [![Blog][🚎blog-img]][🚎blog] -## Code of Conduct +## 🦷 FLOSS Funding + +While ruby-oauth tools are free software and will always be, the project would benefit immensely from some funding. +Raising a monthly budget of... "dollars" would make the project more sustainable. + +We welcome both individual and corporate sponsors! We also offer a +wide array of funding channels to account for your preferences +(although currently [Open Collective][🖇osc] is our preferred funding platform). + +**If you're working in a company that's making significant use of ruby-oauth tools we'd +appreciate it if you suggest to your company to become a ruby-oauth sponsor.** + +You can support the development of ruby-oauth tools via +[GitHub Sponsors][🖇sponsor], +[Liberapay][⛳liberapay], +[PayPal][🖇paypal], +[Open Collective][🖇osc] +and [Tidelift][🏙️entsup-tidelift]. + +| 📍 NOTE | +|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| If doing a sponsorship in the form of donation is problematic for your company
from an accounting standpoint, we'd recommend the use of Tidelift,
where you can get a support-like subscription instead. | + +### Open Collective for Individuals + + +No backers yet. Be the first! + + +Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/ruby-oauth#backer)] + +### Open Collective for Organizations + + +No sponsors yet. Be the first! + + +Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor](https://opencollective.com/ruby-oauth#sponsor)] + +### Another way to support open-source + +> How wonderful it is that nobody need wait a single moment before starting to improve the world.
+>—Anne Frank + +I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions — totaling 79 hours of FLOSS coding over just the past seven days, a pretty regular week for me. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats). + +If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`. + +I’m developing a new library, [floss_funding][🖇floss-funding-gem], designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look. + +**[Floss-Funding.dev][🖇floss-funding.dev]: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags** + +[![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon] + +## 🔐 Security + +See [SECURITY.md][🔐security]. + +## 🤝 Contributing + +If you need some ideas of where to help, you could work on adding more code coverage, +or if it is already 💯 (see [below](#code-coverage)) check [reek](REEK), [issues][🤝gh-issues], or [PRs][🤝gh-pulls], +or use the gem and think about how it could be better. -Everyone interacting in this project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct][🚎code-conduct]. +We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it. -## Contributing +See [CONTRIBUTING.md][🤝contributing] for more detailed instructions. -Bug reports and pull requests are welcome on GitLab at [https://gitlab.com/oauth-xx/oauth/-/issues][issues]. +### 🚀 Release Instructions -This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct][🚎code-conduct]. +See [CONTRIBUTING.md][🤝contributing]. -See [CONTRIBUTING.md][contributing] for detailed instructions on how to help! +### Code Coverage -## Contributors +[![Coverage Graph][🔑codecov-g]][🔑codecov] -See [https://gitlab.com/oauth-xx/oauth/-/graphs/main][🚎contributors] +[![Coveralls Test Coverage][🔑coveralls-img]][🔑coveralls] -## Versioning +[![QLTY Test Coverage][🔑qlty-covi]][🔑qlty-cov] -This library aims to adhere to [Semantic Versioning 2.0.0][🧮semver]. Violations of this scheme should be reported as -bugs. Specifically, if a minor or patch version is released that breaks backward compatibility, a new version should be -immediately released that restores compatibility. Breaking changes to the public API will only be introduced with new -major versions. Compatibility with a major and minor versions of Ruby will only be changed with a major version bump. +### 🪇 Code of Conduct -As a result of this policy, you can (and should) specify a dependency on this gem using -the [Pessimistic Version Constraint][pvc] with two digits of precision. +Everyone interacting with this project's codebases, issue trackers, +chat rooms and mailing lists agrees to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct]. + +## 🌈 Contributors + +[![Contributors][🖐contributors-img]][🖐contributors] + +Made with [contributors-img][🖐contrib-rocks]. + +Also see GitLab Contributors: [https://gitlab.com/ruby-oauth/oauth/-/graphs/main][🚎contributors-gl] + +
+ ⭐️ Star History + + + + + + Star History Chart + + + +
+ +## 📌 Versioning + +This Library adheres to [![Semantic Versioning 2.0.0][📌semver-img]][📌semver]. +Violations of this scheme should be reported as bugs. +Specifically, if a minor or patch version is released that breaks backward compatibility, +a new version should be immediately released that restores compatibility. +Breaking changes to the public API will only be introduced with new major versions. + +> dropping support for a platform is both obviously and objectively a breaking change
+>—Jordan Harband ([@ljharb](https://github.com/ljharb), maintainer of SemVer) [in SemVer issue 716][📌semver-breaking] + +I understand that policy doesn't work universally ("exceptions to every rule!"), +but it is the policy here. +As such, in many cases it is good to specify a dependency on this library using +the [Pessimistic Version Constraint][📌pvc] with two digits of precision. For example: ```ruby -spec.add_dependency "oauth", "~> 1.1" +spec.add_dependency("oauth", "~> 1.0") ```
@@ -401,29 +390,220 @@ See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright -OAuth Ruby has been created and maintained by a large number of talented -individuals. The current maintainer is Peter Boling (@pboling). Please -[support with donations at Liberapay][liberapay_donate]). +## 🤑 A request for help + +Maintainers have teeth and need to pay their dentists. +After getting laid off in an RIF in March and filled with many dozens of rejections, +I'm now spending ~60+ hours a week building open source tools. +I'm hoping to be able to pay for my kids' health insurance this month, +so if you value the work I am doing, I need your support. +Please consider sponsoring me or the project. -Comments are welcome. Contact the [OAuth Ruby mailing list (Google Group)][⛳mail-list] or [join the live chat at https://gitter.im/oauth-xx/oauth-ruby][🏘chat]. +To join the community or get help 👇️ Join the Discord. -[contributing]: https://gitlab.com/oauth-xx/oauth/-/blob/main/CONTRIBUTING.md -[copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year -[actions]: https://github.com/oauth-xx/oauth-ruby/actions -[issues]: https://gitlab.com/oauth-xx/oauth/-/issues -[license]: https://gitlab.com/oauth-xx/oauth/-/blob/main/LICENSE.txt -[pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint +[![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] -## 🤑 One more thing +To say "thanks!" ☝️ Join the Discord or 👇️ send money. -You made it to the bottom of the page, -so perhaps you'll indulge me for another 20 seconds. -I maintain many dozens of gems, including this one, -because I want Ruby to be a great place for people to solve problems, big and small. -Please consider supporting my efforts via the giant yellow link below, -or one of the others at the head of this README. +[![Sponsor ruby-oauth/oauth on Open Source Collective][🖇osc-all-bottom-img]][🖇osc] 💌 [![Sponsor me on GitHub Sponsors][🖇sponsor-bottom-img]][🖇sponsor] 💌 [![Sponsor me on Liberapay][⛳liberapay-bottom-img]][⛳liberapay-img] 💌 [![Donate on PayPal][🖇paypal-bottom-img]][🖇paypal-img] -[![Buy me a latte][🖇buyme-img]][🖇buyme] +### Please give the project a star ⭐ ♥. +Thanks for RTFM. ☺️ + +[⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat +[⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611 +[⛳liberapay]: https://liberapay.com/pboling/donate +[🖇osc-all-img]: https://img.shields.io/opencollective/all/ruby-oauth +[🖇osc-sponsors-img]: https://img.shields.io/opencollective/sponsors/ruby-oauth +[🖇osc-backers-img]: https://img.shields.io/opencollective/backers/ruby-oauth +[🖇osc-backers]: https://opencollective.com/ruby-oauth#backer +[🖇osc-backers-i]: https://opencollective.com/ruby-oauth/backers/badge.svg?style=flat +[🖇osc-sponsors]: https://opencollective.com/ruby-oauth#sponsor +[🖇osc-sponsors-i]: https://opencollective.com/ruby-oauth/sponsors/badge.svg?style=flat +[🖇osc-all-bottom-img]: https://img.shields.io/opencollective/all/ruby-oauth?style=for-the-badge +[🖇osc-sponsors-bottom-img]: https://img.shields.io/opencollective/sponsors/ruby-oauth?style=for-the-badge +[🖇osc-backers-bottom-img]: https://img.shields.io/opencollective/backers/ruby-oauth?style=for-the-badge +[🖇osc]: https://opencollective.com/ruby-oauth +[🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github +[🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github +[🖇sponsor]: https://github.com/sponsors/pboling +[🖇polar-img]: https://img.shields.io/badge/polar-donate-a51611.svg?style=flat +[🖇polar]: https://polar.sh/pboling +[🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat +[🖇kofi]: https://ko-fi.com/O5O86SNP4 +[🖇patreon-img]: https://img.shields.io/badge/patreon-donate-a51611.svg?style=flat +[🖇patreon]: https://patreon.com/galtzo +[🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat [🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte&emoji=&slug=pboling&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff [🖇buyme]: https://www.buymeacoffee.com/pboling +[🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal +[🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A +[🖇paypal]: https://www.paypal.com/paypalme/peterboling +[🖇floss-funding.dev]: https://floss-funding.dev +[🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding +[✉️discord-invite]: https://discord.gg/3qme4XHNKN +[✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord +[✉️ruby-friends-img]: https://img.shields.io/badge/daily.dev-%F0%9F%92%8E_Ruby_Friends-0A0A0A?style=for-the-badge&logo=dailydotdev&logoColor=white +[✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends + +[✇bundle-group-pattern]: https://gist.github.com/pboling/4564780 +[⛳️gem-namespace]: https://github.com/ruby-oauth/oauth +[⛳️namespace-img]: https://img.shields.io/badge/namespace-Oauth-3C2D2D.svg?style=square&logo=ruby&logoColor=white +[⛳️gem-name]: https://rubygems.org/gems/oauth +[⛳️name-img]: https://img.shields.io/badge/name-oauth-3C2D2D.svg?style=square&logo=rubygems&logoColor=red +[⛳️tag-img]: https://img.shields.io/github/tag/ruby-oauth/oauth.svg +[⛳️tag]: http://github.com/ruby-oauth/oauth/releases +[🚂maint-blog]: http://www.railsbling.com/tags/oauth +[🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange +[🚂maint-contact]: http://www.railsbling.com/contact +[🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red +[💖🖇linkedin]: http://www.linkedin.com/in/peterboling +[💖🖇linkedin-img]: https://img.shields.io/badge/PeterBoling-LinkedIn-0B66C2?style=flat&logo=newjapanprowrestling +[💖✌️wellfound]: https://wellfound.com/u/peter-boling +[💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound +[💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling +[💖💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=flat&logo=crunchbase +[💖🐘ruby-mast]: https://ruby.social/@galtzo +[💖🐘ruby-mast-img]: https://img.shields.io/mastodon/follow/109447111526622197?domain=https://ruby.social&style=flat&logo=mastodon&label=Ruby%20@galtzo +[💖🦋bluesky]: https://bsky.app/profile/galtzo.com +[💖🦋bluesky-img]: https://img.shields.io/badge/@galtzo.com-0285FF?style=flat&logo=bluesky&logoColor=white +[💖🌳linktree]: https://linktr.ee/galtzo +[💖🌳linktree-img]: https://img.shields.io/badge/galtzo-purple?style=flat&logo=linktree +[💖💁🏼‍♂️devto]: https://dev.to/galtzo +[💖💁🏼‍♂️devto-img]: https://img.shields.io/badge/dev.to-0A0A0A?style=flat&logo=devdotto&logoColor=white +[💖💁🏼‍♂️aboutme]: https://about.me/peter.boling +[💖💁🏼‍♂️aboutme-img]: https://img.shields.io/badge/about.me-0A0A0A?style=flat&logo=aboutme&logoColor=white +[💖🧊berg]: https://codeberg.org/pboling +[💖🐙hub]: https://github.org/pboling +[💖🛖hut]: https://sr.ht/~galtzo/ +[💖🧪lab]: https://gitlab.com/pboling +[👨🏼‍🏫expsup-upwork]: https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share +[👨🏼‍🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white +[👨🏼‍🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github +[👨🏼‍🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white +[🏙️entsup-tidelift]: https://tidelift.com/subscription/pkg/rubygems-oauth?utm_source=rubygems-oauth&utm_medium=referral&utm_campaign=readme +[🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white +[🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar +[💁🏼‍♂️peterboling]: http://www.peterboling.com +[🚂railsbling]: http://www.railsbling.com +[📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange +[📜src-gl]: https://gitlab.com/ruby-oauth/oauth/ +[📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue +[📜src-cb]: https://codeberg.org/ruby-oauth/oauth +[📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=Github&logoColor=green +[📜src-gh]: https://github.com/ruby-oauth/oauth +[📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white +[📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white +[📜wiki]: https://gitlab.com/ruby-oauth/oauth/-/wikis/home +[📜wiki-img]: https://img.shields.io/badge/wiki-examples-943CD2.svg?style=for-the-badge&logo=Wiki&logoColor=white +[👽dl-rank]: https://rubygems.org/gems/oauth +[👽dl-ranki]: https://img.shields.io/gem/rd/oauth.svg +[👽oss-help]: https://www.codetriage.com/ruby-oauth/oauth +[👽oss-helpi]: https://www.codetriage.com/ruby-oauth/oauth/badges/users.svg +[👽version]: https://rubygems.org/gems/oauth +[👽versioni]: https://img.shields.io/gem/v/oauth.svg +[🔑qlty-mnt]: https://qlty.sh/gh/ruby-oauth/projects/oauth +[🔑qlty-mnti]: https://qlty.sh/gh/ruby-oauth/projects/oauth/maintainability.svg +[🔑qlty-cov]: https://qlty.sh/gh/ruby-oauth/projects/oauth/metrics/code?sort=coverageRating +[🔑qlty-covi]: https://qlty.sh/gh/ruby-oauth/projects/oauth/coverage.svg +[🔑codecov]: https://codecov.io/gh/ruby-oauth/oauth +[🔑codecovi]: https://codecov.io/gh/ruby-oauth/oauth/graph/badge.svg +[🔑coveralls]: https://coveralls.io/github/ruby-oauth/oauth?branch=main +[🔑coveralls-img]: https://coveralls.io/repos/github/ruby-oauth/oauth/badge.svg?branch=main +[🖐codeQL]: https://github.com/ruby-oauth/oauth/security/code-scanning +[🖐codeQL-img]: https://github.com/ruby-oauth/oauth/actions/workflows/codeql-analysis.yml/badge.svg +[🚎1-an-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/ancient.yml +[🚎1-an-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/ancient.yml/badge.svg +[🚎2-cov-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/coverage.yml +[🚎2-cov-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/coverage.yml/badge.svg +[🚎3-hd-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/heads.yml +[🚎3-hd-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/heads.yml/badge.svg +[🚎4-lg-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/legacy.yml +[🚎4-lg-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/legacy.yml/badge.svg +[🚎5-st-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/style.yml +[🚎5-st-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/style.yml/badge.svg +[🚎6-s-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/supported.yml +[🚎6-s-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/supported.yml/badge.svg +[🚎7-us-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/unsupported.yml +[🚎7-us-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/unsupported.yml/badge.svg +[🚎8-ho-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/hoary.yml +[🚎8-ho-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/hoary.yml/badge.svg +[🚎9-t-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/truffle.yml +[🚎9-t-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/truffle.yml/badge.svg +[🚎10-j-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/jruby.yml +[🚎10-j-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/jruby.yml/badge.svg +[🚎11-c-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/current.yml +[🚎11-c-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/current.yml/badge.svg +[🚎12-crh-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/dep-heads.yml +[🚎12-crh-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/dep-heads.yml/badge.svg +[🚎13-🔒️-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/locked_deps.yml +[🚎13-🔒️-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/locked_deps.yml/badge.svg +[🚎14-🔓️-wf]: https://github.com/ruby-oauth/oauth/actions/workflows/unlocked_deps.yml +[🚎14-🔓️-wfi]: https://github.com/ruby-oauth/oauth/actions/workflows/unlocked_deps.yml/badge.svg +[💎ruby-2.3i]: https://img.shields.io/badge/Ruby-2.3-DF00CA?style=for-the-badge&logo=ruby&logoColor=white +[💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge&logo=ruby&logoColor=white +[💎ruby-2.5i]: https://img.shields.io/badge/Ruby-2.5-DF00CA?style=for-the-badge&logo=ruby&logoColor=white +[💎ruby-2.6i]: https://img.shields.io/badge/Ruby-2.6-DF00CA?style=for-the-badge&logo=ruby&logoColor=white +[💎ruby-2.7i]: https://img.shields.io/badge/Ruby-2.7-DF00CA?style=for-the-badge&logo=ruby&logoColor=white +[💎ruby-3.0i]: https://img.shields.io/badge/Ruby-3.0-CC342D?style=for-the-badge&logo=ruby&logoColor=white +[💎ruby-3.1i]: https://img.shields.io/badge/Ruby-3.1-CC342D?style=for-the-badge&logo=ruby&logoColor=white +[💎ruby-3.2i]: https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge&logo=ruby&logoColor=white +[💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge&logo=ruby&logoColor=white +[💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green +[💎ruby-headi]: https://img.shields.io/badge/Ruby-HEAD-CC342D?style=for-the-badge&logo=ruby&logoColor=blue +[💎truby-22.3i]: https://img.shields.io/badge/Truffle_Ruby-22.3_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=pink +[💎truby-23.0i]: https://img.shields.io/badge/Truffle_Ruby-23.0_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=pink +[💎truby-23.1i]: https://img.shields.io/badge/Truffle_Ruby-23.1-34BCB1?style=for-the-badge&logo=ruby&logoColor=pink +[💎truby-c-i]: https://img.shields.io/badge/Truffle_Ruby-current-34BCB1?style=for-the-badge&logo=ruby&logoColor=green +[💎truby-headi]: https://img.shields.io/badge/Truffle_Ruby-HEAD-34BCB1?style=for-the-badge&logo=ruby&logoColor=blue +[💎jruby-9.1i]: https://img.shields.io/badge/JRuby-9.1_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=red +[💎jruby-9.2i]: https://img.shields.io/badge/JRuby-9.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=red +[💎jruby-9.3i]: https://img.shields.io/badge/JRuby-9.3_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=red +[💎jruby-9.4i]: https://img.shields.io/badge/JRuby-9.4-FBE742?style=for-the-badge&logo=ruby&logoColor=red +[💎jruby-c-i]: https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge&logo=ruby&logoColor=green +[💎jruby-headi]: https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge&logo=ruby&logoColor=blue +[🤝gh-issues]: https://github.com/ruby-oauth/oauth/issues +[🤝gh-pulls]: https://github.com/ruby-oauth/oauth/pulls +[🤝gl-issues]: https://gitlab.com/ruby-oauth/oauth/-/issues +[🤝gl-pulls]: https://gitlab.com/ruby-oauth/oauth/-/merge_requests +[🤝cb-issues]: https://codeberg.org/ruby-oauth/oauth/issues +[🤝cb-pulls]: https://codeberg.org/ruby-oauth/oauth/pulls +[🤝cb-donate]: https://donate.codeberg.org/ +[🤝contributing]: CONTRIBUTING.md +[🔑codecov-g]: https://codecov.io/gh/ruby-oauth/oauth/graphs/tree.svg +[🖐contrib-rocks]: https://contrib.rocks +[🖐contributors]: https://github.com/ruby-oauth/oauth/graphs/contributors +[🖐contributors-img]: https://contrib.rocks/image?repo=ruby-oauth/oauth +[🚎contributors-gl]: https://gitlab.com/ruby-oauth/oauth/-/graphs/main +[🪇conduct]: CODE_OF_CONDUCT.md +[🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg +[📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint +[📌semver]: https://semver.org/spec/v2.0.0.html +[📌semver-img]: https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat +[📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139 +[📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html +[📌changelog]: CHANGELOG.md +[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/ +[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat +[📌gitmoji]:https://gitmoji.dev +[📌gitmoji-img]:https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square +[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ +[🧮kloc-img]: https://img.shields.io/badge/KLOC-3.613-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue +[🔐security]: SECURITY.md +[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat +[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year +[📄license]: LICENSE.txt +[📄license-ref]: https://opensource.org/licenses/MIT +[📄license-img]: https://img.shields.io/badge/License-MIT-259D6C.svg +[📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm +[📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat +[🚎yard-current]: http://rubydoc.info/gems/oauth +[🚎yard-head]: https://oauth.galtzo.com +[💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums +[💎SHA_checksums]: https://gitlab.com/ruby-oauth/oauth/-/tree/main/checksums +[💎rlts]: https://github.com/rubocop-lts/rubocop-lts +[💎rlts-img]: https://img.shields.io/badge/code_style_&_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white +[💎appraisal2]: https://github.com/appraisal-rb/appraisal2 +[💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white +[💎d-in-dvcs]: https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/ diff --git a/RUBOCOP.md b/RUBOCOP.md new file mode 100644 index 00000000..f15b9801 --- /dev/null +++ b/RUBOCOP.md @@ -0,0 +1,71 @@ +# RuboCop Usage Guide + +## Overview + +A tale of two RuboCop plugin gems. + +### RuboCop Gradual + +This project uses `rubocop_gradual` instead of vanilla RuboCop for code style checking. The `rubocop_gradual` tool allows for gradual adoption of RuboCop rules by tracking violations in a lock file. + +### RuboCop LTS + +This project uses `rubocop-lts` to ensure, on a best-effort basis, compatibility with Ruby >= 1.9.2. +RuboCop rules are meticulously configured by the `rubocop-lts` family of gems to ensure that a project is compatible with a specific version of Ruby. See: https://rubocop-lts.gitlab.io for more. + +## Checking RuboCop Violations + +To check for RuboCop violations in this project, always use: + +```bash +bundle exec rake rubocop_gradual:check +``` + +**Do not use** the standard RuboCop commands like: +- `bundle exec rubocop` +- `rubocop` + +## Understanding the Lock File + +The `.rubocop_gradual.lock` file tracks all current RuboCop violations in the project. This allows the team to: + +1. Prevent new violations while gradually fixing existing ones +2. Track progress on code style improvements +3. Ensure CI builds don't fail due to pre-existing violations + +## Common Commands + +- **Check violations** + - `bundle exec rake rubocop_gradual` + - `bundle exec rake rubocop_gradual:check` +- **(Safe) Autocorrect violations, and update lockfile if no new violations** + - `bundle exec rake rubocop_gradual:autocorrect` +- **Force update the lock file (w/o autocorrect) to match violations present in code** + - `bundle exec rake rubocop_gradual:force_update` + +## Workflow + +1. Before submitting a PR, run `bundle exec rake rubocop_gradual:autocorrect` + a. or just the default `bundle exec rake`, as autocorrection is a pre-requisite of the default task. +2. If there are new violations, either: + - Fix them in your code + - Run `bundle exec rake rubocop_gradual:force_update` to update the lock file (only for violations you can't fix immediately) +3. Commit the updated `.rubocop_gradual.lock` file along with your changes + +## Never add inline RuboCop disables + +Do not add inline `rubocop:disable` / `rubocop:enable` comments anywhere in the codebase (including specs, except when following the few existing `rubocop:disable` patterns for a rule already being disabled elsewhere in the code). We handle exceptions in two supported ways: + +- Permanent/structural exceptions: prefer adjusting the RuboCop configuration (e.g., in `.rubocop.yml`) to exclude a rule for a path or file pattern when it makes sense project-wide. +- Temporary exceptions while improving code: record the current violations in `.rubocop_gradual.lock` via the gradual workflow: + - `bundle exec rake rubocop_gradual:autocorrect` (preferred; will autocorrect what it can and update the lock only if no new violations were introduced) + - If needed, `bundle exec rake rubocop_gradual:force_update` (as a last resort when you cannot fix the newly reported violations immediately) + +In general, treat the rules as guidance to follow; fix violations rather than ignore them. For example, RSpec conventions in this project expect `described_class` to be used in specs that target a specific class under test. + +## Benefits of rubocop_gradual + +- Allows incremental adoption of code style rules +- Prevents CI failures due to pre-existing violations +- Provides a clear record of code style debt +- Enables focused efforts on improving code quality over time diff --git a/Rakefile b/Rakefile index 071cccc9..1bb7d01b 100644 --- a/Rakefile +++ b/Rakefile @@ -1,23 +1,66 @@ # frozen_string_literal: true -require "bundler/gem_tasks" -%w[rake/testtask fileutils].each { |f| require f } +# kettle-dev Rakefile v1.1.18 - 2025-09-12 +# Ruby 2.3 (Safe Navigation) or higher required +# +# MIT License (see License.txt) +# +# Copyright (c) 2025 Peter H. Boling (galtzo.com) +# +# Expected to work in any project that uses Bundler. +# +# Sets up tasks for appraisal, floss_funding, rspec, minitest, rubocop, reek, yard, and stone_checksums. +# +# rake appraisal:update # Update Appraisal gemfiles and run RuboCop... +# rake bench # Run all benchmarks (alias for bench:run) +# rake bench:list # List available benchmark scripts +# rake bench:run # Run all benchmark scripts (skips on CI) +# rake build:generate_checksums # Generate both SHA256 & SHA512 checksums i... +# rake bundle:audit:check # Checks the Gemfile.lock for insecure depe... +# rake bundle:audit:update # Updates the bundler-audit vulnerability d... +# rake ci:act[opt] # Run 'act' with a selected workflow +# rake coverage # Run specs w/ coverage and open results in... +# rake default # Default tasks aggregator +# rake install # Build and install kettle-dev-1.0.0.gem in... +# rake install:local # Build and install kettle-dev-1.0.0.gem in... +# rake kettle:dev:install # Install kettle-dev GitHub automation and ... +# rake kettle:dev:template # Template kettle-dev files into the curren... +# rake reek # Check for code smells +# rake reek:update # Run reek and store the output into the RE... +# rake release[remote] # Create tag v1.0.0 and build and push kett... +# rake rubocop_gradual # Run RuboCop Gradual +# rake rubocop_gradual:autocorrect # Run RuboCop Gradual with autocorrect (onl... +# rake rubocop_gradual:autocorrect_all # Run RuboCop Gradual with autocorrect (saf... +# rake rubocop_gradual:check # Run RuboCop Gradual to check the lock file +# rake rubocop_gradual:force_update # Run RuboCop Gradual to force update the l... +# rake rubocop_gradual_debug # Run RuboCop Gradual +# rake rubocop_gradual_debug:autocorrect # Run RuboCop Gradual with autocorrect (onl... +# rake rubocop_gradual_debug:autocorrect_all # Run RuboCop Gradual with autocorrect (saf... +# rake rubocop_gradual_debug:check # Run RuboCop Gradual to check the lock file +# rake rubocop_gradual_debug:force_update # Run RuboCop Gradual to force update the l... +# rake spec # Run RSpec code examples +# rake test # Run tests +# rake yard # Generate YARD Documentation +# -Bundler::GemHelper.install_tasks +require "bundler/gem_tasks" if !Dir[File.join(__dir__, "*.gemspec")].empty? -Rake::TestTask.new do |t| - t.libs << "test" - t.test_files = FileList["test/**/*test*.rb"] - t.verbose = true +# Define a base default task early so other files can enhance it. +desc "Default tasks aggregator" +task :default do + puts "Default task complete." end +# External gems that define tasks - add here! +require "kettle/dev" + +### RELEASE TASKS +# Setup stone_checksums begin - require "rubocop/rake_task" - RuboCop::RakeTask.new + require "stone_checksums" rescue LoadError - task :rubocop do - warn "RuboCop is disabled on Ruby #{RUBY_VERSION}" + desc("(stub) build:generate_checksums is unavailable") + task("build:generate_checksums") do + warn("NOTE: stone_checksums isn't installed, or is disabled for #{RUBY_VERSION} in the current environment") end end - -task default: %i[test rubocop] diff --git a/SECURITY.md b/SECURITY.md index d59f0433..841f0150 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,13 +2,13 @@ ## Supported Versions -| Version | Supported | EOL | Post-EOL / Enterprise | -|---------|----------|---------|---------------------------------------| -| 1.1.x | ✅ | 04/2023 | [Tidelift Subscription][tidelift-ref] | -| 1.0.x | ✅ | 04/2023 | [Tidelift Subscription][tidelift-ref] | -| 0.6.x | 🚨 | 04/2023 | [Tidelift Subscription][tidelift-ref] | -| 0.5.x | 🚨 | 04/2023 | [Tidelift Subscription][tidelift-ref] | -| <= 0.5 | ⛔ | ⛔ | ⛔ | +| Version | Supported | Post-EOL / Enterprise | +|---------|-----------|---------------------------------------| +| 1.1.x | ✅ | [Tidelift Subscription][tidelift-ref] | +| 1.0.x | ✅ | [Tidelift Subscription][tidelift-ref] | +| 0.6.x | 🚨 | [Tidelift Subscription][tidelift-ref] | +| 0.5.x | 🚨 | [Tidelift Subscription][tidelift-ref] | +| <= 0.5 | ⛔ | ⛔ | LEGEND: ✅ - Supported @@ -17,11 +17,12 @@ LEGEND: ### EOL Policy -Non-commercial support for the oldest version of Ruby (which itself is going EOL) will be dropped each year in April. +Non-commercial support for the oldest version of Ruby (which itself is going EOL) may be dropped each year in April. -## Reporting a Vulnerability +## Security contact information -To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. ## OAuth for Enterprise @@ -31,3 +32,11 @@ Available as part of the Tidelift Subscription. The maintainers of oauth and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.][tidelift-ref] [tidelift-ref]: https://tidelift.com/subscription/pkg/rubygems-oauth?utm_source=rubygems-oauth&utm_medium=referral&utm_campaign=enterprise&utm_term=repo + +## Additional Support + +If you are interested in support for versions older than the latest release, +please consider sponsoring the project / maintainer @ https://liberapay.com/pboling/donate, +or find other sponsorship links in the [README]. + +[README]: README.md diff --git a/bin/appraisal b/bin/appraisal new file mode 100755 index 00000000..bc7d25bd --- /dev/null +++ b/bin/appraisal @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'appraisal' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("appraisal2", "appraisal") diff --git a/bin/bundle b/bin/bundle deleted file mode 100755 index 52c61aaf..00000000 --- a/bin/bundle +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'bundle' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require "rubygems" - -m = Module.new do - module_function - - def invoked_as_script? - File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__) - end - - def env_var_version - ENV.fetch("BUNDLER_VERSION", nil) - end - - def cli_arg_version - return unless invoked_as_script? # don't want to hijack other binstubs - return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` - - bundler_version = nil - update_index = nil - ARGV.each_with_index do |a, i| - bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN - next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/o - - bundler_version = Regexp.last_match(1) - update_index = i - end - bundler_version - end - - def gemfile - gemfile = ENV.fetch("BUNDLE_GEMFILE", nil) - return gemfile if gemfile && !gemfile.empty? - - File.expand_path("../Gemfile", __dir__) - end - - def lockfile - lockfile = - case File.basename(gemfile) - when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) - else "#{gemfile}.lock" - end - File.expand_path(lockfile) - end - - def lockfile_version - return unless File.file?(lockfile) - - lockfile_contents = File.read(lockfile) - return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/o - - Regexp.last_match(1) - end - - def bundler_requirement - @bundler_requirement ||= - env_var_version || cli_arg_version || - bundler_requirement_for(lockfile_version) - end - - def bundler_requirement_for(version) - return "#{Gem::Requirement.default}.a" unless version - - bundler_gem_version = Gem::Version.new(version) - - requirement = bundler_gem_version.approximate_recommendation - - return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0") - - requirement += ".a" if bundler_gem_version.prerelease? - - requirement - end - - def load_bundler! - ENV["BUNDLE_GEMFILE"] ||= gemfile - - activate_bundler - end - - def activate_bundler - gem_error = activation_error_handling do - gem "bundler", bundler_requirement - end - return if gem_error.nil? - - require_error = activation_error_handling do - require "bundler/version" - end - if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) - return - end - - warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" - exit 42 - end - - def activation_error_handling - yield - nil - rescue StandardError, LoadError => e - e - end -end - -m.load_bundler! - -load Gem.bin_path("bundler", "bundle") if m.invoked_as_script? diff --git a/bin/bundle-audit b/bin/bundle-audit new file mode 100755 index 00000000..018e7022 --- /dev/null +++ b/bin/bundle-audit @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle-audit' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("bundler-audit", "bundle-audit") diff --git a/bin/bundler-audit b/bin/bundler-audit new file mode 100755 index 00000000..fce02919 --- /dev/null +++ b/bin/bundler-audit @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundler-audit' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("bundler-audit", "bundler-audit") diff --git a/bin/code_climate_reek b/bin/code_climate_reek new file mode 100755 index 00000000..ca950c59 --- /dev/null +++ b/bin/code_climate_reek @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'code_climate_reek' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("reek", "code_climate_reek") diff --git a/bin/coderay b/bin/coderay new file mode 100755 index 00000000..4d35abac --- /dev/null +++ b/bin/coderay @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'coderay' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("coderay", "coderay") diff --git a/bin/erb b/bin/erb new file mode 100755 index 00000000..58e756ae --- /dev/null +++ b/bin/erb @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'erb' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("erb", "erb") diff --git a/bin/gem_checksums b/bin/gem_checksums new file mode 100755 index 00000000..dfefdd3a --- /dev/null +++ b/bin/gem_checksums @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'gem_checksums' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("stone_checksums", "gem_checksums") diff --git a/bin/htmldiff b/bin/htmldiff new file mode 100755 index 00000000..f210292f --- /dev/null +++ b/bin/htmldiff @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'htmldiff' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("diff-lcs", "htmldiff") diff --git a/bin/irb b/bin/irb new file mode 100755 index 00000000..66b40911 --- /dev/null +++ b/bin/irb @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'irb' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("irb", "irb") diff --git a/bin/kettle-changelog b/bin/kettle-changelog new file mode 100755 index 00000000..0e7fcc4d --- /dev/null +++ b/bin/kettle-changelog @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'kettle-changelog' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("kettle-dev", "kettle-changelog") diff --git a/bin/kettle-commit-msg b/bin/kettle-commit-msg new file mode 100755 index 00000000..b228ad67 --- /dev/null +++ b/bin/kettle-commit-msg @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'kettle-commit-msg' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("kettle-dev", "kettle-commit-msg") diff --git a/bin/kettle-dev-setup b/bin/kettle-dev-setup new file mode 100755 index 00000000..276319a7 --- /dev/null +++ b/bin/kettle-dev-setup @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'kettle-dev-setup' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("kettle-dev", "kettle-dev-setup") diff --git a/bin/kettle-dvcs b/bin/kettle-dvcs new file mode 100755 index 00000000..b572d48c --- /dev/null +++ b/bin/kettle-dvcs @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'kettle-dvcs' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("kettle-dev", "kettle-dvcs") diff --git a/bin/kettle-pre-release b/bin/kettle-pre-release new file mode 100755 index 00000000..1b98ad61 --- /dev/null +++ b/bin/kettle-pre-release @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'kettle-pre-release' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("kettle-dev", "kettle-pre-release") diff --git a/bin/kettle-readme-backers b/bin/kettle-readme-backers new file mode 100755 index 00000000..fec80bd5 --- /dev/null +++ b/bin/kettle-readme-backers @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'kettle-readme-backers' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("kettle-dev", "kettle-readme-backers") diff --git a/bin/kettle-release b/bin/kettle-release new file mode 100755 index 00000000..1f5758a8 --- /dev/null +++ b/bin/kettle-release @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'kettle-release' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("kettle-dev", "kettle-release") diff --git a/bin/kramdown b/bin/kramdown new file mode 100755 index 00000000..547fd063 --- /dev/null +++ b/bin/kramdown @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'kramdown' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("kramdown", "kramdown") diff --git a/bin/ldiff b/bin/ldiff new file mode 100755 index 00000000..6fee5094 --- /dev/null +++ b/bin/ldiff @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'ldiff' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("diff-lcs", "ldiff") diff --git a/bin/nokogiri b/bin/nokogiri new file mode 100755 index 00000000..8b72331f --- /dev/null +++ b/bin/nokogiri @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'nokogiri' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("nokogiri", "nokogiri") diff --git a/bin/oauth b/bin/oauth new file mode 100755 index 00000000..50ac8db7 --- /dev/null +++ b/bin/oauth @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'oauth' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("oauth-tty", "oauth") diff --git a/bin/pry b/bin/pry new file mode 100755 index 00000000..41bfde55 --- /dev/null +++ b/bin/pry @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'pry' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("pry", "pry") diff --git a/bin/racc b/bin/racc new file mode 100755 index 00000000..35d8ec55 --- /dev/null +++ b/bin/racc @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'racc' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("racc", "racc") diff --git a/bin/rackup b/bin/rackup new file mode 100755 index 00000000..027ef4b0 --- /dev/null +++ b/bin/rackup @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rackup' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rackup", "rackup") diff --git a/bin/rails b/bin/rails new file mode 100755 index 00000000..3cf76b0f --- /dev/null +++ b/bin/rails @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rails' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("railties", "rails") diff --git a/bin/rake b/bin/rake index 51543653..9efbee99 100755 --- a/bin/rake +++ b/bin/rake @@ -8,18 +8,7 @@ # this file is here to facilitate running it. # -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../gemfiles/a7.gemfile", __dir__) - -bundle_binstub = File.expand_path("bundle", __dir__) - -if File.file?(bundle_binstub) - if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300)) - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) require "rubygems" require "bundler/setup" diff --git a/bin/rbs b/bin/rbs new file mode 100755 index 00000000..ffc95a0d --- /dev/null +++ b/bin/rbs @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rbs' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rbs", "rbs") diff --git a/bin/rdbg b/bin/rdbg new file mode 100755 index 00000000..48909590 --- /dev/null +++ b/bin/rdbg @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rdbg' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("debug", "rdbg") diff --git a/bin/rdoc b/bin/rdoc new file mode 100755 index 00000000..021c9ca3 --- /dev/null +++ b/bin/rdoc @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rdoc' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rdoc", "rdoc") diff --git a/bin/reek b/bin/reek new file mode 100755 index 00000000..201905fb --- /dev/null +++ b/bin/reek @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'reek' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("reek", "reek") diff --git a/bin/restclient b/bin/restclient new file mode 100755 index 00000000..348c5b60 --- /dev/null +++ b/bin/restclient @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'restclient' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rest-client", "restclient") diff --git a/bin/ri b/bin/ri new file mode 100755 index 00000000..0d19636d --- /dev/null +++ b/bin/ri @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'ri' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rdoc", "ri") diff --git a/bin/rspec b/bin/rspec new file mode 100755 index 00000000..93e191c2 --- /dev/null +++ b/bin/rspec @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rspec' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rspec-core", "rspec") diff --git a/bin/rubocop b/bin/rubocop index f8c5cfbf..d73598dc 100755 --- a/bin/rubocop +++ b/bin/rubocop @@ -10,17 +10,6 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) -bundle_binstub = File.expand_path("bundle", __dir__) - -if File.file?(bundle_binstub) - if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300)) - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - require "rubygems" require "bundler/setup" diff --git a/bin/rubocop-gradual b/bin/rubocop-gradual new file mode 100755 index 00000000..3d21eeae --- /dev/null +++ b/bin/rubocop-gradual @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'rubocop-gradual' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rubocop-gradual", "rubocop-gradual") diff --git a/bin/ruby-parse b/bin/ruby-parse new file mode 100755 index 00000000..5dc6740f --- /dev/null +++ b/bin/ruby-parse @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'ruby-parse' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("parser", "ruby-parse") diff --git a/bin/ruby-rewrite b/bin/ruby-rewrite new file mode 100755 index 00000000..47cef49c --- /dev/null +++ b/bin/ruby-rewrite @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'ruby-rewrite' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("parser", "ruby-rewrite") diff --git a/bin/setup b/bin/setup new file mode 100755 index 00000000..dce67d86 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/bin/standardrb b/bin/standardrb new file mode 100755 index 00000000..3b77a156 --- /dev/null +++ b/bin/standardrb @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'standardrb' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("standard", "standardrb") diff --git a/bin/thor b/bin/thor new file mode 100755 index 00000000..d4f29ff3 --- /dev/null +++ b/bin/thor @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'thor' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("thor", "thor") diff --git a/bin/yard b/bin/yard new file mode 100755 index 00000000..ad94bbde --- /dev/null +++ b/bin/yard @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'yard' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("yard", "yard") diff --git a/bin/yard-junk b/bin/yard-junk new file mode 100755 index 00000000..6cd0f230 --- /dev/null +++ b/bin/yard-junk @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'yard-junk' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("yard-junk", "yard-junk") diff --git a/bin/yardoc b/bin/yardoc new file mode 100755 index 00000000..5ed01e71 --- /dev/null +++ b/bin/yardoc @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'yardoc' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("yard", "yardoc") diff --git a/bin/yri b/bin/yri new file mode 100755 index 00000000..80fa3d7a --- /dev/null +++ b/bin/yri @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'yri' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("yard", "yri") diff --git a/examples/twitter.rb b/examples/twitter.rb index 3df2a465..f96d8dba 100755 --- a/examples/twitter.rb +++ b/examples/twitter.rb @@ -32,10 +32,11 @@ exit 1 end -consumer = OAuth::Consumer.new \ +consumer = OAuth::Consumer.new( options[:consumer_key], options[:consumer_secret], - site: "https://api.twitter.com" + site: "https://api.twitter.com", +) access_token = OAuth::AccessToken.new(consumer) diff --git a/examples/yql.rb b/examples/yql.rb index 13f8abab..84a3cf86 100755 --- a/examples/yql.rb +++ b/examples/yql.rb @@ -33,10 +33,11 @@ exit 1 end -consumer = OAuth::Consumer.new \ +consumer = OAuth::Consumer.new( options[:consumer_key], options[:consumer_secret], - site: "http://query.yahooapis.com" + site: "http://query.yahooapis.com", +) access_token = OAuth::AccessToken.new(consumer) diff --git a/gemfiles/README.md b/gemfiles/README.md deleted file mode 100644 index 076c7059..00000000 --- a/gemfiles/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# History - -This is a ruby library which is intended to be used in creating Ruby Consumer -and Service Provider applications. It is NOT a Rails plugin, but could easily -be used for the foundation for such a Rails plugin. - -This gem was originally extracted from @pelle's [oauth-plugin](https://github.com/pelle/oauth-plugin) -gem. After extraction that gem was made to depend on this gem. - -Unfortunately, this gem does have some Rails related bits that are -**optional** to load. You don't need Rails! The Rails bits may be pulled out -into a separate gem after the release of version 1.0 of this gem. - -These `gemfiles` help with testing this gem against various versions of Rails-ish-ness. - -```ruby -gem "actionpack", [">= 6", "< 8"] -``` - -# *.gemfile Naming - -In the naming of gemfiles, we will use the below shorthand for actionpack and version - -| Gem | Version | Gemfile | -|------------|---------|------------| -| actionpack | ~> 6.0 | a6.gemfile | -| actionpack | ~> 7.0 | a7.gemfile | - -# References - -Compatibility Matrix for Ruby and Rails: -* https://www.fastruby.io/blog/ruby/rails/versions/compatibility-table.html diff --git a/gemfiles/a6.gemfile b/gemfiles/a6.gemfile deleted file mode 100644 index d6a1d29a..00000000 --- a/gemfiles/a6.gemfile +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -# See README.md in this directory - -gem "actionpack", "~> 6.0" - -eval_gemfile "../Gemfile" diff --git a/gemfiles/a7.gemfile b/gemfiles/a7.gemfile deleted file mode 100644 index d2613f79..00000000 --- a/gemfiles/a7.gemfile +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -# See README.md in this directory - -gem "actionpack", "~> 7.0" - -eval_gemfile "../Gemfile" diff --git a/gemfiles/audit.gemfile b/gemfiles/audit.gemfile new file mode 100644 index 00000000..d0b46ac0 --- /dev/null +++ b/gemfiles/audit.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs.gemfile") diff --git a/gemfiles/coverage.gemfile b/gemfiles/coverage.gemfile new file mode 100644 index 00000000..569b678c --- /dev/null +++ b/gemfiles/coverage.gemfile @@ -0,0 +1,11 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/coverage.gemfile") + +eval_gemfile("modular/optional.gemfile") + +eval_gemfile("modular/x_std_libs.gemfile") diff --git a/gemfiles/current.gemfile b/gemfiles/current.gemfile new file mode 100644 index 00000000..d0b46ac0 --- /dev/null +++ b/gemfiles/current.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs.gemfile") diff --git a/gemfiles/dep_heads.gemfile b/gemfiles/dep_heads.gemfile new file mode 100644 index 00000000..6b16b2f2 --- /dev/null +++ b/gemfiles/dep_heads.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + +gemspec path: "../" + +eval_gemfile("modular/runtime_heads.gemfile") diff --git a/gemfiles/head.gemfile b/gemfiles/head.gemfile new file mode 100644 index 00000000..2df7a424 --- /dev/null +++ b/gemfiles/head.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gem "benchmark", "~> 0.4", ">= 0.4.1" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs.gemfile") diff --git a/gemfiles/modular/a5.gemfile b/gemfiles/modular/a5.gemfile new file mode 100644 index 00000000..43c06115 --- /dev/null +++ b/gemfiles/modular/a5.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 2.2 +gem "actionpack", "~> 5.2.8.1" diff --git a/gemfiles/modular/a6.0.gemfile b/gemfiles/modular/a6.0.gemfile new file mode 100644 index 00000000..1533f5b8 --- /dev/null +++ b/gemfiles/modular/a6.0.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 2.5 +gem "actionpack", "~> 6.0.6.1" diff --git a/gemfiles/modular/a6.1.gemfile b/gemfiles/modular/a6.1.gemfile new file mode 100644 index 00000000..be29797e --- /dev/null +++ b/gemfiles/modular/a6.1.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 2.6 +gem "actionpack", "~> 6.1.7.10" diff --git a/gemfiles/modular/a7.gemfile b/gemfiles/modular/a7.gemfile new file mode 100644 index 00000000..d261f9f9 --- /dev/null +++ b/gemfiles/modular/a7.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 2.7 +gem "actionpack", "~> 7.2.2.2" diff --git a/gemfiles/modular/a8.gemfile b/gemfiles/modular/a8.gemfile new file mode 100644 index 00000000..221f2248 --- /dev/null +++ b/gemfiles/modular/a8.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 3.2 +gem "actionpack", "~> 8.0" diff --git a/gemfiles/modular/coverage.gemfile b/gemfiles/modular/coverage.gemfile new file mode 100644 index 00000000..ee32b0a1 --- /dev/null +++ b/gemfiles/modular/coverage.gemfile @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# We run code coverage on the latest version of Ruby only. + +# Coverage +gem "kettle-soup-cover", "~> 1.0", ">= 1.0.10", require: false diff --git a/gemfiles/modular/debug.gemfile b/gemfiles/modular/debug.gemfile new file mode 100644 index 00000000..3e86091c --- /dev/null +++ b/gemfiles/modular/debug.gemfile @@ -0,0 +1,13 @@ +# Ex-Standard Library gems +gem "irb", "~> 1.15", ">= 1.15.2" # removed from stdlib in 3.5 + +platform :mri do + # Debugging - Ensure ENV["DEBUG"] == "true" to use debuggers within spec suite + # Use binding.break, binding.b, or debugger in code + gem "debug", ">= 1.1" # ruby >= 2.7 + + # Dev Console - Binding.pry - Irb replacement + # gem "pry", "~> 0.14" # ruby >= 2.0 +end + +gem "gem_bench", "~> 2.0", ">= 2.0.5" diff --git a/gemfiles/modular/documentation.gemfile b/gemfiles/modular/documentation.gemfile new file mode 100644 index 00000000..78533908 --- /dev/null +++ b/gemfiles/modular/documentation.gemfile @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# Documentation +gem "kramdown", "~> 2.5", ">= 2.5.1" # Ruby >= 2.5 +gem "kramdown-parser-gfm", "~> 1.1" # Ruby >= 2.3 +gem "yard", "~> 0.9", ">= 0.9.37", require: false +gem "yard-junk", "~> 0.0", ">= 0.0.10", github: "pboling/yard-junk", branch: "next", require: false +gem "yard-relative_markdown_links", "~> 0.5.0" + +# Std Lib extractions +gem "rdoc", "~> 6.11" diff --git a/gemfiles/modular/erb/r2.3/default.gemfile b/gemfiles/modular/erb/r2.3/default.gemfile new file mode 100644 index 00000000..a38f952f --- /dev/null +++ b/gemfiles/modular/erb/r2.3/default.gemfile @@ -0,0 +1,6 @@ +# The cake is a lie. +# erb v2.2, the oldest release on RubyGems.org, was never compatible with Ruby 2.3. +# In addition, erb does not follow SemVer, and old rubies get dropped in a patch. +# This means we have no choice but to use the erb that shipped with Ruby 2.3 +# /opt/hostedtoolcache/Ruby/2.3.8/x64/lib/ruby/gems/2.3.0/gems/erb-2.2.2/lib/erb.rb:670:in `prepare_trim_mode': undefined method `match?' for "-":String (NoMethodError) +# gem "erb", ">= 2.2" # ruby >= 2.3.0 diff --git a/gemfiles/modular/erb/r2.6/v2.2.gemfile b/gemfiles/modular/erb/r2.6/v2.2.gemfile new file mode 100644 index 00000000..7cd85745 --- /dev/null +++ b/gemfiles/modular/erb/r2.6/v2.2.gemfile @@ -0,0 +1,3 @@ +# Ruby >= 2.3.0 (claimed, but not true, minimum support is Ruby 2.4) +# Last version supporting Ruby <= 2.6 +gem "erb", "~> 2.2.2" diff --git a/gemfiles/modular/erb/r2/v3.0.gemfile b/gemfiles/modular/erb/r2/v3.0.gemfile new file mode 100644 index 00000000..c03bd8d8 --- /dev/null +++ b/gemfiles/modular/erb/r2/v3.0.gemfile @@ -0,0 +1 @@ +gem "erb", "~> 3.0" # ruby >= 2.7.0 diff --git a/gemfiles/modular/erb/r3.1/v4.0.gemfile b/gemfiles/modular/erb/r3.1/v4.0.gemfile new file mode 100644 index 00000000..2e9046d8 --- /dev/null +++ b/gemfiles/modular/erb/r3.1/v4.0.gemfile @@ -0,0 +1,2 @@ +# last version compatible with Ruby 3.1 +gem "erb", "~> 4.0" # ruby >= 2.7.0 diff --git a/gemfiles/modular/erb/r3/v5.0.gemfile b/gemfiles/modular/erb/r3/v5.0.gemfile new file mode 100644 index 00000000..97033fa7 --- /dev/null +++ b/gemfiles/modular/erb/r3/v5.0.gemfile @@ -0,0 +1 @@ +gem "erb", "~> 5.0" # ruby >= 3.2.0 diff --git a/gemfiles/modular/erb/vHEAD.gemfile b/gemfiles/modular/erb/vHEAD.gemfile new file mode 100644 index 00000000..65f8433e --- /dev/null +++ b/gemfiles/modular/erb/vHEAD.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 3.2 (dependency of kettle-dev) +gem "erb", github: "ruby/erb", branch: "master" diff --git a/gemfiles/modular/injected.gemfile b/gemfiles/modular/injected.gemfile new file mode 100644 index 00000000..175b724a --- /dev/null +++ b/gemfiles/modular/injected.gemfile @@ -0,0 +1,60 @@ +# NOTE: It is preferable to list development dependencies in the gemspec due to increased +# visibility and discoverability on RubyGems.org. +# However, this gem sits underneath all my other gems, and also "depends on" many of them. +# So instead of depending on them directly it injects them into the other gem's gemspec on install. +# This gem, and its injected dev dependencies, will install on Ruby down to 2.3.x. +# This gem does not inject runtime dependencies. +# Thus, dev dependencies injected into gemspecs must have +# +# required_ruby_version ">= 2.3" (or lower) +# +# Development dependencies that require strictly newer Ruby versions should be in a "gemfile", +# and preferably a modular one (see gemfiles/modular/*.gemfile). + +# Security +gem "bundler-audit", "~> 0.9.2" # ruby >= 2.0.0 + +# Tasks +gem "rake", "~> 13.0" # ruby >= 2.2.0 + +# Debugging +gem "require_bench", "~> 1.0", ">= 1.0.4" # ruby >= 2.2.0 + +# Testing +gem "appraisal2", "~> 3.0" # ruby >= 1.8.7, for testing against multiple versions of dependencies +gem "kettle-test", "~> 1.0" # ruby >= 2.3 +gem "rspec-pending_for" # ruby >= 2.3, used to skip specs on incompatible Rubies + +# Releasing +gem "ruby-progressbar", "~> 1.13" # ruby >= 0 +gem "stone_checksums", "~> 1.0", ">= 1.0.2" # ruby >= 2.2.0 + +# Git integration (optional) +# The 'git' gem is optional; kettle-dev falls back to shelling out to `git` if it is not present. +# The current release of the git gem depends on activesupport, which makes it too heavy to depend on directly +# Compatibility with the git gem is tested via appraisals instead. +# gem("git", ">= 1.19.1") # ruby >= 2.3 + +# Development tasks +gem "gitmoji-regex", "~> 1.0", ">= 1.0.3" # ruby >= 2.3.0 + +# The cake is a lie. erb v2.2, the oldest release on RubyGems.org, was never compatible with Ruby 2.3. +# This means we have no choice but to use the erb that shipped with Ruby 2.3 +# /opt/hostedtoolcache/Ruby/2.3.8/x64/lib/ruby/gems/2.3.0/gems/erb-2.2.2/lib/erb.rb:670:in `prepare_trim_mode': undefined method `match?' for "-":String (NoMethodError) +# gem "erb", ">= 2.2" # ruby >= 2.3.0, not SemVer, old rubies get dropped in a patch. + +# HTTP recording for deterministic specs +# It seems that somehow just having a newer version of appraisal installed breaks +# Ruby 2.3 and 2.4 even if their bundle specifies an older version, +# and as a result it can only be a dependency in the appraisals. +# | An error occurred while loading spec_helper. +# | Failure/Error: require "vcr" +# | +# | NoMethodError: +# | undefined method `delete_prefix' for "CONTENT_LENGTH":String +# | # ./spec/config/vcr.rb:3:in `require' +# | # ./spec/config/vcr.rb:3:in `' +# | # ./spec/spec_helper.rb:8:in `require_relative' +# | # ./spec/spec_helper.rb:8:in `' +# gem "vcr", ">= 4" # 6.0 claims to support ruby >= 2.3, but fails on ruby 2.4 +# gem "webmock", ">= 3" # Last version to support ruby >= 2.3 diff --git a/gemfiles/modular/mutex_m/r2.4/v0.1.gemfile b/gemfiles/modular/mutex_m/r2.4/v0.1.gemfile new file mode 100644 index 00000000..cabf9806 --- /dev/null +++ b/gemfiles/modular/mutex_m/r2.4/v0.1.gemfile @@ -0,0 +1,3 @@ +# Ruby >= 0 +# Last version supporting Ruby <= 2.4 +gem "mutex_m", "~> 0.1" diff --git a/gemfiles/modular/mutex_m/r2/v0.3.gemfile b/gemfiles/modular/mutex_m/r2/v0.3.gemfile new file mode 100644 index 00000000..42e9d9bd --- /dev/null +++ b/gemfiles/modular/mutex_m/r2/v0.3.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 2.5 +gem "mutex_m", "~> 0.2" diff --git a/gemfiles/modular/mutex_m/r3/v0.3.gemfile b/gemfiles/modular/mutex_m/r3/v0.3.gemfile new file mode 100644 index 00000000..42e9d9bd --- /dev/null +++ b/gemfiles/modular/mutex_m/r3/v0.3.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 2.5 +gem "mutex_m", "~> 0.2" diff --git a/gemfiles/modular/mutex_m/vHEAD.gemfile b/gemfiles/modular/mutex_m/vHEAD.gemfile new file mode 100644 index 00000000..8af3b6fc --- /dev/null +++ b/gemfiles/modular/mutex_m/vHEAD.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 2.5 (dependency of omniauth) +gem "mutex_m", github: "ruby/mutex_m", branch: "master" diff --git a/gemfiles/modular/optional.gemfile b/gemfiles/modular/optional.gemfile new file mode 100644 index 00000000..dae6a950 --- /dev/null +++ b/gemfiles/modular/optional.gemfile @@ -0,0 +1 @@ +# Optional dependencies are not depended on directly, but may be used if present. diff --git a/gemfiles/modular/runtime_heads.gemfile b/gemfiles/modular/runtime_heads.gemfile new file mode 100644 index 00000000..6b083341 --- /dev/null +++ b/gemfiles/modular/runtime_heads.gemfile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Test against HEAD of runtime dependencies so we can proactively file bugs + +# Ruby >= 2.2 +gem "version_gem", github: "ruby-oauth/version_gem", branch: "main" + +eval_gemfile("x_std_libs/vHEAD.gemfile") diff --git a/gemfiles/modular/stringio/r2.4/v0.0.2.gemfile b/gemfiles/modular/stringio/r2.4/v0.0.2.gemfile new file mode 100644 index 00000000..94021cf3 --- /dev/null +++ b/gemfiles/modular/stringio/r2.4/v0.0.2.gemfile @@ -0,0 +1,4 @@ +# !!WARNING!! +# NOT SEMVER +# Last version to support Ruby <= 2.5 +gem "stringio", ">= 0.0.2" diff --git a/gemfiles/modular/stringio/r2/v3.0.gemfile b/gemfiles/modular/stringio/r2/v3.0.gemfile new file mode 100644 index 00000000..e85bb18e --- /dev/null +++ b/gemfiles/modular/stringio/r2/v3.0.gemfile @@ -0,0 +1,5 @@ +# !!WARNING!! +# NOT SEMVER +# Version 3.0.7 dropped support for Ruby <= 2.7 +# Version 3.0.0 dropped support for Ruby <= 2.4 +gem "stringio", ">= 3.0" diff --git a/gemfiles/modular/stringio/r3/v3.0.gemfile b/gemfiles/modular/stringio/r3/v3.0.gemfile new file mode 100644 index 00000000..e85bb18e --- /dev/null +++ b/gemfiles/modular/stringio/r3/v3.0.gemfile @@ -0,0 +1,5 @@ +# !!WARNING!! +# NOT SEMVER +# Version 3.0.7 dropped support for Ruby <= 2.7 +# Version 3.0.0 dropped support for Ruby <= 2.4 +gem "stringio", ">= 3.0" diff --git a/gemfiles/modular/stringio/vHEAD.gemfile b/gemfiles/modular/stringio/vHEAD.gemfile new file mode 100644 index 00000000..5f2a7412 --- /dev/null +++ b/gemfiles/modular/stringio/vHEAD.gemfile @@ -0,0 +1,2 @@ +# Ruby >= 2.5 (dependency of omniauth) +gem "stringio", github: "ruby/stringio", branch: "master" diff --git a/gemfiles/modular/style.gemfile b/gemfiles/modular/style.gemfile new file mode 100644 index 00000000..9b5499aa --- /dev/null +++ b/gemfiles/modular/style.gemfile @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# We run rubocop on the latest version of Ruby, +# but in support of the oldest supported version of Ruby + +gem "reek", "~> 6.5" +# gem "rubocop", "~> 1.73", ">= 1.73.2" # constrained by standard +gem "rubocop-packaging", "~> 0.6", ">= 0.6.0" +gem "standard", ">= 1.50" +gem "rubocop-on-rbs", "~> 1.8" # ruby >= 3.1.0 + +# Std Lib extractions +gem "benchmark", "~> 0.4", ">= 0.4.1" # Removed from Std Lib in Ruby 3.5 + +if ENV.fetch("RUBOCOP_LTS_LOCAL", "false").casecmp("true").zero? + home = ENV["HOME"] + gem "rubocop-lts", path: "#{home}/src/rubocop-lts/rubocop-lts" + gem "rubocop-lts-rspec", path: "#{home}/src/rubocop-lts/rubocop-lts-rspec" + gem "rubocop-ruby2_3", path: "#{home}/src/rubocop-lts/rubocop-ruby2_3" + gem "standard-rubocop-lts", path: "#{home}/src/rubocop-lts/standard-rubocop-lts" +else + gem "rubocop-lts", "~> 10.0" + gem "rubocop-ruby2_3" + gem "rubocop-rspec", "~> 3.6" +end diff --git a/gemfiles/modular/x_std_libs.gemfile b/gemfiles/modular/x_std_libs.gemfile new file mode 100644 index 00000000..cb677752 --- /dev/null +++ b/gemfiles/modular/x_std_libs.gemfile @@ -0,0 +1,2 @@ +### Std Lib Extracted Gems +eval_gemfile "x_std_libs/r3/libs.gemfile" diff --git a/gemfiles/modular/x_std_libs/r2.3/libs.gemfile b/gemfiles/modular/x_std_libs/r2.3/libs.gemfile new file mode 100644 index 00000000..2fee8b60 --- /dev/null +++ b/gemfiles/modular/x_std_libs/r2.3/libs.gemfile @@ -0,0 +1,3 @@ +eval_gemfile "../../erb/r2.3/default.gemfile" +eval_gemfile "../../mutex_m/r2.4/v0.1.gemfile" +eval_gemfile "../../stringio/r2.4/v0.0.2.gemfile" diff --git a/gemfiles/modular/x_std_libs/r2.4/libs.gemfile b/gemfiles/modular/x_std_libs/r2.4/libs.gemfile new file mode 100644 index 00000000..c1bcbd8f --- /dev/null +++ b/gemfiles/modular/x_std_libs/r2.4/libs.gemfile @@ -0,0 +1,3 @@ +eval_gemfile "../../erb/r2.6/v2.2.gemfile" +eval_gemfile "../../mutex_m/r2.4/v0.1.gemfile" +eval_gemfile "../../stringio/r2.4/v0.0.2.gemfile" diff --git a/gemfiles/modular/x_std_libs/r2.6/libs.gemfile b/gemfiles/modular/x_std_libs/r2.6/libs.gemfile new file mode 100644 index 00000000..beac38c9 --- /dev/null +++ b/gemfiles/modular/x_std_libs/r2.6/libs.gemfile @@ -0,0 +1,3 @@ +eval_gemfile "../../erb/r2.6/v2.2.gemfile" +eval_gemfile "../../mutex_m/r2/v0.3.gemfile" +eval_gemfile "../../stringio/r2/v3.0.gemfile" diff --git a/gemfiles/modular/x_std_libs/r2/libs.gemfile b/gemfiles/modular/x_std_libs/r2/libs.gemfile new file mode 100644 index 00000000..441c4f03 --- /dev/null +++ b/gemfiles/modular/x_std_libs/r2/libs.gemfile @@ -0,0 +1,3 @@ +eval_gemfile "../../erb/r2/v3.0.gemfile" +eval_gemfile "../../mutex_m/r2/v0.3.gemfile" +eval_gemfile "../../stringio/r2/v3.0.gemfile" diff --git a/gemfiles/modular/x_std_libs/r3.1/libs.gemfile b/gemfiles/modular/x_std_libs/r3.1/libs.gemfile new file mode 100644 index 00000000..bdab5bde --- /dev/null +++ b/gemfiles/modular/x_std_libs/r3.1/libs.gemfile @@ -0,0 +1,3 @@ +eval_gemfile "../../erb/r3.1/v4.0.gemfile" +eval_gemfile "../../mutex_m/r3/v0.3.gemfile" +eval_gemfile "../../stringio/r3/v3.0.gemfile" diff --git a/gemfiles/modular/x_std_libs/r3/libs.gemfile b/gemfiles/modular/x_std_libs/r3/libs.gemfile new file mode 100644 index 00000000..c293a3dd --- /dev/null +++ b/gemfiles/modular/x_std_libs/r3/libs.gemfile @@ -0,0 +1,3 @@ +eval_gemfile "../../erb/r3/v5.0.gemfile" +eval_gemfile "../../mutex_m/r3/v0.3.gemfile" +eval_gemfile "../../stringio/r3/v3.0.gemfile" diff --git a/gemfiles/modular/x_std_libs/vHEAD.gemfile b/gemfiles/modular/x_std_libs/vHEAD.gemfile new file mode 100644 index 00000000..acc5ccbf --- /dev/null +++ b/gemfiles/modular/x_std_libs/vHEAD.gemfile @@ -0,0 +1,3 @@ +eval_gemfile "../erb/vHEAD.gemfile" +eval_gemfile "../mutex_m/vHEAD.gemfile" +eval_gemfile "../stringio/vHEAD.gemfile" diff --git a/gemfiles/ruby_2_3.gemfile b/gemfiles/ruby_2_3.gemfile new file mode 100644 index 00000000..d5a5dac6 --- /dev/null +++ b/gemfiles/ruby_2_3.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs/r2.3/libs.gemfile") diff --git a/gemfiles/ruby_2_4.gemfile b/gemfiles/ruby_2_4.gemfile new file mode 100644 index 00000000..5e2dc1ec --- /dev/null +++ b/gemfiles/ruby_2_4.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs/r2.4/libs.gemfile") diff --git a/gemfiles/ruby_2_5.gemfile b/gemfiles/ruby_2_5.gemfile new file mode 100644 index 00000000..42bf09a9 --- /dev/null +++ b/gemfiles/ruby_2_5.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs/r2.6/libs.gemfile") diff --git a/gemfiles/ruby_2_6.gemfile b/gemfiles/ruby_2_6.gemfile new file mode 100644 index 00000000..42bf09a9 --- /dev/null +++ b/gemfiles/ruby_2_6.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs/r2.6/libs.gemfile") diff --git a/gemfiles/ruby_2_7.gemfile b/gemfiles/ruby_2_7.gemfile new file mode 100644 index 00000000..c4d7f9b1 --- /dev/null +++ b/gemfiles/ruby_2_7.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs/r2/libs.gemfile") diff --git a/gemfiles/ruby_3_0.gemfile b/gemfiles/ruby_3_0.gemfile new file mode 100644 index 00000000..e94adf83 --- /dev/null +++ b/gemfiles/ruby_3_0.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs/r3.1/libs.gemfile") diff --git a/gemfiles/ruby_3_1.gemfile b/gemfiles/ruby_3_1.gemfile new file mode 100644 index 00000000..e94adf83 --- /dev/null +++ b/gemfiles/ruby_3_1.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs/r3.1/libs.gemfile") diff --git a/gemfiles/ruby_3_2.gemfile b/gemfiles/ruby_3_2.gemfile new file mode 100644 index 00000000..326b6d8b --- /dev/null +++ b/gemfiles/ruby_3_2.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs/r3/libs.gemfile") diff --git a/gemfiles/ruby_3_3.gemfile b/gemfiles/ruby_3_3.gemfile new file mode 100644 index 00000000..326b6d8b --- /dev/null +++ b/gemfiles/ruby_3_3.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/x_std_libs/r3/libs.gemfile") diff --git a/gemfiles/style.gemfile b/gemfiles/style.gemfile new file mode 100644 index 00000000..58d3714e --- /dev/null +++ b/gemfiles/style.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/style.gemfile") + +eval_gemfile("modular/x_std_libs.gemfile") diff --git a/gemfiles/unlocked_deps.gemfile b/gemfiles/unlocked_deps.gemfile new file mode 100644 index 00000000..31c29e3e --- /dev/null +++ b/gemfiles/unlocked_deps.gemfile @@ -0,0 +1,15 @@ +# This file was generated by Appraisal2 + +source "https://rubygems.org" + +gemspec path: "../" + +eval_gemfile("modular/coverage.gemfile") + +eval_gemfile("modular/documentation.gemfile") + +eval_gemfile("modular/style.gemfile") + +eval_gemfile("modular/optional.gemfile") + +eval_gemfile("modular/x_std_libs.gemfile") diff --git a/lib/oauth/client/action_controller_request.rb b/lib/oauth/client/action_controller_request.rb index 430b0b76..200df55e 100644 --- a/lib/oauth/client/action_controller_request.rb +++ b/lib/oauth/client/action_controller_request.rb @@ -35,19 +35,23 @@ def self.use_oauth? end def configure_oauth(consumer = nil, token = nil, options = {}) - @oauth_options = { consumer: consumer, - token: token, - scheme: "header", - signature_method: nil, - nonce: nil, - timestamp: nil }.merge(options) + @oauth_options = { + consumer: consumer, + token: token, + scheme: "header", + signature_method: nil, + nonce: nil, + timestamp: nil, + }.merge(options) end def apply_oauth! return unless ActionController::TestRequest.use_oauth? && @oauth_options - @oauth_helper = OAuth::Client::Helper.new(self, - @oauth_options.merge(request_uri: (respond_to?(:fullpath) ? fullpath : request_uri))) + @oauth_helper = OAuth::Client::Helper.new( + self, + @oauth_options.merge(request_uri: (respond_to?(:fullpath) ? fullpath : request_uri)), + ) @oauth_helper.amend_user_agent_header(env) send("set_oauth_#{@oauth_options[:scheme]}") @@ -62,6 +66,7 @@ def set_oauth_parameters @query_parameters.merge!(oauth_signature: @oauth_helper.signature) end - def set_oauth_query_string; end + def set_oauth_query_string + end end end diff --git a/lib/oauth/client/em_http.rb b/lib/oauth/client/em_http.rb index 9f4fa73e..36ab6d27 100644 --- a/lib/oauth/client/em_http.rb +++ b/lib/oauth/client/em_http.rb @@ -25,13 +25,15 @@ class HttpClient # # See Also: {OAuth core spec version 1.0, section 5.4.1}[http://oauth.net/core/1.0#rfc.section.5.4.1] def oauth!(http, consumer = nil, token = nil, options = {}) - options = { request_uri: normalized_oauth_uri(http), - consumer: consumer, - token: token, - scheme: "header", - signature_method: nil, - nonce: nil, - timestamp: nil }.merge(options) + options = { + request_uri: normalized_oauth_uri(http), + consumer: consumer, + token: token, + scheme: "header", + signature_method: nil, + nonce: nil, + timestamp: nil, + }.merge(options) @oauth_helper = OAuth::Client::Helper.new(self, options) __send__(:"set_oauth_#{options[:scheme]}") @@ -51,13 +53,15 @@ def oauth!(http, consumer = nil, token = nil, options = {}) # # See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1] def signature_base_string(http, consumer = nil, token = nil, options = {}) - options = { request_uri: normalized_oauth_uri(http), - consumer: consumer, - token: token, - scheme: "header", - signature_method: nil, - nonce: nil, - timestamp: nil }.merge(options) + options = { + request_uri: normalized_oauth_uri(http), + consumer: consumer, + token: token, + scheme: "header", + signature_method: nil, + nonce: nil, + timestamp: nil, + }.merge(options) OAuth::Client::Helper.new(self, options).signature_base_string end @@ -71,7 +75,7 @@ def normalize_uri encoded_query = encode_query(@conn, @req[:query]) path, query = encoded_query.split("?", 2) uri.query = query unless encoded_query.empty? - uri.path = path + uri.path = path uri end end @@ -80,10 +84,10 @@ def normalize_uri def combine_query(path, query, uri_query) combined_query = if query.is_a?(Hash) - query.map { |k, v| encode_param(k, v) }.join("&") - else - query.to_s - end + query.map { |k, v| encode_param(k, v) }.join("&") + else + query.to_s + end combined_query = [combined_query, uri_query].reject(&:empty?).join("&") unless uri_query.to_s.empty? combined_query.to_s.empty? ? path : "#{path}?#{combined_query}" end @@ -96,10 +100,10 @@ def normalized_oauth_uri(http) uri.port = http.port uri.scheme = if http.respond_to?(:use_ssl?) && http.use_ssl? - "https" - else - "http" - end + "https" + else + "http" + end uri.to_s end diff --git a/lib/oauth/client/helper.rb b/lib/oauth/client/helper.rb index 8e30a036..68eecdc4 100644 --- a/lib/oauth/client/helper.rb +++ b/lib/oauth/client/helper.rb @@ -37,29 +37,33 @@ def oauth_parameters "oauth_timestamp" => timestamp, "oauth_nonce" => nonce, "oauth_verifier" => options[:oauth_verifier], - "oauth_version" => (options[:oauth_version] || "1.0"), - "oauth_session_handle" => options[:oauth_session_handle] + "oauth_version" => options[:oauth_version] || "1.0", + "oauth_session_handle" => options[:oauth_session_handle], } allowed_empty_params = options[:allow_empty_params] if allowed_empty_params != true && !allowed_empty_params.is_a?(Array) - allowed_empty_params = allowed_empty_params == false ? [] : [allowed_empty_params] + allowed_empty_params = (allowed_empty_params == false) ? [] : [allowed_empty_params] end out.select! { |k, v| v.to_s != "" || allowed_empty_params == true || allowed_empty_params.include?(k) } out end def signature(extra_options = {}) - OAuth::Signature.sign(@request, { uri: options[:request_uri], - consumer: options[:consumer], - token: options[:token], - unsigned_parameters: options[:unsigned_parameters] }.merge(extra_options)) + OAuth::Signature.sign(@request, { + uri: options[:request_uri], + consumer: options[:consumer], + token: options[:token], + unsigned_parameters: options[:unsigned_parameters], + }.merge(extra_options)) end def signature_base_string(extra_options = {}) - OAuth::Signature.signature_base_string(@request, { uri: options[:request_uri], - consumer: options[:consumer], - token: options[:token], - parameters: oauth_parameters }.merge(extra_options)) + OAuth::Signature.signature_base_string(@request, { + uri: options[:request_uri], + consumer: options[:consumer], + token: options[:token], + parameters: oauth_parameters, + }.merge(extra_options)) end def token_request? diff --git a/lib/oauth/client/net_http.rb b/lib/oauth/client/net_http.rb index 8b00f5f7..904b79d3 100644 --- a/lib/oauth/client/net_http.rb +++ b/lib/oauth/client/net_http.rb @@ -58,20 +58,31 @@ def signature_base_string(http, consumer = nil, token = nil, options = {}) private def oauth_helper_options(http, consumer, token, options) - { request_uri: oauth_full_request_uri(http, options), + { + request_uri: oauth_full_request_uri(http, options), consumer: consumer, token: token, scheme: "header", signature_method: nil, nonce: nil, timestamp: nil, - body_hash_enabled: true }.merge(options) + body_hash_enabled: true, + }.merge(options) end def oauth_full_request_uri(http, options) uri = URI.parse(path) - uri.host = http.address - uri.port = http.port + + # Guard against strict doubles or alternative HTTP adapters that may not + # expose Net::HTTP's #address/#port API. Only set host/port when available; + # otherwise, leave them to be filled by other options (e.g., :site) or the + # request itself. + if http.respond_to?(:address) + uri.host ||= http.address + end + if http.respond_to?(:port) + uri.port ||= http.port + end if options[:request_endpoint] && options[:site] is_https = options[:site].match(%r{^https://}) @@ -79,11 +90,26 @@ def oauth_full_request_uri(http, options) uri.port ||= is_https ? 443 : 80 end - uri.scheme = if http.respond_to?(:use_ssl?) && http.use_ssl? - "https" - else - "http" - end + # Fall back to the provided site URL if host/scheme are still missing. + if options[:site] + begin + site_uri = URI.parse(options[:site]) + uri.host ||= site_uri.host + uri.scheme ||= site_uri.scheme + uri.port ||= site_uri.port + rescue URI::InvalidURIError + # ignore and use defaults below + end + end + + # As a last resort, ensure scheme/host/port are present to avoid nil errors + uri.scheme ||= if http.respond_to?(:use_ssl?) && http.use_ssl? + "https" + else + "http" + end + uri.host ||= "localhost" + uri.port ||= ((uri.scheme == "https") ? 443 : 80) uri.to_s end @@ -115,10 +141,10 @@ def set_oauth_query_string oauth_params_str = @oauth_helper.oauth_parameters.map { |k, v| [escape(k), escape(v)].join("=") }.join("&") uri = URI.parse(path) uri.query = if uri.query.to_s == "" - oauth_params_str - else - "#{uri.query}&#{oauth_params_str}" - end + oauth_params_str + else + "#{uri.query}&#{oauth_params_str}" + end @path = uri.to_s diff --git a/lib/oauth/consumer.rb b/lib/oauth/consumer.rb index 11ab1fcb..595aaef5 100644 --- a/lib/oauth/consumer.rb +++ b/lib/oauth/consumer.rb @@ -19,8 +19,11 @@ class Consumer end unless defined?(CA_FILE) - CA_FILES = %w[/etc/ssl/certs/ca-certificates.crt /etc/pki/tls/certs/ca-bundle.crt - /usr/share/curl/curl-ca-bundle.crt].freeze + CA_FILES = %w[ + /etc/ssl/certs/ca-certificates.crt + /etc/pki/tls/certs/ca-bundle.crt + /usr/share/curl/curl-ca-bundle.crt + ].freeze CA_FILES.each do |ca_file| if File.exist?(ca_file) CA_FILE = ca_file @@ -70,12 +73,12 @@ class Consumer # spec. Possible values are true and false body_hash_enabled: true, - oauth_version: "1.0" - } + oauth_version: "1.0", + }, ) attr_accessor :options, :key, :secret - attr_writer :site, :http + attr_writer :site, :http # Create a new consumer instance by passing it a configuration hash: # @@ -101,7 +104,7 @@ class Consumer # @photos=@access_token.get('/photos.xml') # def initialize(consumer_key, consumer_secret, options = {}) - @key = consumer_key + @key = consumer_key @secret = consumer_secret # ensure that keys are symbols @@ -116,12 +119,12 @@ def http_method def debug_output @debug_output ||= case @options[:debug_output] - when nil, false - when true - $stdout - else - @options[:debug_output] - end + when nil, false + when true + $stdout + else + @options[:debug_output] + end end # The HTTP object for the site. The HTTP Object is what you get when you do Net::HTTP.new @@ -132,7 +135,7 @@ def http # Contains the root URI for this site def uri(custom_uri = nil) if custom_uri - @uri = custom_uri + @uri = custom_uri @http = create_http # yike, oh well. less intrusive this way else # if no custom passed, we use existing, which, if unset, is set to site uri @uri ||= URI.parse(site) @@ -140,8 +143,14 @@ def uri(custom_uri = nil) end def get_access_token(request_token, request_options = {}, *arguments, &block) - response = token_request(http_method, (access_token_url? ? access_token_url : access_token_path), request_token, - request_options, *arguments, &block) + response = token_request( + http_method, + (access_token_url? ? access_token_url : access_token_path), + request_token, + request_options, + *arguments, + &block + ) OAuth::AccessToken.from_hash(self, response) end @@ -165,18 +174,23 @@ def get_request_token(request_options = {}, *arguments, &block) request_options[:oauth_callback] ||= OAuth::OUT_OF_BAND unless request_options[:exclude_callback] response = if block - token_request( - http_method, - (request_token_url? ? request_token_url : request_token_path), - nil, - request_options, - *arguments, - &block - ) - else - token_request(http_method, (request_token_url? ? request_token_url : request_token_path), nil, - request_options, *arguments) - end + token_request( + http_method, + (request_token_url? ? request_token_url : request_token_path), + nil, + request_options, + *arguments, + &block + ) + else + token_request( + http_method, + (request_token_url? ? request_token_url : request_token_path), + nil, + request_options, + *arguments, + ) + end OAuth::RequestToken.from_hash(self, response) end @@ -191,23 +205,23 @@ def get_request_token(request_options = {}, *arguments, &block) # @consumer.request(:post, '/people', @token, {}, @person.to_xml, { 'Content-Type' => 'application/xml' }) # def request(http_method, path, token = nil, request_options = {}, *arguments) - unless %r{^/}.match?(path) + unless %r{^/} =~ path @http = create_http(path) _uri = URI.parse(path) - path = "#{_uri.path}#{_uri.query ? "?#{_uri.query}" : ""}" + path = "#{_uri.path}#{"?#{_uri.query}" if _uri.query}" end # override the request with your own, this is useful for file uploads which Net::HTTP does not do req = create_signed_request(http_method, path, token, request_options, *arguments) - return nil if block_given? && (yield(req) == :done) + return if block_given? && (yield(req) == :done) rsp = http.request(req) # check for an error reported by the Problem Reporting extension # (https://wiki.oauth.net/ProblemReporting) # note: a 200 may actually be an error; check for an oauth_problem key to be sure if !(headers = rsp.to_hash["www-authenticate"]).nil? && - (h = headers.grep(/^OAuth /)).any? && - h.first.include?("oauth_problem") + (h = headers.grep(/^OAuth /)).any? && + h.first.include?("oauth_problem") # puts "Header: #{h.first}" @@ -247,7 +261,7 @@ def token_request(http_method, path, token = nil, request_options = {}, *argumen # TODO this also drops subsequent values from multi-valued keys CGI.parse(response.body).each_with_object({}) do |(k, v), h| h[k.strip.to_sym] = v.first - h[k.strip] = v.first + h[k.strip] = v.first end end when (300..399) @@ -286,7 +300,7 @@ def site end def request_endpoint - return nil if @options[:request_endpoint].nil? + return if @options[:request_endpoint].nil? @options[:request_endpoint].to_s end @@ -355,24 +369,30 @@ def create_http(_url = nil) _url = request_endpoint unless request_endpoint.nil? our_uri = if _url.nil? || _url[0] =~ %r{^/} - URI.parse(site) - else - your_uri = URI.parse(_url) - if your_uri.host.nil? - # If the _url is a path, missing the leading slash, then it won't have a host, - # and our_uri *must* have a host, so we parse site instead. - URI.parse(site) - else - your_uri - end - end + URI.parse(site) + else + your_uri = URI.parse(_url) + if your_uri.host.nil? + # If the _url is a path, missing the leading slash, then it won't have a host, + # and our_uri *must* have a host, so we parse site instead. + URI.parse(site) + else + your_uri + end + end if proxy.nil? http_object = Net::HTTP.new(our_uri.host, our_uri.port) else proxy_uri = proxy.is_a?(URI) ? proxy : URI.parse(proxy) - http_object = Net::HTTP.new(our_uri.host, our_uri.port, proxy_uri.host, proxy_uri.port, proxy_uri.user, - proxy_uri.password) + http_object = Net::HTTP.new( + our_uri.host, + our_uri.port, + proxy_uri.host, + proxy_uri.port, + proxy_uri.user, + proxy_uri.password, + ) end http_object.use_ssl = (our_uri.scheme == "https") @@ -453,7 +473,7 @@ def create_http_request(http_method, path, *arguments) end def marshal_dump(*_args) - { key: @key, secret: @secret, options: @options } + {key: @key, secret: @secret, options: @options} end def marshal_load(data) diff --git a/lib/oauth/errors/problem.rb b/lib/oauth/errors/problem.rb index 55e12be2..6643a557 100644 --- a/lib/oauth/errors/problem.rb +++ b/lib/oauth/errors/problem.rb @@ -7,7 +7,7 @@ class Problem < OAuth::Unauthorized def initialize(problem, request = nil, params = {}) super(request) @problem = problem - @params = params + @params = params end def to_s diff --git a/lib/oauth/helper.rb b/lib/oauth/helper.rb index d22e8286..3b46b129 100644 --- a/lib/oauth/helper.rb +++ b/lib/oauth/helper.rb @@ -18,11 +18,11 @@ def escape(value) end def _escape(string) - URI::DEFAULT_PARSER.escape(string, OAuth::RESERVED_CHARACTERS) + URI::RFC2396_PARSER.escape(string, OAuth::RESERVED_CHARACTERS) end def unescape(value) - URI::DEFAULT_PARSER.unescape(value.gsub("+", "%2B")) + URI::RFC2396_PARSER.unescape(value.gsub("+", "%2B")) end # Generate a random key of up to +size+ bytes. The value returned is Base64 encoded with non-word @@ -31,7 +31,7 @@ def generate_key(size = 32) Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/, "") end - alias generate_nonce generate_key + alias_method :generate_nonce, :generate_key def generate_timestamp # :nodoc: Time.now.to_i.to_s diff --git a/lib/oauth/oauth.rb b/lib/oauth/oauth.rb index de3c71c6..b8b09b31 100644 --- a/lib/oauth/oauth.rb +++ b/lib/oauth/oauth.rb @@ -6,9 +6,18 @@ module OAuth OUT_OF_BAND = "oob" # required parameters, per sections 6.1.1, 6.3.1, and 7 - PARAMETERS = %w[oauth_callback oauth_consumer_key oauth_token - oauth_signature_method oauth_timestamp oauth_nonce oauth_verifier - oauth_version oauth_signature oauth_body_hash].freeze + PARAMETERS = %w[ + oauth_callback + oauth_consumer_key + oauth_token + oauth_signature_method + oauth_timestamp + oauth_nonce + oauth_verifier + oauth_version + oauth_signature + oauth_body_hash + ].freeze # reserved character regexp, per section 5.1 RESERVED_CHARACTERS = /[^a-zA-Z0-9\-._~]/.freeze diff --git a/lib/oauth/request_proxy/action_controller_request.rb b/lib/oauth/request_proxy/action_controller_request.rb index 79a1d30f..fb192459 100644 --- a/lib/oauth/request_proxy/action_controller_request.rb +++ b/lib/oauth/request_proxy/action_controller_request.rb @@ -43,11 +43,11 @@ def parameters_for_signature params << request.raw_post if raw_post_signature? end - params. - join("&").split("&"). - reject { |s| s.match(/\A\s*\z/) }. - map { |p| p.split("=").map { |esc| CGI.unescape(esc) } }. - reject { |kv| kv[0] == "oauth_signature" } + params + .join("&").split("&") + .reject { |s| s.match(/\A\s*\z/) } + .map { |p| p.split("=").map { |esc| CGI.unescape(esc) } } + .reject { |kv| kv[0] == "oauth_signature" } end def raw_post_signature? diff --git a/lib/oauth/request_proxy/base.rb b/lib/oauth/request_proxy/base.rb index e175b461..7107d63e 100644 --- a/lib/oauth/request_proxy/base.rb +++ b/lib/oauth/request_proxy/base.rb @@ -65,12 +65,12 @@ def oauth_version end # TODO: deprecate these - alias consumer_key oauth_consumer_key - alias token oauth_token - alias nonce oauth_nonce - alias timestamp oauth_timestamp - alias signature oauth_signature - alias signature_method oauth_signature_method + alias_method :consumer_key, :oauth_consumer_key + alias_method :token, :oauth_token + alias_method :nonce, :oauth_nonce + alias_method :timestamp, :oauth_timestamp + alias_method :signature, :oauth_signature + alias_method :signature_method, :oauth_signature_method ## Parameter accessors @@ -97,7 +97,7 @@ def signature_and_unsigned_parameters # See 9.1.2 in specs def normalized_uri u = URI.parse(uri) - "#{u.scheme.downcase}://#{u.host.downcase}#{(u.scheme.casecmp("http").zero? && u.port != 80) || (u.scheme.casecmp("https").zero? && u.port != 443) ? ":#{u.port}" : ""}#{u.path && u.path != "" ? u.path : "/"}" + "#{u.scheme.downcase}://#{u.host.downcase}#{":#{u.port}" if (u.scheme.casecmp("http").zero? && u.port != 80) || (u.scheme.casecmp("https").zero? && u.port != 443)}#{(u.path && u.path != "") ? u.path : "/"}" end # See 9.1.1. in specs Normalize Request Parameters @@ -130,14 +130,14 @@ def signed? def signed_uri(with_oauth: true) if signed? params = if with_oauth - parameters - else - non_oauth_parameters - end + parameters + else + non_oauth_parameters + end [uri, normalize(params)].join("?") else - warn "This request has not yet been signed!" + warn("This request has not yet been signed!") end end diff --git a/lib/oauth/request_proxy/em_http_request.rb b/lib/oauth/request_proxy/em_http_request.rb index 4ed830d9..c908f5bb 100644 --- a/lib/oauth/request_proxy/em_http_request.rb +++ b/lib/oauth/request_proxy/em_http_request.rb @@ -41,10 +41,10 @@ def all_parameters def query_parameters quer = request.req[:query] hash_quer = if quer.respond_to?(:merge) - quer - else - CGI.parse(quer.to_s) - end + quer + else + CGI.parse(quer.to_s) + end CGI.parse(request.conn.query.to_s).merge(hash_quer) end diff --git a/lib/oauth/request_proxy/jabber_request.rb b/lib/oauth/request_proxy/jabber_request.rb index 7f875b9b..f000921f 100644 --- a/lib/oauth/request_proxy/jabber_request.rb +++ b/lib/oauth/request_proxy/jabber_request.rb @@ -18,8 +18,15 @@ def parameters oauth = @request.get_elements("//oauth").first return @params unless oauth - %w[ oauth_token oauth_consumer_key oauth_signature_method oauth_signature - oauth_timestamp oauth_nonce oauth_version ].each do |param| + %w[ + oauth_token + oauth_consumer_key + oauth_signature_method + oauth_signature + oauth_timestamp + oauth_nonce + oauth_version + ].each do |param| next unless (element = oauth.first_element(param)) @params[param] = element.text diff --git a/lib/oauth/request_proxy/net_http.rb b/lib/oauth/request_proxy/net_http.rb index 849c3fa1..67a141de 100644 --- a/lib/oauth/request_proxy/net_http.rb +++ b/lib/oauth/request_proxy/net_http.rb @@ -69,7 +69,7 @@ def post_params end def auth_header_params - return nil unless request["Authorization"] && request["Authorization"][0, 5] == "OAuth" + return unless request["Authorization"] && request["Authorization"][0, 5] == "OAuth" request["Authorization"] end diff --git a/lib/oauth/request_proxy/rest_client_request.rb b/lib/oauth/request_proxy/rest_client_request.rb index 42ef8ae2..1e9836f5 100644 --- a/lib/oauth/request_proxy/rest_client_request.rb +++ b/lib/oauth/request_proxy/rest_client_request.rb @@ -34,7 +34,8 @@ def query_params query ? CGI.parse(query) : {} end - def request_params; end + def request_params + end def post_parameters # Post params are only used if posting form data @@ -52,9 +53,9 @@ def query_string_to_hash(query) query.split("&").inject({}) do |result, q| k, v = q.split("=") if !v.nil? - result.merge({ k => v }) + result.merge({k => v}) elsif !result.key?(k) - result.merge({ k => true }) + result.merge({k => true}) else result end diff --git a/lib/oauth/server.rb b/lib/oauth/server.rb index 033b978c..94db9f1e 100644 --- a/lib/oauth/server.rb +++ b/lib/oauth/server.rb @@ -7,12 +7,13 @@ module OAuth # This is mainly used to create consumer credentials and can pretty much be ignored if you want to create your own class Server include OAuth::Helper + attr_accessor :base_url @@server_paths = { request_token_path: "/oauth/request_token", authorize_path: "/oauth/authorize", - access_token_path: "/oauth/access_token" + access_token_path: "/oauth/access_token", } # Create a new server instance @@ -32,13 +33,16 @@ def generate_consumer_credentials(_params = {}) # mainly for testing purposes def create_consumer creds = generate_credentials - Consumer.new(creds[0], creds[1], - { - site: base_url, - request_token_path: request_token_path, - authorize_path: authorize_path, - access_token_path: access_token_path - }) + Consumer.new( + creds[0], + creds[1], + { + site: base_url, + request_token_path: request_token_path, + authorize_path: authorize_path, + access_token_path: access_token_path, + }, + ) end def request_token_path diff --git a/lib/oauth/signature/base.rb b/lib/oauth/signature/base.rb index a0f97f3e..90ad4ceb 100644 --- a/lib/oauth/signature/base.rb +++ b/lib/oauth/signature/base.rb @@ -43,7 +43,7 @@ def initialize(request, options = {}, &block) # override secrets based on the values returned from the block (if any) if block # consumer secret and token secret need to be looked up based on pieces of the request - secrets = yield block.arity == 1 ? request : [token, consumer_key, nonce, request.timestamp] + secrets = yield (block.arity == 1) ? request : [token, consumer_key, nonce, request.timestamp] if secrets.is_a?(Array) && secrets.size == 2 @token_secret = secrets[0] @consumer_secret = secrets[1] diff --git a/lib/oauth/signature/rsa/sha1.rb b/lib/oauth/signature/rsa/sha1.rb index 01cd1bc7..517da6b4 100644 --- a/lib/oauth/signature/rsa/sha1.rb +++ b/lib/oauth/signature/rsa/sha1.rb @@ -9,8 +9,8 @@ class SHA1 < OAuth::Signature::Base implements "rsa-sha1" def ==(other) - public_key.verify(OpenSSL::Digest.new("SHA1"), - Base64.decode64(other.is_a?(Array) ? other.first : other), signature_base_string) + decoded = Base64.decode64(other.is_a?(Array) ? other.first : other) + public_key.verify(OpenSSL::Digest.new("SHA1"), decoded, signature_base_string) end def public_key @@ -25,7 +25,14 @@ def public_key end def body_hash - Base64.encode64(OpenSSL::Digest.digest("SHA1", request.body || "")).chomp.delete("\n") + # Use SHA1 body hash with compatibility across OpenSSL versions + data = request.body || "" + begin + digest_bytes = OpenSSL::Digest.digest("SHA1", data) + rescue StandardError + digest_bytes = ::Digest::SHA1.digest(data) + end + Base64.encode64(digest_bytes).chomp.delete("\n") end private @@ -47,7 +54,7 @@ def digest options[:private_key] else consumer_secret - end + end, ) private_key.sign(OpenSSL::Digest.new("SHA1"), signature_base_string) diff --git a/lib/oauth/tokens/access_token.rb b/lib/oauth/tokens/access_token.rb index 9b89833b..08e7494c 100644 --- a/lib/oauth/tokens/access_token.rb +++ b/lib/oauth/tokens/access_token.rb @@ -8,7 +8,7 @@ class AccessToken < ConsumerToken def request(http_method, path, *arguments) request_uri = URI.parse(path) site_uri = consumer.uri - is_service_uri_different = (request_uri.absolute? && request_uri != site_uri) + is_service_uri_different = request_uri.absolute? && request_uri != site_uri begin consumer.uri(request_uri) if is_service_uri_different @response = super(http_method, path, *arguments) diff --git a/lib/oauth/tokens/consumer_token.rb b/lib/oauth/tokens/consumer_token.rb index 012faa03..0dd25aa1 100644 --- a/lib/oauth/tokens/consumer_token.rb +++ b/lib/oauth/tokens/consumer_token.rb @@ -4,7 +4,7 @@ module OAuth # Superclass for tokens used by OAuth Clients class ConsumerToken < Token attr_accessor :consumer, :params - attr_reader :response + attr_reader :response def self.from_hash(consumer, hash) token = new(consumer, hash[:oauth_token], hash[:oauth_token_secret]) @@ -15,7 +15,7 @@ def self.from_hash(consumer, hash) def initialize(consumer, token = "", secret = "") super(token, secret) @consumer = consumer - @params = {} + @params = {} end # Make a signed request using given http_method to the path diff --git a/lib/oauth/tokens/request_token.rb b/lib/oauth/tokens/request_token.rb index c35aca80..1a7edc59 100644 --- a/lib/oauth/tokens/request_token.rb +++ b/lib/oauth/tokens/request_token.rb @@ -6,14 +6,14 @@ module OAuth class RequestToken < ConsumerToken # Generate an authorization URL for user authorization def authorize_url(params = nil) - return nil if token.nil? + return if token.nil? params = (params || {}).merge(oauth_token: token) build_url(consumer.authorize_url, params) end def authenticate_url(params = nil) - return nil if token.nil? + return if token.nil? params = (params || {}).merge(oauth_token: token) build_url(consumer.authenticate_url, params) @@ -25,8 +25,13 @@ def callback_confirmed? # exchange for AccessToken on server def get_access_token(options = {}, *arguments) - response = consumer.token_request(consumer.http_method, - (consumer.access_token_url? ? consumer.access_token_url : consumer.access_token_path), self, options, *arguments) + response = consumer.token_request( + consumer.http_method, + (consumer.access_token_url? ? consumer.access_token_url : consumer.access_token_path), + self, + options, + *arguments, + ) OAuth::AccessToken.from_hash(consumer, response) end diff --git a/oauth.gemspec b/oauth.gemspec index a4776495..c47fece5 100644 --- a/oauth.gemspec +++ b/oauth.gemspec @@ -1,52 +1,30 @@ +# coding: utf-8 # frozen_string_literal: true -require_relative "lib/oauth/version" +gem_version = + if RUBY_VERSION >= "3.1" # rubocop:disable Gemspec/RubyVersionGlobalsUsage + # Loading Version into an anonymous module allows version.rb to get code coverage from SimpleCov! + # See: https://github.com/simplecov-ruby/simplecov/issues/557#issuecomment-2630782358 + # See: https://github.com/panorama-ed/memo_wise/pull/397 + Module.new.tap { |mod| Kernel.load("#{__dir__}/lib/oauth/version.rb", mod) }::OAuth::Version::VERSION + else + require_relative "lib/oauth/version" + OAuth::Version::VERSION + end Gem::Specification.new do |spec| - # "oauth-tty" was extracted from this gem with release 1.1 of this gem - # It is now a dependency for backward compatibility. - # The dependency will be removed with release 2.0, by April 2023. - spec.add_dependency("oauth-tty", ["~> 1.0", ">= 1.0.1"]) - spec.add_dependency("snaky_hash", "~> 2.0") - spec.add_dependency("version_gem", "~> 1.1") - - spec.cert_chain = ["certs/pboling.pem"] - spec.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $PROGRAM_NAME.end_with?("gem") - spec.name = "oauth" - spec.version = OAuth::Version::VERSION - spec.license = "MIT" - - spec.authors = ["Pelle Braendgaard", "Blaine Cook", "Larry Halff", "Jesse Clark", "Jon Crosby", - "Seth Fitzsimmons", "Matt Sanford", "Aaron Quint", "Peter Boling"] - spec.email = ["peter.boling@gmail.com", "oauth-ruby@googlegroups.com"] - spec.summary = "OAuth 1.0 Core Ruby implementation" - spec.description = "A Ruby wrapper for the original OAuth 1.0 spec." - - spec.homepage = "https://gitlab.com/oauth-xx/oauth" - spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = "#{spec.homepage}/-/tree/v#{spec.version}" - spec.metadata["changelog_uri"] = "#{spec.homepage}/-/blob/v#{spec.version}/CHANGELOG.md" - spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/-/issues" - spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/#{spec.name}/#{spec.version}" - spec.metadata["wiki_uri"] = "#{spec.homepage}/-/wiki" - spec.metadata["mailing_list_uri"] = "https://groups.google.com/g/oauth-ruby" - spec.metadata["funding_uri"] = "https://liberapay.com/pboling" - spec.metadata["rubygems_mfa_required"] = "true" + spec.version = gem_version + spec.authors = ["Pelle Braendgaard", "Blaine Cook", "Larry Halff", "Jesse Clark", "Jon Crosby", "Seth Fitzsimmons", "Matt Sanford", "Aaron Quint", "Peter Boling"] + spec.email = ["floss@galtzo.com", "oauth-ruby@googlegroups.com"] - spec.files = Dir[ - "lib/**/*", - "CHANGELOG.md", - "CODE_OF_CONDUCT.md", - "CONTRIBUTING.md", - "LICENSE.txt", - "README.md", - "SECURITY.md", - ] - - spec.required_ruby_version = ">= 2.7" + spec.summary = "🔑 OAuth 1.0a Core Ruby implementation" + spec.description = "🔑 A Ruby wrapper for the original OAuth 1.0a spec." + spec.homepage = "https://github.com/ruby-oauth/oauth" + spec.licenses = ["MIT"] + spec.required_ruby_version = ">= 2.3" spec.post_install_message = " -You have installed oauth version #{OAuth::Version::VERSION}, congratulations! +You have installed oauth version #{gem_version}, congratulations! Please see: • #{spec.homepage}/-/blob/main/SECURITY.md @@ -57,7 +35,7 @@ Major updates: • Update your local: git checkout master; git branch -m master main; git branch --unset-upstream; git branch -u origin/main 2. Github has been replaced with Gitlab; I wrote about some of the reasons here: • https://dev.to/galtzo/im-leaving-github-50ba -• Update your local: git remote set-url origin git@gitlab.com:oauth-xx/oauth.git +• Update your local: git remote set-url origin git@gitlab.com:ruby-oauth/oauth.git 3. Google Group is active (again)! • https://groups.google.com/g/oauth-ruby/c/QA_dtrXWXaE 4. Gitter Chat is active (still)! @@ -79,15 +57,152 @@ Please report issues, and support the project! Thanks, |7eter l-|. l3oling " + + # Linux distros often package gems and securely certify them independent + # of the official RubyGem certification process. Allowed via ENV["SKIP_GEM_SIGNING"] + # Ref: https://gitlab.com/ruby-oauth/version_gem/-/issues/3 + # Hence, only enable signing if `SKIP_GEM_SIGNING` is not set in ENV. + # See CONTRIBUTING.md + unless ENV.include?("SKIP_GEM_SIGNING") + user_cert = "certs/#{ENV.fetch("GEM_CERT_USER", ENV["USER"])}.pem" + cert_file_path = File.join(__dir__, user_cert) + cert_chain = cert_file_path.split(",") + cert_chain.select! { |fp| File.exist?(fp) } + if cert_file_path && cert_chain.any? + spec.cert_chain = cert_chain + if $PROGRAM_NAME.end_with?("gem") && ARGV[0] == "build" + spec.signing_key = File.join(Gem.user_home, ".ssh", "gem-private_key.pem") + end + end + end + + spec.metadata["homepage_uri"] = "https://#{spec.name.tr("_", "-")}.galtzo.com/" + spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/v#{spec.version}" + spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/v#{spec.version}/CHANGELOG.md" + spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues" + spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/#{spec.name}/#{spec.version}" + spec.metadata["funding_uri"] = "https://github.com/sponsors/pboling" + spec.metadata["wiki_uri"] = "#{spec.homepage}/wiki" + spec.metadata["news_uri"] = "https://www.railsbling.com/tags/#{spec.name}" + spec.metadata["discord_uri"] = "https://discord.gg/3qme4XHNKN" + spec.metadata["rubygems_mfa_required"] = "true" + + # Specify which files are part of the released package. + spec.files = Dir[ + # Executables and tasks + "exe/*", + "lib/**/*.rb", + "lib/**/*.rake", + # Signatures + "sig/**/*.rbs", + ] + + # Automatically included with gem package, no need to list again in files. + spec.extra_rdoc_files = Dir[ + # Files (alphabetical) + "CHANGELOG.md", + "CITATION.cff", + "CODE_OF_CONDUCT.md", + "CONTRIBUTING.md", + "FUNDING.md", + "LICENSE.txt", + "README.md", + "REEK", + "RUBOCOP.md", + "SECURITY.md", + ] + spec.rdoc_options += [ + "--title", + "#{spec.name} - #{spec.summary}", + "--main", + "README.md", + "--exclude", + "^sig/", + "--line-numbers", + "--inline-source", + "--quiet", + ] + spec.require_paths = ["lib"] + spec.bindir = "bin" + # files listed are relative paths from bindir above. + spec.executables = [] + + # "oauth-tty" was extracted from this gem with release 1.1 of this gem + # It is now a dependency for backward compatibility. + # The dependency will be removed with release 2.0, by April 2023. + spec.add_dependency("oauth-tty", ["~> 1.0", ">= 1.0.1"]) + spec.add_dependency("snaky_hash", "~> 2.0") + + # Utilities + spec.add_dependency("version_gem", "~> 1.1", ">= 1.1.9") # ruby >= 2.2.0 + + # NOTE: It is preferable to list development dependencies in the gemspec due to increased + # visibility and discoverability on RubyGems.org. + # However, development dependencies in gemspec will install on + # all versions of Ruby that will run in CI. + # This gem, and its gemspec runtime dependencies, will install on Ruby down to 2.3.x. + # This gem, and its gemspec development dependencies, will install on Ruby down to 2.3.x. + # This is because in CI easy installation of Ruby, via setup-ruby, is for >= 2.3. + # Thus, dev dependencies in gemspec must have + # + # required_ruby_version ">= 2.3" (or lower) + # + # Development dependencies that require strictly newer Ruby versions should be in a "gemfile", + # and preferably a modular one (see gemfiles/modular/*.gemfile). + spec.add_development_dependency("em-http-request", "~> 1.1.7") - spec.add_development_dependency("iconv") - spec.add_development_dependency("minitest", "~> 5.15.0") spec.add_development_dependency("mocha") - spec.add_development_dependency("rack", "~> 3.1") + spec.add_development_dependency("rack", ">= 2.0.0") spec.add_development_dependency("rack-test") - spec.add_development_dependency("rake", "~> 13.0") spec.add_development_dependency("rest-client") - spec.add_development_dependency("rubocop-lts", "~> 18.0") spec.add_development_dependency("typhoeus", ">= 0.1.13") - spec.add_development_dependency("webmock", "<= 3.26.0") + + # Dev, Test, & Release Tasks + spec.add_development_dependency("kettle-dev", "~> 1.1") # ruby >= 2.3.0 + + # Security + spec.add_development_dependency("bundler-audit", "~> 0.9.2") # ruby >= 2.0.0 + + # Tasks + spec.add_development_dependency("rake", "~> 13.0") # ruby >= 2.2.0 + + # Debugging + spec.add_development_dependency("require_bench", "~> 1.0", ">= 1.0.4") # ruby >= 2.2.0 + + # Testing + spec.add_development_dependency("appraisal2", "~> 3.0") # ruby >= 1.8.7, for testing against multiple versions of dependencies + spec.add_development_dependency("kettle-test", "~> 1.0") # ruby >= 2.3 + spec.add_development_dependency("rspec-pending_for", "~> 0.0", ">= 0.0.17") # ruby >= 2.3, used to skip specs on incompatible Rubies + + # Releasing + spec.add_development_dependency("ruby-progressbar", "~> 1.13") # ruby >= 0 + spec.add_development_dependency("stone_checksums", "~> 1.0", ">= 1.0.2") # ruby >= 2.2.0 + + # Git integration (optional) + # The 'git' gem is optional; oauth falls back to shelling out to `git` if it is not present. + # The current release of the git gem depends on activesupport, which makes it too heavy to depend on directly + # spec.add_dependency("git", ">= 1.19.1") # ruby >= 2.3 + + # Development tasks + # The cake is a lie. erb v2.2, the oldest release on RubyGems.org, was never compatible with Ruby 2.3. + # This means we have no choice but to use the erb that shipped with Ruby 2.3 + # /opt/hostedtoolcache/Ruby/2.3.8/x64/lib/ruby/gems/2.3.0/gems/erb-2.2.2/lib/erb.rb:670:in `prepare_trim_mode': undefined method `match?' for "-":String (NoMethodError) + # spec.add_development_dependency("erb", ">= 2.2") # ruby >= 2.3.0, not SemVer, old rubies get dropped in a patch. + spec.add_development_dependency("gitmoji-regex", "~> 1.0", ">= 1.0.3") # ruby >= 2.3.0 + + # HTTP recording for deterministic specs + # It seems that somehow just having a newer version of appraisal installed breaks + # Ruby 2.3 and 2.4 even if their bundle specifies an older version, + # and as a result it can only be a dependency in the appraisals. + # | An error occurred while loading spec_helper. + # | Failure/Error: require "vcr" + # | + # | NoMethodError: + # | undefined method `delete_prefix' for "CONTENT_LENGTH":String + # | # ./spec/config/vcr.rb:3:in `require' + # | # ./spec/config/vcr.rb:3:in `' + # | # ./spec/spec_helper.rb:8:in `require_relative' + # | # ./spec/spec_helper.rb:8:in `' + spec.add_development_dependency("vcr", ">= 4") # 6.0 claims to support ruby >= 2.3, but fails on ruby 2.4 + spec.add_development_dependency("webmock", ">= 3") # Last version to support ruby >= 2.3 end diff --git a/spec/config/debug.rb b/spec/config/debug.rb new file mode 100644 index 00000000..310c26c7 --- /dev/null +++ b/spec/config/debug.rb @@ -0,0 +1,4 @@ +load_debugger = ENV.fetch("DEBUG", "false").casecmp("true").zero? +puts "LOADING DEBUGGER: #{load_debugger}" if load_debugger + +require "debug" if load_debugger diff --git a/spec/config/rspec/rack_test.rb b/spec/config/rspec/rack_test.rb new file mode 100644 index 00000000..0fc97caf --- /dev/null +++ b/spec/config/rspec/rack_test.rb @@ -0,0 +1,5 @@ +require "rack/test" + +RSpec.configure do |config| + config.include Rack::Test::Methods +end diff --git a/spec/config/rspec/rspec_block_is_expected.rb b/spec/config/rspec/rspec_block_is_expected.rb new file mode 100644 index 00000000..ab140fa4 --- /dev/null +++ b/spec/config/rspec/rspec_block_is_expected.rb @@ -0,0 +1,2 @@ +require "rspec/block_is_expected" +require "rspec/block_is_expected/matchers/not" diff --git a/spec/config/rspec/rspec_core.rb b/spec/config/rspec/rspec_core.rb new file mode 100644 index 00000000..c2da671f --- /dev/null +++ b/spec/config/rspec/rspec_core.rb @@ -0,0 +1,11 @@ +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end diff --git a/spec/config/rspec/rspec_pending_for.rb b/spec/config/rspec/rspec_pending_for.rb new file mode 100644 index 00000000..a0fa7fd0 --- /dev/null +++ b/spec/config/rspec/rspec_pending_for.rb @@ -0,0 +1,6 @@ +# rspec-pending_for: enable skipping on incompatible Ruby versions +require "rspec/pending_for" + +RSpec.configure do |config| + config.include Rspec::PendingFor +end diff --git a/spec/config/rspec/version_gem.rb b/spec/config/rspec/version_gem.rb new file mode 100644 index 00000000..1517e9cc --- /dev/null +++ b/spec/config/rspec/version_gem.rb @@ -0,0 +1 @@ +require "version_gem/rspec" diff --git a/spec/config/vcr.rb b/spec/config/vcr.rb new file mode 100644 index 00000000..0c7362a8 --- /dev/null +++ b/spec/config/vcr.rb @@ -0,0 +1,12 @@ +# VCR/WebMock: record and replay HTTP to external services (RubyGems, GitHub, etc.) +require "webmock/rspec" +require "vcr" +VCR.configure do |c| + c.cassette_library_dir = File.join(__dir__, "..", "support", "fixtures", "vcr_cassettes") + c.hook_into :webmock + c.configure_rspec_metadata! + c.default_cassette_options = {record: :once} + # Allow localhost and code coverage server, block others by default when no cassette + c.ignore_localhost = true +end +WebMock.disable_net_connect!(allow_localhost: true) diff --git a/spec/config/warnings.rb b/spec/config/warnings.rb new file mode 100644 index 00000000..46dcc0bb --- /dev/null +++ b/spec/config/warnings.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Targeted silencing of a noisy deprecation coming from the oauth gem on Ruby >= 3.4. +# +# Example warning seen during specs: +# .../gems/oauth-1.1.0/lib/oauth/helper.rb:21: warning: URI::RFC3986_PARSER.escape is obsolete. Use URI::RFC2396_PARSER.escape explicitly. +# +# We only filter this single message to avoid hiding other useful warnings. +# This is test-only; it does not affect consumers of the library. +begin + original_warning_warn = Warning.method(:warn) + + Warning.define_singleton_method(:warn) do |message| + if message =~ /lib\/oauth\/helper\.rb:.*URI::RFC3986_PARSER\.escape is obsolete/ + # swallow this specific, known deprecation from the oauth gem + nil + else + original_warning_warn.call(message) + end + end +rescue StandardError # rubocop:disable Lint/SuppressedException -- best-effort; don't break older Rubies + # If Warning.method(:warn) is not available on this Ruby, or redefining fails, + # silently skip — tests can proceed without the filter. +end diff --git a/spec/oauth/backwards_compatibility_spec.rb b/spec/oauth/backwards_compatibility_spec.rb new file mode 100644 index 00000000..f691d24e --- /dev/null +++ b/spec/oauth/backwards_compatibility_spec.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +RSpec.describe "Backwards compatibility" do + it "aliases OAuth::CLI to OAuth::TTY::CLI" do + require "oauth/cli" + expect(OAuth::CLI).to be(OAuth::TTY::CLI) + end +end diff --git a/spec/oauth/consumer_integration_spec.rb b/spec/oauth/consumer_integration_spec.rb new file mode 100644 index 00000000..63cb8b06 --- /dev/null +++ b/spec/oauth/consumer_integration_spec.rb @@ -0,0 +1,230 @@ +# frozen_string_literal: true + +require "spec_helper" +require "net/http" + +RSpec.describe "OAuth::Consumer integration" do + let(:consumer) do + OAuth::Consumer.new( + "consumer_key_86cad9", + "5888bf0345e5d237", + { + site: "http://blabla.bla", + proxy: "http://user:password@proxy.bla:8080", + request_token_path: "/oauth/example/request_token.php", + access_token_path: "/oauth/example/access_token.php", + authorize_path: "/oauth/example/authorize.php", + scheme: :header, + http_method: :get, + }, + ) + end + let(:token) { OAuth::ConsumerToken.new(consumer, "token_411a7f", "3196ffd991c8ebdb") } + let(:request_uri) { URI.parse("http://example.com/test?key=value") } + let(:request_parameters) { {"key" => "value"} } + let(:nonce) { 225_579_211_881_198_842_005_988_698_334_675_835_446 } + let(:timestamp) { "1199645624" } + + before do + consumer.http = Net::HTTP.new(request_uri.host, request_uri.port) + end + + describe "#sign! on Net::HTTP requests" do + it "signs auth headers on GET requests deterministically" do + request = Net::HTTP::Get.new("#{request_uri.path}?key=value") + token.sign!(request, {nonce: nonce, timestamp: timestamp}) + + expect(request.method).to eq("GET") + expect(request.path).to eq("/test?key=value") + expected = "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"1oO2izFav1GP4kEH2EskwXkCRFg%3D\", oauth_version=\"1.0\"".delete(",").split.sort + actual = request["authorization"].delete(",").split.sort + expect(actual).to eq(expected) + end + + it "reflects signature_method set on consumer in Authorization header" do + request = Net::HTTP::Get.new(request_uri.path) + other_consumer = consumer.dup + other_consumer.options[:signature_method] = "PLAINTEXT" + other_token = OAuth::ConsumerToken.new(other_consumer, "token_411a7f", "3196ffd991c8ebdb") + + other_token.sign!(request, {nonce: nonce, timestamp: timestamp}) + + expect(request["authorization"]).not_to match(/oauth_signature_method=\"HMAC-SHA1\"/) + expect(request["authorization"]).to match(/oauth_signature_method=\"PLAINTEXT\"/) + end + + it "affects signature_base_string when signature_method is PLAINTEXT" do + other_consumer = consumer.dup + other_consumer.options[:signature_method] = "PLAINTEXT" + request = Net::HTTP::Get.new("/") + + signature_base_string = other_consumer.signature_base_string(request) + expect(signature_base_string).not_to match(/HMAC-SHA1/) + expect(signature_base_string).to eq("#{other_consumer.secret}&") + end + + it "signs auth headers on POST requests" do + request = Net::HTTP::Post.new(request_uri.path) + request.set_form_data(request_parameters) + token.sign!(request, {nonce: nonce, timestamp: timestamp}) + + expect(request.method).to eq("POST") + expect(request.path).to eq("/test") + expect(request.body).to eq("key=value") + expected = "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"26g7wHTtNO6ZWJaLltcueppHYiI%3D\", oauth_version=\"1.0\"".delete(",").split.sort + actual = request["authorization"].delete(",").split.sort + expect(actual).to eq(expected) + end + + it "can sign POST params with scheme body" do + request = Net::HTTP::Post.new(request_uri.path) + request.set_form_data(request_parameters) + token.sign!(request, {scheme: "body", nonce: nonce, timestamp: timestamp}) + + expect(request.method).to eq("POST") + expect(request.path).to eq("/test") + joined = request.body.split("&").sort.join("&") + expect(joined).to match(%r{key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=26g7wHTtNO6ZWJaLltcueppHYiI%3[Dd]&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0}) + expect(request["authorization"]).to be_nil + end + end + + describe "::create_signed_request" do + it "uses auth headers on GET" do + request = consumer.create_signed_request( + :get, + "#{request_uri.path}?key=value", + token, + {nonce: nonce, timestamp: timestamp}, + request_parameters, + ) + + expect(request.method).to eq("GET") + expect(request.path).to eq("/test?key=value") + expected = "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"1oO2izFav1GP4kEH2EskwXkCRFg%3D\", oauth_version=\"1.0\"".delete(",").split.sort + actual = request["authorization"].delete(",").split.sort + expect(actual).to eq(expected) + end + + it "uses auth headers on POST" do + request = consumer.create_signed_request( + :post, + request_uri.path, + token, + {nonce: nonce, timestamp: timestamp}, + request_parameters, + {}, + ) + + expect(request.method).to eq("POST") + expect(request.path).to eq("/test") + expect(request.body).to eq("key=value") + expected = "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"26g7wHTtNO6ZWJaLltcueppHYiI%3D\", oauth_version=\"1.0\"".delete(",").split.sort + actual = request["authorization"].delete(",").split.sort + expect(actual).to eq(expected) + end + + it "can sign POST params with scheme body" do + request = consumer.create_signed_request( + :post, + request_uri.path, + token, + {scheme: "body", nonce: nonce, timestamp: timestamp}, + request_parameters, + {}, + ) + + expect(request.method).to eq("POST") + expect(request.path).to eq("/test") + joined = request.body.split("&").sort.join("&") + expect(joined).to match(%r{key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=26g7wHTtNO6ZWJaLltcueppHYiI%3[Dd]&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0}) + expect(request["authorization"]).to be_nil + end + end + + describe "integration against term.ie example endpoints", :vcr do + before do + # Stub classic term.ie endpoints used by historical oauth tests + # Request token + stub_request(:any, "http://term.ie/oauth/example/request_token.php") + .to_return(status: 200, body: "oauth_token=requestkey&oauth_token_secret=requestsecret") + # Access token + stub_request(:any, "http://term.ie/oauth/example/access_token.php") + .to_return(status: 200, body: "oauth_token=accesskey&oauth_token_secret=accesssecret") + # Echo API (GET) + stub_request(:get, %r{http://term\.ie/oauth/example/echo_api\.php\?ok=hello&test=this}) + .to_return(status: 200, body: "ok=hello&test=this") + # Echo API (POST) + stub_request(:post, "http://term.ie/oauth/example/echo_api.php") + .with(body: {"ok" => "hello", "test" => "this"}) + .to_return(status: 200, body: "ok=hello&test=this") + end + + it "can perform a full token dance and call a protected resource" do + consumer2 = OAuth::Consumer.new( + "key", + "secret", + { + site: "http://term.ie", + request_token_path: "/oauth/example/request_token.php", + access_token_path: "/oauth/example/access_token.php", + authorize_path: "/oauth/example/authorize.php", + }, + ) + + expect(consumer2.request_token_url).to eq("http://term.ie/oauth/example/request_token.php") + expect(consumer2.access_token_url).to eq("http://term.ie/oauth/example/access_token.php") + + expect(consumer2.request_token_url?).to be(false) + expect(consumer2.access_token_url?).to be(false) + expect(consumer2.authorize_url?).to be(false) + + request_token = consumer2.get_request_token + expect(request_token.token).to eq("requestkey") + expect(request_token.secret).to eq("requestsecret") + expect(request_token.authorize_url).to eq("http://term.ie/oauth/example/authorize.php?oauth_token=requestkey") + + access_token = request_token.get_access_token + expect(access_token.token).to eq("accesskey") + expect(access_token.secret).to eq("accesssecret") + + response = access_token.get("/oauth/example/echo_api.php?ok=hello&test=this") + expect(response.code).to eq("200") + expect(response.body).to eq("ok=hello&test=this") + + response = access_token.post("/oauth/example/echo_api.php", {"ok" => "hello", "test" => "this"}) + expect(response.code).to eq("200") + expect(response.body).to eq("ok=hello&test=this") + end + + it "builds correct signature base string for request token" do + consumer2 = OAuth::Consumer.new( + "key", + "secret", + { + site: "http://term.ie", + request_token_path: "/oauth/example/request_token.php", + access_token_path: "/oauth/example/access_token.php", + authorize_path: "/oauth/example/authorize.php", + scheme: :header, + }, + ) + options = {nonce: "nonce", timestamp: Time.now.to_i.to_s} + + request = Net::HTTP::Get.new("/oauth/example/request_token.php") + signature_base_string = consumer2.signature_base_string(request, nil, options) + + expect(signature_base_string).to eq( + "GET&http%3A%2F%2Fterm.ie%2Foauth%2Fexample%2Frequest_token.php&oauth_consumer_key%3Dkey%26oauth_nonce%3D#{options[:nonce]}%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D#{options[:timestamp]}%26oauth_version%3D1.0", + ) + + consumer2.sign!(request, nil, options) + expect(request.method).to eq("GET") + expect(request.body).to be_nil + + response = consumer2.http.request(request) + expect(response.code).to eq("200") + expect(response.body).to eq("oauth_token=requestkey&oauth_token_secret=requestsecret") + end + end +end diff --git a/spec/oauth/consumer_spec.rb b/spec/oauth/consumer_spec.rb new file mode 100644 index 00000000..16164db0 --- /dev/null +++ b/spec/oauth/consumer_spec.rb @@ -0,0 +1,219 @@ +# frozen_string_literal: true + +require "stringio" + +RSpec.describe OAuth::Consumer do + let(:consumer_key) { "consumer_key_86cad9" } + let(:consumer_secret) { "5888bf0345e5d237" } + + describe "#initialize and attribute defaults" do + it "sets provided attributes and defaults" do + consumer = described_class.new( + consumer_key, + consumer_secret, + site: "http://blabla.bla", + proxy: "http://user:password@proxy.bla:8080", + request_token_path: "/oauth/example/request_token.php", + access_token_path: "/oauth/example/access_token.php", + authorize_path: "/oauth/example/authorize.php", + scheme: :header, + http_method: :get, + ) + + expect(consumer.key).to eq(consumer_key) + expect(consumer.secret).to eq(consumer_secret) + expect(consumer.site).to eq("http://blabla.bla") + expect(consumer.proxy).to eq("http://user:password@proxy.bla:8080") + expect(consumer.request_token_path).to eq("/oauth/example/request_token.php") + expect(consumer.access_token_path).to eq("/oauth/example/access_token.php") + expect(consumer.request_token_url).to eq("http://blabla.bla/oauth/example/request_token.php") + expect(consumer.access_token_url).to eq("http://blabla.bla/oauth/example/access_token.php") + expect(consumer.authorize_url).to eq("http://blabla.bla/oauth/example/authorize.php") + expect(consumer.scheme).to eq(:header) + expect(consumer.http_method).to eq(:get) + expect(consumer.debug_output).to be_nil + end + + it "applies sensible defaults when only site given" do + consumer = described_class.new("key", "secret", site: "http://twitter.com") + + expect(consumer.key).to eq("key") + expect(consumer.secret).to eq("secret") + expect(consumer.site).to eq("http://twitter.com") + expect(consumer.proxy).to be_nil + expect(consumer.request_token_path).to eq("/oauth/request_token") + expect(consumer.access_token_path).to eq("/oauth/access_token") + expect(consumer.request_token_url).to eq("http://twitter.com/oauth/request_token") + expect(consumer.access_token_url).to eq("http://twitter.com/oauth/access_token") + expect(consumer.authorize_url).to eq("http://twitter.com/oauth/authorize") + expect(consumer.scheme).to eq(:header) + expect(consumer.http_method).to eq(:post) + expect(consumer.debug_output).to be_nil + end + + it "treats debug_output: true as $stdout" do + consumer = described_class.new("key", "secret", debug_output: true) + expect(consumer.debug_output).to be($stdout) + end + + it "accepts an IO for debug_output" do + io = StringIO.new + consumer = described_class.new("key", "secret", debug_output: io) + expect(consumer.debug_output).to be(io) + end + + it "allows overriding full URLs without appending site path" do + consumer = described_class.new( + "key", + "secret", + site: "http://twitter.com", + request_token_url: "http://oauth.twitter.com/request_token", + access_token_url: "http://oauth.twitter.com/access_token", + authorize_url: "http://site.twitter.com/authorize", + ) + + expect(consumer.request_token_path).to eq("/oauth/request_token") + expect(consumer.access_token_path).to eq("/oauth/access_token") + expect(consumer.request_token_url).to eq("http://oauth.twitter.com/request_token") + expect(consumer.access_token_url).to eq("http://oauth.twitter.com/access_token") + expect(consumer.authorize_url).to eq("http://site.twitter.com/authorize") + expect(consumer.http_method).to eq(:post) + expect(consumer.scheme).to eq(:header) + end + end + + describe "request URL path joining" do + it "does not duplicate path when site has no path component" do + consumer = described_class.new("key", "secret", site: "http://twitter.com") + + # We don't need to actually perform the HTTP call; we just want to verify + # that the request path sent to Net::HTTP::Get.new is correct. + request_double = instance_double("Net::HTTP::Get").as_null_object + expect(Net::HTTP::Get).to receive(:new).with("/people", kind_of(Hash)).and_return(request_double) + + http_double = double("http", request: double("response", to_hash: {}), address: "identi.ca") + expect(consumer).to receive(:create_http).and_return(http_double) + + consumer.request(:get, "/people", nil, {}) + end + + it "prefixes site path when site includes a path" do + consumer = described_class.new("key", "secret", site: "http://identi.ca/api") + + request_double = instance_double("Net::HTTP::Get").as_null_object + expect(Net::HTTP::Get).to receive(:new).with("/api/people", kind_of(Hash)).and_return(request_double) + + http_double = double("http", request: double("response", to_hash: {}), address: "identi.ca") + expect(consumer).to receive(:create_http).and_return(http_double) + + consumer.request(:get, "/people", nil, {}) + end + end + + describe "signed requests" do + it "form-encodes nested params for POST" do + consumer = described_class.new("key", "secret", site: "http://twitter.com") + + request = consumer.create_signed_request( + :post, + "/people", + nil, + {}, + {key: {subkey: "value"}}, + ) + + expect(request.body).to eq("key%5Bsubkey%5D=value") + expect(request.content_type).to eq("application/x-www-form-urlencoded") + end + end + + describe "SSL verify toggle" do + it "sets VERIFY_NONE when no_verify: true" do + consumer = described_class.new( + "key", + "secret", + site: "https://api.mysite.co.nz/v1", + request_token_url: "https://authentication.mysite.co.nz/Oauth/RequestToken", + no_verify: true, + ) + + stub_request(:post, "https://authentication.mysite.co.nz/Oauth/RequestToken").to_return(body: "success", status: 200) + + # Any Net::HTTP instance created during get_request_token should get verify_mode set + http_instance = instance_double(Net::HTTP) + allow(Net::HTTP).to receive(:new).and_return(http_instance) + allow(http_instance).to receive(:use_ssl=) + allow(http_instance).to receive(:ca_file=) + allow(http_instance).to receive(:verify_depth=) + allow(http_instance).to receive(:read_timeout=) + allow(http_instance).to receive(:open_timeout=) + allow(http_instance).to receive(:ssl_version=) + allow(http_instance).to receive(:cert=) + allow(http_instance).to receive(:key=) + allow(http_instance).to receive(:set_debug_output) + allow(http_instance).to receive(:address).and_return("authentication.mysite.co.nz") + expect(http_instance).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE) + allow(http_instance).to receive(:request).and_return(double(to_hash: {}, code: "200", body: "")) + + # The request inside get_request_token is not important beyond hitting the code path + expect { consumer.get_request_token }.not_to raise_error + end + + it "sets VERIFY_PEER when no_verify: false" do + consumer = described_class.new( + "key", + "secret", + site: "https://api.mysite.co.nz/v1", + request_token_url: "https://authentication.mysite.co.nz/Oauth/RequestToken", + no_verify: false, + ) + + stub_request(:post, "https://authentication.mysite.co.nz/Oauth/RequestToken").to_return(body: "success", status: 200) + + http_instance = instance_double(Net::HTTP) + allow(Net::HTTP).to receive(:new).and_return(http_instance) + allow(http_instance).to receive(:use_ssl=) + allow(http_instance).to receive(:ca_file=) + allow(http_instance).to receive(:verify_depth=) + allow(http_instance).to receive(:read_timeout=) + allow(http_instance).to receive(:open_timeout=) + allow(http_instance).to receive(:ssl_version=) + allow(http_instance).to receive(:cert=) + allow(http_instance).to receive(:key=) + allow(http_instance).to receive(:set_debug_output) + allow(http_instance).to receive(:address).and_return("authentication.mysite.co.nz") + expect(http_instance).to receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) + allow(http_instance).to receive(:request).and_return(double(to_hash: {}, code: "200", body: "")) + + expect { consumer.get_request_token }.not_to raise_error + end + + it "respects full request_token_url without prefixing site" do + consumer = described_class.new( + "key", + "secret", + site: "https://api.mysite.co.nz/v1", + request_token_url: "https://authentication.mysite.co.nz/Oauth/RequestToken", + ) + + stub_request(:post, "https://authentication.mysite.co.nz/Oauth/RequestToken").to_return(body: "success", status: 200) + + http_instance = instance_double(Net::HTTP) + allow(Net::HTTP).to receive(:new).and_return(http_instance) + allow(http_instance).to receive(:use_ssl=) + allow(http_instance).to receive(:ca_file=) + allow(http_instance).to receive(:verify_depth=) + allow(http_instance).to receive(:read_timeout=) + allow(http_instance).to receive(:open_timeout=) + allow(http_instance).to receive(:ssl_version=) + allow(http_instance).to receive(:cert=) + allow(http_instance).to receive(:key=) + allow(http_instance).to receive(:set_debug_output) + allow(http_instance).to receive(:address).and_return("authentication.mysite.co.nz") + allow(http_instance).to receive(:verify_mode=) + allow(http_instance).to receive(:request).and_return(double(to_hash: {}, code: "200", body: "")) + + expect { consumer.get_request_token }.not_to raise_error + end + end +end diff --git a/spec/oauth/em_http_client_spec.rb b/spec/oauth/em_http_client_spec.rb new file mode 100644 index 00000000..2a93e9ec --- /dev/null +++ b/spec/oauth/em_http_client_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +begin + require "eventmachine" + require "em-http-request" + + RSpec.describe "EventMachine HTTP client integration" do + it "is skipped until em-http-request is available" do + skip("em-http-request not exercised in this environment") + end + end +rescue LoadError + RSpec.describe "EventMachine HTTP client integration" do + it "is pending because em-http-request is not installed" do + pending("em-http-request not installed") + raise "unreachable" + end + end +end diff --git a/spec/oauth/helper_spec.rb b/spec/oauth/helper_spec.rb new file mode 100644 index 00000000..159702ec --- /dev/null +++ b/spec/oauth/helper_spec.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +RSpec.describe OAuth::Helper do + describe "::parse_header" do + it "parses a valid OAuth header" do + header = 'OAuth realm="http://example.com/method", oauth_consumer_key="vince_clortho", oauth_token="token_value", oauth_signature_method="HMAC-SHA1", oauth_signature="signature_here", oauth_timestamp="1240004133", oauth_nonce="nonce", oauth_version="1.0" ' + + params = described_class.parse_header(header) + + expect(params["realm"]).to eq("http://example.com/method") + expect(params["oauth_consumer_key"]).to eq("vince_clortho") + expect(params["oauth_token"]).to eq("token_value") + expect(params["oauth_signature_method"]).to eq("HMAC-SHA1") + expect(params["oauth_signature"]).to eq("signature_here") + expect(params["oauth_timestamp"]).to eq("1240004133") + expect(params["oauth_nonce"]).to eq("nonce") + expect(params["oauth_version"]).to eq("1.0") + end + + it "raises on ill-formed header" do + expect { described_class.parse_header("OAuth garbage") }.to raise_error(OAuth::Problem) + end + + it "raises on header with equals in a value" do + header = 'OAuth realm="http://example.com/method", oauth_consumer_key="vince_clortho", oauth_token="token_value", oauth_signature_method="HMAC-SHA1", oauth_signature="signature_here_with_=", oauth_timestamp="1240004133", oauth_nonce="nonce", oauth_version="1.0" ' + expect { described_class.parse_header(header) }.to raise_error(OAuth::Problem) + end + + it "parses header with ampersands between params" do + header = 'OAuth realm="http://example.com/method"&oauth_consumer_key="vince_clortho"&oauth_token="token_value"&oauth_signature_method="HMAC-SHA1"&oauth_signature="signature_here"&oauth_timestamp="1240004133"&oauth_nonce="nonce"&oauth_version="1.0"' + + params = described_class.parse_header(header) + + expect(params["realm"]).to eq("http://example.com/method") + expect(params["oauth_consumer_key"]).to eq("vince_clortho") + expect(params["oauth_token"]).to eq("token_value") + expect(params["oauth_signature_method"]).to eq("HMAC-SHA1") + expect(params["oauth_signature"]).to eq("signature_here") + expect(params["oauth_timestamp"]).to eq("1240004133") + expect(params["oauth_nonce"]).to eq("nonce") + expect(params["oauth_version"]).to eq("1.0") + end + end + + describe "::normalize" do + it "normalizes nested params to query-string format" do + params = { + "oauth_nonce" => "nonce", + "weight" => {value: "65"}, + "oauth_signature_method" => "HMAC-SHA1", + "oauth_timestamp" => "1240004133", + "oauth_consumer_key" => "vince_clortho", + "oauth_token" => "token_value", + "oauth_version" => "1.0", + } + + expect( + described_class.normalize(params), + ).to eq("oauth_consumer_key=vince_clortho&oauth_nonce=nonce&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1240004133&oauth_token=token_value&oauth_version=1.0&weight%5Bvalue%5D=65") + end + + it "normalizes with nested array of hashes" do + params = { + "oauth_nonce" => "nonce", + "weight" => {value: "65"}, + "items" => [{"a" => 1}, {"b" => 2}], + "oauth_signature_method" => "HMAC-SHA1", + "oauth_timestamp" => "1240004133", + "oauth_consumer_key" => "vince_clortho", + "oauth_token" => "token_value", + "oauth_version" => "1.0", + } + + expect( + described_class.normalize(params), + ).to eq("items%5B%5D%5Ba%5D=1&items%5B%5D%5Bb%5D=2&oauth_consumer_key=vince_clortho&oauth_nonce=nonce&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1240004133&oauth_token=token_value&oauth_version=1.0&weight%5Bvalue%5D=65") + end + end + + describe "::normalize_nested_query" do + it "handles empty and simple cases" do + expect(described_class.normalize_nested_query({})).to be_empty + expect(described_class.normalize_nested_query({foo: "bar"})).to eq(["foo=bar"]) + expect(described_class.normalize_nested_query({foo: "bar"}, "prefix")).to eq(["prefix%5Bfoo%5D=bar"]) + end + + it "handles nested hash with ordering" do + expect( + described_class.normalize_nested_query({user: {twitter_id: 123, date: "2011-10-05", age: 12}}, "prefix"), + ).to eq([ + "prefix%5Buser%5D%5Bage%5D=12", + "prefix%5Buser%5D%5Bdate%5D=2011-10-05", + "prefix%5Buser%5D%5Btwitter_id%5D=123", + ]) + end + end +end diff --git a/spec/oauth/net_http_client_spec.rb b/spec/oauth/net_http_client_spec.rb new file mode 100644 index 00000000..528af974 --- /dev/null +++ b/spec/oauth/net_http_client_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "net/http" + +RSpec.describe "Net::HTTP client OAuth helpers" do + let(:consumer) { OAuth::Consumer.new("consumer_key_86cad9", "5888bf0345e5d237") } + let(:token) { OAuth::Token.new("token_411a7f", "3196ffd991c8ebdb") } + let(:uri) { URI.parse("http://example.com/test?key=value") } + let(:http) { Net::HTTP.new(uri.host, uri.port) } + let(:nonce) { 225_579_211_881_198_842_005_988_698_334_675_835_446 } + let(:timestamp) { "1199645624" } + + it "adds Authorization header for GET with params" do + request = Net::HTTP::Get.new("#{uri.path}?key=value") + request.oauth!(http, consumer, token, {nonce: nonce, timestamp: timestamp}) + + expect(request.method).to eq("GET") + expect(request.path).to eq("/test?key=value") + expect(request["authorization"]).to include("OAuth ") + # Do not assert the exact signature bytes here; just ensure core fields are present + expect(request["authorization"]).to include( + "oauth_consumer_key=\"consumer_key_86cad9\"", + ) + expect(request["authorization"]).to include( + "oauth_token=\"token_411a7f\"", + ) + expect(request["authorization"]).to match(/oauth_signature_method=\"HMAC-SHA1\"|oauth_signature_method=\"PLAINTEXT\"/) + end + + it "adds body hash for POST with data and content type" do + request = Net::HTTP::Post.new(uri.path) + request.body = "data" + request.content_type = "text/ascii" + + request.oauth!(http, consumer, token, {nonce: nonce, timestamp: timestamp}) + + expect(request.method).to eq("POST") + expect(request.path).to eq("/test") + expect(request["authorization"]).to include("oauth_body_hash=") + end +end diff --git a/spec/oauth/request_proxy/action_controller_spec.rb b/spec/oauth/request_proxy/action_controller_spec.rb new file mode 100644 index 00000000..d40e59c6 --- /dev/null +++ b/spec/oauth/request_proxy/action_controller_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +begin + require "action_controller" + require "oauth/request_proxy/action_controller_request" + + RSpec.describe OAuth::RequestProxy::ActionControllerRequest do + it "proxies ActionController::TestRequest with params" do + # Use a minimal Rack env that ActionController::TestRequest can wrap + env = { + "REQUEST_METHOD" => "GET", + "rack.input" => StringIO.new(""), + "QUERY_STRING" => "foo=bar", + "PATH_INFO" => "/widgets", + } + req = ActionController::TestRequest.create(env) + + proxy = OAuth::RequestProxy.proxy(req, {uri: "http://example.com/widgets"}) + expect(proxy.method).to eq("GET") + expect(proxy.normalized_uri).to eq("http://example.com/widgets") + expect(proxy.parameters).to include("foo" => ["bar"]) + end + end +rescue LoadError + RSpec.describe "OAuth ActionController Request Proxy" do + it "is pending because actionpack is not installed" do + skip("actionpack not installed") + end + end +end diff --git a/spec/oauth/request_proxy/action_dispatch_spec.rb b/spec/oauth/request_proxy/action_dispatch_spec.rb new file mode 100644 index 00000000..923759da --- /dev/null +++ b/spec/oauth/request_proxy/action_dispatch_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +begin + require "action_dispatch" + require "oauth/request_proxy/action_dispatch_request" + + RSpec.describe OAuth::RequestProxy::ActionDispatchRequest do + it "proxies ActionDispatch::Request with params" do + env = { + "REQUEST_METHOD" => "GET", + "rack.input" => StringIO.new(""), + "QUERY_STRING" => "a=1&b=2", + "PATH_INFO" => "/test", + } + req = ActionDispatch::Request.new(env) + + proxy = OAuth::RequestProxy.proxy(req, {uri: "http://example.com/test"}) + expect(proxy.method).to eq("GET") + expect(proxy.normalized_uri).to eq("http://example.com/test") + expect(proxy.parameters).to include("a" => ["1"], "b" => ["2"]) + end + end +rescue LoadError + RSpec.describe "OAuth ActionDispatch Request Proxy" do + it "is pending because actionpack is not installed" do + skip("actionpack not installed") + end + end +end diff --git a/spec/oauth/request_proxy/curb_spec.rb b/spec/oauth/request_proxy/curb_spec.rb new file mode 100644 index 00000000..fd610ead --- /dev/null +++ b/spec/oauth/request_proxy/curb_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +begin + require "curb" + require "oauth/request_proxy/curb_request" + + RSpec.describe OAuth::RequestProxy::CurbRequest do + it "is constructible when curb is available" do + req = Curl::Easy.new("http://example.com/test?a=1") + proxy = OAuth::RequestProxy.proxy(req, {uri: "http://example.com/test"}) + expect(proxy.method).to be_a(String) + end + end +rescue LoadError + RSpec.describe "OAuth Curb Request Proxy" do + it "is pending because curb is not installed" do + skip("curb not installed") + end + end +end diff --git a/spec/oauth/request_proxy/em_http_spec.rb b/spec/oauth/request_proxy/em_http_spec.rb new file mode 100644 index 00000000..eeed2065 --- /dev/null +++ b/spec/oauth/request_proxy/em_http_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +begin + require "em-http-request" + require "oauth/request_proxy/em_http_request" + + RSpec.describe "OAuth EM-HTTP Request Proxy" do + it "is pending until em-http-request usage is exercised in tests" do + skip("em-http-request adapter not exercised in this environment") + end + end +rescue LoadError + RSpec.describe "OAuth EM-HTTP Request Proxy" do + it "is pending because em-http-request is not installed" do + pending("em-http-request not installed") + raise "unreachable" + end + end +end diff --git a/spec/oauth/request_proxy/net_http_spec.rb b/spec/oauth/request_proxy/net_http_spec.rb new file mode 100644 index 00000000..d402ad46 --- /dev/null +++ b/spec/oauth/request_proxy/net_http_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "net/http" +require "oauth/request_proxy/net_http" + +RSpec.describe OAuth::RequestProxy::Net::HTTP::HTTPRequest do + let(:uri) { URI.parse("http://example.com/test?key=value") } + + it "proxies Net::HTTP GET requests with query params" do + req = Net::HTTP::Get.new("#{uri.path}?key=value") + proxy = OAuth::RequestProxy.proxy(req, {uri: "http://example.com/test"}) + + expect(proxy.method).to eq("GET") + expect(proxy.normalized_uri).to eq("http://example.com/test") + expect(proxy.parameters).to include("key" => ["value"]) + end + + it "includes form params for POST with form-encoded content type" do + req = Net::HTTP::Post.new(uri.path) + req["Content-Type"] = "application/x-www-form-urlencoded" + req.body = "a=1&b=2" + + proxy = OAuth::RequestProxy.proxy(req, {uri: "http://example.com/test"}) + + expect(proxy.method).to eq("POST") + expect(proxy.parameters).to include("a" => ["1"], "b" => ["2"]) + end +end diff --git a/spec/oauth/request_proxy/rack_spec.rb b/spec/oauth/request_proxy/rack_spec.rb new file mode 100644 index 00000000..26ef2cf4 --- /dev/null +++ b/spec/oauth/request_proxy/rack_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +begin + require "rack" + require "oauth/request_proxy/rack_request" + + RSpec.describe OAuth::RequestProxy::RackRequest do + include Rack::Test::Methods + + def app + proc { |_env| [200, {"Content-Type" => "text/plain"}, ["OK"]] } + end + + it "proxies Rack::Request with query params" do + get "/test", {"a" => "1", "b" => "2"} + rack_req = last_request + + proxy = OAuth::RequestProxy.proxy(rack_req, {uri: "http://example.com/test"}) + + expect(proxy.method).to eq("GET") + expect(proxy.normalized_uri).to eq("http://example.org/test") + # For Rack::Request proxy, parameters are simple strings, not arrays + expect(proxy.parameters).to include("a" => "1", "b" => "2") + end + + it "proxies Rack::Request POST form params" do + post "/test", {"x" => "9", "y" => "10"} + rack_req = last_request + + proxy = OAuth::RequestProxy.proxy(rack_req, {uri: "http://example.com/test"}) + + expect(proxy.method).to eq("POST") + # For Rack::Request proxy, parameters are simple strings, not arrays + expect(proxy.parameters).to include("x" => "9", "y" => "10") + end + end +rescue LoadError + RSpec.describe "OAuth Rack Request Proxy" do + it "is pending because Rack is not available" do + pending("rack not installed") + raise "unreachable" + end + end +end diff --git a/spec/oauth/request_proxy/rest_client_spec.rb b/spec/oauth/request_proxy/rest_client_spec.rb new file mode 100644 index 00000000..b6b4e807 --- /dev/null +++ b/spec/oauth/request_proxy/rest_client_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +begin + require "rest-client" + require "oauth/request_proxy/rest_client_request" + + RSpec.describe OAuth::RequestProxy::RestClient::Request do + it "is constructible when rest-client is available" do + req = RestClient::Request.new(method: :get, url: "http://example.com/test?x=1") + proxy = OAuth::RequestProxy.proxy(req, {uri: "http://example.com/test"}) + expect(proxy.method).to eq("GET") + expect(proxy.parameters).to include("x" => ["1"]) + end + end +rescue LoadError + RSpec.describe "OAuth RestClient Request Proxy" do + it "is pending because rest-client is not installed" do + pending("rest-client not installed") + raise "unreachable" + end + end +end diff --git a/spec/oauth/request_proxy/typhoeus_spec.rb b/spec/oauth/request_proxy/typhoeus_spec.rb new file mode 100644 index 00000000..f7a316cb --- /dev/null +++ b/spec/oauth/request_proxy/typhoeus_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +begin + require "typhoeus" + require "oauth/request_proxy/typhoeus_request" + + RSpec.describe OAuth::RequestProxy::Typhoeus::Request do + it "is constructible when typhoeus is available" do + req = Typhoeus::Request.new("http://example.com/test", method: :get, params: {a: 1}) + proxy = OAuth::RequestProxy.proxy(req, {uri: "http://example.com/test"}) + expect(proxy.method).to eq("GET") + expect(proxy.parameters).to include("a" => ["1"]) + end + end +rescue LoadError + RSpec.describe "OAuth Typhoeus Request Proxy" do + it "is pending because typhoeus is not installed" do + pending("typhoeus not installed") + raise "unreachable" + end + end +end diff --git a/spec/oauth/server_spec.rb b/spec/oauth/server_spec.rb new file mode 100644 index 00000000..18467217 --- /dev/null +++ b/spec/oauth/server_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "spec_helper" +require "oauth/server" + +RSpec.describe OAuth::Server do + subject(:server) { described_class.new("http://test.com") } + + describe "defaults" do + it "has default paths" do + expect(server.request_token_path).to eq("/oauth/request_token") + expect(server.authorize_path).to eq("/oauth/authorize") + expect(server.access_token_path).to eq("/oauth/access_token") + end + + it "builds default URLs from site + paths" do + expect(server.request_token_url).to eq("http://test.com/oauth/request_token") + expect(server.authorize_url).to eq("http://test.com/oauth/authorize") + expect(server.access_token_url).to eq("http://test.com/oauth/access_token") + end + end + + describe "#create_consumer and #generate_consumer_credentials" do + it "generates consumer credentials" do + consumer = server.generate_consumer_credentials + expect(consumer.key).to be_a(String) + expect(consumer.secret).to be_a(String) + expect(consumer.key).not_to be_empty + expect(consumer.secret).not_to be_empty + end + + it "creates a configured consumer with credentials" do + consumer = server.create_consumer + + expect(consumer).to be_a(OAuth::Consumer) + expect(consumer.key).to be_a(String) + expect(consumer.secret).to be_a(String) + expect(consumer.site).to eq("http://test.com") + expect(consumer.request_token_path).to eq("/oauth/request_token") + expect(consumer.authorize_path).to eq("/oauth/authorize") + expect(consumer.access_token_path).to eq("/oauth/access_token") + expect(consumer.request_token_url).to eq("http://test.com/oauth/request_token") + expect(consumer.authorize_url).to eq("http://test.com/oauth/authorize") + expect(consumer.access_token_url).to eq("http://test.com/oauth/access_token") + end + end +end diff --git a/spec/oauth/server_token_spec.rb b/spec/oauth/server_token_spec.rb new file mode 100644 index 00000000..fa70ccf3 --- /dev/null +++ b/spec/oauth/server_token_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" +require "oauth/tokens/server_token" + +RSpec.describe OAuth::ServerToken do + describe "initialization" do + it "initializes with generated token and secret" do + allow_any_instance_of(described_class).to receive(:generate_key).with(16).and_return("TOKEN16") + allow_any_instance_of(described_class).to receive(:generate_key).with(no_args).and_return("SECRET32") + + st = described_class.new + + expect(st.token).to eq("TOKEN16") + expect(st.secret).to eq("SECRET32") + end + + it "defaults secret length to helper generate_key (no args)" do + allow_any_instance_of(described_class).to receive(:generate_key).with(16).and_return("A" * 8) + allow_any_instance_of(described_class).to receive(:generate_key).with(no_args).and_return("B" * 12) + + st = described_class.new + + expect(st.token).to eq("A" * 8) + expect(st.secret).to eq("B" * 12) + end + end +end diff --git a/spec/oauth/signature/base_spec.rb b/spec/oauth/signature/base_spec.rb new file mode 100644 index 00000000..c013d814 --- /dev/null +++ b/spec/oauth/signature/base_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require "oauth/signature/base" +require "net/http" + +RSpec.describe OAuth::Signature::Base do + describe "#initialize" do + it "requires at least one request argument" do + expect { described_class.new }.to raise_error(ArgumentError) + end + + it "requires a valid request proxy object" do + request = nil + expect do + described_class.new(request) do |_token| + # no-op block + end + end.to raise_error(TypeError) + end + + it "succeeds when the request proxy is valid" do + raw_request = Net::HTTP::Get.new("/test") + request = OAuth::RequestProxy.proxy(raw_request) + + # Should not raise + expect do + described_class.new(request) { |_token| } + end.not_to raise_error + end + end +end diff --git a/spec/oauth/signature/hmac_sha1_spec.rb b/spec/oauth/signature/hmac_sha1_spec.rb new file mode 100644 index 00000000..6ffd8499 --- /dev/null +++ b/spec/oauth/signature/hmac_sha1_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require "oauth/signature/hmac/sha1" + +RSpec.describe OAuth::Signature::HMAC::SHA1 do + describe "#verify" do + it "returns true when the request signature is correct" do + request = OAuth::RequestProxy::MockRequest.new( + "method" => "POST", + "uri" => "https://photos.example.net/initialize", + "parameters" => { + "oauth_consumer_key" => "dpf43f3p2l4k3l03", + "oauth_signature_method" => "HMAC-SHA1", + "oauth_timestamp" => "137131200", + "oauth_nonce" => "wIjqoS", + "oauth_callback" => "http://printer.example.com/ready", + "oauth_version" => "1.0", + "oauth_signature" => "xcHYBV3AbyoDz7L4dV10P3oLCjY=", + }, + ) + + expect(described_class.new(request, consumer_secret: "kd94hf93k423kf44").verify).to be true + end + + it "returns false when the request signature is wrong" do + # this guards against a historical bug in Base#== + request = OAuth::RequestProxy::MockRequest.new( + "method" => "POST", + "uri" => "https://photos.example.net/initialize", + "parameters" => { + "oauth_consumer_key" => "dpf43f3p2l4k3l03", + "oauth_signature_method" => "HMAC-SHA1", + "oauth_timestamp" => "137131200", + "oauth_nonce" => "wIjqoS", + "oauth_callback" => "http://printer.example.com/ready", + "oauth_version" => "1.0", + "oauth_signature" => "xcHYBV3AbyoDz7L4dV10P3oLCjZ=", + }, + ) + + expect(described_class.new(request, consumer_secret: "kd94hf93k423kf44").verify).to be false + end + end +end diff --git a/spec/oauth/signature/hmac_sha256_spec.rb b/spec/oauth/signature/hmac_sha256_spec.rb new file mode 100644 index 00000000..fa2e44b6 --- /dev/null +++ b/spec/oauth/signature/hmac_sha256_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require "oauth/signature/hmac/sha256" + +RSpec.describe OAuth::Signature::HMAC::SHA256 do + describe "#verify" do + it "returns true when the request signature is correct" do + request = OAuth::RequestProxy::MockRequest.new( + "method" => "POST", + "uri" => "https://photos.example.net/initialize", + "parameters" => { + "oauth_consumer_key" => "dpf43f3p2l4k3l03", + "oauth_signature_method" => "HMAC-SHA256", + "oauth_timestamp" => "137131200", + "oauth_nonce" => "wIjqoS", + "oauth_callback" => "http://printer.example.com/ready", + "oauth_version" => "1.0", + "oauth_signature" => "tkpCGNHi3laWBHQ9+Ka5IOeixEuhxg12LTMlLJxQxKc=", + }, + ) + + # This test in MiniTest asserts truthiness for a specific vector; here we assert it verifies. + expect(described_class.new(request, consumer_secret: "kd94hf93k423kf44").verify).to be true + end + + it "returns false when the request signature is wrong" do + request = OAuth::RequestProxy::MockRequest.new( + "method" => "POST", + "uri" => "https://photos.example.net/initialize", + "parameters" => { + "oauth_consumer_key" => "dpf43f3p2l4k3l03", + "oauth_signature_method" => "HMAC-SHA256", + "oauth_timestamp" => "137131200", + "oauth_nonce" => "wIjqoS", + "oauth_callback" => "http://printer.example.com/ready", + "oauth_version" => "1.0", + "oauth_signature" => "not-a-real-valid-signature==", + }, + ) + + expect(described_class.new(request, consumer_secret: "kd94hf93k423kf44").verify).to be false + end + end +end diff --git a/spec/oauth/signature/plaintext_spec.rb b/spec/oauth/signature/plaintext_spec.rb new file mode 100644 index 00000000..059d3311 --- /dev/null +++ b/spec/oauth/signature/plaintext_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require "net/http" + +RSpec.describe "OAuth::Signature PLAINTEXT" do + describe "::available_methods" do + it "includes 'plaintext'" do + expect(OAuth::Signature.available_methods).to include("plaintext") + end + end + + describe "verification" do + it "verifies example without token secret" do + request = Net::HTTP::Get.new("/photos?file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_signature=kd94hf93k423kf44%26&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=PLAINTEXT") + + consumer = OAuth::Consumer.new("dpf43f3p2l4k3l03", "kd94hf93k423kf44") + token = OAuth::Token.new("nnch734d00sl2jdk", nil) + + expect( + OAuth::Signature.verify( + request, + consumer: consumer, + token: token, + uri: "http://photos.example.net/photos", + ), + ).to be true + end + + it "verifies example with token secret" do + request = Net::HTTP::Get.new("/photos?file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_signature=kd94hf93k423kf44%26pfkkdhi9sl3r4s00&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=PLAINTEXT") + + consumer = OAuth::Consumer.new("dpf43f3p2l4k3l03", "kd94hf93k423kf44") + token = OAuth::Token.new("nnch734d00sl2jdk", "pfkkdhi9sl3r4s00") + + expect( + OAuth::Signature.verify( + request, + consumer: consumer, + token: token, + uri: "http://photos.example.net/photos", + ), + ).to be true + end + end +end diff --git a/spec/oauth/signature/rsa_sha1_spec.rb b/spec/oauth/signature/rsa_sha1_spec.rb new file mode 100644 index 00000000..0466738f --- /dev/null +++ b/spec/oauth/signature/rsa_sha1_spec.rb @@ -0,0 +1,189 @@ +# frozen_string_literal: true + +require "net/http" +require "base64" +require "openssl" +require "oauth/consumer" +require "oauth/signature/rsa/sha1" + +RSpec.describe OAuth::Signature::RSA::SHA1 do + def project_spec_dir + # two levels up from spec/oauth/signature -> spec + File.expand_path(File.join(__dir__, "../..")) + end + + def pem_path + File.expand_path("support/fixtures/keys/rsa.pem", project_spec_dir) + end + + def cert_path + File.expand_path("support/fixtures/keys/rsa.cert", project_spec_dir) + end + + def consumer_key + "dpf43f3p2l4k3l03" + end + + def x509_certificate + OpenSSL::X509::Certificate.new(File.read(cert_path)) + end + + def pkey_rsa + OpenSSL::PKey::RSA.new(File.read(pem_path)) + end + + def sha1_available? + key = pkey_rsa + key.sign(OpenSSL::Digest.new("SHA1"), "x") + true + rescue OpenSSL::PKey::PKeyError + false + end + + let(:request) do + Net::HTTP::Get.new("/photos?file=vacaction.jpg&size=original&oauth_version=1.0&oauth_consumer_key=#{consumer_key}&oauth_timestamp=1196666512&oauth_nonce=13917289812797014437&oauth_signature_method=RSA-SHA1") + end + + let(:consumer) { OAuth::Consumer.new(consumer_key, pkey_rsa) } + + before do + skip("Skipping RSA-SHA1 tests: SHA1 digest not available in this OpenSSL provider") unless sha1_available? + end + + it "implements rsa-sha1" do + expect(OAuth::Signature.available_methods).to include("rsa-sha1") + end + + it "produces matching signature base string for example request" do + sbs = OAuth::Signature.signature_base_string(request, { + consumer: consumer, + uri: "http://photos.example.net/photos", + }) + + expect(sbs).to eq( + "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512%26oauth_version%3D1.0%26size%3Doriginal", + ) + end + + it "produces matching signature for example request" do + signature = OAuth::Signature.sign(request, { + consumer: consumer, + uri: "http://photos.example.net/photos", + }) + + expect(signature).to eq( + "jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=", + ) + end + + it "produces matching signature using private_key_file option" do + consumer2 = OAuth::Consumer.new(consumer_key, nil) + + signature = OAuth::Signature.sign(request, { + consumer: consumer2, + private_key_file: pem_path, + uri: "http://photos.example.net/photos", + }) + + expect(signature).to eq( + "jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=", + ) + end + + it "verifies signature when consumer has x509 certificate" do + req2 = Net::HTTP::Get.new("/photos?oauth_signature_method=RSA-SHA1&oauth_version=1.0&oauth_consumer_key=#{consumer_key}&oauth_timestamp=1196666512&oauth_nonce=13917289812797014437&file=vacaction.jpg&size=original&oauth_signature=jvTp%2FwX1TYtByB1m%2BPbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2%2F9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW%2F%2Fe%2BRinhejgCuzoH26dyF8iY2ZZ%2F5D1ilgeijhV%2FvBka5twt399mXwaYdCwFYE%3D") + consumer3 = OAuth::Consumer.new(consumer_key, x509_certificate) + + expect( + OAuth::Signature.verify(req2, { + consumer: consumer3, + uri: "http://photos.example.net/photos", + }), + ).to be true + end + + it "verifies signature with pem in consumer secret" do + req2 = Net::HTTP::Get.new("/photos?oauth_signature_method=RSA-SHA1&oauth_version=1.0&oauth_consumer_key=#{consumer_key}&oauth_timestamp=1196666512&oauth_nonce=13917289812797014437&file=vacaction.jpg&size=original&oauth_signature=jvTp%2FwX1TYtByB1m%2BPbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2%2F9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW%2F%2Fe%2BRinhejgCuzoH26dyF8iY2ZZ%2F5D1ilgeijhV%2FvBka5twt399mXwaYdCwFYE%3D") + + expect( + OAuth::Signature.verify(req2, { + consumer: consumer, + uri: "http://photos.example.net/photos", + }), + ).to be true + end + + it "computes body_hash with request body" do + post = Net::HTTP::Post.new("/photos") + post.body = "abc123" + + proxy = OAuth::RequestProxy.proxy(post, {uri: "http://photos.example.net/photos"}) + signer = described_class.new(proxy, {consumer: consumer}) + + expected = begin + Base64.encode64(OpenSSL::Digest.digest("SHA1", "abc123")).chomp.delete("\n") + rescue StandardError + Base64.encode64(Digest::SHA1.digest("abc123")).chomp.delete("\n") + end + + expect(signer.body_hash).to eq(expected) + end + + it "derives public key from string PEM" do + pem = File.read(pem_path) + consumer_pem = OAuth::Consumer.new(consumer_key, pem) + + proxy = OAuth::RequestProxy.proxy(request, {uri: "http://photos.example.net/photos"}) + signer = described_class.new(proxy, {consumer: consumer_pem}) + + expect(signer.public_key).to be_a(OpenSSL::PKey::RSA) + end + + it "derives public key from string certificate" do + cert = File.read(cert_path) + consumer_cert = OAuth::Consumer.new(consumer_key, cert) + + proxy = OAuth::RequestProxy.proxy(request, {uri: "http://photos.example.net/photos"}) + signer = described_class.new(proxy, {consumer: consumer_cert}) + + expect(signer.public_key).to be_a(OpenSSL::PKey::RSA) + end + + it "supports direct equality operator with correct signature" do + proxy = OAuth::RequestProxy.proxy(request, {uri: "http://photos.example.net/photos", consumer: consumer}) + signer = described_class.new(proxy, {consumer: consumer}) + good_sig = "jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=" + expect(signer == good_sig).to be true + end + + it "supports direct equality operator with incorrect signature" do + proxy = OAuth::RequestProxy.proxy(request, {uri: "http://photos.example.net/photos", consumer: consumer}) + signer = described_class.new(proxy, {consumer: consumer}) + expect(signer == "Invalid==").to be false + end + + it "honors :private_key option for digest/signature" do + consumer_nil = OAuth::Consumer.new(consumer_key, nil) + proxy = OAuth::RequestProxy.proxy(request, {uri: "http://photos.example.net/photos"}) + + sig1 = OAuth::Signature.sign(request, { + consumer: consumer_nil, + private_key: File.read(pem_path), + uri: "http://photos.example.net/photos", + }) + + signer = described_class.new(proxy, {consumer: consumer_nil, private_key: File.read(pem_path)}) + sig2 = signer.signature + + expect(sig1).to eq(sig2) + end + + it "verify returns false for invalid signature" do + bad_request = Net::HTTP::Get.new("/photos?oauth_signature_method=RSA-SHA1&oauth_version=1.0&oauth_consumer_key=#{consumer_key}&oauth_timestamp=1196666512&oauth_nonce=13917289812797014437&file=vacaction.jpg&size=original&oauth_signature=INVALID%3D") + consumer_cert = OAuth::Consumer.new(consumer_key, x509_certificate) + + expect( + OAuth::Signature.verify(bad_request, {consumer: consumer_cert, uri: "http://photos.example.net/photos"}), + ).to be false + end +end diff --git a/spec/oauth/token_spec.rb b/spec/oauth/token_spec.rb new file mode 100644 index 00000000..a3262cf2 --- /dev/null +++ b/spec/oauth/token_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "oauth/token" + +RSpec.describe OAuth::Token do + describe "#initialize" do + it "assigns token and secret" do + token = described_class.new("xyz", "123") + expect(token.token).to eq("xyz") + expect(token.secret).to eq("123") + end + end +end diff --git a/spec/oauth/tokens/access_token_spec.rb b/spec/oauth/tokens/access_token_spec.rb new file mode 100644 index 00000000..d3a0dca2 --- /dev/null +++ b/spec/oauth/tokens/access_token_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "oauth/tokens/access_token" +require "oauth/consumer" + +RSpec.describe OAuth::AccessToken do + let(:fake_response) do + { + user_id: 5_734_758_743_895, + oauth_token: "key", + oauth_token_secret: "secret", + } + end + + let(:consumer) { OAuth::Consumer.new("key", "secret", {}) } + + describe "::from_hash" do + it "creates an access token exposing response params" do + access_token = described_class.from_hash(consumer, fake_response) + expect(access_token).to be_a(described_class) + expect(access_token).to respond_to(:params) + expect(access_token.params[:user_id]).to eq(5_734_758_743_895) + end + end +end diff --git a/spec/oauth/tokens/request_token_spec.rb b/spec/oauth/tokens/request_token_spec.rb new file mode 100644 index 00000000..69a528c6 --- /dev/null +++ b/spec/oauth/tokens/request_token_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "oauth/tokens/request_token" +require "oauth/consumer" + +RSpec.describe OAuth::RequestToken do + let(:consumer) { OAuth::Consumer.new("key", "secret", {}) } + let(:request_token) { described_class.new(consumer, "key", "secret") } + + describe "authorize_url" do + it "includes oauth_token and additional params when provided" do + url = request_token.authorize_url(oauth_callback: "github.com") + expect(url).to be_a(String) + expect(url).to match(/oauth_token=/) + expect(url).to match(/oauth_callback=/) + end + + it "includes only oauth_token when no params provided" do + url = request_token.authorize_url(nil) + expect(url).to be_a(String) + expect(url).to match(/\?oauth_token=/) + + url2 = request_token.authorize_url + expect(url2).to be_a(String) + expect(url2).to match(/\?oauth_token=/) + end + + it "returns nil when token is nil" do + request_token.token = nil + expect(request_token.authorize_url).to be_nil + end + end + + describe "authenticate_url" do + it "includes oauth_token and additional params when provided" do + url = request_token.authenticate_url(oauth_callback: "github.com") + expect(url).to be_a(String) + expect(url).to match(/oauth_token=/) + expect(url).to match(/oauth_callback=/) + end + + it "includes only oauth_token when no params provided" do + url = request_token.authenticate_url(nil) + expect(url).to be_a(String) + expect(url).to match(/\?oauth_token=/) + + url2 = request_token.authenticate_url + expect(url2).to be_a(String) + expect(url2).to match(/\?oauth_token=/) + end + + it "returns nil when token is nil" do + request_token.token = nil + expect(request_token.authenticate_url).to be_nil + end + end + + describe "private build_url (via stubbed subclass)" do + class StubbedToken < OAuth::RequestToken + def build_url_promoted(root_domain, params) + build_url(root_domain, params) + end + end + + it "percent-encodes values and joins with ?" do + token = StubbedToken.new(nil, nil, nil) + expect(token).to respond_to(:build_url_promoted) + url = token.build_url_promoted("http://github.com/oauth/authorize", {foo: "bar bar"}) + expect(url).to eq("http://github.com/oauth/authorize?foo=bar+bar") + end + end +end diff --git a/spec/oauth/tty/cli_spec.rb b/spec/oauth/tty/cli_spec.rb new file mode 100644 index 00000000..9be3db7c --- /dev/null +++ b/spec/oauth/tty/cli_spec.rb @@ -0,0 +1,298 @@ +# frozen_string_literal: true + +RSpec.describe OAuth::TTY::CLI do + let(:stdout) { StringIO.new } + let(:stdin) { StringIO.new } + let(:stderr) { StringIO.new } + + def run_command(args = []) + cmd = args.shift + # Ensure isolated capture per run; clear any previous content on shared stdout + stdout.truncate(0) + stdout.rewind + described_class.new(stdout, stdin, stderr, cmd, args).run + stdout.rewind + stdout.read + end + + def parse(command) + cli = described_class.new(stdout, stdin, stderr, command, []) + cli.send(:parse_command, command) + end + + describe "#parse_command" do + it "parses dashed variants" do + expect(parse("-v")).to eq("version") + expect(parse("--version")).to eq("version") + + expect(parse("-h")).to eq("help") + expect(parse("--help")).to eq("help") + expect(parse("-H")).to eq("help") + expect(parse("--HELP")).to eq("help") + end + + it "parses default/unknown as help" do + expect(parse("")).to eq("help") + expect(parse(nil)).to eq("help") + expect(parse("NotACommand")).to eq("help") + end + + it "parses abbreviated lowercase commands" do + expect(parse("h")).to eq("help") + expect(parse("v")).to eq("version") + expect(parse("q")).to eq("query") + expect(parse("a")).to eq("authorize") + expect(parse("s")).to eq("sign") + end + + it "parses full lowercase commands" do + expect(parse("help")).to eq("help") + expect(parse("version")).to eq("version") + expect(parse("query")).to eq("query") + expect(parse("authorize")).to eq("authorize") + expect(parse("sign")).to eq("sign") + end + + it "parses abbreviated uppercase commands" do + expect(parse("H")).to eq("help") + expect(parse("V")).to eq("version") + expect(parse("Q")).to eq("query") + expect(parse("A")).to eq("authorize") + expect(parse("S")).to eq("sign") + end + + it "parses full uppercase commands" do + expect(parse("HELP")).to eq("help") + expect(parse("VERSION")).to eq("version") + expect(parse("QUERY")).to eq("query") + expect(parse("AUTHORIZE")).to eq("authorize") + expect(parse("SIGN")).to eq("sign") + end + end + + describe "#run", :check_output do + it "prints help when empty args" do + out = run_command + expect(out).to match(/Usage: /) + end + + it "prints help" do + out = run_command(%w[help]) + expect(out).to match(/Usage: /) + end + + it "prints version" do + out = run_command(%w[version]) + # Normalize whitespace per line so the spec is insensitive to indentation differences + normalized = out.lines.map { |l| l.strip } + expected = [ + "OAuth Gem #{OAuth::Version::VERSION}", + "OAuth TTY Gem #{OAuth::TTY::Version::VERSION}" + ] + expect(normalized).to eq(expected) + end + + it "prints help for query with no args" do + out = run_command(%w[query]) + expect(out).to eq(help_output) + end + + it "prints help for sign with no args" do + out = run_command(%w[sign]) + expect(out).to eq(help_output) + end + + it "prints help for authorize with no args" do + out = run_command(%w[authorize]) + expect(out).to eq(help_output) + end + + it "performs query and prints request/response" do + consumer = instance_double("OAuth::Consumer") + access_token = instance_double("OAuth::AccessToken") + response = instance_double("Response", code: "!code!", message: "!message!", body: "!body!") + + allow(OAuth::Helper).to receive(:generate_key).and_return("GENERATE_KEY") + allow(OAuth::Helper).to receive(:generate_timestamp).and_return("GENERATE_TIMESTAMP") + + expect(OAuth::Consumer).to receive(:new) do |key, secret, options| + expect(key).to eq("oauth_consumer_key") + expect(secret).to eq("oauth_consumer_secret") + expect(options).to eq({scheme: :header}) + consumer + end + + expect(OAuth::AccessToken).to receive(:new) do |c, token, secret| + expect(c).to equal(consumer) + expect(token).to eq("TOKEN") + expect(secret).to eq("SECRET") + access_token + end + + expect(access_token).to receive(:request).with(:post, "http://example.com/oauth/url?oauth_consumer_key=oauth_consumer_key&oauth_nonce=GENERATE_KEY&oauth_timestamp=GENERATE_TIMESTAMP&oauth_token=TOKEN&oauth_signature_method=HMAC-SHA1&oauth_version=1.0").and_return(response) + + out = run_command %w[ + query + --consumer-key + oauth_consumer_key + --consumer-secret + oauth_consumer_secret + --token + TOKEN + --secret + SECRET + --uri + http://example.com/oauth/url + ] + + expect(out).to eq(<<~EXPECTED) + http://example.com/oauth/url?oauth_consumer_key=oauth_consumer_key&oauth_nonce=GENERATE_KEY&oauth_timestamp=GENERATE_TIMESTAMP&oauth_token=TOKEN&oauth_signature_method=HMAC-SHA1&oauth_version=1.0 + !code! !message! + !body! + EXPECTED + end + + it "performs authorize and prompts/prints response" do + access_token = instance_double("OAuth::AccessToken", params: {}) + consumer = instance_double("OAuth::Consumer") + request_token = instance_double("OAuth::RequestToken") + + allow(OAuth::Helper).to receive(:generate_key).and_return("GENERATE_KEY") + allow(OAuth::Helper).to receive(:generate_timestamp).and_return("GENERATE_TIMESTAMP") + + expect(OAuth::Consumer).to receive(:new) do |key, secret, options| + expected = {access_token_url: nil, authorize_url: nil, request_token_url: nil, scheme: :header, http_method: :get} + expect(key).to eq("oauth_consumer_key") + expect(secret).to eq("oauth_consumer_secret") + expect(options).to eq(expected) + consumer + end + + expect(consumer).to receive(:get_request_token).with({oauth_callback: nil}, {}).and_return(request_token) + expect(request_token).to receive(:callback_confirmed?).and_return(false) + expect(request_token).to receive(:authorize_url).and_return("!url1!") + expect(request_token).to receive(:get_access_token).with({oauth_verifier: nil}).and_return(access_token) + + out = run_command %w[ + authorize + --consumer-key + oauth_consumer_key + --consumer-secret + oauth_consumer_secret + --method + GET + --uri + http://example.com/oauth/url + ] + + expect(out).to eq(<<~EXPECTED) + Please visit this url to authorize: + !url1! + Press return to continue... + Response: + EXPECTED + end + + it "signs a request and prints signature details and value" do + instance_double("OAuth::AccessToken", params: {}) + instance_double("OAuth::Consumer") + instance_double("OAuth::RequestToken") + + allow(OAuth::Helper).to receive(:generate_key).and_return("GENERATE_KEY") + allow(OAuth::Helper).to receive(:generate_timestamp).and_return("GENERATE_TIMESTAMP") + + out = [] + + out << run_command(%w[ + sign + --consumer-key + oauth_consumer_key + --consumer-secret + oauth_consumer_secret + --method + GET + --token + TOKEN + --secret + SECRET + --uri + http://example.com/oauth/url + -v + ]) + + out << run_command(%w[ + sign + --consumer-key + oauth_consumer_key + --consumer-secret + oauth_consumer_secret + --method + GET + --token + TOKEN + --secret + SECRET + --uri + http://example.com/oauth/url + ]) + + expect(out.pop).to eq(<<~EXPECTED) + MujZyJYT5ix2s388yF8sExvPIgA= + EXPECTED + + expect(out.pop).to eq(<<~EXPECTED) + OAuth parameters: + oauth_consumer_key: oauth_consumer_key + oauth_nonce: GENERATE_KEY + oauth_timestamp: GENERATE_TIMESTAMP + oauth_token: TOKEN + oauth_signature_method: HMAC-SHA1 + oauth_version: 1.0 + + Method: GET + URI: http://example.com/oauth/url + Normalized params: oauth_consumer_key=oauth_consumer_key&oauth_nonce=GENERATE_KEY&oauth_signature_method=HMAC-SHA1&oauth_timestamp=GENERATE_TIMESTAMP&oauth_token=TOKEN&oauth_version=1.0 + Signature base string: GET&http%3A%2F%2Fexample.com%2Foauth%2Furl&oauth_consumer_key%3Doauth_consumer_key%26oauth_nonce%3DGENERATE_KEY%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3DGENERATE_TIMESTAMP%26oauth_token%3DTOKEN%26oauth_version%3D1.0 + OAuth Request URI: http://example.com/oauth/url?oauth_consumer_key=oauth_consumer_key&oauth_nonce=GENERATE_KEY&oauth_signature=MujZyJYT5ix2s388yF8sExvPIgA%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=GENERATE_TIMESTAMP&oauth_token=TOKEN&oauth_version=1.0 + Request URI: http://example.com/oauth/url? + Authorization header: OAuth oauth_consumer_key="oauth_consumer_key", oauth_nonce="GENERATE_KEY", oauth_timestamp="GENERATE_TIMESTAMP", oauth_token="TOKEN", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_signature="MujZyJYT5ix2s388yF8sExvPIgA%3D" + Signature: MujZyJYT5ix2s388yF8sExvPIgA= + Escaped signature: MujZyJYT5ix2s388yF8sExvPIgA%3D + EXPECTED + end + end + + def help_output + <<~EXPECTED + Usage: oauth [ARGS] + -B, --body Use the request body for OAuth parameters. + --consumer-key KEY Specifies the consumer key to use. + --consumer-secret SECRET Specifies the consumer secret to use. + -H, --header Use the 'Authorization' header for OAuth parameters (default). + -Q, --query-string Use the query string for OAuth parameters. + -O, --options FILE Read options from a file + + options for signing and querying + --method METHOD Specifies the method (e.g. GET) to use when signing. + --nonce NONCE Specifies the nonce to use. + --parameters PARAMS Specifies the parameters to use when signing. + --signature-method METHOD Specifies the signature method to use; defaults to HMAC-SHA1. + --token TOKEN Specifies the token to use. + --secret SECRET Specifies the token secret to use. + --timestamp TIMESTAMP Specifies the timestamp to use. + --realm REALM Specifies the realm to use. + --uri URI Specifies the URI to use when signing. + --version [VERSION] Specifies the OAuth version to use. + --no-version Omit oauth_version. + --xmpp Generate XMPP stanzas. + -v, --verbose Be verbose. + + options for authorization + --access-token-url URL Specifies the access token URL. + --authorize-url URL Specifies the authorization URL. + --callback-url URL Specifies a callback URL. + --request-token-url URL Specifies the request token URL. + --scope SCOPE Specifies the scope (Google-specific). + EXPECTED + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..340ed658 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# External libs +# N/A + +# External RSpec & related config +require "kettle/test/rspec" + +# Internal ENV config +require_relative "config/debug" +require_relative "config/vcr" +require_relative "config/warnings" + +# RSpec config +require_relative "config/rspec/rack_test" +require_relative "config/rspec/rspec_block_is_expected" +require_relative "config/rspec/rspec_core" +require_relative "config/rspec/rspec_pending_for" +require_relative "config/rspec/version_gem" + +# Config for development dependencies of this library +# i.e., not configured by this library +# +# Simplecov & related config (must run BEFORE any other requires) +# NOTE: Gemfiles for older rubies won't have kettle-soup-cover. +# The rescue LoadError handles that scenario. +begin + require "kettle-soup-cover" + require "simplecov" if Kettle::Soup::Cover::DO_COV # `.simplecov` is run here! +rescue LoadError => error + # check the error message and re-raise when unexpected + raise error unless error.message.include?("kettle") +end + +# this library +require "oauth" +# To ensure oauth-tty remains compatible with this gem, +# and because it was part of this gem until recently. +# oauth/tty will become an optional dependency in a future major release. +require "oauth/tty" diff --git a/test/keys/rsa.cert b/spec/support/fixtures/keys/rsa.cert similarity index 100% rename from test/keys/rsa.cert rename to spec/support/fixtures/keys/rsa.cert diff --git a/test/keys/rsa.pem b/spec/support/fixtures/keys/rsa.pem similarity index 100% rename from test/keys/rsa.pem rename to spec/support/fixtures/keys/rsa.pem diff --git a/spec/support/fixtures/vcr_cassettes/OAuth_Consumer_integration/integration_against_term_ie_example_endpoints/can_perform_a_full_token_dance_and_call_a_protected_resource.yml b/spec/support/fixtures/vcr_cassettes/OAuth_Consumer_integration/integration_against_term_ie_example_endpoints/can_perform_a_full_token_dance_and_call_a_protected_resource.yml new file mode 100644 index 00000000..acc90a1b --- /dev/null +++ b/spec/support/fixtures/vcr_cassettes/OAuth_Consumer_integration/integration_against_term_ie_example_endpoints/can_perform_a_full_token_dance_and_call_a_protected_resource.yml @@ -0,0 +1,42 @@ +--- +http_interactions: +- request: + method: post + uri: http://term.ie/oauth/example/request_token.php + body: + encoding: UTF-8 + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - OAuth gem v1.1.1 + Content-Length: + - '0' + Authorization: + - OAuth oauth_callback="oob", oauth_consumer_key="key", oauth_nonce="dgy9Y9dD3j3ztPSNu7B5wfZ1Jv4LlNoQs2cKhwZKg", + oauth_signature="PwrNfKOjuLi%2FQtVIIGetLJQs4b4%3D", oauth_signature_method="HMAC-SHA1", + oauth_timestamp="1757656800", oauth_version="1.0" + response: + status: + code: 404 + message: Not Found + headers: + Server: + - nginx/1.22.1 + Date: + - Fri, 12 Sep 2025 10:50:18 GMT + Content-Type: + - text/html + Content-Length: + - '153' + Connection: + - keep-alive + body: + encoding: UTF-8 + string: "\r\n404 Not Found\r\n\r\n

404 + Not Found

\r\n
nginx/1.22.1
\r\n\r\n\r\n" + recorded_at: Fri, 12 Sep 2025 06:00:00 GMT +recorded_with: VCR 6.3.1 diff --git a/test/cases/spec/1_0-final/construct_request_url_test.rb b/test/cases/spec/1_0-final/construct_request_url_test.rb deleted file mode 100644 index cb15b485..00000000 --- a/test/cases/spec/1_0-final/construct_request_url_test.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../../test_helper" - -# See http://oauth.net/core/1.0/#anchor14 -# -# 9.1.2. Construct Request URL -# -# The Signature Base String includes the request absolute URL, tying the signature to a specific endpoint. The URL used in the Signature Base String MUST include the scheme, authority, and path, and MUST exclude the query and fragment as defined by [RFC3986] section 3. -# -# If the absolute request URL is not available to the Service Provider (it is always available to the Consumer), it can be constructed by combining the scheme being used, the HTTP Host header, and the relative HTTP request URL. If the Host header is not available, the Service Provider SHOULD use the host name communicated to the Consumer in the documentation or other means. -# -# The Service Provider SHOULD document the form of URL used in the Signature Base String to avoid ambiguity due to URL normalization. Unless specified, URL scheme and authority MUST be lowercase and include the port number; http default port 80 and https default port 443 MUST be excluded. -# -# For example, the request: -# -# HTTP://Example.com:80/resource?id=123 -# Is included in the Signature Base String as: -# -# http://example.com/resource - -class ConstructRequestUrlTest < OAuthCase - def test_from_spec - assert_request_url("http://example.com/resource", "HTTP://Example.com:80/resource?id=123") - end - - def test_simple_url_with_ending_slash - assert_request_url("http://example.com/", "http://example.com/") - end - - def test_simple_url_without_ending_slash - assert_request_url("http://example.com/", "http://example.com") - end - - def test_of_normalized_http - assert_request_url("http://example.com/resource", "http://example.com/resource") - end - - def test_of_https - assert_request_url("https://example.com/resource", "HTTPS://Example.com:443/resource?id=123") - end - - def test_of_normalized_https - assert_request_url("https://example.com/resource", "https://example.com/resource") - end - - def test_of_http_with_non_standard_port - assert_request_url("http://example.com:8080/resource", "http://example.com:8080/resource") - end - - def test_of_https_with_non_standard_port - assert_request_url("https://example.com:8080/resource", "https://example.com:8080/resource") - end - - protected - - def assert_request_url(expected, given, message = nil) - assert_equal expected, request({}, "GET", given).normalized_uri, message - end -end diff --git a/test/cases/spec/1_0-final/normalize_request_parameters_test.rb b/test/cases/spec/1_0-final/normalize_request_parameters_test.rb deleted file mode 100644 index e104fa04..00000000 --- a/test/cases/spec/1_0-final/normalize_request_parameters_test.rb +++ /dev/null @@ -1,86 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../../test_helper" - -# See http://oauth.net/core/1.0/#anchor14 -# -# 9.1.1. Normalize Request Parameters -# -# The request parameters are collected, sorted and concatenated into a normalized string: -# -# Parameters in the OAuth HTTP Authorization header excluding the realm parameter. -# Parameters in the HTTP POST request body (with a content-type of application/x-www-form-urlencoded). -# HTTP GET parameters added to the URLs in the query part (as defined by [RFC3986] section 3). -# The oauth_signature parameter MUST be excluded. -# -# The parameters are normalized into a single string as follows: -# -# Parameters are sorted by name, using lexicographical byte value ordering. -# If two or more parameters share the same name, they are sorted by their value. For example: -# -# a=1, c=hi%20there, f=25, f=50, f=a, z=p, z=t -# Parameters are concatenated in their sorted order into a single string. For each parameter, -# the name is separated from the corresponding value by an ‘=’ character (ASCII code 61), even -# if the value is empty. Each name-value pair is separated by an ‘&’ character (ASCII code 38). For example: -# a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t -# - -class NormalizeRequestParametersTest < OAuthCase - def test_parameters_for_signature - params = { "a" => 1, "c" => "hi there", "f" => "a", "z" => "t" } - assert_equal params, request(params).parameters_for_signature - end - - def test_parameters_for_signature_removes_oauth_signature - params = { "a" => 1, "c" => "hi there", "f" => "a", "z" => "t" } - assert_equal params, request(params.merge({ "oauth_signature" => "blalbla" })).parameters_for_signature - end - - def test_spec_example - assert_normalized "a=1&c=hi%20there&f=25&f=50&f=a&z=p&z=t", - { "a" => 1, "c" => "hi there", "f" => %w[25 50 a], "z" => %w[p t] } - end - - def test_sorts_parameters_correctly - # values for 'f' are scrambled - assert_normalized "a=1&c=hi%20there&f=5&f=70&f=a&z=p&z=t", - { "a" => 1, "c" => "hi there", "f" => %w[a 70 5], "z" => %w[p t] } - end - - def test_empty - assert_normalized "", {} - end - - # These are from the wiki http://wiki.oauth.net/TestCases - # in the section Normalize Request Parameters - # Parameters have already been x-www-form-urlencoded (i.e. + = ) - def test_wiki1 - assert_normalized "name=", { "name" => nil } - end - - def test_wiki2 - assert_normalized "a=b", { "a" => "b" } - end - - def test_wiki3 - assert_normalized "a=b&c=d", { "a" => "b", "c" => "d" } - end - - def test_wiki4 - assert_normalized "a=x%20y&a=x%21y", { "a" => ["x!y", "x y"] } - end - - def test_wiki5 - assert_normalized "x=a&x%21y=a", { "x!y" => "a", "x" => "a" } - end - - protected - - def assert_normalized(expected, params, message = nil) - assert_equal expected, normalize_request_parameters(params), message - end - - def normalize_request_parameters(params = {}) - request(params).normalized_parameters - end -end diff --git a/test/cases/spec/1_0-final/parameter_encodings_test.rb b/test/cases/spec/1_0-final/parameter_encodings_test.rb deleted file mode 100644 index c57cafc2..00000000 --- a/test/cases/spec/1_0-final/parameter_encodings_test.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../../test_helper" - -# See http://oauth.net/core/1.0/#encoding_parameters -# -# 5.1. Parameter Encoding -# -# All parameter names and values are escaped using the [RFC3986] percent-encoding (%xx) mechanism. -# Characters not in the unreserved character set ([RFC3986] section 2.3) MUST be encoded. Characters -# in the unreserved character set MUST NOT be encoded. Hexadecimal characters in encodings MUST be -# upper case. Text names and values MUST be encoded as UTF-8 octets before percent-encoding them per [RFC3629]. -# -# unreserved = ALPHA, DIGIT, '-', '.', '_', '~' -# - -class ParameterEncodingTest < OAuthCase - def test_encodings_alpha_num - assert_encoding "abcABC123", "abcABC123" - end - - def test_encodings_non_escaped - assert_encoding "-._~", "-._~" - end - - def test_encodings_percent - assert_encoding "%25", "%" - end - - def test_encodings_plus - assert_encoding "%2B", "+" - end - - def test_encodings_space - assert_encoding "%20", " " - end - - def test_encodings_query_param_symbols - assert_encoding "%26%3D%2A", "&=*" - end - - def test_encodings_unicode_lf - assert_encoding "%0A", unicode_to_utf8("U+000A") - end - - def test_encodings_unicode_space - assert_encoding "%20", unicode_to_utf8("U+0020") - end - - def test_encodings_unicode007f - assert_encoding "%7F", unicode_to_utf8("U+007F") - end - - def test_encodings_unicode0080 - assert_encoding "%C2%80", unicode_to_utf8("U+0080") - end - - def test_encoding_unicode2708 - assert_encoding "%E2%9C%88", unicode_to_utf8("U+2708") - end - - def test_encodings_unicode3001 - assert_encoding "%E3%80%81", unicode_to_utf8("U+3001") - end - - protected - - def unicode_to_utf8(unicode) - return unicode if unicode =~ /\A[[:space:]]*\z/m - - str = "" - - # :xdigit: character set (hexadecimal) includes the character set of :digit: (decimal) - unicode.scan(/(U\+(?:[[:xdigit:]]{4,5}|10[[:xdigit:]]{4})|.)/mu) do - c = Regexp.last_match(1) - str += if /^U\+/.match?(c) - [c[2..].hex].pack("U*") - else - c - end - end - - str - end - - def assert_encoding(expected, given, message = nil) - assert_equal expected, OAuth::Helper.escape(given), message - end -end diff --git a/test/cases/spec/1_0-final/signature_base_strings_test.rb b/test/cases/spec/1_0-final/signature_base_strings_test.rb deleted file mode 100644 index b288716a..00000000 --- a/test/cases/spec/1_0-final/signature_base_strings_test.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../../test_helper" - -# See http://oauth.net/core/1.0/#anchor14 -# -# 9.1. Signature Base String -# -# The Signature Base String is a consistent reproducible concatenation of the request elements -# into a single string. The string is used as an input in hashing or signing algorithms. The -# HMAC-SHA1 signature method provides both a standard and an example of using the Signature -# Base String with a signing algorithm to generate signatures. All the request parameters MUST -# be encoded as described in Parameter Encoding prior to constructing the Signature Base String. -# - -class SignatureBaseStringTest < OAuthCase - def test_a51 - parameters = { - "oauth_consumer_key" => "dpf43f3p2l4k3l03", - "oauth_token" => "nnch734d00sl2jdk", - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "1191242096", - "oauth_nonce" => "kllo9940pd9333jh", - "oauth_version" => "1.0", - "file" => "vacation.jpg", - "size" => "original" - } - sbs = "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal" - - assert_signature_base_string sbs, parameters, "GET", "http://photos.example.net/photos" - end - - # These are from the wiki http://wiki.oauth.net/TestCases - # in the section Concatenate Test Elements - - def test_wiki_1_simple_with_ending_slash - parameters = { - "n" => "v" - } - sbs = "GET&http%3A%2F%2Fexample.com%2F&n%3Dv" - - assert_signature_base_string sbs, parameters, "GET", "http://example.com/" - end - - def test_wiki_2_simple_without_ending_slash - parameters = { - "n" => "v" - } - sbs = "GET&http%3A%2F%2Fexample.com%2F&n%3Dv" - - assert_signature_base_string sbs, parameters, "GET", "http://example.com" - end - - def test_wiki_2_request_token - parameters = { - "oauth_version" => "1.0", - "oauth_consumer_key" => "dpf43f3p2l4k3l03", - "oauth_timestamp" => "1191242090", - "oauth_nonce" => "hsu94j3884jdopsl", - "oauth_signature_method" => "PLAINTEXT", - "oauth_signature" => "ignored" - } - sbs = "POST&https%3A%2F%2Fphotos.example.net%2Frequest_token&oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dhsu94j3884jdopsl%26oauth_signature_method%3DPLAINTEXT%26oauth_timestamp%3D1191242090%26oauth_version%3D1.0" - - assert_signature_base_string sbs, parameters, "POST", "https://photos.example.net/request_token" - end - - protected - - def assert_signature_base_string(expected, params = {}, method = "GET", uri = "http://photos.example.net/photos", message = "Signature Base String does not match") - assert_equal expected, signature_base_string(params, method, uri), message - end - - def signature_base_string(params = {}, method = "GET", uri = "http://photos.example.net/photos") - request(params, method, uri).signature_base_string - end -end diff --git a/test/support/minitest_helpers.rb b/test/support/minitest_helpers.rb deleted file mode 100644 index 1bffdad6..00000000 --- a/test/support/minitest_helpers.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module OAuth - module MinitestHelpers - def assert_matching_headers(expected, actual) - # transform into sorted arrays - auth_intro, auth_params = actual.split(" ", 2) - assert_equal("OAuth", auth_intro) - expected = expected.split(/(,|\s)/).reject { |v| v == "" || v =~ /^[,\s]+/ }.sort - auth_params = auth_params.split(/(,|\s)/).reject { |v| v == "" || v =~ /^[,\s]+/ }.sort - assert_equal expected, auth_params - end - - def stub_test_ie - stub_request(:any, - "http://term.ie/oauth/example/request_token.php").to_return(body: "oauth_token=requestkey&oauth_token_secret=requestsecret") - stub_request(:post, - "http://term.ie/oauth/example/access_token.php").to_return(body: "oauth_token=accesskey&oauth_token_secret=accesssecret") - stub_request(:get, %r{http://term\.ie/oauth/example/echo_api\.php\?.+}).to_return(lambda { |request| - { body: request.uri.query } - }) - stub_request(:post, "http://term.ie/oauth/example/echo_api.php").to_return(lambda { |request| - { body: request.body } - }) - end - end -end - -Minitest::Test.include OAuth::MinitestHelpers diff --git a/test/support/oauth_case.rb b/test/support/oauth_case.rb deleted file mode 100644 index 439f6c8b..00000000 --- a/test/support/oauth_case.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require "oauth/signature" -require "oauth/request_proxy/mock_request" - -class OAuthCase < Minitest::Test - # avoid whining about a lack of tests - def run(*args) - @method_name ||= nil - return if @method_name.to_s == "default_test" - - super - end - - protected - - # Creates a fake request - def request(params = {}, method = "GET", uri = "http://photos.example.net/photos") - OAuth::RequestProxy.proxy({ "parameters" => params, "method" => method, "uri" => uri }) - end -end diff --git a/test/test_helper.rb b/test/test_helper.rb deleted file mode 100644 index e42a6345..00000000 --- a/test/test_helper.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -# ensure test env -ENV["RACK_ENV"] = "test" - -# Third Party Libraries -require "stringio" -require "minitest" -require "mocha/minitest" -require "rack/test" -require "webmock/minitest" - -DEBUG = ENV["DEBUG"] == "true" - -ruby_version = Gem::Version.new(RUBY_VERSION) -minimum_version = ->(version, engine = "ruby") { ruby_version >= Gem::Version.new(version) && RUBY_ENGINE == engine } -actual_version = lambda do |major, minor| - actual = Gem::Version.new(ruby_version) - major == actual.segments[0] && minor == actual.segments[1] && RUBY_ENGINE == "ruby" -end -debugging = minimum_version.call("2.7") && DEBUG -RUN_COVERAGE = minimum_version.call("2.7") && (ENV.fetch("COVER_ALL") { ENV.fetch("CI_CODECOV") { ENV["CI"].nil? } }) -ALL_FORMATTERS = actual_version.call(2, 7) && (ENV.fetch("COVER_ALL") do - ENV.fetch("CI_CODECOV") do - ENV.fetch("CI", nil) - end -end) - -require "byebug" if debugging - -if RUN_COVERAGE - require "simplecov" # Config file `.simplecov` is run immediately when simplecov loads - require "codecov" - require "simplecov-json" - require "simplecov-lcov" - require "simplecov-cobertura" - # This will override the formatter set in .simplecov - if ALL_FORMATTERS - SimpleCov::Formatter::LcovFormatter.config do |c| - c.report_with_single_file = true - c.single_report_path = "coverage/lcov.info" - end - - SimpleCov.formatters = [ - SimpleCov::Formatter::HTMLFormatter, - SimpleCov::Formatter::CoberturaFormatter, # XML for Jenkins - SimpleCov::Formatter::LcovFormatter, - SimpleCov::Formatter::JSONFormatter, # For CodeClimate - SimpleCov::Formatter::Codecov # For CodeCov - ] - end -end - -# This gem -require "oauth" - -# Test Support Code -require "support/minitest_helpers" -require "support/oauth_case" - -# Run the tests! -require "minitest/autorun" diff --git a/test/units/access_token_test.rb b/test/units/access_token_test.rb deleted file mode 100644 index 4bf5c42b..00000000 --- a/test/units/access_token_test.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class AccessTokenTest < Minitest::Test - def setup - @fake_response = { - user_id: 5_734_758_743_895, - oauth_token: "key", - oauth_token_secret: "secret" - } - # setup a fake req. token. mocking Consumer would be more appropriate... - @access_token = OAuth::AccessToken.from_hash( - OAuth::Consumer.new("key", "secret", {}), - @fake_response - ) - end - - def test_provides_response_parameters - assert @access_token - assert_respond_to @access_token, :params - end - - def test_access_token_makes_non_oauth_response_params_available - assert @access_token.params[:user_id] - assert_equal 5_734_758_743_895, @access_token.params[:user_id] - end -end diff --git a/test/units/action_controller_request_proxy_test.rb b/test/units/action_controller_request_proxy_test.rb deleted file mode 100644 index 7957dda7..00000000 --- a/test/units/action_controller_request_proxy_test.rb +++ /dev/null @@ -1,146 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/request_proxy/action_controller_request" - -class ActionControllerRequestProxyTest < Minitest::Test - def request_proxy(request_method = :get, uri_params = {}, body_params = {}) - request = ActionDispatch::TestRequest.create - request.request_uri = "/" - - case request_method - when :post - request.env["REQUEST_METHOD"] = "POST" - when :put - request.env["REQUEST_METHOD"] = "PUT" - when :patch - request.env["REQUEST_METHOD"] = "PATCH" - end - - request.env["REQUEST_URI"] = "/" - request.env["RAW_POST_DATA"] = body_params.to_query - request.env["QUERY_STRING"] = body_params.to_query - request.env["CONTENT_TYPE"] = "application/x-www-form-urlencoded" - - yield request if block_given? - OAuth::RequestProxy::ActionControllerRequest.new(request, parameters: uri_params) - end - - def test_that_proxy_simple_get_request_works_with_query_params - request_proxy = request_proxy(:get, { "key" => "value" }) - - expected_parameters = [%w[key value]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "GET", request_proxy.method - end - - def test_that_proxy_simple_post_request_works_with_query_params - request_proxy = request_proxy(:post, { "key" => "value" }) - - expected_parameters = [%w[key value]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_put_request_works_with_query_params - request_proxy = request_proxy(:put, { "key" => "value" }) - - expected_parameters = [%w[key value]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "PUT", request_proxy.method - end - - def test_that_proxy_simple_patch_request_works_with_query_params - request_proxy = request_proxy(:patch, { "key" => "value" }) - - expected_parameters = [%w[key value]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "PATCH", request_proxy.method - end - - def test_that_proxy_simple_get_request_works_with_post_params - request_proxy = request_proxy(:get, {}, { "key" => "value" }) - - expected_parameters = [] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "GET", request_proxy.method - end - - def test_that_proxy_simple_post_request_works_with_post_params - request_proxy = request_proxy(:post, {}, { "key" => "value" }) - - expected_parameters = [%w[key value]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_put_request_works_with_post_params - request_proxy = request_proxy(:put, {}, { "key" => "value" }) - - expected_parameters = [%w[key value]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "PUT", request_proxy.method - end - - def test_that_proxy_simple_patch_request_works_with_post_params - request_proxy = request_proxy(:patch, {}, { "key" => "value" }) - - expected_parameters = [] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "PATCH", request_proxy.method - end - - def test_that_proxy_simple_get_request_works_with_mixed_params - request_proxy = request_proxy(:get, { "key" => "value" }, { "key2" => "value2" }) - - expected_parameters = [%w[key value]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "GET", request_proxy.method - end - - def test_that_proxy_simple_post_request_works_with_mixed_params - request_proxy = request_proxy(:post, { "key" => "value" }, { "key2" => "value2" }) - - expected_parameters = [%w[key value], %w[key2 value2]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_put_request_works_with_mixed_params - request_proxy = request_proxy(:put, { "key" => "value" }, { "key2" => "value2" }) - - expected_parameters = [%w[key value], %w[key2 value2]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "PUT", request_proxy.method - end - - def test_that_proxy_simple_patch_request_works_with_mixed_params - request_proxy = request_proxy(:patch, { "key" => "value" }, { "key2" => "value2" }) - - expected_parameters = [%w[key value]] - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "PATCH", request_proxy.method - end - - def test_parameter_keys_should_preserve_brackets_from_hash - assert_equal( - [["message[body]", "This is a test"]], - request_proxy(:post, { message: { body: "This is a test" } }).parameters_for_signature - ) - end - - def test_parameter_values_with_amps_should_not_break_parameter_parsing - assert_equal( - [["message[body]", "http://foo.com/?a=b&c=d"]], - request_proxy(:post, { message: { body: "http://foo.com/?a=b&c=d" } }).parameters_for_signature - ) - end - - def test_parameter_keys_should_preserve_brackets_from_array - assert_equal( - [["foo[]", "123"], ["foo[]", "456"]], - request_proxy(:post, { foo: [123, 456] }).parameters_for_signature.sort - ) - end -end diff --git a/test/units/action_dispatch_request_proxy_test.rb b/test/units/action_dispatch_request_proxy_test.rb deleted file mode 100644 index 0161481a..00000000 --- a/test/units/action_dispatch_request_proxy_test.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/request_proxy/action_dispatch_request" - -class ActionRequestProxyTest < Minitest::Test - def test_that_proxy_simple_get_request_works - request = ActionDispatch::Request.new(Rack::MockRequest.env_for("http://example.com/test?key=value")) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test?key=value" }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "GET", request_proxy.method - end - - def test_that_proxy_simple_post_request_works - request = ActionDispatch::Request.new(Rack::MockRequest.env_for("http://example.com/test", method: "POST")) - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_post_and_get_request_works - request = ActionDispatch::Request.new(Rack::MockRequest.env_for("http://example.com/test?key=value", - method: "POST", input: "key2=value2")) - params = { "key2" => "value2" } - request_proxy = OAuth::RequestProxy.proxy(request, - { uri: "http://example.com/test?key=value", parameters: params }) - - expected_parameters = { "key" => "value", "key2" => "value2" } - assert_equal expected_parameters, request_proxy.parameters - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end -end diff --git a/test/units/cli_test.rb b/test/units/cli_test.rb deleted file mode 100644 index 8ab34962..00000000 --- a/test/units/cli_test.rb +++ /dev/null @@ -1,307 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/cli" - -class CliTest < Minitest::Test - def test_parse_dashed - assert_equal "version", parse("-v") - assert_equal "version", parse("--version") - - assert_equal "help", parse("-h") - assert_equal "help", parse("--help") - assert_equal "help", parse("-H") - assert_equal "help", parse("--HELP") - end - - def test_parse_default - assert_equal "help", parse("") - assert_equal "help", parse(nil) - - assert_equal "help", parse("NotACommand") - end - - def test_parse_abbr_lower - assert_equal "help", parse("h") - assert_equal "version", parse("v") - assert_equal "query", parse("q") - assert_equal "authorize", parse("a") - assert_equal "sign", parse("s") - end - - def test_parse_full_lower - assert_equal "help", parse("help") - assert_equal "version", parse("version") - assert_equal "query", parse("query") - assert_equal "authorize", parse("authorize") - assert_equal "sign", parse("sign") - end - - def test_parse_abbr_upper - assert_equal "help", parse("H") - assert_equal "version", parse("V") - assert_equal "query", parse("Q") - assert_equal "authorize", parse("A") - assert_equal "sign", parse("S") - end - - def test_parse_full_upper - assert_equal "help", parse("HELP") - assert_equal "version", parse("VERSION") - assert_equal "query", parse("QUERY") - assert_equal "authorize", parse("AUTHORIZE") - assert_equal "sign", parse("SIGN") - end - - def test_help_empty - out = run_command - - assert_match(/Usage: /, out) - end - - def test_help - out = run_command(%w[help]) - - assert_match(/Usage: /, out) - end - - def test_version - out = run_command(%w[version]) - - assert_match "OAuth Gem #{OAuth::Version::VERSION}\n", out - end - - def test_query_empty - out = run_command(%w[query]) - - assert_equal help_output, out - end - - def test_sign_empty - out = run_command(%w[sign]) - - assert_equal help_output, out - end - - def test_authorize_empty - out = run_command(%w[authorize]) - - assert_equal help_output, out - end - - def test_query - consumer = Minitest::Mock.new - access_token = MiniTest::Mock.new - response = MiniTest::Mock.new - - consumer_new = lambda { |oauth_consumer_key, oauth_consumer_secret, options| - expected_options = { scheme: :header } - assert_equal "oauth_consumer_key", oauth_consumer_key - assert_equal "oauth_consumer_secret", oauth_consumer_secret - assert_equal expected_options, options - consumer - } - access_token_new = lambda { |consumer1, token, secret| - assert_equal consumer1.object_id, consumer.object_id - assert_equal "TOKEN", token - assert_equal "SECRET", secret - access_token - } - - # mock expects: - # method return arguments - #------------------------------------------------------------- - response.expect(:code, "!code!") - response.expect(:message, "!message!") - response.expect(:body, "!body!") - access_token.expect(:request, response, [:post, "http://example.com/oauth/url?oauth_consumer_key=oauth_consumer_key&oauth_nonce=GENERATE_KEY&oauth_timestamp=GENERATE_TIMESTAMP&oauth_token=TOKEN&oauth_signature_method=HMAC-SHA1&oauth_version=1.0"]) - - OAuth::Helper.stub(:generate_key, "GENERATE_KEY") do - OAuth::Helper.stub(:generate_timestamp, "GENERATE_TIMESTAMP") do - OAuth::AccessToken.stub(:new, access_token_new) do - OAuth::Consumer.stub(:new, consumer_new) do - out = run_command %w[query - --consumer-key oauth_consumer_key - --consumer-secret oauth_consumer_secret - --token TOKEN - --secret SECRET - --uri http://example.com/oauth/url] - - assert_equal(<<~EXPECTED, out) - http://example.com/oauth/url?oauth_consumer_key=oauth_consumer_key&oauth_nonce=GENERATE_KEY&oauth_timestamp=GENERATE_TIMESTAMP&oauth_token=TOKEN&oauth_signature_method=HMAC-SHA1&oauth_version=1.0 - !code! !message! - !body! - EXPECTED - end - end - end - end - end - - def test_authorize - access_token = MiniTest::Mock.new - consumer = MiniTest::Mock.new - request_token = MiniTest::Mock.new - - consumer_new = lambda { |oauth_consumer_key, oauth_consumer_secret, options| - expected_options = { access_token_url: nil, authorize_url: nil, request_token_url: nil, scheme: :header, - http_method: :get } - assert_equal "oauth_consumer_key", oauth_consumer_key - assert_equal "oauth_consumer_secret", oauth_consumer_secret - assert_equal expected_options, options - consumer - } - - # mock expects: - # method return arguments - #---------------------------------------------------------------------- - access_token.expect(:params, {}) - consumer.expect(:get_request_token, request_token, [{ oauth_callback: nil }, {}]) - request_token.expect(:callback_confirmed?, false) - request_token.expect(:authorize_url, "!url1!") - request_token.expect(:get_access_token, access_token, [{ oauth_verifier: nil }]) - - OAuth::Helper.stub(:generate_key, "GENERATE_KEY") do - OAuth::Helper.stub(:generate_timestamp, "GENERATE_TIMESTAMP") do - OAuth::Consumer.stub(:new, consumer_new) do - out = run_command %w[authorize - --consumer-key oauth_consumer_key - --consumer-secret oauth_consumer_secret - --method GET - --uri http://example.com/oauth/url] - - assert_equal(<<~EXPECTED, out) - Please visit this url to authorize: - !url1! - Press return to continue... - Response: - EXPECTED - end - end - end - end - - def test_sign - access_token = MiniTest::Mock.new - consumer = MiniTest::Mock.new - request_token = MiniTest::Mock.new - - consumer_new = lambda { |oauth_consumer_key, oauth_consumer_secret, options| - expected_options = { access_token_url: nil, authorize_url: nil, request_token_url: nil, scheme: :header, - http_method: :get } - assert_equal "oauth_consumer_key", oauth_consumer_key - assert_equal "oauth_consumer_secret", oauth_consumer_secret - assert_equal expected_options, options - consumer - } - - # mock expects: - # method return arguments - #---------------------------------------------------------------------- - access_token.expect(:params, {}) - consumer.expect(:get_request_token, request_token, [{ oauth_callback: nil }, {}]) - request_token.expect(:callback_confirmed?, false) - request_token.expect(:authorize_url, "!url1!") - request_token.expect(:get_access_token, access_token, [{ oauth_verifier: nil }]) - - out = [] - - OAuth::Helper.stub(:generate_key, "GENERATE_KEY") do - OAuth::Helper.stub(:generate_timestamp, "GENERATE_TIMESTAMP") do - OAuth::Consumer.stub(:new, consumer_new) do - out.push run_command %w[sign - --consumer-key oauth_consumer_key - --consumer-secret oauth_consumer_secret - --method GET - --token TOKEN - --secret SECRET - --uri http://example.com/oauth/url - -v] - - out.push run_command %w[sign - --consumer-key oauth_consumer_key - --consumer-secret oauth_consumer_secret - --method GET - --token TOKEN - --secret SECRET - --uri http://example.com/oauth/url] - end - end - end - - assert_equal(<<~EXPECTED, out.pop) - MujZyJYT5ix2s388yF8sExvPIgA= - EXPECTED - - assert_equal(<<~EXPECTED, out.pop) - OAuth parameters: - oauth_consumer_key: oauth_consumer_key - oauth_nonce: GENERATE_KEY - oauth_timestamp: GENERATE_TIMESTAMP - oauth_token: TOKEN - oauth_signature_method: HMAC-SHA1 - oauth_version: 1.0 - - Method: GET - URI: http://example.com/oauth/url - Normalized params: oauth_consumer_key=oauth_consumer_key&oauth_nonce=GENERATE_KEY&oauth_signature_method=HMAC-SHA1&oauth_timestamp=GENERATE_TIMESTAMP&oauth_token=TOKEN&oauth_version=1.0 - Signature base string: GET&http%3A%2F%2Fexample.com%2Foauth%2Furl&oauth_consumer_key%3Doauth_consumer_key%26oauth_nonce%3DGENERATE_KEY%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3DGENERATE_TIMESTAMP%26oauth_token%3DTOKEN%26oauth_version%3D1.0 - OAuth Request URI: http://example.com/oauth/url?oauth_consumer_key=oauth_consumer_key&oauth_nonce=GENERATE_KEY&oauth_signature=MujZyJYT5ix2s388yF8sExvPIgA%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=GENERATE_TIMESTAMP&oauth_token=TOKEN&oauth_version=1.0 - Request URI: http://example.com/oauth/url? - Authorization header: OAuth oauth_consumer_key=\"oauth_consumer_key\", oauth_nonce=\"GENERATE_KEY\", oauth_timestamp=\"GENERATE_TIMESTAMP\", oauth_token=\"TOKEN\", oauth_signature_method=\"HMAC-SHA1\", oauth_version=\"1.0\", oauth_signature=\"MujZyJYT5ix2s388yF8sExvPIgA%3D\" - Signature: MujZyJYT5ix2s388yF8sExvPIgA= - Escaped signature: MujZyJYT5ix2s388yF8sExvPIgA%3D - EXPECTED - end - - private - - def run_command(arguments = []) - s = StringIO.new - command = arguments.shift - OAuth::CLI.new(s, StringIO.new, StringIO.new, command, arguments).run - - s.rewind - s.read - end - - def parse(command) - cli = OAuth::CLI.new(StringIO.new, StringIO.new, StringIO.new, command, []) - cli.send(:parse_command, command) - end - - def help_output - <<~EXPECTED - Usage: oauth [ARGS] - -B, --body Use the request body for OAuth parameters. - --consumer-key KEY Specifies the consumer key to use. - --consumer-secret SECRET Specifies the consumer secret to use. - -H, --header Use the 'Authorization' header for OAuth parameters (default). - -Q, --query-string Use the query string for OAuth parameters. - -O, --options FILE Read options from a file - - options for signing and querying - --method METHOD Specifies the method (e.g. GET) to use when signing. - --nonce NONCE Specifies the nonce to use. - --parameters PARAMS Specifies the parameters to use when signing. - --signature-method METHOD Specifies the signature method to use; defaults to HMAC-SHA1. - --token TOKEN Specifies the token to use. - --secret SECRET Specifies the token secret to use. - --timestamp TIMESTAMP Specifies the timestamp to use. - --realm REALM Specifies the realm to use. - --uri URI Specifies the URI to use when signing. - --version [VERSION] Specifies the OAuth version to use. - --no-version Omit oauth_version. - --xmpp Generate XMPP stanzas. - -v, --verbose Be verbose. - - options for authorization - --access-token-url URL Specifies the access token URL. - --authorize-url URL Specifies the authorization URL. - --callback-url URL Specifies a callback URL. - --request-token-url URL Specifies the request token URL. - --scope SCOPE Specifies the scope (Google-specific). - EXPECTED - end -end diff --git a/test/units/client_helper_test.rb b/test/units/client_helper_test.rb deleted file mode 100644 index 8cc8a578..00000000 --- a/test/units/client_helper_test.rb +++ /dev/null @@ -1,149 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/client" - -class ClientHelperTest < Minitest::Test - def setup - @consumer = OAuth::Consumer.new( - "consumer_key_86cad9", "5888bf0345e5d237", - { - site: "http://blabla.bla", - proxy: "http://user:password@proxy.bla:8080", - request_token_path: "/oauth/example/request_token.php", - access_token_path: "/oauth/example/access_token.php", - authorize_path: "/oauth/example/authorize.php", - scheme: :header, - http_method: :get - } - ) - end - - def test_oauth_parameters_allow_empty_params_default - helper = OAuth::Client::Helper.new(nil, { - consumer: @consumer - }) - helper.stub :timestamp, "0" do - helper.stub :nonce, "nonce" do - expected = { - "oauth_consumer_key" => "consumer_key_86cad9", - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "0", - "oauth_nonce" => "nonce", - "oauth_version" => "1.0" - } - assert_equal expected, helper.oauth_parameters - end - end - end - - def test_oauth_parameters_allow_empty_params_true - input = true - helper = OAuth::Client::Helper.new(nil, { - consumer: @consumer, - allow_empty_params: input - }) - helper.stub :timestamp, "0" do - helper.stub :nonce, "nonce" do - expected = { - "oauth_body_hash" => nil, - "oauth_callback" => nil, - "oauth_consumer_key" => "consumer_key_86cad9", - "oauth_token" => "", - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "0", - "oauth_nonce" => "nonce", - "oauth_verifier" => nil, - "oauth_version" => "1.0", - "oauth_session_handle" => nil - } - assert_equal expected, helper.oauth_parameters - end - end - end - - def test_oauth_parameters_allow_empty_params_false - input = false - helper = OAuth::Client::Helper.new(nil, { - consumer: @consumer, - allow_empty_params: input - }) - helper.stub :timestamp, "0" do - helper.stub :nonce, "nonce" do - expected = { - "oauth_consumer_key" => "consumer_key_86cad9", - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "0", - "oauth_nonce" => "nonce", - "oauth_version" => "1.0" - } - assert_equal expected, helper.oauth_parameters - end - end - end - - def test_oauth_parameters_allow_empty_params_only_oauth_token_as_string - input = "oauth_token" - helper = OAuth::Client::Helper.new(nil, { - consumer: @consumer, - allow_empty_params: input - }) - helper.stub :timestamp, "0" do - helper.stub :nonce, "nonce" do - expected = { - "oauth_consumer_key" => "consumer_key_86cad9", - "oauth_token" => "", - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "0", - "oauth_nonce" => "nonce", - "oauth_version" => "1.0" - } - assert_equal expected, helper.oauth_parameters - end - end - end - - def test_oauth_parameters_allow_empty_params_only_oauth_token_as_array - input = ["oauth_token"] - helper = OAuth::Client::Helper.new(nil, { - consumer: @consumer, - allow_empty_params: input - }) - helper.stub :timestamp, "0" do - helper.stub :nonce, "nonce" do - expected = { - "oauth_consumer_key" => "consumer_key_86cad9", - "oauth_token" => "", - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "0", - "oauth_nonce" => "nonce", - "oauth_version" => "1.0" - } - assert_equal expected, helper.oauth_parameters - end - end - end - - def test_oauth_parameters_allow_empty_params_oauth_token_and_oauth_session_handle - input = %w[oauth_token oauth_session_handle] - helper = OAuth::Client::Helper.new(nil, { - consumer: @consumer, - allow_empty_params: input - }) - helper.stub :timestamp, "0" do - helper.stub :nonce, "nonce" do - expected = { - "oauth_consumer_key" => "consumer_key_86cad9", - "oauth_token" => "", - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "0", - "oauth_nonce" => "nonce", - "oauth_version" => "1.0", - "oauth_session_handle" => nil - } - assert_equal expected, helper.oauth_parameters - end - end - end -end diff --git a/test/units/consumer_integration_test.rb b/test/units/consumer_integration_test.rb deleted file mode 100644 index ebc3bb5e..00000000 --- a/test/units/consumer_integration_test.rb +++ /dev/null @@ -1,322 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class ConsumerIntegrationTest < Minitest::Test - def setup - @consumer = OAuth::Consumer.new( - "consumer_key_86cad9", "5888bf0345e5d237", - { - site: "http://blabla.bla", - proxy: "http://user:password@proxy.bla:8080", - request_token_path: "/oauth/example/request_token.php", - access_token_path: "/oauth/example/access_token.php", - authorize_path: "/oauth/example/authorize.php", - scheme: :header, - http_method: :get - } - ) - @token = OAuth::ConsumerToken.new(@consumer, "token_411a7f", "3196ffd991c8ebdb") - @request_uri = URI.parse("http://example.com/test?key=value") - @request_parameters = { "key" => "value" } - @nonce = 225_579_211_881_198_842_005_988_698_334_675_835_446 - @timestamp = "1199645624" - @consumer.http = Net::HTTP.new(@request_uri.host, @request_uri.port) - end - - def test_that_signing_auth_headers_on_get_requests_works - request = Net::HTTP::Get.new("#{@request_uri.path}?#{request_parameters_to_s}") - @token.sign!(request, { nonce: @nonce, timestamp: @timestamp }) - - assert_equal "GET", request.method - assert_equal "/test?key=value", request.path - assert_equal "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"1oO2izFav1GP4kEH2EskwXkCRFg%3D\", oauth_version=\"1.0\"".delete(",").split.sort, - request["authorization"].delete(",").split.sort - end - - def test_that_setting_signature_method_on_consumer_effects_signing - request = Net::HTTP::Get.new(@request_uri.path) - consumer = @consumer.dup - consumer.options[:signature_method] = "PLAINTEXT" - token = OAuth::ConsumerToken.new(consumer, "token_411a7f", "3196ffd991c8ebdb") - token.sign!(request, { nonce: @nonce, timestamp: @timestamp }) - - refute_match(/oauth_signature_method="HMAC-SHA1"/, request["authorization"]) - assert_match(/oauth_signature_method="PLAINTEXT"/, request["authorization"]) - end - - def test_that_setting_signature_method_on_consumer_effects_signature_base_string - # Net::HTTP::Get.new(@request_uri.path) - consumer = @consumer.dup - consumer.options[:signature_method] = "PLAINTEXT" - - request = Net::HTTP::Get.new("/") - signature_base_string = consumer.signature_base_string(request) - - refute_match(/HMAC-SHA1/, signature_base_string) - assert_equal("#{consumer.secret}&", signature_base_string) - end - - def test_that_plaintext_signature_works - # Invalid test because server expects double-escaped signature - - # consumer = OAuth::Consumer.new("key", "secret", - # :site => "http://term.ie", :signature_method => 'PLAINTEXT') - # access_token = OAuth::AccessToken.new(consumer, 'accesskey', 'accesssecret') - # response = access_token.get("/oauth/example/echo_api.php?echo=hello") - - # assert_equal 'echo=hello', response.body - end - - def test_that_signing_auth_headers_on_post_requests_works - request = Net::HTTP::Post.new(@request_uri.path) - request.set_form_data(@request_parameters) - @token.sign!(request, { nonce: @nonce, timestamp: @timestamp }) - # assert_equal "",request.oauth_helper.signature_base_string - - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_equal "key=value", request.body - assert_equal "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"26g7wHTtNO6ZWJaLltcueppHYiI%3D\", oauth_version=\"1.0\"".delete(",").split.sort, - request["authorization"].delete(",").split.sort - end - - def test_that_signing_post_params_works - request = Net::HTTP::Post.new(@request_uri.path) - request.set_form_data(@request_parameters) - @token.sign!(request, { scheme: "body", nonce: @nonce, timestamp: @timestamp }) - - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_match( - /key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=26g7wHTtNO6ZWJaLltcueppHYiI%3[Dd]&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0/, request.body.split("&").sort.join("&") - ) - assert_nil request["authorization"] - end - - def test_that_using_auth_headers_on_get_on_create_signed_requests_works - request = @consumer.create_signed_request(:get, "#{@request_uri.path}?#{request_parameters_to_s}", @token, - { nonce: @nonce, timestamp: @timestamp }, @request_parameters) - - assert_equal "GET", request.method - assert_equal "/test?key=value", request.path - assert_equal "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"1oO2izFav1GP4kEH2EskwXkCRFg%3D\", oauth_version=\"1.0\"".delete(",").split.sort, - request["authorization"].delete(",").split.sort - end - - def test_using_auth_headers_on_post_works - request = @consumer.create_signed_request(:post, @request_uri.path, @token, - { nonce: @nonce, timestamp: @timestamp }, @request_parameters, {}) - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_equal "key=value", request.body - assert_equal "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"26g7wHTtNO6ZWJaLltcueppHYiI%3D\", oauth_version=\"1.0\"".delete(",").split.sort, - request["authorization"].delete(",").split.sort - end - - def test_signing_post_params_works2 - request = @consumer.create_signed_request(:post, @request_uri.path, @token, - { scheme: "body", nonce: @nonce, timestamp: @timestamp }, @request_parameters, {}) - - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_match( - /key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=26g7wHTtNO6ZWJaLltcueppHYiI%3[Dd]&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0/, request.body.split("&").sort.join("&") - ) - assert_nil request["authorization"] - end - - def test_step_by_step_token_request - stub_test_ie - - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://term.ie", - request_token_path: "/oauth/example/request_token.php", - access_token_path: "/oauth/example/access_token.php", - authorize_path: "/oauth/example/authorize.php", - scheme: :header - } - ) - options = { nonce: "nonce", timestamp: Time.now.to_i.to_s } - - request = Net::HTTP::Get.new("/oauth/example/request_token.php") - signature_base_string = @consumer.signature_base_string(request, nil, options) - assert_equal "GET&http%3A%2F%2Fterm.ie%2Foauth%2Fexample%2Frequest_token.php&oauth_consumer_key%3Dkey%26oauth_nonce%3D#{options[:nonce]}%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D#{options[:timestamp]}%26oauth_version%3D1.0", - signature_base_string - @consumer.sign!(request, nil, options) - - assert_equal "GET", request.method - assert_nil request.body - response = @consumer.http.request(request) - assert_equal "200", response.code - assert_equal "oauth_token=requestkey&oauth_token_secret=requestsecret", response.body - end - - def test_get_token_sequence - stub_test_ie - - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://term.ie", - request_token_path: "/oauth/example/request_token.php", - access_token_path: "/oauth/example/access_token.php", - authorize_path: "/oauth/example/authorize.php" - } - ) - assert_equal "http://term.ie/oauth/example/request_token.php", @consumer.request_token_url - assert_equal "http://term.ie/oauth/example/access_token.php", @consumer.access_token_url - - refute_predicate @consumer, :request_token_url?, "Should not use fully qualified request token url" - refute_predicate @consumer, :access_token_url?, "Should not use fully qualified access token url" - refute_predicate @consumer, :authorize_url?, "Should not use fully qualified url" - - @request_token = @consumer.get_request_token - assert @request_token - assert_equal "requestkey", @request_token.token - assert_equal "requestsecret", @request_token.secret - assert_equal "http://term.ie/oauth/example/authorize.php?oauth_token=requestkey", @request_token.authorize_url - - @access_token = @request_token.get_access_token - assert @access_token - assert_equal "accesskey", @access_token.token - assert_equal "accesssecret", @access_token.secret - - @response = @access_token.get("/oauth/example/echo_api.php?ok=hello&test=this") - assert @response - assert_equal "200", @response.code - assert_equal("ok=hello&test=this", @response.body) - - @response = @access_token.post("/oauth/example/echo_api.php", { "ok" => "hello", "test" => "this" }) - assert @response - assert_equal "200", @response.code - assert_equal("ok=hello&test=this", @response.body) - end - - def test_get_token_sequence_using_fqdn - stub_test_ie - - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://term.ie", - request_token_url: "http://term.ie/oauth/example/request_token.php", - access_token_url: "http://term.ie/oauth/example/access_token.php", - authorize_url: "http://term.ie/oauth/example/authorize.php" - } - ) - assert_equal "http://term.ie/oauth/example/request_token.php", @consumer.request_token_url - assert_equal "http://term.ie/oauth/example/access_token.php", @consumer.access_token_url - - assert_predicate @consumer, :request_token_url?, "Should use fully qualified request token url" - assert_predicate @consumer, :access_token_url?, "Should use fully qualified access token url" - assert_predicate @consumer, :authorize_url?, "Should use fully qualified url" - - @request_token = @consumer.get_request_token - assert @request_token - assert_equal "requestkey", @request_token.token - assert_equal "requestsecret", @request_token.secret - assert_equal "http://term.ie/oauth/example/authorize.php?oauth_token=requestkey", @request_token.authorize_url - - @access_token = @request_token.get_access_token - assert @access_token - assert_equal "accesskey", @access_token.token - assert_equal "accesssecret", @access_token.secret - - @response = @access_token.get("/oauth/example/echo_api.php?ok=hello&test=this") - assert @response - assert_equal "200", @response.code - assert_equal("ok=hello&test=this", @response.body) - - @response = @access_token.post("/oauth/example/echo_api.php", { "ok" => "hello", "test" => "this" }) - assert @response - assert_equal "200", @response.code - assert_equal("ok=hello&test=this", @response.body) - end - - # This test does an actual https request (the result doesn't matter) - # to initialize the same way as get_request_token does. Can be any - # site that supports https. - # - # It also generates "warning: using default DH parameters." which I - # don't know how to get rid of - # def test_serialization_with_https - # consumer = OAuth::Consumer.new('token', 'secret', :site => 'https://plazes.net') - # consumer.http.verify_mode = OpenSSL::SSL::VERIFY_NONE - # consumer.http.get('/') - # - # assert_nothing_raised do - # # Specifically this should not raise TypeError: no marshal_dump - # # is defined for class OpenSSL::SSL::SSLContext - # Marshal.dump(consumer) - # end - # end - # - def test_get_request_token_with_custom_arguments - stub_test_ie - - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://term.ie", - request_token_path: "/oauth/example/request_token.php", - access_token_path: "/oauth/example/access_token.php", - authorize_path: "/oauth/example/authorize.php" - } - ) - - @consumer.get_request_token({}, - { scope: "http://www.google.com/calendar/feeds http://picasaweb.google.com/data" }) - - # Because this is a POST request, create_http_request should take the first element of *arguments - # and turn it into URL-encoded data in the body of the POST. - end - - def test_post_with_body_stream - stub_test_ie - - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://term.ie", - request_token_path: "/oauth/example/request_token.php", - access_token_path: "/oauth/example/access_token.php", - authorize_path: "/oauth/example/authorize.php" - } - ) - - @request_token = @consumer.get_request_token - @access_token = @request_token.get_access_token - - request_body_string = "Hello, hello, hello" - request_body_stream = StringIO.new(request_body_string) - - @response = @access_token.post("/oauth/example/echo_api.php", request_body_stream) - assert @response - assert_equal "200", @response.code - - request_body_file = File.open(__FILE__) - - @response = @access_token.post("/oauth/example/echo_api.php", request_body_file) - assert @response - assert_equal "200", @response.code - - # unfortunately I don't know of a way to test that the body data was received correctly since the test server at http://term.ie - # echos back any non-oauth parameters but not the body. However, this does test that the request is still correctly signed - # (including the Content-Length header) and that the server received Content-Length bytes of body since it won't process the - # request & respond until the full body length is received. - end - - private - - def request_parameters_to_s - @request_parameters.map { |k, v| "#{k}=#{v}" }.join("&") - end -end diff --git a/test/units/consumer_test.rb b/test/units/consumer_test.rb deleted file mode 100644 index cb93505d..00000000 --- a/test/units/consumer_test.rb +++ /dev/null @@ -1,365 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -# This performs testing against Andy Smith's test server http://term.ie/oauth/example/ -# Thanks Andy. -# This also means you have to be online to be able to run these. -class ConsumerTest < Minitest::Test - def setup - @consumer = OAuth::Consumer.new( - "consumer_key_86cad9", "5888bf0345e5d237", - { - site: "http://blabla.bla", - proxy: "http://user:password@proxy.bla:8080", - request_token_path: "/oauth/example/request_token.php", - access_token_path: "/oauth/example/access_token.php", - authorize_path: "/oauth/example/authorize.php", - scheme: :header, - http_method: :get - } - ) - @token = OAuth::ConsumerToken.new(@consumer, "token_411a7f", "3196ffd991c8ebdb") - @request_uri = URI.parse("http://example.com/test?key=value") - @request_parameters = { "key" => "value" } - @nonce = 225_579_211_881_198_842_005_988_698_334_675_835_446 - @timestamp = "1199645624" - @consumer.http = Net::HTTP.new(@request_uri.host, @request_uri.port) - end - - def test_initializer - assert_equal "consumer_key_86cad9", @consumer.key - assert_equal "5888bf0345e5d237", @consumer.secret - assert_equal "http://blabla.bla", @consumer.site - assert_equal "http://user:password@proxy.bla:8080", @consumer.proxy - assert_equal "/oauth/example/request_token.php", @consumer.request_token_path - assert_equal "/oauth/example/access_token.php", @consumer.access_token_path - assert_equal "http://blabla.bla/oauth/example/request_token.php", @consumer.request_token_url - assert_equal "http://blabla.bla/oauth/example/access_token.php", @consumer.access_token_url - assert_equal "http://blabla.bla/oauth/example/authorize.php", @consumer.authorize_url - assert_equal :header, @consumer.scheme - assert_equal :get, @consumer.http_method - assert_nil @consumer.debug_output - end - - def test_defaults - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://twitter.com" - } - ) - assert_equal "key", @consumer.key - assert_equal "secret", @consumer.secret - assert_equal "http://twitter.com", @consumer.site - assert_nil @consumer.proxy - assert_equal "/oauth/request_token", @consumer.request_token_path - assert_equal "/oauth/access_token", @consumer.access_token_path - assert_equal "http://twitter.com/oauth/request_token", @consumer.request_token_url - assert_equal "http://twitter.com/oauth/access_token", @consumer.access_token_url - assert_equal "http://twitter.com/oauth/authorize", @consumer.authorize_url - assert_equal :header, @consumer.scheme - assert_equal :post, @consumer.http_method - assert_nil @consumer.debug_output - end - - def test_debug_output_true - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - debug_output: true - } - ) - assert_equal $stdout, @consumer.debug_output - end - - def test_debug_output - stringio = StringIO.new - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - debug_output: stringio - } - ) - assert_equal stringio, @consumer.debug_output - end - - def test_site_without_path - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://twitter.com" - } - ) - request = stub(oauth!: nil) - http = stub(request: stub(to_hash: {}), address: "identi.ca") - Net::HTTP::Get.expects(:new).with("/people", {}).returns(request) - @consumer.expects(:create_http).returns(http) - @consumer.request(:get, "/people", nil, {}) - end - - def test_site_with_path - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://identi.ca/api" - } - ) - request = stub(oauth!: nil) - http = stub(request: stub(to_hash: {}), address: "identi.ca") - Net::HTTP::Get.expects(:new).with("/api/people", {}).returns(request) - @consumer.expects(:create_http).returns(http) - @consumer.request(:get, "/people", nil, {}) - end - - def test_post_of_nested_params_maintains_nesting - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://twitter.com" - } - ) - request = @consumer.create_signed_request( - :post, - "/people", - nil, - {}, - { - key: { - subkey: "value" - } - } - ) - assert_equal "key%5Bsubkey%5D=value", request.body - assert_equal("application/x-www-form-urlencoded", request.content_type) - end - - def test_override_paths - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "http://twitter.com", - request_token_url: "http://oauth.twitter.com/request_token", - access_token_url: "http://oauth.twitter.com/access_token", - authorize_url: "http://site.twitter.com/authorize" - } - ) - assert_equal "key", @consumer.key - assert_equal "secret", @consumer.secret - assert_equal "http://twitter.com", @consumer.site - assert_equal "/oauth/request_token", @consumer.request_token_path - assert_equal "/oauth/access_token", @consumer.access_token_path - assert_equal "http://oauth.twitter.com/request_token", @consumer.request_token_url - assert_equal "http://oauth.twitter.com/access_token", @consumer.access_token_url - assert_equal "http://site.twitter.com/authorize", @consumer.authorize_url - assert_equal :header, @consumer.scheme - assert_equal :post, @consumer.http_method - end - - def test_getting_tokens_doesnt_add_paths_if_full_url_is_specified - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "https://api.mysite.co.nz/v1", - request_token_url: "https://authentication.mysite.co.nz/Oauth/RequestToken" - } - ) - - stub_request(:post, "https://authentication.mysite.co.nz/Oauth/RequestToken").to_return(body: "success", - status: 200) - @consumer.get_request_token - end - - def test_noverify_true - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "https://api.mysite.co.nz/v1", - request_token_url: "https://authentication.mysite.co.nz/Oauth/RequestToken", - no_verify: true - } - ) - - stub_request(:post, "https://authentication.mysite.co.nz/Oauth/RequestToken").to_return(body: "success", - status: 200) - - Net::HTTP.any_instance.expects(:"verify_mode=").with(OpenSSL::SSL::VERIFY_NONE) - - @consumer.get_request_token - end - - def test_noverify_false - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "https://api.mysite.co.nz/v1", - request_token_url: "https://authentication.mysite.co.nz/Oauth/RequestToken", - no_verify: false - } - ) - - stub_request(:post, "https://authentication.mysite.co.nz/Oauth/RequestToken").to_return(body: "success", - status: 200) - - Net::HTTP.any_instance.expects(:"verify_mode=").with(OpenSSL::SSL::VERIFY_PEER) - @consumer.get_request_token - end - - def test_noverify_empty - @consumer = OAuth::Consumer.new( - "key", - "secret", - { - site: "https://api.mysite.co.nz/v1", - request_token_url: "https://authentication.mysite.co.nz/Oauth/RequestToken" - } - ) - - stub_request(:post, "https://authentication.mysite.co.nz/Oauth/RequestToken").to_return(body: "success", - status: 200) - - Net::HTTP.any_instance.expects(:"verify_mode=").with(OpenSSL::SSL::VERIFY_PEER) - @consumer.get_request_token - end - - def test_token_request_identifies_itself_as_a_token_request - request_options = {} - @consumer.stubs(:request).returns(create_stub_http_response) - @consumer.token_request(:post, "/", "token", request_options) { nil } - assert request_options[:token_request] - end - - def test_that_token_response_should_be_uri_parameter_format_as_default - @consumer.expects(:request).returns(create_stub_http_response("oauth_token=token&oauth_token_secret=secret")) - - hash = @consumer.token_request(:get, "") - - assert_equal "token", hash[:oauth_token] - assert_equal "secret", hash[:oauth_token_secret] - end - - def test_can_provided_a_block_to_interpret_token_response - @consumer.expects(:request).returns(create_stub_http_response) - - hash = @consumer.token_request(:get, "") { { oauth_token: "token", oauth_token_secret: "secret" } } - - assert_equal "token", hash[:oauth_token] - assert_equal "secret", hash[:oauth_token_secret] - end - - def test_token_request_follows_redirect - redirect_url = @request_uri.clone - redirect_url.path = "/oauth/example/request_token_redirect.php" - stub_request(:get, /.*#{@request_uri.path}/).to_return(status: 301, - headers: { "Location" => redirect_url.to_s }) - stub_request(:get, /.*#{redirect_url.path}/).to_return(body: "oauth_token=token&oauth_token_secret=secret") - - hash = @consumer.token_request(:get, @request_uri.path) do - { oauth_token: "token", oauth_token_secret: "secret" } - end - - assert_equal "token", hash[:oauth_token] - assert_equal "secret", hash[:oauth_token_secret] - end - - def test_follow_redirect_different_host_same_path - request_uri = URI.parse("https://example.com/request_token") - redirect_uri = URI.parse("https://foobar.com/request_token") - - stub_request(:get, "http://example.com/request_token").to_return(status: 301, - headers: { "Location" => redirect_uri.to_s }) - stub_request(:get, - "https://foobar.com/request_token").to_return(body: "oauth_token=token&oauth_token_secret=secret") - - hash = @consumer.token_request(:get, request_uri.path) do - { oauth_token: "token", oauth_token_secret: "secret" } - end - - assert_equal "token", hash[:oauth_token] - assert_equal "secret", hash[:oauth_token_secret] - end - - def test_not_following_redirect_with_same_uri - request_uri = URI.parse("http://example.com/request_token") - redirect_uri = request_uri.clone - - stub_request(:get, request_uri.to_s).to_return( - status: 301, - headers: { "Location" => redirect_uri.to_s } - ) - - assert_raises Net::HTTPRetriableError do - @consumer.token_request(:get, request_uri.path) do - { oauth_token: "token", oauth_token_secret: "secret" } - end - end - end - - def test_that_can_provide_a_block_to_interpret_a_request_token_response - @consumer.expects(:request).returns(create_stub_http_response) - - token = @consumer.get_request_token { { oauth_token: "token", oauth_token_secret: "secret" } } - - assert_equal "token", token.token - assert_equal "secret", token.secret - end - - def test_that_block_is_not_mandatory_for_getting_an_access_token - stub_token = mock - @consumer.expects(:request).returns(create_stub_http_response("oauth_token=token&oauth_token_secret=secret")) - - token = @consumer.get_access_token(stub_token) - - assert_equal "token", token.token - assert_equal "secret", token.secret - end - - def test_that_can_provide_a_block_to_interpret_an_access_token_response - stub_token = mock - @consumer.expects(:request).returns(create_stub_http_response) - - token = @consumer.get_access_token(stub_token) { { oauth_token: "token", oauth_token_secret: "secret" } } - - assert_equal "token", token.token - assert_equal "secret", token.secret - end - - def test_that_not_setting_ignore_callback_will_include_oauth_callback_in_request_options - request_options = {} - @consumer.stubs(:request).returns(create_stub_http_response) - - @consumer.get_request_token(request_options) { { oauth_token: "token", oauth_token_secret: "secret" } } - - assert_equal "oob", request_options[:oauth_callback] - end - - def test_that_setting_ignore_callback_will_exclude_oauth_callback_in_request_options - request_options = { exclude_callback: true } - @consumer.stubs(:request).returns(create_stub_http_response) - - @consumer.get_request_token(request_options) { { oauth_token: "token", oauth_token_secret: "secret" } } - - assert_nil request_options[:oauth_callback] - end - - private - - def create_stub_http_response(expected_body = nil) - stub_http_response = stub - stub_http_response.stubs(:code).returns(200) - stub_http_response.stubs(:body).tap do |expectation| - expectation.returns(expected_body) unless expected_body.nil? - end - stub_http_response - end -end diff --git a/test/units/curb_request_proxy_test.rb b/test/units/curb_request_proxy_test.rb deleted file mode 100644 index ed38b140..00000000 --- a/test/units/curb_request_proxy_test.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -begin - require "oauth/request_proxy/curb_request" - require "curb" - - class CurbRequestProxyTest < Minitest::Test - def test_that_proxy_simple_get_request_works - request = Curl::Easy.new("/test?key=value") - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test?key=value" }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - end - - def test_that_proxy_simple_post_request_works_with_arguments - request = Curl::Easy.new("/test") - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - end - - def test_that_proxy_simple_post_request_works_with_form_data - request = Curl::Easy.new("/test") - request.post_body = "key=value" - request.headers["Content-Type"] = "application/x-www-form-urlencoded" - - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - end - - def test_that_proxy_simple_put_request_works_with_arguments - request = Curl::Easy.new("/test") - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - end - - def test_that_proxy_simple_put_request_works_with_form_data - request = Curl::Easy.new("/test") - request.post_body = "key=value" - - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - expected_parameters = {} - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - end - - def test_that_proxy_post_request_works_with_mixed_parameter_sources - request = Curl::Easy.new("/test?key=value") - request.post_body = "key2=value2" - request.headers["Content-Type"] = "application/x-www-form-urlencoded" - request_proxy = OAuth::RequestProxy.proxy(request, - { uri: "http://example.com/test?key=value", - parameters: { "key3" => "value3" } }) - - expected_parameters = { "key" => ["value"], "key2" => "value2", "key3" => "value3" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - end - end -rescue LoadError => e - warn "! problems loading curb, skipping these tests: #{e}" -end diff --git a/test/units/em_http_client_test.rb b/test/units/em_http_client_test.rb deleted file mode 100644 index 559e550b..00000000 --- a/test/units/em_http_client_test.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" -begin - require "oauth/client/em_http" - - class EmHttpClientTest < Minitest::Test - def setup - @consumer = OAuth::Consumer.new("consumer_key_86cad9", "5888bf0345e5d237") - @token = OAuth::Token.new("token_411a7f", "3196ffd991c8ebdb") - @request_uri = URI.parse("http://example.com/test?key=value") - @request_parameters = { "key" => "value" } - @nonce = 225_579_211_881_198_842_005_988_698_334_675_835_446 - @timestamp = "1199645624" - # This is really unneeded I guess. - @http = Net::HTTP.new(@request_uri.host, @request_uri.port) - end - - def test_that_using_auth_headers_on_get_requests_works - request = create_client - request.oauth!(@http, @consumer, @token, { nonce: @nonce, timestamp: @timestamp }) - - assert_equal "GET", request.req[:method] - assert_equal "/test", request.normalize_uri.path - assert_equal "key=value", request.normalize_uri.query - correct_headers = "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"1oO2izFav1GP4kEH2EskwXkCRFg%3D\", oauth_version=\"1.0\"" - generated_headers = authz_header(request) - assert_equal_authz_headers correct_headers, generated_headers - end - - def test_that_using_auth_headers_on_get_requests_works_with_plaintext - c = OAuth::Consumer.new("consumer_key_86cad9", "5888bf0345e5d237", { - signature_method: "PLAINTEXT" - }) - request = create_client - request.oauth!(@http, c, @token, { nonce: @nonce, timestamp: @timestamp, signature_method: "PLAINTEXT" }) - - assert_equal "GET", request.req[:method] - assert_equal "/test", request.conn.path - assert_equal "key=value", request.conn.query - assert_equal_authz_headers "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"PLAINTEXT\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"5888bf0345e5d237%263196ffd991c8ebdb\", oauth_version=\"1.0\"", - authz_header(request) - end - - def test_that_using_auth_headers_on_post_requests_works - request = create_client(uri: "http://example.com/test", method: "POST", body: @request_parameters, - head: { "Content-Type" => "application/x-www-form-urlencoded" }) - request.oauth!(@http, @consumer, @token, { nonce: @nonce, timestamp: @timestamp }) - - assert_equal "POST", request.req[:method] - assert_equal "/test", request.conn.path - assert_equal_authz_headers "OAuth oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"26g7wHTtNO6ZWJaLltcueppHYiI%3D\", oauth_version=\"1.0\"", - authz_header(request) - assert_equal "key=value", request.normalize_body(request.req[:body]) - end - - protected - - def create_client(options = {}) - options[:method] = options.key?(:method) ? options[:method].upcase : "GET" - uri = options.delete(:uri) || @request_uri.to_s - EventMachine::HttpClient.new(URI.parse(uri), options) - end - - def authz_header(request) - request.req[:head]["Authorization"] - end - - def assert_equal_authz_headers(expected, actual) - refute_nil actual - assert_equal expected[0, 6], actual[0, 6] - assert_equal expected[6..1].split(", ").sort, actual[6..1].split(", ").sort - end - end -rescue LoadError => e - warn "! problem loading em-http, skipping these tests: #{e}" -end diff --git a/test/units/em_http_request_proxy_test.rb b/test/units/em_http_request_proxy_test.rb deleted file mode 100644 index 8c5db47d..00000000 --- a/test/units/em_http_request_proxy_test.rb +++ /dev/null @@ -1,118 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -begin - require "em-http" - require "oauth/request_proxy/em_http_request" - - class EmHttpRequestProxyTest < Minitest::Test - def test_request_proxy_works_with_simple_request - proxy = create_request_proxy - assert_empty(proxy.parameters) - end - - def test_request_proxy_works_with_query_string_params - assert_equal({ "name" => ["Fred"] }, create_request_proxy(query: "name=Fred").parameters) - assert_equal({ "name" => ["Fred"] }, create_request_proxy(query: { name: "Fred" }).parameters) - proxy = create_request_proxy(query: { name: "Fred" }, uri: "http://example.com/?awesome=true") - assert_equal({ "name" => ["Fred"], "awesome" => ["true"] }, proxy.parameters) - end - - def test_request_proxy_works_with_post_body_params_with_correct_content_type - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "POST" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "POST", - body: "a=1" - assert_equal({ "a" => ["1"] }, proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "POST", - body: { "a" => 1 } - assert_equal({ "a" => ["1"] }, proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "PUT" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "PUT", - body: "a=1" - assert_equal({ "a" => ["1"] }, proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "PUT", - body: { "a" => 1 } - assert_equal({ "a" => ["1"] }, proxy.parameters) - end - - def test_request_proxy_ignore_post_body_with_invalid_content_type - proxy = create_request_proxy head: { "Content-Type" => "text/plain" }, method: "POST" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "text/plain" }, method: "POST", body: "a=1" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "text/plain" }, method: "POST", body: { "a" => 1 } - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "text/plain" }, method: "PUT" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "text/plain" }, method: "PUT", body: "a=1" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "text/plain" }, method: "PUT", body: { "a" => 1 } - assert_empty(proxy.parameters) - end - - def test_request_proxy_ignores_post_body_with_invalid_method - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, - method: "DELETE" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "DELETE", - body: "a=1" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "DELETE", - body: { "a" => 1 } - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "GET" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "GET", - body: "a=1" - assert_empty(proxy.parameters) - proxy = create_request_proxy head: { "Content-Type" => "application/x-www-form-urlencoded" }, method: "GET", - body: { "a" => 1 } - assert_empty(proxy.parameters) - end - - def test_request_proxy_works_with_argument_params - assert_equal({ "a" => ["1"] }, - create_request_proxy(proxy_options: { parameters: { "a" => "1" } }).parameters) - end - - def test_request_proxy_works_with_mixed_params - proxy = create_request_proxy(proxy_options: { parameters: { "a" => "1" } }, query: { "c" => "1" }, - uri: "http://example.com/test?b=1") - assert_equal({ "a" => ["1"], "b" => ["1"], "c" => ["1"] }, proxy.parameters) - proxy = create_request_proxy(proxy_options: { parameters: { "a" => "1" } }, body: { "b" => "1" }, query: { "c" => "1" }, - uri: "http://example.com/test?d=1", method: "POST", head: { "Content-Type" => "application/x-www-form-urlencoded" }) - assert_equal({ "a" => ["1"], "b" => ["1"], "c" => ["1"], "d" => ["1"] }, proxy.parameters) - end - - def test_request_has_the_correct_uri - assert_equal "http://example.com/", create_request_proxy.uri - assert_equal "http://example.com/?a=1", create_request_proxy(query: "a=1").request.normalize_uri.to_s - assert_equal "http://example.com/?a=1", create_request_proxy(query: { "a" => "1" }).request.normalize_uri.to_s - end - - def test_request_proxy_has_correct_method - assert_equal "GET", create_request_proxy(method: "GET").request.req[:method] - assert_equal "PUT", create_request_proxy(method: "PUT").request.req[:method] - assert_equal "POST", create_request_proxy(method: "POST").request.req[:method] - assert_equal "DELETE", create_request_proxy(method: "DELETE").request.req[:method] - end - - protected - - def create_client(options = {}) - options[:method] = options.key?(:method) ? options[:method].upcase : "GET" - uri = options.delete(:uri) || "http://example.com/" - EventMachine::HttpClient.new(URI.parse(uri), options) - end - - def create_request_proxy(opts = {}) - arguments = opts.delete(:proxy_options) || {} - OAuth::RequestProxy.proxy(create_client(opts), arguments) - end - end -rescue LoadError => e - warn "! problem loading em-http, skipping these tests: #{e}" -end diff --git a/test/units/hmac_sha1_test.rb b/test/units/hmac_sha1_test.rb deleted file mode 100644 index e1cdfc13..00000000 --- a/test/units/hmac_sha1_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class TestSignatureHmacSha1 < Minitest::Test - def test_that_hmac_sha1_implements_hmac_sha1 - assert_includes OAuth::Signature.available_methods, "hmac-sha1" - end - - def test_that_get_request_from_oauth_test_cases_produces_matching_signature - request = Net::HTTP::Get.new("/photos?file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1") - - consumer = OAuth::Consumer.new("dpf43f3p2l4k3l03", "kd94hf93k423kf44") - token = OAuth::Token.new("nnch734d00sl2jdk", "pfkkdhi9sl3r4s00") - - signature = OAuth::Signature.sign(request, { consumer: consumer, - token: token, - uri: "http://photos.example.net/photos" }) - - assert_equal "tR3+Ty81lMeYAr/Fid0kMTYa/WM=", signature - end -end diff --git a/test/units/hmac_sha256_test.rb b/test/units/hmac_sha256_test.rb deleted file mode 100644 index b6cb1416..00000000 --- a/test/units/hmac_sha256_test.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class TestSignatureHmacSha256 < Minitest::Test - def test_that_hmac_sha256_implements_hmac_sha256 - assert_includes OAuth::Signature.available_methods, "hmac-sha256" - end - - def test_that_get_request_from_oauth_test_cases_produces_matching_signature - request = Net::HTTP::Get.new("/photos?file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA256") - - consumer = OAuth::Consumer.new("dpf43f3p2l4k3l03", "kd94hf93k423kf44") - token = OAuth::Token.new("nnch734d00sl2jdk", "pfkkdhi9sl3r4s00") - - signature = OAuth::Signature.sign(request, { consumer: consumer, - token: token, - uri: "http://photos.example.net/photos", - signature_method: "HMAC-SHA256" }) - - assert_equal "WVPzl1j6ZsnkIjWr7e3OZ3jkenL57KwaLFhYsroX1hg=", signature - end -end diff --git a/test/units/net_http_client_test.rb b/test/units/net_http_client_test.rb deleted file mode 100644 index 78469acf..00000000 --- a/test/units/net_http_client_test.rb +++ /dev/null @@ -1,340 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class NetHTTPClientTest < Minitest::Test - def setup - @consumer = OAuth::Consumer.new("consumer_key_86cad9", "5888bf0345e5d237") - @token = OAuth::Token.new("token_411a7f", "3196ffd991c8ebdb") - @request_uri = URI.parse("http://example.com/test?key=value") - @request_parameters = { "key" => "value" } - @nonce = 225_579_211_881_198_842_005_988_698_334_675_835_446 - @timestamp = "1199645624" - @http = Net::HTTP.new(@request_uri.host, @request_uri.port) - end - - def test_that_using_auth_headers_on_get_requests_works - request = Net::HTTP::Get.new("#{@request_uri.path}?#{request_parameters_to_s}") - request.oauth!(@http, @consumer, @token, { nonce: @nonce, timestamp: @timestamp }) - - assert_equal "GET", request.method - assert_equal "/test?key=value", request.path - correct_sorted_params = "oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"1oO2izFav1GP4kEH2EskwXkCRFg%3D\", oauth_version=\"1.0\"" - auth_intro = request["authorization"].split(" ", 2).first - assert_equal("OAuth", auth_intro) - assert_matching_headers correct_sorted_params, request["authorization"] - end - - def test_that_using_auth_headers_on_get_requests_works_with_plaintext - c = OAuth::Consumer.new("consumer_key_86cad9", "5888bf0345e5d237", { - signature_method: "PLAINTEXT" - }) - request = Net::HTTP::Get.new("#{@request_uri.path}?#{request_parameters_to_s}") - request.oauth!(@http, c, @token, { nonce: @nonce, timestamp: @timestamp, signature_method: "PLAINTEXT" }) - - assert_equal "GET", request.method - assert_equal "/test?key=value", request.path - assert_matching_headers "oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"PLAINTEXT\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"5888bf0345e5d237%263196ffd991c8ebdb\", oauth_version=\"1.0\"", - request["authorization"] - end - - def test_that_using_auth_headers_on_post_requests_works - request = Net::HTTP::Post.new(@request_uri.path) - request.set_form_data(@request_parameters) - request.oauth!(@http, @consumer, @token, { nonce: @nonce, timestamp: @timestamp }) - - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_equal "key=value", request.body - correct_sorted_params = "oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"26g7wHTtNO6ZWJaLltcueppHYiI%3D\", oauth_version=\"1.0\"" - assert_matching_headers correct_sorted_params, request["authorization"] - end - - def test_that_using_auth_headers_on_post_requests_with_data_works - request = Net::HTTP::Post.new(@request_uri.path) - request.body = "data" - request.content_type = "text/ascii" - request.oauth!(@http, @consumer, @token, { nonce: @nonce, timestamp: @timestamp }) - - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_equal "data", request.body - assert_equal "text/ascii", request.content_type - assert_matching_headers "oauth_nonce=\"225579211881198842005988698334675835446\", oauth_body_hash=\"oXyaqmHoChv3HQ2FCvTluqmAC70%3D\", oauth_signature_method=\"HMAC-SHA1\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"0DA6pGTapdHSqC15RZelY5rNLDw%3D\", oauth_version=\"1.0\"", - request["authorization"] - end - - def test_that_body_hash_is_obmitted_when_token_request - request = Net::HTTP::Post.new(@request_uri.path) - request.oauth!(@http, @consumer, @token, { nonce: @nonce, timestamp: @timestamp, token_request: true }) - assert_no_match(/oauth_body_hash/, request["authorization"]) - end - - def test_that_body_hash_is_obmitted_when_no_algorithm_is_defined - request = Net::HTTP::Post.new(@request_uri.path) - request.body = "data" - request.content_type = "text/ascii" - request.oauth!(@http, @consumer, @token, - { nonce: @nonce, timestamp: @timestamp, signature_method: "plaintext" }) - - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_equal "data", request.body - assert_equal "text/ascii", request.content_type - assert_matching_headers "oauth_nonce=\"225579211881198842005988698334675835446\", oauth_signature_method=\"plaintext\", oauth_token=\"token_411a7f\", oauth_timestamp=\"1199645624\", oauth_consumer_key=\"consumer_key_86cad9\", oauth_signature=\"5888bf0345e5d237%263196ffd991c8ebdb\", oauth_version=\"1.0\"", - request["authorization"] - end - - def test_that_version_is_added_to_existing_user_agent - request = Net::HTTP::Post.new(@request_uri.path) - request["User-Agent"] = "MyApp" - request.set_form_data(@request_parameters) - request.oauth!(@http, @consumer, @token, { nonce: @nonce, timestamp: @timestamp }) - - assert_equal "MyApp (OAuth gem v#{OAuth::Version::VERSION})", request["User-Agent"] - end - - def test_that_version_is_set_when_no_user_agent - request = Net::HTTP::Post.new(@request_uri.path) - request.set_form_data(@request_parameters) - request.oauth!(@http, @consumer, @token, { nonce: @nonce, timestamp: @timestamp }) - - assert_equal "OAuth gem v#{OAuth::Version::VERSION}", request["User-Agent"] - end - - def test_that_using_get_params_works - request = Net::HTTP::Get.new("#{@request_uri.path}?#{request_parameters_to_s}") - request.oauth!(@http, @consumer, @token, { scheme: "query_string", nonce: @nonce, timestamp: @timestamp }) - - assert_equal "GET", request.method - uri = URI.parse(request.path) - assert_equal "/test", uri.path - assert_nil uri.fragment - assert_equal "key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=1oO2izFav1GP4kEH2EskwXkCRFg%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0", - uri.query.split("&").sort.join("&") - assert_nil request["authorization"] - end - - def test_that_using_get_params_works_with_plaintext - request = Net::HTTP::Get.new("#{@request_uri.path}?#{request_parameters_to_s}") - request.oauth!(@http, @consumer, @token, - { scheme: "query_string", nonce: @nonce, timestamp: @timestamp, signature_method: "PLAINTEXT" }) - - assert_equal "GET", request.method - uri = URI.parse(request.path) - assert_equal "/test", uri.path - assert_nil uri.fragment - assert_equal "key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=5888bf0345e5d237%263196ffd991c8ebdb&oauth_signature_method=PLAINTEXT&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0", - uri.query.split("&").sort.join("&") - assert_nil request["authorization"] - end - - def test_that_using_post_params_works - request = Net::HTTP::Post.new(@request_uri.path) - request.set_form_data(@request_parameters) - request.oauth!(@http, @consumer, @token, { scheme: "body", nonce: @nonce, timestamp: @timestamp }) - - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_match( - /key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=26g7wHTtNO6ZWJaLltcueppHYiI%3[Dd]&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0/, request.body.split("&").sort.join("&") - ) - assert_nil request["authorization"] - end - - def test_that_using_post_params_works_with_plaintext - request = Net::HTTP::Post.new(@request_uri.path) - request.set_form_data(@request_parameters) - request.oauth!(@http, @consumer, @token, - { scheme: "body", nonce: @nonce, timestamp: @timestamp, signature_method: "PLAINTEXT" }) - - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_equal "key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=5888bf0345e5d237%263196ffd991c8ebdb&oauth_signature_method=PLAINTEXT&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0", - request.body.split("&").sort.join("&") - assert_nil request["authorization"] - end - - def test_that_using_post_body_works - request = Net::HTTP::Post.new(@request_uri.path) - request["content-type"] = "application/x-www-form-urlencoded" - request.body = "this is a test of the emergency broad cast system. This is only a test." - request.oauth!(@http, @consumer, @token, { nonce: @nonce, timestamp: @timestamp }) - - assert_equal "POST", request.method - assert_equal "/test", request.path - assert_match( - /OAuth oauth_consumer_key="consumer_key_86cad9", oauth_nonce="225579211881198842005988698334675835446", oauth_signature="%2[fF]DMMBOJzQ6JmEaXlAXDLGtD1z2I%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1199645624", oauth_token="token_411a7f", oauth_version="1.0"/, request["authorization"].split("&").sort.join("&") - ) - # assert_nil request['authorization'] - end - - def test_that_using_post_with_uri_params_works - request = Net::HTTP::Post.new("#{@request_uri.path}?#{request_parameters_to_s}") - request.set_form_data({}) # just to make sure we have a correct mime type and thus no body hash - request.oauth!(@http, @consumer, @token, { scheme: "query_string", nonce: @nonce, timestamp: @timestamp }) - - assert_equal "POST", request.method - uri = URI.parse(request.path) - assert_equal "/test", uri.path - assert_nil uri.fragment - assert_equal "key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=26g7wHTtNO6ZWJaLltcueppHYiI%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0", - uri.query.split("&").sort.join("&") - assert_equal "", request.body - assert_nil request["authorization"] - end - - def test_that_using_post_with_uri_and_form_params_works - request = Net::HTTP::Post.new("#{@request_uri.path}?#{request_parameters_to_s}") - request.set_form_data({ "key2" => "value2" }) - request.oauth!(@http, @consumer, @token, { scheme: :query_string, nonce: @nonce, timestamp: @timestamp }) - - assert_equal "POST", request.method - uri = URI.parse(request.path) - assert_equal "/test", uri.path - assert_nil uri.fragment - assert_equal "key=value&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=4kSU8Zd1blWo3W6qJH7eaRTMkg0%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0", - uri.query.split("&").sort.join("&") - assert_equal "key2=value2", request.body - assert_nil request["authorization"] - end - - def test_that_using_post_with_uri_and_data_works - request = Net::HTTP::Post.new("#{@request_uri.path}?#{request_parameters_to_s}") - request.body = "data" - request.content_type = "text/ascii" - request.oauth!(@http, @consumer, @token, { scheme: :query_string, nonce: @nonce, timestamp: @timestamp }) - - assert_equal "POST", request.method - uri = URI.parse(request.path) - assert_equal "/test", uri.path - assert_nil uri.fragment - assert_equal "data", request.body - assert_equal "text/ascii", request.content_type - assert_equal "key=value&oauth_body_hash=oXyaqmHoChv3HQ2FCvTluqmAC70%3D&oauth_consumer_key=consumer_key_86cad9&oauth_nonce=225579211881198842005988698334675835446&oauth_signature=MHRKU42iVHU4Ke9kBUDa9Zw6IAM%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1199645624&oauth_token=token_411a7f&oauth_version=1.0", - uri.query.split("&").sort.join("&") - assert_nil request["authorization"] - end - - def test_example_from_specs - consumer = OAuth::Consumer.new("dpf43f3p2l4k3l03", "kd94hf93k423kf44") - token = OAuth::Token.new("nnch734d00sl2jdk", "pfkkdhi9sl3r4s00") - request_uri = URI.parse("http://photos.example.net/photos?file=vacation.jpg&size=original") - nonce = "kllo9940pd9333jh" - timestamp = "1191242096" - http = Net::HTTP.new(request_uri.host, request_uri.port) - - request = Net::HTTP::Get.new("#{request_uri.path}?#{request_uri.query}") - signature_base_string = request.signature_base_string(http, consumer, token, - { nonce: nonce, timestamp: timestamp }) - assert_equal "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal", - signature_base_string - - # request = Net::HTTP::Get.new(request_uri.path + "?" + request_uri.query) - request.oauth!(http, consumer, token, - { nonce: nonce, timestamp: timestamp, realm: "http://photos.example.net/" }) - - assert_equal "GET", request.method - correct_sorted_params = 'oauth_nonce="kllo9940pd9333jh", oauth_signature_method="HMAC-SHA1", oauth_token="nnch734d00sl2jdk", oauth_timestamp="1191242096", oauth_consumer_key="dpf43f3p2l4k3l03", oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", oauth_version="1.0"'.split(", ").sort - correct_sorted_params.unshift 'OAuth realm="http://photos.example.net/"' - assert_equal correct_sorted_params, request["authorization"].split(", ").sort - end - - def test_step_by_step_token_request - token_response = "oauth_token=requestkey&oauth_token_secret=requestsecret" - stub_request(:get, %r{http://term\.ie/oauth/example/request_token\.php(\?.*)?}).to_return(body: token_response) - - consumer = OAuth::Consumer.new( - "key", - "secret" - ) - request_uri = URI.parse("http://term.ie/oauth/example/request_token.php") - nonce = rand(2**128).to_s - timestamp = Time.now.to_i.to_s - http = Net::HTTP.new(request_uri.host, request_uri.port) - - request = Net::HTTP::Get.new(request_uri.path) - signature_base_string = request.signature_base_string(http, consumer, nil, - { scheme: :query_string, nonce: nonce, timestamp: timestamp }) - assert_equal "GET&http%3A%2F%2Fterm.ie%2Foauth%2Fexample%2Frequest_token.php&oauth_consumer_key%3Dkey%26oauth_nonce%3D#{nonce}%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D#{timestamp}%26oauth_version%3D1.0", - signature_base_string - - # request = Net::HTTP::Get.new(request_uri.path) - request.oauth!(http, consumer, nil, { scheme: :query_string, nonce: nonce, timestamp: timestamp }) - assert_equal "GET", request.method - assert_nil request.body - assert_nil request["authorization"] - # assert_equal 'OAuth oauth_nonce="kllo9940pd9333jh", oauth_signature_method="HMAC-SHA1", oauth_token="", oauth_timestamp="'+timestamp+'", oauth_consumer_key="key", oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", oauth_version="1.0"', request['authorization'] - - response = http.request(request) - assert_equal "200", response.code - # assert_equal request['authorization'],response.body - assert_equal token_response, response.body - end - - def test_that_put_bodies_signed - request = Net::HTTP::Put.new(@request_uri.path) - request.body = "baz" - request["Content-Type"] = "application/xml" - signature_base_string = request.signature_base_string(@http, @consumer, nil, - { nonce: @nonce, timestamp: @timestamp }) - assert_equal "PUT&http%3A%2F%2Fexample.com%2Ftest&oauth_body_hash%3DDvAa1AWdFoH9K%252B%252F2AHm3f6wH27k%253D%26oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0", - signature_base_string - end - - def test_that_put_bodies_not_signed_even_if_form_urlencoded - request = Net::HTTP::Put.new(@request_uri.path) - request.set_form_data({ "key2" => "value2" }) - signature_base_string = request.signature_base_string(@http, @consumer, nil, - { nonce: @nonce, timestamp: @timestamp }) - assert_equal "PUT&http%3A%2F%2Fexample.com%2Ftest&key2%3Dvalue2%26oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0", - signature_base_string - end - - def test_that_post_bodies_signed_if_form_urlencoded - request = Net::HTTP::Post.new(@request_uri.path) - request.set_form_data({ "key2" => "value2" }) - signature_base_string = request.signature_base_string(@http, @consumer, nil, - { nonce: @nonce, timestamp: @timestamp }) - assert_equal "POST&http%3A%2F%2Fexample.com%2Ftest&key2%3Dvalue2%26oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0", - signature_base_string - end - - def test_that_post_bodies_signed_if_other_content_type - request = Net::HTTP::Post.new(@request_uri.path) - request.body = "baz" - request["Content-Type"] = "application/xml" - signature_base_string = request.signature_base_string(@http, @consumer, nil, - { nonce: @nonce, timestamp: @timestamp }) - assert_equal "POST&http%3A%2F%2Fexample.com%2Ftest&oauth_body_hash%3DDvAa1AWdFoH9K%252B%252F2AHm3f6wH27k%253D%26oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0", - signature_base_string - end - - def test_that_post_bodies_not_signed_if_body_hash_disabled - request = Net::HTTP::Post.new(@request_uri.path) - request.body = "baz" - request["Content-Type"] = "application/xml" - signature_base_string = request.signature_base_string(@http, @consumer, nil, - { nonce: @nonce, timestamp: @timestamp, body_hash_enabled: false }) - assert_equal "POST&http%3A%2F%2Fexample.com%2Ftest&oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0", - signature_base_string - end - - def test_that_site_address_is_not_modified_in_place - options = { site: "http://twitter.com", request_endpoint: "http://api.twitter.com" } - request = Net::HTTP::Get.new("#{@request_uri.path}?#{request_parameters_to_s}") - request.oauth!(@http, @consumer, @token, options) - assert_equal "http://twitter.com", options[:site] - assert_equal "http://api.twitter.com", options[:request_endpoint] - end - - protected - - def assert_no_match(exp, act, _deprecated = nil) - refute_match(exp, act) - end - - def request_parameters_to_s - @request_parameters.map { |k, v| "#{k}=#{v}" }.join("&") - end -end diff --git a/test/units/net_http_request_proxy_test.rb b/test/units/net_http_request_proxy_test.rb deleted file mode 100644 index 2f549bcc..00000000 --- a/test/units/net_http_request_proxy_test.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class NetHTTPRequestProxyTest < Minitest::Test - def test_that_proxy_simple_get_request_works - request = Net::HTTP::Get.new("/test?key=value") - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test?key=value" }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "GET", request_proxy.method - end - - def test_that_proxy_simple_post_request_works_with_arguments - request = Net::HTTP::Post.new("/test") - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_post_request_works_with_form_data - request = Net::HTTP::Post.new("/test") - params = { "key" => "value" } - request.set_form_data(params) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_put_request_works_with_argugments - request = Net::HTTP::Put.new("/test") - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "PUT", request_proxy.method - end - - def test_that_proxy_simple_put_request_works_with_form_data - request = Net::HTTP::Put.new("/test") - params = { "key" => "value" } - request.set_form_data(params) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "PUT", request_proxy.method - end - - def test_that_proxy_post_request_uses_post_parameters - request = Net::HTTP::Post.new("/test?key=value") - request.set_form_data({ "key2" => "value2" }) - request_proxy = OAuth::RequestProxy.proxy(request, - { uri: "http://example.com/test?key=value", - parameters: { "key3" => "value3" } }) - - expected_parameters = { "key" => ["value"], "key2" => ["value2"], "key3" => ["value3"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end -end diff --git a/test/units/oauth_helper_test.rb b/test/units/oauth_helper_test.rb deleted file mode 100644 index 07f2613d..00000000 --- a/test/units/oauth_helper_test.rb +++ /dev/null @@ -1,112 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class TestOAuthHelper < Minitest::Test - def test_parse_valid_header - header = "OAuth " \ - 'realm="http://example.com/method", ' \ - 'oauth_consumer_key="vince_clortho", ' \ - 'oauth_token="token_value", ' \ - 'oauth_signature_method="HMAC-SHA1", ' \ - 'oauth_signature="signature_here", ' \ - 'oauth_timestamp="1240004133", oauth_nonce="nonce", ' \ - 'oauth_version="1.0" ' - - params = OAuth::Helper.parse_header(header) - - assert_equal "http://example.com/method", params["realm"] - assert_equal "vince_clortho", params["oauth_consumer_key"] - assert_equal "token_value", params["oauth_token"] - assert_equal "HMAC-SHA1", params["oauth_signature_method"] - assert_equal "signature_here", params["oauth_signature"] - assert_equal "1240004133", params["oauth_timestamp"] - assert_equal "nonce", params["oauth_nonce"] - assert_equal "1.0", params["oauth_version"] - end - - def test_parse_header_ill_formed - header = "OAuth garbage" - - assert_raises OAuth::Problem do - OAuth::Helper.parse_header(header) - end - end - - def test_parse_header_contains_equals - header = "OAuth " \ - 'realm="http://example.com/method", ' \ - 'oauth_consumer_key="vince_clortho", ' \ - 'oauth_token="token_value", ' \ - 'oauth_signature_method="HMAC-SHA1", ' \ - 'oauth_signature="signature_here_with_=", ' \ - 'oauth_timestamp="1240004133", oauth_nonce="nonce", ' \ - 'oauth_version="1.0" ' - - assert_raises OAuth::Problem do - OAuth::Helper.parse_header(header) - end - end - - def test_parse_valid_header_with_and_signs - header = "OAuth " \ - 'realm="http://example.com/method"&' \ - 'oauth_consumer_key="vince_clortho"&' \ - 'oauth_token="token_value"&' \ - 'oauth_signature_method="HMAC-SHA1"&' \ - 'oauth_signature="signature_here"&' \ - 'oauth_timestamp="1240004133"&oauth_nonce="nonce"&' \ - 'oauth_version="1.0"' - - params = OAuth::Helper.parse_header(header) - - assert_equal "http://example.com/method", params["realm"] - assert_equal "vince_clortho", params["oauth_consumer_key"] - assert_equal "token_value", params["oauth_token"] - assert_equal "HMAC-SHA1", params["oauth_signature_method"] - assert_equal "signature_here", params["oauth_signature"] - assert_equal "1240004133", params["oauth_timestamp"] - assert_equal "nonce", params["oauth_nonce"] - assert_equal "1.0", params["oauth_version"] - end - - def test_normalize - params = { - "oauth_nonce" => "nonce", - "weight" => { value: "65" }, - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "1240004133", - "oauth_consumer_key" => "vince_clortho", - "oauth_token" => "token_value", - "oauth_version" => "1.0" - } - assert_equal( - "oauth_consumer_key=vince_clortho&oauth_nonce=nonce&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1240004133&oauth_token=token_value&oauth_version=1.0&weight%5Bvalue%5D=65", OAuth::Helper.normalize(params) - ) - end - - def test_normalize_with_nested_array_of_hashes - params = { - "oauth_nonce" => "nonce", - "weight" => { value: "65" }, - "items" => [{ "a" => 1 }, { "b" => 2 }], - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "1240004133", - "oauth_consumer_key" => "vince_clortho", - "oauth_token" => "token_value", - "oauth_version" => "1.0" - } - assert_equal( - "items%5B%5D%5Ba%5D=1&items%5B%5D%5Bb%5D=2&oauth_consumer_key=vince_clortho&oauth_nonce=nonce&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1240004133&oauth_token=token_value&oauth_version=1.0&weight%5Bvalue%5D=65", OAuth::Helper.normalize(params) - ) - end - - def test_normalize_nested_query - assert_empty(OAuth::Helper.normalize_nested_query({})) - assert_equal(["foo=bar"], OAuth::Helper.normalize_nested_query({ foo: "bar" })) - assert_equal(["prefix%5Bfoo%5D=bar"], OAuth::Helper.normalize_nested_query({ foo: "bar" }, "prefix")) - assert_equal(["prefix%5Buser%5D%5Bage%5D=12", - "prefix%5Buser%5D%5Bdate%5D=2011-10-05", - "prefix%5Buser%5D%5Btwitter_id%5D=123"], OAuth::Helper.normalize_nested_query({ user: { twitter_id: 123, date: "2011-10-05", age: 12 } }, "prefix")) - end -end diff --git a/test/units/rack_request_proxy_test.rb b/test/units/rack_request_proxy_test.rb deleted file mode 100644 index 54fc2525..00000000 --- a/test/units/rack_request_proxy_test.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/request_proxy/rack_request" - -class RackRequestProxyTest < Minitest::Test - def test_that_proxy_simple_get_request_works - request = Rack::Request.new(Rack::MockRequest.env_for("http://example.com/test?key=value")) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test?key=value" }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "GET", request_proxy.method - end - - def test_that_proxy_simple_post_request_works - request = Rack::Request.new(Rack::MockRequest.env_for("http://example.com/test", method: "POST")) - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_post_and_get_request_works - request = Rack::Request.new(Rack::MockRequest.env_for("http://example.com/test?key=value", method: "POST", - input: "key2=value2")) - params = { "key2" => "value2" } - request_proxy = OAuth::RequestProxy.proxy(request, - { uri: "http://example.com/test?key=value", parameters: params }) - - expected_parameters = { "key" => "value", "key2" => "value2" } - assert_equal expected_parameters, request_proxy.parameters - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end -end diff --git a/test/units/request_token_test.rb b/test/units/request_token_test.rb deleted file mode 100644 index 2334753d..00000000 --- a/test/units/request_token_test.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class StubbedToken < OAuth::RequestToken - define_method :build_url_promoted do |root_domain, params| - build_url root_domain, params - end -end - -class TestRequestToken < Minitest::Test - def setup - # setup a fake req. token. mocking Consumer would be more appropriate... - @request_token = OAuth::RequestToken.new( - OAuth::Consumer.new("key", "secret", {}), - "key", - "secret" - ) - end - - def test_request_token_builds_authorize_url_connectly_with_additional_params - auth_url = @request_token.authorize_url({ oauth_callback: "github.com" }) - assert auth_url - assert_match(/oauth_token/, auth_url) - assert_match(/oauth_callback/, auth_url) - end - - def test_request_token_builds_authorize_url_connectly_with_no_or_nil_params - # we should only have 1 key in the url returned if we didn't pass anything. - # this is the only required param to authenticate the client. - auth_url = @request_token.authorize_url(nil) - assert auth_url - assert_match(/\?oauth_token=/, auth_url) - - auth_url = @request_token.authorize_url - assert auth_url - assert_match(/\?oauth_token=/, auth_url) - end - - def test_request_token_returns_nil_authorize_url_when_token_is_nil - @request_token.token = nil - assert_nil @request_token.authorize_url - end - - def test_request_token_builds_authenticate_url_connectly_with_additional_params - authenticate_url = @request_token.authenticate_url({ oauth_callback: "github.com" }) - assert authenticate_url - assert_match(/oauth_token/, authenticate_url) - assert_match(/oauth_callback/, authenticate_url) - end - - def test_request_token_builds_authenticate_url_connectly_with_no_or_nil_params - # we should only have 1 key in the url returned if we didn't pass anything. - # this is the only required param to authenticate the client. - authenticate_url = @request_token.authenticate_url(nil) - assert authenticate_url - assert_match(/\?oauth_token=/, authenticate_url) - - authenticate_url2 = @request_token.authenticate_url - assert authenticate_url2 - assert_match(/\?oauth_token=/, authenticate_url2) - end - - def test_request_token_returns_nil_authenticate_url_when_token_is_nil - @request_token.token = nil - assert_nil @request_token.authenticate_url - end - - # TODO: mock out the Consumer to test the Consumer/AccessToken interaction. - def test_get_access_token; end - - def test_build_url - @stubbed_token = StubbedToken.new(nil, nil, nil) - assert_respond_to @stubbed_token, :build_url_promoted - url = @stubbed_token.build_url_promoted( - "http://github.com/oauth/authorize", - { foo: "bar bar" } - ) - assert url - assert_equal "http://github.com/oauth/authorize?foo=bar+bar", url - end -end diff --git a/test/units/rest_client_request_proxy_test.rb b/test/units/rest_client_request_proxy_test.rb deleted file mode 100644 index 94ddd82d..00000000 --- a/test/units/rest_client_request_proxy_test.rb +++ /dev/null @@ -1,90 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/request_proxy/rest_client_request" -require "rest-client" - -class RestlClientRequestProxyTest < Minitest::Test - def test_that_proxy_simple_get_request_works - request = ::RestClient::Request.new(method: :get, url: "http://example.com/test?key=value") - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test?key=value" }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "GET", request_proxy.method - end - - def test_that_proxy_simple_post_request_works_with_arguments - request = ::RestClient::Request.new(method: :post, url: "http://example.com/test") - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_post_request_works_with_form_data - request = ::RestClient::Request.new(method: :post, url: "http://example.com/test", - payload: { "key" => "value" }, - headers: { "Content-Type" => "application/x-www-form-urlencoded" }) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_post_request_ignores_non_form_data_payload - request = ::RestClient::Request.new( - method: :post, url: "http://example.com/test", payload: '{"key": "value"}', headers: { content_type: :json } - ) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - assert_empty request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_put_request_works_with_arguments - request = ::RestClient::Request.new(method: :put, url: "http://example.com/test") - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "PUT", request_proxy.method - end - - def test_that_proxy_simple_put_request_works_with_form_data - request = ::RestClient::Request.new(method: :put, url: "http://example.com/test", - payload: { "key" => "value" }, - headers: { "Content-Type" => "application/x-www-form-urlencoded" }) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "PUT", request_proxy.method - end - - def test_that_proxy_post_request_works_with_mixed_parameter_sources - request = ::RestClient::Request.new(url: "http://example.com/test?key=value", - method: :post, - payload: { "key2" => "value2" }, - headers: { "Content-Type" => "application/x-www-form-urlencoded" }) - request_proxy = OAuth::RequestProxy.proxy(request, - { uri: "http://example.com/test?key=value", - parameters: { "key3" => "value3" } }) - - expected_parameters = { "key" => ["value"], "key2" => "value2", "key3" => "value3" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end -end diff --git a/test/units/rsa_sha1_test.rb b/test/units/rsa_sha1_test.rb deleted file mode 100644 index e90b47da..00000000 --- a/test/units/rsa_sha1_test.rb +++ /dev/null @@ -1,81 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/consumer" -require "oauth/signature/rsa/sha1" - -class TestSignatureRsaSha1 < Minitest::Test - def setup - @request = Net::HTTP::Get.new("/photos?file=vacaction.jpg&size=original&oauth_version=1.0&oauth_consumer_key=#{consumer_key}&oauth_timestamp=1196666512&oauth_nonce=13917289812797014437&oauth_signature_method=RSA-SHA1") - - @consumer = OAuth::Consumer.new(consumer_key, pkey_rsa) - end - - def test_that_rsa_sha1_implements_rsa_sha1 - assert_includes OAuth::Signature.available_methods, "rsa-sha1" - end - - def test_that_get_request_from_oauth_test_cases_produces_matching_signature_base_string - sbs = OAuth::Signature.signature_base_string(@request, { consumer: @consumer, - uri: "http://photos.example.net/photos" }) - - assert_equal "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512%26oauth_version%3D1.0%26size%3Doriginal", - sbs - end - - def test_that_get_request_from_oauth_test_cases_produces_matching_signature - signature = OAuth::Signature.sign(@request, { consumer: @consumer, - uri: "http://photos.example.net/photos" }) - - assert_equal "jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=", - signature - end - - def test_that_get_request_from_oauth_test_cases_produces_matching_signature_using_private_key_file - @consumer = OAuth::Consumer.new(consumer_key, nil) - - signature = OAuth::Signature.sign(@request, { consumer: @consumer, - private_key_file: pem_path, - uri: "http://photos.example.net/photos" }) - - assert_equal "jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=", - signature - end - - def test_that_get_request_from_oauth_test_cases_verifies_signature - @request = Net::HTTP::Get.new("/photos?oauth_signature_method=RSA-SHA1&oauth_version=1.0&oauth_consumer_key=#{consumer_key}&oauth_timestamp=1196666512&oauth_nonce=13917289812797014437&file=vacaction.jpg&size=original&oauth_signature=jvTp%2FwX1TYtByB1m%2BPbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2%2F9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW%2F%2Fe%2BRinhejgCuzoH26dyF8iY2ZZ%2F5D1ilgeijhV%2FvBka5twt399mXwaYdCwFYE%3D") - @consumer = OAuth::Consumer.new(consumer_key, x509_certificate) - - assert OAuth::Signature.verify(@request, { consumer: @consumer, - uri: "http://photos.example.net/photos" }) - end - - def test_that_get_request_from_oauth_test_cases_verifies_signature_with_pem - @request = Net::HTTP::Get.new("/photos?oauth_signature_method=RSA-SHA1&oauth_version=1.0&oauth_consumer_key=#{consumer_key}&oauth_timestamp=1196666512&oauth_nonce=13917289812797014437&file=vacaction.jpg&size=original&oauth_signature=jvTp%2FwX1TYtByB1m%2BPbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2%2F9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW%2F%2Fe%2BRinhejgCuzoH26dyF8iY2ZZ%2F5D1ilgeijhV%2FvBka5twt399mXwaYdCwFYE%3D") - assert OAuth::Signature.verify(@request, { consumer: @consumer, - uri: "http://photos.example.net/photos" }) - end - - private - - def consumer_key - "dpf43f3p2l4k3l03" - end - - def x509_certificate - OpenSSL::X509::Certificate.new(File.read(cert_path)) - end - - def pkey_rsa - OpenSSL::PKey::RSA.new(File.read(pem_path)) - end - - def cert_path - "#{File.dirname(__FILE__)}/../keys/rsa.cert" - end - - def pem_path - "#{File.dirname(__FILE__)}/../keys/rsa.pem" - end -end diff --git a/test/units/server_test.rb b/test/units/server_test.rb deleted file mode 100644 index 34814aa4..00000000 --- a/test/units/server_test.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/server" - -class ServerTest < Minitest::Test - def setup - @server = OAuth::Server.new "http://test.com" - end - - def test_default_paths - assert_equal "/oauth/request_token", @server.request_token_path - assert_equal "/oauth/authorize", @server.authorize_path - assert_equal "/oauth/access_token", @server.access_token_path - end - - def test_default_urls - assert_equal "http://test.com/oauth/request_token", @server.request_token_url - assert_equal "http://test.com/oauth/authorize", @server.authorize_url - assert_equal "http://test.com/oauth/access_token", @server.access_token_url - end - - def test_generate_consumer_credentials - consumer = @server.generate_consumer_credentials - assert consumer.key - assert consumer.secret - end - - def test_create_consumer - @consumer = @server.create_consumer - assert @consumer - assert @consumer.key - assert @consumer.secret - assert_equal "http://test.com", @consumer.site - assert_equal "/oauth/request_token", @consumer.request_token_path - assert_equal "/oauth/authorize", @consumer.authorize_path - assert_equal "/oauth/access_token", @consumer.access_token_path - assert_equal "http://test.com/oauth/request_token", @consumer.request_token_url - assert_equal "http://test.com/oauth/authorize", @consumer.authorize_url - assert_equal "http://test.com/oauth/access_token", @consumer.access_token_url - end -end diff --git a/test/units/signature_base_test.rb b/test/units/signature_base_test.rb deleted file mode 100644 index bd71c648..00000000 --- a/test/units/signature_base_test.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/signature/base" -require "net/http" - -class SignatureBaseTest < Minitest::Test - def test_that_initialize_requires_one_request_argument - assert_raises ArgumentError do - OAuth::Signature::Base.new - end - end - - def test_that_initialize_requires_a_valid_request_argument - request = nil - assert_raises TypeError do - OAuth::Signature::Base.new(request) do |token| - # just a stub - end - end - end - - def test_that_initialize_succeeds_when_the_request_proxy_is_valid - # this isn't quite valid, but it will do. - raw_request = Net::HTTP::Get.new("/test") - request = OAuth::RequestProxy.proxy(raw_request) - - OAuth::Signature::Base.new(request) do |token| - # just a stub - end - end -end diff --git a/test/units/signature_hmac_sha1_test.rb b/test/units/signature_hmac_sha1_test.rb deleted file mode 100644 index 71b79379..00000000 --- a/test/units/signature_hmac_sha1_test.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/signature/hmac/sha1" - -class SignatureHMACSHA1Test < Minitest::Test - def test_that_verify_returns_true_when_the_request_signature_is_right - request = OAuth::RequestProxy::MockRequest.new( - "method" => "POST", - "uri" => "https://photos.example.net/initialize", - "parameters" => { - "oauth_consumer_key" => "dpf43f3p2l4k3l03", - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "137131200", - "oauth_nonce" => "wIjqoS", - "oauth_callback" => "http://printer.example.com/ready", - "oauth_version" => "1.0", - "oauth_signature" => "xcHYBV3AbyoDz7L4dV10P3oLCjY=" - } - ) - assert OAuth::Signature::HMAC::SHA1.new(request, consumer_secret: "kd94hf93k423kf44").verify - end - - def test_that_verify_returns_false_when_the_request_signature_is_wrong - # Test a bug in the OAuth::Signature::Base#== method: when the Base64.decode64 method is - # used on the "self" and "other" signature (as in version 0.4.7), the result may be incorrectly "true". - request = OAuth::RequestProxy::MockRequest.new( - "method" => "POST", - "uri" => "https://photos.example.net/initialize", - "parameters" => { - "oauth_consumer_key" => "dpf43f3p2l4k3l03", - "oauth_signature_method" => "HMAC-SHA1", - "oauth_timestamp" => "137131200", - "oauth_nonce" => "wIjqoS", - "oauth_callback" => "http://printer.example.com/ready", - "oauth_version" => "1.0", - "oauth_signature" => "xcHYBV3AbyoDz7L4dV10P3oLCjZ=" - } - ) - refute OAuth::Signature::HMAC::SHA1.new(request, consumer_secret: "kd94hf93k423kf44").verify - end -end diff --git a/test/units/signature_hmac_sha256_test.rb b/test/units/signature_hmac_sha256_test.rb deleted file mode 100644 index e5850522..00000000 --- a/test/units/signature_hmac_sha256_test.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/signature/hmac/sha256" - -class SignatureHMACSHA256Test < Minitest::Test - def test_that_verify_returns_true_when_the_request_signature_is_right - request = OAuth::RequestProxy::MockRequest.new( - "method" => "POST", - "uri" => "https://photos.example.net/initialize", - "parameters" => { - "oauth_consumer_key" => "dpf43f3p2l4k3l03", - "oauth_signature_method" => "HMAC-SHA256", - "oauth_timestamp" => "137131200", - "oauth_nonce" => "wIjqoS", - "oauth_callback" => "http://printer.example.com/ready", - "oauth_version" => "1.0", - "oauth_signature" => "tkpCGNHi3laWBHQ9+Ka5IOeixEuhxg12LTMlLJxQxKc=" - } - ) - assert OAuth::Signature::HMAC::SHA256.new(request, consumer_secret: "kd94hf93k423kf44").verify - end - - def test_that_verify_returns_false_when_the_request_signature_is_wrong - # Test a bug in the OAuth::Signature::Base#== method: when the Base64.decode64 method is - # used on the "self" and "other" signature (as in version 0.4.7), the result may be incorrectly "true". - request = OAuth::RequestProxy::MockRequest.new( - "method" => "POST", - "uri" => "https://photos.example.net/initialize", - "parameters" => { - "oauth_consumer_key" => "dpf43f3p2l4k3l03", - "oauth_signature_method" => "HMAC-SHA256", - "oauth_timestamp" => "137131200", - "oauth_nonce" => "wIjqoS", - "oauth_callback" => "http://printer.example.com/ready", - "oauth_version" => "1.0", - "oauth_signature" => "tkpCGNHi3laWBHQ9+Ka5IOeixEuhxg12LTMlLJxQxKZ=" - } - ) - refute OAuth::Signature::HMAC::SHA256.new(request, consumer_secret: "kd94hf93k423kf44").verify - end -end diff --git a/test/units/signature_plain_text_test.rb b/test/units/signature_plain_text_test.rb deleted file mode 100644 index c2993d32..00000000 --- a/test/units/signature_plain_text_test.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class TestSignaturePlaintext < Minitest::Test - def test_that_plaintext_implements_plaintext - assert_includes OAuth::Signature.available_methods, "plaintext" - end - - def test_that_get_request_from_oauth_test_cases_produces_matching_signature - request = Net::HTTP::Get.new("/photos?file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_signature=kd94hf93k423kf44%26&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=PLAINTEXT") - - consumer = OAuth::Consumer.new("dpf43f3p2l4k3l03", "kd94hf93k423kf44") - token = OAuth::Token.new("nnch734d00sl2jdk", nil) - - assert OAuth::Signature.verify(request, { consumer: consumer, - token: token, - uri: "http://photos.example.net/photos" }) - end - - def test_that_get_request_from_oauth_test_cases_produces_matching_signature_part_two - request = Net::HTTP::Get.new("/photos?file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_signature=kd94hf93k423kf44%26pfkkdhi9sl3r4s00&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=PLAINTEXT") - - consumer = OAuth::Consumer.new("dpf43f3p2l4k3l03", "kd94hf93k423kf44") - token = OAuth::Token.new("nnch734d00sl2jdk", "pfkkdhi9sl3r4s00") - - assert OAuth::Signature.verify(request, { consumer: consumer, - token: token, - uri: "http://photos.example.net/photos" }) - end -end diff --git a/test/units/signature_test.rb b/test/units/signature_test.rb deleted file mode 100644 index a38cff15..00000000 --- a/test/units/signature_test.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -class TestOauth < Minitest::Test - def test_parameter_escaping_kcode_invariant - %w[n N e E s S u U].each do |kcode| - assert_equal "%E3%81%82", OAuth::Helper.escape("あ"), - "Failed to correctly escape Japanese under $KCODE = #{kcode}" - assert_equal "%C3%A9", OAuth::Helper.escape("é"), - "Failed to correctly escape e+acute under $KCODE = #{kcode}" - end - end -end diff --git a/test/units/token_test.rb b/test/units/token_test.rb deleted file mode 100644 index ec55ab7b..00000000 --- a/test/units/token_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -require "oauth/token" - -class TokenTest < Minitest::Test - def setup; end - - def test_token_constructor_produces_valid_token - token = OAuth::Token.new("xyz", "123") - assert_equal "xyz", token.token - assert_equal "123", token.secret - end -end diff --git a/test/units/typhoeus_request_proxy_test.rb b/test/units/typhoeus_request_proxy_test.rb deleted file mode 100644 index da1200c5..00000000 --- a/test/units/typhoeus_request_proxy_test.rb +++ /dev/null @@ -1,102 +0,0 @@ -# frozen_string_literal: true - -require_relative "../test_helper" - -begin - require "oauth/request_proxy/typhoeus_request" - require "typhoeus" - - class TyphoeusRequestProxyTest < Minitest::Test - def test_that_proxy_simple_get_request_works - request = ::Typhoeus::Request.new("/test?key=value") - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test?key=value" }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "GET", request_proxy.method - end - - def test_that_proxy_simple_post_request_works_with_arguments - request = Typhoeus::Request.new("/test", method: :post) - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_post_request_works_with_form_data - request = Typhoeus::Request.new("/test", method: :post, - params: { "key" => "value" }, - headers: { "Content-Type" => "application/x-www-form-urlencoded" }) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - - def test_that_proxy_simple_put_request_works_with_arguments - request = Typhoeus::Request.new("/test", method: :put) - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "PUT", request_proxy.method - end - - def test_that_proxy_simple_put_request_works_with_form_data - request = Typhoeus::Request.new("/test", method: :put, params: { "key" => "value" }) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "PUT", request_proxy.method - end - - def test_that_proxy_simple_patch_request_works_with_arguments - request = Typhoeus::Request.new("/test", method: :patch) - params = { "key" => "value" } - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test", parameters: params }) - - expected_parameters = { "key" => "value" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "PATCH", request_proxy.method - end - - def test_that_proxy_simple_patch_request_works_with_form_data - request = Typhoeus::Request.new("/test", method: :patch, params: { "key" => "value" }) - request_proxy = OAuth::RequestProxy.proxy(request, { uri: "http://example.com/test" }) - - expected_parameters = { "key" => ["value"] } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "PATCH", request_proxy.method - end - - def test_that_proxy_post_request_works_with_mixed_parameter_sources - request = Typhoeus::Request.new("/test?key=value", - method: :post, - params: { "key2" => "value2" }, - headers: { "Content-Type" => "application/x-www-form-urlencoded" }) - request_proxy = OAuth::RequestProxy.proxy(request, - { uri: "http://example.com/test?key=value", - parameters: { "key3" => "value3" } }) - - expected_parameters = { "key" => ["value"], "key2" => ["value2"], "key3" => "value3" } - assert_equal expected_parameters, request_proxy.parameters_for_signature - assert_equal "http://example.com/test", request_proxy.normalized_uri - assert_equal "POST", request_proxy.method - end - end -rescue LoadError => e - warn "! problem loading typhoeus, skipping these tests: #{e}" -end From ac89e85fea5ffea16f7a207e08655c9240f4c71d Mon Sep 17 00:00:00 2001 From: "|7eter l-|. l3oling" Date: Fri, 12 Sep 2025 23:24:45 +0700 Subject: [PATCH 05/15] Potential fix for code scanning alert no. 7: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: |7eter l-|. l3oling --- .github/workflows/discord-notifier.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/discord-notifier.yml b/.github/workflows/discord-notifier.yml index ad98367a..2b638d59 100644 --- a/.github/workflows/discord-notifier.yml +++ b/.github/workflows/discord-notifier.yml @@ -1,4 +1,6 @@ name: Discord Notify +permissions: + contents: read on: check_run: From e58cc2f7aae037ea66149b7620e5bf9ff2cf8dc2 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 04:11:09 -0600 Subject: [PATCH 06/15] =?UTF-8?q?=F0=9F=8E=A8=20Template=20bootstrap=20by?= =?UTF-8?q?=20kettle-dev-setup=20v1.1.18?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Conflicts: # .github/workflows/discord-notifier.yml # .rubocop_gradual.lock # Appraisal.root.gemfile # gemfiles/audit.gemfile # gemfiles/coverage.gemfile # gemfiles/current.gemfile # gemfiles/head.gemfile # gemfiles/ruby_2_7.gemfile # gemfiles/ruby_3_0.gemfile # gemfiles/ruby_3_1.gemfile # gemfiles/ruby_3_2.gemfile # gemfiles/ruby_3_3.gemfile # gemfiles/style.gemfile # gemfiles/unlocked_deps.gemfile # spec/oauth/tty/cli_spec.rb --- .idea/oauth.iml | 1 - .rubocop_gradual.lock | 42 +++++++++++++++++----------------- Appraisal.root.gemfile | 2 ++ gemfiles/audit.gemfile | 2 ++ gemfiles/coverage.gemfile | 2 ++ gemfiles/current.gemfile | 2 ++ gemfiles/head.gemfile | 1 + gemfiles/ruby_2_7.gemfile | 2 ++ gemfiles/ruby_3_0.gemfile | 2 ++ gemfiles/ruby_3_1.gemfile | 2 ++ gemfiles/ruby_3_2.gemfile | 2 ++ gemfiles/ruby_3_3.gemfile | 2 ++ gemfiles/style.gemfile | 2 ++ gemfiles/unlocked_deps.gemfile | 2 ++ spec/oauth/tty/cli_spec.rb | 2 +- 15 files changed, 45 insertions(+), 23 deletions(-) diff --git a/.idea/oauth.iml b/.idea/oauth.iml index ed2346ed..9c912c5e 100644 --- a/.idea/oauth.iml +++ b/.idea/oauth.iml @@ -64,7 +64,6 @@ - diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index 74b33740..d5ae15ea 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -105,27 +105,27 @@ [6, 1, 34, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/request_token*_spec.rb`.", 2527248508], [59, 5, 147, "RSpec/LeakyConstantDeclaration: Stub class constant instead of declaring explicitly.", 455072902] ], - "spec/oauth/tty/cli_spec.rb:588579763": [ + "spec/oauth/tty/cli_spec.rb:3404752772": [ [3, 1, 30, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/tty/cli*_spec.rb`.", 2849860169], - [106, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316], - [107, 38, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493], - [108, 34, 10, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 4294324198], - [110, 7, 73, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [111].", 39742504], - [111, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [110].", 4144335528], - [127, 7, 20, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4235470523], - [151, 38, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493], - [152, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316], - [153, 39, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648], - [155, 7, 73, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [156].", 39742504], - [156, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [155].", 4144335528], - [166, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277], - [167, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262], - [168, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262], - [169, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262], - [192, 23, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493], - [193, 23, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316], - [194, 23, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648], - [196, 7, 73, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [197].", 39742504], - [197, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [196].", 4144335528] + [111, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316], + [112, 38, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493], + [113, 34, 10, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 4294324198], + [115, 7, 73, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [116].", 39742504], + [116, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [115].", 4144335528], + [132, 7, 20, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 4235470523], + [156, 38, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493], + [157, 34, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316], + [158, 39, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648], + [160, 7, 73, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [161].", 39742504], + [161, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [160].", 4144335528], + [171, 7, 16, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 3492346277], + [172, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262], + [173, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262], + [174, 7, 21, "RSpec/StubbedMock: Prefer `allow` over `expect` when configuring a response.", 2407753262], + [197, 23, 20, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 1228090493], + [198, 23, 17, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 285748316], + [199, 23, 21, "RSpec/VerifiedDoubleReference: Use a constant class reference for verified doubles. String references are not verifying unless the class is loaded.", 3390344648], + [201, 7, 73, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [202].", 39742504], + [202, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [201].", 4144335528] ] } diff --git a/Appraisal.root.gemfile b/Appraisal.root.gemfile index 02afd183..515f81db 100644 --- a/Appraisal.root.gemfile +++ b/Appraisal.root.gemfile @@ -10,3 +10,5 @@ source "https://rubygems.org" # We do not load the standard Gemfile, as it is tailored for local development. gemspec + +gem "oauth-tty", github: "ruby-oauth/oauth-tty", branch: "main" diff --git a/gemfiles/audit.gemfile b/gemfiles/audit.gemfile index d0b46ac0..05ea8736 100644 --- a/gemfiles/audit.gemfile +++ b/gemfiles/audit.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/x_std_libs.gemfile") diff --git a/gemfiles/coverage.gemfile b/gemfiles/coverage.gemfile index 569b678c..d85cc480 100644 --- a/gemfiles/coverage.gemfile +++ b/gemfiles/coverage.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/coverage.gemfile") diff --git a/gemfiles/current.gemfile b/gemfiles/current.gemfile index d0b46ac0..05ea8736 100644 --- a/gemfiles/current.gemfile +++ b/gemfiles/current.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/x_std_libs.gemfile") diff --git a/gemfiles/head.gemfile b/gemfiles/head.gemfile index 2df7a424..cf9235e6 100644 --- a/gemfiles/head.gemfile +++ b/gemfiles/head.gemfile @@ -2,6 +2,7 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" gem "benchmark", "~> 0.4", ">= 0.4.1" gemspec path: "../" diff --git a/gemfiles/ruby_2_7.gemfile b/gemfiles/ruby_2_7.gemfile index c4d7f9b1..f1ef3107 100644 --- a/gemfiles/ruby_2_7.gemfile +++ b/gemfiles/ruby_2_7.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/x_std_libs/r2/libs.gemfile") diff --git a/gemfiles/ruby_3_0.gemfile b/gemfiles/ruby_3_0.gemfile index e94adf83..3b30037b 100644 --- a/gemfiles/ruby_3_0.gemfile +++ b/gemfiles/ruby_3_0.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/x_std_libs/r3.1/libs.gemfile") diff --git a/gemfiles/ruby_3_1.gemfile b/gemfiles/ruby_3_1.gemfile index e94adf83..3b30037b 100644 --- a/gemfiles/ruby_3_1.gemfile +++ b/gemfiles/ruby_3_1.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/x_std_libs/r3.1/libs.gemfile") diff --git a/gemfiles/ruby_3_2.gemfile b/gemfiles/ruby_3_2.gemfile index 326b6d8b..432b326d 100644 --- a/gemfiles/ruby_3_2.gemfile +++ b/gemfiles/ruby_3_2.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/x_std_libs/r3/libs.gemfile") diff --git a/gemfiles/ruby_3_3.gemfile b/gemfiles/ruby_3_3.gemfile index 326b6d8b..432b326d 100644 --- a/gemfiles/ruby_3_3.gemfile +++ b/gemfiles/ruby_3_3.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/x_std_libs/r3/libs.gemfile") diff --git a/gemfiles/style.gemfile b/gemfiles/style.gemfile index 58d3714e..2c1522a1 100644 --- a/gemfiles/style.gemfile +++ b/gemfiles/style.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/style.gemfile") diff --git a/gemfiles/unlocked_deps.gemfile b/gemfiles/unlocked_deps.gemfile index 31c29e3e..1d348053 100644 --- a/gemfiles/unlocked_deps.gemfile +++ b/gemfiles/unlocked_deps.gemfile @@ -2,6 +2,8 @@ source "https://rubygems.org" +gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" + gemspec path: "../" eval_gemfile("modular/coverage.gemfile") diff --git a/spec/oauth/tty/cli_spec.rb b/spec/oauth/tty/cli_spec.rb index 9be3db7c..c85e879e 100644 --- a/spec/oauth/tty/cli_spec.rb +++ b/spec/oauth/tty/cli_spec.rb @@ -87,7 +87,7 @@ def parse(command) normalized = out.lines.map { |l| l.strip } expected = [ "OAuth Gem #{OAuth::Version::VERSION}", - "OAuth TTY Gem #{OAuth::TTY::Version::VERSION}" + "OAuth TTY Gem #{OAuth::TTY::Version::VERSION}", ] expect(normalized).to eq(expected) end From 18fa5bd289bcbe79403c9444c591958b6065536e Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 10:44:48 -0600 Subject: [PATCH 07/15] =?UTF-8?q?=F0=9F=92=9A=20Modernize=20MacOS=20and=20?= =?UTF-8?q?Windows=20workflows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - make them evergreen --- .github/workflows/macos.yml | 6 +----- .github/workflows/windows.yml | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 98cd82c2..97ac74e8 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -4,8 +4,6 @@ on: push: branches: - 'main' - - '*-maintenance' - - '*-dev' - '*-stable' tags: - '!*' # Do not execute on tags @@ -38,9 +36,7 @@ jobs: bundler: - latest ruby: - - "2.7" - - "3.0" - - "3.1" + - ruby - truffleruby runs-on: macos-latest diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 22db7bdf..b675c91c 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,8 +4,6 @@ on: push: branches: - 'main' - - '*-maintenance' - - '*-dev' - '*-stable' tags: - '!*' # Do not execute on tags @@ -38,9 +36,7 @@ jobs: bundler: - latest ruby: - - "2.7" - - "3.0" - - "3.1" + - ruby runs-on: windows-latest continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} From 5dd8a765240718ac025364001a66c2128e042a03 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 10:56:05 -0600 Subject: [PATCH 08/15] =?UTF-8?q?=F0=9F=92=9A=20Fixing=20old=20Rubies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .rubocop.yml | 4 + README.md | 26 +- REEK | 0 docs/ActionController.html | 115 + docs/ActionController/Base.html | 248 ++ docs/ActionController/TestRequest.html | 623 +++++ docs/EventMachine.html | 126 + docs/EventMachine/HttpClient.html | 494 ++++ docs/Net.html | 115 + docs/Net/HTTPGenericRequest.html | 407 +++ docs/OAuth.html | 187 ++ docs/OAuth/AccessToken.html | 691 +++++ docs/OAuth/Client.html | 117 + docs/OAuth/Client/Helper.html | 991 +++++++ docs/OAuth/Consumer.html | 2381 +++++++++++++++++ docs/OAuth/ConsumerToken.html | 671 +++++ docs/OAuth/Error.html | 128 + docs/OAuth/Helper.html | 798 ++++++ docs/OAuth/OAuthTestHelper.html | 250 ++ docs/OAuth/Problem.html | 438 +++ docs/OAuth/RequestProxy.html | 299 +++ .../RequestProxy/ActionControllerRequest.html | 513 ++++ .../RequestProxy/ActionDispatchRequest.html | 169 ++ docs/OAuth/RequestProxy/Base.html | 1951 ++++++++++++++ docs/OAuth/RequestProxy/Curl.html | 115 + docs/OAuth/RequestProxy/Curl/Easy.html | 340 +++ docs/OAuth/RequestProxy/EventMachine.html | 115 + .../EventMachine/HttpRequest.html | 350 +++ docs/OAuth/RequestProxy/JabberRequest.html | 427 +++ docs/OAuth/RequestProxy/MockRequest.html | 417 +++ docs/OAuth/RequestProxy/Net.html | 115 + docs/OAuth/RequestProxy/Net/HTTP.html | 115 + .../RequestProxy/Net/HTTP/HTTPRequest.html | 393 +++ docs/OAuth/RequestProxy/RackRequest.html | 399 +++ docs/OAuth/RequestProxy/RestClient.html | 115 + .../RequestProxy/RestClient/Request.html | 340 +++ docs/OAuth/RequestProxy/Typhoeus.html | 115 + docs/OAuth/RequestProxy/Typhoeus/Request.html | 342 +++ .../RequestProxy/UnknownRequestType.html | 124 + docs/OAuth/RequestToken.html | 483 ++++ docs/OAuth/Server.html | 840 ++++++ docs/OAuth/ServerToken.html | 240 ++ docs/OAuth/Signature.html | 545 ++++ docs/OAuth/Signature/Base.html | 925 +++++++ docs/OAuth/Signature/HMAC.html | 117 + docs/OAuth/Signature/HMAC/SHA1.html | 226 ++ docs/OAuth/Signature/HMAC/SHA256.html | 226 ++ docs/OAuth/Signature/PLAINTEXT.html | 385 +++ docs/OAuth/Signature/RSA.html | 115 + docs/OAuth/Signature/RSA/SHA1.html | 362 +++ .../Signature/UnknownSignatureMethod.html | 124 + docs/OAuth/Token.html | 438 +++ docs/OAuth/Unauthorized.html | 361 +++ docs/OAuth/Version.html | 121 + docs/_index.html | 593 ++++ docs/class_list.html | 54 + docs/css/common.css | 1 + docs/css/full_list.css | 58 + docs/css/style.css | 503 ++++ docs/file.CHANGELOG.html | 686 +++++ docs/file.CITATION.html | 92 + docs/file.CODE_OF_CONDUCT.html | 201 ++ docs/file.CONTRIBUTING.html | 318 +++ docs/file.FUNDING.html | 114 + docs/file.LICENSE.html | 70 + docs/file.README.html | 562 ++++ docs/file.REEK.html | 71 + docs/file.RUBOCOP.html | 171 ++ docs/file.SECURITY.html | 138 + docs/file_list.html | 104 + docs/frames.html | 22 + docs/index.html | 562 ++++ docs/js/app.js | 344 +++ docs/js/full_list.js | 242 ++ docs/js/jquery.js | 4 + docs/method_list.html | 1654 ++++++++++++ docs/top-level-namespace.html | 110 + lib/oauth/helper.rb | 4 +- 78 files changed, 27735 insertions(+), 15 deletions(-) create mode 100644 REEK create mode 100644 docs/ActionController.html create mode 100644 docs/ActionController/Base.html create mode 100644 docs/ActionController/TestRequest.html create mode 100644 docs/EventMachine.html create mode 100644 docs/EventMachine/HttpClient.html create mode 100644 docs/Net.html create mode 100644 docs/Net/HTTPGenericRequest.html create mode 100644 docs/OAuth.html create mode 100644 docs/OAuth/AccessToken.html create mode 100644 docs/OAuth/Client.html create mode 100644 docs/OAuth/Client/Helper.html create mode 100644 docs/OAuth/Consumer.html create mode 100644 docs/OAuth/ConsumerToken.html create mode 100644 docs/OAuth/Error.html create mode 100644 docs/OAuth/Helper.html create mode 100644 docs/OAuth/OAuthTestHelper.html create mode 100644 docs/OAuth/Problem.html create mode 100644 docs/OAuth/RequestProxy.html create mode 100644 docs/OAuth/RequestProxy/ActionControllerRequest.html create mode 100644 docs/OAuth/RequestProxy/ActionDispatchRequest.html create mode 100644 docs/OAuth/RequestProxy/Base.html create mode 100644 docs/OAuth/RequestProxy/Curl.html create mode 100644 docs/OAuth/RequestProxy/Curl/Easy.html create mode 100644 docs/OAuth/RequestProxy/EventMachine.html create mode 100644 docs/OAuth/RequestProxy/EventMachine/HttpRequest.html create mode 100644 docs/OAuth/RequestProxy/JabberRequest.html create mode 100644 docs/OAuth/RequestProxy/MockRequest.html create mode 100644 docs/OAuth/RequestProxy/Net.html create mode 100644 docs/OAuth/RequestProxy/Net/HTTP.html create mode 100644 docs/OAuth/RequestProxy/Net/HTTP/HTTPRequest.html create mode 100644 docs/OAuth/RequestProxy/RackRequest.html create mode 100644 docs/OAuth/RequestProxy/RestClient.html create mode 100644 docs/OAuth/RequestProxy/RestClient/Request.html create mode 100644 docs/OAuth/RequestProxy/Typhoeus.html create mode 100644 docs/OAuth/RequestProxy/Typhoeus/Request.html create mode 100644 docs/OAuth/RequestProxy/UnknownRequestType.html create mode 100644 docs/OAuth/RequestToken.html create mode 100644 docs/OAuth/Server.html create mode 100644 docs/OAuth/ServerToken.html create mode 100644 docs/OAuth/Signature.html create mode 100644 docs/OAuth/Signature/Base.html create mode 100644 docs/OAuth/Signature/HMAC.html create mode 100644 docs/OAuth/Signature/HMAC/SHA1.html create mode 100644 docs/OAuth/Signature/HMAC/SHA256.html create mode 100644 docs/OAuth/Signature/PLAINTEXT.html create mode 100644 docs/OAuth/Signature/RSA.html create mode 100644 docs/OAuth/Signature/RSA/SHA1.html create mode 100644 docs/OAuth/Signature/UnknownSignatureMethod.html create mode 100644 docs/OAuth/Token.html create mode 100644 docs/OAuth/Unauthorized.html create mode 100644 docs/OAuth/Version.html create mode 100644 docs/_index.html create mode 100644 docs/class_list.html create mode 100644 docs/css/common.css create mode 100644 docs/css/full_list.css create mode 100644 docs/css/style.css create mode 100644 docs/file.CHANGELOG.html create mode 100644 docs/file.CITATION.html create mode 100644 docs/file.CODE_OF_CONDUCT.html create mode 100644 docs/file.CONTRIBUTING.html create mode 100644 docs/file.FUNDING.html create mode 100644 docs/file.LICENSE.html create mode 100644 docs/file.README.html create mode 100644 docs/file.REEK.html create mode 100644 docs/file.RUBOCOP.html create mode 100644 docs/file.SECURITY.html create mode 100644 docs/file_list.html create mode 100644 docs/frames.html create mode 100644 docs/index.html create mode 100644 docs/js/app.js create mode 100644 docs/js/full_list.js create mode 100644 docs/js/jquery.js create mode 100644 docs/method_list.html create mode 100644 docs/top-level-namespace.html diff --git a/.rubocop.yml b/.rubocop.yml index aa30b933..6a5e0e0a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,5 +9,9 @@ plugins: rubocop-on-rbs RBS: Enabled: true +AllCops: + Exclude: + - 'lib/oauth/helper.rb' + Layout/IndentationConsistency: Exclude: ['*.md'] diff --git a/README.md b/README.md index c3e9ff62..96699cc4 100644 --- a/README.md +++ b/README.md @@ -38,19 +38,19 @@ The primary maintainer since 2020 is Peter Boling (@pboling). ## 💡 Info you can shake a stick at -| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] | -|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Works with JRuby | ![JRuby 9.1 Compat][💎jruby-9.1i] ![JRuby 9.2 Compat][💎jruby-9.2i] ![JRuby 9.3 Compat][💎jruby-9.3i]
[![JRuby 9.4 Compat][💎jruby-9.4i]][🚎10-j-wf] [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] | -| Works with Truffle Ruby | ![Truffle Ruby 22.3 Compat][💎truby-22.3i] ![Truffle Ruby 23.0 Compat][💎truby-23.0i]
[![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] | -| Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎4-lg-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎6-s-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] | -| Works with MRI Ruby 2 | [![Ruby 2.3 Compat][💎ruby-2.3i]][🚎1-an-wf] [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎1-an-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎1-an-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎7-us-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎7-us-wf] | -| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] | -| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] | -| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![Wiki][📜wiki-img]][📜wiki] | -| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] | -| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] | -| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] | -| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] | +| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] | +|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Works with JRuby | ![JRuby 9.1 Compat][💎jruby-9.1i] ![JRuby 9.2 Compat][💎jruby-9.2i] ![JRuby 9.3 Compat][💎jruby-9.3i]
[![JRuby 9.4 Compat][💎jruby-9.4i]][🚎10-j-wf] [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] | +| Works with Truffle Ruby | ![Truffle Ruby 22.3 Compat][💎truby-22.3i] ![Truffle Ruby 23.0 Compat][💎truby-23.0i]
[![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] | +| Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎4-lg-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎6-s-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] | +| Works with MRI Ruby 2 | [![Ruby 2.3 Compat][💎ruby-2.3i]][🚎1-an-wf] [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎1-an-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎1-an-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎7-us-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎7-us-wf] | +| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] | +| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] | +| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![Wiki][📜wiki-img]][📜wiki] | +| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] | +| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] | +| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] | +| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] | ### Compatibility diff --git a/REEK b/REEK new file mode 100644 index 00000000..e69de29b diff --git a/docs/ActionController.html b/docs/ActionController.html new file mode 100644 index 00000000..8d7a0a20 --- /dev/null +++ b/docs/ActionController.html @@ -0,0 +1,115 @@ + + + + + + + Module: ActionController + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: ActionController + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/client/action_controller_request.rb
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: Base, TestRequest + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/ActionController/Base.html b/docs/ActionController/Base.html new file mode 100644 index 00000000..0541ad29 --- /dev/null +++ b/docs/ActionController/Base.html @@ -0,0 +1,248 @@ + + + + + + + Class: ActionController::Base + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: ActionController::Base + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/client/action_controller_request.rb
+
+ +
+ + + + + + + + + +

+ Instance Method Summary + collapse +

+ + + + + + +
+

Instance Method Details

+ + +
+

+ + #process_with_new_base_test(request, response = nil) ⇒ Object + + + + + +

+ + + + +
+
+
+
+15
+16
+17
+18
+
+
# File 'lib/oauth/client/action_controller_request.rb', line 15
+
+def process_with_new_base_test(request, response = nil)
+  request.apply_oauth! if request.respond_to?(:apply_oauth!)
+  super(request, response)
+end
+
+
+ +
+

+ + #process_with_oauth(request, response = nil) ⇒ Object + + + + + +

+ + + + +
+
+
+
+20
+21
+22
+23
+
+
# File 'lib/oauth/client/action_controller_request.rb', line 20
+
+def process_with_oauth(request, response = nil)
+  request.apply_oauth! if request.respond_to?(:apply_oauth!)
+  process_without_oauth(request, response)
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/ActionController/TestRequest.html b/docs/ActionController/TestRequest.html new file mode 100644 index 00000000..24f1e9a3 --- /dev/null +++ b/docs/ActionController/TestRequest.html @@ -0,0 +1,623 @@ + + + + + + + Class: ActionController::TestRequest + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: ActionController::TestRequest + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/client/action_controller_request.rb
+
+ +
+ + + + + +

Class Attribute Summary collapse

+
    + +
  • + + + .use_oauth ⇒ Object + + + + + + + + + + writeonly + + + + + + + + +

    Sets the attribute use_oauth.

    +
    + +
  • + + +
+ + + + + +

+ Class Method Summary + collapse +

+ + + +

+ Instance Method Summary + collapse +

+ + + + + +
+

Class Attribute Details

+ + + +
+

+ + .use_oauth=(value) ⇒ Object (writeonly) + + + + + +

+
+

Sets the attribute use_oauth

+ + +
+
+
+

Parameters:

+
    + +
  • + + value + + + + + + + — +

    the value to set the attribute use_oauth to.

    +
    + +
  • + +
+ + +
+ + + + +
+
+
+
+30
+31
+32
+
+
# File 'lib/oauth/client/action_controller_request.rb', line 30
+
+def use_oauth=(value)
+  @use_oauth = value
+end
+
+
+ +
+ + +
+

Class Method Details

+ + +
+

+ + .use_oauth?Boolean + + + + + +

+
+ + + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+33
+34
+35
+
+
# File 'lib/oauth/client/action_controller_request.rb', line 33
+
+def self.use_oauth?
+  @use_oauth
+end
+
+
+ +
+ +
+

Instance Method Details

+ + +
+

+ + #apply_oauth!Object + + + + + +

+ + + + +
+
+
+
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+
+
# File 'lib/oauth/client/action_controller_request.rb', line 48
+
+def apply_oauth!
+  return unless ActionController::TestRequest.use_oauth? && @oauth_options
+
+  @oauth_helper = OAuth::Client::Helper.new(
+    self,
+    @oauth_options.merge(request_uri: (respond_to?(:fullpath) ? fullpath : request_uri)),
+  )
+  @oauth_helper.amend_user_agent_header(env)
+
+  send("set_oauth_#{@oauth_options[:scheme]}")
+end
+
+
+ +
+

+ + #configure_oauth(consumer = nil, token = nil, options = {}) ⇒ Object + + + + + +

+ + + + +
+
+
+
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+
+
# File 'lib/oauth/client/action_controller_request.rb', line 37
+
+def configure_oauth(consumer = nil, token = nil, options = {})
+  @oauth_options = {
+    consumer: consumer,
+    token: token,
+    scheme: "header",
+    signature_method: nil,
+    nonce: nil,
+    timestamp: nil,
+  }.merge(options)
+end
+
+
+ +
+

+ + #set_oauth_headerObject + + + + + +

+ + + + +
+
+
+
+60
+61
+62
+
+
# File 'lib/oauth/client/action_controller_request.rb', line 60
+
+def set_oauth_header
+  env["Authorization"] = @oauth_helper.header
+end
+
+
+ +
+

+ + #set_oauth_parametersObject + + + + + +

+ + + + +
+
+
+
+64
+65
+66
+67
+
+
# File 'lib/oauth/client/action_controller_request.rb', line 64
+
+def set_oauth_parameters
+  @query_parameters = @oauth_helper.parameters_with_oauth
+  @query_parameters.merge!(oauth_signature: @oauth_helper.signature)
+end
+
+
+ +
+

+ + #set_oauth_query_stringObject + + + + + +

+ + + + +
+
+
+
+69
+70
+
+
# File 'lib/oauth/client/action_controller_request.rb', line 69
+
+def set_oauth_query_string
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/EventMachine.html b/docs/EventMachine.html new file mode 100644 index 00000000..b0a0af3b --- /dev/null +++ b/docs/EventMachine.html @@ -0,0 +1,126 @@ + + + + + + + Module: EventMachine + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: EventMachine + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/client/em_http.rb
+
+ +
+ +

Overview

+
+

Extensions for em-http so that we can use consumer.sign! with an EventMachine::HttpClient
+instance. This is purely syntactic sugar.

+ + +
+
+
+ + +

Defined Under Namespace

+

+ + + + + Classes: HttpClient + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/EventMachine/HttpClient.html b/docs/EventMachine/HttpClient.html new file mode 100644 index 00000000..3b738d45 --- /dev/null +++ b/docs/EventMachine/HttpClient.html @@ -0,0 +1,494 @@ + + + + + + + Class: EventMachine::HttpClient + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: EventMachine::HttpClient + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/client/em_http.rb
+
+ +
+ + + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #oauth_helper ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute oauth_helper.

    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + + +
+

Instance Attribute Details

+ + + +
+

+ + #oauth_helperObject (readonly) + + + + + +

+
+

Returns the value of attribute oauth_helper.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+11
+12
+13
+
+
# File 'lib/oauth/client/em_http.rb', line 11
+
+def oauth_helper
+  @oauth_helper
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #normalize_uriObject + + + + + +

+
+

This code was lifted from the em-http-request because it was removed from
+the gem June 19, 2010
+see: http://github.com/igrigorik/em-http-request/commit/d536fc17d56dbe55c487eab01e2ff9382a62598b

+ + +
+
+
+ + +
+ + + + +
+
+
+
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+
+
# File 'lib/oauth/client/em_http.rb', line 72
+
+def normalize_uri
+  @normalized_uri ||= begin
+    uri = @conn.dup
+    encoded_query = encode_query(@conn, @req[:query])
+    path, query = encoded_query.split("?", 2)
+    uri.query = query unless encoded_query.empty?
+    uri.path = path
+    uri
+  end
+end
+
+
+ +
+

+ + #oauth!(http, consumer = nil, token = nil, options = {}) ⇒ Object + + + + + +

+
+

Add the OAuth information to an HTTP request. Depending on the options[:scheme] setting
+this may add a header, additional query string parameters, or additional POST body parameters.
+The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
+header.

+ +
    +
  • http - Configured Net::HTTP instance, ignored in this scenario except for getting host.
  • +
  • consumer - OAuth::Consumer instance
  • +
  • token - OAuth::Token instance
  • +
  • options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+,
    ++signature_method+, +nonce+, +timestamp+)
  • +
+ +

This method also modifies the User-Agent header to add the OAuth gem version.

+ +

See Also: core spec version 1.0, section 5.4.1[http://oauth.net/core/1.0#rfc.section.5.4.1]

+ + +
+
+
+ + +
+ + + + +
+
+
+
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+
+
# File 'lib/oauth/client/em_http.rb', line 27
+
+def oauth!(http, consumer = nil, token = nil, options = {})
+  options = {
+    request_uri: normalized_oauth_uri(http),
+    consumer: consumer,
+    token: token,
+    scheme: "header",
+    signature_method: nil,
+    nonce: nil,
+    timestamp: nil,
+  }.merge(options)
+
+  @oauth_helper = OAuth::Client::Helper.new(self, options)
+  __send__(:"set_oauth_#{options[:scheme]}")
+end
+
+
+ +
+

+ + #signature_base_string(http, consumer = nil, token = nil, options = {}) ⇒ Object + + + + + +

+
+

Create a string suitable for signing for an HTTP request. This process involves parameter
+normalization as specified in the OAuth specification. The exact normalization also depends
+on the options[:scheme] being used so this must match what will be used for the request
+itself. The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
+header.

+ +
    +
  • http - Configured Net::HTTP instance
  • +
  • consumer - OAuth::Consumer instance
  • +
  • token - OAuth::Token instance
  • +
  • options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+,
    ++signature_method+, +nonce+, +timestamp+)
  • +
+ +

See Also: core spec version 1.0, section 9.1.1[http://oauth.net/core/1.0#rfc.section.9.1.1]

+ + +
+
+
+ + +
+ + + + +
+
+
+
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+
+
# File 'lib/oauth/client/em_http.rb', line 55
+
+def signature_base_string(http, consumer = nil, token = nil, options = {})
+  options = {
+    request_uri: normalized_oauth_uri(http),
+    consumer: consumer,
+    token: token,
+    scheme: "header",
+    signature_method: nil,
+    nonce: nil,
+    timestamp: nil,
+  }.merge(options)
+
+  OAuth::Client::Helper.new(self, options).signature_base_string
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/Net.html b/docs/Net.html new file mode 100644 index 00000000..e56cfd7d --- /dev/null +++ b/docs/Net.html @@ -0,0 +1,115 @@ + + + + + + + Module: Net + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: Net + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/client/net_http.rb
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: HTTPGenericRequest + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/Net/HTTPGenericRequest.html b/docs/Net/HTTPGenericRequest.html new file mode 100644 index 00000000..962724fb --- /dev/null +++ b/docs/Net/HTTPGenericRequest.html @@ -0,0 +1,407 @@ + + + + + + + Class: Net::HTTPGenericRequest + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: Net::HTTPGenericRequest + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + +
+
Includes:
+
OAuth::Helper
+
+ + + + + + +
+
Defined in:
+
lib/oauth/client/net_http.rb
+
+ +
+ + + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #oauth_helper ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute oauth_helper.

    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods included from OAuth::Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+ +
+

Instance Attribute Details

+ + + +
+

+ + #oauth_helperObject (readonly) + + + + + +

+
+

Returns the value of attribute oauth_helper.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+10
+11
+12
+
+
# File 'lib/oauth/client/net_http.rb', line 10
+
+def oauth_helper
+  @oauth_helper
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #oauth!(http, consumer = nil, token = nil, options = {}) ⇒ Object + + + + + +

+
+

Add the OAuth information to an HTTP request. Depending on the options[:scheme] setting
+this may add a header, additional query string parameters, or additional POST body parameters.
+The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
+header.

+ +
    +
  • http - Configured Net::HTTP instance
  • +
  • consumer - OAuth::Consumer instance
  • +
  • token - OAuth::Token instance
  • +
  • options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+,
    ++signature_method+, +nonce+, +timestamp+, +body_hash+)
  • +
+ +

This method also modifies the User-Agent header to add the OAuth gem version.

+ +

See Also: core spec version 1.0, section 5.4.1[http://oauth.net/core/1.0#rfc.section.5.4.1],
+ Request Body Hash 1.0 Draft 4[http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html,
+ http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html#when_to_include]

+ + +
+
+
+ + +
+ + + + +
+
+
+
+28
+29
+30
+31
+32
+33
+34
+
+
# File 'lib/oauth/client/net_http.rb', line 28
+
+def oauth!(http, consumer = nil, token = nil, options = {})
+  helper_options = oauth_helper_options(http, consumer, token, options)
+  @oauth_helper = OAuth::Client::Helper.new(self, helper_options)
+  @oauth_helper.amend_user_agent_header(self)
+  @oauth_helper.hash_body if oauth_body_hash_required?(helper_options)
+  send("set_oauth_#{helper_options[:scheme]}")
+end
+
+
+ +
+

+ + #signature_base_string(http, consumer = nil, token = nil, options = {}) ⇒ Object + + + + + +

+
+

Create a string suitable for signing for an HTTP request. This process involves parameter
+normalization as specified in the OAuth specification. The exact normalization also depends
+on the options[:scheme] being used so this must match what will be used for the request
+itself. The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
+header.

+ +
    +
  • http - Configured Net::HTTP instance
  • +
  • consumer - OAuth::Consumer instance
  • +
  • token - OAuth::Token instance
  • +
  • options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+,
    ++signature_method+, +nonce+, +timestamp+)
  • +
+ +

See Also: core spec version 1.0, section 5.4.1[http://oauth.net/core/1.0#rfc.section.5.4.1],
+ Request Body Hash 1.0 Draft 4[http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html,
+ http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html#when_to_include]

+ + +
+
+
+ + +
+ + + + +
+
+
+
+51
+52
+53
+54
+55
+56
+
+
# File 'lib/oauth/client/net_http.rb', line 51
+
+def signature_base_string(http, consumer = nil, token = nil, options = {})
+  helper_options = oauth_helper_options(http, consumer, token, options)
+  @oauth_helper = OAuth::Client::Helper.new(self, helper_options)
+  @oauth_helper.hash_body if oauth_body_hash_required?(helper_options)
+  @oauth_helper.signature_base_string
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth.html b/docs/OAuth.html new file mode 100644 index 00000000..317ce91b --- /dev/null +++ b/docs/OAuth.html @@ -0,0 +1,187 @@ + + + + + + + Module: OAuth + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/oauth.rb,
+ lib/oauth/client.rb,
lib/oauth/helper.rb,
lib/oauth/server.rb,
lib/oauth/version.rb,
lib/oauth/consumer.rb,
lib/oauth/signature.rb,
lib/oauth/errors/error.rb,
lib/oauth/tokens/token.rb,
lib/oauth/client/helper.rb,
lib/oauth/request_proxy.rb,
lib/oauth/errors/problem.rb,
lib/oauth/signature/base.rb,
lib/oauth/oauth_test_helper.rb,
lib/oauth/request_proxy/base.rb,
lib/oauth/signature/rsa/sha1.rb,
lib/oauth/errors/unauthorized.rb,
lib/oauth/signature/hmac/sha1.rb,
lib/oauth/signature/plaintext.rb,
lib/oauth/tokens/access_token.rb,
lib/oauth/tokens/server_token.rb,
lib/oauth/tokens/request_token.rb,
lib/oauth/signature/hmac/sha256.rb,
lib/oauth/tokens/consumer_token.rb,
lib/oauth/request_proxy/net_http.rb,
lib/oauth/request_proxy/curb_request.rb,
lib/oauth/request_proxy/mock_request.rb,
lib/oauth/request_proxy/rack_request.rb,
lib/oauth/request_proxy/jabber_request.rb,
lib/oauth/request_proxy/em_http_request.rb,
lib/oauth/request_proxy/typhoeus_request.rb,
lib/oauth/request_proxy/rest_client_request.rb,
lib/oauth/request_proxy/action_dispatch_request.rb,
lib/oauth/request_proxy/action_controller_request.rb
+
+
+ +
+ +

Defined Under Namespace

+

+ + + Modules: Client, Helper, OAuthTestHelper, RequestProxy, Signature, Version + + + + Classes: AccessToken, Consumer, ConsumerToken, Error, Problem, RequestToken, Server, ServerToken, Token, Unauthorized + + +

+ + +

+ Constant Summary + collapse +

+ +
+ +
OUT_OF_BAND = +
+
+

request tokens are passed between the consumer and the provider out of
+band (i.e. callbacks cannot be used), per section 6.1.1

+ + +
+
+
+ + +
+
+
"oob"
+ +
PARAMETERS = +
+
+

required parameters, per sections 6.1.1, 6.3.1, and 7

+ + +
+
+
+ + +
+
+
%w[
+  oauth_callback
+  oauth_consumer_key
+  oauth_token
+  oauth_signature_method
+  oauth_timestamp
+  oauth_nonce
+  oauth_verifier
+  oauth_version
+  oauth_signature
+  oauth_body_hash
+].freeze
+ +
RESERVED_CHARACTERS = +
+
+

reserved character regexp, per section 5.1

+ + +
+
+
+ + +
+
+
/[^a-zA-Z0-9\-._~]/.freeze
+ +
+ + + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/AccessToken.html b/docs/OAuth/AccessToken.html new file mode 100644 index 00000000..f3290cf3 --- /dev/null +++ b/docs/OAuth/AccessToken.html @@ -0,0 +1,691 @@ + + + + + + + Class: OAuth::AccessToken + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::AccessToken + + + +

+
+ +
+
Inherits:
+
+ ConsumerToken + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/tokens/access_token.rb
+
+ +
+ +

Overview

+
+

The Access Token is used for the actual “real” web service calls that you perform against the server

+ + +
+
+
+ + +
+ + + + + +

Instance Attribute Summary

+ +

Attributes inherited from ConsumerToken

+

#consumer, #params, #response

+ + + +

Attributes inherited from Token

+

#secret, #token

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from ConsumerToken

+

from_hash, #initialize, #sign!

+ + + + + + + + + +

Methods inherited from Token

+

#initialize, #to_query

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::ConsumerToken

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #delete(path, headers = {}) ⇒ Object + + + + + +

+
+

Make a regular DELETE request using AccessToken

+ +

@response = @token.delete(‘/people/123’)
+ @response = @token.delete(‘/people/123’, { ‘Accept’ => ‘application/xml’ })

+ + +
+
+
+ + +
+ + + + +
+
+
+
+81
+82
+83
+
+
# File 'lib/oauth/tokens/access_token.rb', line 81
+
+def delete(path, headers = {})
+  request(:delete, path, headers)
+end
+
+
+ +
+

+ + #get(path, headers = {}) ⇒ Object + + + + + +

+
+

Make a regular GET request using AccessToken

+ +

@response = @token.get(‘/people’)
+ @response = @token.get(‘/people’, { ‘Accept’=>’application/xml’ })

+ + +
+
+
+ + +
+ + + + +
+
+
+
+28
+29
+30
+
+
# File 'lib/oauth/tokens/access_token.rb', line 28
+
+def get(path, headers = {})
+  request(:get, path, headers)
+end
+
+
+ +
+

+ + #head(path, headers = {}) ⇒ Object + + + + + +

+
+

Make a regular HEAD request using AccessToken

+ +

@response = @token.head(‘/people’)

+ + +
+
+
+ + +
+ + + + +
+
+
+
+36
+37
+38
+
+
# File 'lib/oauth/tokens/access_token.rb', line 36
+
+def head(path, headers = {})
+  request(:head, path, headers)
+end
+
+
+ +
+

+ + #patch(path, body = "", headers = {}) ⇒ Object + + + + + +

+
+

Make a regular PATCH request using AccessToken

+ +

@response = @token.patch(‘/people/123’)
+ @response = @token.patch(‘/people/123’, { :name => ‘Bob’, :email => ‘bob@mailinator.com’ })
+ @response = @token.patch(‘/people/123’, { :name => ‘Bob’, :email => ‘bob@mailinator.com’ }, { ‘Accept’ => ‘application/xml’ })
+ @response = @token.patch(‘/people/123’, nil, { ‘Accept’ => ‘application/xml’ })
+ @response = @token.patch(‘/people/123’, @person.to_xml, { ‘Accept’ => ‘application/xml’, ‘Content-Type’ => ‘application/xml’ })

+ + +
+
+
+ + +
+ + + + +
+
+
+
+72
+73
+74
+
+
# File 'lib/oauth/tokens/access_token.rb', line 72
+
+def patch(path, body = "", headers = {})
+  request(:patch, path, body, headers)
+end
+
+
+ +
+

+ + #post(path, body = "", headers = {}) ⇒ Object + + + + + +

+
+

Make a regular POST request using AccessToken

+ +

@response = @token.post(‘/people’)
+ @response = @token.post(‘/people’, { :name => ‘Bob’, :email => ‘bob@mailinator.com’ })
+ @response = @token.post(‘/people’, { :name => ‘Bob’, :email => ‘bob@mailinator.com’ }, { ‘Accept’ => ‘application/xml’ })
+ @response = @token.post(‘/people’, nil, {‘Accept’ => ‘application/xml’ })
+ @response = @token.post(‘/people’, @person.to_xml, { ‘Accept’=>’application/xml’, ‘Content-Type’ => ‘application/xml’ })

+ + +
+
+
+ + +
+ + + + +
+
+
+
+48
+49
+50
+
+
# File 'lib/oauth/tokens/access_token.rb', line 48
+
+def post(path, body = "", headers = {})
+  request(:post, path, body, headers)
+end
+
+
+ +
+

+ + #put(path, body = "", headers = {}) ⇒ Object + + + + + +

+
+

Make a regular PUT request using AccessToken

+ +

@response = @token.put(‘/people/123’)
+ @response = @token.put(‘/people/123’, { :name => ‘Bob’, :email => ‘bob@mailinator.com’ })
+ @response = @token.put(‘/people/123’, { :name => ‘Bob’, :email => ‘bob@mailinator.com’ }, { ‘Accept’ => ‘application/xml’ })
+ @response = @token.put(‘/people/123’, nil, { ‘Accept’ => ‘application/xml’ })
+ @response = @token.put(‘/people/123’, @person.to_xml, { ‘Accept’ => ‘application/xml’, ‘Content-Type’ => ‘application/xml’ })

+ + +
+
+
+ + +
+ + + + +
+
+
+
+60
+61
+62
+
+
# File 'lib/oauth/tokens/access_token.rb', line 60
+
+def put(path, body = "", headers = {})
+  request(:put, path, body, headers)
+end
+
+
+ +
+

+ + #request(http_method, path, *arguments) ⇒ Object + + + + + +

+
+

The less intrusive way. Otherwise, if we are to do it correctly inside consumer,
+we need to restructure and touch more methods: request(), sign!(), etc.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+
+
# File 'lib/oauth/tokens/access_token.rb', line 8
+
+def request(http_method, path, *arguments)
+  request_uri = URI.parse(path)
+  site_uri = consumer.uri
+  is_service_uri_different = request_uri.absolute? && request_uri != site_uri
+  begin
+    consumer.uri(request_uri) if is_service_uri_different
+    @response = super(http_method, path, *arguments)
+  ensure
+    # NOTE: reset for wholesomeness? meaning that we admit only AccessToken service calls may use different URIs?
+    # so reset in case consumer is still used for other token-management tasks subsequently?
+    consumer.uri(site_uri) if is_service_uri_different
+  end
+  @response
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Client.html b/docs/OAuth/Client.html new file mode 100644 index 00000000..b4307caa --- /dev/null +++ b/docs/OAuth/Client.html @@ -0,0 +1,117 @@ + + + + + + + Module: OAuth::Client + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::Client + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/client.rb,
+ lib/oauth/client/helper.rb
+
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: Helper + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Client/Helper.html b/docs/OAuth/Client/Helper.html new file mode 100644 index 00000000..446886e0 --- /dev/null +++ b/docs/OAuth/Client/Helper.html @@ -0,0 +1,991 @@ + + + + + + + Class: OAuth::Client::Helper + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::Client::Helper + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + +
+
Includes:
+
Helper
+
+ + + + + + +
+
Defined in:
+
lib/oauth/client/helper.rb
+
+ +
+ + + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #options ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute options.

    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +
+

+ + #initialize(request, options = {}) ⇒ Helper + + + + + +

+
+

Returns a new instance of Helper.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+14
+15
+16
+17
+18
+
+
# File 'lib/oauth/client/helper.rb', line 14
+
+def initialize(request, options = {})
+  @request = request
+  @options = options
+  @options[:signature_method] ||= "HMAC-SHA1"
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #optionsObject (readonly) + + + + + +

+
+

Returns the value of attribute options.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+20
+21
+22
+
+
# File 'lib/oauth/client/helper.rb', line 20
+
+def options
+  @options
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #amend_user_agent_header(headers) ⇒ Object + + + + + +

+ + + + +
+
+
+
+77
+78
+79
+80
+81
+82
+83
+84
+85
+
+
# File 'lib/oauth/client/helper.rb', line 77
+
+def amend_user_agent_header(headers)
+  @oauth_ua_string ||= "OAuth gem v#{OAuth::Version::VERSION}"
+  # Net::HTTP in 1.9 appends Ruby
+  if headers["User-Agent"] && headers["User-Agent"] != "Ruby"
+    headers["User-Agent"] += " (#{@oauth_ua_string})"
+  else
+    headers["User-Agent"] = @oauth_ua_string
+  end
+end
+
+
+ +
+

+ + #hash_bodyObject + + + + + +

+ + + + +
+
+
+
+73
+74
+75
+
+
# File 'lib/oauth/client/helper.rb', line 73
+
+def hash_body
+  @options[:body_hash] = OAuth::Signature.body_hash(@request, parameters: oauth_parameters)
+end
+
+
+ +
+

+ + #headerObject + + + + + +

+ + + + +
+
+
+
+87
+88
+89
+90
+91
+92
+93
+94
+95
+
+
# File 'lib/oauth/client/helper.rb', line 87
+
+def header
+  parameters = oauth_parameters
+  parameters["oauth_signature"] = signature(options.merge(parameters: parameters))
+
+  header_params_str = parameters.sort.map { |k, v| "#{k}=\"#{escape(v)}\"" }.join(", ")
+
+  realm = "realm=\"#{options[:realm]}\", " if options[:realm]
+  "OAuth #{realm}#{header_params_str}"
+end
+
+
+ +
+

+ + #nonceObject + + + + + +

+ + + + +
+
+
+
+22
+23
+24
+
+
# File 'lib/oauth/client/helper.rb', line 22
+
+def nonce
+  options[:nonce] ||= generate_key
+end
+
+
+ +
+

+ + #oauth_parametersObject + + + + + +

+ + + + +
+
+
+
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+
+
# File 'lib/oauth/client/helper.rb', line 30
+
+def oauth_parameters
+  out = {
+    "oauth_body_hash" => options[:body_hash],
+    "oauth_callback" => options[:oauth_callback],
+    "oauth_consumer_key" => options[:consumer].key,
+    "oauth_token" => options[:token] ? options[:token].token : "",
+    "oauth_signature_method" => options[:signature_method],
+    "oauth_timestamp" => timestamp,
+    "oauth_nonce" => nonce,
+    "oauth_verifier" => options[:oauth_verifier],
+    "oauth_version" => options[:oauth_version] || "1.0",
+    "oauth_session_handle" => options[:oauth_session_handle],
+  }
+  allowed_empty_params = options[:allow_empty_params]
+  if allowed_empty_params != true && !allowed_empty_params.is_a?(Array)
+    allowed_empty_params = (allowed_empty_params == false) ? [] : [allowed_empty_params]
+  end
+  out.select! { |k, v| v.to_s != "" || allowed_empty_params == true || allowed_empty_params.include?(k) }
+  out
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+97
+98
+99
+
+
# File 'lib/oauth/client/helper.rb', line 97
+
+def parameters
+  OAuth::RequestProxy.proxy(@request).parameters
+end
+
+
+ +
+

+ + #parameters_with_oauthObject + + + + + +

+ + + + +
+
+
+
+101
+102
+103
+
+
# File 'lib/oauth/client/helper.rb', line 101
+
+def parameters_with_oauth
+  oauth_parameters.merge(parameters)
+end
+
+
+ +
+

+ + #signature(extra_options = {}) ⇒ Object + + + + + +

+ + + + +
+
+
+
+51
+52
+53
+54
+55
+56
+57
+58
+
+
# File 'lib/oauth/client/helper.rb', line 51
+
+def signature(extra_options = {})
+  OAuth::Signature.sign(@request, {
+    uri: options[:request_uri],
+    consumer: options[:consumer],
+    token: options[:token],
+    unsigned_parameters: options[:unsigned_parameters],
+  }.merge(extra_options))
+end
+
+
+ +
+

+ + #signature_base_string(extra_options = {}) ⇒ Object + + + + + +

+ + + + +
+
+
+
+60
+61
+62
+63
+64
+65
+66
+67
+
+
# File 'lib/oauth/client/helper.rb', line 60
+
+def signature_base_string(extra_options = {})
+  OAuth::Signature.signature_base_string(@request, {
+    uri: options[:request_uri],
+    consumer: options[:consumer],
+    token: options[:token],
+    parameters: oauth_parameters,
+  }.merge(extra_options))
+end
+
+
+ +
+

+ + #timestampObject + + + + + +

+ + + + +
+
+
+
+26
+27
+28
+
+
# File 'lib/oauth/client/helper.rb', line 26
+
+def timestamp
+  options[:timestamp] ||= generate_timestamp
+end
+
+
+ +
+

+ + #token_request?Boolean + + + + + +

+
+ + + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+69
+70
+71
+
+
# File 'lib/oauth/client/helper.rb', line 69
+
+def token_request?
+  @options[:token_request].eql?(true)
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Consumer.html b/docs/OAuth/Consumer.html new file mode 100644 index 00000000..2f719640 --- /dev/null +++ b/docs/OAuth/Consumer.html @@ -0,0 +1,2381 @@ + + + + + + + Class: OAuth::Consumer + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::Consumer + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/consumer.rb
+
+ +
+ + + +

+ Constant Summary + collapse +

+ +
+ +
CA_FILE = + +
+
nil
+ +
@@default_options = + +
+
SnakyHash::SymbolKeyed.new(
+  {
+    # Signature method used by server. Defaults to HMAC-SHA1
+    signature_method: "HMAC-SHA1",
+     # default paths on site. These are the same as the defaults set up by the generators
+    request_token_path: "/oauth/request_token",
+    authenticate_path: "/oauth/authenticate",
+    authorize_path: "/oauth/authorize",
+    access_token_path: "/oauth/access_token",
+     proxy: nil,
+    # How do we send the oauth values to the server see
+    # https://oauth.net/core/1.0/#consumer_req_param for more info
+    #
+    # Possible values:
+    #
+    #   :header - via the Authorize header (Default) ( option 1. in spec)
+    #   :body - url form encoded in body of POST request ( option 2. in spec)
+    #   :query_string - via the query part of the url ( option 3. in spec)
+    scheme: :header,
+     # Default http method used for OAuth Token Requests (defaults to :post)
+    http_method: :post,
+     # Add a custom ca_file for consumer
+    # :ca_file       => '/etc/certs.pem'
+     # Possible values:
+    #
+    # nil, false - no debug output
+    # true - uses $stdout
+    # some_value - uses some_value
+    debug_output: nil,
+     # Defaults to producing a body_hash as part of the signature but
+    # can be disabled since it's not officially part of the OAuth 1.0
+    # spec. Possible values are true and false
+    body_hash_enabled: true,
+     oauth_version: "1.0",
+  },
+)
+ +
+ + + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #http ⇒ Object + + + + + + + + + + + + + + + + +

    The HTTP object for the site.

    +
    + +
  • + + +
  • + + + #key ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute key.

    +
    + +
  • + + +
  • + + + #options ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute options.

    +
    + +
  • + + +
  • + + + #secret ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute secret.

    +
    + +
  • + + +
  • + + + #site ⇒ Object + + + + + + + + + + + + + + + + +
    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + +
+

Constructor Details

+ +
+

+ + #initialize(consumer_key, consumer_secret, options = {}) ⇒ Consumer + + + + + +

+
+

Create a new consumer instance by passing it a configuration hash:

+ +

@consumer = OAuth::Consumer.new(key, secret, {
+ :site => “http://term.ie”,
+ :scheme => :header,
+ :http_method => :post,
+ :request_token_path => “/oauth/example/request_token.php”,
+ :access_token_path => “/oauth/example/access_token.php”,
+ :authorize_path => “/oauth/example/authorize.php”,
+ :body_hash_enabled => false
+ })

+ +

Start the process by requesting a token

+ +

@request_token = @consumer.get_request_token
+ session[:request_token] = @request_token
+ redirect_to @request_token.authorize_url

+ +

When user returns create an access_token

+ +

@access_token = @request_token.get_access_token
+ @photos=@access_token.get(‘/photos.xml’)

+ + +
+
+
+ + +
+ + + + +
+
+
+
+106
+107
+108
+109
+110
+111
+112
+113
+
+
# File 'lib/oauth/consumer.rb', line 106
+
+def initialize(consumer_key, consumer_secret, options = {})
+  @key = consumer_key
+  @secret = consumer_secret
+
+  # ensure that keys are symbols
+  snaky_options = SnakyHash::SymbolKeyed.new(options)
+  @options = @@default_options.merge(snaky_options)
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #httpObject + + + + + +

+
+

The HTTP object for the site. The HTTP Object is what you get when you do Net::HTTP.new

+ + +
+
+
+ + +
+ + + + +
+
+
+
+131
+132
+133
+
+
# File 'lib/oauth/consumer.rb', line 131
+
+def http
+  @http ||= create_http
+end
+
+
+ + + +
+

+ + #keyObject + + + + + +

+
+

Returns the value of attribute key.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+80
+81
+82
+
+
# File 'lib/oauth/consumer.rb', line 80
+
+def key
+  @key
+end
+
+
+ + + +
+

+ + #optionsObject + + + + + +

+
+

Returns the value of attribute options.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+80
+81
+82
+
+
# File 'lib/oauth/consumer.rb', line 80
+
+def options
+  @options
+end
+
+
+ + + +
+

+ + #secretObject + + + + + +

+
+

Returns the value of attribute secret.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+80
+81
+82
+
+
# File 'lib/oauth/consumer.rb', line 80
+
+def secret
+  @secret
+end
+
+
+ + + +
+

+ + #siteObject + + + + + +

+ + + + +
+
+
+
+298
+299
+300
+
+
# File 'lib/oauth/consumer.rb', line 298
+
+def site
+  @options[:site].to_s
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #access_token_pathObject + + + + + +

+ + + + +
+
+
+
+324
+325
+326
+
+
# File 'lib/oauth/consumer.rb', line 324
+
+def access_token_path
+  @options[:access_token_path]
+end
+
+
+ +
+

+ + #access_token_urlObject + + + + + +

+ + + + +
+
+
+
+353
+354
+355
+
+
# File 'lib/oauth/consumer.rb', line 353
+
+def access_token_url
+  @options[:access_token_url] || (site + access_token_path)
+end
+
+
+ +
+

+ + #access_token_url?Boolean + + + + + +

+
+ + + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+357
+358
+359
+
+
# File 'lib/oauth/consumer.rb', line 357
+
+def access_token_url?
+  @options.key?(:access_token_url)
+end
+
+
+ +
+

+ + #authenticate_pathObject + + + + + +

+ + + + +
+
+
+
+316
+317
+318
+
+
# File 'lib/oauth/consumer.rb', line 316
+
+def authenticate_path
+  @options[:authenticate_path]
+end
+
+
+ +
+

+ + #authenticate_urlObject + + + + + +

+ + + + +
+
+
+
+337
+338
+339
+
+
# File 'lib/oauth/consumer.rb', line 337
+
+def authenticate_url
+  @options[:authenticate_url] || (site + authenticate_path)
+end
+
+
+ +
+

+ + #authenticate_url?Boolean + + + + + +

+
+ + + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+341
+342
+343
+
+
# File 'lib/oauth/consumer.rb', line 341
+
+def authenticate_url?
+  @options.key?(:authenticate_url)
+end
+
+
+ +
+

+ + #authorize_pathObject + + + + + +

+ + + + +
+
+
+
+320
+321
+322
+
+
# File 'lib/oauth/consumer.rb', line 320
+
+def authorize_path
+  @options[:authorize_path]
+end
+
+
+ +
+

+ + #authorize_urlObject + + + + + +

+ + + + +
+
+
+
+345
+346
+347
+
+
# File 'lib/oauth/consumer.rb', line 345
+
+def authorize_url
+  @options[:authorize_url] || (site + authorize_path)
+end
+
+
+ +
+

+ + #authorize_url?Boolean + + + + + +

+
+ + + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+349
+350
+351
+
+
# File 'lib/oauth/consumer.rb', line 349
+
+def authorize_url?
+  @options.key?(:authorize_url)
+end
+
+
+ +
+

+ + #create_signed_request(http_method, path, token = nil, request_options = {}, *arguments) ⇒ Object + + + + + +

+
+

Creates and signs an http request.
+It’s recommended to use the Token classes to set this up correctly

+ + +
+
+
+ + +
+ + + + +
+
+
+
+243
+244
+245
+246
+247
+
+
# File 'lib/oauth/consumer.rb', line 243
+
+def create_signed_request(http_method, path, token = nil, request_options = {}, *arguments)
+  request = create_http_request(http_method, path, *arguments)
+  sign!(request, token, request_options)
+  request
+end
+
+
+ +
+

+ + #debug_outputObject + + + + + +

+ + + + +
+
+
+
+120
+121
+122
+123
+124
+125
+126
+127
+128
+
+
# File 'lib/oauth/consumer.rb', line 120
+
+def debug_output
+  @debug_output ||= case @options[:debug_output]
+  when nil, false
+  when true
+    $stdout
+  else
+    @options[:debug_output]
+  end
+end
+
+
+ +
+

+ + #get_access_token(request_token, request_options = {}, *arguments, &block) ⇒ Object + + + + + +

+ + + + +
+
+
+
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+
+
# File 'lib/oauth/consumer.rb', line 145
+
+def get_access_token(request_token, request_options = {}, *arguments, &block)
+  response = token_request(
+    http_method,
+    (access_token_url? ? access_token_url : access_token_path),
+    request_token,
+    request_options,
+    *arguments,
+    &block
+  )
+  OAuth::AccessToken.from_hash(self, response)
+end
+
+
+ +
+

+ + #get_request_token(request_options = {}, *arguments, &block) ⇒ Object + + + + + +

+
+

Makes a request to the service for a new OAuth::RequestToken

+ +

@request_token = @consumer.get_request_token

+ +

To include OAuth parameters:

+ +

@request_token = @consumer.get_request_token \
+ :oauth_callback => “http://example.com/cb”

+ +

To include application-specific parameters:

+ +

@request_token = @consumer.get_request_token({}, :foo => “bar”)

+ +

TODO oauth_callback should be a mandatory parameter

+ + +
+
+
+ + +
+ + + + +
+
+
+
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+
+
# File 'lib/oauth/consumer.rb', line 171
+
+def get_request_token(request_options = {}, *arguments, &block)
+  # if oauth_callback wasn't provided, it is assumed that oauth_verifiers
+  # will be exchanged out of band
+  request_options[:oauth_callback] ||= OAuth::OUT_OF_BAND unless request_options[:exclude_callback]
+
+  response = if block
+    token_request(
+      http_method,
+      (request_token_url? ? request_token_url : request_token_path),
+      nil,
+      request_options,
+      *arguments,
+      &block
+    )
+  else
+    token_request(
+      http_method,
+      (request_token_url? ? request_token_url : request_token_path),
+      nil,
+      request_options,
+      *arguments,
+    )
+  end
+  OAuth::RequestToken.from_hash(self, response)
+end
+
+
+ +
+

+ + #http_methodObject + + + + + +

+
+

The default http method

+ + +
+
+
+ + +
+ + + + +
+
+
+
+116
+117
+118
+
+
# File 'lib/oauth/consumer.rb', line 116
+
+def http_method
+  @http_method ||= @options[:http_method] || :post
+end
+
+
+ +
+

+ + #proxyObject + + + + + +

+ + + + +
+
+
+
+361
+362
+363
+
+
# File 'lib/oauth/consumer.rb', line 361
+
+def proxy
+  @options[:proxy]
+end
+
+
+ +
+

+ + #request(http_method, path, token = nil, request_options = {}, *arguments) ⇒ Object + + + + + +

+
+

Creates, signs and performs an http request.
+It’s recommended to use the OAuth::Token classes to set this up correctly.
+request_options take precedence over consumer-wide options when signing
+ a request.
+arguments are POST and PUT bodies (a Hash, string-encoded parameters, or
+ absent), followed by additional HTTP headers.

+ +

@consumer.request(:get, ‘/people’, @token, { :scheme => :query_string })
+ @consumer.request(:post, ‘/people’, @token, {}, @person.to_xml, { ‘Content-Type’ => ‘application/xml’ })

+ + +
+
+
+ + +
+ + + + +
+
+
+
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+
+
# File 'lib/oauth/consumer.rb', line 207
+
+def request(http_method, path, token = nil, request_options = {}, *arguments)
+  unless %r{^/} =~ path
+    @http = create_http(path)
+    _uri = URI.parse(path)
+    path = "#{_uri.path}#{"?#{_uri.query}" if _uri.query}"
+  end
+
+  # override the request with your own, this is useful for file uploads which Net::HTTP does not do
+  req = create_signed_request(http_method, path, token, request_options, *arguments)
+  return if block_given? && (yield(req) == :done)
+
+  rsp = http.request(req)
+  # check for an error reported by the Problem Reporting extension
+  # (https://wiki.oauth.net/ProblemReporting)
+  # note: a 200 may actually be an error; check for an oauth_problem key to be sure
+  if !(headers = rsp.to_hash["www-authenticate"]).nil? &&
+      (h = headers.grep(/^OAuth /)).any? &&
+      h.first.include?("oauth_problem")
+
+    # puts "Header: #{h.first}"
+
+    # TODO: doesn't handle broken responses from api.login.yahoo.com
+    # remove debug code when done
+    params = OAuth::Helper.parse_header(h.first)
+
+    # puts "Params: #{params.inspect}"
+    # puts "Body: #{rsp.body}"
+
+    raise OAuth::Problem.new(params.delete("oauth_problem"), rsp, params)
+  end
+
+  rsp
+end
+
+
+ +
+

+ + #request_endpointObject + + + + + +

+ + + + +
+
+
+
+302
+303
+304
+305
+306
+
+
# File 'lib/oauth/consumer.rb', line 302
+
+def request_endpoint
+  return if @options[:request_endpoint].nil?
+
+  @options[:request_endpoint].to_s
+end
+
+
+ +
+

+ + #request_token_pathObject + + + + + +

+ + + + +
+
+
+
+312
+313
+314
+
+
# File 'lib/oauth/consumer.rb', line 312
+
+def request_token_path
+  @options[:request_token_path]
+end
+
+
+ +
+

+ + #request_token_urlObject + + + + + +

+
+

TODO: this is ugly, rewrite

+ + +
+
+
+ + +
+ + + + +
+
+
+
+329
+330
+331
+
+
# File 'lib/oauth/consumer.rb', line 329
+
+def request_token_url
+  @options[:request_token_url] || (site + request_token_path)
+end
+
+
+ +
+

+ + #request_token_url?Boolean + + + + + +

+
+ + + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+333
+334
+335
+
+
# File 'lib/oauth/consumer.rb', line 333
+
+def request_token_url?
+  @options.key?(:request_token_url)
+end
+
+
+ +
+

+ + #schemeObject + + + + + +

+ + + + +
+
+
+
+308
+309
+310
+
+
# File 'lib/oauth/consumer.rb', line 308
+
+def scheme
+  @options[:scheme]
+end
+
+
+ +
+

+ + #sign!(request, token = nil, request_options = {}) ⇒ Object + + + + + +

+
+

Sign the Request object. Use this if you have an externally generated http request object you want to sign.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+289
+290
+291
+
+
# File 'lib/oauth/consumer.rb', line 289
+
+def sign!(request, token = nil, request_options = {})
+  request.oauth!(http, self, token, options.merge(request_options))
+end
+
+
+ +
+

+ + #signature_base_string(request, token = nil, request_options = {}) ⇒ Object + + + + + +

+
+

Return the signature_base_string

+ + +
+
+
+ + +
+ + + + +
+
+
+
+294
+295
+296
+
+
# File 'lib/oauth/consumer.rb', line 294
+
+def signature_base_string(request, token = nil, request_options = {})
+  request.signature_base_string(http, self, token, options.merge(request_options))
+end
+
+
+ +
+

+ + #token_request(http_method, path, token = nil, request_options = {}, *arguments) ⇒ Object + + + + + +

+
+

Creates a request and parses the result as url_encoded. This is used internally for the RequestToken and AccessToken requests.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+
+
# File 'lib/oauth/consumer.rb', line 250
+
+def token_request(http_method, path, token = nil, request_options = {}, *arguments)
+  request_options[:token_request] ||= true
+  response = request(http_method, path, token, request_options, *arguments)
+  case response.code.to_i
+
+  when (200..299)
+    if block_given?
+      yield response.body
+    else
+      # symbolize keys
+      # TODO this could be considered unexpected behavior; symbols or not?
+      # TODO this also drops subsequent values from multi-valued keys
+      CGI.parse(response.body).each_with_object({}) do |(k, v), h|
+        h[k.strip.to_sym] = v.first
+        h[k.strip] = v.first
+      end
+    end
+  when (300..399)
+    # Parse redirect to follow
+    uri = URI.parse(response["location"])
+    our_uri = URI.parse(site)
+
+    # Guard against infinite redirects
+    response.error! if uri.path == path && our_uri.host == uri.host
+
+    if uri.path == path && our_uri.host != uri.host
+      options[:site] = "#{uri.scheme}://#{uri.host}"
+      @http = create_http
+    end
+
+    token_request(http_method, uri.path, token, request_options, arguments)
+  when (400..499)
+    raise OAuth::Unauthorized, response
+  else
+    response.error!
+  end
+end
+
+
+ +
+

+ + #uri(custom_uri = nil) ⇒ Object + + + + + +

+
+

Contains the root URI for this site

+ + +
+
+
+ + +
+ + + + +
+
+
+
+136
+137
+138
+139
+140
+141
+142
+143
+
+
# File 'lib/oauth/consumer.rb', line 136
+
+def uri(custom_uri = nil)
+  if custom_uri
+    @uri = custom_uri
+    @http = create_http # yike, oh well. less intrusive this way
+  else # if no custom passed, we use existing, which, if unset, is set to site uri
+    @uri ||= URI.parse(site)
+  end
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/ConsumerToken.html b/docs/OAuth/ConsumerToken.html new file mode 100644 index 00000000..07067257 --- /dev/null +++ b/docs/OAuth/ConsumerToken.html @@ -0,0 +1,671 @@ + + + + + + + Class: OAuth::ConsumerToken + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::ConsumerToken + + + +

+
+ +
+
Inherits:
+
+ Token + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/tokens/consumer_token.rb
+
+ +
+ +

Overview

+
+

Superclass for tokens used by OAuth Clients

+ + +
+
+
+ + +
+

Direct Known Subclasses

+

AccessToken, RequestToken

+
+ + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #consumer ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute consumer.

    +
    + +
  • + + +
  • + + + #params ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute params.

    +
    + +
  • + + +
  • + + + #response ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute response.

    +
    + +
  • + + +
+ + + + + +

Attributes inherited from Token

+

#secret, #token

+ + + +

+ Class Method Summary + collapse +

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Token

+

#to_query

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +
+

+ + #initialize(consumer, token = "", secret = "") ⇒ ConsumerToken + + + + + +

+
+

Returns a new instance of ConsumerToken.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+15
+16
+17
+18
+19
+
+
# File 'lib/oauth/tokens/consumer_token.rb', line 15
+
+def initialize(consumer, token = "", secret = "")
+  super(token, secret)
+  @consumer = consumer
+  @params = {}
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #consumerObject + + + + + +

+
+

Returns the value of attribute consumer.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+6
+7
+8
+
+
# File 'lib/oauth/tokens/consumer_token.rb', line 6
+
+def consumer
+  @consumer
+end
+
+
+ + + +
+

+ + #paramsObject + + + + + +

+
+

Returns the value of attribute params.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+6
+7
+8
+
+
# File 'lib/oauth/tokens/consumer_token.rb', line 6
+
+def params
+  @params
+end
+
+
+ + + +
+

+ + #responseObject (readonly) + + + + + +

+
+

Returns the value of attribute response.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+7
+8
+9
+
+
# File 'lib/oauth/tokens/consumer_token.rb', line 7
+
+def response
+  @response
+end
+
+
+ +
+ + +
+

Class Method Details

+ + +
+

+ + .from_hash(consumer, hash) ⇒ Object + + + + + +

+ + + + +
+
+
+
+9
+10
+11
+12
+13
+
+
# File 'lib/oauth/tokens/consumer_token.rb', line 9
+
+def self.from_hash(consumer, hash)
+  token = new(consumer, hash[:oauth_token], hash[:oauth_token_secret])
+  token.params = hash
+  token
+end
+
+
+ +
+ +
+

Instance Method Details

+ + +
+

+ + #request(http_method, path, *arguments) ⇒ Object + + + + + +

+
+

Make a signed request using given http_method to the path

+ +

@token.request(:get, ‘/people’)
+ @token.request(:post, ‘/people’, @person.to_xml, { ‘Content-Type’ => ‘application/xml’ })

+ + +
+
+
+ + +
+ + + + +
+
+
+
+26
+27
+28
+
+
# File 'lib/oauth/tokens/consumer_token.rb', line 26
+
+def request(http_method, path, *arguments)
+  @response = consumer.request(http_method, path, self, {}, *arguments)
+end
+
+
+ +
+

+ + #sign!(request, options = {}) ⇒ Object + + + + + +

+
+

Sign a request generated elsewhere using Net:HTTP::Post.new or friends

+ + +
+
+
+ + +
+ + + + +
+
+
+
+31
+32
+33
+
+
# File 'lib/oauth/tokens/consumer_token.rb', line 31
+
+def sign!(request, options = {})
+  consumer.sign!(request, self, options)
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Error.html b/docs/OAuth/Error.html new file mode 100644 index 00000000..4f98ee7c --- /dev/null +++ b/docs/OAuth/Error.html @@ -0,0 +1,128 @@ + + + + + + + Exception: OAuth::Error + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Exception: OAuth::Error + + + +

+
+ +
+
Inherits:
+
+ StandardError + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/errors/error.rb
+
+ +
+ +
+

Direct Known Subclasses

+

Unauthorized

+
+ + + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Helper.html b/docs/OAuth/Helper.html new file mode 100644 index 00000000..a2ce0b90 --- /dev/null +++ b/docs/OAuth/Helper.html @@ -0,0 +1,798 @@ + + + + + + + Module: OAuth::Helper + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::Helper + + + +

+
+ + + + + + + + + +
+
Included in:
+
Net::HTTPGenericRequest, Client::Helper, RequestProxy::Base, Server, Signature::Base, Token
+
+ + + +
+
Defined in:
+
lib/oauth/helper.rb
+
+ +
+ + + + + + + + + +

+ Class Method Summary + collapse +

+ + + + + + +
+

Class Method Details

+ + +
+

+ + ._escape(string) ⇒ Object + + + + + +

+ + + + +
+
+
+
+20
+21
+22
+
+
# File 'lib/oauth/helper.rb', line 20
+
+def _escape(string)
+  URI::DEFAULT_PARSER.escape(string, OAuth::RESERVED_CHARACTERS)
+end
+
+
+ +
+

+ + .escape(value) ⇒ Object + + + + + +

+
+

Escape +value+ by URL encoding all non-reserved character.

+ +

See Also: core spec version 1.0, section 5.1[http://oauth.net/core/1.0#rfc.section.5.1]

+ + +
+
+
+ + +
+ + + + +
+
+
+
+14
+15
+16
+17
+18
+
+
# File 'lib/oauth/helper.rb', line 14
+
+def escape(value)
+  _escape(value.to_s.to_str)
+rescue ArgumentError
+  _escape(value.to_s.to_str.force_encoding(Encoding::UTF_8))
+end
+
+
+ +
+

+ + .generate_key(size = 32) ⇒ Object + + + + Also known as: + generate_nonce + + + + +

+
+

Generate a random key of up to +size+ bytes. The value returned is Base64 encoded with non-word
+characters removed.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+30
+31
+32
+
+
# File 'lib/oauth/helper.rb', line 30
+
+def generate_key(size = 32)
+  Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/, "")
+end
+
+
+ +
+

+ + .generate_timestampObject + + + + + +

+
+

:nodoc:

+ + +
+
+
+ + +
+ + + + +
+
+
+
+36
+37
+38
+
+
# File 'lib/oauth/helper.rb', line 36
+
+def generate_timestamp # :nodoc:
+  Time.now.to_i.to_s
+end
+
+
+ +
+

+ + .normalize(params) ⇒ Object + + + + + +

+
+

Normalize a +Hash+ of parameter values. Parameters are sorted by name, using lexicographical
+byte value ordering. If two or more parameters share the same name, they are sorted by their value.
+Parameters are concatenated in their sorted order into a single string. For each parameter, the name
+is separated from the corresponding value by an “=” character, even if the value is empty. Each
+name-value pair is separated by an “&” character.

+ +

See Also: core spec version 1.0, section 9.1.1[http://oauth.net/core/1.0#rfc.section.9.1.1]

+ + +
+
+
+ + +
+ + + + +
+
+
+
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+
+
# File 'lib/oauth/helper.rb', line 47
+
+def normalize(params)
+  params.sort.map do |k, values|
+    case values
+    when Array
+      # make sure the array has an element so we don't lose the key
+      values << nil if values.empty?
+      # multiple values were provided for a single key
+      if values[0].is_a?(Hash)
+        normalize_nested_query(values, k)
+      else
+        values.sort.collect do |v|
+          [escape(k), escape(v)].join("=")
+        end
+      end
+    when Hash
+      normalize_nested_query(values, k)
+    else
+      [escape(k), escape(values)].join("=")
+    end
+  end * "&"
+end
+
+
+ +
+

+ + .normalize_nested_query(value, prefix = nil) ⇒ Object + + + + + +

+
+

Returns a string representation of the Hash like in URL query string
+build_nested_query(=> {:level_2 => [‘value_1’,’value_2’]}, ‘prefix’))
+ #=> [“prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_1”, “prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_2”]

+ + +
+
+
+ + +
+ + + + +
+
+
+
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+
+
# File 'lib/oauth/helper.rb', line 72
+
+def normalize_nested_query(value, prefix = nil)
+  case value
+  when Array
+    value.map do |v|
+      normalize_nested_query(v, "#{prefix}[]")
+    end.flatten.sort
+  when Hash
+    value.map do |k, v|
+      normalize_nested_query(v, prefix ? "#{prefix}[#{k}]" : k)
+    end.flatten.sort
+  else
+    [escape(prefix), escape(value)].join("=")
+  end
+end
+
+
+ +
+

+ + .parse_header(header) ⇒ Object + + + + + +

+
+

Parse an Authorization / WWW-Authenticate header into a hash. Takes care of unescaping and
+removing surrounding quotes. Raises a OAuth::Problem if the header is not parsable into a
+valid hash. Does not validate the keys or values.

+ +

hash = parse_header(headers[‘Authorization’] || headers[‘WWW-Authenticate’])
+ hash[‘oauth_timestamp’]
+ #=>”1234567890”

+ + +
+
+
+ +

Raises:

+ + +
+ + + + +
+
+
+
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+
+
# File 'lib/oauth/helper.rb', line 95
+
+def parse_header(header)
+  # decompose
+  params = header[6, header.length].split(/[,=&]/)
+
+  # odd number of arguments - must be a malformed header.
+  raise OAuth::Problem, "Invalid authorization header" if params.size.odd?
+
+  params.map! do |v|
+    # strip and unescape
+    val = unescape(v.strip)
+    # strip quotes
+    val.sub(/^"(.*)"$/, '\1')
+  end
+
+  # convert into a Hash
+  Hash[*params.flatten]
+end
+
+
+ +
+

+ + .stringify_keys(hash) ⇒ Object + + + + + +

+ + + + +
+
+
+
+113
+114
+115
+116
+117
+118
+119
+
+
# File 'lib/oauth/helper.rb', line 113
+
+def stringify_keys(hash)
+  new_h = {}
+  hash.each do |k, v|
+    new_h[k.to_s] = v.is_a?(Hash) ? stringify_keys(v) : v
+  end
+  new_h
+end
+
+
+ +
+

+ + .unescape(value) ⇒ Object + + + + + +

+ + + + +
+
+
+
+24
+25
+26
+
+
# File 'lib/oauth/helper.rb', line 24
+
+def unescape(value)
+  URI::DEFAULT_PARSER.unescape(value.gsub("+", "%2B"))
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/OAuthTestHelper.html b/docs/OAuth/OAuthTestHelper.html new file mode 100644 index 00000000..2b669255 --- /dev/null +++ b/docs/OAuth/OAuthTestHelper.html @@ -0,0 +1,250 @@ + + + + + + + Module: OAuth::OAuthTestHelper + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::OAuthTestHelper + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/oauth_test_helper.rb
+
+ +
+ + + + + + + + + +

+ Instance Method Summary + collapse +

+ + + + + + +
+

Instance Method Details

+ + +
+

+ + #mock_incoming_request_with_authorize_header(request) ⇒ Object + + + + + +

+ + + + +
+
+
+
+17
+18
+19
+20
+21
+22
+23
+24
+25
+
+
# File 'lib/oauth/oauth_test_helper.rb', line 17
+
+def mock_incoming_request_with_authorize_header(request)
+  incoming = ActionController::TestRequest.new
+  incoming.request_uri = request.path
+  incoming.host = request.uri.host
+  incoming.env["HTTP_AUTHORIZATION"] = request.to_auth_string
+  incoming.env["SERVER_PORT"] = request.uri.port
+  incoming.env["REQUEST_METHOD"] = request.http_method
+  incoming
+end
+
+
+ +
+

+ + #mock_incoming_request_with_query(request) ⇒ Object + + + + + +

+ + + + +
+
+
+
+8
+9
+10
+11
+12
+13
+14
+15
+
+
# File 'lib/oauth/oauth_test_helper.rb', line 8
+
+def mock_incoming_request_with_query(request)
+  incoming = ActionController::TestRequest.new(request.to_hash)
+  incoming.request_uri = request.path
+  incoming.host = request.uri.host
+  incoming.env["SERVER_PORT"] = request.uri.port
+  incoming.env["REQUEST_METHOD"] = request.http_method
+  incoming
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Problem.html b/docs/OAuth/Problem.html new file mode 100644 index 00000000..ea4a1264 --- /dev/null +++ b/docs/OAuth/Problem.html @@ -0,0 +1,438 @@ + + + + + + + Exception: OAuth::Problem + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Exception: OAuth::Problem + + + +

+
+ +
+
Inherits:
+
+ Unauthorized + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/errors/problem.rb
+
+ +
+ + + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #params ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute params.

    +
    + +
  • + + +
  • + + + #problem ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute problem.

    +
    + +
  • + + +
+ + + + + +

Attributes inherited from Unauthorized

+

#request

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + + + + + +
+

Constructor Details

+ +
+

+ + #initialize(problem, request = nil, params = {}) ⇒ Problem + + + + + +

+
+

Returns a new instance of Problem.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+7
+8
+9
+10
+11
+
+
# File 'lib/oauth/errors/problem.rb', line 7
+
+def initialize(problem, request = nil, params = {})
+  super(request)
+  @problem = problem
+  @params = params
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #paramsObject (readonly) + + + + + +

+
+

Returns the value of attribute params.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+5
+6
+7
+
+
# File 'lib/oauth/errors/problem.rb', line 5
+
+def params
+  @params
+end
+
+
+ + + +
+

+ + #problemObject (readonly) + + + + + +

+
+

Returns the value of attribute problem.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+5
+6
+7
+
+
# File 'lib/oauth/errors/problem.rb', line 5
+
+def problem
+  @problem
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #to_sObject + + + + + +

+ + + + +
+
+
+
+13
+14
+15
+
+
# File 'lib/oauth/errors/problem.rb', line 13
+
+def to_s
+  problem
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy.html b/docs/OAuth/RequestProxy.html new file mode 100644 index 00000000..aa8b538e --- /dev/null +++ b/docs/OAuth/RequestProxy.html @@ -0,0 +1,299 @@ + + + + + + + Module: OAuth::RequestProxy + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::RequestProxy + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy.rb,
+ lib/oauth/request_proxy/base.rb,
lib/oauth/request_proxy/net_http.rb,
lib/oauth/request_proxy/curb_request.rb,
lib/oauth/request_proxy/mock_request.rb,
lib/oauth/request_proxy/rack_request.rb,
lib/oauth/request_proxy/jabber_request.rb,
lib/oauth/request_proxy/em_http_request.rb,
lib/oauth/request_proxy/typhoeus_request.rb,
lib/oauth/request_proxy/rest_client_request.rb,
lib/oauth/request_proxy/action_dispatch_request.rb,
lib/oauth/request_proxy/action_controller_request.rb
+
+
+ +
+ +

Defined Under Namespace

+

+ + + Modules: Curl, EventMachine, Net, RestClient, Typhoeus + + + + Classes: ActionControllerRequest, ActionDispatchRequest, Base, JabberRequest, MockRequest, RackRequest, UnknownRequestType + + +

+ + + + + + + + +

+ Class Method Summary + collapse +

+ + + + + + +
+

Class Method Details

+ + +
+

+ + .available_proxiesObject + + + + + +

+
+

:nodoc:

+ + +
+
+
+ + +
+ + + + +
+
+
+
+5
+6
+7
+
+
# File 'lib/oauth/request_proxy.rb', line 5
+
+def self.available_proxies # :nodoc:
+  @available_proxies ||= {}
+end
+
+
+ +
+

+ + .proxy(request, options = {}) ⇒ Object + + + + + +

+
+ + + +
+
+
+ +

Raises:

+ + +
+ + + + +
+
+
+
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+
+
# File 'lib/oauth/request_proxy.rb', line 9
+
+def self.proxy(request, options = {})
+  return request if request.is_a?(OAuth::RequestProxy::Base)
+
+  klass = available_proxies[request.class]
+
+  # Search for possible superclass matches.
+  if klass.nil?
+    request_parent = available_proxies.keys.find { |rc| request.is_a?(rc) }
+    klass = available_proxies[request_parent]
+  end
+
+  raise UnknownRequestType, request.class.to_s unless klass
+
+  klass.new(request, options)
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/ActionControllerRequest.html b/docs/OAuth/RequestProxy/ActionControllerRequest.html new file mode 100644 index 00000000..c4aad5bc --- /dev/null +++ b/docs/OAuth/RequestProxy/ActionControllerRequest.html @@ -0,0 +1,513 @@ + + + + + + + Class: OAuth::RequestProxy::ActionControllerRequest + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::ActionControllerRequest + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/action_controller_request.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #normalized_uri, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #methodObject + + + + + +

+ + + + +
+
+
+
+14
+15
+16
+
+
# File 'lib/oauth/request_proxy/action_controller_request.rb', line 14
+
+def method
+  request.method.to_s.upcase
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+22
+23
+24
+25
+26
+27
+28
+29
+30
+
+
# File 'lib/oauth/request_proxy/action_controller_request.rb', line 22
+
+def parameters
+  if options[:clobber_request]
+    options[:parameters] || {}
+  else
+    params = request_params.merge(query_params).merge(header_params)
+    params.stringify_keys! if params.respond_to?(:stringify_keys!)
+    params.merge(options[:parameters] || {})
+  end
+end
+
+
+ +
+

+ + #parameters_for_signatureObject + + + + + +

+
+

Override from OAuth::RequestProxy::Base to avoid round-trip
+conversion to Hash or Array and thus preserve the original
+parameter names

+ + +
+
+
+ + +
+ + + + +
+
+
+
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+
+
# File 'lib/oauth/request_proxy/action_controller_request.rb', line 35
+
+def parameters_for_signature
+  params = []
+  params << options[:parameters].to_query if options[:parameters]
+
+  unless options[:clobber_request]
+    params << header_params.to_query
+    params << request.query_string unless query_string_blank?
+
+    params << request.raw_post if raw_post_signature?
+  end
+
+  params
+    .join("&").split("&")
+    .reject { |s| s.match(/\A\s*\z/) }
+    .map { |p| p.split("=").map { |esc| CGI.unescape(esc) } }
+    .reject { |kv| kv[0] == "oauth_signature" }
+end
+
+
+ +
+

+ + #raw_post_signature?Boolean + + + + + +

+
+ + + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+53
+54
+55
+
+
# File 'lib/oauth/request_proxy/action_controller_request.rb', line 53
+
+def raw_post_signature?
+  (request.post? || request.put?) && request.content_type.to_s.downcase.start_with?("application/x-www-form-urlencoded")
+end
+
+
+ +
+

+ + #uriObject + + + + + +

+ + + + +
+
+
+
+18
+19
+20
+
+
# File 'lib/oauth/request_proxy/action_controller_request.rb', line 18
+
+def uri
+  request.url
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/ActionDispatchRequest.html b/docs/OAuth/RequestProxy/ActionDispatchRequest.html new file mode 100644 index 00000000..a5895234 --- /dev/null +++ b/docs/OAuth/RequestProxy/ActionDispatchRequest.html @@ -0,0 +1,169 @@ + + + + + + + Class: OAuth::RequestProxy::ActionDispatchRequest + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::ActionDispatchRequest + + + +

+
+ +
+
Inherits:
+
+ RackRequest + +
    +
  • Object
  • + + + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/action_dispatch_request.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + + + + + + + +

Method Summary

+ +

Methods inherited from RackRequest

+

#method, #parameters, #signature, #uri

+ + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #normalized_uri, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, #parameters, #parameters_for_signature, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/Base.html b/docs/OAuth/RequestProxy/Base.html new file mode 100644 index 00000000..48160cea --- /dev/null +++ b/docs/OAuth/RequestProxy/Base.html @@ -0,0 +1,1951 @@ + + + + + + + Class: OAuth::RequestProxy::Base + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::Base + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + +
+
Includes:
+
Helper
+
+ + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/base.rb
+
+ +
+ + + + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #options ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute options.

    +
    + +
  • + + +
  • + + + #request ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute request.

    +
    + +
  • + + +
  • + + + #unsigned_parameters ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute unsigned_parameters.

    +
    + +
  • + + +
+ + + + + +

+ Class Method Summary + collapse +

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +
+

+ + #initialize(request, options = {}) ⇒ Base + + + + + +

+
+

Returns a new instance of Base.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+17
+18
+19
+20
+21
+
+
# File 'lib/oauth/request_proxy/base.rb', line 17
+
+def initialize(request, options = {})
+  @request = request
+  @unsigned_parameters = (options[:unsigned_parameters] || []).map(&:to_s)
+  @options = options
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #optionsObject + + + + + +

+
+

Returns the value of attribute options.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+15
+16
+17
+
+
# File 'lib/oauth/request_proxy/base.rb', line 15
+
+def options
+  @options
+end
+
+
+ + + +
+

+ + #requestObject + + + + + +

+
+

Returns the value of attribute request.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+15
+16
+17
+
+
# File 'lib/oauth/request_proxy/base.rb', line 15
+
+def request
+  @request
+end
+
+
+ + + +
+

+ + #unsigned_parametersObject + + + + + +

+
+

Returns the value of attribute unsigned_parameters.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+15
+16
+17
+
+
# File 'lib/oauth/request_proxy/base.rb', line 15
+
+def unsigned_parameters
+  @unsigned_parameters
+end
+
+
+ +
+ + +
+

Class Method Details

+ + +
+

+ + .proxies(klass) ⇒ Object + + + + + +

+ + + + +
+
+
+
+11
+12
+13
+
+
# File 'lib/oauth/request_proxy/base.rb', line 11
+
+def self.proxies(klass)
+  OAuth::RequestProxy.available_proxies[klass] = self
+end
+
+
+ +
+ +
+

Instance Method Details

+ + +
+

+ + #non_oauth_parametersObject + + + + + +

+ + + + +
+
+
+
+89
+90
+91
+
+
# File 'lib/oauth/request_proxy/base.rb', line 89
+
+def non_oauth_parameters
+  parameters.select { |k, _v| !OAuth::PARAMETERS.include?(k) }
+end
+
+
+ +
+

+ + #normalized_parametersObject + + + + + +

+
+

See 9.1.1. in specs Normalize Request Parameters

+ + +
+
+
+ + +
+ + + + +
+
+
+
+104
+105
+106
+
+
# File 'lib/oauth/request_proxy/base.rb', line 104
+
+def normalized_parameters
+  normalize(parameters_for_signature)
+end
+
+
+ +
+

+ + #normalized_uriObject + + + + + +

+
+

See 9.1.2 in specs

+ + +
+
+
+ + +
+ + + + +
+
+
+
+98
+99
+100
+101
+
+
# File 'lib/oauth/request_proxy/base.rb', line 98
+
+def normalized_uri
+  u = URI.parse(uri)
+  "#{u.scheme.downcase}://#{u.host.downcase}#{":#{u.port}" if (u.scheme.casecmp("http").zero? && u.port != 80) || (u.scheme.casecmp("https").zero? && u.port != 443)}#{(u.path && u.path != "") ? u.path : "/"}"
+end
+
+
+ +
+

+ + #oauth_callbackObject + + + + + +

+
+

OAuth parameters

+ + +
+
+
+ + +
+ + + + +
+
+
+
+25
+26
+27
+
+
# File 'lib/oauth/request_proxy/base.rb', line 25
+
+def oauth_callback
+  parameters["oauth_callback"]
+end
+
+
+ +
+

+ + #oauth_consumer_keyObject + + + + Also known as: + consumer_key + + + + +

+ + + + +
+
+
+
+29
+30
+31
+
+
# File 'lib/oauth/request_proxy/base.rb', line 29
+
+def oauth_consumer_key
+  parameters["oauth_consumer_key"]
+end
+
+
+ +
+

+ + #oauth_header(options = {}) ⇒ Object + + + + + +

+
+

Authorization header for OAuth

+ + +
+
+
+ + +
+ + + + +
+
+
+
+145
+146
+147
+148
+149
+150
+
+
# File 'lib/oauth/request_proxy/base.rb', line 145
+
+def oauth_header(options = {})
+  header_params_str = oauth_parameters.map { |k, v| "#{k}=\"#{escape(v)}\"" }.join(", ")
+
+  realm = "realm=\"#{options[:realm]}\", " if options[:realm]
+  "OAuth #{realm}#{header_params_str}"
+end
+
+
+ +
+

+ + #oauth_nonceObject + + + + Also known as: + nonce + + + + +

+ + + + +
+
+
+
+33
+34
+35
+
+
# File 'lib/oauth/request_proxy/base.rb', line 33
+
+def oauth_nonce
+  parameters["oauth_nonce"]
+end
+
+
+ +
+

+ + #oauth_parametersObject + + + + + +

+ + + + +
+
+
+
+85
+86
+87
+
+
# File 'lib/oauth/request_proxy/base.rb', line 85
+
+def oauth_parameters
+  parameters.select { |k, v| OAuth::PARAMETERS.include?(k) && !v.nil? && v != "" }
+end
+
+
+ +
+

+ + #oauth_signatureObject + + + + Also known as: + signature + + + + +

+ + + + +
+
+
+
+37
+38
+39
+40
+
+
# File 'lib/oauth/request_proxy/base.rb', line 37
+
+def oauth_signature
+  # TODO: can this be nil?
+  [parameters["oauth_signature"]].flatten.first || ""
+end
+
+
+ +
+

+ + #oauth_signature_methodObject + + + + Also known as: + signature_method + + + + +

+ + + + +
+
+
+
+42
+43
+44
+45
+46
+47
+48
+49
+
+
# File 'lib/oauth/request_proxy/base.rb', line 42
+
+def oauth_signature_method
+  case parameters["oauth_signature_method"]
+  when Array
+    parameters["oauth_signature_method"].first
+  else
+    parameters["oauth_signature_method"]
+  end
+end
+
+
+ +
+

+ + #oauth_timestampObject + + + + Also known as: + timestamp + + + + +

+ + + + +
+
+
+
+51
+52
+53
+
+
# File 'lib/oauth/request_proxy/base.rb', line 51
+
+def oauth_timestamp
+  parameters["oauth_timestamp"]
+end
+
+
+ +
+

+ + #oauth_tokenObject + + + + Also known as: + token + + + + +

+ + + + +
+
+
+
+55
+56
+57
+
+
# File 'lib/oauth/request_proxy/base.rb', line 55
+
+def oauth_token
+  parameters["oauth_token"]
+end
+
+
+ +
+

+ + #oauth_verifierObject + + + + + +

+ + + + +
+
+
+
+59
+60
+61
+
+
# File 'lib/oauth/request_proxy/base.rb', line 59
+
+def oauth_verifier
+  parameters["oauth_verifier"]
+end
+
+
+ +
+

+ + #oauth_versionObject + + + + + +

+ + + + +
+
+
+
+63
+64
+65
+
+
# File 'lib/oauth/request_proxy/base.rb', line 63
+
+def oauth_version
+  parameters["oauth_version"]
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+
+

Parameter accessors

+ + +
+
+
+ +

Raises:

+
    + +
  • + + + (NotImplementedError) + + + +
  • + +
+ +
+ + + + +
+
+
+
+77
+78
+79
+
+
# File 'lib/oauth/request_proxy/base.rb', line 77
+
+def parameters
+  raise NotImplementedError, "Must be implemented by subclasses"
+end
+
+
+ +
+

+ + #parameters_for_signatureObject + + + + + +

+ + + + +
+
+
+
+81
+82
+83
+
+
# File 'lib/oauth/request_proxy/base.rb', line 81
+
+def parameters_for_signature
+  parameters.select { |k, _v| !signature_and_unsigned_parameters.include?(k) }
+end
+
+
+ +
+

+ + #query_string_blank?Boolean + + + + + +

+
+ + + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+152
+153
+154
+155
+156
+157
+158
+
+
# File 'lib/oauth/request_proxy/base.rb', line 152
+
+def query_string_blank?
+  if (uri = request.env["REQUEST_URI"])
+    uri.split("?", 2)[1].nil?
+  else
+    request.query_string.match(/\A\s*\z/)
+  end
+end
+
+
+ +
+

+ + #sign(options = {}) ⇒ Object + + + + + +

+ + + + +
+
+
+
+108
+109
+110
+
+
# File 'lib/oauth/request_proxy/base.rb', line 108
+
+def sign(options = {})
+  OAuth::Signature.sign(self, options)
+end
+
+
+ +
+

+ + #sign!(options = {}) ⇒ Object + + + + + +

+ + + + +
+
+
+
+112
+113
+114
+115
+116
+
+
# File 'lib/oauth/request_proxy/base.rb', line 112
+
+def sign!(options = {})
+  parameters["oauth_signature"] = sign(options)
+  @signed = true
+  signature
+end
+
+
+ +
+

+ + #signature_and_unsigned_parametersObject + + + + + +

+ + + + +
+
+
+
+93
+94
+95
+
+
# File 'lib/oauth/request_proxy/base.rb', line 93
+
+def signature_and_unsigned_parameters
+  unsigned_parameters + ["oauth_signature"]
+end
+
+
+ +
+

+ + #signature_base_stringObject + + + + + +

+
+

See 9.1 in specs

+ + +
+
+
+ + +
+ + + + +
+
+
+
+119
+120
+121
+122
+
+
# File 'lib/oauth/request_proxy/base.rb', line 119
+
+def signature_base_string
+  base = [method, normalized_uri, normalized_parameters]
+  base.map { |v| escape(v) }.join("&")
+end
+
+
+ +
+

+ + #signed?Boolean + + + + + +

+
+

Has this request been signed yet?

+ + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+125
+126
+127
+
+
# File 'lib/oauth/request_proxy/base.rb', line 125
+
+def signed?
+  @signed
+end
+
+
+ +
+

+ + #signed_uri(with_oauth: true) ⇒ Object + + + + + +

+
+

URI, including OAuth parameters

+ + +
+
+
+ + +
+ + + + +
+
+
+
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+
+
# File 'lib/oauth/request_proxy/base.rb', line 130
+
+def signed_uri(with_oauth: true)
+  if signed?
+    params = if with_oauth
+      parameters
+    else
+      non_oauth_parameters
+    end
+
+    [uri, normalize(params)].join("?")
+  else
+    warn("This request has not yet been signed!")
+  end
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/Curl.html b/docs/OAuth/RequestProxy/Curl.html new file mode 100644 index 00000000..e31855e3 --- /dev/null +++ b/docs/OAuth/RequestProxy/Curl.html @@ -0,0 +1,115 @@ + + + + + + + Module: OAuth::RequestProxy::Curl + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::RequestProxy::Curl + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/curb_request.rb
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: Easy + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/Curl/Easy.html b/docs/OAuth/RequestProxy/Curl/Easy.html new file mode 100644 index 00000000..954cd148 --- /dev/null +++ b/docs/OAuth/RequestProxy/Curl/Easy.html @@ -0,0 +1,340 @@ + + + + + + + Class: OAuth::RequestProxy::Curl::Easy + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::Curl::Easy + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/curb_request.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #normalized_uri, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, #parameters_for_signature, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #methodObject + + + + + +

+ + + + +
+
+
+
+22
+23
+24
+
+
# File 'lib/oauth/request_proxy/curb_request.rb', line 22
+
+def method
+  nil
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+30
+31
+32
+33
+34
+35
+36
+
+
# File 'lib/oauth/request_proxy/curb_request.rb', line 30
+
+def parameters
+  if options[:clobber_request]
+    options[:parameters]
+  else
+    post_parameters.merge(query_parameters).merge(options[:parameters] || {})
+  end
+end
+
+
+ +
+

+ + #uriObject + + + + + +

+ + + + +
+
+
+
+26
+27
+28
+
+
# File 'lib/oauth/request_proxy/curb_request.rb', line 26
+
+def uri
+  options[:uri].to_s
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/EventMachine.html b/docs/OAuth/RequestProxy/EventMachine.html new file mode 100644 index 00000000..c89c5da6 --- /dev/null +++ b/docs/OAuth/RequestProxy/EventMachine.html @@ -0,0 +1,115 @@ + + + + + + + Module: OAuth::RequestProxy::EventMachine + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::RequestProxy::EventMachine + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/em_http_request.rb
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: HttpRequest + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/EventMachine/HttpRequest.html b/docs/OAuth/RequestProxy/EventMachine/HttpRequest.html new file mode 100644 index 00000000..b372d87f --- /dev/null +++ b/docs/OAuth/RequestProxy/EventMachine/HttpRequest.html @@ -0,0 +1,350 @@ + + + + + + + Class: OAuth::RequestProxy::EventMachine::HttpRequest + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::EventMachine::HttpRequest + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/em_http_request.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #normalized_uri, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, #parameters_for_signature, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #methodObject + + + + + +

+
+

Request in this con

+ + +
+
+
+ + +
+ + + + +
+
+
+
+19
+20
+21
+
+
# File 'lib/oauth/request_proxy/em_http_request.rb', line 19
+
+def method
+  request.req[:method]
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+27
+28
+29
+30
+31
+32
+33
+
+
# File 'lib/oauth/request_proxy/em_http_request.rb', line 27
+
+def parameters
+  if options[:clobber_request]
+    options[:parameters]
+  else
+    all_parameters
+  end
+end
+
+
+ +
+

+ + #uriObject + + + + + +

+ + + + +
+
+
+
+23
+24
+25
+
+
# File 'lib/oauth/request_proxy/em_http_request.rb', line 23
+
+def uri
+  request.conn.normalize.to_s
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/JabberRequest.html b/docs/OAuth/RequestProxy/JabberRequest.html new file mode 100644 index 00000000..f456ef6b --- /dev/null +++ b/docs/OAuth/RequestProxy/JabberRequest.html @@ -0,0 +1,427 @@ + + + + + + + Class: OAuth::RequestProxy::JabberRequest + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::JabberRequest + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/jabber_request.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, #parameters_for_signature, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #methodObject + + + + + +

+ + + + +
+
+
+
+38
+39
+40
+
+
# File 'lib/oauth/request_proxy/jabber_request.rb', line 38
+
+def method
+  @request.name
+end
+
+
+ +
+

+ + #normalized_uriObject + + + + + +

+ + + + +
+
+
+
+46
+47
+48
+
+
# File 'lib/oauth/request_proxy/jabber_request.rb', line 46
+
+def normalized_uri
+  uri
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
+
# File 'lib/oauth/request_proxy/jabber_request.rb', line 13
+
+def parameters
+  return @params if @params
+
+  @params = {}
+
+  oauth = @request.get_elements("//oauth").first
+  return @params unless oauth
+
+  %w[
+    oauth_token
+    oauth_consumer_key
+    oauth_signature_method
+    oauth_signature
+    oauth_timestamp
+    oauth_nonce
+    oauth_version
+  ].each do |param|
+    next unless (element = oauth.first_element(param))
+
+    @params[param] = element.text
+  end
+
+  @params
+end
+
+
+ +
+

+ + #uriObject + + + + + +

+ + + + +
+
+
+
+42
+43
+44
+
+
# File 'lib/oauth/request_proxy/jabber_request.rb', line 42
+
+def uri
+  [@request.from.strip.to_s, @request.to.strip.to_s].join("&")
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/MockRequest.html b/docs/OAuth/RequestProxy/MockRequest.html new file mode 100644 index 00000000..f9963627 --- /dev/null +++ b/docs/OAuth/RequestProxy/MockRequest.html @@ -0,0 +1,417 @@ + + + + + + + Class: OAuth::RequestProxy::MockRequest + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::MockRequest + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/mock_request.rb
+
+ +
+ +

Overview

+
+

RequestProxy for Hashes to facilitate simpler signature creation.
+Usage:
+ request = OAuth::RequestProxy.proxy \
+ “method” => “iq”,
+ “uri” => [from, to] * “&”,
+ “parameters” => {
+ “oauth_consumer_key” => oauth_consumer_key,
+ “oauth_token” => oauth_token,
+ “oauth_signature_method” => “HMAC-SHA1”
+ }

+ +

signature = OAuth::Signature.sign \
+ request,
+ :consumer_secret => oauth_consumer_secret,
+ :token_secret => oauth_token_secret,

+ + +
+
+
+ + +
+ + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, #parameters_for_signature, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #methodObject + + + + + +

+ + + + +
+
+
+
+29
+30
+31
+
+
# File 'lib/oauth/request_proxy/mock_request.rb', line 29
+
+def method
+  @request["method"]
+end
+
+
+ +
+

+ + #normalized_uriObject + + + + + +

+ + + + +
+
+
+
+33
+34
+35
+36
+37
+38
+39
+
+
# File 'lib/oauth/request_proxy/mock_request.rb', line 33
+
+def normalized_uri
+  super
+rescue StandardError
+  # if this is a non-standard URI, it may not parse properly
+  # in that case, assume that it's already been normalized
+  uri
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+25
+26
+27
+
+
# File 'lib/oauth/request_proxy/mock_request.rb', line 25
+
+def parameters
+  @request["parameters"]
+end
+
+
+ +
+

+ + #uriObject + + + + + +

+ + + + +
+
+
+
+41
+42
+43
+
+
# File 'lib/oauth/request_proxy/mock_request.rb', line 41
+
+def uri
+  @request["uri"]
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/Net.html b/docs/OAuth/RequestProxy/Net.html new file mode 100644 index 00000000..41c8f3bf --- /dev/null +++ b/docs/OAuth/RequestProxy/Net.html @@ -0,0 +1,115 @@ + + + + + + + Module: OAuth::RequestProxy::Net + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::RequestProxy::Net + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/net_http.rb
+
+ +
+ +

Defined Under Namespace

+

+ + + Modules: HTTP + + + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/Net/HTTP.html b/docs/OAuth/RequestProxy/Net/HTTP.html new file mode 100644 index 00000000..434cf263 --- /dev/null +++ b/docs/OAuth/RequestProxy/Net/HTTP.html @@ -0,0 +1,115 @@ + + + + + + + Module: OAuth::RequestProxy::Net::HTTP + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::RequestProxy::Net::HTTP + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/net_http.rb
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: HTTPRequest + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/Net/HTTP/HTTPRequest.html b/docs/OAuth/RequestProxy/Net/HTTP/HTTPRequest.html new file mode 100644 index 00000000..36a5e881 --- /dev/null +++ b/docs/OAuth/RequestProxy/Net/HTTP/HTTPRequest.html @@ -0,0 +1,393 @@ + + + + + + + Class: OAuth::RequestProxy::Net::HTTP::HTTPRequest + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::Net::HTTP::HTTPRequest + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/net_http.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #normalized_uri, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, #parameters_for_signature, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #bodyObject + + + + + +

+ + + + +
+
+
+
+31
+32
+33
+
+
# File 'lib/oauth/request_proxy/net_http.rb', line 31
+
+def body
+  request.body
+end
+
+
+ +
+

+ + #methodObject + + + + + +

+ + + + +
+
+
+
+15
+16
+17
+
+
# File 'lib/oauth/request_proxy/net_http.rb', line 15
+
+def method
+  request.method
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+23
+24
+25
+26
+27
+28
+29
+
+
# File 'lib/oauth/request_proxy/net_http.rb', line 23
+
+def parameters
+  if options[:clobber_request]
+    options[:parameters]
+  else
+    all_parameters
+  end
+end
+
+
+ +
+

+ + #uriObject + + + + + +

+ + + + +
+
+
+
+19
+20
+21
+
+
# File 'lib/oauth/request_proxy/net_http.rb', line 19
+
+def uri
+  options[:uri].to_s
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/RackRequest.html b/docs/OAuth/RequestProxy/RackRequest.html new file mode 100644 index 00000000..a9a29eac --- /dev/null +++ b/docs/OAuth/RequestProxy/RackRequest.html @@ -0,0 +1,399 @@ + + + + + + + Class: OAuth::RequestProxy::RackRequest + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::RackRequest + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/rack_request.rb
+
+ +
+ +
+

Direct Known Subclasses

+

ActionDispatchRequest

+
+ + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #normalized_uri, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, #parameters_for_signature, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #methodObject + + + + + +

+ + + + +
+
+
+
+12
+13
+14
+
+
# File 'lib/oauth/request_proxy/rack_request.rb', line 12
+
+def method
+  request.env["rack.methodoverride.original_method"] || request.request_method
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+20
+21
+22
+23
+24
+25
+26
+27
+
+
# File 'lib/oauth/request_proxy/rack_request.rb', line 20
+
+def parameters
+  if options[:clobber_request]
+    options[:parameters] || {}
+  else
+    params = request_params.merge(query_params).merge(header_params)
+    params.merge(options[:parameters] || {})
+  end
+end
+
+
+ +
+

+ + #signatureObject + + + + + +

+ + + + +
+
+
+
+29
+30
+31
+
+
# File 'lib/oauth/request_proxy/rack_request.rb', line 29
+
+def signature
+  parameters["oauth_signature"]
+end
+
+
+ +
+

+ + #uriObject + + + + + +

+ + + + +
+
+
+
+16
+17
+18
+
+
# File 'lib/oauth/request_proxy/rack_request.rb', line 16
+
+def uri
+  request.url
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/RestClient.html b/docs/OAuth/RequestProxy/RestClient.html new file mode 100644 index 00000000..c2d9a6a6 --- /dev/null +++ b/docs/OAuth/RequestProxy/RestClient.html @@ -0,0 +1,115 @@ + + + + + + + Module: OAuth::RequestProxy::RestClient + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::RequestProxy::RestClient + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/rest_client_request.rb
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: Request + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/RestClient/Request.html b/docs/OAuth/RequestProxy/RestClient/Request.html new file mode 100644 index 00000000..c2060756 --- /dev/null +++ b/docs/OAuth/RequestProxy/RestClient/Request.html @@ -0,0 +1,340 @@ + + + + + + + Class: OAuth::RequestProxy::RestClient::Request + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::RestClient::Request + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/rest_client_request.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #normalized_uri, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, #parameters_for_signature, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #methodObject + + + + + +

+ + + + +
+
+
+
+14
+15
+16
+
+
# File 'lib/oauth/request_proxy/rest_client_request.rb', line 14
+
+def method
+  request.method.to_s.upcase
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+22
+23
+24
+25
+26
+27
+28
+
+
# File 'lib/oauth/request_proxy/rest_client_request.rb', line 22
+
+def parameters
+  if options[:clobber_request]
+    options[:parameters] || {}
+  else
+    post_parameters.merge(query_params).merge(options[:parameters] || {})
+  end
+end
+
+
+ +
+

+ + #uriObject + + + + + +

+ + + + +
+
+
+
+18
+19
+20
+
+
# File 'lib/oauth/request_proxy/rest_client_request.rb', line 18
+
+def uri
+  request.url
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/Typhoeus.html b/docs/OAuth/RequestProxy/Typhoeus.html new file mode 100644 index 00000000..88365dca --- /dev/null +++ b/docs/OAuth/RequestProxy/Typhoeus.html @@ -0,0 +1,115 @@ + + + + + + + Module: OAuth::RequestProxy::Typhoeus + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::RequestProxy::Typhoeus + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/typhoeus_request.rb
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: Request + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/Typhoeus/Request.html b/docs/OAuth/RequestProxy/Typhoeus/Request.html new file mode 100644 index 00000000..b27c3fab --- /dev/null +++ b/docs/OAuth/RequestProxy/Typhoeus/Request.html @@ -0,0 +1,342 @@ + + + + + + + Class: OAuth::RequestProxy::Typhoeus::Request + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestProxy::Typhoeus::Request + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy/typhoeus_request.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#options, #request, #unsigned_parameters

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#initialize, #non_oauth_parameters, #normalized_parameters, #normalized_uri, #oauth_callback, #oauth_consumer_key, #oauth_header, #oauth_nonce, #oauth_parameters, #oauth_signature, #oauth_signature_method, #oauth_timestamp, #oauth_token, #oauth_verifier, #oauth_version, #parameters_for_signature, proxies, #query_string_blank?, #sign, #sign!, #signature_and_unsigned_parameters, #signature_base_string, #signed?, #signed_uri

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::RequestProxy::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #methodObject + + + + + +

+ + + + +
+
+
+
+25
+26
+27
+28
+
+
# File 'lib/oauth/request_proxy/typhoeus_request.rb', line 25
+
+def method
+  request_method = request.options[:method].to_s.upcase
+  request_method.empty? ? "GET" : request_method
+end
+
+
+ +
+

+ + #parametersObject + + + + + +

+ + + + +
+
+
+
+34
+35
+36
+37
+38
+39
+40
+
+
# File 'lib/oauth/request_proxy/typhoeus_request.rb', line 34
+
+def parameters
+  if options[:clobber_request]
+    options[:parameters]
+  else
+    post_parameters.merge(query_parameters).merge(options[:parameters] || {})
+  end
+end
+
+
+ +
+

+ + #uriObject + + + + + +

+ + + + +
+
+
+
+30
+31
+32
+
+
# File 'lib/oauth/request_proxy/typhoeus_request.rb', line 30
+
+def uri
+  options[:uri].to_s
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestProxy/UnknownRequestType.html b/docs/OAuth/RequestProxy/UnknownRequestType.html new file mode 100644 index 00000000..6df208af --- /dev/null +++ b/docs/OAuth/RequestProxy/UnknownRequestType.html @@ -0,0 +1,124 @@ + + + + + + + Exception: OAuth::RequestProxy::UnknownRequestType + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Exception: OAuth::RequestProxy::UnknownRequestType + + + +

+
+ +
+
Inherits:
+
+ RuntimeError + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/request_proxy.rb
+
+ +
+ + + + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/RequestToken.html b/docs/OAuth/RequestToken.html new file mode 100644 index 00000000..45dac62e --- /dev/null +++ b/docs/OAuth/RequestToken.html @@ -0,0 +1,483 @@ + + + + + + + Class: OAuth::RequestToken + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::RequestToken + + + +

+
+ +
+
Inherits:
+
+ ConsumerToken + + + show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/tokens/request_token.rb
+
+ +
+ +

Overview

+
+

The RequestToken is used for the initial Request.
+This is normally created by the Consumer object.

+ + +
+
+
+ + +
+ + + + + +

Instance Attribute Summary

+ +

Attributes inherited from ConsumerToken

+

#consumer, #params, #response

+ + + +

Attributes inherited from Token

+

#secret, #token

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from ConsumerToken

+

from_hash, #initialize, #request, #sign!

+ + + + + + + + + +

Methods inherited from Token

+

#initialize, #to_query

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::ConsumerToken

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #authenticate_url(params = nil) ⇒ Object + + + + + +

+ + + + +
+
+
+
+15
+16
+17
+18
+19
+20
+
+
# File 'lib/oauth/tokens/request_token.rb', line 15
+
+def authenticate_url(params = nil)
+  return if token.nil?
+
+  params = (params || {}).merge(oauth_token: token)
+  build_url(consumer.authenticate_url, params)
+end
+
+
+ +
+

+ + #authorize_url(params = nil) ⇒ Object + + + + + +

+
+

Generate an authorization URL for user authorization

+ + +
+
+
+ + +
+ + + + +
+
+
+
+8
+9
+10
+11
+12
+13
+
+
# File 'lib/oauth/tokens/request_token.rb', line 8
+
+def authorize_url(params = nil)
+  return if token.nil?
+
+  params = (params || {}).merge(oauth_token: token)
+  build_url(consumer.authorize_url, params)
+end
+
+
+ +
+

+ + #callback_confirmed?Boolean + + + + + +

+
+ + + +
+
+
+ +

Returns:

+
    + +
  • + + + (Boolean) + + + +
  • + +
+ +
+ + + + +
+
+
+
+22
+23
+24
+
+
# File 'lib/oauth/tokens/request_token.rb', line 22
+
+def callback_confirmed?
+  params[:oauth_callback_confirmed] == "true"
+end
+
+
+ +
+

+ + #get_access_token(options = {}, *arguments) ⇒ Object + + + + + +

+
+

exchange for AccessToken on server

+ + +
+
+
+ + +
+ + + + +
+
+
+
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
+
# File 'lib/oauth/tokens/request_token.rb', line 27
+
+def get_access_token(options = {}, *arguments)
+  response = consumer.token_request(
+    consumer.http_method,
+    (consumer.access_token_url? ? consumer.access_token_url : consumer.access_token_path),
+    self,
+    options,
+    *arguments,
+  )
+  OAuth::AccessToken.from_hash(consumer, response)
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Server.html b/docs/OAuth/Server.html new file mode 100644 index 00000000..642d1295 --- /dev/null +++ b/docs/OAuth/Server.html @@ -0,0 +1,840 @@ + + + + + + + Class: OAuth::Server + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::Server + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + +
+
Includes:
+
Helper
+
+ + + + + + +
+
Defined in:
+
lib/oauth/server.rb
+
+ +
+ +

Overview

+
+

This is mainly used to create consumer credentials and can pretty much be ignored if you want to create your own

+ + +
+
+
+ + +
+ +

+ Constant Summary + collapse +

+ +
+ +
@@server_paths = + +
+
{
+  request_token_path: "/oauth/request_token",
+  authorize_path: "/oauth/authorize",
+  access_token_path: "/oauth/access_token",
+}
+ +
+ + + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #base_url ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute base_url.

    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +
+

+ + #initialize(base_url, paths = {}) ⇒ Server + + + + + +

+
+

Create a new server instance

+ + +
+
+
+ + +
+ + + + +
+
+
+
+20
+21
+22
+23
+
+
# File 'lib/oauth/server.rb', line 20
+
+def initialize(base_url, paths = {})
+  @base_url = base_url
+  @paths = @@server_paths.merge(paths)
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #base_urlObject + + + + + +

+
+

Returns the value of attribute base_url.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+11
+12
+13
+
+
# File 'lib/oauth/server.rb', line 11
+
+def base_url
+  @base_url
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #access_token_pathObject + + + + + +

+ + + + +
+
+
+
+64
+65
+66
+
+
# File 'lib/oauth/server.rb', line 64
+
+def access_token_path
+  @paths[:access_token_path]
+end
+
+
+ +
+

+ + #access_token_urlObject + + + + + +

+ + + + +
+
+
+
+68
+69
+70
+
+
# File 'lib/oauth/server.rb', line 68
+
+def access_token_url
+  base_url + access_token_path
+end
+
+
+ +
+

+ + #authorize_pathObject + + + + + +

+ + + + +
+
+
+
+56
+57
+58
+
+
# File 'lib/oauth/server.rb', line 56
+
+def authorize_path
+  @paths[:authorize_path]
+end
+
+
+ +
+

+ + #authorize_urlObject + + + + + +

+ + + + +
+
+
+
+60
+61
+62
+
+
# File 'lib/oauth/server.rb', line 60
+
+def authorize_url
+  base_url + authorize_path
+end
+
+
+ +
+

+ + #create_consumerObject + + + + + +

+
+

mainly for testing purposes

+ + +
+
+
+ + +
+ + + + +
+
+
+
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+
+
# File 'lib/oauth/server.rb', line 34
+
+def create_consumer
+  creds = generate_credentials
+  Consumer.new(
+    creds[0],
+    creds[1],
+    {
+      site: base_url,
+      request_token_path: request_token_path,
+      authorize_path: authorize_path,
+      access_token_path: access_token_path,
+    },
+  )
+end
+
+
+ +
+

+ + #generate_consumer_credentials(_params = {}) ⇒ Object + + + + + +

+ + + + +
+
+
+
+29
+30
+31
+
+
# File 'lib/oauth/server.rb', line 29
+
+def generate_consumer_credentials(_params = {})
+  Consumer.new(*generate_credentials)
+end
+
+
+ +
+

+ + #generate_credentialsObject + + + + + +

+ + + + +
+
+
+
+25
+26
+27
+
+
# File 'lib/oauth/server.rb', line 25
+
+def generate_credentials
+  [generate_key(16), generate_key]
+end
+
+
+ +
+

+ + #request_token_pathObject + + + + + +

+ + + + +
+
+
+
+48
+49
+50
+
+
# File 'lib/oauth/server.rb', line 48
+
+def request_token_path
+  @paths[:request_token_path]
+end
+
+
+ +
+

+ + #request_token_urlObject + + + + + +

+ + + + +
+
+
+
+52
+53
+54
+
+
# File 'lib/oauth/server.rb', line 52
+
+def request_token_url
+  base_url + request_token_path
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/ServerToken.html b/docs/OAuth/ServerToken.html new file mode 100644 index 00000000..5bbf56e4 --- /dev/null +++ b/docs/OAuth/ServerToken.html @@ -0,0 +1,240 @@ + + + + + + + Class: OAuth::ServerToken + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::ServerToken + + + +

+
+ +
+
Inherits:
+
+ Token + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/tokens/server_token.rb
+
+ +
+ +

Overview

+
+

Used on the server for generating tokens

+ + +
+
+
+ + +
+ + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Token

+

#secret, #token

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Token

+

#to_query

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +
+

+ + #initializeServerToken + + + + + +

+
+

Returns a new instance of ServerToken.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+6
+7
+8
+
+
# File 'lib/oauth/tokens/server_token.rb', line 6
+
+def initialize
+  super(generate_key(16), generate_key)
+end
+
+
+ +
+ + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Signature.html b/docs/OAuth/Signature.html new file mode 100644 index 00000000..8596ca91 --- /dev/null +++ b/docs/OAuth/Signature.html @@ -0,0 +1,545 @@ + + + + + + + Module: OAuth::Signature + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::Signature + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/signature.rb,
+ lib/oauth/signature/base.rb,
lib/oauth/signature/rsa/sha1.rb,
lib/oauth/signature/hmac/sha1.rb,
lib/oauth/signature/plaintext.rb,
lib/oauth/signature/hmac/sha256.rb
+
+
+ +
+ +

Defined Under Namespace

+

+ + + Modules: HMAC, RSA + + + + Classes: Base, PLAINTEXT, UnknownSignatureMethod + + +

+ + + + + + + + +

+ Class Method Summary + collapse +

+ + + + + + +
+

Class Method Details

+ + +
+

+ + .available_methodsObject + + + + + +

+
+

Returns a list of available signature methods

+ + +
+
+
+ + +
+ + + + +
+
+
+
+6
+7
+8
+
+
# File 'lib/oauth/signature.rb', line 6
+
+def self.available_methods
+  @available_methods ||= {}
+end
+
+
+ +
+

+ + .body_hash(request, options = {}, &block) ⇒ Object + + + + + +

+
+

Create the body hash for a request

+ + +
+
+
+ + +
+ + + + +
+
+
+
+42
+43
+44
+
+
# File 'lib/oauth/signature.rb', line 42
+
+def self.body_hash(request, options = {}, &block)
+  build(request, options, &block).body_hash
+end
+
+
+ +
+

+ + .build(request, options = {}, &block) ⇒ Object + + + + + +

+
+

Build a signature from a +request+.

+ +

Raises UnknownSignatureMethod exception if the signature method is unknown.

+ + +
+
+
+ +

Raises:

+ + +
+ + + + +
+
+
+
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+
+
# File 'lib/oauth/signature.rb', line 13
+
+def self.build(request, options = {}, &block)
+  request = OAuth::RequestProxy.proxy(request, options)
+  klass = available_methods[
+    (request.signature_method ||
+    ((c = request.options[:consumer]) && c.options[:signature_method]) ||
+    "").downcase]
+  raise UnknownSignatureMethod, request.signature_method unless klass
+
+  klass.new(request, options, &block)
+end
+
+
+ +
+

+ + .sign(request, options = {}, &block) ⇒ Object + + + + + +

+
+

Sign a +request+

+ + +
+
+
+ + +
+ + + + +
+
+
+
+25
+26
+27
+
+
# File 'lib/oauth/signature.rb', line 25
+
+def self.sign(request, options = {}, &block)
+  build(request, options, &block).signature
+end
+
+
+ +
+

+ + .signature_base_string(request, options = {}, &block) ⇒ Object + + + + + +

+
+

Create the signature base string for +request+. This string is the normalized parameter information.

+ +

See Also: core spec version 1.0, section 9.1.1[http://oauth.net/core/1.0#rfc.section.9.1.1]

+ + +
+
+
+ + +
+ + + + +
+
+
+
+37
+38
+39
+
+
# File 'lib/oauth/signature.rb', line 37
+
+def self.signature_base_string(request, options = {}, &block)
+  build(request, options, &block).signature_base_string
+end
+
+
+ +
+

+ + .verify(request, options = {}, &block) ⇒ Object + + + + + +

+
+

Verify the signature of +request+

+ + +
+
+
+ + +
+ + + + +
+
+
+
+30
+31
+32
+
+
# File 'lib/oauth/signature.rb', line 30
+
+def self.verify(request, options = {}, &block)
+  build(request, options, &block).verify
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Signature/Base.html b/docs/OAuth/Signature/Base.html new file mode 100644 index 00000000..e588c56b --- /dev/null +++ b/docs/OAuth/Signature/Base.html @@ -0,0 +1,925 @@ + + + + + + + Class: OAuth::Signature::Base + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::Signature::Base + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + +
+
Includes:
+
Helper
+
+ + + + + + +
+
Defined in:
+
lib/oauth/signature/base.rb
+
+ +
+ +
+

Direct Known Subclasses

+

HMAC::SHA1, HMAC::SHA256, PLAINTEXT, RSA::SHA1

+
+ + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #consumer_secret ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute consumer_secret.

    +
    + +
  • + + +
  • + + + #options ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute options.

    +
    + +
  • + + +
  • + + + #request ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute request.

    +
    + +
  • + + +
  • + + + #token_secret ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute token_secret.

    +
    + +
  • + + +
+ + + + + +

+ Class Method Summary + collapse +

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +
+

+ + #initialize(request, options = {}, &block) ⇒ Base + + + + + +

+
+

Returns a new instance of Base.

+ + +
+
+
+ +

Raises:

+
    + +
  • + + + (TypeError) + + + +
  • + +
+ +
+ + + + +
+
+
+
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+
+
# File 'lib/oauth/signature/base.rb', line 23
+
+def initialize(request, options = {}, &block)
+  raise TypeError unless request.is_a?(OAuth::RequestProxy::Base)
+
+  @request = request
+  @options = options
+
+  ## consumer secret was determined beforehand
+
+  @consumer_secret = options[:consumer].secret if options[:consumer]
+
+  # presence of :consumer_secret option will override any Consumer that's provided
+  @consumer_secret = options[:consumer_secret] if options[:consumer_secret]
+
+  ## token secret was determined beforehand
+
+  @token_secret = options[:token].secret if options[:token]
+
+  # presence of :token_secret option will override any Token that's provided
+  @token_secret = options[:token_secret] if options[:token_secret]
+
+  # override secrets based on the values returned from the block (if any)
+  if block
+    # consumer secret and token secret need to be looked up based on pieces of the request
+    secrets = yield (block.arity == 1) ? request : [token, consumer_key, nonce, request.timestamp]
+    if secrets.is_a?(Array) && secrets.size == 2
+      @token_secret = secrets[0]
+      @consumer_secret = secrets[1]
+    end
+  end
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #consumer_secretObject (readonly) + + + + + +

+
+

Returns the value of attribute consumer_secret.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+14
+15
+16
+
+
# File 'lib/oauth/signature/base.rb', line 14
+
+def consumer_secret
+  @consumer_secret
+end
+
+
+ + + +
+

+ + #optionsObject + + + + + +

+
+

Returns the value of attribute options.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+13
+14
+15
+
+
# File 'lib/oauth/signature/base.rb', line 13
+
+def options
+  @options
+end
+
+
+ + + +
+

+ + #requestObject (readonly) + + + + + +

+
+

Returns the value of attribute request.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+14
+15
+16
+
+
# File 'lib/oauth/signature/base.rb', line 14
+
+def request
+  @request
+end
+
+
+ + + +
+

+ + #token_secretObject (readonly) + + + + + +

+
+

Returns the value of attribute token_secret.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+14
+15
+16
+
+
# File 'lib/oauth/signature/base.rb', line 14
+
+def token_secret
+  @token_secret
+end
+
+
+ +
+ + +
+

Class Method Details

+ + +
+

+ + .implements(signature_method = nil) ⇒ Object + + + + + +

+ + + + +
+
+
+
+16
+17
+18
+19
+20
+21
+
+
# File 'lib/oauth/signature/base.rb', line 16
+
+def self.implements(signature_method = nil)
+  return @implements if signature_method.nil?
+
+  @implements = signature_method
+  OAuth::Signature.available_methods[@implements] = self
+end
+
+
+ +
+ +
+

Instance Method Details

+ + +
+

+ + #==(other) ⇒ Object + + + + + +

+ + + + +
+
+
+
+58
+59
+60
+61
+62
+
+
# File 'lib/oauth/signature/base.rb', line 58
+
+def ==(other)
+  check = signature.bytesize ^ other.bytesize
+  signature.bytes.zip(other.bytes) { |x, y| check |= x ^ y.to_i }
+  check.zero?
+end
+
+
+ +
+

+ + #body_hashObject + + + + + +

+ + + + +
+
+
+
+72
+73
+74
+
+
# File 'lib/oauth/signature/base.rb', line 72
+
+def body_hash
+  raise_instantiation_error
+end
+
+
+ +
+

+ + #signatureObject + + + + + +

+ + + + +
+
+
+
+54
+55
+56
+
+
# File 'lib/oauth/signature/base.rb', line 54
+
+def signature
+  Base64.encode64(digest).chomp.delete("\n")
+end
+
+
+ +
+

+ + #signature_base_stringObject + + + + + +

+ + + + +
+
+
+
+68
+69
+70
+
+
# File 'lib/oauth/signature/base.rb', line 68
+
+def signature_base_string
+  request.signature_base_string
+end
+
+
+ +
+

+ + #verifyObject + + + + + +

+ + + + +
+
+
+
+64
+65
+66
+
+
# File 'lib/oauth/signature/base.rb', line 64
+
+def verify
+  self == request.signature
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Signature/HMAC.html b/docs/OAuth/Signature/HMAC.html new file mode 100644 index 00000000..89059654 --- /dev/null +++ b/docs/OAuth/Signature/HMAC.html @@ -0,0 +1,117 @@ + + + + + + + Module: OAuth::Signature::HMAC + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::Signature::HMAC + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/signature/hmac/sha1.rb,
+ lib/oauth/signature/hmac/sha256.rb
+
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: SHA1, SHA256 + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Signature/HMAC/SHA1.html b/docs/OAuth/Signature/HMAC/SHA1.html new file mode 100644 index 00000000..1a31f41e --- /dev/null +++ b/docs/OAuth/Signature/HMAC/SHA1.html @@ -0,0 +1,226 @@ + + + + + + + Class: OAuth::Signature::HMAC::SHA1 + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::Signature::HMAC::SHA1 + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/signature/hmac/sha1.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#consumer_secret, #options, #request, #token_secret

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#==, implements, #initialize, #signature, #signature_base_string, #verify

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::Signature::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #body_hashObject + + + + + +

+ + + + +
+
+
+
+11
+12
+13
+
+
# File 'lib/oauth/signature/hmac/sha1.rb', line 11
+
+def body_hash
+  Base64.encode64(OpenSSL::Digest.digest("SHA1", request.body || "")).chomp.delete("\n")
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Signature/HMAC/SHA256.html b/docs/OAuth/Signature/HMAC/SHA256.html new file mode 100644 index 00000000..341694ab --- /dev/null +++ b/docs/OAuth/Signature/HMAC/SHA256.html @@ -0,0 +1,226 @@ + + + + + + + Class: OAuth::Signature::HMAC::SHA256 + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::Signature::HMAC::SHA256 + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/signature/hmac/sha256.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#consumer_secret, #options, #request, #token_secret

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

#==, implements, #initialize, #signature, #signature_base_string, #verify

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::Signature::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #body_hashObject + + + + + +

+ + + + +
+
+
+
+11
+12
+13
+
+
# File 'lib/oauth/signature/hmac/sha256.rb', line 11
+
+def body_hash
+  Base64.encode64(OpenSSL::Digest.digest("SHA256", request.body || "")).chomp.delete("\n")
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Signature/PLAINTEXT.html b/docs/OAuth/Signature/PLAINTEXT.html new file mode 100644 index 00000000..f6b3e86d --- /dev/null +++ b/docs/OAuth/Signature/PLAINTEXT.html @@ -0,0 +1,385 @@ + + + + + + + Class: OAuth::Signature::PLAINTEXT + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::Signature::PLAINTEXT + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/signature/plaintext.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#consumer_secret, #options, #request, #token_secret

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

implements, #initialize, #verify

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::Signature::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #==(other) ⇒ Object + + + + + +

+ + + + +
+
+
+
+14
+15
+16
+
+
# File 'lib/oauth/signature/plaintext.rb', line 14
+
+def ==(other)
+  signature.to_s == other.to_s
+end
+
+
+ +
+

+ + #body_hashObject + + + + + +

+ + + + +
+
+
+
+22
+23
+24
+
+
# File 'lib/oauth/signature/plaintext.rb', line 22
+
+def body_hash
+  nil
+end
+
+
+ +
+

+ + #signatureObject + + + + + +

+ + + + +
+
+
+
+10
+11
+12
+
+
# File 'lib/oauth/signature/plaintext.rb', line 10
+
+def signature
+  signature_base_string
+end
+
+
+ +
+

+ + #signature_base_stringObject + + + + + +

+ + + + +
+
+
+
+18
+19
+20
+
+
# File 'lib/oauth/signature/plaintext.rb', line 18
+
+def signature_base_string
+  secret
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Signature/RSA.html b/docs/OAuth/Signature/RSA.html new file mode 100644 index 00000000..b075b17a --- /dev/null +++ b/docs/OAuth/Signature/RSA.html @@ -0,0 +1,115 @@ + + + + + + + Module: OAuth::Signature::RSA + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::Signature::RSA + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/signature/rsa/sha1.rb
+
+ +
+ +

Defined Under Namespace

+

+ + + + + Classes: SHA1 + + +

+ + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Signature/RSA/SHA1.html b/docs/OAuth/Signature/RSA/SHA1.html new file mode 100644 index 00000000..e1e5dc8f --- /dev/null +++ b/docs/OAuth/Signature/RSA/SHA1.html @@ -0,0 +1,362 @@ + + + + + + + Class: OAuth::Signature::RSA::SHA1 + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::Signature::RSA::SHA1 + + + +

+
+ +
+
Inherits:
+
+ Base + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/signature/rsa/sha1.rb
+
+ +
+ + + + + + + +

Instance Attribute Summary

+ +

Attributes inherited from Base

+

#consumer_secret, #options, #request, #token_secret

+ + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods inherited from Base

+

implements, #initialize, #signature, #signature_base_string, #verify

+ + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +

This class inherits a constructor from OAuth::Signature::Base

+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #==(other) ⇒ Object + + + + + +

+ + + + +
+
+
+
+11
+12
+13
+14
+
+
# File 'lib/oauth/signature/rsa/sha1.rb', line 11
+
+def ==(other)
+  decoded = Base64.decode64(other.is_a?(Array) ? other.first : other)
+  public_key.verify(OpenSSL::Digest.new("SHA1"), decoded, signature_base_string)
+end
+
+
+ +
+

+ + #body_hashObject + + + + + +

+ + + + +
+
+
+
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
+
# File 'lib/oauth/signature/rsa/sha1.rb', line 27
+
+def body_hash
+  # Use SHA1 body hash with compatibility across OpenSSL versions
+  data = request.body || ""
+  begin
+    digest_bytes = OpenSSL::Digest.digest("SHA1", data)
+  rescue StandardError
+    digest_bytes = ::Digest::SHA1.digest(data)
+  end
+  Base64.encode64(digest_bytes).chomp.delete("\n")
+end
+
+
+ +
+

+ + #public_keyObject + + + + + +

+ + + + +
+
+
+
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+
+
# File 'lib/oauth/signature/rsa/sha1.rb', line 16
+
+def public_key
+  case consumer_secret
+  when String
+    decode_public_key
+  when OpenSSL::X509::Certificate
+    consumer_secret.public_key
+  else
+    consumer_secret
+  end
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Signature/UnknownSignatureMethod.html b/docs/OAuth/Signature/UnknownSignatureMethod.html new file mode 100644 index 00000000..b86db311 --- /dev/null +++ b/docs/OAuth/Signature/UnknownSignatureMethod.html @@ -0,0 +1,124 @@ + + + + + + + Exception: OAuth::Signature::UnknownSignatureMethod + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Exception: OAuth::Signature::UnknownSignatureMethod + + + +

+
+ +
+
Inherits:
+
+ RuntimeError + +
    +
  • Object
  • + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/signature.rb
+
+ +
+ + + + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Token.html b/docs/OAuth/Token.html new file mode 100644 index 00000000..461a86cc --- /dev/null +++ b/docs/OAuth/Token.html @@ -0,0 +1,438 @@ + + + + + + + Class: OAuth::Token + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Class: OAuth::Token + + + +

+
+ +
+
Inherits:
+
+ Object + +
    +
  • Object
  • + + + +
+ show all + +
+
+ + + + + + +
+
Includes:
+
Helper
+
+ + + + + + +
+
Defined in:
+
lib/oauth/tokens/token.rb
+
+ +
+ +

Overview

+
+

Superclass for the various tokens used by OAuth

+ + +
+
+
+ + +
+

Direct Known Subclasses

+

ConsumerToken, ServerToken

+
+ + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #secret ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute secret.

    +
    + +
  • + + +
  • + + + #token ⇒ Object + + + + + + + + + + + + + + + + +

    Returns the value of attribute token.

    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + + + +

Methods included from Helper

+

_escape, escape, generate_key, generate_timestamp, normalize, normalize_nested_query, parse_header, stringify_keys, unescape

+
+

Constructor Details

+ +
+

+ + #initialize(token, secret) ⇒ Token + + + + + +

+
+

Returns a new instance of Token.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+10
+11
+12
+13
+
+
# File 'lib/oauth/tokens/token.rb', line 10
+
+def initialize(token, secret)
+  @token = token
+  @secret = secret
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #secretObject + + + + + +

+
+

Returns the value of attribute secret.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+8
+9
+10
+
+
# File 'lib/oauth/tokens/token.rb', line 8
+
+def secret
+  @secret
+end
+
+
+ + + +
+

+ + #tokenObject + + + + + +

+
+

Returns the value of attribute token.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+8
+9
+10
+
+
# File 'lib/oauth/tokens/token.rb', line 8
+
+def token
+  @token
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #to_queryObject + + + + + +

+ + + + +
+
+
+
+15
+16
+17
+
+
# File 'lib/oauth/tokens/token.rb', line 15
+
+def to_query
+  "oauth_token=#{escape(token)}&oauth_token_secret=#{escape(secret)}"
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Unauthorized.html b/docs/OAuth/Unauthorized.html new file mode 100644 index 00000000..4a4f8212 --- /dev/null +++ b/docs/OAuth/Unauthorized.html @@ -0,0 +1,361 @@ + + + + + + + Exception: OAuth::Unauthorized + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Exception: OAuth::Unauthorized + + + +

+
+ +
+
Inherits:
+
+ Error + +
    +
  • Object
  • + + + + + + + +
+ show all + +
+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/errors/unauthorized.rb
+
+ +
+ +
+

Direct Known Subclasses

+

Problem

+
+ + + + +

Instance Attribute Summary collapse

+
    + +
  • + + + #request ⇒ Object + + + + + + + + + readonly + + + + + + + + + +

    Returns the value of attribute request.

    +
    + +
  • + + +
+ + + + + +

+ Instance Method Summary + collapse +

+ + + + + + + + + + + +
+

Constructor Details

+ +
+

+ + #initialize(request = nil) ⇒ Unauthorized + + + + + +

+
+

Returns a new instance of Unauthorized.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+7
+8
+9
+10
+
+
# File 'lib/oauth/errors/unauthorized.rb', line 7
+
+def initialize(request = nil)
+  super()
+  @request = request
+end
+
+
+ +
+ +
+

Instance Attribute Details

+ + + +
+

+ + #requestObject (readonly) + + + + + +

+
+

Returns the value of attribute request.

+ + +
+
+
+ + +
+ + + + +
+
+
+
+5
+6
+7
+
+
# File 'lib/oauth/errors/unauthorized.rb', line 5
+
+def request
+  @request
+end
+
+
+ +
+ + +
+

Instance Method Details

+ + +
+

+ + #to_sObject + + + + + +

+ + + + +
+
+
+
+12
+13
+14
+15
+16
+
+
# File 'lib/oauth/errors/unauthorized.rb', line 12
+
+def to_s
+  return "401 Unauthorized" if request.nil?
+
+  "#{request.code} #{request.message}"
+end
+
+
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/OAuth/Version.html b/docs/OAuth/Version.html new file mode 100644 index 00000000..10727d86 --- /dev/null +++ b/docs/OAuth/Version.html @@ -0,0 +1,121 @@ + + + + + + + Module: OAuth::Version + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Module: OAuth::Version + + + +

+
+ + + + + + + + + + + +
+
Defined in:
+
lib/oauth/version.rb
+
+ +
+ + + +

+ Constant Summary + collapse +

+ +
+ +
VERSION = + +
+
"1.1.1"
+ +
+ + + + + + + + + + +
+ + + +
+ + \ No newline at end of file diff --git a/docs/_index.html b/docs/_index.html new file mode 100644 index 00000000..c9bd9d69 --- /dev/null +++ b/docs/_index.html @@ -0,0 +1,593 @@ + + + + + + + Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Documentation by YARD 0.9.37

+
+

Alphabetic Index

+ +

File Listing

+ + +
+

Namespace Listing A-Z

+ + + + + + + + +
+ + + + + +
    +
  • B
  • +
      + +
    • + Base + + (OAuth::Signature) + +
    • + +
    • + Base + + (OAuth::RequestProxy) + +
    • + +
    • + Base + + (ActionController) + +
    • + +
    +
+ + + + + + + + +
    +
  • H
  • +
      + +
    • + HMAC + + (OAuth::Signature) + +
    • + +
    • + HTTP + + (OAuth::RequestProxy::Net) + +
    • + +
    • + HTTPGenericRequest + + (Net) + +
    • + +
    • + HTTPRequest + + (OAuth::RequestProxy::Net::HTTP) + +
    • + +
    • + Helper + + (OAuth::Client) + +
    • + +
    • + Helper + + (OAuth) + +
    • + +
    • + HttpClient + + (EventMachine) + +
    • + +
    • + HttpRequest + + (OAuth::RequestProxy::EventMachine) + +
    • + +
    +
+ + + + + +
    +
  • M
  • + +
+ + +
+ + +
    +
  • N
  • +
      + +
    • + Net + +
    • + +
    • + Net + + (OAuth::RequestProxy) + +
    • + +
    +
+ + + + + +
    +
  • P
  • + +
+ + +
    +
  • R
  • +
      + +
    • + RSA + + (OAuth::Signature) + +
    • + +
    • + RackRequest + + (OAuth::RequestProxy) + +
    • + +
    • + Request + + (OAuth::RequestProxy::Typhoeus) + +
    • + +
    • + Request + + (OAuth::RequestProxy::RestClient) + +
    • + +
    • + RequestProxy + + (OAuth) + +
    • + +
    • + RequestToken + + (OAuth) + +
    • + +
    • + RestClient + + (OAuth::RequestProxy) + +
    • + +
    +
+ + +
    +
  • S
  • +
      + +
    • + SHA1 + + (OAuth::Signature::RSA) + +
    • + +
    • + SHA1 + + (OAuth::Signature::HMAC) + +
    • + +
    • + SHA256 + + (OAuth::Signature::HMAC) + +
    • + +
    • + Server + + (OAuth) + +
    • + +
    • + ServerToken + + (OAuth) + +
    • + +
    • + Signature + + (OAuth) + +
    • + +
    +
+ + +
    +
  • T
  • +
      + +
    • + TestRequest + + (ActionController) + +
    • + +
    • + Token + + (OAuth) + +
    • + +
    • + Typhoeus + + (OAuth::RequestProxy) + +
    • + +
    +
+ + + + + +
    +
  • V
  • +
      + +
    • + Version + + (OAuth) + +
    • + +
    +
+ +
+ +
+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/class_list.html b/docs/class_list.html new file mode 100644 index 00000000..cca39ba9 --- /dev/null +++ b/docs/class_list.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + Class List + + + +
+
+

Class List

+ + + +
+ + +
+ + diff --git a/docs/css/common.css b/docs/css/common.css new file mode 100644 index 00000000..cf25c452 --- /dev/null +++ b/docs/css/common.css @@ -0,0 +1 @@ +/* Override this file with custom rules */ \ No newline at end of file diff --git a/docs/css/full_list.css b/docs/css/full_list.css new file mode 100644 index 00000000..6eef5e4a --- /dev/null +++ b/docs/css/full_list.css @@ -0,0 +1,58 @@ +body { + margin: 0; + font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif; + font-size: 13px; + height: 101%; + overflow-x: hidden; + background: #fafafa; +} + +h1 { padding: 12px 10px; padding-bottom: 0; margin: 0; font-size: 1.4em; } +.clear { clear: both; } +.fixed_header { position: fixed; background: #fff; width: 100%; padding-bottom: 10px; margin-top: 0; top: 0; z-index: 9999; height: 70px; } +#search { position: absolute; right: 5px; top: 9px; padding-left: 24px; } +#content.insearch #search, #content.insearch #noresults { background: url(data:image/gif;base64,R0lGODlhEAAQAPYAAP///wAAAPr6+pKSkoiIiO7u7sjIyNjY2J6engAAAI6OjsbGxjIyMlJSUuzs7KamppSUlPLy8oKCghwcHLKysqSkpJqamvT09Pj4+KioqM7OzkRERAwMDGBgYN7e3ujo6Ly8vCoqKjY2NkZGRtTU1MTExDw8PE5OTj4+PkhISNDQ0MrKylpaWrS0tOrq6nBwcKysrLi4uLq6ul5eXlxcXGJiYoaGhuDg4H5+fvz8/KKiohgYGCwsLFZWVgQEBFBQUMzMzDg4OFhYWBoaGvDw8NbW1pycnOLi4ubm5kBAQKqqqiQkJCAgIK6urnJyckpKSjQ0NGpqatLS0sDAwCYmJnx8fEJCQlRUVAoKCggICLCwsOTk5ExMTPb29ra2tmZmZmhoaNzc3KCgoBISEiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCAAAACwAAAAAEAAQAAAHaIAAgoMgIiYlg4kACxIaACEJCSiKggYMCRselwkpghGJBJEcFgsjJyoAGBmfggcNEx0flBiKDhQFlIoCCA+5lAORFb4AJIihCRbDxQAFChAXw9HSqb60iREZ1omqrIPdJCTe0SWI09GBACH5BAkIAAAALAAAAAAQABAAAAdrgACCgwc0NTeDiYozCQkvOTo9GTmDKy8aFy+NOBA7CTswgywJDTIuEjYFIY0JNYMtKTEFiRU8Pjwygy4ws4owPyCKwsMAJSTEgiQlgsbIAMrO0dKDGMTViREZ14kYGRGK38nHguHEJcvTyIEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDAggPg4iJAAMJCRUAJRIqiRGCBI0WQEEJJkWDERkYAAUKEBc4Po1GiKKJHkJDNEeKig4URLS0ICImJZAkuQAhjSi/wQyNKcGDCyMnk8u5rYrTgqDVghgZlYjcACTA1sslvtHRgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCQARAtOUoQRGRiFD0kJUYWZhUhKT1OLhR8wBaaFBzQ1NwAlkIszCQkvsbOHL7Y4q4IuEjaqq0ZQD5+GEEsJTDCMmIUhtgk1lo6QFUwJVDKLiYJNUd6/hoEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4uen4ICCA+IkIsDCQkVACWmhwSpFqAABQoQF6ALTkWFnYMrVlhWvIKTlSAiJiVVPqlGhJkhqShHV1lCW4cMqSkAR1ofiwsjJyqGgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCSMhREZGIYYGY2ElYebi56fhyWQniSKAKKfpaCLFlAPhl0gXYNGEwkhGYREUywag1wJwSkHNDU3D0kJYIMZQwk8MjPBLx9eXwuETVEyAC/BOKsuEjYFhoEAIfkECQgAAAAsAAAAABAAEAAAB2eAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4ueICImip6CIQkJKJ4kigynKaqKCyMnKqSEK05StgAGQRxPYZaENqccFgIID4KXmQBhXFkzDgOnFYLNgltaSAAEpxa7BQoQF4aBACH5BAkIAAAALAAAAAAQABAAAAdogACCg4SFggJiPUqCJSWGgkZjCUwZACQkgxGEXAmdT4UYGZqCGWQ+IjKGGIUwPzGPhAc0NTewhDOdL7Ykji+dOLuOLhI2BbaFETICx4MlQitdqoUsCQ2vhKGjglNfU0SWmILaj43M5oEAOwAAAAAAAAAAAA==) no-repeat center left; } +#full_list { padding: 0; list-style: none; margin-left: 0; margin-top: 80px; font-size: 1.1em; } +#full_list ul { padding: 0; } +#full_list li { padding: 0; margin: 0; list-style: none; } +#full_list li .item { padding: 5px 5px 5px 12px; } +#noresults { padding: 7px 12px; background: #fff; } +#content.insearch #noresults { margin-left: 7px; } +li.collapsed ul { display: none; } +li a.toggle { cursor: default; position: relative; left: -5px; top: 4px; text-indent: -999px; width: 10px; height: 9px; margin-left: -10px; display: block; float: left; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTM5jWRgMAAAAVdEVYdENyZWF0aW9uIFRpbWUAMy8xNC8wOeNZPpQAAAE2SURBVDiNrZTBccIwEEXfelIAHUA6CZ24BGaWO+FuzZAK4k6gg5QAdGAq+Bxs2Yqx7BzyL7Llp/VfzZeQhCTc/ezuGzKKnKSzpCxXJM8fwNXda3df5RZETlIt6YUzSQDs93sl8w3wBZxCCE10GM1OcWbWjB2mWgEH4Mfdyxm3PSepBHibgQE2wLe7r4HjEidpnXMYdQPKEMJcsZ4zs2POYQOcaPfwMVOo58zsAdMt18BuoVDPxUJRacELbXv3hUIX2vYmOUvi8C8ydz/ThjXrqKqqLbDIAdsCKBd+Wo7GWa7o9qzOQHVVVXeAbs+yHHCH4aTsaCOQqunmUy1yBUAXkdMIfMlgF5EXLo2OpV/c/Up7jG4hhHcYLgWzAZXUc2b2ixsfvc/RmNNfOXD3Q/oeL9axJE1yT9IOoUu6MGUkAAAAAElFTkSuQmCC) no-repeat bottom left; } +li.collapsed a.toggle { cursor: default; background-position: top left; } +li { color: #666; cursor: pointer; } +li.deprecated { text-decoration: line-through; font-style: italic; } +li.odd { background: #f0f0f0; } +li.even { background: #fafafa; } +.item:hover { background: #ddd; } +li small:before { content: "("; } +li small:after { content: ")"; } +li small.search_info { display: none; } +a, a:visited { text-decoration: none; color: #05a; } +li.clicked > .item { background: #05a; color: #ccc; } +li.clicked > .item a, li.clicked > .item a:visited { color: #eee; } +li.clicked > .item a.toggle { opacity: 0.5; background-position: bottom right; } +li.collapsed.clicked a.toggle { background-position: top right; } +#search input { border: 1px solid #bbb; border-radius: 3px; } +#full_list_nav { margin-left: 10px; font-size: 0.9em; display: block; color: #aaa; } +#full_list_nav a, #nav a:visited { color: #358; } +#full_list_nav a:hover { background: transparent; color: #5af; } +#full_list_nav span:after { content: ' | '; } +#full_list_nav span:last-child:after { content: ''; } + +#content h1 { margin-top: 0; } +li { white-space: nowrap; cursor: normal; } +li small { display: block; font-size: 0.8em; } +li small:before { content: ""; } +li small:after { content: ""; } +li small.search_info { display: none; } +#search { width: 170px; position: static; margin: 3px; margin-left: 10px; font-size: 0.9em; color: #666; padding-left: 0; padding-right: 24px; } +#content.insearch #search { background-position: center right; } +#search input { width: 110px; } + +#full_list.insearch ul { display: block; } +#full_list.insearch .item { display: none; } +#full_list.insearch .found { display: block; padding-left: 11px !important; } +#full_list.insearch li a.toggle { display: none; } +#full_list.insearch li small.search_info { display: block; } diff --git a/docs/css/style.css b/docs/css/style.css new file mode 100644 index 00000000..f169a651 --- /dev/null +++ b/docs/css/style.css @@ -0,0 +1,503 @@ +html { + width: 100%; + height: 100%; +} +body { + font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif; + font-size: 13px; + width: 100%; + margin: 0; + padding: 0; + display: flex; + display: -webkit-flex; + display: -ms-flexbox; +} + +#nav { + position: relative; + width: 100%; + height: 100%; + border: 0; + border-right: 1px dotted #eee; + overflow: auto; +} +.nav_wrap { + margin: 0; + padding: 0; + width: 20%; + height: 100%; + position: relative; + display: flex; + display: -webkit-flex; + display: -ms-flexbox; + flex-shrink: 0; + -webkit-flex-shrink: 0; + -ms-flex: 1 0; +} +#resizer { + position: absolute; + right: -5px; + top: 0; + width: 10px; + height: 100%; + cursor: col-resize; + z-index: 9999; +} +#main { + flex: 5 1; + -webkit-flex: 5 1; + -ms-flex: 5 1; + outline: none; + position: relative; + background: #fff; + padding: 1.2em; + padding-top: 0.2em; + box-sizing: border-box; +} + +@media (max-width: 920px) { + .nav_wrap { width: 100%; top: 0; right: 0; overflow: visible; position: absolute; } + #resizer { display: none; } + #nav { + z-index: 9999; + background: #fff; + display: none; + position: absolute; + top: 40px; + right: 12px; + width: 500px; + max-width: 80%; + height: 80%; + overflow-y: scroll; + border: 1px solid #999; + border-collapse: collapse; + box-shadow: -7px 5px 25px #aaa; + border-radius: 2px; + } +} + +@media (min-width: 920px) { + body { height: 100%; overflow: hidden; } + #main { height: 100%; overflow: auto; } + #search { display: none; } +} + +@media (max-width: 320px) { + body { height: 100%; overflow: hidden; overflow-wrap: break-word; } + #main { height: 100%; overflow: auto; } +} + +#main img { max-width: 100%; } +h1 { font-size: 25px; margin: 1em 0 0.5em; padding-top: 4px; border-top: 1px dotted #d5d5d5; } +h1.noborder { border-top: 0px; margin-top: 0; padding-top: 4px; } +h1.title { margin-bottom: 10px; } +h1.alphaindex { margin-top: 0; font-size: 22px; } +h2 { + padding: 0; + padding-bottom: 3px; + border-bottom: 1px #aaa solid; + font-size: 1.4em; + margin: 1.8em 0 0.5em; + position: relative; +} +h2 small { font-weight: normal; font-size: 0.7em; display: inline; position: absolute; right: 0; } +h2 small a { + display: block; + height: 20px; + border: 1px solid #aaa; + border-bottom: 0; + border-top-left-radius: 5px; + background: #f8f8f8; + position: relative; + padding: 2px 7px; +} +a { font-weight: 550; } +.clear { clear: both; } +.inline { display: inline; } +.inline p:first-child { display: inline; } +.docstring, .tags, #filecontents { font-size: 15px; line-height: 1.5145em; } +.docstring p > code, .docstring p > tt, .tags p > code, .tags p > tt { + color: #c7254e; background: #f9f2f4; padding: 2px 4px; font-size: 1em; + border-radius: 4px; +} +.docstring h1, .docstring h2, .docstring h3, .docstring h4 { padding: 0; border: 0; border-bottom: 1px dotted #bbb; } +.docstring h1 { font-size: 1.2em; } +.docstring h2 { font-size: 1.1em; } +.docstring h3, .docstring h4 { font-size: 1em; border-bottom: 0; padding-top: 10px; } +.summary_desc .object_link a, .docstring .object_link a { + font-family: monospace; font-size: 1.05em; + color: #05a; background: #EDF4FA; padding: 2px 4px; font-size: 1em; + border-radius: 4px; +} +.rdoc-term { padding-right: 25px; font-weight: bold; } +.rdoc-list p { margin: 0; padding: 0; margin-bottom: 4px; } +.summary_desc pre.code .object_link a, .docstring pre.code .object_link a { + padding: 0px; background: inherit; color: inherit; border-radius: inherit; +} + +/* style for */ +#filecontents table, .docstring table { border-collapse: collapse; } +#filecontents table th, #filecontents table td, +.docstring table th, .docstring table td { border: 1px solid #ccc; padding: 8px; padding-right: 17px; } +#filecontents table tr:nth-child(odd), +.docstring table tr:nth-child(odd) { background: #eee; } +#filecontents table tr:nth-child(even), +.docstring table tr:nth-child(even) { background: #fff; } +#filecontents table th, .docstring table th { background: #fff; } + +/* style for
    */ +#filecontents li > p, .docstring li > p { margin: 0px; } +#filecontents ul, .docstring ul { padding-left: 20px; } +/* style for
    */ +#filecontents dl, .docstring dl { border: 1px solid #ccc; } +#filecontents dt, .docstring dt { background: #ddd; font-weight: bold; padding: 3px 5px; } +#filecontents dd, .docstring dd { padding: 5px 0px; margin-left: 18px; } +#filecontents dd > p, .docstring dd > p { margin: 0px; } + +.note { + color: #222; + margin: 20px 0; + padding: 10px; + border: 1px solid #eee; + border-radius: 3px; + display: block; +} +.docstring .note { + border-left-color: #ccc; + border-left-width: 5px; +} +.note.todo { background: #ffffc5; border-color: #ececaa; } +.note.returns_void { background: #efefef; } +.note.deprecated { background: #ffe5e5; border-color: #e9dada; } +.note.title.deprecated { background: #ffe5e5; border-color: #e9dada; } +.note.private { background: #ffffc5; border-color: #ececaa; } +.note.title { padding: 3px 6px; font-size: 0.9em; font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif; display: inline; } +.summary_signature + .note.title { margin-left: 7px; } +h1 .note.title { font-size: 0.5em; font-weight: normal; padding: 3px 5px; position: relative; top: -3px; text-transform: capitalize; } +.note.title { background: #efefef; } +.note.title.constructor { color: #fff; background: #6a98d6; border-color: #6689d6; } +.note.title.writeonly { color: #fff; background: #45a638; border-color: #2da31d; } +.note.title.readonly { color: #fff; background: #6a98d6; border-color: #6689d6; } +.note.title.private { background: #d5d5d5; border-color: #c5c5c5; } +.note.title.not_defined_here { background: transparent; border: none; font-style: italic; } +.discussion .note { margin-top: 6px; } +.discussion .note:first-child { margin-top: 0; } + +h3.inherited { + font-style: italic; + font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif; + font-weight: normal; + padding: 0; + margin: 0; + margin-top: 12px; + margin-bottom: 3px; + font-size: 13px; +} +p.inherited { + padding: 0; + margin: 0; + margin-left: 25px; +} + +.box_info dl { + margin: 0; + border: 0; + width: 100%; + font-size: 1em; + display: flex; + display: -webkit-flex; + display: -ms-flexbox; +} +.box_info dl dt { + flex-shrink: 0; + -webkit-flex-shrink: 1; + -ms-flex-shrink: 1; + width: 100px; + text-align: right; + font-weight: bold; + border: 1px solid #aaa; + border-width: 1px 0px 0px 1px; + padding: 6px 0; + padding-right: 10px; +} +.box_info dl dd { + flex-grow: 1; + -webkit-flex-grow: 1; + -ms-flex: 1; + max-width: 420px; + padding: 6px 0; + padding-right: 20px; + border: 1px solid #aaa; + border-width: 1px 1px 0 0; + overflow: hidden; + position: relative; +} +.box_info dl:last-child > * { + border-bottom: 1px solid #aaa; +} +.box_info dl:nth-child(odd) > * { background: #eee; } +.box_info dl:nth-child(even) > * { background: #fff; } +.box_info dl > * { margin: 0; } + +ul.toplevel { list-style: none; padding-left: 0; font-size: 1.1em; } +.index_inline_list { padding-left: 0; font-size: 1.1em; } + +.index_inline_list li { + list-style: none; + display: inline-block; + padding: 0 12px; + line-height: 30px; + margin-bottom: 5px; +} + +dl.constants { margin-left: 10px; } +dl.constants dt { font-weight: bold; font-size: 1.1em; margin-bottom: 5px; } +dl.constants.compact dt { display: inline-block; font-weight: normal } +dl.constants dd { width: 75%; white-space: pre; font-family: monospace; margin-bottom: 18px; } +dl.constants .docstring .note:first-child { margin-top: 5px; } + +.summary_desc { + margin-left: 32px; + display: block; + font-family: sans-serif; + font-size: 1.1em; + margin-top: 8px; + line-height: 1.5145em; + margin-bottom: 0.8em; +} +.summary_desc tt { font-size: 0.9em; } +dl.constants .note { padding: 2px 6px; padding-right: 12px; margin-top: 6px; } +dl.constants .docstring { margin-left: 32px; font-size: 0.9em; font-weight: normal; } +dl.constants .tags { padding-left: 32px; font-size: 0.9em; line-height: 0.8em; } +dl.constants .discussion *:first-child { margin-top: 0; } +dl.constants .discussion *:last-child { margin-bottom: 0; } + +.method_details { border-top: 1px dotted #ccc; margin-top: 25px; padding-top: 0; } +.method_details.first { border: 0; margin-top: 5px; } +.method_details.first h3.signature { margin-top: 1em; } +p.signature, h3.signature { + font-size: 1.1em; font-weight: normal; font-family: Monaco, Consolas, Courier, monospace; + padding: 6px 10px; margin-top: 1em; + background: #E8F4FF; border: 1px solid #d8d8e5; border-radius: 5px; +} +p.signature tt, +h3.signature tt { font-family: Monaco, Consolas, Courier, monospace; } +p.signature .overload, +h3.signature .overload { display: block; } +p.signature .extras, +h3.signature .extras { font-weight: normal; font-family: sans-serif; color: #444; font-size: 1em; } +p.signature .not_defined_here, +h3.signature .not_defined_here, +p.signature .aliases, +h3.signature .aliases { display: block; font-weight: normal; font-size: 0.9em; font-family: sans-serif; margin-top: 0px; color: #555; } +p.signature .aliases .names, +h3.signature .aliases .names { font-family: Monaco, Consolas, Courier, monospace; font-weight: bold; color: #000; font-size: 1.2em; } + +.tags .tag_title { font-size: 1.05em; margin-bottom: 0; font-weight: bold; } +.tags .tag_title tt { color: initial; padding: initial; background: initial; } +.tags ul { margin-top: 5px; padding-left: 30px; list-style: square; } +.tags ul li { margin-bottom: 3px; } +.tags ul .name { font-family: monospace; font-weight: bold; } +.tags ul .note { padding: 3px 6px; } +.tags { margin-bottom: 12px; } + +.tags .examples .tag_title { margin-bottom: 10px; font-weight: bold; } +.tags .examples .inline p { padding: 0; margin: 0; font-weight: bold; font-size: 1em; } +.tags .examples .inline p:before { content: "▸"; font-size: 1em; margin-right: 5px; } + +.tags .overload .overload_item { list-style: none; margin-bottom: 25px; } +.tags .overload .overload_item .signature { + padding: 2px 8px; + background: #F1F8FF; border: 1px solid #d8d8e5; border-radius: 3px; +} +.tags .overload .signature { margin-left: -15px; font-family: monospace; display: block; font-size: 1.1em; } +.tags .overload .docstring { margin-top: 15px; } + +.defines { display: none; } + +#method_missing_details .notice.this { position: relative; top: -8px; color: #888; padding: 0; margin: 0; } + +.showSource { font-size: 0.9em; } +.showSource a, .showSource a:visited { text-decoration: none; color: #666; } + +#content a, #content a:visited { text-decoration: none; color: #05a; } +#content a:hover { background: #ffffa5; } + +ul.summary { + list-style: none; + font-family: monospace; + font-size: 1em; + line-height: 1.5em; + padding-left: 0px; +} +ul.summary a, ul.summary a:visited { + text-decoration: none; font-size: 1.1em; +} +ul.summary li { margin-bottom: 5px; } +.summary_signature { padding: 4px 8px; background: #f8f8f8; border: 1px solid #f0f0f0; border-radius: 5px; } +.summary_signature:hover { background: #CFEBFF; border-color: #A4CCDA; cursor: pointer; } +.summary_signature.deprecated { background: #ffe5e5; border-color: #e9dada; } +ul.summary.compact li { display: inline-block; margin: 0px 5px 0px 0px; line-height: 2.6em;} +ul.summary.compact .summary_signature { padding: 5px 7px; padding-right: 4px; } +#content .summary_signature:hover a, +#content .summary_signature:hover a:visited { + background: transparent; + color: #049; +} + +p.inherited a { font-family: monospace; font-size: 0.9em; } +p.inherited { word-spacing: 5px; font-size: 1.2em; } + +p.children { font-size: 1.2em; } +p.children a { font-size: 0.9em; } +p.children strong { font-size: 0.8em; } +p.children strong.modules { padding-left: 5px; } + +ul.fullTree { display: none; padding-left: 0; list-style: none; margin-left: 0; margin-bottom: 10px; } +ul.fullTree ul { margin-left: 0; padding-left: 0; list-style: none; } +ul.fullTree li { text-align: center; padding-top: 18px; padding-bottom: 12px; background: url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAHtJREFUeNqMzrEJAkEURdGzuhgZbSoYWcAWoBVsB4JgZAGmphsZCZYzTQgWNCYrDN9RvMmHx+X916SUBFbo8CzD1idXrLErw1mQttgXtyrOcQ/Ny5p4Qh+2XqLYYazsPWNTiuMkRxa4vcV+evuNAUOLIx5+c2hyzv7hNQC67Q+/HHmlEwAAAABJRU5ErkJggg==) no-repeat top center; } +ul.fullTree li:first-child { padding-top: 0; background: transparent; } +ul.fullTree li:last-child { padding-bottom: 0; } +.showAll ul.fullTree { display: block; } +.showAll .inheritName { display: none; } + +#search { position: absolute; right: 12px; top: 0px; z-index: 9000; } +#search a { + display: block; float: left; + padding: 4px 8px; text-decoration: none; color: #05a; fill: #05a; + border: 1px solid #d8d8e5; + border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; + background: #F1F8FF; + box-shadow: -1px 1px 3px #ddd; +} +#search a:hover { background: #f5faff; color: #06b; fill: #06b; } +#search a.active { + background: #568; padding-bottom: 20px; color: #fff; fill: #fff; + border: 1px solid #457; + border-top-left-radius: 5px; border-top-right-radius: 5px; +} +#search a.inactive { color: #999; fill: #999; } +.inheritanceTree, .toggleDefines { + float: right; + border-left: 1px solid #aaa; + position: absolute; top: 0; right: 0; + height: 100%; + background: #f6f6f6; + padding: 5px; + min-width: 55px; + text-align: center; +} + +#menu { font-size: 1.3em; color: #bbb; } +#menu .title, #menu a { font-size: 0.7em; } +#menu .title a { font-size: 1em; } +#menu .title { color: #555; } +#menu a, #menu a:visited { color: #333; text-decoration: none; border-bottom: 1px dotted #bbd; } +#menu a:hover { color: #05a; } + +#footer { margin-top: 15px; border-top: 1px solid #ccc; text-align: center; padding: 7px 0; color: #999; } +#footer a, #footer a:visited { color: #444; text-decoration: none; border-bottom: 1px dotted #bbd; } +#footer a:hover { color: #05a; } + +#listing ul.alpha { font-size: 1.1em; } +#listing ul.alpha { margin: 0; padding: 0; padding-bottom: 10px; list-style: none; } +#listing ul.alpha li.letter { font-size: 1.4em; padding-bottom: 10px; } +#listing ul.alpha ul { margin: 0; padding-left: 15px; } +#listing ul small { color: #666; font-size: 0.7em; } + +li.r1 { background: #f0f0f0; } +li.r2 { background: #fafafa; } + +#content ul.summary li.deprecated .summary_signature a, +#content ul.summary li.deprecated .summary_signature a:visited { text-decoration: line-through; font-style: italic; } + +#toc { + position: relative; + float: right; + overflow-x: auto; + right: -3px; + margin-left: 20px; + margin-bottom: 20px; + padding: 20px; padding-right: 30px; + max-width: 300px; + z-index: 5000; + background: #fefefe; + border: 1px solid #ddd; + box-shadow: -2px 2px 6px #bbb; +} +#toc .title { margin: 0; } +#toc ol { padding-left: 1.8em; } +#toc li { font-size: 1.1em; line-height: 1.7em; } +#toc > ol > li { font-size: 1.1em; font-weight: bold; } +#toc ol > li > ol { font-size: 0.9em; } +#toc ol ol > li > ol { padding-left: 2.3em; } +#toc ol + li { margin-top: 0.3em; } +#toc.hidden { padding: 10px; background: #fefefe; box-shadow: none; } +#toc.hidden:hover { background: #fafafa; } +#filecontents h1 + #toc.nofloat { margin-top: 0; } +@media (max-width: 560px) { + #toc { + margin-left: 0; + margin-top: 16px; + float: none; + max-width: none; + } +} + +/* syntax highlighting */ +.source_code { display: none; padding: 3px 8px; border-left: 8px solid #ddd; margin-top: 5px; } +#filecontents pre.code, .docstring pre.code, .source_code pre { font-family: monospace; } +#filecontents pre.code, .docstring pre.code { display: block; } +.source_code .lines { padding-right: 12px; color: #555; text-align: right; } +#filecontents pre.code, .docstring pre.code, +.tags pre.example { + padding: 9px 14px; + margin-top: 4px; + border: 1px solid #e1e1e8; + background: #f7f7f9; + border-radius: 4px; + font-size: 1em; + overflow-x: auto; + line-height: 1.2em; +} +pre.code { color: #000; tab-size: 2; } +pre.code .info.file { color: #555; } +pre.code .val { color: #036A07; } +pre.code .tstring_content, +pre.code .heredoc_beg, pre.code .heredoc_end, +pre.code .qwords_beg, pre.code .qwords_end, pre.code .qwords_sep, +pre.code .words_beg, pre.code .words_end, pre.code .words_sep, +pre.code .qsymbols_beg, pre.code .qsymbols_end, pre.code .qsymbols_sep, +pre.code .symbols_beg, pre.code .symbols_end, pre.code .symbols_sep, +pre.code .tstring, pre.code .dstring { color: #036A07; } +pre.code .fid, pre.code .rubyid_new, pre.code .rubyid_to_s, +pre.code .rubyid_to_sym, pre.code .rubyid_to_f, +pre.code .dot + pre.code .id, +pre.code .rubyid_to_i pre.code .rubyid_each { color: #0085FF; } +pre.code .comment { color: #0066FF; } +pre.code .const, pre.code .constant { color: #585CF6; } +pre.code .label, +pre.code .symbol { color: #C5060B; } +pre.code .kw, +pre.code .rubyid_require, +pre.code .rubyid_extend, +pre.code .rubyid_include { color: #0000FF; } +pre.code .ivar { color: #318495; } +pre.code .gvar, +pre.code .rubyid_backref, +pre.code .rubyid_nth_ref { color: #6D79DE; } +pre.code .regexp, .dregexp { color: #036A07; } +pre.code a { border-bottom: 1px dotted #bbf; } +/* inline code */ +*:not(pre) > code { + padding: 1px 3px 1px 3px; + border: 1px solid #E1E1E8; + background: #F7F7F9; + border-radius: 4px; +} + +/* Color fix for links */ +#content .summary_desc pre.code .id > .object_link a, /* identifier */ +#content .docstring pre.code .id > .object_link a { color: #0085FF; } +#content .summary_desc pre.code .const > .object_link a, /* constant */ +#content .docstring pre.code .const > .object_link a { color: #585CF6; } diff --git a/docs/file.CHANGELOG.html b/docs/file.CHANGELOG.html new file mode 100644 index 00000000..ad6d6ef9 --- /dev/null +++ b/docs/file.CHANGELOG.html @@ -0,0 +1,686 @@ + + + + + + + File: CHANGELOG + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
    + + +

    Changelog

    + +

    SemVer 2.0.0 Keep-A-Changelog 1.0.0

    + +

    All notable changes to this project will be documented in this file.

    + +

    The format is based on Keep a Changelog,
    +and this project adheres to Semantic Versioning,
    +and yes, platform and engine support are part of the public API.
    +Please file a bug if you notice a violation of semantic versioning.

    + +

    Unreleased

    +

    Added

    +
      +
    • kettle-dev v1.1.18 +

      Changed

      +
    • +
    • converted minitest => rspec +

      Deprecated

      +

      Removed

      +

      Fixed

      +

      Security

      +
    • +
    + +

    +1.1.0 2022-08-29 (tag)

    +

    Changed

    +
      +
    • +OAuth::CLI has been extracted to a new gem, oauth-tty, hosted on Gitlab +
        +
      • The public API of oauth-tty is backwards compatible (meaning OAuth::CLI)
      • +
      • The change within the oauth gem is backwards compatible as oauth-tty has been added as a dependency
      • +
      • Minor version bump is cautionary, as many lines of code have changed.
      • +
      +
    • +
    • +OAuth::Comsumer#options hash is now handled by snaky_hash, which was extracted from oauth2 +
        +
      • symbolized keys, dot-access and snake-case are now normalized
      • +
      +
    • +
    + +

    +1.0.1 2022-08-29 (tag)

    +

    Changed

    +
      +
    • +OAuth::Comsumer#options hash is now handled by snaky_hash, which was extracted from oauth2 +
        +
      • symbolized keys, dot-access and snake-case are now normalized
      • +
      +
    • +
    + +

    +1.0.0 2022-08-23 (tag)

    +

    Changed

    +
      +
    • Dropped support for Ruby < 2.7
    • +
    • Dropped support for Rails < 6 +

      Added

      +
    • +
    • New EOL Policy +
        +
      • Non-commercial support for the oldest version of Ruby (which itself is going EOL) will be dropped each year in April
      • +
      +
    • +
    + +

    +0.6.2 2022-08-29 (tag)

    +

    Changed

    +
      +
    • +OAuth::Comsumer#options hash is now handled by snaky_hash, which was extracted from oauth2 +
        +
      • symbolized keys, dot-access and snake-case are now normalized
      • +
      +
    • +
    + +

    +0.6.1 2022-08-23 (tag)

    +

    Changed

    +
      +
    • Fixed documentation in SECURITY.md
    • +
    • Change references to master => main +

      Added

      +
    • +
    • Post install note about v0.6.x EOL approaching in April, 2024
    • +
    + +

    +0.6.0 2022-08-23 (tag)

    +

    Added

    +
      +
    • New option body_hash_enabled which defaults to true to maintain backward compatibility with prior releases. Setting to false disables generation of a oauth_body_hash component as part of the signature computation.
    • +
    • Improved documentation of support policy via Tidelift
    • +
    • Stop testing against active_support v2 +

      Changed

      +
    • +
    • Utilize version_gem extracted from oauth2 gem for VERSION +
        +
      • Added new OAuth::Version namespace
      • +
      • VERSION constant now at OAuth::Version::VERSION +

        Removed

        +
      • +
      +
    • +
    • Ruby 2.0, 2.1, 2.2, and 2.3 are no longer valid install targets
    • +
    + +

    +0.5.14 2022-08-29 (tag)

    +

    The “hopeful last 0.5.x” Release

    +

    Fixed

    +
      +
    • More typos fixed
    • +
    + +

    +0.5.13 2022-08-23 (tag)

    +

    The “I think I caught ‘em all!” Release

    +

    Fixed

    +
      +
    • Typo oauth2 => oauth as gem name in one more place.
    • +
    + +

    +0.5.12 2022-08-23 (tag)

    +

    The “Typoes are just the worst!” Release

    +

    Fixed

    +
      +
    • Typo oauth2 => oauth as gem name in a couple places.
    • +
    + +

    +0.5.11 2022-08-23 (tag)

    +

    The “Is this the last release with a silly name?” Release

    +

    Added

    +
      +
    • Post install note about v0.5.x EOL approaching in April, 2023 +

      Changed

      +
    • +
    • Improved documentation
    • +
    • Switched branch references from master to main
    • +
    • CI builds are now all green!
    • +
    + +

    [0.5.10] 2022-05-04 (tag)

    +

    The “Can it be the end of the line for 0.5.x?” Release

    +

    Added

    +
      +
    • Major updates to Documentation
    • +
    • More CI Hardening
    • +
    • Align CI builds with official Ruby Compatibility Matrix
    • +
    • Project tooling in preparation for final release of 0.5.x series +
        +
      • diffend
      • +
      +
    • +
    + +

    +0.5.9 2022-05-03 (tag)

    +

    Added

    +
      +
    • Documentation related to Ruby compatibility
    • +
    • Updated CHANGELOG.md formatting
    • +
    • Corrected CHANGELOG.md typos
    • +
    • Hardened the CI build for the next few years(?!)
    • +
    • Require MFA to push new version to Rubygems
    • +
    • Replace Hash Rocket syntax with JSON-style symbols where possible
    • +
    • Project tooling in preparation for final release of 0.5.x series +
        +
      • rubocop-ruby2_0
      • +
      • overcommit
      • +
      +
    • +
    + +

    +0.5.8 2021-11-10 (tag)

    +

    Added

    +
      +
    • Added more documentation files to packaged gem, e.g. SECURITY.md, CODE_OF_CONDUCT.md +

      Fixed

      +
    • +
    • Removed reference to RUBY_VERSION from gemspec, as it depends on rake release, which is problematic on some ruby engines. (by @pboling)
    • +
    + +

    +0.5.7 2021-11-02 (tag)

    +

    Added

    +
      +
    • Setup Rubocop (#205, #208 by @pboling)
    • +
    • Added CODE_OF_CONDUCT.md (#217, #218 by @pboling)
    • +
    • Added FUNDING.yml (#217, #218 by @pboling)
    • +
    • Added Client Certificate Options: :ssl_client_cert and :ssl_client_key (#136, #220 by @pboling)
    • +
    • Handle a nested array of hashes in OAuth::Helper.normalize (#80, #221 by @pboling) +

      Changed

      +
    • +
    • Switch from TravisCI to Github Actions (#202, #207, #176 by @pboling)
    • +
    • Upgrade webmock to v3.14.0 (#196 by @pboling)
    • +
    • Upgrade em-http-request to v1.1.7 (#173 by @pboling)
    • +
    • Upgrade mocha to v1.13.0 (#193 by @pboling)
    • +
    • HISTORY renamed to CHANGELOG.md, and follows Keep a Changelog (#214, #215 by @pboling)
    • +
    • CHANGELOG, LICENSE, and README now ship with packaged gem (#214, #215 by @pboling)
    • +
    • README.rdoc renamed to README.md (#217, #218 by @pboling)
    • +
    • Require plaintext signature method by default (#135 by @confiks & @pboling) +

      Fixed

      +
    • +
    • Fixed Infinite Redirect in v0.5.5, v0.5.6 (#186, #210 by @pboling)
    • +
    • Fixed NoMethodError on missing leading slash in path (#194, #211 by @pboling)
    • +
    • Fixed NoMethodError on nil request object (#165, #212 by @pboling)
    • +
    • Fixed Unsafe String Comparison (#156, #209 by @pboling and @drosseau)
    • +
    • Fixed typos in Gemspec (#204, #203, #208 by @pboling)
    • +
    • Copyright Notice in LICENSE - added correct years (#217, #218 by @pboling)
    • +
    • Fixed request proxy Class constant reference scopes - was missing :: in many places (#225, #226 by @pboling) +

      Removed

      +
    • +
    • Remove direct development dependency on nokogiri (#299 by @pboling)
    • +
    + +

    +0.5.6 2021-04-02 (tag)

    +

    Added

    +
      +
    • Add metadata to Gemspec file
    • +
    • Add support for PUT requests with Action Controller (#181) +

      Changed

      +
    • +
    • Change default timeout to be the same as Net::HTTP default, 60 seconds instead of 30 seconds.
    • +
    + +

    +0.5.5 2020-01-19 (tag)

    +

    Added

    +
      +
    • Add :allow_empty_params option (#155) +

      Changed

      +
    • +
    • Allow redirect to different host but same path
    • +
    • Various cleanups +

      Fixed

      +
    • +
    • Fixes ssl-noverify
    • +
    • Fixed README example (#158, #159, by @pboling)
    • +
    + +

    +0.5.4 2017-12-08 (tag)

    +

    Changed

    +
      +
    • Various cleanups (charliesome) +

      Fixed

      +
    • +
    • Fixes UnknownRequestType on Rails 5.1 for ActionDispatch::Request (xprazak2)
    • +
    + +

    +0.5.3 2017-05-24 (tag)

    +

    Fixed

    +
      +
    • Fix #145 - broken CLI required loading active_support (James Pinto) +

      Changed

      +
    • +
    • Removing legacy scripts (James Pinto)
    • +
    + +

    +0.5.2 2017-05-17 (tag)

    +

    Added

    +
      +
    • Adding a development dependency that had not been mentioned (James Pinto)
    • +
    • Adding CodeClimate (James Pinto)
    • +
    • Adding support to Ruby 2.4 and head (James Pinto) +

      Changed

      +
    • +
    • Use assert_nil so as to silence a Minitest 6 deprecation warning (James Pinto)
    • +
    • Stop bundling tests files in the gem (Michal Papis)
    • +
    • Minor cleanup on tests (James Pinto)
    • +
    • TravisCI no longer needs libcurl-dev (James Pinto)
    • +
    • Nokogiri 1.7 does not accept Ruby 2.0 (James Pinto)
    • +
    • Upgrading to CodeClimate 1.0 (James Pinto)
    • +
    • Locking gemspec to Rails 4 so as to allow our next version for Rails 5 (James Pinto)
    • +
    • moving development dependency to gemspec (James Pinto)
    • +
    • Silencing ‘Net::HTTPResponse#header is obsolete’ (James Pinto)
    • +
    • Silencing some test warnings (James Pinto)
    • +
    • Silencing ‘loading in progress, circular require considered harmful’ (James Pinto)
    • +
    • Silence ‘URI.escape obsolete’ (James Pinto)
    • +
    • Refactored CLI (James Pinto)
    • +
    • Moving test files into test/units/ (James Pinto)
    • +
    • Reimplementing #82 - Debug Output Option (James Pinto) +

      Fixed

      +
    • +
    • Fix #113 adding paths when a full URL has been specified (James Pinto)
    • +
    • Bug Fix, webmock 2.0 has introduced a new bug (James Pinto)
    • +
    • Making a test/support dir (James Pinto)
    • +
    • Fix #177 - Adjusting to webmock latest recommended implementation for minitest (James Pinto)
    • +
    + +

    +0.5.1 2016-02-29 (tag)

    +

    Added

    +
      +
    • Add license info to the gemspec (Robert Reiz) +

      Fixed

      +
    • +
    • Proper handling for empty query string in RequestToken#build_authorize_url (midchildan,
      +Harald Sitter)
    • +
    • Replace calls to String#blank? with its implementation (Sergio Gil Pérez de la Manga) +

      Changed

      +
    • +
    • Loosen some development dependencies. Add libcurl-dev to travis
    • +
    • Fixes to travis config. Switch to rubygems for installation and loading +

      Removed

      +
    • +
    • Remove obsolete comment (Arthur Nogueira Neves)
    • +
    • Remove jeweler from gemspec
    • +
    + +

    +0.5.0 2016-02-20 (tag)

    +

    Added

    +
      +
    • Add support for HTTP PATCH method (Richard Huang)
    • +
    • Allow reading private key from a string (Khaja Minhajuddin)
    • +
    • Add rest-client proxy (Khem Veasna)
    • +
    • Add byebug. (Kevin Hughes)
    • +
    • Allow reading certificate file path from environment variable. Add CentOS cert file path (Danil Vlasov) +

      Changed

      +
    • +
    • Replace jeweler with real spec and bundler tasks
    • +
    • Extract version to separate file
    • +
    • Use OpenSSL for all digest and hashing. Remove signature methods not defined by OAuth spec. (Kevin Hughes)
    • +
    • Change token requests to exclude oauth_body_hash. Update doc links in comments. (John Remmen) +

      Fixed

      +
    • +
    • Fix ability to pass in an authorize url with a query string (Roger Smith)
    • +
    • Fix bug in signature verification (r-stu31)
    • +
    • Use standard key name (oauth_token_secret) in Token#to_query (Craig Walker)
    • +
    • Fix error in CLI when using query without supplying a method (grafikchaos)
    • +
    • Compatibility fix for Typhoeus >= 0.5.0 (Chad Feller)
    • +
    • Rails 3+ / ActiveSupport::SafeBuffer patch (Clif Reeder)
    • +
    • Handle nil token gracefully for RequestToken#authorize_url (Brian John)
    • +
    • Fix typhoeus compatibility (Vladimir Mikhailov)
    • +
    • Fix oauth cli option parser on Ruby 2.2 (Felix Bünemann)
    • +
    • Update gemspec for security fixes. Convert to Minitest. Add .travis.yml. (Kevin Hughes)
    • +
    • Fix some warnings (amatsuda)
    • +
    • Various fixes/updates to README (Evan Arnold, Jonathan Camenisch, Brian John, Ankur Sethi)
    • +
    + +

    +0.4.7 2012-09-03 (tag)

    +

    Added

    +
      +
    • Set a configurable timeout for all requests (Rick Olson) +

      Fixed

      +
    • +
    • Fix merging paths if the path is not empty
    • +
    • Fix nested hash params in Consumer#request (Ernie Miller)
    • +
    + +

    +0.4.6 2012-04-21 (tag)

    +

    Changed

    +
      +
    • Make use the path component of the :site parameter (Jonathon M. Abbott) +

      Fixed

      +
    • +
    • Fixed nested attributes in #normalize (Shaliko Usubov)
    • +
    • Fixed post body’s being dropped in 1.9 (Steven Hammond)
    • +
    • Fixed PUT request handling (Anton Panasenko)
    • +
    + +

    +0.4.5 2011-06-25 (tag)

    +

    Added

    +
      +
    • Add explicit require for rsa/sha1 (Juris Galang)
    • +
    • Add gemtest support (Adrian Feldman) +

      Changed

      +
    • +
    • Use webmock to mock all http-requests in tests (Adrian Feldman)
    • +
    • Mention Typhoeus require in the README (Kim Ahlström)
    • +
    • Use Net::HTTPGenericRequest (Jakub Kuźma) +

      Fixed

      +
    • +
    • Fix POST Requests with Typhoeus proxy (niedhui)
    • +
    • Fix incorrect hardcoded port (Ian Taylor)
    • +
    + +

    +0.4.4 2010-10-31 (tag)

    +

    Added

    +
      +
    • Added support for Rails 3 in client/action_controller_request (Pelle) +

      Fixed

      +
    • +
    • Fix LoadError rescue in tests: return can’t be used in this context (Hans de Graaff)
    • +
    • HTTP headers should be strings. (seancribbs)
    • +
    • ensure consumer uri gets set back to original config even if an error occurs (Brian Finney)
    • +
    • Yahoo uses & to split records in OAuth headers (Brian Finney)
    • +
    + +

    +0.4.3 2010-09-01 (tag)

    +

    Fixed

    +
      +
    • Fix for em-http proxy (ichverstehe) +

      +0.4.2 2010-08-13 (tag)

      +

      Added

      +
    • +
    • Added Bundler (rc) Gemfile for easier dev/testing +

      Fixed

      +
    • +
    • Fixed compatibility with Ruby 1.9.2 (ecavazos)
    • +
    • Fixed the em-http request proxy (Joshua Hull)
    • +
    • Fix for oauth proxy string manipulation (Jakub Suder)
    • +
    + +

    +0.4.1 2010-06-16 (tag)

    +

    Added

    +
      +
    • Added support for using OAuth with proxies (Marsh Gardiner) +

      Fixed

      +
    • +
    • Rails 3 Compatibility fixes (Pelle Braendgaard)
    • +
    • Fixed load errors on tests for missing (non-required) libraries
    • +
    + +

    +0.4.0 2010-04-22 (tag)

    +

    Added

    +
      +
    • Added computation of oauth_body_hash as per OAuth Request Body Hash 1.0 Draft 4 (Michael Reinsch)
    • +
    • Added the optional oauth_session_handle parameter for the Yahoo implementation (Will Bailey)
    • +
    • Added optional block to OAuth::Consumer.get_*_token (Neill Pearman)
    • +
    • Exclude oauth_callback with :exclude_callback (Neill Pearman)
    • +
    • Support for Ruby 1.9 (Aaron Quint, Corey Donahoe, et al)
    • +
    • Support for Typhoeus (Bill Kocik)
    • +
    • Support for em-http (EventMachine) (Darcy Laycock)
    • +
    • Support for curb (André Luis Leal Cardoso Junior)
    • +
    • New website (Aaron Quint) +

      Changed

      +
    • +
    • Better marshalling implementation (Yoan Blanc)
    • +
    • Replaced hoe with Jeweler (Aaron Quint) +

      Fixed

      +
    • +
    • Strip extraneous spaces and line breaks from access_token responses (observed in the wild with Yahoo!’s OAuth+OpenID hybrid) (Eric Hartmann)
    • +
    • Stop double-escaping PLAINTEXT signatures (Jimmy Zimmerman)
    • +
    • OAuth::Client::Helper won’t override the specified oauth_version (Philip Kromer)
    • +
    • Fixed an encoding / multibyte issue (成田 一生)
    • +
    + +

    +0.3.6 2009-09-14 (tag)

    +

    Added

    +
      +
    • Added -B CLI option to use the :body authentication scheme (Seth)
    • +
    • Support POST and PUT with raw bodies (Yu-Shan Fung et al)
    • +
    • Added :ca_file consumer option to allow consumer specific certificate override. (Pelle) +

      Changed

      +
    • +
    • Test clean-up (Xavier Shay, Hannes Tydén) +

      Fixed

      +
    • +
    • Respect --method in authorize CLI command (Seth)
    • +
    + +

    +0.3.5 2009-06-03 (tag)

    +

    Added

    +
      +
    • +query CLI command to access protected resources (Seth)
    • +
    • Added -H, -Q CLI options for specifying the authentication scheme (Seth)
    • +
    • Added -O CLI option for specifying a file containing options (Seth)
    • +
    • Support streamable body contents for large request bodies (Seth Cousins)
    • +
    • Support for OAuth 1.0a (Seth)
    • +
    • Added proxy support to OAuth::Consumer (Marshall Huss)
    • +
    • Added –scope CLI option for Google’s ‘scope’ parameter (Seth)
    • +
    + +

    +0.3.4 2009-05-06 (tag)

    +

    Changed

    +
      +
    • OAuth::Client::Helper uses OAuth::Version::VERSION (chadisfaction) +

      Fixed

      +
    • +
    • Fix OAuth::RequestProxy::ActionControllerRequest’s handling of params (Tristan Groléat)
    • +
    + +

    +0.3.3 2009-05-04 (tag)

    +

    Added

    +
      +
    • Support for arguments in OAuth::Consumer#get_access_token (Matt Sanford)
    • +
    • Add gem version to user-agent header (Matt Sanford) +

      Changed

      +
    • +
    • Improved error handling for invalid Authorization headers (Matt Sanford)
    • +
    • Handle input from aggressive form encoding libraries (Matt Wood) +

      Fixed

      +
    • +
    • Corrected OAuth XMPP namespace (Seth)
    • +
    • Fixed signatures for non-ASCII under $KCODE other than ‘u’ (Matt Sanford)
    • +
    • Fixed edge cases in ActionControllerRequestProxy where params were being incorrectly signed (Marcos Wright Kuhns)
    • +
    + +

    +0.3.2 2009-03-23 (tag)

    +

    Added

    +
      +
    • Support applications using the MethodOverride Rack middleware (László Bácsi)
    • +
    • +authorize command for oauth CLI (Seth)
    • +
    • Initial support for Problem Reporting extension (Seth)
    • +
    • Verify SSL certificates if CA certificates are available (Seth)
    • +
    • Added help to the ‘oauth’ CLI (Seth) +

      Fixed

      +
    • +
    • 2xx statuses should be treated as success (Anders Conbere)
    • +
    • Fixed ActionController parameter escaping behavior (Thiago Arrais, László Bácsi, Brett Gibson, et al)
    • +
    • Fixed signature calculation when both options and a block were provided to OAuth::Signature::Base#initialize (Seth)
    • +
    • Fixed a problem when attempting to normalize MockRequest URIs (Seth)
    • +
    + +

    +0.3.1 2009-01-26 (tag)

    +

    Fixed

    +
      +
    • Fixed a problem with relative and absolute token request paths. (Michael Wood)
    • +
    + +

    +0.3.0 2009-01-25

    +

    Added

    +
      +
    • Support ActionController::Request from Edge Rails (László Bácsi)
    • +
    • Added #normalized_parameters to OAuth::RequestProxy::Base (Pelle)
    • +
    • Command-line app for generating signatures. (Seth) +

      Changed

      +
    • +
    • OAuth::Signature.sign and friends now yield the RequestProxy instead of the token when the passed block’s arity is 1. (Seth)
    • +
    • Improved test-cases and compatibility for encoding issues. (Pelle) +

      Fixed

      +
    • +
    • Correctly handle multi-valued parameters (Seth)
    • +
    • Token requests are made to the configured URL rather than generating a potentially incorrect one. (Kellan Elliott-McCrea)
    • +
    + +

    0.2.7 2008-09-10

    +

    The lets fix the last release release

    +

    Fixed

    +
      +
    • Fixed plain text signatures (Andrew Arrow)
    • +
    • Fixed RSA requests using OAuthTokens. (Philip Lipu Tsai)
    • +
    + +

    0.2.6 2008-09-09

    +

    The lets RSA release

    +

    Added

    +
      +
    • Improved support for Ruby 1.8.7 (Bill Kocik)
    • +
    • Added support for ‘private_key_file’ option for RSA signatures (Chris Mear) +

      Changed

      +
    • +
    • Improved RSA testing
    • +
    • Omit token when signing with RSA +

      Fixed

      +
    • +
    • Fixed RSA verification to support RSA providers now using Ruby and RSA
    • +
    • Fixed several edge cases where params were being incorrectly signed (Scott Hill)
    • +
    • Fixed RSA signing (choonkeat)
    • +
    + +

    0.2.2 2008-02-22

    +

    Lets actually support SSL release

    +

    Fixed

    +
      +
    • Use HTTPS when required.
    • +
    + +

    0.2 2008-1-19

    +

    All together now release

    +

    Changed

    +
      +
    • This is a big release, where we have merged the efforts of various parties into one common library.
      +This means there are definitely some API changes you should be aware of. They should be minimal
      +but please have a look at the unit tests.
    • +
    + +

    0.1.2 2007-12-1

    +

    Fixed

    +
      +
    • Fixed checks for missing OAuth params to improve performance
    • +
    • Includes Pat’s fix for getting the realm out.
    • +
    + +

    0.1.1 2007-11-26

    +

    Added

    +
      +
    • First release as a GEM
    • +
    • Moved all non-Rails functionality from the Rails plugin:
      +http://code.google.com/p/oauth-plugin/
    • +
    + +
    + + + +
    + + \ No newline at end of file diff --git a/docs/file.CITATION.html b/docs/file.CITATION.html new file mode 100644 index 00000000..776c0141 --- /dev/null +++ b/docs/file.CITATION.html @@ -0,0 +1,92 @@ + + + + + + + File: CITATION + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
    + + +

    cff-version: 1.2.0
    +title: oauth
    +message: >-
    + If you use this work and you want to cite it,
    + then you can use the metadata from this file.
    +type: software
    +authors:

    +
      +
    • given-names: Peter Hurn
      +family-names: Boling
      +email: peter@railsbling.com
      +affiliation: railsbling.com
      +orcid: ‘https://orcid.org/0009-0008-8519-441X’
      +identifiers:
    • +
    • type: url
      +value: ‘https://github.com/ruby-oauth/oauth’
      +description: oauth
      +repository-code: ‘https://github.com/ruby-oauth/oauth’
      +abstract: >-
      + oauth
      +license: See license file
    • +
    +
    + + + +
    + + \ No newline at end of file diff --git a/docs/file.CODE_OF_CONDUCT.html b/docs/file.CODE_OF_CONDUCT.html new file mode 100644 index 00000000..ef4a0d83 --- /dev/null +++ b/docs/file.CODE_OF_CONDUCT.html @@ -0,0 +1,201 @@ + + + + + + + File: CODE_OF_CONDUCT + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
    + + +

    Contributor Covenant Code of Conduct

    + +

    Our Pledge

    + +

    We as members, contributors, and leaders pledge to make participation in our
    +community a harassment-free experience for everyone, regardless of age, body
    +size, visible or invisible disability, ethnicity, sex characteristics, gender
    +identity and expression, level of experience, education, socio-economic status,
    +nationality, personal appearance, race, caste, color, religion, or sexual
    +identity and orientation.

    + +

    We pledge to act and interact in ways that contribute to an open, welcoming,
    +diverse, inclusive, and healthy community.

    + +

    Our Standards

    + +

    Examples of behavior that contributes to a positive environment for our
    +community include:

    + +
      +
    • Demonstrating empathy and kindness toward other people
    • +
    • Being respectful of differing opinions, viewpoints, and experiences
    • +
    • Giving and gracefully accepting constructive feedback
    • +
    • Accepting responsibility and apologizing to those affected by our mistakes,
      +and learning from the experience
    • +
    • Focusing on what is best not just for us as individuals, but for the overall
      +community
    • +
    + +

    Examples of unacceptable behavior include:

    + +
      +
    • The use of sexualized language or imagery, and sexual attention or advances of
      +any kind
    • +
    • Trolling, insulting or derogatory comments, and personal or political attacks
    • +
    • Public or private harassment
    • +
    • Publishing others’ private information, such as a physical or email address,
      +without their explicit permission
    • +
    • Other conduct which could reasonably be considered inappropriate in a
      +professional setting
    • +
    + +

    Enforcement Responsibilities

    + +

    Community leaders are responsible for clarifying and enforcing our standards of
    +acceptable behavior and will take appropriate and fair corrective action in
    +response to any behavior that they deem inappropriate, threatening, offensive,
    +or harmful.

    + +

    Community leaders have the right and responsibility to remove, edit, or reject
    +comments, commits, code, wiki edits, issues, and other contributions that are
    +not aligned to this Code of Conduct, and will communicate reasons for moderation
    +decisions when appropriate.

    + +

    Scope

    + +

    This Code of Conduct applies within all community spaces, and also applies when
    +an individual is officially representing the community in public spaces.
    +Examples of representing our community include using an official email address,
    +posting via an official social media account, or acting as an appointed
    +representative at an online or offline event.

    + +

    Enforcement

    + +

    Instances of abusive, harassing, or otherwise unacceptable behavior may be
    +reported to the community leaders responsible for enforcement at
    +Contact Maintainer.
    +All complaints will be reviewed and investigated promptly and fairly.

    + +

    All community leaders are obligated to respect the privacy and security of the
    +reporter of any incident.

    + +

    Enforcement Guidelines

    + +

    Community leaders will follow these Community Impact Guidelines in determining
    +the consequences for any action they deem in violation of this Code of Conduct:

    + +

    1. Correction

    + +

    Community Impact: Use of inappropriate language or other behavior deemed
    +unprofessional or unwelcome in the community.

    + +

    Consequence: A private, written warning from community leaders, providing
    +clarity around the nature of the violation and an explanation of why the
    +behavior was inappropriate. A public apology may be requested.

    + +

    2. Warning

    + +

    Community Impact: A violation through a single incident or series of
    +actions.

    + +

    Consequence: A warning with consequences for continued behavior. No
    +interaction with the people involved, including unsolicited interaction with
    +those enforcing the Code of Conduct, for a specified period of time. This
    +includes avoiding interactions in community spaces as well as external channels
    +like social media. Violating these terms may lead to a temporary or permanent
    +ban.

    + +

    3. Temporary Ban

    + +

    Community Impact: A serious violation of community standards, including
    +sustained inappropriate behavior.

    + +

    Consequence: A temporary ban from any sort of interaction or public
    +communication with the community for a specified period of time. No public or
    +private interaction with the people involved, including unsolicited interaction
    +with those enforcing the Code of Conduct, is allowed during this period.
    +Violating these terms may lead to a permanent ban.

    + +

    4. Permanent Ban

    + +

    Community Impact: Demonstrating a pattern of violation of community
    +standards, including sustained inappropriate behavior, harassment of an
    +individual, or aggression toward or disparagement of classes of individuals.

    + +

    Consequence: A permanent ban from any sort of public interaction within the
    +community.

    + +

    Attribution

    + +

    This Code of Conduct is adapted from the Contributor Covenant,
    +version 2.1, available at
    +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.

    + +

    Community Impact Guidelines were inspired by
    +Mozilla’s code of conduct enforcement ladder.

    + +

    For answers to common questions about this code of conduct, see the FAQ at
    +https://www.contributor-covenant.org/faq. Translations are available at
    +https://www.contributor-covenant.org/translations.

    + +
    + + + +
    + + \ No newline at end of file diff --git a/docs/file.CONTRIBUTING.html b/docs/file.CONTRIBUTING.html new file mode 100644 index 00000000..332d6e97 --- /dev/null +++ b/docs/file.CONTRIBUTING.html @@ -0,0 +1,318 @@ + + + + + + + File: CONTRIBUTING + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
    + + +

    Contributing

    + +

    Bug reports and pull requests are welcome on CodeBerg, GitLab, or GitHub.
    +This project should be a safe, welcoming space for collaboration, so contributors agree to adhere to
    +the code of conduct.

    + +

    To submit a patch, please fork the project, create a patch with tests, and send a pull request.

    + +

    Remember to Keep A Changelog if you make changes.

    + +

    Help out!

    + +

    Take a look at the reek list which is the file called REEK and find something to improve.

    + +

    Follow these instructions:

    + +
      +
    1. Fork the repository
    2. +
    3. Create a feature branch (git checkout -b my-new-feature)
    4. +
    5. Make some fixes.
    6. +
    7. Commit changes (git commit -am 'Added some feature')
    8. +
    9. Push to the branch (git push origin my-new-feature)
    10. +
    11. Make sure to add tests for it. This is important, so it doesn’t break in a future release.
    12. +
    13. Create new Pull Request.
    14. +
    + +

    Executables vs Rake tasks

    + +

    Executables shipped by oauth can be used with or without generating the binstubs.
    +They will work when oauth is installed globally (i.e., gem install oauth) and do not require that oauth be in your bundle.

    + +
      +
    • kettle-changelog
    • +
    • kettle-commit-msg
    • +
    • oauth-setup
    • +
    • kettle-dvcs
    • +
    • kettle-pre-release
    • +
    • kettle-readme-backers
    • +
    • kettle-release
    • +
    + +

    However, the rake tasks provided by oauth do require oauth to be added as a development dependency and loaded in your Rakefile.
    +See the full list of rake tasks in head of Rakefile

    + +

    Gemfile

    +
    group :development do
    +  gem "oauth", require: false
    +end
    +
    + +

    Rakefile

    +
    # Rakefile
    +require "oauth"
    +
    + +

    Environment Variables for Local Development

    + +

    Below are the primary environment variables recognized by stone_checksums (and its integrated tools). Unless otherwise noted, set boolean values to the string “true” to enable.

    + +

    General/runtime

    +
      +
    • DEBUG: Enable extra internal logging for this library (default: false)
    • +
    • REQUIRE_BENCH: Enable require_bench to profile requires (default: false)
    • +
    • CI: When set to true, adjusts default rake tasks toward CI behavior
    • +
    + +

    Coverage (kettle-soup-cover / SimpleCov)

    +
      +
    • K_SOUP_COV_DO: Enable coverage collection (default: true in .envrc)
    • +
    • K_SOUP_COV_FORMATTERS: Comma-separated list of formatters (html, xml, rcov, lcov, json, tty)
    • +
    • K_SOUP_COV_MIN_LINE: Minimum line coverage threshold (integer, e.g., 100)
    • +
    • K_SOUP_COV_MIN_BRANCH: Minimum branch coverage threshold (integer, e.g., 100)
    • +
    • K_SOUP_COV_MIN_HARD: Fail the run if thresholds are not met (true/false)
    • +
    • K_SOUP_COV_MULTI_FORMATTERS: Enable multiple formatters at once (true/false)
    • +
    • K_SOUP_COV_OPEN_BIN: Path to browser opener for HTML (empty disables auto-open)
    • +
    • MAX_ROWS: Limit console output rows for simplecov-console (e.g., 1)
      +Tip: When running a single spec file locally, you may want K_SOUP_COV_MIN_HARD=false to avoid failing thresholds for a partial run.
    • +
    + +

    GitHub API and CI helpers

    +
      +
    • GITHUB_TOKEN or GH_TOKEN: Token used by ci:act and release workflow checks to query GitHub Actions status at higher rate limits
    • +
    + +

    Releasing and signing

    +
      +
    • SKIP_GEM_SIGNING: If set, skip gem signing during build/release
    • +
    • GEM_CERT_USER: Username for selecting your public cert in certs/<USER>.pem (defaults to $USER)
    • +
    • SOURCE_DATE_EPOCH: Reproducible build timestamp. kettle-release will set this automatically for the session.
    • +
    + +

    Git hooks and commit message helpers (exe/kettle-commit-msg)

    +
      +
    • GIT_HOOK_BRANCH_VALIDATE: Branch name validation mode (e.g., jira) or false to disable
    • +
    • GIT_HOOK_FOOTER_APPEND: Append a footer to commit messages when goalie allows (true/false)
    • +
    • GIT_HOOK_FOOTER_SENTINEL: Required when footer append is enabled — a unique first-line sentinel to prevent duplicates
    • +
    • GIT_HOOK_FOOTER_APPEND_DEBUG: Extra debug output in the footer template (true/false)
    • +
    + +

    For a quick starting point, this repository’s .envrc shows sane defaults, and .env.local can override them locally.

    + +

    Appraisals

    + +

    From time to time the appraisal2 gemfiles in gemfiles/ will need to be updated.
    +They are created and updated with the commands:

    + +
    bin/rake appraisal:update
    +
    + +

    When adding an appraisal to CI, check the runner tool cache to see which runner to use.

    + +

    The Reek List

    + +

    Take a look at the reek list which is the file called REEK and find something to improve.

    + +

    To refresh the reek list:

    + +
    bundle exec reek > REEK
    +
    + +

    Run Tests

    + +

    To run all tests

    + +
    bundle exec rake test
    +
    + +

    Spec organization (required)

    + +
      +
    • One spec file per class/module. For each class or module under lib/, keep all of its unit tests in a single spec file under spec/ that mirrors the path and file name exactly: lib/oauth/release_cli.rb -> spec/oauth/release_cli_spec.rb.
    • +
    • Never add a second spec file for the same class/module. Examples of disallowed names: *_more_spec.rb, *_extra_spec.rb, *_status_spec.rb, or any other suffix that still targets the same class. If you find yourself wanting a second file, merge those examples into the canonical spec file for that class/module.
    • +
    • Exception: Integration specs that intentionally span multiple classes. Place these under spec/integration/ (or a clearly named integration folder), and do not directly mirror a single class. Name them after the scenario, not a class.
    • +
    • Migration note: If a duplicate spec file exists, move all examples into the canonical file and delete the duplicate. Do not leave stubs or empty files behind.
    • +
    + +

    Lint It

    + +

    Run all the default tasks, which includes running the gradually autocorrecting linter, rubocop-gradual.

    + +
    bundle exec rake
    +
    + +

    Or just run the linter.

    + +
    bundle exec rake rubocop_gradual:autocorrect
    +
    + +

    For more detailed information about using RuboCop in this project, please see the RUBOCOP.md guide. This project uses rubocop_gradual instead of vanilla RuboCop, which requires specific commands for checking violations.

    + +

    Important: Do not add inline RuboCop disables

    + +

    Never add # rubocop:disable ... / # rubocop:enable ... comments to code or specs (except when following the few existing rubocop:disable patterns for a rule already being disabled elsewhere in the code). Instead:

    + +
      +
    • Prefer configuration-based exclusions when a rule should not apply to certain paths or files (e.g., via .rubocop.yml).
    • +
    • When a violation is temporary and you plan to fix it later, record it in .rubocop_gradual.lock using the gradual workflow: +
        +
      • +bundle exec rake rubocop_gradual:autocorrect (preferred)
      • +
      • +bundle exec rake rubocop_gradual:force_update (only when you cannot fix the violations immediately)
      • +
      +
    • +
    + +

    As a general rule, fix style issues rather than ignoring them. For example, our specs should follow RSpec conventions like using described_class for the class under test.

    + +

    Contributors

    + +

    Your picture could be here!

    + +

    Contributors

    + +

    Made with contributors-img.

    + +

    Also see GitLab Contributors: https://gitlab.com/ruby-oauth/oauth/-/graphs/main

    + +

    For Maintainers

    + +

    One-time, Per-maintainer, Setup

    + +

    IMPORTANT: To sign a build,
    +a public key for signing gems will need to be picked up by the line in the
    +gemspec defining the spec.cert_chain (check the relevant ENV variables there).
    +All releases to RubyGems.org are signed releases.
    +See: RubyGems Security Guide

    + +

    NOTE: To build without signing the gem set SKIP_GEM_SIGNING to any value in the environment.

    + +

    To release a new version:

    + +

    Automated process

    + +
      +
    1. Update version.rb to contian the correct version-to-be-released.
    2. +
    3. Run bundle exec kettle-changelog.
    4. +
    5. Run bundle exec kettle-release.
    6. +
    + +

    Manual process

    + +
      +
    1. Run bin/setup && bin/rake as a “test, coverage, & linting” sanity check
    2. +
    3. Update the version number in version.rb, and ensure CHANGELOG.md reflects changes
    4. +
    5. Run bin/setup && bin/rake again as a secondary check, and to update Gemfile.lock +
    6. +
    7. Run git commit -am "🔖 Prepare release v<VERSION>" to commit the changes
    8. +
    9. Run git push to trigger the final CI pipeline before release, and merge PRs + +
    10. +
    11. Run export GIT_TRUNK_BRANCH_NAME="$(git remote show origin | grep 'HEAD branch' | cut -d ' ' -f5)" && echo $GIT_TRUNK_BRANCH_NAME +
    12. +
    13. Run git checkout $GIT_TRUNK_BRANCH_NAME +
    14. +
    15. Run git pull origin $GIT_TRUNK_BRANCH_NAME to ensure latest trunk code
    16. +
    17. Optional for older Bundler (< 2.7.0): Set SOURCE_DATE_EPOCH so rake build and rake release use the same timestamp and generate the same checksums +
        +
      • If your Bundler is >= 2.7.0, you can skip this; builds are reproducible by default.
      • +
      • Run export SOURCE_DATE_EPOCH=$EPOCHSECONDS && echo $SOURCE_DATE_EPOCH +
      • +
      • If the echo above has no output, then it didn’t work.
      • +
      • Note: zsh/datetime module is needed, if running zsh.
      • +
      • In older versions of bash you can use date +%s instead, i.e. export SOURCE_DATE_EPOCH=$(date +%s) && echo $SOURCE_DATE_EPOCH +
      • +
      +
    18. +
    19. Run bundle exec rake build +
    20. +
    21. Run bin/gem_checksums (more context 1, 2)
      +to create SHA-256 and SHA-512 checksums. This functionality is provided by the stone_checksums
      +gem. +
        +
      • The script automatically commits but does not push the checksums
      • +
      +
    22. +
    23. Sanity check the SHA256, comparing with the output from the bin/gem_checksums command: +
        +
      • sha256sum pkg/<gem name>-<version>.gem
      • +
      +
    24. +
    25. Run bundle exec rake release which will create a git tag for the version,
      +push git commits and tags, and push the .gem file to rubygems.org +
    26. +
    + +
    + + + +
    + + \ No newline at end of file diff --git a/docs/file.FUNDING.html b/docs/file.FUNDING.html new file mode 100644 index 00000000..2c7db7a3 --- /dev/null +++ b/docs/file.FUNDING.html @@ -0,0 +1,114 @@ + + + + + + + File: FUNDING + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
    + + +
    + +

    Official Discord 👉️ Live Chat on Discord

    + +

    Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

    + +

    OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

    + +

    Buy me a coffee Donate on Polar Donate to my FLOSS or refugee efforts at ko-fi.com Donate to my FLOSS or refugee efforts using Patreon

    + + + +

    🤑 Request for Help

    + +

    Maintainers have teeth and need to pay their dentists.
    +After getting laid off in an RIF in March and filled with many dozens of rejections,
    +I’m now spending ~60+ hours a week building open source tools.
    +I’m hoping to be able to pay for my kids’ health insurance this month,
    +so if you value the work I am doing, I need your support.
    +Please consider sponsoring me or the project.

    + +

    To join the community or get help 👇️ Join the Discord.

    + +

    Live Chat on Discord

    + +

    To say “thanks for maintaining such a great tool” ☝️ Join the Discord or 👇️ send money.

    + +

    Sponsor ruby-oauth/oauth on Open Source Collective 💌 Sponsor me on GitHub Sponsors 💌 Sponsor me on Liberapay 💌 Donate on PayPal

    + +

    Another Way to Support Open Source Software

    + +
    +

    How wonderful it is that nobody need wait a single moment before starting to improve the world.

    +—Anne Frank

    +
    + +

    I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions — totaling 79 hours of FLOSS coding over just the past seven days, a pretty regular week for me. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).

    + +

    If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in bundle fund.

    + +

    I’m developing a new library, floss_funding, designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.

    + +

    Floss-Funding.dev: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags

    + +
    + + + +
    + + \ No newline at end of file diff --git a/docs/file.LICENSE.html b/docs/file.LICENSE.html new file mode 100644 index 00000000..78a65b77 --- /dev/null +++ b/docs/file.LICENSE.html @@ -0,0 +1,70 @@ + + + + + + + File: LICENSE + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
    + + +
    The MIT License (MIT)

    Copyright (c) 2020-2025 Peter H. Boling, of Galtzo.com, and oauth contributors
    Copyright (c) 2007-2012, 2016-2017 Blaine Cook, Larry Halff, Pelle Braendgaard

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    + + + +
    + + \ No newline at end of file diff --git a/docs/file.README.html b/docs/file.README.html new file mode 100644 index 00000000..9163133c --- /dev/null +++ b/docs/file.README.html @@ -0,0 +1,562 @@ + + + + + + + File: README + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
    + + +

    Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0 ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5 oauth Logo by Aboling0, CC BY-SA 4.0

    + +

    🔑 Ruby OAuth

    + +

    Version GitHub tag (latest SemVer) License: MIT Downloads Rank Open Source Helpers CodeCov Test Coverage Coveralls Test Coverage QLTY Test Coverage QLTY Maintainability CI Heads CI Runtime Dependencies @ HEAD CI Current CI Truffle Ruby CI JRuby Deps Locked Deps Unlocked CI Supported CI Legacy CI Unsupported CI Ancient CI Test Coverage CI Style CodeQL

    + +

    if ci_badges.map(&:color).detect { it != "green"} ☝️ let me know, as I may have missed the discord notification.

    + +
    + +

    if ci_badges.map(&:color).all? { it == "green"} 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.

    + +

    OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal Buy me a coffee Donate on Polar Donate at ko-fi.com

    + +

    🌻 Synopsis

    + +

    OAuth 1.0a is an industry-standard protocol for authorization.

    + +

    This is a RubyGem for implementing both OAuth 1.0a clients and servers in Ruby applications.
    +See the sibling oauth2 gem for OAuth 2.0, 2.1, & OIDC clients in Ruby.

    + + + +

    OAuth Ruby has been maintained by a large number of talented
    +individuals over the years.
    +The primary maintainer since 2020 is Peter Boling (@pboling).

    + +

    💡 Info you can shake a stick at

    + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Tokens to Remember +Gem name Gem namespace +
Works with JRuby +JRuby 9.1 Compat JRuby 9.2 Compat JRuby 9.3 Compat
JRuby 9.4 Compat JRuby 10.0 Compat JRuby HEAD Compat +
Works with Truffle Ruby +Truffle Ruby 22.3 Compat Truffle Ruby 23.0 Compat
Truffle Ruby 23.1 Compat Truffle Ruby 24.1 Compat +
Works with MRI Ruby 3 +Ruby 3.0 Compat Ruby 3.1 Compat Ruby 3.2 Compat Ruby 3.3 Compat Ruby 3.4 Compat Ruby HEAD Compat +
Works with MRI Ruby 2 +Ruby 2.3 Compat Ruby 2.4 Compat Ruby 2.5 Compat Ruby 2.6 Compat Ruby 2.7 Compat +
Support & Community +Join Me on Daily.dev's RubyFriends Live Chat on Discord Get help from me on Upwork Get help from me on Codementor +
Source +Source on GitLab.com Source on CodeBerg.org Source on Github.com The best SHA: dQw4w9WgXcQ! +
Documentation +Current release on RubyDoc.info YARD on Galtzo.com Maintainer Blog Wiki +
Compliance +License: MIT 📄ilo-declaration-img Security Policy Contributor Covenant 2.1 SemVer 2.0.0 +
Style +Enforced Code Style Linter Keep-A-Changelog 1.0.0 Gitmoji Commits Compatibility appraised by: appraisal2 +
Maintainer 🎖️ +Follow Me on LinkedIn Follow Me on Ruby.Social Follow Me on Bluesky Contact Maintainer My technical writing +
+... 💖 +Find Me on WellFound: Find Me on CrunchBase My LinkTree More About Me 🧊 🐙 🛖 🧪 +
+ +

Compatibility

+ +

Compatible with MRI Ruby 2.3+, and concordant releases of JRuby, and TruffleRuby.

+ + + + + + + + + + + + + + +
🚚 Amazing test matrix was brought to you by🔎 appraisal2 🔎 and the color 💚 green 💚
👟 Check it out!github.com/appraisal-rb/appraisal2
+ +

Federated DVCS

+ +
+ Find this repo on other forges (Coming soon!) + +| Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions | +|-------------------------------------------------|-----------------------------------------------------------------------|---------------------------|--------------------------|---------------------------|--------------------------|------------------------------| +| 🧪 [ruby-oauth/oauth on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜wiki] | 🏀 Tiny Matrix | ➖ | +| 🧊 [ruby-oauth/oauth on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ | +| 🐙 [ruby-oauth/oauth on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | ➖ | 💯 Full Matrix | [💚][gh-discussions] | +| 🎮️ [Discord Server][✉️discord-invite] | [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] | [Let's][✉️discord-invite] | [talk][✉️discord-invite] | [about][✉️discord-invite] | [this][✉️discord-invite] | [library!][✉️discord-invite] | + +
+ +

Enterprise Support Tidelift +

+ +

Available as part of the Tidelift Subscription.

+ +
+ Need enterprise-level guarantees? + +The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. + +[![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift] + +- 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies +- 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar] +- 💡Tidelift pays maintainers to maintain the software you depend on!
📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers + +Alternatively: + +- [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] +- [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] +- [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] + +
+ +

✨ Installation

+ +

Install the gem and add to the application’s Gemfile by executing:

+ +
bundle add oauth
+
+ +

If bundler is not being used to manage dependencies, install the gem by executing:

+ +
gem install oauth
+
+ +

🔒 Secure Installation

+ +
+ For Medium or High Security Installations + +This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by +[stone_checksums][💎stone_checksums]. Be sure the gem you install hasn’t been tampered with +by following the instructions below. + +Add my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate: + +```console +gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem) +``` + +You only need to do that once. Then proceed to install with: + +```console +gem install oauth -P HighSecurity +``` + +The `HighSecurity` trust profile will verify signed gems, and not allow the installation of unsigned dependencies. + +If you want to up your security game full-time: + +```console +bundle config set --global trust-policy MediumSecurity +``` + +`MediumSecurity` instead of `HighSecurity` is necessary if not all the gems you use are signed. + +NOTE: Be prepared to track down certs for signed gems and add them the same way you added mine. + +
+ +

⚙️ Configuration

+ +

This is a ruby library which is intended to be used in creating Ruby Consumer
+and Service Provider applications. It is NOT a Rails plugin, but could easily
+be used for the foundation for such a Rails plugin.

+ +

This gem was originally extracted from @pelle’s oauth-plugin
+gem. After extraction that gem was made to depend on this gem.

+ +

Unfortunately, this gem does have some Rails related bits that are
+optional to load. You don’t need Rails! The Rails bits may be pulled out
+into a separate gem with the 1.x minor updates of this gem.

+ +

🔧 Basic Usage

+ +

Extensions

+ + + +

Examples

+ +

We need to specify the oauth_callback url explicitly, otherwise it defaults to
+“oob” (Out of Band)

+ +
callback_url = "http://127.0.0.1:3000/oauth/callback"
+
+ +

Create a new OAuth::Consumer instance by passing it a configuration hash:

+ +
oauth_consumer = OAuth::Consumer.new("key", "secret", site: "https://agree2")
+
+ +

Start the process by requesting a token

+ +
request_token = oauth_consumer.get_request_token(oauth_callback: callback_url)
+
+session[:token] = request_token.token
+session[:token_secret] = request_token.secret
+redirect_to request_token.authorize_url(oauth_callback: callback_url)
+
+ +

When user returns create an access_token

+ +
hash = {oauth_token: session[:token], oauth_token_secret: session[:token_secret]}
+request_token = OAuth::RequestToken.from_hash(oauth_consumer, hash)
+access_token = request_token.get_access_token
+# For 3-legged authorization, flow oauth_verifier is passed as param in callback
+# access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier])
+@photos = access_token.get("/photos.xml")
+
+ +

Now that you have an access token, you can use Typhoeus to interact with the
+OAuth provider if you choose.

+ +
require "typhoeus"
+require "oauth/request_proxy/typhoeus_request"
+oauth_params = {consumer: oauth_consumer, token: access_token}
+hydra = Typhoeus::Hydra.new
+req = Typhoeus::Request.new(uri, options) # :method needs to be specified in options
+oauth_helper = OAuth::Client::Helper.new(req, oauth_params.merge(request_uri: uri))
+req.options[:headers]["Authorization"] = oauth_helper.header # Signs the request
+hydra.queue(req)
+hydra.run
+@response = req.response
+
+ +

More Information

+ +
    +
  • RubyDoc Documentation: [![RubyDoc.info][🚎yard-img]][🚎yard]
  • +
  • Mailing List/Google Group: [![Mailing List][⛳mail-list-img]][⛳mail-list]
  • +
  • Live Chat on Gitter: [![Join the chat at https://gitter.im/ruby-oauth/oauth-ruby][🏘chat-img]][🏘chat]
  • +
  • Maintainer’s Blog: [![Blog][🚎blog-img]][🚎blog]
  • +
+ +

🦷 FLOSS Funding

+ +

While ruby-oauth tools are free software and will always be, the project would benefit immensely from some funding.
+Raising a monthly budget of… “dollars” would make the project more sustainable.

+ +

We welcome both individual and corporate sponsors! We also offer a
+wide array of funding channels to account for your preferences
+(although currently Open Collective is our preferred funding platform).

+ +

If you’re working in a company that’s making significant use of ruby-oauth tools we’d
+appreciate it if you suggest to your company to become a ruby-oauth sponsor.

+ +

You can support the development of ruby-oauth tools via
+GitHub Sponsors,
+Liberapay,
+PayPal,
+Open Collective
+and Tidelift.

+ + + + + + + + + + + + +
📍 NOTE
If doing a sponsorship in the form of donation is problematic for your company
from an accounting standpoint, we’d recommend the use of Tidelift,
where you can get a support-like subscription instead.
+ +

Open Collective for Individuals

+ + +

No backers yet. Be the first!
+

+ +

Support us with a monthly donation and help us continue our activities. [Become a backer]

+ +

Open Collective for Organizations

+ + +

No sponsors yet. Be the first!
+

+ +

Become a sponsor and get your logo on our README on GitHub with a link to your site. [Become a sponsor]

+ +

Another way to support open-source

+ +
+

How wonderful it is that nobody need wait a single moment before starting to improve the world.

+—Anne Frank

+
+ +

I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions — totaling 79 hours of FLOSS coding over just the past seven days, a pretty regular week for me. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).

+ +

If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in bundle fund.

+ +

I’m developing a new library, floss_funding, designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.

+ +

Floss-Funding.dev: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags

+ +

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal Buy me a coffee Donate on Polar Donate to my FLOSS or refugee efforts at ko-fi.com Donate to my FLOSS or refugee efforts using Patreon

+ +

🔐 Security

+ +

See SECURITY.md.

+ +

🤝 Contributing

+ +

If you need some ideas of where to help, you could work on adding more code coverage,
+or if it is already 💯 (see below) check reek, issues, or PRs,
+or use the gem and think about how it could be better.

+ +

We Keep A Changelog so if you make changes, remember to update it.

+ +

See CONTRIBUTING.md for more detailed instructions.

+ +

🚀 Release Instructions

+ +

See CONTRIBUTING.md.

+ +

Code Coverage

+ +

Coverage Graph

+ +

Coveralls Test Coverage

+ +

QLTY Test Coverage

+ +

🪇 Code of Conduct

+ +

Everyone interacting with this project’s codebases, issue trackers,
+chat rooms and mailing lists agrees to follow the Contributor Covenant 2.1.

+ +

🌈 Contributors

+ +

Contributors

+ +

Made with contributors-img.

+ +

Also see GitLab Contributors: https://gitlab.com/ruby-oauth/oauth/-/graphs/main

+ +
+ ⭐️ Star History + + + + + + Star History Chart + + + +
+ +

📌 Versioning

+ +

This Library adheres to Semantic Versioning 2.0.0.
+Violations of this scheme should be reported as bugs.
+Specifically, if a minor or patch version is released that breaks backward compatibility,
+a new version should be immediately released that restores compatibility.
+Breaking changes to the public API will only be introduced with new major versions.

+ +
+

dropping support for a platform is both obviously and objectively a breaking change

+—Jordan Harband (@ljharb, maintainer of SemVer) in SemVer issue 716

+
+ +

I understand that policy doesn’t work universally (“exceptions to every rule!”),
+but it is the policy here.
+As such, in many cases it is good to specify a dependency on this library using
+the Pessimistic Version Constraint with two digits of precision.

+ +

For example:

+ +
spec.add_dependency("oauth", "~> 1.0")
+
+ +
+📌 Is "Platform Support" part of the public API? More details inside. + +SemVer should, IMO, but doesn't explicitly, say that dropping support for specific Platforms +is a *breaking change* to an API. +It is obvious to many, but not all, and since the spec is silent, the bike shedding is endless. + +To get a better understanding of how SemVer is intended to work over a project's lifetime, +read this article from the creator of SemVer: + +- ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred] + +
+ +

See CHANGELOG.md for a list of releases.

+ +

📄 License

+ +

The gem is available as open source under the terms of
+the MIT License License: MIT.
+See LICENSE.txt for the official Copyright Notice.

+ + + + + +

🤑 A request for help

+ +

Maintainers have teeth and need to pay their dentists.
+After getting laid off in an RIF in March and filled with many dozens of rejections,
+I’m now spending ~60+ hours a week building open source tools.
+I’m hoping to be able to pay for my kids’ health insurance this month,
+so if you value the work I am doing, I need your support.
+Please consider sponsoring me or the project.

+ +

To join the community or get help 👇️ Join the Discord.

+ +

Live Chat on Discord

+ +

To say “thanks!” ☝️ Join the Discord or 👇️ send money.

+ +

Sponsor ruby-oauth/oauth on Open Source Collective 💌 Sponsor me on GitHub Sponsors 💌 Sponsor me on Liberapay 💌 Donate on PayPal

+ +

Please give the project a star ⭐ ♥.

+ +

Thanks for RTFM. ☺️

+ + + + + + + + \ No newline at end of file diff --git a/docs/file.REEK.html b/docs/file.REEK.html new file mode 100644 index 00000000..a0fc47a4 --- /dev/null +++ b/docs/file.REEK.html @@ -0,0 +1,71 @@ + + + + + + + File: REEK + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + +
+ + \ No newline at end of file diff --git a/docs/file.RUBOCOP.html b/docs/file.RUBOCOP.html new file mode 100644 index 00000000..974313be --- /dev/null +++ b/docs/file.RUBOCOP.html @@ -0,0 +1,171 @@ + + + + + + + File: RUBOCOP + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

RuboCop Usage Guide

+ +

Overview

+ +

A tale of two RuboCop plugin gems.

+ +

RuboCop Gradual

+ +

This project uses rubocop_gradual instead of vanilla RuboCop for code style checking. The rubocop_gradual tool allows for gradual adoption of RuboCop rules by tracking violations in a lock file.

+ +

RuboCop LTS

+ +

This project uses rubocop-lts to ensure, on a best-effort basis, compatibility with Ruby >= 1.9.2.
+RuboCop rules are meticulously configured by the rubocop-lts family of gems to ensure that a project is compatible with a specific version of Ruby. See: https://rubocop-lts.gitlab.io for more.

+ +

Checking RuboCop Violations

+ +

To check for RuboCop violations in this project, always use:

+ +
bundle exec rake rubocop_gradual:check
+
+ +

Do not use the standard RuboCop commands like:

+
    +
  • bundle exec rubocop
  • +
  • rubocop
  • +
+ +

Understanding the Lock File

+ +

The .rubocop_gradual.lock file tracks all current RuboCop violations in the project. This allows the team to:

+ +
    +
  1. Prevent new violations while gradually fixing existing ones
  2. +
  3. Track progress on code style improvements
  4. +
  5. Ensure CI builds don’t fail due to pre-existing violations
  6. +
+ +

Common Commands

+ +
    +
  • +Check violations +
      +
    • bundle exec rake rubocop_gradual
    • +
    • bundle exec rake rubocop_gradual:check
    • +
    +
  • +
  • +(Safe) Autocorrect violations, and update lockfile if no new violations +
      +
    • bundle exec rake rubocop_gradual:autocorrect
    • +
    +
  • +
  • +Force update the lock file (w/o autocorrect) to match violations present in code +
      +
    • bundle exec rake rubocop_gradual:force_update
    • +
    +
  • +
+ +

Workflow

+ +
    +
  1. Before submitting a PR, run bundle exec rake rubocop_gradual:autocorrect
    +a. or just the default bundle exec rake, as autocorrection is a pre-requisite of the default task.
  2. +
  3. If there are new violations, either: +
      +
    • Fix them in your code
    • +
    • Run bundle exec rake rubocop_gradual:force_update to update the lock file (only for violations you can’t fix immediately)
    • +
    +
  4. +
  5. Commit the updated .rubocop_gradual.lock file along with your changes
  6. +
+ +

Never add inline RuboCop disables

+ +

Do not add inline rubocop:disable / rubocop:enable comments anywhere in the codebase (including specs, except when following the few existing rubocop:disable patterns for a rule already being disabled elsewhere in the code). We handle exceptions in two supported ways:

+ +
    +
  • Permanent/structural exceptions: prefer adjusting the RuboCop configuration (e.g., in .rubocop.yml) to exclude a rule for a path or file pattern when it makes sense project-wide.
  • +
  • Temporary exceptions while improving code: record the current violations in .rubocop_gradual.lock via the gradual workflow: +
      +
    • +bundle exec rake rubocop_gradual:autocorrect (preferred; will autocorrect what it can and update the lock only if no new violations were introduced)
    • +
    • If needed, bundle exec rake rubocop_gradual:force_update (as a last resort when you cannot fix the newly reported violations immediately)
    • +
    +
  • +
+ +

In general, treat the rules as guidance to follow; fix violations rather than ignore them. For example, RSpec conventions in this project expect described_class to be used in specs that target a specific class under test.

+ +

Benefits of rubocop_gradual

+ +
    +
  • Allows incremental adoption of code style rules
  • +
  • Prevents CI failures due to pre-existing violations
  • +
  • Provides a clear record of code style debt
  • +
  • Enables focused efforts on improving code quality over time
  • +
+
+ + + +
+ + \ No newline at end of file diff --git a/docs/file.SECURITY.html b/docs/file.SECURITY.html new file mode 100644 index 00000000..62b77c04 --- /dev/null +++ b/docs/file.SECURITY.html @@ -0,0 +1,138 @@ + + + + + + + File: SECURITY + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Security Policy

+ +

Supported Versions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VersionSupportedPost-EOL / Enterprise
1.1.xTidelift Subscription
1.0.xTidelift Subscription
0.6.x🚨Tidelift Subscription
0.5.x🚨Tidelift Subscription
<= 0.5
+ +

LEGEND:
+✅ - Supported
+🚨 - Will only receive critical bug and security updates.
+⛔ - No Support

+ +

EOL Policy

+ +

Non-commercial support for the oldest version of Ruby (which itself is going EOL) may be dropped each year in April.

+ +

Security contact information

+ +

To report a security vulnerability, please use the
+Tidelift security contact.
+Tidelift will coordinate the fix and disclosure.

+ +

OAuth for Enterprise

+ +

Available as part of the Tidelift Subscription.

+ +

The maintainers of oauth and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. Learn more.

+ +

Additional Support

+ +

If you are interested in support for versions older than the latest release,
+please consider sponsoring the project / maintainer @ https://liberapay.com/pboling/donate,
+or find other sponsorship links in the README.

+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/file_list.html b/docs/file_list.html new file mode 100644 index 00000000..393218db --- /dev/null +++ b/docs/file_list.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + File List + + + +
+
+

File List

+ + + +
+ + +
+ + diff --git a/docs/frames.html b/docs/frames.html new file mode 100644 index 00000000..6586005f --- /dev/null +++ b/docs/frames.html @@ -0,0 +1,22 @@ + + + + + Documentation by YARD 0.9.37 + + + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..78464185 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,562 @@ + + + + + + + File: README + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
+ + +

Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0 ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5 oauth Logo by Aboling0, CC BY-SA 4.0

+ +

🔑 Ruby OAuth

+ +

Version GitHub tag (latest SemVer) License: MIT Downloads Rank Open Source Helpers CodeCov Test Coverage Coveralls Test Coverage QLTY Test Coverage QLTY Maintainability CI Heads CI Runtime Dependencies @ HEAD CI Current CI Truffle Ruby CI JRuby Deps Locked Deps Unlocked CI Supported CI Legacy CI Unsupported CI Ancient CI Test Coverage CI Style CodeQL

+ +

if ci_badges.map(&:color).detect { it != "green"} ☝️ let me know, as I may have missed the discord notification.

+ +
+ +

if ci_badges.map(&:color).all? { it == "green"} 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.

+ +

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal Buy me a coffee Donate on Polar Donate at ko-fi.com

+ +

🌻 Synopsis

+ +

OAuth 1.0a is an industry-standard protocol for authorization.

+ +

This is a RubyGem for implementing both OAuth 1.0a clients and servers in Ruby applications.
+See the sibling oauth2 gem for OAuth 2.0, 2.1, & OIDC clients in Ruby.

+ + + +

OAuth Ruby has been maintained by a large number of talented
+individuals over the years.
+The primary maintainer since 2020 is Peter Boling (@pboling).

+ +

💡 Info you can shake a stick at

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Tokens to Remember +Gem name Gem namespace +
Works with JRuby +JRuby 9.1 Compat JRuby 9.2 Compat JRuby 9.3 Compat
JRuby 9.4 Compat JRuby 10.0 Compat JRuby HEAD Compat +
Works with Truffle Ruby +Truffle Ruby 22.3 Compat Truffle Ruby 23.0 Compat
Truffle Ruby 23.1 Compat Truffle Ruby 24.1 Compat +
Works with MRI Ruby 3 +Ruby 3.0 Compat Ruby 3.1 Compat Ruby 3.2 Compat Ruby 3.3 Compat Ruby 3.4 Compat Ruby HEAD Compat +
Works with MRI Ruby 2 +Ruby 2.3 Compat Ruby 2.4 Compat Ruby 2.5 Compat Ruby 2.6 Compat Ruby 2.7 Compat +
Support & Community +Join Me on Daily.dev's RubyFriends Live Chat on Discord Get help from me on Upwork Get help from me on Codementor +
Source +Source on GitLab.com Source on CodeBerg.org Source on Github.com The best SHA: dQw4w9WgXcQ! +
Documentation +Current release on RubyDoc.info YARD on Galtzo.com Maintainer Blog Wiki +
Compliance +License: MIT 📄ilo-declaration-img Security Policy Contributor Covenant 2.1 SemVer 2.0.0 +
Style +Enforced Code Style Linter Keep-A-Changelog 1.0.0 Gitmoji Commits Compatibility appraised by: appraisal2 +
Maintainer 🎖️ +Follow Me on LinkedIn Follow Me on Ruby.Social Follow Me on Bluesky Contact Maintainer My technical writing +
+... 💖 +Find Me on WellFound: Find Me on CrunchBase My LinkTree More About Me 🧊 🐙 🛖 🧪 +
+ +

Compatibility

+ +

Compatible with MRI Ruby 2.3+, and concordant releases of JRuby, and TruffleRuby.

+ + + + + + + + + + + + + + +
🚚 Amazing test matrix was brought to you by🔎 appraisal2 🔎 and the color 💚 green 💚
👟 Check it out!github.com/appraisal-rb/appraisal2
+ +

Federated DVCS

+ +
+ Find this repo on other forges (Coming soon!) + +| Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions | +|-------------------------------------------------|-----------------------------------------------------------------------|---------------------------|--------------------------|---------------------------|--------------------------|------------------------------| +| 🧪 [ruby-oauth/oauth on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜wiki] | 🏀 Tiny Matrix | ➖ | +| 🧊 [ruby-oauth/oauth on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ | +| 🐙 [ruby-oauth/oauth on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | ➖ | 💯 Full Matrix | [💚][gh-discussions] | +| 🎮️ [Discord Server][✉️discord-invite] | [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] | [Let's][✉️discord-invite] | [talk][✉️discord-invite] | [about][✉️discord-invite] | [this][✉️discord-invite] | [library!][✉️discord-invite] | + +
+ +

Enterprise Support Tidelift +

+ +

Available as part of the Tidelift Subscription.

+ +
+ Need enterprise-level guarantees? + +The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. + +[![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift] + +- 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies +- 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar] +- 💡Tidelift pays maintainers to maintain the software you depend on!
📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and *supports* open source maintainers + +Alternatively: + +- [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] +- [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] +- [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] + +
+ +

✨ Installation

+ +

Install the gem and add to the application’s Gemfile by executing:

+ +
bundle add oauth
+
+ +

If bundler is not being used to manage dependencies, install the gem by executing:

+ +
gem install oauth
+
+ +

🔒 Secure Installation

+ +
+ For Medium or High Security Installations + +This gem is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by +[stone_checksums][💎stone_checksums]. Be sure the gem you install hasn’t been tampered with +by following the instructions below. + +Add my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate: + +```console +gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem) +``` + +You only need to do that once. Then proceed to install with: + +```console +gem install oauth -P HighSecurity +``` + +The `HighSecurity` trust profile will verify signed gems, and not allow the installation of unsigned dependencies. + +If you want to up your security game full-time: + +```console +bundle config set --global trust-policy MediumSecurity +``` + +`MediumSecurity` instead of `HighSecurity` is necessary if not all the gems you use are signed. + +NOTE: Be prepared to track down certs for signed gems and add them the same way you added mine. + +
+ +

⚙️ Configuration

+ +

This is a ruby library which is intended to be used in creating Ruby Consumer
+and Service Provider applications. It is NOT a Rails plugin, but could easily
+be used for the foundation for such a Rails plugin.

+ +

This gem was originally extracted from @pelle’s oauth-plugin
+gem. After extraction that gem was made to depend on this gem.

+ +

Unfortunately, this gem does have some Rails related bits that are
+optional to load. You don’t need Rails! The Rails bits may be pulled out
+into a separate gem with the 1.x minor updates of this gem.

+ +

🔧 Basic Usage

+ +

Extensions

+ + + +

Examples

+ +

We need to specify the oauth_callback url explicitly, otherwise it defaults to
+“oob” (Out of Band)

+ +
callback_url = "http://127.0.0.1:3000/oauth/callback"
+
+ +

Create a new OAuth::Consumer instance by passing it a configuration hash:

+ +
oauth_consumer = OAuth::Consumer.new("key", "secret", site: "https://agree2")
+
+ +

Start the process by requesting a token

+ +
request_token = oauth_consumer.get_request_token(oauth_callback: callback_url)
+
+session[:token] = request_token.token
+session[:token_secret] = request_token.secret
+redirect_to request_token.authorize_url(oauth_callback: callback_url)
+
+ +

When user returns create an access_token

+ +
hash = {oauth_token: session[:token], oauth_token_secret: session[:token_secret]}
+request_token = OAuth::RequestToken.from_hash(oauth_consumer, hash)
+access_token = request_token.get_access_token
+# For 3-legged authorization, flow oauth_verifier is passed as param in callback
+# access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier])
+@photos = access_token.get("/photos.xml")
+
+ +

Now that you have an access token, you can use Typhoeus to interact with the
+OAuth provider if you choose.

+ +
require "typhoeus"
+require "oauth/request_proxy/typhoeus_request"
+oauth_params = {consumer: oauth_consumer, token: access_token}
+hydra = Typhoeus::Hydra.new
+req = Typhoeus::Request.new(uri, options) # :method needs to be specified in options
+oauth_helper = OAuth::Client::Helper.new(req, oauth_params.merge(request_uri: uri))
+req.options[:headers]["Authorization"] = oauth_helper.header # Signs the request
+hydra.queue(req)
+hydra.run
+@response = req.response
+
+ +

More Information

+ +
    +
  • RubyDoc Documentation: [![RubyDoc.info][🚎yard-img]][🚎yard]
  • +
  • Mailing List/Google Group: [![Mailing List][⛳mail-list-img]][⛳mail-list]
  • +
  • Live Chat on Gitter: [![Join the chat at https://gitter.im/ruby-oauth/oauth-ruby][🏘chat-img]][🏘chat]
  • +
  • Maintainer’s Blog: [![Blog][🚎blog-img]][🚎blog]
  • +
+ +

🦷 FLOSS Funding

+ +

While ruby-oauth tools are free software and will always be, the project would benefit immensely from some funding.
+Raising a monthly budget of… “dollars” would make the project more sustainable.

+ +

We welcome both individual and corporate sponsors! We also offer a
+wide array of funding channels to account for your preferences
+(although currently Open Collective is our preferred funding platform).

+ +

If you’re working in a company that’s making significant use of ruby-oauth tools we’d
+appreciate it if you suggest to your company to become a ruby-oauth sponsor.

+ +

You can support the development of ruby-oauth tools via
+GitHub Sponsors,
+Liberapay,
+PayPal,
+Open Collective
+and Tidelift.

+ + + + + + + + + + + + +
📍 NOTE
If doing a sponsorship in the form of donation is problematic for your company
from an accounting standpoint, we’d recommend the use of Tidelift,
where you can get a support-like subscription instead.
+ +

Open Collective for Individuals

+ + +

No backers yet. Be the first!
+

+ +

Support us with a monthly donation and help us continue our activities. [Become a backer]

+ +

Open Collective for Organizations

+ + +

No sponsors yet. Be the first!
+

+ +

Become a sponsor and get your logo on our README on GitHub with a link to your site. [Become a sponsor]

+ +

Another way to support open-source

+ +
+

How wonderful it is that nobody need wait a single moment before starting to improve the world.

+—Anne Frank

+
+ +

I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions — totaling 79 hours of FLOSS coding over just the past seven days, a pretty regular week for me. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈‍ cats).

+ +

If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in bundle fund.

+ +

I’m developing a new library, floss_funding, designed to empower open-source developers like myself to get paid for the work we do, in a sustainable way. Please give it a look.

+ +

Floss-Funding.dev: 👉️ No network calls. 👉️ No tracking. 👉️ No oversight. 👉️ Minimal crypto hashing. 💡 Easily disabled nags

+ +

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal Buy me a coffee Donate on Polar Donate to my FLOSS or refugee efforts at ko-fi.com Donate to my FLOSS or refugee efforts using Patreon

+ +

🔐 Security

+ +

See SECURITY.md.

+ +

🤝 Contributing

+ +

If you need some ideas of where to help, you could work on adding more code coverage,
+or if it is already 💯 (see below) check reek, issues, or PRs,
+or use the gem and think about how it could be better.

+ +

We Keep A Changelog so if you make changes, remember to update it.

+ +

See CONTRIBUTING.md for more detailed instructions.

+ +

🚀 Release Instructions

+ +

See CONTRIBUTING.md.

+ +

Code Coverage

+ +

Coverage Graph

+ +

Coveralls Test Coverage

+ +

QLTY Test Coverage

+ +

🪇 Code of Conduct

+ +

Everyone interacting with this project’s codebases, issue trackers,
+chat rooms and mailing lists agrees to follow the Contributor Covenant 2.1.

+ +

🌈 Contributors

+ +

Contributors

+ +

Made with contributors-img.

+ +

Also see GitLab Contributors: https://gitlab.com/ruby-oauth/oauth/-/graphs/main

+ +
+ ⭐️ Star History + + + + + + Star History Chart + + + +
+ +

📌 Versioning

+ +

This Library adheres to Semantic Versioning 2.0.0.
+Violations of this scheme should be reported as bugs.
+Specifically, if a minor or patch version is released that breaks backward compatibility,
+a new version should be immediately released that restores compatibility.
+Breaking changes to the public API will only be introduced with new major versions.

+ +
+

dropping support for a platform is both obviously and objectively a breaking change

+—Jordan Harband (@ljharb, maintainer of SemVer) in SemVer issue 716

+
+ +

I understand that policy doesn’t work universally (“exceptions to every rule!”),
+but it is the policy here.
+As such, in many cases it is good to specify a dependency on this library using
+the Pessimistic Version Constraint with two digits of precision.

+ +

For example:

+ +
spec.add_dependency("oauth", "~> 1.0")
+
+ +
+📌 Is "Platform Support" part of the public API? More details inside. + +SemVer should, IMO, but doesn't explicitly, say that dropping support for specific Platforms +is a *breaking change* to an API. +It is obvious to many, but not all, and since the spec is silent, the bike shedding is endless. + +To get a better understanding of how SemVer is intended to work over a project's lifetime, +read this article from the creator of SemVer: + +- ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred] + +
+ +

See CHANGELOG.md for a list of releases.

+ +

📄 License

+ +

The gem is available as open source under the terms of
+the MIT License License: MIT.
+See LICENSE.txt for the official Copyright Notice.

+ + + + + +

🤑 A request for help

+ +

Maintainers have teeth and need to pay their dentists.
+After getting laid off in an RIF in March and filled with many dozens of rejections,
+I’m now spending ~60+ hours a week building open source tools.
+I’m hoping to be able to pay for my kids’ health insurance this month,
+so if you value the work I am doing, I need your support.
+Please consider sponsoring me or the project.

+ +

To join the community or get help 👇️ Join the Discord.

+ +

Live Chat on Discord

+ +

To say “thanks!” ☝️ Join the Discord or 👇️ send money.

+ +

Sponsor ruby-oauth/oauth on Open Source Collective 💌 Sponsor me on GitHub Sponsors 💌 Sponsor me on Liberapay 💌 Donate on PayPal

+ +

Please give the project a star ⭐ ♥.

+ +

Thanks for RTFM. ☺️

+ +
+ + + +
+ + \ No newline at end of file diff --git a/docs/js/app.js b/docs/js/app.js new file mode 100644 index 00000000..b5610eff --- /dev/null +++ b/docs/js/app.js @@ -0,0 +1,344 @@ +(function () { + var localStorage = {}, + sessionStorage = {}; + try { + localStorage = window.localStorage; + } catch (e) {} + try { + sessionStorage = window.sessionStorage; + } catch (e) {} + + function createSourceLinks() { + $(".method_details_list .source_code").before( + "[View source]" + ); + $(".toggleSource").toggle( + function () { + $(this).parent().nextAll(".source_code").slideDown(100); + $(this).text("Hide source"); + }, + function () { + $(this).parent().nextAll(".source_code").slideUp(100); + $(this).text("View source"); + } + ); + } + + function createDefineLinks() { + var tHeight = 0; + $(".defines").after(" more..."); + $(".toggleDefines").toggle( + function () { + tHeight = $(this).parent().prev().height(); + $(this).prev().css("display", "inline"); + $(this).parent().prev().height($(this).parent().height()); + $(this).text("(less)"); + }, + function () { + $(this).prev().hide(); + $(this).parent().prev().height(tHeight); + $(this).text("more..."); + } + ); + } + + function createFullTreeLinks() { + var tHeight = 0; + $(".inheritanceTree").toggle( + function () { + tHeight = $(this).parent().prev().height(); + $(this).parent().toggleClass("showAll"); + $(this).text("(hide)"); + $(this).parent().prev().height($(this).parent().height()); + }, + function () { + $(this).parent().toggleClass("showAll"); + $(this).parent().prev().height(tHeight); + $(this).text("show all"); + } + ); + } + + function searchFrameButtons() { + $(".full_list_link").click(function () { + toggleSearchFrame(this, $(this).attr("href")); + return false; + }); + window.addEventListener("message", function (e) { + if (e.data === "navEscape") { + $("#nav").slideUp(100); + $("#search a").removeClass("active inactive"); + $(window).focus(); + } + }); + + $(window).resize(function () { + if ($("#search:visible").length === 0) { + $("#nav").removeAttr("style"); + $("#search a").removeClass("active inactive"); + $(window).focus(); + } + }); + } + + function toggleSearchFrame(id, link) { + var frame = $("#nav"); + $("#search a").removeClass("active").addClass("inactive"); + if (frame.attr("src") === link && frame.css("display") !== "none") { + frame.slideUp(100); + $("#search a").removeClass("active inactive"); + } else { + $(id).addClass("active").removeClass("inactive"); + if (frame.attr("src") !== link) frame.attr("src", link); + frame.slideDown(100); + } + } + + function linkSummaries() { + $(".summary_signature").click(function () { + document.location = $(this).find("a").attr("href"); + }); + } + + function summaryToggle() { + $(".summary_toggle").click(function (e) { + e.preventDefault(); + localStorage.summaryCollapsed = $(this).text(); + $(".summary_toggle").each(function () { + $(this).text($(this).text() == "collapse" ? "expand" : "collapse"); + var next = $(this).parent().parent().nextAll("ul.summary").first(); + if (next.hasClass("compact")) { + next.toggle(); + next.nextAll("ul.summary").first().toggle(); + } else if (next.hasClass("summary")) { + var list = $('
    '); + list.html(next.html()); + list.find(".summary_desc, .note").remove(); + list.find("a").each(function () { + $(this).html($(this).find("strong").html()); + $(this).parent().html($(this)[0].outerHTML); + }); + next.before(list); + next.toggle(); + } + }); + return false; + }); + if (localStorage.summaryCollapsed == "collapse") { + $(".summary_toggle").first().click(); + } else { + localStorage.summaryCollapsed = "expand"; + } + } + + function constantSummaryToggle() { + $(".constants_summary_toggle").click(function (e) { + e.preventDefault(); + localStorage.summaryCollapsed = $(this).text(); + $(".constants_summary_toggle").each(function () { + $(this).text($(this).text() == "collapse" ? "expand" : "collapse"); + var next = $(this).parent().parent().nextAll("dl.constants").first(); + if (next.hasClass("compact")) { + next.toggle(); + next.nextAll("dl.constants").first().toggle(); + } else if (next.hasClass("constants")) { + var list = $('
    '); + list.html(next.html()); + list.find("dt").each(function () { + $(this).addClass("summary_signature"); + $(this).text($(this).text().split("=")[0]); + if ($(this).has(".deprecated").length) { + $(this).addClass("deprecated"); + } + }); + // Add the value of the constant as "Tooltip" to the summary object + list.find("pre.code").each(function () { + console.log($(this).parent()); + var dt_element = $(this).parent().prev(); + var tooltip = $(this).text(); + if (dt_element.hasClass("deprecated")) { + tooltip = "Deprecated. " + tooltip; + } + dt_element.attr("title", tooltip); + }); + list.find(".docstring, .tags, dd").remove(); + next.before(list); + next.toggle(); + } + }); + return false; + }); + if (localStorage.summaryCollapsed == "collapse") { + $(".constants_summary_toggle").first().click(); + } else { + localStorage.summaryCollapsed = "expand"; + } + } + + function generateTOC() { + if ($("#filecontents").length === 0) return; + var _toc = $('
      '); + var show = false; + var toc = _toc; + var counter = 0; + var tags = ["h2", "h3", "h4", "h5", "h6"]; + var i; + var curli; + if ($("#filecontents h1").length > 1) tags.unshift("h1"); + for (i = 0; i < tags.length; i++) { + tags[i] = "#filecontents " + tags[i]; + } + var lastTag = parseInt(tags[0][1], 10); + $(tags.join(", ")).each(function () { + if ($(this).parents(".method_details .docstring").length != 0) return; + if (this.id == "filecontents") return; + show = true; + var thisTag = parseInt(this.tagName[1], 10); + if (this.id.length === 0) { + var proposedId = $(this).attr("toc-id"); + if (typeof proposedId != "undefined") this.id = proposedId; + else { + var proposedId = $(this) + .text() + .replace(/[^a-z0-9-]/gi, "_"); + if ($("#" + proposedId).length > 0) { + proposedId += counter; + counter++; + } + this.id = proposedId; + } + } + if (thisTag > lastTag) { + for (i = 0; i < thisTag - lastTag; i++) { + if (typeof curli == "undefined") { + curli = $("
    1. "); + toc.append(curli); + } + toc = $("
        "); + curli.append(toc); + curli = undefined; + } + } + if (thisTag < lastTag) { + for (i = 0; i < lastTag - thisTag; i++) { + toc = toc.parent(); + toc = toc.parent(); + } + } + var title = $(this).attr("toc-title"); + if (typeof title == "undefined") title = $(this).text(); + curli = $('
      1. ' + title + "
      2. "); + toc.append(curli); + lastTag = thisTag; + }); + if (!show) return; + html = + ''; + $("#content").prepend(html); + $("#toc").append(_toc); + $("#toc .hide_toc").toggle( + function () { + $("#toc .top").slideUp("fast"); + $("#toc").toggleClass("hidden"); + $("#toc .title small").toggle(); + }, + function () { + $("#toc .top").slideDown("fast"); + $("#toc").toggleClass("hidden"); + $("#toc .title small").toggle(); + } + ); + } + + function navResizeFn(e) { + if (e.which !== 1) { + navResizeFnStop(); + return; + } + + sessionStorage.navWidth = e.pageX.toString(); + $(".nav_wrap").css("width", e.pageX); + $(".nav_wrap").css("-ms-flex", "inherit"); + } + + function navResizeFnStop() { + $(window).unbind("mousemove", navResizeFn); + window.removeEventListener("message", navMessageFn, false); + } + + function navMessageFn(e) { + if (e.data.action === "mousemove") navResizeFn(e.data.event); + if (e.data.action === "mouseup") navResizeFnStop(); + } + + function navResizer() { + $("#resizer").mousedown(function (e) { + e.preventDefault(); + $(window).mousemove(navResizeFn); + window.addEventListener("message", navMessageFn, false); + }); + $(window).mouseup(navResizeFnStop); + + if (sessionStorage.navWidth) { + navResizeFn({ which: 1, pageX: parseInt(sessionStorage.navWidth, 10) }); + } + } + + function navExpander() { + if (typeof pathId === "undefined") return; + var done = false, + timer = setTimeout(postMessage, 500); + function postMessage() { + if (done) return; + clearTimeout(timer); + var opts = { action: "expand", path: pathId }; + document.getElementById("nav").contentWindow.postMessage(opts, "*"); + done = true; + } + + window.addEventListener( + "message", + function (event) { + if (event.data === "navReady") postMessage(); + return false; + }, + false + ); + } + + function mainFocus() { + var hash = window.location.hash; + if (hash !== "" && $(hash)[0]) { + $(hash)[0].scrollIntoView(); + } + + setTimeout(function () { + $("#main").focus(); + }, 10); + } + + function navigationChange() { + // This works around the broken anchor navigation with the YARD template. + window.onpopstate = function () { + var hash = window.location.hash; + if (hash !== "" && $(hash)[0]) { + $(hash)[0].scrollIntoView(); + } + }; + } + + $(document).ready(function () { + navResizer(); + navExpander(); + createSourceLinks(); + createDefineLinks(); + createFullTreeLinks(); + searchFrameButtons(); + linkSummaries(); + summaryToggle(); + constantSummaryToggle(); + generateTOC(); + mainFocus(); + navigationChange(); + }); +})(); diff --git a/docs/js/full_list.js b/docs/js/full_list.js new file mode 100644 index 00000000..12bba48d --- /dev/null +++ b/docs/js/full_list.js @@ -0,0 +1,242 @@ +(function() { + +var $clicked = $(null); +var searchTimeout = null; +var searchCache = []; +var caseSensitiveMatch = false; +var ignoreKeyCodeMin = 8; +var ignoreKeyCodeMax = 46; +var commandKey = 91; + +RegExp.escape = function(text) { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); +} + +function escapeShortcut() { + $(document).keydown(function(evt) { + if (evt.which == 27) { + window.parent.postMessage('navEscape', '*'); + } + }); +} + +function navResizer() { + $(window).mousemove(function(e) { + window.parent.postMessage({ + action: 'mousemove', event: {pageX: e.pageX, which: e.which} + }, '*'); + }).mouseup(function(e) { + window.parent.postMessage({action: 'mouseup'}, '*'); + }); + window.parent.postMessage("navReady", "*"); +} + +function clearSearchTimeout() { + clearTimeout(searchTimeout); + searchTimeout = null; +} + +function enableLinks() { + // load the target page in the parent window + $('#full_list li').on('click', function(evt) { + $('#full_list li').removeClass('clicked'); + $clicked = $(this); + $clicked.addClass('clicked'); + evt.stopPropagation(); + + if (evt.target.tagName === 'A') return true; + + var elem = $clicked.find('> .item .object_link a')[0]; + var e = evt.originalEvent; + var newEvent = new MouseEvent(evt.originalEvent.type); + newEvent.initMouseEvent(e.type, e.canBubble, e.cancelable, e.view, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget); + elem.dispatchEvent(newEvent); + evt.preventDefault(); + return false; + }); +} + +function enableToggles() { + // show/hide nested classes on toggle click + $('#full_list a.toggle').on('click', function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + $(this).parent().parent().toggleClass('collapsed'); + $(this).attr('aria-expanded', function (i, attr) { + return attr == 'true' ? 'false' : 'true' + }); + highlight(); + }); + + // navigation of nested classes using keyboard + $('#full_list a.toggle').on('keypress',function(evt) { + // enter key is pressed + if (evt.which == 13) { + evt.stopPropagation(); + evt.preventDefault(); + $(this).parent().parent().toggleClass('collapsed'); + $(this).attr('aria-expanded', function (i, attr) { + return attr == 'true' ? 'false' : 'true' + }); + highlight(); + } + }); +} + +function populateSearchCache() { + $('#full_list li .item').each(function() { + var $node = $(this); + var $link = $node.find('.object_link a'); + if ($link.length > 0) { + searchCache.push({ + node: $node, + link: $link, + name: $link.text(), + fullName: $link.attr('title').split(' ')[0] + }); + } + }); +} + +function enableSearch() { + $('#search input').keyup(function(event) { + if (ignoredKeyPress(event)) return; + if (this.value === "") { + clearSearch(); + } else { + performSearch(this.value); + } + }); + + $('#full_list').after(""); +} + +function ignoredKeyPress(event) { + if ( + (event.keyCode > ignoreKeyCodeMin && event.keyCode < ignoreKeyCodeMax) || + (event.keyCode == commandKey) + ) { + return true; + } else { + return false; + } +} + +function clearSearch() { + clearSearchTimeout(); + $('#full_list .found').removeClass('found').each(function() { + var $link = $(this).find('.object_link a'); + $link.text($link.text()); + }); + $('#full_list, #content').removeClass('insearch'); + $clicked.parents().removeClass('collapsed'); + highlight(); +} + +function performSearch(searchString) { + clearSearchTimeout(); + $('#full_list, #content').addClass('insearch'); + $('#noresults').text('').hide(); + partialSearch(searchString, 0); +} + +function partialSearch(searchString, offset) { + var lastRowClass = ''; + var i = null; + for (i = offset; i < Math.min(offset + 50, searchCache.length); i++) { + var item = searchCache[i]; + var searchName = (searchString.indexOf('::') != -1 ? item.fullName : item.name); + var matchString = buildMatchString(searchString); + var matchRegexp = new RegExp(matchString, caseSensitiveMatch ? "" : "i"); + if (searchName.match(matchRegexp) == null) { + item.node.removeClass('found'); + item.link.text(item.link.text()); + } + else { + item.node.addClass('found'); + item.node.removeClass(lastRowClass).addClass(lastRowClass == 'r1' ? 'r2' : 'r1'); + lastRowClass = item.node.hasClass('r1') ? 'r1' : 'r2'; + item.link.html(item.name.replace(matchRegexp, "$&")); + } + } + if(i == searchCache.length) { + searchDone(); + } else { + searchTimeout = setTimeout(function() { + partialSearch(searchString, i); + }, 0); + } +} + +function searchDone() { + searchTimeout = null; + highlight(); + var found = $('#full_list li:visible').size(); + if (found === 0) { + $('#noresults').text('No results were found.'); + } else { + // This is read out to screen readers + $('#noresults').text('There are ' + found + ' results.'); + } + $('#noresults').show(); + $('#content').removeClass('insearch'); +} + +function buildMatchString(searchString, event) { + caseSensitiveMatch = searchString.match(/[A-Z]/) != null; + var regexSearchString = RegExp.escape(searchString); + if (caseSensitiveMatch) { + regexSearchString += "|" + + $.map(searchString.split(''), function(e) { return RegExp.escape(e); }). + join('.+?'); + } + return regexSearchString; +} + +function highlight() { + $('#full_list li:visible').each(function(n) { + $(this).removeClass('even odd').addClass(n % 2 == 0 ? 'odd' : 'even'); + }); +} + +/** + * Expands the tree to the target element and its immediate + * children. + */ +function expandTo(path) { + var $target = $(document.getElementById('object_' + path)); + $target.addClass('clicked'); + $target.removeClass('collapsed'); + $target.parentsUntil('#full_list', 'li').removeClass('collapsed'); + + $target.find('a.toggle').attr('aria-expanded', 'true') + $target.parentsUntil('#full_list', 'li').each(function(i, el) { + $(el).find('> div > a.toggle').attr('aria-expanded', 'true'); + }); + + if($target[0]) { + window.scrollTo(window.scrollX, $target.offset().top - 250); + highlight(); + } +} + +function windowEvents(event) { + var msg = event.data; + if (msg.action === "expand") { + expandTo(msg.path); + } + return false; +} + +window.addEventListener("message", windowEvents, false); + +$(document).ready(function() { + escapeShortcut(); + navResizer(); + enableLinks(); + enableToggles(); + populateSearchCache(); + enableSearch(); +}); + +})(); diff --git a/docs/js/jquery.js b/docs/js/jquery.js new file mode 100644 index 00000000..198b3ff0 --- /dev/null +++ b/docs/js/jquery.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.1 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
        a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
        "+""+"
        ",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
        t
        ",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
        ",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; +f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

        ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
        ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
        ","
        "],thead:[1,"","
        "],tr:[2,"","
        "],td:[3,"","
        "],col:[2,"","
        "],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
        ","
        "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() +{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
        ").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/docs/method_list.html b/docs/method_list.html new file mode 100644 index 00000000..e3111ba2 --- /dev/null +++ b/docs/method_list.html @@ -0,0 +1,1654 @@ + + + + + + + + + + + + + + + + + + Method List + + + +
        +
        +

        Method List

        + + + +
        + + +
        + + diff --git a/docs/top-level-namespace.html b/docs/top-level-namespace.html new file mode 100644 index 00000000..e2f7a493 --- /dev/null +++ b/docs/top-level-namespace.html @@ -0,0 +1,110 @@ + + + + + + + Top Level Namespace + + — Documentation by YARD 0.9.37 + + + + + + + + + + + + + + + + + + + +
        + + +

        Top Level Namespace + + + +

        +
        + + + + + + + + + + + +
        + +

        Defined Under Namespace

        +

        + + + Modules: ActionController, EventMachine, Net, OAuth + + + + +

        + + + + + + + + + +
        + + + +
        + + \ No newline at end of file diff --git a/lib/oauth/helper.rb b/lib/oauth/helper.rb index 3b46b129..b0e38506 100644 --- a/lib/oauth/helper.rb +++ b/lib/oauth/helper.rb @@ -18,11 +18,11 @@ def escape(value) end def _escape(string) - URI::RFC2396_PARSER.escape(string, OAuth::RESERVED_CHARACTERS) + URI::DEFAULT_PARSER.escape(string, OAuth::RESERVED_CHARACTERS) end def unescape(value) - URI::RFC2396_PARSER.unescape(value.gsub("+", "%2B")) + URI::DEFAULT_PARSER.unescape(value.gsub("+", "%2B")) end # Generate a random key of up to +size+ bytes. The value returned is Base64 encoded with non-word From a27962aabe456fcc55cd06780bab52a718873860 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 18:18:31 -0600 Subject: [PATCH 09/15] =?UTF-8?q?=E2=9E=96=20em-http-request?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - lazy Loaded so it can be optional, since some systems fail to install it, and it doesn't seem maintained --- .idea/oauth.iml | 4 +- .rubocop_gradual.lock | 8 +- CHANGELOG.md | 2 + Gemfile.lock | 4 +- gemfiles/modular/optional.gemfile | 6 + lib/oauth/client/em_http.rb | 205 +++++++++++---------- lib/oauth/optional.rb | 20 ++ lib/oauth/request_proxy/em_http_request.rb | 103 ++++++----- oauth.gemspec | 1 - 9 files changed, 192 insertions(+), 161 deletions(-) create mode 100644 lib/oauth/optional.rb diff --git a/.idea/oauth.iml b/.idea/oauth.iml index 9c912c5e..1a328d5a 100644 --- a/.idea/oauth.iml +++ b/.idea/oauth.iml @@ -74,7 +74,7 @@ - + @@ -125,7 +125,7 @@ - + diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index d5ae15ea..cd7cf395 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -34,11 +34,11 @@ "lib/oauth/tokens/consumer_token.rb:3696415131": [ [9, 5, 155, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 349576019] ], - "oauth.gemspec:108374725": [ + "oauth.gemspec:3033391650": [ [133, 3, 56, "Gemspec/DependencyVersion: Dependency version specification is required.", 644892567], - [154, 3, 40, "Gemspec/DependencyVersion: Dependency version specification is required.", 2300588954], - [156, 3, 44, "Gemspec/DependencyVersion: Dependency version specification is required.", 1905290578], - [157, 3, 46, "Gemspec/DependencyVersion: Dependency version specification is required.", 4289565910] + [153, 3, 40, "Gemspec/DependencyVersion: Dependency version specification is required.", 2300588954], + [155, 3, 44, "Gemspec/DependencyVersion: Dependency version specification is required.", 1905290578], + [156, 3, 46, "Gemspec/DependencyVersion: Dependency version specification is required.", 4289565910] ], "spec/oauth/backwards_compatibility_spec.rb:4041711732": [ [3, 16, 25, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 3956042931] diff --git a/CHANGELOG.md b/CHANGELOG.md index 403f21d6..0582036f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ Please file a bug if you notice a violation of semantic versioning. - converted minitest => rspec ### Deprecated ### Removed +- dependency on em-http-request + - now lazy Loaded so it can be optional, since some systems fail to install it, and it doesn't seem maintained ### Fixed ### Security diff --git a/Gemfile.lock b/Gemfile.lock index ff62953d..7c4930ac 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -162,7 +162,7 @@ GEM pp (0.6.2) prettyprint prettyprint (0.2.0) - prism (1.4.0) + prism (1.5.0) psych (5.2.6) date stringio @@ -301,7 +301,7 @@ GEM snaky_hash (2.0.3) hashie (>= 0.1.0, < 6) version_gem (>= 1.1.8, < 3) - standard (1.51.0) + standard (1.51.1) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) rubocop (~> 1.80.2) diff --git a/gemfiles/modular/optional.gemfile b/gemfiles/modular/optional.gemfile index dae6a950..854f8686 100644 --- a/gemfiles/modular/optional.gemfile +++ b/gemfiles/modular/optional.gemfile @@ -1 +1,7 @@ # Optional dependencies are not depended on directly, but may be used if present. + +# em-http-request depends on http_parser.rb, which is supposed to be, +# but appears to not be, compatible with JRuby. +# http_parser.rb is highly unmaintained, with no responses on issues for 2 years. +# em-http-request is even less maintained, with no releases in 5 years. +gem "em-http-request", "~> 1.1.7" # ruby >= 0 diff --git a/lib/oauth/client/em_http.rb b/lib/oauth/client/em_http.rb index 36ab6d27..0f47fcb5 100644 --- a/lib/oauth/client/em_http.rb +++ b/lib/oauth/client/em_http.rb @@ -1,123 +1,126 @@ # frozen_string_literal: true -require "em-http" require "oauth/helper" -require "oauth/request_proxy/em_http_request" +require "oauth/optional" -# Extensions for em-http so that we can use consumer.sign! with an EventMachine::HttpClient -# instance. This is purely syntactic sugar. -module EventMachine - class HttpClient - attr_reader :oauth_helper +if OAuth::Optional.em_http_available? + require "oauth/request_proxy/em_http_request" - # Add the OAuth information to an HTTP request. Depending on the options[:scheme] setting - # this may add a header, additional query string parameters, or additional POST body parameters. - # The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+ - # header. - # - # * http - Configured Net::HTTP instance, ignored in this scenario except for getting host. - # * consumer - OAuth::Consumer instance - # * token - OAuth::Token instance - # * options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+, - # +signature_method+, +nonce+, +timestamp+) - # - # This method also modifies the User-Agent header to add the OAuth gem version. - # - # See Also: {OAuth core spec version 1.0, section 5.4.1}[http://oauth.net/core/1.0#rfc.section.5.4.1] - def oauth!(http, consumer = nil, token = nil, options = {}) - options = { - request_uri: normalized_oauth_uri(http), - consumer: consumer, - token: token, - scheme: "header", - signature_method: nil, - nonce: nil, - timestamp: nil, - }.merge(options) + # Extensions for em-http so that we can use consumer.sign! with an EventMachine::HttpClient + # instance. This is purely syntactic sugar. + module EventMachine + class HttpClient + attr_reader :oauth_helper - @oauth_helper = OAuth::Client::Helper.new(self, options) - __send__(:"set_oauth_#{options[:scheme]}") - end + # Add the OAuth information to an HTTP request. Depending on the options[:scheme] setting + # this may add a header, additional query string parameters, or additional POST body parameters. + # The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+ + # header. + # + # * http - Configured Net::HTTP instance, ignored in this scenario except for getting host. + # * consumer - OAuth::Consumer instance + # * token - OAuth::Token instance + # * options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+, + # +signature_method+, +nonce+, +timestamp+) + # + # This method also modifies the User-Agent header to add the OAuth gem version. + # + # See Also: {OAuth core spec version 1.0, section 5.4.1}[http://oauth.net/core/1.0#rfc.section.5.4.1] + def oauth!(http, consumer = nil, token = nil, options = {}) + options = { + request_uri: normalized_oauth_uri(http), + consumer: consumer, + token: token, + scheme: "header", + signature_method: nil, + nonce: nil, + timestamp: nil, + }.merge(options) - # Create a string suitable for signing for an HTTP request. This process involves parameter - # normalization as specified in the OAuth specification. The exact normalization also depends - # on the options[:scheme] being used so this must match what will be used for the request - # itself. The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+ - # header. - # - # * http - Configured Net::HTTP instance - # * consumer - OAuth::Consumer instance - # * token - OAuth::Token instance - # * options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+, - # +signature_method+, +nonce+, +timestamp+) - # - # See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1] - def signature_base_string(http, consumer = nil, token = nil, options = {}) - options = { - request_uri: normalized_oauth_uri(http), - consumer: consumer, - token: token, - scheme: "header", - signature_method: nil, - nonce: nil, - timestamp: nil, - }.merge(options) + @oauth_helper = OAuth::Client::Helper.new(self, options) + __send__(:"set_oauth_#{options[:scheme]}") + end - OAuth::Client::Helper.new(self, options).signature_base_string - end + # Create a string suitable for signing for an HTTP request. This process involves parameter + # normalization as specified in the OAuth specification. The exact normalization also depends + # on the options[:scheme] being used so this must match what will be used for the request + # itself. The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+ + # header. + # + # * http - Configured Net::HTTP instance + # * consumer - OAuth::Consumer instance + # * token - OAuth::Token instance + # * options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+, + # +signature_method+, +nonce+, +timestamp+) + # + # See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1] + def signature_base_string(http, consumer = nil, token = nil, options = {}) + options = { + request_uri: normalized_oauth_uri(http), + consumer: consumer, + token: token, + scheme: "header", + signature_method: nil, + nonce: nil, + timestamp: nil, + }.merge(options) - # This code was lifted from the em-http-request because it was removed from - # the gem June 19, 2010 - # see: http://github.com/igrigorik/em-http-request/commit/d536fc17d56dbe55c487eab01e2ff9382a62598b - def normalize_uri - @normalized_uri ||= begin - uri = @conn.dup - encoded_query = encode_query(@conn, @req[:query]) - path, query = encoded_query.split("?", 2) - uri.query = query unless encoded_query.empty? - uri.path = path - uri + OAuth::Client::Helper.new(self, options).signature_base_string end - end - protected + # This code was lifted from the em-http-request because it was removed from + # the gem June 19, 2010 + # see: http://github.com/igrigorik/em-http-request/commit/d536fc17d56dbe55c487eab01e2ff9382a62598b + def normalize_uri + @normalized_uri ||= begin + uri = @conn.dup + encoded_query = encode_query(@conn, @req[:query]) + path, query = encoded_query.split("?", 2) + uri.query = query unless encoded_query.empty? + uri.path = path + uri + end + end + + protected - def combine_query(path, query, uri_query) - combined_query = if query.is_a?(Hash) - query.map { |k, v| encode_param(k, v) }.join("&") - else - query.to_s + def combine_query(path, query, uri_query) + combined_query = if query.is_a?(Hash) + query.map { |k, v| encode_param(k, v) }.join("&") + else + query.to_s + end + combined_query = [combined_query, uri_query].reject(&:empty?).join("&") unless uri_query.to_s.empty? + combined_query.to_s.empty? ? path : "#{path}?#{combined_query}" end - combined_query = [combined_query, uri_query].reject(&:empty?).join("&") unless uri_query.to_s.empty? - combined_query.to_s.empty? ? path : "#{path}?#{combined_query}" - end - # Since we expect to get the host etc details from the http instance (...), - # we create a fake url here. Surely this is a horrible, horrible idea? - def normalized_oauth_uri(http) - uri = URI.parse(normalize_uri.path) - uri.host = http.address - uri.port = http.port + # Since we expect to get the host etc details from the http instance (...), + # we create a fake url here. Surely this is a horrible, horrible idea? + def normalized_oauth_uri(http) + uri = URI.parse(normalize_uri.path) + uri.host = http.address + uri.port = http.port - uri.scheme = if http.respond_to?(:use_ssl?) && http.use_ssl? - "https" - else - "http" + uri.scheme = if http.respond_to?(:use_ssl?) && http.use_ssl? + "https" + else + "http" + end + uri.to_s end - uri.to_s - end - def set_oauth_header - req[:head] ||= {} - req[:head].merge!("Authorization" => @oauth_helper.header) - end + def set_oauth_header + req[:head] ||= {} + req[:head].merge!("Authorization" => @oauth_helper.header) + end - def set_oauth_body - raise NotImplementedError, "please use the set_oauth_header method instead" - end + def set_oauth_body + raise NotImplementedError, "please use the set_oauth_header method instead" + end - def set_oauth_query_string - raise NotImplementedError, "please use the set_oauth_header method instead" + def set_oauth_query_string + raise NotImplementedError, "please use the set_oauth_header method instead" + end end end end diff --git a/lib/oauth/optional.rb b/lib/oauth/optional.rb new file mode 100644 index 00000000..b9860afc --- /dev/null +++ b/lib/oauth/optional.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module OAuth + # Helpers for optional, lazily loaded integrations. + module Optional + class << self + # Try to load EventMachine HTTP client support provided by em-http-request. + # + # Returns true if available, false if the dependency is not installed. + # Never raises LoadError. + def em_http_available? + # em-http-request provides "em-http" entrypoint + require "em-http" + true + rescue LoadError + false + end + end + end +end diff --git a/lib/oauth/request_proxy/em_http_request.rb b/lib/oauth/request_proxy/em_http_request.rb index c908f5bb..a5a2b33a 100644 --- a/lib/oauth/request_proxy/em_http_request.rb +++ b/lib/oauth/request_proxy/em_http_request.rb @@ -1,74 +1,75 @@ # frozen_string_literal: true require "oauth/request_proxy/base" -# em-http also uses adddressable so there is no need to require uri. -require "em-http" +require "oauth/optional" require "cgi" -module OAuth - module RequestProxy - module EventMachine - class HttpRequest < OAuth::RequestProxy::Base - # A Proxy for use when you need to sign EventMachine::HttpClient instances. - # It needs to be called once the client is construct but before data is sent. - # Also see oauth/client/em-http - proxies ::EventMachine::HttpClient +if OAuth::Optional.em_http_available? + module OAuth + module RequestProxy + module EventMachine + class HttpRequest < OAuth::RequestProxy::Base + # A Proxy for use when you need to sign EventMachine::HttpClient instances. + # It needs to be called once the client is construct but before data is sent. + # Also see oauth/client/em-http + proxies ::EventMachine::HttpClient - # Request in this con + # Request in this con - def method - request.req[:method] - end + def method + request.req[:method] + end - def uri - request.conn.normalize.to_s - end + def uri + request.conn.normalize.to_s + end - def parameters - if options[:clobber_request] - options[:parameters] - else - all_parameters + def parameters + if options[:clobber_request] + options[:parameters] + else + all_parameters + end end - end - protected + protected - def all_parameters - merged_parameters({}, post_parameters, query_parameters, options[:parameters]) - end + def all_parameters + merged_parameters({}, post_parameters, query_parameters, options[:parameters]) + end - def query_parameters - quer = request.req[:query] - hash_quer = if quer.respond_to?(:merge) - quer - else - CGI.parse(quer.to_s) + def query_parameters + quer = request.req[:query] + hash_quer = if quer.respond_to?(:merge) + quer + else + CGI.parse(quer.to_s) + end + CGI.parse(request.conn.query.to_s).merge(hash_quer) end - CGI.parse(request.conn.query.to_s).merge(hash_quer) - end - def post_parameters - headers = request.req[:head] || {} - form_encoded = headers["Content-Type"].to_s.downcase.start_with?("application/x-www-form-urlencoded") - if %w[POST PUT].include?(method) && form_encoded - CGI.parse(request.normalize_body(request.req[:body]).to_s) - else - {} + def post_parameters + headers = request.req[:head] || {} + form_encoded = headers["Content-Type"].to_s.downcase.start_with?("application/x-www-form-urlencoded") + if %w[POST PUT].include?(method) && form_encoded + CGI.parse(request.normalize_body(request.req[:body]).to_s) + else + {} + end end - end - def merged_parameters(params, *extra_params) - extra_params.compact.each do |params_pairs| - params_pairs.each_pair do |key, value| - if params.key?(key) - params[key.to_s] += value - else - params[key.to_s] = [value].flatten + def merged_parameters(params, *extra_params) + extra_params.compact.each do |params_pairs| + params_pairs.each_pair do |key, value| + if params.key?(key) + params[key.to_s] += value + else + params[key.to_s] = [value].flatten + end end end + params end - params end end end diff --git a/oauth.gemspec b/oauth.gemspec index c47fece5..5c473db9 100644 --- a/oauth.gemspec +++ b/oauth.gemspec @@ -150,7 +150,6 @@ Thanks, |7eter l-|. l3oling # Development dependencies that require strictly newer Ruby versions should be in a "gemfile", # and preferably a modular one (see gemfiles/modular/*.gemfile). - spec.add_development_dependency("em-http-request", "~> 1.1.7") spec.add_development_dependency("mocha") spec.add_development_dependency("rack", ">= 2.0.0") spec.add_development_dependency("rack-test") From 8509168e96c09ad07a38d56b2982b39c7f7f0ae1 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 22:27:27 -0600 Subject: [PATCH 10/15] =?UTF-8?q?=E2=9C=A8=20Internal=20escape=20&=20unesc?= =?UTF-8?q?ape=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Stop relying on URI / CGI for escaping and unescaping - They are both unstable across supported versions of Ruby (including 3.5 HEAD) - Tests against Rails-specific code are now run in CI --- .envrc | 4 +- .github/workflows/coverage.yml | 6 +- .rubocop_gradual.lock | 2 +- CHANGELOG.md | 4 + Gemfile.lock | 146 ++++++++++++++++++ gemfiles/modular/optional.gemfile | 6 + lib/oauth/helper.rb | 26 +++- .../action_controller_request.rb | 8 +- .../request_proxy/action_dispatch_request.rb | 41 +++++ spec/oauth/helper_spec.rb | 67 ++++++++ .../request_proxy/action_controller_spec.rb | 13 +- 11 files changed, 307 insertions(+), 16 deletions(-) diff --git a/.envrc b/.envrc index ea38cf68..819027b1 100755 --- a/.envrc +++ b/.envrc @@ -21,8 +21,8 @@ export K_SOUP_COV_DO=true # Means you want code coverage export K_SOUP_COV_COMMAND_NAME="Test Coverage" # Available formats are html, xml, rcov, lcov, json, tty export K_SOUP_COV_FORMATTERS="html,xml,rcov,lcov,json,tty" -export K_SOUP_COV_MIN_BRANCH=52 # Means you want to enforce X% branch coverage -export K_SOUP_COV_MIN_LINE=82 # Means you want to enforce X% line coverage +export K_SOUP_COV_MIN_BRANCH=50 # Means you want to enforce X% branch coverage +export K_SOUP_COV_MIN_LINE=81 # Means you want to enforce X% line coverage export K_SOUP_COV_MIN_HARD=true # Means you want the build to fail if the coverage thresholds are not met export K_SOUP_COV_MULTI_FORMATTERS=true export K_SOUP_COV_OPEN_BIN= # Means don't try to open coverage results in browser diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index ba438c26..45ef0023 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -6,8 +6,8 @@ permissions: id-token: write env: - K_SOUP_COV_MIN_BRANCH: 52 - K_SOUP_COV_MIN_LINE: 82 + K_SOUP_COV_MIN_BRANCH: 50 + K_SOUP_COV_MIN_LINE: 81 K_SOUP_COV_MIN_HARD: true K_SOUP_COV_FORMATTERS: "xml,rcov,lcov,tty" K_SOUP_COV_DO: true @@ -115,7 +115,7 @@ jobs: hide_complexity: true indicators: true output: both - thresholds: '82 52' + thresholds: '81 50' continue-on-error: ${{ matrix.experimental != 'false' }} - name: Add Coverage PR Comment diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index cd7cf395..e7e01ef7 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -61,7 +61,7 @@ [212, 7, 83, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [214].", 4182076280], [214, 7, 96, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [212].", 3226867591] ], - "spec/oauth/helper_spec.rb:2421896228": [ + "spec/oauth/helper_spec.rb:3023999608": [ [3, 1, 28, "RSpec/SpecFilePathFormat: Spec path should end with `o_auth/helper*_spec.rb`.", 2306013808] ], "spec/oauth/net_http_client_spec.rb:797264696": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 0582036f..bef39cbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ Please file a bug if you notice a violation of semantic versioning. ## [Unreleased] ### Added - kettle-dev v1.1.18 +- Internal escape & unescape methods + - Stop relying on URI / CGI for escaping and unescaping + - They are both unstable across supported versions of Ruby (including 3.5 HEAD) +- Tests against Rails-specific code are now run in CI ### Changed - converted minitest => rspec ### Deprecated diff --git a/Gemfile.lock b/Gemfile.lock index 7c4930ac..3bd1da2a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -29,6 +29,77 @@ PATH GEM remote: https://rubygems.org/ specs: + actioncable (8.0.2.1) + actionpack (= 8.0.2.1) + activesupport (= 8.0.2.1) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (8.0.2.1) + actionpack (= 8.0.2.1) + activejob (= 8.0.2.1) + activerecord (= 8.0.2.1) + activestorage (= 8.0.2.1) + activesupport (= 8.0.2.1) + mail (>= 2.8.0) + actionmailer (8.0.2.1) + actionpack (= 8.0.2.1) + actionview (= 8.0.2.1) + activejob (= 8.0.2.1) + activesupport (= 8.0.2.1) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (8.0.2.1) + actionview (= 8.0.2.1) + activesupport (= 8.0.2.1) + nokogiri (>= 1.8.5) + rack (>= 2.2.4) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (8.0.2.1) + actionpack (= 8.0.2.1) + activerecord (= 8.0.2.1) + activestorage (= 8.0.2.1) + activesupport (= 8.0.2.1) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (8.0.2.1) + activesupport (= 8.0.2.1) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (8.0.2.1) + activesupport (= 8.0.2.1) + globalid (>= 0.3.6) + activemodel (8.0.2.1) + activesupport (= 8.0.2.1) + activerecord (8.0.2.1) + activemodel (= 8.0.2.1) + activesupport (= 8.0.2.1) + timeout (>= 0.4.0) + activestorage (8.0.2.1) + actionpack (= 8.0.2.1) + activejob (= 8.0.2.1) + activerecord (= 8.0.2.1) + activesupport (= 8.0.2.1) + marcel (~> 1.0) + activesupport (8.0.2.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) ansi (1.5.0) @@ -41,14 +112,17 @@ GEM base64 (0.3.0) benchmark (0.4.1) bigdecimal (3.2.3) + builder (3.3.0) bundler-audit (0.9.2) bundler (>= 1.2.0, < 3) thor (~> 1.0) concurrent-ruby (1.3.5) + connection_pool (2.5.4) cookiejar (0.3.4) crack (1.0.0) bigdecimal rexml + crass (1.0.6) date (3.4.1) debug (1.11.0) irb (~> 1.10) @@ -58,6 +132,7 @@ GEM diffy (3.4.4) docile (1.4.1) domain_name (0.6.20240107) + drb (2.2.3) dry-configurable (1.3.0) dry-core (~> 1.1) zeitwerk (~> 2.6) @@ -97,6 +172,7 @@ GEM base64 eventmachine (>= 1.0.0.beta.4) erb (5.0.2) + erubi (1.13.1) ethon (0.15.0) ffi (>= 1.15.0) eventmachine (1.2.7) @@ -106,12 +182,16 @@ GEM version_gem (~> 1.1, >= 1.1.4) gitmoji-regex (1.0.3) version_gem (~> 1.1, >= 1.1.8) + globalid (1.2.1) + activesupport (>= 6.1) hashdiff (1.2.1) hashie (5.0.0) http-accept (1.7.0) http-cookie (1.0.8) domain_name (~> 0.5) http_parser.rb (0.8.0) + i18n (1.14.7) + concurrent-ruby (~> 1.0) io-console (0.8.1) irb (1.15.2) pp (>= 0.6.0) @@ -144,14 +224,35 @@ GEM language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) + loofah (2.24.1) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) mime-types (3.7.0) logger mime-types-data (~> 3.2025, >= 3.2025.0507) mime-types-data (3.2025.0909) + mini_mime (1.1.5) + minitest (5.25.5) mocha (2.7.1) ruby2_keywords (>= 0.0.5) mutex_m (0.3.0) + net-imap (0.5.10) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.1) + net-protocol netrc (0.11.0) + nio4r (2.7.4) nokogiri (1.18.9-x86_64-linux-gnu) racc (~> 1.4) ostruct (0.6.3) @@ -169,8 +270,42 @@ GEM public_suffix (6.0.2) racc (1.8.1) rack (3.2.1) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) rack-test (2.2.0) rack (>= 1.3) + rackup (2.2.1) + rack (>= 3) + rails (8.0.2.1) + actioncable (= 8.0.2.1) + actionmailbox (= 8.0.2.1) + actionmailer (= 8.0.2.1) + actionpack (= 8.0.2.1) + actiontext (= 8.0.2.1) + actionview (= 8.0.2.1) + activejob (= 8.0.2.1) + activemodel (= 8.0.2.1) + activerecord (= 8.0.2.1) + activestorage (= 8.0.2.1) + activesupport (= 8.0.2.1) + bundler (>= 1.15.0) + railties (= 8.0.2.1) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.0.2.1) + actionpack (= 8.0.2.1) + activesupport (= 8.0.2.1) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.3.0) rbs (3.9.5) @@ -279,6 +414,7 @@ GEM ruby2_keywords (0.0.5) ruby_engine (2.0.3) ruby_version (1.0.3) + securerandom (0.4.1) silent_stream (1.0.12) logger (~> 1.2) version_gem (>= 1.1.8, < 3) @@ -330,11 +466,16 @@ GEM delegate (~> 0.1) rspec (~> 3.0) timecop (>= 0.7, < 1) + timeout (0.4.3) typhoeus (1.5.0) ethon (>= 0.9.0, < 0.16.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.1.0) + uri (1.0.3) + useragent (0.16.11) vcr (6.3.1) base64 version_gem (1.1.9) @@ -342,6 +483,10 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) + websocket-driver (0.8.0) + base64 + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) yard (0.9.37) yard-relative_markdown_links (0.5.0) nokogiri (>= 1.14.3, < 2) @@ -372,6 +517,7 @@ DEPENDENCIES oauth-tty! rack (>= 2.0.0) rack-test + rails (>= 5.0) rake (~> 13.0) rdoc (~> 6.11) reek (~> 6.5) diff --git a/gemfiles/modular/optional.gemfile b/gemfiles/modular/optional.gemfile index 854f8686..61ee95f9 100644 --- a/gemfiles/modular/optional.gemfile +++ b/gemfiles/modular/optional.gemfile @@ -5,3 +5,9 @@ # http_parser.rb is highly unmaintained, with no responses on issues for 2 years. # em-http-request is even less maintained, with no releases in 5 years. gem "em-http-request", "~> 1.1.7" # ruby >= 0 + +# This gem ships with Rails-specific integrations because it began life +# as a Rails plugin, and they haven't been extracted yet. +# They are not required for, or by, the core library, +# and must be required explicitly. +gem "rails", ">= 5.0" diff --git a/lib/oauth/helper.rb b/lib/oauth/helper.rb index b0e38506..78d670a9 100644 --- a/lib/oauth/helper.rb +++ b/lib/oauth/helper.rb @@ -18,11 +18,33 @@ def escape(value) end def _escape(string) - URI::DEFAULT_PARSER.escape(string, OAuth::RESERVED_CHARACTERS) + # Percent-encode per RFC 3986 (unreserved: A-Z a-z 0-9 - . _ ~) + # Encode by byte to ensure stable behavior across Ruby versions and encodings. + bytes = string.to_s.b.bytes + bytes.map do |b| + ch = b.chr + if ch =~ OAuth::RESERVED_CHARACTERS + "%%%02X" % b + else + ch + end + end.join end def unescape(value) - URI::DEFAULT_PARSER.unescape(value.gsub("+", "%2B")) + # Do NOT treat "+" as space; OAuth treats '+' as a literal plus unless percent-encoded. + str = value.to_s.gsub("+", "%2B") + # Decode %HH sequences; leave malformed sequences intact. + decoded = str.gsub(/%([0-9A-Fa-f]{2})/) { Regexp.last_match(1).to_i(16).chr } + # Prefer UTF-8 when the decoded bytes form valid UTF-8; otherwise, return as binary. + begin + utf8 = decoded.dup + utf8.force_encoding(Encoding::UTF_8) + decoded = utf8 if utf8.valid_encoding? + rescue NameError + # Older Rubies without Encoding constants: keep original encoding. + end + decoded end # Generate a random key of up to +size+ bytes. The value returned is Base64 encoded with non-word diff --git a/lib/oauth/request_proxy/action_controller_request.rb b/lib/oauth/request_proxy/action_controller_request.rb index fb192459..608bd43a 100644 --- a/lib/oauth/request_proxy/action_controller_request.rb +++ b/lib/oauth/request_proxy/action_controller_request.rb @@ -16,14 +16,18 @@ def method end def uri - request.url + options[:uri] || request.url end def parameters if options[:clobber_request] options[:parameters] || {} else - params = request_params.merge(query_params).merge(header_params) + # Rails proxies should expose array-style values for params to align with + # historical oauth gem behavior / specs. Header params remain scalars. + rq = wrap_values(request_params) + qq = wrap_values(query_params) + params = rq.merge(qq).merge(header_params) params.stringify_keys! if params.respond_to?(:stringify_keys!) params.merge(options[:parameters] || {}) end diff --git a/lib/oauth/request_proxy/action_dispatch_request.rb b/lib/oauth/request_proxy/action_dispatch_request.rb index a94141eb..34229839 100644 --- a/lib/oauth/request_proxy/action_dispatch_request.rb +++ b/lib/oauth/request_proxy/action_dispatch_request.rb @@ -6,6 +6,47 @@ module OAuth module RequestProxy class ActionDispatchRequest < OAuth::RequestProxy::RackRequest proxies ::ActionDispatch::Request + + # Prefer the explicitly provided URI, which carries scheme/host info + # when ActionDispatch env may be minimal in tests. + def uri + options[:uri] || super + end + + # Rails' ActionDispatch proxy should expose array-style parameters + # for request/query params to align with legacy oauth gem expectations. + def parameters + if options[:clobber_request] + options[:parameters] || {} + else + rq = wrap_values(request_params) + qq = wrap_values(query_params) + params = rq.merge(qq).merge(header_params) + params.merge(options[:parameters] || {}) + end + end + + protected + + def query_params + # ActionDispatch::Request responds to GET + request.GET + end + + def request_params + if request.content_type && request.content_type.to_s.downcase.start_with?("application/x-www-form-urlencoded") + request.POST + else + {} + end + end + + def wrap_values(hash) + return {} unless hash + hash.each_with_object({}) do |(k, v), acc| + acc[k] = (v.is_a?(Array) || v.nil?) ? v : [v] + end + end end end end diff --git a/spec/oauth/helper_spec.rb b/spec/oauth/helper_spec.rb index 159702ec..477702a5 100644 --- a/spec/oauth/helper_spec.rb +++ b/spec/oauth/helper_spec.rb @@ -94,4 +94,71 @@ ]) end end + + describe "::escape" do + it "leaves unreserved characters unchanged (RFC3986)" do + # unreserved = ALPHA / DIGIT / '-' / '.' / '_' / '~' + input = "AZaz09-._~" + expect(described_class.escape(input)).to eq(input) + end + + it "percent-encodes reserved and other characters (space, plus, etc)" do + input = " a+b*c%/&=" + # space -> %20, plus -> %2B, asterisk -> %2A, percent -> %25, slash -> %2F, ampersand -> %26, equals -> %3D + expect(described_class.escape(input)).to eq("%20a%2Bb%2Ac%25%2F%26%3D") + end + + it "encodes non-ASCII characters by UTF-8 bytes (e.g., é)" do + input = "Café" + # 'é' -> 0xC3 0xA9 in UTF-8 + expect(described_class.escape(input)).to eq("Caf%C3%A9") + end + + it "handles binary-encoded strings by bytes and forces UTF-8 on error" do + bytes = [0xC3, 0xA9] # UTF-8 for 'é' + suspicious = bytes.pack("C*").force_encoding(Encoding::ASCII_8BIT) + # Prepend simple ASCII to ensure mix + input = "X" + suspicious + expect(described_class.escape(input)).to eq("X%C3%A9") + end + end + + describe "::unescape" do + it "decodes percent-encoded sequences (including %20 to space)" do + expect(described_class.unescape("Hello%20World%21")).to eq("Hello World!") + end + + it "does NOT treat '+' as space" do + # Important OAuth semantic: '+' should be literal plus unless percent-encoded + expect(described_class.unescape("a+b")).to eq("a+b") + expect(described_class.unescape("a%2Bb")).to eq("a+b") + end + + it "decodes multibyte UTF-8 sequences back to original" do + expect(described_class.unescape("Caf%C3%A9")).to eq("Café") + snowman = "%E2%98%83" # U+2603 + expect(described_class.unescape(snowman)).to eq("\u2603") + end + + it "leaves malformed percent sequences intact" do + expect(described_class.unescape("%ZZ")).to eq("%ZZ") + expect(described_class.unescape("abc%2")).to eq("abc%2") + expect(described_class.unescape("%")).to eq("%") + end + + it "round-trips escape -> unescape for representative inputs" do + samples = [ + "simple", + "AZaz09-._~", + "a b+c&d=e/f%g*h", + "Café", + "\u2603 and space", + ] + samples.each do |s| + enc = described_class.escape(s) + dec = described_class.unescape(enc) + expect(dec).to eq(s) + end + end + end end diff --git a/spec/oauth/request_proxy/action_controller_spec.rb b/spec/oauth/request_proxy/action_controller_spec.rb index d40e59c6..7955d208 100644 --- a/spec/oauth/request_proxy/action_controller_spec.rb +++ b/spec/oauth/request_proxy/action_controller_spec.rb @@ -6,17 +6,18 @@ RSpec.describe OAuth::RequestProxy::ActionControllerRequest do it "proxies ActionController::TestRequest with params" do - # Use a minimal Rack env that ActionController::TestRequest can wrap + # Use a minimal Rack env that ActionController::TestRequest can wrap. + # In Rails 8, TestRequest parameter parsing relies on the request body for form-encoded POSTs. env = { - "REQUEST_METHOD" => "GET", - "rack.input" => StringIO.new(""), - "QUERY_STRING" => "foo=bar", + "REQUEST_METHOD" => "POST", + "rack.input" => StringIO.new("foo=bar"), + "CONTENT_TYPE" => "application/x-www-form-urlencoded", "PATH_INFO" => "/widgets", } - req = ActionController::TestRequest.create(env) + req = ActionDispatch::Request.new(env) proxy = OAuth::RequestProxy.proxy(req, {uri: "http://example.com/widgets"}) - expect(proxy.method).to eq("GET") + expect(proxy.method).to eq("POST") expect(proxy.normalized_uri).to eq("http://example.com/widgets") expect(proxy.parameters).to include("foo" => ["bar"]) end From ee3425353093dc03663247500b81049faf90c69b Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 22:44:47 -0600 Subject: [PATCH 11/15] =?UTF-8?q?=F0=9F=92=9A=20add=20backports=20to=20fix?= =?UTF-8?q?=20vcr=20on=20Ruby=202.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/oauth.iml | 41 +++++++++++++++++++++++++++++++++++++++++ .rubocop_gradual.lock | 2 +- Gemfile.lock | 1 + oauth.gemspec | 10 +++++----- spec/ext/backports.rb | 4 ++++ spec/spec_helper.rb | 5 ++++- 6 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 spec/ext/backports.rb diff --git a/.idea/oauth.iml b/.idea/oauth.iml index 1a328d5a..9a5437b4 100644 --- a/.idea/oauth.iml +++ b/.idea/oauth.iml @@ -11,6 +11,17 @@ + + + + + + + + + + + @@ -19,11 +30,14 @@ + + + @@ -31,6 +45,7 @@ + @@ -41,16 +56,19 @@ + + + @@ -62,11 +80,21 @@ + + + + + + + + + + @@ -79,7 +107,13 @@ + + + + + + @@ -116,6 +150,7 @@ + @@ -135,12 +170,18 @@ + + + + + + diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index e7e01ef7..d5920964 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -34,7 +34,7 @@ "lib/oauth/tokens/consumer_token.rb:3696415131": [ [9, 5, 155, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 349576019] ], - "oauth.gemspec:3033391650": [ + "oauth.gemspec:1987611209": [ [133, 3, 56, "Gemspec/DependencyVersion: Dependency version specification is required.", 644892567], [153, 3, 40, "Gemspec/DependencyVersion: Dependency version specification is required.", 2300588954], [155, 3, 44, "Gemspec/DependencyVersion: Dependency version specification is required.", 1905290578], diff --git a/Gemfile.lock b/Gemfile.lock index 3bd1da2a..b7b79fac 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -498,6 +498,7 @@ PLATFORMS DEPENDENCIES appraisal2 (~> 3.0) + backports (~> 3.25, >= 3.25.1) benchmark (~> 0.4, >= 0.4.1) bundler-audit (~> 0.9.2) debug (>= 1.1) diff --git a/oauth.gemspec b/oauth.gemspec index 5c473db9..ce5c3b44 100644 --- a/oauth.gemspec +++ b/oauth.gemspec @@ -190,9 +190,7 @@ Thanks, |7eter l-|. l3oling spec.add_development_dependency("gitmoji-regex", "~> 1.0", ">= 1.0.3") # ruby >= 2.3.0 # HTTP recording for deterministic specs - # It seems that somehow just having a newer version of appraisal installed breaks - # Ruby 2.3 and 2.4 even if their bundle specifies an older version, - # and as a result it can only be a dependency in the appraisals. + # Ruby 2.3 / 2.4 can fail with: # | An error occurred while loading spec_helper. # | Failure/Error: require "vcr" # | @@ -202,6 +200,8 @@ Thanks, |7eter l-|. l3oling # | # ./spec/config/vcr.rb:3:in `' # | # ./spec/spec_helper.rb:8:in `require_relative' # | # ./spec/spec_helper.rb:8:in `' - spec.add_development_dependency("vcr", ">= 4") # 6.0 claims to support ruby >= 2.3, but fails on ruby 2.4 - spec.add_development_dependency("webmock", ">= 3") # Last version to support ruby >= 2.3 + # So that's why we need backports. + spec.add_development_dependency("backports", "~> 3.25", ">= 3.25.1") # ruby >= 0 + spec.add_development_dependency("vcr", ">= 4") # 6.0 claims to support ruby >= 2.3, but fails on ruby 2.4 + spec.add_development_dependency("webmock", ">= 3") # Last version to support ruby >= 2.3 end diff --git a/spec/ext/backports.rb b/spec/ext/backports.rb new file mode 100644 index 00000000..322d8f4d --- /dev/null +++ b/spec/ext/backports.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +require "backports/2.5.0/hash/transform_keys" +require "backports/2.5.0/string/delete_prefix" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 340ed658..a37183ed 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true -# External libs +# Global Extensions +require_relative "ext/backports" + +# External libraries # N/A # External RSpec & related config From ab778a82b368a6ce48dfba5813e113c7f15f0e6c Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Fri, 12 Sep 2025 22:51:39 -0600 Subject: [PATCH 12/15] =?UTF-8?q?=F0=9F=92=9A=20update=20code=20coverage?= =?UTF-8?q?=20threshold?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/locked_deps.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/locked_deps.yml b/.github/workflows/locked_deps.yml index 38e380a7..c44033a0 100644 --- a/.github/workflows/locked_deps.yml +++ b/.github/workflows/locked_deps.yml @@ -25,8 +25,8 @@ env: # because it would be redundant with the coverage workflow. # Also we can validate all output formats without breaking CodeCov, # since we aren't submitting these reports anywhere. - K_SOUP_COV_MIN_BRANCH: 56 - K_SOUP_COV_MIN_LINE: 84 + K_SOUP_COV_MIN_BRANCH: 54 + K_SOUP_COV_MIN_LINE: 83 K_SOUP_COV_MIN_HARD: false K_SOUP_COV_FORMATTERS: "html,xml,rcov,lcov,json,tty" K_SOUP_COV_DO: true From d8d8068034d4e617301e2619e75b46c29eecc26f Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 13 Sep 2025 00:30:09 -0600 Subject: [PATCH 13/15] =?UTF-8?q?=F0=9F=91=B7=20Disable=20workflows=20that?= =?UTF-8?q?=20may=20interfere=20with=20Org-level=20Advanced=20Security=20C?= =?UTF-8?q?onfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - also discord-notifier.yml, as it is unused for now --- .github/{workflows => disabled-workflows}/codeql-analysis.yml | 0 .github/{workflows => disabled-workflows}/dependency-review.yml | 0 .github/{workflows => disabled-workflows}/discord-notifier.yml | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename .github/{workflows => disabled-workflows}/codeql-analysis.yml (100%) rename .github/{workflows => disabled-workflows}/dependency-review.yml (100%) rename .github/{workflows => disabled-workflows}/discord-notifier.yml (100%) diff --git a/.github/workflows/codeql-analysis.yml b/.github/disabled-workflows/codeql-analysis.yml similarity index 100% rename from .github/workflows/codeql-analysis.yml rename to .github/disabled-workflows/codeql-analysis.yml diff --git a/.github/workflows/dependency-review.yml b/.github/disabled-workflows/dependency-review.yml similarity index 100% rename from .github/workflows/dependency-review.yml rename to .github/disabled-workflows/dependency-review.yml diff --git a/.github/workflows/discord-notifier.yml b/.github/disabled-workflows/discord-notifier.yml similarity index 100% rename from .github/workflows/discord-notifier.yml rename to .github/disabled-workflows/discord-notifier.yml From 625aefb4110a5cf576861f51fae7eee3b33586bd Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 13 Sep 2025 00:50:05 -0600 Subject: [PATCH 14/15] =?UTF-8?q?=F0=9F=94=A5=20remove=20disabled=20GHA=20?= =?UTF-8?q?workflows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../disabled-workflows/codeql-analysis.yml | 70 ------------------- .../disabled-workflows/dependency-review.yml | 20 ------ .../disabled-workflows/discord-notifier.yml | 41 ----------- 3 files changed, 131 deletions(-) delete mode 100644 .github/disabled-workflows/codeql-analysis.yml delete mode 100644 .github/disabled-workflows/dependency-review.yml delete mode 100644 .github/disabled-workflows/discord-notifier.yml diff --git a/.github/disabled-workflows/codeql-analysis.yml b/.github/disabled-workflows/codeql-analysis.yml deleted file mode 100644 index 45a8ec2c..00000000 --- a/.github/disabled-workflows/codeql-analysis.yml +++ /dev/null @@ -1,70 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ main, '*-stable' ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ main, '*-stable' ] - schedule: - - cron: '35 1 * * 5' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'ruby' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://git.io/codeql-language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v5 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 diff --git a/.github/disabled-workflows/dependency-review.yml b/.github/disabled-workflows/dependency-review.yml deleted file mode 100644 index 046e9c88..00000000 --- a/.github/disabled-workflows/dependency-review.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Dependency Review Action -# -# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. -# -# Source repository: https://github.com/actions/dependency-review-action -# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement -name: 'Dependency Review' -on: [pull_request] - -permissions: - contents: read - -jobs: - dependency-review: - runs-on: ubuntu-latest - steps: - - name: 'Checkout Repository' - uses: actions/checkout@v5 - - name: 'Dependency Review' - uses: actions/dependency-review-action@v4 diff --git a/.github/disabled-workflows/discord-notifier.yml b/.github/disabled-workflows/discord-notifier.yml deleted file mode 100644 index 2b638d59..00000000 --- a/.github/disabled-workflows/discord-notifier.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Discord Notify -permissions: - contents: read - -on: - check_run: - types: [completed] - discussion: - types: [ created ] - discussion_comment: - types: [ created ] - fork: - gollum: - issues: - types: [ opened ] - issue_comment: - types: [ created ] - pull_request: - types: [ opened, reopened, closed ] - release: - types: [ published ] - watch: - types: [ started ] - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - notify: - if: false - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - - name: Actions Status Discord - uses: sarisia/actions-status-discord@v1 - if: always() - with: - webhook: ${{ secrets.DISCORD_WEBHOOK }} - status: ${{ job.status }} - username: GitHub Actions From a0bf3a2cf95bf15a605ed80bd98be3fadcd76450 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: Sat, 13 Sep 2025 01:00:40 -0600 Subject: [PATCH 15/15] =?UTF-8?q?=F0=9F=92=9A=20cgi=20mostly=20removed=20f?= =?UTF-8?q?rom=20ruby-head,=20so=20add=20cgi=20as=20dev=20dependency=20-?= =?UTF-8?q?=20in=20support=20of=20vcr:=20https://github.com/vcr/vcr/issues?= =?UTF-8?q?/1057?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .rubocop_gradual.lock | 2 +- Appraisals | 2 ++ gemfiles/head.gemfile | 1 + oauth.gemspec | 3 +++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock index d5920964..9ca71065 100644 --- a/.rubocop_gradual.lock +++ b/.rubocop_gradual.lock @@ -34,7 +34,7 @@ "lib/oauth/tokens/consumer_token.rb:3696415131": [ [9, 5, 155, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 349576019] ], - "oauth.gemspec:1987611209": [ + "oauth.gemspec:1419764957": [ [133, 3, 56, "Gemspec/DependencyVersion: Dependency version specification is required.", 644892567], [153, 3, 40, "Gemspec/DependencyVersion: Dependency version specification is required.", 2300588954], [155, 3, 44, "Gemspec/DependencyVersion: Dependency version specification is required.", 1905290578], diff --git a/Appraisals b/Appraisals index 8c19e494..b037049b 100644 --- a/Appraisals +++ b/Appraisals @@ -30,6 +30,8 @@ end # Used for head (nightly) releases of ruby, truffleruby, and jruby. # Split into discrete appraisals if one of them needs a dependency locked discretely. appraise "head" do + # Why is gem "cgi" here? See: https://github.com/vcr/vcr/issues/1057 + gem "cgi", ">= 0.5" gem "benchmark", "~> 0.4", ">= 0.4.1" eval_gemfile "modular/x_std_libs.gemfile" end diff --git a/gemfiles/head.gemfile b/gemfiles/head.gemfile index cf9235e6..8bc4feb0 100644 --- a/gemfiles/head.gemfile +++ b/gemfiles/head.gemfile @@ -3,6 +3,7 @@ source "https://rubygems.org" gem "oauth-tty", branch: "main", git: "https://github.com/ruby-oauth/oauth-tty" +gem "cgi", ">= 0.5" gem "benchmark", "~> 0.4", ">= 0.4.1" gemspec path: "../" diff --git a/oauth.gemspec b/oauth.gemspec index ce5c3b44..2d1c8989 100644 --- a/oauth.gemspec +++ b/oauth.gemspec @@ -202,6 +202,9 @@ Thanks, |7eter l-|. l3oling # | # ./spec/spec_helper.rb:8:in `' # So that's why we need backports. spec.add_development_dependency("backports", "~> 3.25", ">= 3.25.1") # ruby >= 0 + # In Ruby 3.5 (HEAD) the CGI library has been pared down, so we also need to depend on gem "cgi" for ruby@head + # This is done in the "head" appraisal. + # See: https://github.com/vcr/vcr/issues/1057 spec.add_development_dependency("vcr", ">= 4") # 6.0 claims to support ruby >= 2.3, but fails on ruby 2.4 spec.add_development_dependency("webmock", ">= 3") # Last version to support ruby >= 2.3 end