Skip to content

Commit

Permalink
Merge branch 'release/0.1.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
poteto committed Sep 13, 2015
2 parents 496ac5c + 662afae commit 4510677
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 51 deletions.
147 changes: 101 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,25 @@ Using this addon, you can easily use bundled adapters for various analytics serv

Writing your own adapters for currently unsupported analytics services is easy too. If you'd like to then share it with the world, submit a pull request and we'll add it to the bundled adapters.

#### Currently supported services and options

1. `GoogleAnalytics`
- `id`: [Property ID](https://support.google.com/analytics/answer/1032385?hl=en), e.g. `UA-XXXX-Y`
2. `Mixpanel`
- `token`: [Mixpanel token](https://mixpanel.com/help/questions/articles/where-can-i-find-my-project-token)
3. `GoogleTagManager`
- `id`: [Container ID](https://developers.google.com/tag-manager/quickstart), e.g. `GTM-XXXX`

- `dataLayer`: An array containing a single POJO of information, e.g.:
```js
dataLayer = [{
'pageCategory': 'signup',
'visitorType': 'high-value'
}];
```
4. `KISSMetrics` (WIP)
5. `CrazyEgg` (WIP)

## Installing The Addon

For Ember CLI >= `0.2.3`:
Expand All @@ -23,6 +42,65 @@ For Ember CLI < `0.2.3`:
ember install:addon ember-metrics
```

## Configuration

To setup, you should first configure the service through `config/environment`:

```javascript
module.exports = function(environment) {
var ENV = {
metricsAdapters: [
{
name: 'GoogleAnalytics',
config: {
id: 'UA-XXXX-Y'
}
},
{
name: 'Mixpanel',
config: {
token: '0f76c037-4d76-4fce-8a0f-a9a8f89d1453'
}
},
{
name: 'LocalAdapter',
config: {
foo: 'bar'
}
}
]
}
}
```

Adapter names are PascalCased. Refer to the [list of supported adapters](#currently-supported-services-and-options) above for more information.

The `metricsAdapters` option in `ENV` accepts an array of objects containing settings for each analytics service you want to use in your app in the following format:

```js
/**
* @param {String} name Adapter name
* @param {Object} config Configuration options for the service
*/
{
name: 'Analytics',
config: {}
}
```

Values in the `config` portion of the object are dependent on the adapter. If you're writing your own adapter, you will be able to retrieve the options passed into it:
```js
// Example adapter
export default BaseAdapter.extend({
init() {
const { apiKey, options } = Ember.get(this, 'config');
this.setupService(apiKey);
this.setOptions(options);
}
});
```
## Usage
In order to use the addon, you must first [configure](#configuration) it, then inject it into any Object registered in the container that you wish to track. For example, you can call a `trackPage` event across all your analytics services whenever you transition into a route, like so:
Expand All @@ -34,7 +112,7 @@ import config from './config/environment';
const Router = Ember.Router.extend({
location: config.locationType,
metrics: inject.service(),
metrics: Ember.inject.service(),
didTransition() {
this._super(...arguments);
Expand Down Expand Up @@ -109,51 +187,6 @@ ga('send', {

To add an attribute, just prefix it with `metrics` and enter it in camelcase.

## Configuration

To setup, you should first configure the service through `config/environment`:

```javascript
module.exports = function(environment) {
var ENV = {
metricsAdapters: [
{
name: 'GoogleAnalytics',
config: {
id: 'UA-XXXX-Y'
}
},
{
name: 'Mixpanel',
config: {
token: '0f76c037-4d76-4fce-8a0f-a9a8f89d1453'
}
},
{
name: 'LocalAdapter',
config: {
foo: 'bar'
}
}
]
}
}
```

The `metricsAdapters` option in `ENV` accepts an array of objects containing settings for each analytics service you want to use in your app in the following format:

```js
{
name: 'Analytics',
config: {
anyKey: 'someValue',
apiKey: 'eb4bb7f1'
}
}
```

Values in the `config` portion of the object are dependent on the adapter.

### Lazy Initialization

If your app implements dynamic API keys for various analytics integration, you can defer the initialization of the adapters. Instead of configuring `ember-metrics` through `config/environment`, you can call the following from any Object registered in the container:
Expand Down Expand Up @@ -203,6 +236,28 @@ This method is called when an adapter is activated by the service. It is respons
When the adapter is destroyed, it should remove its script tag and property. This is usually defined on the `window`.
### Usage
Once you have implemented your adapter, you can add it to your [app's config](#configuration), like so:

```js
module.exports = function(environment) {
var ENV = {
metricsAdapters: [
{
name: 'MyAdapter',
config: {
secret: '29fJs90qnfEa',
options: {
foo: 'bar'
}
}
}
]
}
}
```

## Testing

For unit tests, you will need to specify the adapters in use under `needs`, like so:
Expand Down
22 changes: 20 additions & 2 deletions addon/metrics-adapters/google-analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import objectTransforms from '../utils/object-transforms';
import BaseAdapter from './base';

const {
isPresent,
copy,
assert,
merge,
get,
Expand All @@ -18,22 +20,38 @@ export default BaseAdapter.extend({
},

init() {
const config = get(this, 'config');
const config = copy(get(this, 'config'));
const { id } = config;

assert(`[ember-metrics] You must pass a valid \`id\` to the ${this.toString()} adapter`, id);

delete config.id;

const hasOptions = isPresent(Object.keys(config));

if (canUseDOM) {
/* jshint ignore:start */
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
window.ga('create', id, 'auto');
/* jshint ignore:end */

if (hasOptions) {
window.ga('create', id, config);
} else {
window.ga('create', id, 'auto');
}
}
},

identify(options = {}) {
const compactedOptions = compact(options);
const { distinctId } = compactedOptions;

window.ga('set', 'userId', distinctId);
},

trackEvent(options = {}) {
const compactedOptions = compact(options);
const sendEvent = { hitType: 'event' };
Expand Down
2 changes: 1 addition & 1 deletion app/initializers/metrics.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import config from '../config/environment';

export function initialize(_container, application ) {
const { metricsAdapters } = config;
const { metricsAdapters = {} } = config;

application.register('config:metrics', metricsAdapters, { instantiate: false });
application.inject('service:metrics', 'metricsAdapters', 'config:metrics');
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ember-metrics",
"version": "0.1.4",
"version": "0.1.5",
"description": "Send data to multiple analytics integrations without re-implementing new API",
"directories": {
"doc": "doc",
Expand Down
11 changes: 11 additions & 0 deletions tests/unit/metrics-adapters/google-analytics-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ moduleFor('ember-metrics@metrics-adapter:google-analytics', 'google-analytics ad
}
});

test('#identify calls ga with the right arguments', function(assert) {
const adapter = this.subject({ config });
const stub = sandbox.stub(window, 'ga', () => {
return true;
});
adapter.identify({
distinctId: 123
});
assert.ok(stub.calledWith('set', 'userId', 123), 'it sends the correct arguments');
});

test('#trackEvent returns the correct response shape', function(assert) {
const adapter = this.subject({ config });
sandbox.stub(window, 'ga');
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/services/metrics-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ test('#invoke invokes the named method on activated adapters', function(assert)

assert.ok(GoogleAnalyticsSpy.calledOnce, 'it invokes the identify method on the adapter');
assert.ok(GoogleAnalyticsSpy.calledWith(options), 'it invokes with the correct arguments');
assert.ok(GoogleAnalyticsStub.calledOnce, 'it invoked the GoogleAnalytics method');
assert.ok(MixpanelSpy.calledOnce, 'it invokes the identify method on the adapter');
assert.ok(MixpanelSpy.calledWith(options), 'it invokes with the correct arguments');
assert.ok(MixpanelStub.calledOnce, 'it invoked the Mixpanel method');
assert.equal(GoogleAnalyticsStub.callCount, 0, 'it does not call methods that are not implemented by the adapter');
});

test('#invoke invokes the named method on a single activated adapter', function(assert) {
Expand Down

0 comments on commit 4510677

Please sign in to comment.