{"id":13463348,"url":"https://github.com/assaf/vanity","last_synced_at":"2025-09-29T00:32:43.616Z","repository":{"id":713534,"uuid":"360400","full_name":"assaf/vanity","owner":"assaf","description":"Experiment Driven Development for Ruby","archived":true,"fork":false,"pushed_at":"2023-03-16T10:12:48.000Z","size":3334,"stargazers_count":1538,"open_issues_count":36,"forks_count":268,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-09-04T21:43:06.931Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://vanity.labnotes.org","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/assaf.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","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}},"created_at":"2009-11-04T05:51:35.000Z","updated_at":"2025-09-01T19:17:12.000Z","dependencies_parsed_at":"2023-07-05T18:48:13.072Z","dependency_job_id":null,"html_url":"https://github.com/assaf/vanity","commit_stats":{"total_commits":761,"total_committers":100,"mean_commits":7.61,"dds":0.6872536136662286,"last_synced_commit":"d65179482259217a90cefa742098653076e884d2"},"previous_names":[],"tags_count":68,"template":false,"template_full_name":null,"purl":"pkg:github/assaf/vanity","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assaf%2Fvanity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assaf%2Fvanity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assaf%2Fvanity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assaf%2Fvanity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/assaf","download_url":"https://codeload.github.com/assaf/vanity/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/assaf%2Fvanity/sbom","scorecard":{"id":212967,"data":{"date":"2025-08-11","repo":{"name":"github.com/assaf/vanity","commit":"d65179482259217a90cefa742098653076e884d2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.8,"checks":[{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"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":"Code-Review","score":3,"reason":"Found 8/24 approved changesets -- score normalized to 3","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":"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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/linting.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":"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":"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/linting.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/assaf/vanity/linting.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/linting.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/assaf/vanity/linting.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/linting.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/assaf/vanity/linting.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/assaf/vanity/test.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yml:50: update your workflow using https://app.stepsecurity.io/secureworkflow/assaf/vanity/test.yml/master?enable=pin","Info:   0 out of   3 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 'master'"],"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":"Vulnerabilities","score":0,"reason":"37 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-qcm3-vfq5-wfr2","Warn: Project is vulnerable to: GHSA-3hhc-qp5v-9p2j","Warn: Project is vulnerable to: GHSA-579w-22j4-4749","Warn: Project is vulnerable to: GHSA-mhwp-qhpc-h3jm","Warn: Project is vulnerable to: GHSA-xrr6-3pc4-m447","Warn: Project is vulnerable to: GHSA-j6gc-792m-qgm2","Warn: Project is vulnerable to: GHSA-j96r-xvjq-r9pg","Warn: Project is vulnerable to: GHSA-pj73-v5mw-pm9j","Warn: Project is vulnerable to: GHSA-vxvp-4xwc-jpp6","Warn: Project is vulnerable to: GHSA-m7fq-cf8q-35q7","Warn: Project is vulnerable to: GHSA-4xjh-m3qx-49wc","Warn: Project is vulnerable to: GHSA-mqm2-cgpr-p4m6","Warn: Project is vulnerable to: GHSA-22f2-v57c-j9cx","Warn: Project is vulnerable to: GHSA-3h57-hmj3-gj3p","Warn: Project is vulnerable to: GHSA-54rr-7fvw-6x8f","Warn: Project is vulnerable to: GHSA-65f5-mfpf-vfhj","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-93pm-5p5f-3ghx","Warn: Project is vulnerable to: GHSA-c6qg-cjj8-47qp","Warn: Project is vulnerable to: GHSA-gjh7-p2fx-99vx","Warn: Project is vulnerable to: GHSA-rqv2-275x-2jq5","Warn: Project is vulnerable to: GHSA-vpfw-47h7-xj4g","Warn: Project is vulnerable to: GHSA-xj5v-6v4g-jfw6","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-mqcp-p2hv-vw6x","Warn: Project is vulnerable to: GHSA-6f62-3596-g6w7","Warn: Project is vulnerable to: GHSA-r995-q44h-hr64","Warn: Project is vulnerable to: GHSA-jj47-x69x-mxrm","Warn: Project is vulnerable to: GHSA-wwh7-4jw9-33x6","Warn: Project is vulnerable to: GHSA-8mq4-9jjh-9xrc"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 19 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"}}]},"last_synced_at":"2025-08-17T01:05:42.977Z","repository_id":713534,"created_at":"2025-08-17T01:05:42.977Z","updated_at":"2025-08-17T01:05:42.977Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":277450939,"owners_count":25819971,"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","status":"online","status_checked_at":"2025-09-28T02:00:08.834Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2024-07-31T13:00:51.622Z","updated_at":"2025-09-29T00:32:43.281Z","avatar_url":"https://github.com/assaf.png","language":"Ruby","readme":"# Vanity\n\n[![Gem Version](https://badge.fury.io/rb/vanity.svg)](https://rubygems.org/gems/vanity)\n[![Test Status](https://github.com/assaf/vanity/workflows/Test/badge.svg)](https://github.com/assaf/vanity/actions)\n[![Ruby Toolbox](https://img.shields.io/badge/dynamic/json?color=blue\u0026label=Ruby%20Toolbox\u0026query=%24.projects%5B0%5D.score\u0026url=https%3A%2F%2Fwww.ruby-toolbox.com%2Fapi%2Fprojects%2Fcompare%2Fvanity\u0026logo=data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhcyIgZGF0YS1pY29uPSJmbGFzayIgY2xhc3M9InN2Zy1pbmxpbmUtLWZhIGZhLWZsYXNrIGZhLXctMTQiIHJvbGU9ImltZyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgNDQ4IDUxMiI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik00MzcuMiA0MDMuNUwzMjAgMjE1VjY0aDhjMTMuMyAwIDI0LTEwLjcgMjQtMjRWMjRjMC0xMy4zLTEwLjctMjQtMjQtMjRIMTIwYy0xMy4zIDAtMjQgMTAuNy0yNCAyNHYxNmMwIDEzLjMgMTAuNyAyNCAyNCAyNGg4djE1MUwxMC44IDQwMy41Qy0xOC41IDQ1MC42IDE1LjMgNTEyIDcwLjkgNTEyaDMwNi4yYzU1LjcgMCA4OS40LTYxLjUgNjAuMS0xMDguNXpNMTM3LjkgMzIwbDQ4LjItNzcuNmMzLjctNS4yIDUuOC0xMS42IDUuOC0xOC40VjY0aDY0djE2MGMwIDYuOSAyLjIgMTMuMiA1LjggMTguNGw0OC4yIDc3LjZoLTE3MnoiPjwvcGF0aD48L3N2Zz4=)](https://www.ruby-toolbox.com/projects/vanity)\n\nVanity is an A/B testing framework for Rails that is datastore agnostic.\n\n*   All about Vanity: http://vanity.labnotes.org\n*   On Github: http://github.com/assaf/vanity\n\n[![Dashboard](doc/images/sidebar_test.png)](http://github.com/assaf/vanity)\n\n\u003c!-- toc --\u003e\n\n- [Installation](#installation)\n- [Setup](#setup)\n  * [Datastore](#datastore)\n    + [Redis Setup](#redis-setup)\n    + [MongoDB Setup](#mongodb-setup)\n    + [SQL Database Setup](#sql-database-setup)\n    + [Forking servers and reconnecting](#forking-servers-and-reconnecting)\n  * [Initialization](#initialization)\n  * [User identification](#user-identification)\n    + [Rails](#rails)\n    + [Other](#other)\n  * [Define a A/B test](#define-a-ab-test)\n  * [Present the different options to your users](#present-the-different-options-to-your-users)\n  * [Measure conversion](#measure-conversion)\n  * [Check the report](#check-the-report)\n    + [Rails report dashboard](#rails-report-dashboard)\n- [Registering participants with Javascript](#registering-participants-with-javascript)\n- [Compatibility](#compatibility)\n- [Testing](#testing)\n- [Updating documentation](#updating-documentation)\n- [Contributing](#contributing)\n- [Credits/License](#creditslicense)\n\n\u003c!-- tocstop --\u003e\n\n## Installation\n\nAdd to your Gemfile:\n\n```ruby\ngem \"vanity\"\n```\n\n(For support for older versions of Rails and Ruby 1.8, please see the [1.9.x\nbranch](https://github.com/assaf/vanity/tree/1-9-stable).)\n\n## Setup\n\n### Datastore\n\nChoose a datastore that best fits your needs and preferences for storing\nexperiment results. Choose one of: Redis, MongoDB or an SQL database. While\nRedis is usually faster, it may add additional complexity to your stack.\nDatastores should be configured using a `config/vanity.yml`.\n\n#### Redis Setup\n\nAdd to your Gemfile:\n\n```ruby\ngem \"redis\", \"\u003e= 3.2\"\ngem \"redis-namespace\", \"\u003e= 1.1.0\"\n```\n\nBy default Vanity is configured to use Redis on localhost port 6379 with\ndatabase 0.\n\nA sample `config/vanity.yml` might look like:\n\n```yaml\ntest:\n  collecting: false\nproduction:\n  adapter: redis\n  url: redis://\u003c%= ENV[\"REDIS_USER\"] %\u003e:\u003c%= ENV[\"REDIS_PASSWORD\"] %\u003e@\u003c%= ENV[\"REDIS_HOST\"] %\u003e:\u003c%= ENV[\"REDIS_PORT\"] %\u003e/0\n```\n\nIf you want to use your test environment with RSpec you will need to add an\nadapter to test:\n\n```yaml\ntest:\n  adapter: redis\n  collecting: false\n```\n\nTo re-use an existing redis connection, you can call `Vanity.connect!` explicitly, for example:\n\n```ruby\nVanity.connect!(\n  adapter: :redis,\n  redis: $redis\n)\n```\n\n#### MongoDB Setup\n\nAdd to your Gemfile:\n\n```ruby\ngem \"mongo\", \"~\u003e 2.0\" # For Mongo 1.x support see Vanity versions 2.1 and below.\n```\n\nA sample `config/vanity.yml` might look like:\n\n```yaml\ndevelopment:\n  adapter: mongodb\n  database: analytics\ntest:\n  collecting: false\nproduction:\n  adapter: mongodb\n  database: analytics\n```\n\n#### SQL Database Setup\n\nVanity supports multiple SQL stores (like MySQL, MariaDB, Postgres, Sqlite,\netc.) using ActiveRecord, which is built into Rails. If you're using\nDataMapper, Sequel or another persistence framework, add to your Gemfile:\n\n```ruby\n    gem \"active_record\"\n```\n\nA sample `config/vanity.yml` might look like:\n\n```yaml\ndevelopment:\n  adapter: active_record\n  active_record_adapter: sqlite3\n  database: db/development.sqlite3\ntest:\n  adapter: active_record\n  active_record_adapter: default\n  collecting: false\nproduction:\n  adapter: active_record\n  active_record_adapter: postgresql\n  \u003c% uri = URI.parse(ENV['DATABASE_URL']) %\u003e\n  host:     \u003c%= uri.host %\u003e\n  username: \u003c%= uri.user%\u003e\n  password: \u003c%= uri.password %\u003e\n  port:     \u003c%= uri.port %\u003e\n  database: \u003c%= uri.path.sub('/', '') %\u003e\n```\n\nIf you're going to store data in the database, run the generator and\nmigrations to create the database schema:\n\n```sh\n$ rails generate vanity\n$ rake db:migrate\n```\n\n#### Forking servers and reconnecting\n\nIf you're using a forking server (like Passenger or Unicorn), you should\nreconnect after a new worker is created:\n\n```ruby\n# unicorn.rb\nafter_fork do |server, worker|\n  defined?(Vanity) \u0026\u0026 Vanity.reconnect!\nend\n\n# an initializer\nif defined?(PhusionPassenger)\n  PhusionPassenger.on_event(:starting_worker_process) do |forked|\n    # We're in smart spawning mode.\n    if forked\n      defined?(Vanity) \u0026\u0026 Vanity.reconnect!\n    end\n  end\nend\n```\n\nIf you're using explicit options with `Vanity.connect!`, you should call `disconnect!` first, for example:\n\n```ruby\nVanity.disconnect!\nVanity.connect!(\n  adapter: 'redis',\n  redis: $redis\n)\n```\n\n### Initialization\n\nIf you're using Rails, this is done automagically. Otherwise, some manual setup is required, for example on an app's booting:\n\n```\n$redis = Redis.new # or from elsewhere\nVanity.configure do |config|\n  # ... any config\nend\nVanity.connect!(\n  adapter: :redis,\n  redis: $redis\n)\nVanity.load!\n```\n\n### User identification\n\n#### Rails\n\nTurn Vanity on, and pass a reference to a method that identifies a user. For\nexample:\n\n```ruby\nclass ApplicationController \u003c ActionController::Base\n  use_vanity :current_user\nend\n```\n\nFor more information, please see the [identity\ndocumentation](http://vanity.labnotes.org/identity.html).\n\n#### Other\n\nVanity pulls the identity from a \"context\" object that responds to `vanity_identity`, so we need to define a `Vanity.context` (this is how the [ActionMailer integration](https://github.com/assaf/vanity/blob/master/lib/vanity/frameworks/rails.rb#L107-L133) works):\n\n```\nclass AVanityContext\n  def vanity_identity\n    \"123\"\n  end\nend\n\nVanity.context = AVanityContext.new() # Any object that responds to `#vanity_identity`\n```\n\nIf you're using plain ruby objects, you could also alias something in your identity model to respond similarly and then set that as the vanity context:\n```\nclass User\n  alias_method :vanity_identity, :id\nend\n```\n\n### Define a A/B test\n\nThis experiment goes in the file `experiments/price_options.rb`:\n\n```ruby\nab_test \"Price options\" do\n  description \"Mirror, mirror on the wall, who's the better price of all?\"\n  alternatives 19, 25, 29\n  metrics :signups\nend\n```\n\nIf the experiment uses a metric as above (\"signups\"), there needs to be a\ncorresponding ruby file for that metric, `experiments/metrics/signups.rb`.\n\n```ruby\nmetric \"Signup (Activation)\" do\n  description \"Measures how many people signed up for our awesome service.\"\nend\n```\n\n### Present the different options to your users\n\nIn Rails' templates, this is straightforward:\n\n```erb\n\u003ch2\u003eGet started for only $\u003c%= ab_test :price_options %\u003e a month!\u003c/h2\u003e\n```\n\nOutside of templates:\n\n```\nVanity.ab_test(:invite_subject)\n```\n\n### Measure conversion\n\nConversions are created via the `Vanity.track!` method. A user should already be added to an experiment, via `ab_test` before this is called - otherwise, the conversion will be tracked, but the user will not be added to the experiment.\n\nFor example, in Rails:\n\n```ruby\nclass SignupController \u003c ApplicationController\n  def signup\n    @account = Account.new(params[:account])\n    if @account.save\n      Vanity.track!(:signups)\n      redirect_to @acccount\n    else\n      render action: :offer\n    end\n  end\nend\n```\n\nOutside of an Rails controller, for example in a Rack handler:\n\n```\nidentity_object = Identity.new(env['rack.session'])\nVanity.track!(:click, {\n  # can be any object that responds to `to_s` with a string\n  # that contains the unique identifier or the string identifier itself\n  :identity=\u003eidentity_object,\n  :values=\u003e[1] # optional\n})\n```\n\n### Check the report\n\n```sh\nvanity report --output vanity.html\n```\n\n#### Rails report dashboard\n\nTo view metrics and experiment results with the dashboard in Rails 3 \u0026 Rails\n4:\n\n```sh\nrails generate controller Vanity --helper=false\n```\n\nIn `config/routes.rb`, add:\n\n```ruby\nget '/vanity' =\u003e'vanity#index'\nget '/vanity/participant/:id' =\u003e 'vanity#participant'\npost '/vanity/complete'\npost '/vanity/chooses'\npost '/vanity/reset'\npost '/vanity/enable'\npost '/vanity/disable'\npost '/vanity/add_participant'\nget '/vanity/image'\n```\n\nThe controller should look like:\n\n```ruby\nclass VanityController \u003c ApplicationController\n  include Vanity::Rails::Dashboard\n  layout false  # exclude this if you want to use your application layout\nend\n```\n\n## Registering participants with Javascript\n\nIf robots or spiders make up a significant portion of your sites traffic they\ncan affect your conversion rate. Vanity can optionally add participants to the\nexperiments using asynchronous javascript callbacks, which will keep many\nrobots out. For those robots that do execute Javascript and are well-behaved\n(like Googlebot), Vanity filters out requests based on their user-agent\nstring.\n\nIn Rails, add the following to `application.rb`:\n\n```ruby\nVanity.configure do |config|\n  config.use_js = true\n\n  # Optionally configure the add_participant route that is added with Vanity::Rails::Dashboard,\n  # make sure that this action does not require authentication\n  # config.add_participant_route = '/vanity/add_participant'\nend\n```\n\nThen add `\u003c%= vanity_js %\u003e` to any page that calls an A/B test **after calling\n`ab_test`**. `vanity_js` needs to be included after your call to ab_test so\nthat it knows which version of the experiment the participant is a member of.\nThe helper will render nothing if the there are no ab_tests running on the\ncurrent page, so adding `vanity_js` to the bottom of your layouts is a good\noption. Keep in mind that if you set `use_js` and don't include `vanity_js` in\nyour view no participants will be recorded.\n\n## Compatibility\n\nHere's what's tested and known to work:\n\n    Rails: 5.2+\n    Ruby: 2.5+\n    JRuby: 9.1+\n    Persistence: Redis (redis-rb \u003e= 3.2.1), Mongo, ActiveRecord\n\n## Testing\n\nFor view tests/specs or integration testing, it's handy to set the outcome of\nan experiment. This may be done using the `chooses` method. For example:\n\n```ruby\nVanity.playground.experiment(:price_options).chooses(19)\n```\n\nSee [the docs on testing](http://vanity.labnotes.org/ab_testing.html#test) for more.\n\n## Updating documentation\n\nDocumenation is written in the textile format in the [docs](docs/) directory,\nand is hosted on Github Pages. To update the docs commit changes to the master\nbranch in this repository, then:\n\n```sh\nbundle exec rake docs # output HTML files into html/\ngit checkout gh-pages\nmv html/* . # Move generated html to the top of the repo\ngit commit # Add, commit and push any changes!\n```\n\nGo ahead and target a pull request against the `gh-pages` branch.\n\n## Contributing\n\n*   Fork the project\n*   Please use a feature branch to make your changes, it's easier to test them\n    that way\n*   To set up the test suite run `bundle`, then run `appraisal install` to\n    prepare the test suite to run against multiple versions of Rails\n*   Fix, patch, enhance, document, improve, sprinkle pixie dust\n*   Tests. Please. Run `appraisal rake test`, of if you can, `rake test:all`.\n    (This project uses Github Actions where the test suite is run against multiple\n    versions of ruby, rails and backends.)\n*   Send a pull request on GitHub\n\n\n## Credits/License\n\nOriginal code, copyright of Assaf Arkin, released under the MIT license.\n\nDocumentation available under the Creative Commons Attribution license.\n\nFor full list of credits and licenses:\nhttp://vanity.labnotes.org/credits.html.\n","funding_links":[],"categories":["Testing","Ruby","others","Feature Flippers and A/B Testing","Abstraction"],"sub_categories":["A/B Testing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fassaf%2Fvanity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fassaf%2Fvanity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fassaf%2Fvanity/lists"}