Introduction
Welcome to the Ramen Developer Documentation.
FAQ
How do I install RamenJS?
There are several ways to install RamenJS into your application:
- Segment.com
- Eager.io
- Our Ruby on Rails gem
- Manually
What platforms do you support?
RamenJS can be installed into any web application, although using it with a Flash application would be tough :)
Here are some of the platforms we’ve seen install RamenJS:
- Ruby on Rails
- Django
- Angular
- Meteor.js
- Wordpress
- Joomla
- Drupal
- PHP
Does RamenJS work on “Single Page Apps”?
Yes it does. See our Examples section.
Installation
Segment.com
Segment.com is a “customer data hub”. If you already are a Segment customer, you can install Ramen in just a few clicks without needing to write any code or deploy any changes to your application.
RamenJS supports the following Segment methods:
identify
group
track
page
Installation methods
Click the ‘Install via Segment’ button on the Getting Started page of your Ramen Organization.
Wait a couple minutes, and then head over to your application and
navigate to a page where Segment’s analytics.js
is installed.
After refreshing the page a few times, refresh this page and you
should see that RamenJS is receiving data.
Enable Secure Mode (Optional)
Once you’ve verified Ramen is enabled, you may want to enable Secure Mode.
Secure Mode ensures that nobody can pretend to be your customers. Enabling
Secure Mode requires some changes that a developer will have to make in your
Segment.com analytics.js
code.
Click here to view the Segment.com / Ramen Secure Mode Documentation
Learn More About Segment.com
To learn more about Segment, click here
Eager.io
Eager.io lets you install JavaScript libraries at the touch of a button. If you are already using Eager, you can install Ramen in just a few clicks without needing to write any code or deploy any changes to your application. If not, we strongly recommend looking into it, because it is awesome.
Installation methods
Click the ‘Install via Eager’ button on the Getting Started page of your Ramen Organization.
Wait a couple minutes, and then head over to your application and navigate to a page where Eager is loaded. After refreshing the page a few times, refresh this page and you should see that RamenJS is receiving data.
Learn More About Eager.io
To learn more about Eager, click here
Ruby on Rails Rubygem
Ramen maintains an official Rubygem for Ruby on Rails applications. The Rubygem is used on Ramen.is itself.
Wordpress
Thanks to our integration with Eager, installing RamenJS on Wordpress is a breeze.
Installation methods
Click the ‘Install via Eager’ button on the Getting Started page of your Ramen Organization.
When you are prompted, download and install the Wordpress plugin from Eager.
Wait a couple minutes, and then head over to your application and navigate to a page where Eager is loaded. After refreshing the page a few times, refresh this page and you should see that RamenJS is receiving data.
Drupal
Thanks to our integration with Eager, installing RamenJS on Drupal is a breeze.
Installation methods
Click the ‘Install via Eager’ button on the Getting Started page of your Ramen Organization.
When you are prompted, download and install the Drupal plugin from Eager.
Wait a couple minutes, and then head over to your application and navigate to a page where Eager is loaded. After refreshing the page a few times, refresh this page and you should see that RamenJS is receiving data.
Joomla
Thanks to our integration with Eager, installing RamenJS on Joomla is a breeze.
Installation methods
Click the ‘Install via Eager’ button on the Getting Started page of your Ramen Organization.
When you are prompted, download and install the Joomla plugin from Eager.
Wait a couple minutes, and then head over to your application and navigate to a page where Eager is loaded. After refreshing the page a few times, refresh this page and you should see that RamenJS is receiving data.
Angular
While there is no official library for Angular, we do have an Example App
Pay close attention to the comments on the commits to see what is going on: Commits
Concepts
Boostrapping
Getting RamenJS installed into your application requires two bits of code.
<script> window.ramenSettings = { organization_id: "lkadjf89383hr89", user: { id: "903849h", name: "Bugs Bunny", email: "ryan+bugs@ramen.is" } }; </script> <script src="https://cdn.ramen.is/assets/ramen.js" async></script>
First, you need to define the window.ramenSettings
object.
Then, you need to include the RamenJS library.
The order of operations is important here.
window.ramenSettings
must be defined before RamenJS
is loaded.
Custom installation
<script src="https://cdn.ramen.is/assets/ramen.js" async></script> <!-- Later... --> <script> $(function() { // See note Ramen.init.update({ organization_id: "lkadjf89383hr89", user: { id: "903849h", name: "Bugs Bunny", email: "ryan+bugs@ramen.is" } }); }); </script>
For some more complicated apps, you may not want to,
or be able to, define ramenSettings
at the time the
page renders.
In that case, load the RamenJS script tag as shown.
Later on, you can call Ramen.init.update
after the
page has completely loaded.
A note on order of operations
Note that calls to anything under the Ramen
object
need to be wrapped in some sort of onReady
callback
This is an issue we’ll be addressing with RamenJS v2.0, which will ship in the first half of 2016.
Secure Mode
Secure Mode ensures that nobody can spoof being one of your customers. It is disabled by default, because it takes a little extra developer time to setup correcgtly.
To enable secure mode, you need to add a few things
to your ramenSettings
object:
timestamp
: The current time in epoch secondsauth_hash
: A SHA-256 hash
Timestamp
ts = Time.now.to_i
ts = Math.floor(new Date()) / 1000)
The timestamp
attribute must be a time within the last
15 minutes.
Calculating auth_hash
array = [] array << "ryan@ramen.is" array << 42 array << "Ryan Angilly" array << 1234567890 array << "oihs9uhasdg8934y8" str = array.join(":") digest = (Digest::SHA256.new << str).to_s digest #=> "09cc0a69233f1bf41e68ed00b9256fde5a4542e9fdba635fe2a9a4edf9ee1ba1"
The hash is calculated as follows:
Create an array with the following elements:
email
: eg."ryan@ramen.is"
id
: eg.42
name
: eg.Ryan Angilly
timestamp
: eg.1234567890
ORGANIZATION_SECRET_KEY
: eg.oihs9uhasdg8934y8
Your array now looks like this:
["ryan@ramen.is", 42, "Ryan Angilly", 1234567890, "oihs9uhasdg8934y8"
Join the elements of that array together with a colon (
:
) into a stringYour string is now:
"ryan@ramen.is:42:Ryan Angilly:1234567890:oihs9uhasdg8934y8"
Generate a SHA256 hash from that string
The SHA256 hash for the string in step 4 is:
09cc0a69233f1bf41e68ed00b9256fde5a4542e9fdba635fe2a9a4edf9ee1ba1
Customers
<script> window.ramenSettings = { organization_id: "lkadjf89383hr89", user: { id: "903849h", name: "Bugs Bunny", email: "ryan+bugs@ramen.is" } }; </script>
Ramen is used for asking questions, but only of your users. This means that until a User has logged into your app, there is no way Ramen will be able to interact with them.
In Ramen terminology, a we call a person logged into you application a ‘Customer’.
RamenJS must know the following things about your customers:
Attribute | Type | Required | Notes |
---|---|---|---|
id | String | Yes | A unique identifier |
String | Yes | A valid email address | |
name | String | Yes | The name of the person |
In additional, ramenSettings
also accepts the following options:
Attribute | Type | Required | Notes |
---|---|---|---|
created_at | Integer | No | The unix timestamp (epoch seconds, not milliseconds) for when this account was created. |
value | Float | No | An arbitrary value for that person. Used for Audience creation. |
traits | Object | No | See Custom Traits below |
bug_image_url | String | No | URL for an image to replace the standard green Ramen ‘R’ icon. See Customization below. |
question_id | String | No | The ID of a question. This will forces a specific question to appear no matter what. |
Companies
<script> window.ramenSettings = { organization_id: "83hakj3", user: { /* Explained above */ }, company: { id: "42", name: "Ramen", url: "https://ramen.is" value: 353000.0, traits: { is_rad: true } } }; </script>
You can group a Customer into a Company. A Company is an organization that the currently logged in person belongs to.
Company data can be used when creating Audiences. So you could, for example, target a question to only appear to Customers that are members of a specific Company.
RamenJS wants to know the following things about Companies:
Attribute | Type | Required | Notes |
---|---|---|---|
id | String | Yes | A unique identifier |
url | String | Yes | A valid URL for the comapny |
name | String | Yes | The name of the company |
value | Float | No | An arbitrary value for that company. Used for Audience creation. |
traits | Object | No | See Custom Traits below |
Custom Traits
<script> window.ramenSettings = { organization_id: "iafh89335", user: { /* Explained above */ }, company: { id: "42", name: "Ramen", url: "https://ramen.is" value: 353000.0, traits: { is_rad: true, funded_at: 1234567890 } } }; </script>
Both Customers and Companies can have “Traits” assigned to them. Traits are key/value pairs that can be used for Audience creation:
Names
Trait names are strings that can contain any letter,
any number, underscores (_
) and dashes (-
).
Trait names are case-sensitive.
Types
Trait values can be the following types:
- String
- Boolean
- Integer
- Float
- Time (See below)
Time types
Time types are declared by ending the trait
name in _at
. For example:
last_seen_at
upgraded_at
You can pass in times in the following formats:
- Epoch seconds:
1234567890
- XML Schema:
2009-02-13T23:31:30Z
(UTC) or2009-02-13T16:31:30-07:00
(with offset)
Type Casting
The first time RamenJS sees a new trait with a non-null value, it will record the type and cast all future traits to that type.
Customization
Icon
<script> window.ramenSettings = { organization_id: "lkadjf89383hr89", user: { id: "903849h", name: "Bugs Bunny", email: "ryan+bugs@ramen.is", bug_image_url: "https://thecatapi.com/api/images/get?format=src" } }; </script>
The RamenJS popups were designed to be minimal so that they would Just Look Good™ on any webpage. The style of the popups cannot be modified, but the icon, the little green Ramen ‘R’ can be chanaged.
Product Center
Product Center is a feature that lets you discuss Features with your Customers. Product Center is only available on certain paid plans.
Custom Links
<script> window.ramenSettings = { organization_id: "...", user: { /* Explained above */ }, company: { /* Explained above */ }, custom_links: [ { title: 'E-mail support', href: 'mailto:support@ramen.is', }, { title: 'Chat with support', callback: 'Interom("showNewMessage")' } /* This is not a type. If you want to use Intercom for in-app chat, this is one way to make it play nice w/ Ramen. */ ] }; </script>
We provide a way for you to customize links that appear at the bottom of Product Center.
Custom Links have a title
property and either an
href
or a callback
property. href
s
must be full URIs (including protocol, ie. https).
callback
s can be JavaScript closures
or strings that will be eval
’d
You are allowed to provide up to 3 Custom Links.
Placement
<nav> <ul> <li><a href="/account">Your Account</a></li> <li><a href="/product-center" id="custom-ramen-trigger">Product Center</a></li> </ul> </nav> <style> ._r-badge:after { top: -5px; right: -5px; background-color: red; -webkit-border-radius: 100px; -moz-border-radius: 100px; border-radius: 100px; content: " "; height: 18px; width: 18px; position: absolute; font-size: 12px; line-height: 16px; } ._r-related { font-weight: bold; } </style> <script> window.ramenSettings = { organization_id: YOUR_ORG_ID, user: {...}, company: {...}, custom_trigger_id: "custom-ramen-trigger" }; </script>
You can set a custom placement, called a ‘trigger’ for the RamenJS widget so that instead of showing the icon in the bottom right or left corner of the screen, a link in your nav can be used to trigger opening Product Center.
To enable this customization, set
ramenSettings.custom_trigger_id
to the
ID of the element.
Styling the Custom Trigger
We apply different styles to the RamenJS widget:
- When there is a Feature related to the current URL, the widget icon will be full color.
- When there are no related features, a grayscale image is used instead.
- When there are newly deployed features, a circular red badge is placed over the top right corner of the icon.
When using a customer trigger, we do not attempt to modify the style to reflect state changes. Instead, we will add classes to the element. It will be up to you to style the elements.
_r-related
: We will add this class when there are Features related to the current URL_r-badge
: We will add this class when there are newly deployed features that have not yet been seen by your customers.
Examples
Here are some heavily commented examples.
Ruby/ERB
If you’re on Ruby on Rails, you should check out our Rubygem, but if not, here is a Ruby/ERB example.
<!-- 1. OPTIONAL: This <link> tag can goes at the top of your <head> to prefetch DNS --> <link rel="dns-prefetch" href="http://cdn.ramen.dev"> <!-- 2. This script tag can go anywhere in the <body> but **before** the RamenJS tag --> <script> <% @ts = Time.now.to_i %> <!-- Optional unless you want to use Secure Mode --> window.ramenSettings = { organization_id: "ORGANIZATION_ID", user: { // Required. If you don't have a name, pass in their email here. name: <%= current_user.name.to_json.html_safe %>, // Required. If you don't have email addresses for your logged in users, please contact support@ramen.is email: <%= current_user.email.to_json.html_safe %>, // Required. This must be unique. Changing this creates a new Customer. id: <%= current_user.id.to_s.to_json.html_safe %>, // Optional created_at: <%= current_user.created_at.to_i %> } // If you uncomment the optional attributes below, don't forget to add a comma (",") here // The next two keys are optional unless you want to enable Secure Mode // timestamp: <%= @ts %>, // auth_hash: <%= (Digest::SHA2.new << "#{current_user.email}:#{current_user.id}:#{current_user.name}:#{@ts}:ORGANIZATION_SECRET_KEY").to_s.to_json.html_safe %> }; </script> <!-- 3. This goes just before the closing </body> tag and **always after** where you set window.ramenSettings --> <script src="http://cdn.ramen.dev/assets/ramen.js" async> </script>
JavaScript
<!-- 1. OPTIONAL: This <link> tag can goes at the top of your <head> to prefetch DNS --> <link rel="dns-prefetch" href="https://cdn.ramen.is"> <!-- 2. This script tag can go anywhere in the <body> but **before** the RamenJS tag --> <script> window.ramenSettings = { organization_id: "YOUR_ORGANIZATION_ID", user: { // Required. If you don't have a name, pass in their email here. name: "Ryan Angilly", // Required. If you don't have email addresses for your logged in users, // please contact support@ramen.is email: "ryan@ramen.is", // Required. This must be unique. Changing this creates a new Customer. id: "527de734763a556882001003", // Optional created_at: 1234567890 } // If you uncomment the optional attributes below, don't forget to add a comma (",") here // The next two keys are optional unless you want to enable Secure Mode // timestamp: ts_used_server_side_to_calculate_auth_hash, // auth_hash: auth_hash_calculated_server_side }; </script> <!-- 3. This goes just before the closing </body> tag and **always after** where you set window.ramenSettings --> <script src="https://ramen.is/assets/ramen.js" async> </script>
Wordpress/PHP
If you’re on Wordpress, you should checkout our Eager.io integration, but here is an example:
<!-- 1. OPTIONAL: This <link> tag can goes at the top of your <head> to prefetch DNS --> <link rel="dns-prefetch" href="http://cdn.ramen.dev"> <!-- 2. This script tag can go anywhere in the <body> but **before** the RamenJS tag --> <?php if ( is_user_logged_in() ) { ?> <?php global $current_user; get_currentuserinfo(); $user_roles = $current_user->roles; $user_role = array_shift($user_roles); // The next two keys are optional unless you want to enable Secure Mode $_ramen_ts = time(); $_ramen_items = array($current_user->user_email, $current_user->ID, $current_user->display_name, $_ramen_ts, "ORGANIZATION_SECRET_KEY"); ?> <script> window.ramenSettings = { organization_id: "ORGANIZATION_ID", user: { // Required. If you don't have a name, pass in their email here. name: "<?php echo $current_user->display_name; ?>", // Required. If you don't have email addresses for your logged in users, please contact support@ramen.is email: "<?php echo $current_user->user_email; ?>", // Required. This must be unique. Changing this creates a new Customer. id: <?php echo $current_user->ID; ?> , // Optional created_at: <?php echo $current_user->created_at; ?> } // If you uncomment the optional attributes below, don't forget to add a comma (",") here // The next two keys are optional unless you want to enable Secure Mode // timestamp: <?php echo $_ramen_ts ?>, // auth_hash: "<?php echo hash("sha256", implode(":", $_ramen_items)); ?>" }; </script> <?php } ?> <!-- 3. This goes just before the closing </body> tag and **always after** where you set window.ramenSettings --> <script src="http://cdn.ramen.dev/assets/ramen.js" async> </script>
Python
Thanks to fjania for putting this together. Gist available here
import hashlib import time def ramen(request): ramen_js = ''; if not request.user.is_anonymous(): email = request.user.email user_id = request.user.id name = "{} {}".format(request.user.first_name, request.user.last_name) timestamp = int(time.time()) secret_key = "SECRET_KEY_GOES_HERE" auth_input = "{}:{}:{}:{}:{}".format( email, user_id, name, timestamp, secret_key, ) auth_hash = hashlib.sha256(auth_input).hexdigest() ramen_js = ''' <!-- This can go anywhere before the ramen.js tag --> <script> window.ramenSettings = {{ organization_id: "555cc17077656204242f0000", user: {{ name: "{name}", email: "{email}", id: "{id}", }}, timestamp: {timestamp}, auth_hash: "{auth_hash}" }}; </script> <!-- This goes before the </body> tag --> <script src="https://cdn.ramen.is/assets/ramen.js" async> </script> '''.format( name = name, id = user_id, email = email, timestamp = timestamp, auth_hash = auth_hash, ) return { 'ramen_js': ramen_js }
Reference
window.ramenSettings
All the options you can set on ramenSettings, the same object
you can pass to Ramen.Api.update(config)
for subsequent updates.
window.ramenSettings = { /* * Core Options */ // Your Organization ID // Required organization_id: "", // The currently logged in user // Required user: { }, // The company of the currently logged in user company: { }, // Environment. Defaults to production. If you set this // to anything other than 'production' then you can use // Secure Mode but it will not be forced on. This way // You can test it in Development, Staging, etc... before // deploying // Default: "production" environment: "production", /* * Secure Mode Options * Secure Mode is disabled by default. Once enabled, it * cannot be turned off. */ // Options for Secure Mode ts: 1234567890, auth_hash: "mumbo-jumbo", /* * Misc Options */ // Disables watching `window.location`, and automatically calling // .page() when a change (include anchor changes) are noticed. // Default: false disable_location_watch: false, // The ID of a Question you want to force ask // Default: null question_id: "string-id-of-question", // Make RamenJS not log to console // Default: false silent: false, /* * Product Center Options */ // Custom Trigger Element customer_trigger_id: "el-id", // Custom Links custom_links: [ ] };
Ramen.Api.update(config)
config = { user: { id: '34349853', name: 'Marky Marcus', email: 'ryan+marky@ramen.is' } }; Ramen.Api.update(config);
This updates the data for the currently logged in user.
Ramen.Api.track(event_name)
<a href="/unsubscribe" onclick="Ramen.Api.track('Clicked Unsubscribe Link');"> I'm out. </a>
You can track events using Ramen.Api.track(event_name)
and then use that data
in Audience creation.
For example, you could create an event called “Clicked Unsubscribe Link”, and have a question appear on the following page.
Ramen.Api.page()
Ramen.Api.page()
Notify Ramen that the page has changed. This will trigger a lookup to see if the new page means the current customer should be asked a question.
By default, Ramen
will monitor window.location
for changes, but you
can force this with a call to .page()
Ramen.Api.die()
Ramen.Api.die()
Kill RamenJS. This will:
- Remove all elements added to the DOM
- Remove all listeners added
- Clear all intervals and timeouts
Credits
Weston Platter did a ton of work getting this site up. Tweet him a cat pic
A Cat Gif
Here is a cat gif:
Copyright Ramen, Inc. 2012 - 2016