# Ruby on Rails

Add ProductLense to a Rails app in the application layout, then identify the signed-in user with a safely encoded id. Works with Turbo.

In Rails the script goes into the layout every page renders, usually `app/views/layouts/application.html.erb`. Turbo Drive changes the URL with the history API, which ProductLense records as pageviews, and it keeps the script loaded across visits.

## Add the script

Replace `SITE_ID` with the id from Settings → Sites in the ProductLense dashboard.

```erb title="app/views/layouts/application.html.erb" {6-7}
<!DOCTYPE html>
<html>
  <head>
    <title><%= content_for(:title) || "Example" %></title>
    <%= csrf_meta_tags %>
    <script>window.productlense=window.productlense||function(){(window.productlense.q=window.productlense.q||[]).push(arguments)}</script>
    <script defer data-site="SITE_ID" src="https://api.productlense.app/a.js"></script>
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
```

To keep the site id out of the template, store it in credentials and print it with `<%= Rails.application.credentials.dig(:productlense, :site_id) %>`.

## Identify users

`to_json` escapes `<`, `>` and `&` by default in Rails, so the value is safe inside a script block:

```erb title="app/views/layouts/application.html.erb" {3}
<% if current_user %>
  <script>
    window.productlense('identify', <%= current_user.id.to_s.to_json.html_safe %>, { email: <%= current_user.email.to_json.html_safe %> });
  </script>
<% end %>
```

Put it in the `<body>`, so Turbo runs it again after each visit. Repeated `identify` calls with the same id are harmless. Use the same id you store on the customer in your billing provider; see [how payments are attributed](/docs/revenue/attribution).

## Content Security Policy

If `config/initializers/content_security_policy.rb` is enabled, allow ProductLense and give the inline scripts the request nonce:

```ruby title="config/initializers/content_security_policy.rb"
Rails.application.configure do
  config.content_security_policy do |policy|
    policy.script_src  :self, "https://api.productlense.app"
    policy.connect_src :self, "https://api.productlense.app"
  end
  config.content_security_policy_nonce_generator = ->(request) { SecureRandom.base64(16) }
  config.content_security_policy_nonce_directives = %w(script-src)
end
```

Then write the inline scripts as `<%= javascript_tag nonce: true do %> ... <% end %>`.

## Verify your installation

1. Deploy, then open your site in a normal browser window.
2. Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.
3. In the browser's developer tools, the Network tab shows a `POST` to `https://api.productlense.app/api/e` with status `204`. Follow a link: Turbo navigates and a second pageview follows.

`bin/rails server` runs on `localhost`, where nothing is sent unless you add `data-dev="true"` to the tag. If the visit does not appear, follow [Verify your installation](/docs/install/verify).
