{"id":13482907,"url":"https://github.com/bploetz/versionist","last_synced_at":"2025-03-27T13:33:05.692Z","repository":{"id":2013961,"uuid":"2949173","full_name":"bploetz/versionist","owner":"bploetz","description":"A plugin for versioning Rails based RESTful APIs.","archived":true,"fork":false,"pushed_at":"2024-02-22T15:59:28.000Z","size":226,"stargazers_count":968,"open_issues_count":10,"forks_count":50,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-03-23T08:17:02.578Z","etag":null,"topics":["api","rails","rest","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/bploetz.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":"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}},"created_at":"2011-12-09T18:37:07.000Z","updated_at":"2025-02-06T23:05:46.000Z","dependencies_parsed_at":"2024-06-18T13:55:29.105Z","dependency_job_id":"3d8c6c60-6d3c-4509-aeb8-a8d77bb3b7ae","html_url":"https://github.com/bploetz/versionist","commit_stats":{"total_commits":145,"total_committers":12,"mean_commits":"12.083333333333334","dds":0.3448275862068966,"last_synced_commit":"80b09ce472c0661d3994eeede17b96feb636a17e"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bploetz%2Fversionist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bploetz%2Fversionist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bploetz%2Fversionist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bploetz%2Fversionist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bploetz","download_url":"https://codeload.github.com/bploetz/versionist/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245854735,"owners_count":20683405,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["api","rails","rest","ruby"],"created_at":"2024-07-31T17:01:06.632Z","updated_at":"2025-03-27T13:33:03.672Z","avatar_url":"https://github.com/bploetz.png","language":"Ruby","funding_links":[],"categories":["Versions","API Builder","API Builder and Discovery"],"sub_categories":[],"readme":"# versionist\n\n[![Build Status](https://travis-ci.org/bploetz/versionist.svg?branch=master)](https://travis-ci.org/bploetz/versionist)\n\nA plugin for versioning Rails based RESTful APIs. Versionist supports three versioning strategies out of the box:\n\n- Specifying version via an HTTP header\n- Specifying version by prepending paths with a version slug\n- Specifying version via a request parameter\n\nA version of your API consists of:\n\n- Namespaced controllers/routes\n- Namespaced presenters\n- Namespaced tests\n- Documentation\n\nVersionist includes Rails generators for generating new versions of your API as well as new components within an existing version.\n\n\n## Installation\n\nAdd the following dependency to your Rails application's `Gemfile` file and run `bundle install`:\n\n    gem 'versionist'\n\n\n## Configuration\n\nVersionist provides the method `api_version` that you use in your Rails application's `config/routes.rb` file to constrain a collection of routes to a specific version of your API.\nThe versioning strategies used by the collection of routes constrained by `api_version` is set by specifying `:header`, `:path`, and/or `:parameter` (and their supporting values)\nin the configuration Hash passed to `api_version`. You configure the module namespace for your API version by specifying `:module` in the configuration Hash passed to `api_version`.\n\n### Upgrading from Versionist 0.x to 1.x+\n\nA backwards incompatible change was made to the format of the configuration hash passed to `api_version` starting in Versionist 1.0.\nPrior to 1.0, `api_version` expected hashes with the following structure:\n\n```ruby\napi_version(:module =\u003e \"V1\", :header =\u003e \"Accept\", :value =\u003e \"application/vnd.mycompany.com; version=1\") do\n  ...\nend\n```\n\nIn order to support multiple concurrent versioning strategies per api version, `api_version` expects that the `:header`, `:parameter`, and `:path`\nkeys point to hashes and contain the required keys.\n\n```ruby\napi_version(:module =\u003e \"V1\", :header =\u003e {:name =\u003e \"Accept\", :value =\u003e \"application/vnd.mycompany.com; version=1\"}) do\n  ...\nend\n\napi_version(:module =\u003e \"V1\", :parameter =\u003e {:name =\u003e \"version\", :value =\u003e \"1\"}) do\n  ...\nend\n\napi_version(:module =\u003e \"V1\", :path =\u003e {:value =\u003e \"v1\"}) do\n  ...\nend\n```\n\nAn error will be thrown at startup if your `config/routes.rb` file contains 0.x style `api_version` entries when running with Versionist 1.x+.\n\n## Versioning Strategies\n\n### HTTP Header\n\nThis strategy uses an HTTP header to request a specific version of your API.\n\n    Accept: application/vnd.mycompany.com; version=1,application/json\n    GET /foos\n\nYou configure the header to be inspected and the header value specifying the version in the configuration Hash passed to `api_version`.\n\nExamples:\n\n##### Content negotiation via the `Accept` header:\n\n```ruby\nMyApi::Application.routes.draw do\n  api_version(:module =\u003e \"V1\", :header =\u003e {:name =\u003e \"Accept\", :value =\u003e \"application/vnd.mycompany.com; version=1\"}) do\n    match '/foos.(:format)' =\u003e 'foos#index', :via =\u003e :get\n    match '/foos_no_format' =\u003e 'foos#index', :via =\u003e :get\n    resources :bars\n  end\nend\n```\n\n`Accept` Header Gotcha\n\nPlease note: when your routes do not include an explicit format in the URL (i.e. `match 'foos.(:format)' =\u003e foos#index`), Rails inspects the `Accept` header to determine the requested format. Since\nan `Accept` header can have multiple values, Rails uses the *first* one present to determine the format. If your custom version header happens to be the first value in the `Accept` header, Rails would \nincorrectly try to interpret it as the format. If you use the `Accept` header, Versionist will move your custom version header (if found) to the end of the `Accept` header so as to not interfere with\nRails' format resolution logic. This is the only case where Versionist will alter the incoming request.\n\n\n##### Custom header:\n\n```ruby\nMyApi::Application.routes.draw do\n  api_version(:module =\u003e \"V20120317\", :header =\u003e {:name =\u003e \"Api-Version\", :value =\u003e \"v20120317\"}) do\n    match '/foos.(:format)' =\u003e 'foos#index', :via =\u003e :get\n    match '/foos_no_format' =\u003e 'foos#index', :via =\u003e :get\n    resources :bars\n  end\nend\n```\n\n### Path\n\nThis strategy uses a URL path prefix to request a specific version of your API.\n\n    GET /v3/foos\n\nYou configure the path version prefix to be applied to the routes.\n\nExample:\n\n```ruby\nMyApi::Application.routes.draw do\n  api_version(:module =\u003e \"V3\", :path =\u003e {:value =\u003e \"v3\"}) do\n    match '/foos.(:format)' =\u003e 'foos#index', :via =\u003e :get\n    match '/foos_no_format' =\u003e 'foos#index', :via =\u003e :get\n    resources :bars\n  end\nend\n```\n\n### Request Parameter\n\nThis strategy uses a request parameter to request a specific version of your API.\n\n    GET /foos?version=v2\n\nYou configure the parameter name and value to be applied to the routes.\n\nExample:\n\n```ruby\nMyApi::Application.routes.draw do\n  api_version(:module =\u003e \"V2\", :parameter =\u003e {:name =\u003e \"version\", :value =\u003e \"v2\"}) do\n    match '/foos.(:format)' =\u003e 'foos#index', :via =\u003e :get\n    match '/foos_no_format' =\u003e 'foos#index', :via =\u003e :get\n    resources :bars\n  end\nend\n```\n\n### Default Version\n\nIf a request is made to your API without specifying a specific version, by default a RoutingError (i.e. 404) will occur. You can optionally configure Versionist to\nreturn a specific version by default when none is specified. To specify that a version should be used as the default, include `:default =\u003e true` in the config hash\npassed to the `api_version` method.\n\nExample.\n\n```ruby\nMyApi::Application.routes.draw do\n  api_version(:module =\u003e \"V20120317\", :header =\u003e {:name =\u003e \"Api-Version\", :value =\u003e \"v20120317\"}, :default =\u003e true) do\n    match '/foos.(:format)' =\u003e 'foos#index', :via =\u003e :get\n    match '/foos_no_format' =\u003e 'foos#index', :via =\u003e :get\n    resources :bars\n  end\nend\n```\n\nIf you attempt to specify more than one default version, an error will be thrown at startup.\n\nNote that when you configure a default API version, you will see the routes under your default version show up twice when running `rake routes`. This is due to the fact that Versionist adds another `scope` to your routes to handle the default case. Unfortunately `rake routes` does not show you enough contextual information to be able to differentiate the two, but this is the expected behavior.\n\n\n### Rails Route :defaults Hash\n\nThe `api_version` method also supports Rails' [`:defaults`](http://guides.rubyonrails.org/routing.html#defining-defaults) hash (note that this is different than\nthe `:default` key which controls the default API version described above). If a `:defaults` hash is passed to `api_version`, it will be applied to the collection\nof routes constrainted by `api_version`.\n\nExample.\n\n```ruby\nMyApi::Application.routes.draw do\n  api_version(:module =\u003e \"V20120317\", :header =\u003e {:name =\u003e \"Api-Version\", :value =\u003e \"v20120317\"}, :defaults =\u003e {:format =\u003e :json}, :default =\u003e true) do\n    match '/foos.(:format)' =\u003e 'foos#index', :via =\u003e :get\n    match '/foos_no_format' =\u003e 'foos#index', :via =\u003e :get\n    resources :bars\n  end\nend\n```\n\n## Multiple Versioning Strategies Per API Version\n\nAn API version may optionally support multiple concurrent versioning strategies.\n\nExample.\n```ruby\nMyApi::Application.routes.draw do\n  api_version(:module =\u003e \"V1\", :header =\u003e {:name =\u003e \"Accept\", :value =\u003e \"application/vnd.mycompany.com; version=1\"}, :path =\u003e {:value =\u003e \"v1\"}) do\n    match '/foos.(:format)' =\u003e 'foos#index', :via =\u003e :get\n    match '/foos_no_format' =\u003e 'foos#index', :via =\u003e :get\n    resources :bars\n  end\nend\n```\n\n## A Note About Testing\n\nRails functional tests (ActionController::TestCase) and RSpec Controller specs are for testing controller action methods in isolation.\nThey do not go through the full Rails stack, specifically the Rails dispatcher code path, which is where versionist hooks in to do its thing.\n\nIn order to test your versioned API routes, use integration tests (ActionDispatch::IntegrationTest) if you're using Test::Unit, or Request specs if you're using RSpec.\n\nTest::Unit Example:\n```ruby\n# test/integration/v1/test_controller_test.rb\nrequire 'test_helper'\n\nclass V1::TestControllerTest \u003c ActionDispatch::IntegrationTest\n  test \"should get v1\" do\n    get '/test', {}, {'Accept' =\u003e 'application/vnd.mycompany.com; version=1'}\n    assert_response 200\n    assert_equal \"v1\", @response.body\n  end\nend\n```\n\nRSpec Example:\n```ruby\n# spec/requests/v1/test_controller_spec.rb\nrequire 'spec_helper'\n\ndescribe V1::TestController do\n  it \"should get v1\" do\n    get '/test', {}, {'Accept' =\u003e 'application/vnd.mycompany.com; version=1'}\n    assert_response 200\n    assert_equal \"v1\", response.body\n  end\nend\n```\n\n## Generators\n\nVersionist comes with generators to facilitate managing the versions of your API. To see the available generators, simply run\n`rails generate`, and you will see the versionist generators under the `versionist` namespace.\n\nThe following generators are available:\n\n### `versionist:new_api_version`\n\ncreates the infrastructure for a new API version. This will create:\n\n- A new controller namespace, base controller and test\n- A new presenters namespace, base presenter and test\n- A new documentation directory and base files\n\nUsage\n\n    rails generate versionist:new_api_version \u003cversion\u003e \u003cmodule namespace\u003e [options]\n\nExamples:\n\n    # HTTP header versioning strategy\n    rails generate versionist:new_api_version v2 V2 --header=name:Accept value:\"application/vnd.mycompany.com; version=2\"\n\n    # request parameter versioning strategy\n    rails generate versionist:new_api_version v2 V2 --parameter=name:version value:2\n\n    # path versioning strategy\n    rails generate versionist:new_api_version v2 V2 --path=value:v2\n\n    # multiple versioning strategies\n    rails generate versionist:new_api_version v2 V2 --header=name:Accept value:\"application/vnd.mycompany.com; version=2\" --parameter=name:version value:2\n\n    # default version\n    rails generate versionist:new_api_version v2 V2 --path=value:v2 --default\n\n    # route :defaults hash\n    rails generate versionist:new_api_version v2 V2 --path=value:v2 --defaults=format:json\n\n\n    rails generate versionist:new_api_version v2 V2 --header=name:Accept value:\"application/vnd.mycompany.com; version=2\"\n      route  api_version(:module =\u003e \"V2\", :header =\u003e {:name =\u003e \"Accept\", :value =\u003e \"application/vnd.mycompany.com; version=2\"}) do\n      end\n      create  app/controllers/v2\n      create  app/controllers/v2/base_controller.rb\n      create  spec/controllers/v2\n      create  spec/controllers/v2/base_controller_spec.rb\n      create  spec/requests/v2\n      create  spec/requests/v2/base_controller_spec.rb\n      create  app/presenters/v2\n      create  app/presenters/v2/base_presenter.rb\n      create  spec/presenters/v2\n      create  spec/presenters/v2/base_presenter_spec.rb\n      create  app/helpers/v2\n      create  spec/helpers/v2\n      create  public/docs/v2\n      create  public/docs/v2/index.html\n      create  public/docs/v2/style.css\n\n\n### `versionist:new_controller`\n\ncreates a new controller class with the given name under the given version module.\n\nUsage\n\n    rails generate versionist:new_controller \u003cname\u003e \u003cmodule namespace\u003e\n\nExample:\n\n    rails generate versionist:new_controller foos V2\n      create  app/controllers/v2/foos_controller.rb\n      create  spec/controllers/v2/foos_controller_spec.rb\n      create  spec/requests/v2/foos_controller_spec.rb\n\n\n### `versionist:new_presenter`\n\ncreates a new presenter class with the given name under the given version module.\n\nUsage\n\n    rails generate versionist:new_presenter \u003cname\u003e \u003cmodule namespace\u003e\n\nExample:\n\n    rails generate versionist:new_presenter foos V2\n      create  app/presenters/v2/foos_presenter.rb\n      create  spec/presenters/v2/foos_presenter_spec.rb\n\n\n### `versionist:copy_api_version`\n\ncopies an existing API version to a new API version. This will do the following:\n\n- Copy all existing routes in config/routes.rb from the old API version to routes for the new API version in config/routes.rb (**see note below**)\n- Copy all existing controllers and tests from the old API version to the new API version\n- Copy all existing presenters and tests from the old API version to the new API version\n- Copy all existing helpers and tests from the old API version to the new API version\n- Copy all documentation from the old API version to the new API version\n\n**Note**: routes can only be copied with MRI Ruby 1.9 and above, as this feature relies on Ripper which is only available \nin stdlib in MRI Ruby 1.9 and above. Outside of routes copying, the other copy steps will work just fine in Ruby 1.8 and other\nnon-MRI Ruby implementations.\n\nUsage\n\n    rails generate versionist:copy_api_version \u003cold version\u003e \u003cold module namespace\u003e \u003cnew version\u003e \u003cnew module namespace\u003e\n\nExample:\n\n    rails generate versionist:copy_api_version v2 V2 v3 V3\n      route  api_version(:module =\u003e \"V3\", :header=\u003e\"Accept\", :value=\u003e\"application/vnd.mycompany.com; version=3\") do\n      end\n      Copying all files from app/controllers/v2 to app/controllers/v3\n      Copying all files from spec/controllers/v2 to spec/controllers/v3\n      Copying all files from app/presenters/v2 to app/presenters/v3\n      Copying all files from spec/presenters/v2 to spec/presenters/v3\n      Copying all files from app/helpers/v2 to app/helpers/v3\n      Copying all files from spec/helpers/v2 to spec/helpers/v3\n      Copying all files from public/docs/v2 to public/docs/v3\n\n## Additional Resources\n- [API Versioning using Versionist](http://www.multunus.com/blog/2014/04/api-versioning-using-versionist/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbploetz%2Fversionist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbploetz%2Fversionist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbploetz%2Fversionist/lists"}