{"id":22753236,"url":"https://github.com/nebulab/segmentation","last_synced_at":"2026-04-30T10:38:38.170Z","repository":{"id":141428817,"uuid":"367573204","full_name":"nebulab/segmentation","owner":"nebulab","description":"The missing SDK for integrating Segment in your Ruby on Rails application.","archived":false,"fork":false,"pushed_at":"2021-06-30T15:39:43.000Z","size":40,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-05T09:33:11.862Z","etag":null,"topics":["analytics","rails","ruby","segment","tracking"],"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/nebulab.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2021-05-15T07:58:09.000Z","updated_at":"2021-06-30T15:39:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"4295bf7c-cdb1-4664-b7ca-d6c3fa20b478","html_url":"https://github.com/nebulab/segmentation","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nebulab%2Fsegmentation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nebulab%2Fsegmentation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nebulab%2Fsegmentation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nebulab%2Fsegmentation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nebulab","download_url":"https://codeload.github.com/nebulab/segmentation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246285817,"owners_count":20752958,"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":["analytics","rails","ruby","segment","tracking"],"created_at":"2024-12-11T06:09:43.737Z","updated_at":"2026-04-30T10:38:38.135Z","avatar_url":"https://github.com/nebulab.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Segmentation\n\nSegmentation is a lightweight SDK that makes it easier to implement a full-stack \n[Segment](https://segment.com) integration in your Ruby on Rails application.\n\nSegmentation aims to solve a few common problems when integrating Segment in a Rails application:\n\n- Different Segment destinations require different keys in your payload. If you don't send these,\n  your destinations will not work correctly. Segmentation provides a thin layer for abstracting the\n  requirements of each destination.\n- Sometimes, you need to track an event even though your user is not present on the page. When this\n  happens, you may still need to include certain data about the user that's only available when the\n  user is present. Segmentation allows you to store this data to use it at a later stage in a\n  completely transparent way.\n- While backend tracking is more precise, frontend tracking is richer. Segmentation allows you to\n  get the best of both worlds by initiating the tracking from the backend and completing it on the\n  frontend.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'segmentation'\n```\n\nAnd then execute:\n\n```console\n$ bundle\n```\n\nNext, run the install generator:\n\n```console\n$ rails g segmentation:install\n```\n\nThis will generate a starting configuration at `config/initializer/segmentation.rb` and inject the\nSegmentation controller helpers into `ApplicationController`. Make sure to review and adjust the\ndefaults according to your needs!\n\nThe last step is to render the JS code needed to load Segment and Segmentation. Add this to head\nof your Rails layout:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003c%= segmentation_js_tag %\u003e\n    \u003c!-- ... --\u003e\n  \u003c/head\u003e\n\n  \u003c!-- ... --\u003e\n\u003c/html\u003e\n```\n\n## Architecture\n\nSegmentation revolves around four basic concepts:\n\n- **Client:** it exposes the public-facing Segmentation API and allows you identify users and track\n  events. The Segmentation client wraps any compatible Segment client (e.g., [analytics-ruby](https://github.com/segmentio/analytics-ruby)\n  or [SimpleSegment](https://github.com/whatthewhat/simple_segment)) and matches its API 1:1, so\n  that you can easily swap your old Segment client for the Segmentation client with no changes.\n- **Context:** it contains all the information about the current request, anonymous ID and current\n  user. It also abstracts some of the complexity of dealing with storages and destinations.\n- **Storage:** it allows you to store the user's analytics metadata so that it can be used when\n  tracking off-site events. An ActiveRecord adapter and a null adapter are provided out of the box,\n  but you can very easily implement your own (e.g., Redis).\n- **Destinations:** they abstract the complexity of dealing with different Segment destinations by\n  enriching the event payload with the specific properties needed by each destination.\n\n### Implementing your own destinations\n\nSegment supports hundreds of different destinations, and it would be impossible for Segmentation to\nkeep up with the complexity of integrating with each destination. Instead, Segmentation provides a\nsimple API for abstracting the logic behind each destination and leaves it up to you to implement\nthe destinations you need.\n\nFor example, the [Facebook Pixel destination](https://segment.com/docs/connections/destinations/catalog/facebook-pixel/)\nin Segment works best when you always pass the user's IP, user agent and the values of the user's\n`_fbc` and `_fbp` cookies. This kind of information is obviously not available when you're tracking\nan event outside of the request's lifecycle, so Segmentation allows you to store it when the user is\npresent and automatically retrieve it when tracking events for that user.\n\nHere's what the Facebook Pixel destination might look like:\n\n```ruby\nclass FacebookPixelDestination \u003c Segmentation::Destination\n  # Accepts a Rails request and returns the fields\n  # to store on the configured storage adapter.\n  def fields_from_request(request)\n    {}.tap do |fields|\n      if request\n        fields[:ip] = request.remote_ip\n        fields[:userAgent] = request.user_agent\n\n        fields[:fbc] = request.cookies['_fbc'] if request.cookies['_fbc'].present?\n        fields[:fbp] = request.cookies['_fbp'] if request.cookies['_fbp'].present?\n      end\n    end\n  end\n\n  # Accepts the fields stored on the current user and\n  # returns the properties to enrich the context with.\n  def context_from_fields(fields)\n    {\n      ip: fields[:ip],\n      userAgent: fields[:userAgent],\n    }.compact\n  end\n\n  # Accepts the fields stored on the current user and\n  # returns the properties to enrich the event properties with.\n  def properties_from_fields(fields)\n    {\n      fbc: fields[:fbc],\n      fbp: fields[:fbp],\n    }.compact\n  end\nend\n```\n\nIn order to use your destination, you need to tell Segmentation it exists:\n\n```ruby\n# config/initializers/segmentation.rb\nSegmentation.configure do |config|\n  # ...\n\n  config.destinations = [FacebookPixelDestination.new]\nend\n```\n\nThat's all you need! Now, Segmentation will store the latest version of the fields whenever the user\nmakes an authenticated request in your app, and it will include them when the you track an event for\nthat user.\n\n### Implementing your own storage\n\nOut of the box, Segmentation ships with two storage adapters: `ActiveRecordStorage`, which can be\nused to store fields on your user's record in the DB, and `NullStorage`, which can be used as a\nfallback when the user is not authenticated and therefore you cannot store any fields for them.\n\nIf you want to implement a custom storage, you can easily do it. Here's what a Redis storage might\nlook like, for example:\n\n```ruby\nclass RedisStorage \u003c Storage\n  attr_reader :redis\n\n  def initialize(redis)\n    @redis = redis\n  end\n\n  def read(user)\n    JSON.parse(redis.get(\"segmentation-fields/#{user.id}\")).with_indifferent_access\n  end\n\n  def write(user, fields)\n    redis.set(\"segmentation-fields/#{user.id}\", JSON.dump(fields)).with_indifferent_access\n  end\nend\n```\n\nFinally, tell Segmentation to use your new storage:\n\n```ruby\n# config/initializers/segmentation.rb\nSegmentation.configure do |config|\n  # ...\n\n  config.storage_builder = proc do |user|\n    if user\n      # If the user is authenticated, store their data in Redis.\n      RedisStorage.new(Redis.new)\n    else\n      # If the user is a guest, don't attempt to store their data.\n      Segmentation::Storages::NullStorage.new\n    end\n  end\nend\n```\n\nRemember that your `user` object might be `nil`! Your storage either needs to play nice with that\ncase, or you need to fall back to the `NullStorage` in that case.\n\n## Usage\n\n### Identifying users\n\nIf you include the `Segmentation::ControllerHelpers` module in your base controller, Segmentation\nwill automatically identify your user upon each authenticated request!\n\nYou can still identify users manually by calling `Segmentation::Client#identify` if needed.\n\n### Tracking events\n\n#### Tracking from a controller\n\nThe most common way to track an event in Segment is to do it from a controller. When you track\nevents from a controller, Segmentation already has all the context it needs to enrich the event\npayload with your user's traits and destinations.\n\nTo track an event from a controller, call `#segmentation_server_track(event, properties, options)`:\n\n```ruby\n# app/controllers/posts_controller.rb\nclass PostsController \u003c ApplicationController\n  def create\n    # ...\n\n    segmentation_server_track 'Post Created', {\n      title: @post.title,\n      # ...\n    }\n  end\nend\n```\n\n### Tracking from outside a controller\n\nSometimes, you may want to track Segment events from outside a controller. This may be the case if\nyou're performing an action on behalf of a user who's not present on the page anymore (e.g., the\nuser initiated the action, but you're executing it in a background job).\n\nTo track an event from outside a controller, you'll need to build your own Segmentation context and\nclient:\n\n```ruby\n# app/services/create_post.rb\nclass CreatePost\n  def call(...)\n    # ...\n    \n    context = Segmentation::Context.new(user: post.user)\n    segmentation = Segmentation::Client.new(context)\n      \n    segmentation.track('Post Created', {\n      title: post.title\n    })\n  end\nend\n```\n\n#### Tracking via JS\n\n\u003e NOTE: If your user is blocking tracking snippets through an extension, this method will still fail\n\u003e to track the events you initiate from the backend!\n\nA third option is to initiate the tracking from the backend, but execute it in the frontend. This\nis useful when you want the event payload to be enriched with data that's only available on the\nfrontend.\n\nTo track an event via JS, call `#segmentation_client_track(event, properties, options)`:\n\n```ruby\n# app/controllers/posts_controller.rb\nclass PostsController \u003c ApplicationController\n  def create\n    # ...\n\n    segmentation_client_track 'Post Created', {\n      title: @post.title,\n      # ...\n    }\n  end\nend\n```\n\nUnder the hood, Segmentation will store the event data in a Base64-encoded JSON cookie. On the next\npage load, Segmentation will read the cookie and track any events it contains via JS.\n\n## Testing\n\nIf you want to test that the right `identify` and `track` calls are being sent, you can use the\nSegmentation testing backend and RSpec matchers.\n\nFirst of all, add this to your `spec/rails_helper.rb`:\n\n```ruby\nrequire \"segmentation/testing/backend\"\nrequire \"segmentation/testing/rspec\"\n\nRSpec.configure do |config|\n  config.include Segmentation::Testing::RSpec\n  \n  config.before do\n    Segmentation.config.backend = Segmentation::Testing::Backend.new\n  end\nend\n```\n\nYou can then do the following in your tests to assert `#identify` calls:\n\n```ruby\n# run some code that identifies the user... \n\nexpect(Segmentation).to have_identified(traits: a_hash_including({ first_name: 'John' }))\n```\n\nYou can also assert `#track` calls:\n\n```ruby\n# run some code that tracks an event...\n\nexpect(Segmentation).to have_tracked(\n  event: 'Order Completed',\n  properties: a_hash_including(total: 61.0),\n)\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run\nthe tests. You can also run `bin/console` for an interactive prompt that will allow you to\nexperiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new\nversion, update the version number in `version.rb`, and then run `bundle exec rake release`, which\nwill create a git tag for the version, push git commits and tags, and push the `.gem` file to\n[rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/nebulab/segmentation.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnebulab%2Fsegmentation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnebulab%2Fsegmentation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnebulab%2Fsegmentation/lists"}