{"id":13428153,"url":"https://github.com/heartcombo/responders","last_synced_at":"2026-03-16T22:35:46.972Z","repository":{"id":741854,"uuid":"392785","full_name":"heartcombo/responders","owner":"heartcombo","description":"A set of Rails responders to dry up your application","archived":false,"fork":false,"pushed_at":"2026-03-13T12:49:18.000Z","size":465,"stargazers_count":2050,"open_issues_count":7,"forks_count":155,"subscribers_count":29,"default_branch":"main","last_synced_at":"2026-03-16T13:43:57.043Z","etag":null,"topics":["controllers","flash-messages","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/heartcombo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2009-12-02T13:19:13.000Z","updated_at":"2026-03-13T12:49:22.000Z","dependencies_parsed_at":"2024-06-16T05:30:52.664Z","dependency_job_id":"6e1ca7cf-8051-4a54-9405-f2f915c4e0c2","html_url":"https://github.com/heartcombo/responders","commit_stats":{"total_commits":350,"total_committers":75,"mean_commits":4.666666666666667,"dds":0.7857142857142857,"last_synced_commit":"956cd874a997083d350951fde4c6cc4944f64e80"},"previous_names":["plataformatec/responders"],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/heartcombo/responders","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heartcombo%2Fresponders","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heartcombo%2Fresponders/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heartcombo%2Fresponders/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heartcombo%2Fresponders/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/heartcombo","download_url":"https://codeload.github.com/heartcombo/responders/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heartcombo%2Fresponders/sbom","scorecard":{"id":459129,"data":{"date":"2025-08-11","repo":{"name":"github.com/heartcombo/responders","commit":"9bdc60dfbfa8001641c1c4df7bc73c3fc2a4cf41"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.6,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":1,"reason":"Found 3/24 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/rubocop.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/rubocop.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/heartcombo/responders/rubocop.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rubocop.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/heartcombo/responders/rubocop.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:60: update your workflow using https://app.stepsecurity.io/secureworkflow/heartcombo/responders/test.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yml:61: update your workflow using https://app.stepsecurity.io/secureworkflow/heartcombo/responders/test.yml/main?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: MIT-LICENSE:0","Info: FSF or OSI recognized license: MIT License: MIT-LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 10 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"27 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-fwhr-88qx-h9g7","Warn: Project is vulnerable to: GHSA-vfg9-r3fq-jvx4","Warn: Project is vulnerable to: GHSA-vfm5-rmrh-j26v","Warn: Project is vulnerable to: GHSA-x76w-6vjr-8xgj","Warn: Project is vulnerable to: GHSA-353f-x4gh-cqq8","Warn: Project is vulnerable to: GHSA-5w6v-399v-w3cc","Warn: Project is vulnerable to: GHSA-mrxw-mxhj-p664","Warn: Project is vulnerable to: GHSA-r95h-9x8f-r3f7","Warn: Project is vulnerable to: GHSA-vvfq-8hwr-qm4m","Warn: Project is vulnerable to: GHSA-7g2v-jj9q-g3rg","Warn: Project is vulnerable to: GHSA-7wqh-767x-r66v","Warn: Project is vulnerable to: GHSA-8cgq-6mh2-7j6v","Warn: Project is vulnerable to: GHSA-gjh7-p2fx-99vx","Warn: Project is vulnerable to: GHSA-9j94-67jr-4cqj","Warn: Project is vulnerable to: GHSA-2x5m-9ch4-qgrr","Warn: Project is vulnerable to: GHSA-638j-pmjw-jq48","Warn: Project is vulnerable to: GHSA-cfjx-w229-hgx5","Warn: Project is vulnerable to: GHSA-rxv5-gxqc-xx8g","Warn: Project is vulnerable to: GHSA-w8gc-x259-rc7x","Warn: Project is vulnerable to: GHSA-2rxp-v6pw-ch6m","Warn: Project is vulnerable to: GHSA-4xqq-m2hx-25v8","Warn: Project is vulnerable to: GHSA-5866-49gr-22v4","Warn: Project is vulnerable to: GHSA-r55c-59qm-vjw6","Warn: Project is vulnerable to: GHSA-vg3r-rm7w-2xgh","Warn: Project is vulnerable to: GHSA-vmwr-mc7x-5vc3","Warn: Project is vulnerable to: GHSA-6f62-3596-g6w7","Warn: Project is vulnerable to: GHSA-r995-q44h-hr64"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T10:37:32.804Z","repository_id":741854,"created_at":"2025-08-19T10:37:32.804Z","updated_at":"2025-08-19T10:37:32.804Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30591406,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-16T20:13:02.716Z","status":"ssl_error","status_checked_at":"2026-03-16T20:11:47.037Z","response_time":96,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["controllers","flash-messages","rails","ruby"],"created_at":"2024-07-31T01:00:47.602Z","updated_at":"2026-03-16T22:35:46.967Z","avatar_url":"https://github.com/heartcombo.png","language":"Ruby","readme":"# Responders\n\n[![Gem Version](https://badge.fury.io/rb/responders.svg)](https://badge.fury.io/rb/responders)\n\nA set of responders modules to dry up your Rails app.\n\n## Installation\n\nAdd the responders gem to your Gemfile:\n\n    gem \"responders\"\n\nUpdate your bundle and run the install generator:\n\n    $ bundle install\n    $ rails g responders:install\n\nIf you are including this gem to support backwards compatibility for responders in previous releases of Rails, you only need to include the gem and bundle.\n\n    $ bundle install\n\n## Responders Types\n\n### FlashResponder\n\nSets the flash based on the controller action and resource status.\nFor instance, if you do: `respond_with(@post)` on a POST request and the resource `@post`\ndoes not contain errors, it will automatically set the flash message to\n`\"Post was successfully created\"` as long as you configure your I18n file:\n\n```yaml\n  flash:\n    actions:\n      create:\n        notice: \"%{resource_name} was successfully created.\"\n      update:\n        notice: \"%{resource_name} was successfully updated.\"\n      destroy:\n        notice: \"%{resource_name} was successfully destroyed.\"\n        alert: \"%{resource_name} could not be destroyed.\"\n```\n\nIn case the resource contains errors, you should use the failure key on I18n. This is\nuseful to dry up flash messages from your controllers. Note: by default alerts for `update`\nand `destroy` actions are commented in generated I18n file. If you need a specific message\nfor a controller, let's say, for `PostsController`, you can also do:\n\n```yaml\n  flash:\n    posts:\n      create:\n        notice: \"Your post was created and will be published soon\"\n```\n\nThis responder is activated in all non get requests. By default it will use the keys\n`:notice` and `:alert`, but they can be changed in your application:\n\n```ruby\nconfig.responders.flash_keys = [ :success, :failure ]\n```\n\nYou can also have embedded HTML. Just create a `_html` scope.\n\n```yaml\n  flash:\n    actions:\n      create:\n        alert_html: \"\u003cstrong\u003eOH NOES!\u003c/strong\u003e You did it wrong!\"\n    posts:\n      create:\n        notice_html: \"\u003cstrong\u003eYay!\u003c/strong\u003e You did it!\"\n```\n\nSee also the `namespace_lookup` option to search the full hierarchy of possible keys.\n\n### HttpCacheResponder\n\nAutomatically adds Last-Modified headers to API requests. This\nallows clients to easily query the server if a resource changed and if the client tries\nto retrieve a resource that has not been modified, it returns not_modified status.\n\n### CollectionResponder\n\nMakes your create and update action redirect to the collection on success.\n\n### LocationResponder\n\nThis responder allows you to use callable objects as the redirect location.\nUseful when you want to use the `respond_with` method with\na custom route that requires persisted objects, but the validation may fail.\n\nNote: this responder is included by default, and doesn't need to be included\non the top of your controller (including it will issue a deprecation warning).\n\n```ruby\nclass ThingsController \u003c ApplicationController\n  respond_to :html\n\n  def create\n    @thing = Thing.create(params[:thing])\n    respond_with @thing, location: -\u003e { thing_path(@thing) }\n  end\nend\n```\n\n**Dealing with namespaced routes**\n\nIn order for the LocationResponder to find the correct route helper for namespaced routes you need to pass the namespaces to `respond_with`:\n\n```ruby\nclass Api::V1::ThingsController \u003c ApplicationController\n  respond_to :json\n\n  # POST /api/v1/things\n  def create\n    @thing = Thing.create(thing_params)\n    respond_with :api, :v1, @thing\n  end\nend\n```\n\n## Configuring your own responder\n\nResponders only provides a set of modules and to use them you have to create your own\nresponder. After you run the install command, the following responder will be\ngenerated in your application:\n\n```ruby\n# lib/application_responder.rb\nclass ApplicationResponder \u003c ActionController::Responder\n  include Responders::FlashResponder\n  include Responders::HttpCacheResponder\nend\n```\n\nYour application also needs to be configured to use it:\n\n```ruby\n# app/controllers/application_controller.rb\nrequire \"application_responder\"\n\nclass ApplicationController \u003c ActionController::Base\n  self.responder = ApplicationResponder\n  respond_to :html\nend\n```\n\n## Controller method\n\nThis gem also includes the controller method `responders`, which allows you to cherry-pick which\nresponders you want included in your controller.\n\n```ruby\nclass InvitationsController \u003c ApplicationController\n  responders :flash, :http_cache\nend\n```\n\n## Interpolation Options\n\nYou can pass in extra interpolation options for the translation by adding an `flash_interpolation_options` method to your controller:\n\n```ruby\nclass InvitationsController \u003c ApplicationController\n  responders :flash, :http_cache\n\n  def create\n    @invitation = Invitation.create(params[:invitation])\n    respond_with @invitation\n  end\n\n  private\n\n  def flash_interpolation_options\n    { resource_name: @invitation.email }\n  end\nend\n```\n\nNow you would see the message `\"name@example.com was successfully created\"` instead of the default `\"Invitation was successfully created.\"`\n\n## Generator\n\nThis gem also includes a responders controller generator, so your scaffold can be customized\nto use `respond_with` instead of default `respond_to` blocks. From 2.1, you need to explicitly opt-in to use this generator by adding the following to your `config/application.rb`:\n\n```ruby\nconfig.app_generators.scaffold_controller :responders_controller\n```\n\n## Failure handling\n\nResponders don't use `valid?` to check for errors in models to figure out if\nthe request was successful or not, and relies on your controllers to call\n`save` or `create` to trigger the validations.\n\n```ruby\ndef create\n  @widget = Widget.new(widget_params)\n  # @widget will be a valid record for responders, as we haven't called `save`\n  # on it, and will always redirect to the `widgets_path`.\n  respond_with @widget, location: -\u003e { widgets_path }\nend\n```\n\nResponders will check if the `errors` object in your model is empty or not. Take\nthis in consideration when implementing different actions or writing test\nassertions on this behavior for your controllers.\n\n```ruby\ndef create\n  @widget = Widget.new(widget_params)\n  @widget.errors.add(:base, :invalid)\n  # `respond_with` will render the `new` template again,\n  # and set the status based on the configured `error_status`.\n  respond_with @widget\nend\n```\n\n## Verifying request formats\n\n`respond_with` will raise an `ActionController::UnknownFormat` if the request\nMIME type was not configured through the class level `respond_to`, but the\naction will still be executed and any side effects (like creating a new record)\nwill still occur. To raise the `UnknownFormat` exception before your action\nis invoked you can set the `verify_requested_format!` method as a `before_action`\non your controller.\n\n```ruby\nclass WidgetsController \u003c ApplicationController\n  respond_to :json\n  before_action :verify_requested_format!\n\n  # POST /widgets.html won't reach the `create` action.\n  def create\n    widget = Widget.create(widget_params)\n    respond_with widget\n  end\nend\n```\n\n## Configuring error and redirect statuses\n\nBy default, `respond_with` will respond to errors on `HTML` \u0026 `JS` requests using the HTTP status code `200 OK`,\nand perform redirects using the HTTP status code `302 Found`, both for backwards compatibility reasons.\n\nYou can configure this behavior by setting `config.responders.error_status` and `config.responders.redirect_status` to the desired status codes.\n\n```ruby\nconfig.responders.error_status = :unprocessable_entity\nconfig.responders.redirect_status = :see_other\n```\n\nThese can also be set in your custom `ApplicationResponder` if you have generated one: (see install instructions)\n\n```ruby\nclass ApplicationResponder \u003c ActionController::Responder\n  self.error_status = :unprocessable_entity\n  self.redirect_status = :see_other\nend\n```\n\n_Note_: the application responder generated for new apps already configures a different set of defaults: `422 Unprocessable Entity` for errors, and `303 See Other` for redirects. _Responders may change the defaults to match these in a future major release._\n\n### Hotwire/Turbo and fetch APIs\n\nHotwire/Turbo expects successful redirects after form submissions to respond with HTTP status `303 See Other`, and error responses to be 4xx or 5xx statuses, for example `422 Unprocessable Entity` for displaying form validation errors and `500 Internal Server Error` for other server errors. [Turbo documentation: Redirecting After a Form Submission](https://turbo.hotwired.dev/handbook/drive#redirecting-after-a-form-submission).\n\nThe example configuration showed above matches the statuses that better integrate with Hotwire/Turbo.\n\n## Examples\n\nWant more examples ? Check out these blog posts:\n\n* [Embracing REST with mind, body and soul](http://blog.plataformatec.com.br/2009/08/embracing-rest-with-mind-body-and-soul/)\n* [Three reasons to love ActionController::Responder](http://weblog.rubyonrails.org/2009/8/31/three-reasons-love-responder/)\n* [My five favorite things about Rails 3](https://web.archive.org/web/20201109041436/https://blog.engineyard.com/my-five-favorite-things-about-rails-3)\n\n## Supported Ruby / Rails versions\n\nWe intend to maintain support for all Ruby / Rails versions that haven't reached end-of-life.\n\nFor more information about specific versions please check [Ruby](https://www.ruby-lang.org/en/downloads/branches/)\nand [Rails](https://guides.rubyonrails.org/maintenance_policy.html) maintenance policies, and our test matrix.\n\n## Bugs and Feedback\n\nIf you discover any bugs or want to drop a line, feel free to create an issue on GitHub.\n\n## License\n\nMIT License.\nCopyright 2020-CURRENT Rafael França, Carlos Antonio da Silva.\nCopyright 2009-2019 Plataformatec.\n","funding_links":[],"categories":["Production","Ruby","Decorators"],"sub_categories":["Security"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheartcombo%2Fresponders","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fheartcombo%2Fresponders","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheartcombo%2Fresponders/lists"}