backbone-mixpanel 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
@@ -0,0 +1,232 @@
1
+ describe("Backbone.Mixpanel", function () {
2
+ var bbm, options;
3
+
4
+ var initialize = function() {
5
+ bbm = Backbone.Mixpanel.init(options);
6
+ };
7
+
8
+ beforeEach(function () {
9
+ window.mixpanel = jasmine.createSpyObj("mixpanel", ["init", "track", "register", "name_tag"]);
10
+
11
+ options = { token: "abc123", customData: ['id', 'desc']};
12
+ });
13
+
14
+ describe(".initialize", function () {
15
+ beforeEach(function () {
16
+ options = {
17
+ token: "abc123",
18
+ userInfo: { id: 45, paid: true },
19
+ nameTag: "Foo-Bar"
20
+ };
21
+
22
+ initialize();
23
+ });
24
+
25
+ it("should initialize the mixpanel js", function () {
26
+ expect(mixpanel.init).toHaveBeenCalledWith("abc123");
27
+ });
28
+
29
+ it("should set the user information", function () {
30
+ expect(mixpanel.register).toHaveBeenCalledWith({ id: 45, paid: true });
31
+ });
32
+
33
+ it("should set a name tag", function () {
34
+ expect(mixpanel.name_tag).toHaveBeenCalledWith("Foo-Bar");
35
+ });
36
+
37
+ describe("when mixpanel is not defined", function () {
38
+ beforeEach(function () {
39
+ window.mixpanel = undefined;
40
+ });
41
+
42
+ it("should not error", function () {
43
+ expect(function() { initialize() }).not.toThrow();
44
+ });
45
+ });
46
+
47
+ describe("when not given a token", function () {
48
+ var message;
49
+
50
+ beforeEach(function () {
51
+ message = "Backbone.Mixpanel.init requires the mixpanel token from your account.";
52
+
53
+ delete options.token;
54
+ });
55
+
56
+ it("should error", function () {
57
+ expect(function() { initialize() }).toThrow(message);
58
+ });
59
+ });
60
+ });
61
+
62
+ describe("#wrapEvent", function () {
63
+ var func, wrapped, event;
64
+
65
+ beforeEach(function () {
66
+ initialize();
67
+
68
+ spyOn(bbm, "trackEvent");
69
+ func = jasmine.createSpy("Function");
70
+
71
+ wrapped = bbm.wrapEvent(func, "the default value");
72
+ event = jasmine.createSpy("Event");
73
+ });
74
+
75
+ describe("when not given a function", function () {
76
+ it("should error", function () {
77
+ expect(function() { bbm.wrapEvent() }).toThrow("Wrapping requires a function to wrap");
78
+ });
79
+ });
80
+
81
+ describe("when not given a default value", function () {
82
+ it("should error", function () {
83
+ expect(function() { bbm.wrapEvent(func) }).toThrow("Wrapping requires a default description");
84
+ });
85
+ });
86
+
87
+ it("should return a function that wraps the original function", function () {
88
+ wrapped({});
89
+ expect(func).toHaveBeenCalled();
90
+ });
91
+
92
+ it("should track the event", function () {
93
+ wrapped({});
94
+ expect(bbm.trackEvent).toHaveBeenCalled();
95
+ });
96
+
97
+ describe("for the description", function () {
98
+ describe("when the event's target has a data attribute", function () {
99
+ beforeEach(function () {
100
+ event.currentTarget = '<div data-event="Foo Event :)"></div>';
101
+
102
+ wrapped(event);
103
+ });
104
+
105
+ it("should log the event text", function () {
106
+ expect(bbm.trackEvent).toHaveBeenCalledWith("Foo Event :)", {});
107
+ });
108
+ });
109
+
110
+ describe("when the event's target does not have a data attribute", function () {
111
+ beforeEach(function () {
112
+ event.currentTarget = '<div data-invalid=":/"></div>';
113
+
114
+ wrapped(event);
115
+ });
116
+
117
+ it("should log the default description", function () {
118
+ expect(bbm.trackEvent).toHaveBeenCalledWith("the default value", {});
119
+ });
120
+ });
121
+ });
122
+
123
+ describe("for the additional data", function () {
124
+ describe("when the event's target has a metadata attribute", function () {
125
+ beforeEach(function () {
126
+ event.currentTarget = '<div data-id="THE ID"></div>';
127
+
128
+ wrapped(event);
129
+ });
130
+
131
+ it("should log custom id", function () {
132
+ expect(bbm.trackEvent.mostRecentCall.args[1].id).toEqual("THE ID");
133
+ });
134
+
135
+ describe("when the event's target has multiple metadata attributes", function () {
136
+ beforeEach(function () {
137
+ event.currentTarget = '<div data-id="12" data-desc="A Description!"></div>';
138
+
139
+ wrapped(event);
140
+ });
141
+
142
+ it("should log custom attributes", function () {
143
+ expect(bbm.trackEvent.mostRecentCall.args[1].id).toEqual(12);
144
+ expect(bbm.trackEvent.mostRecentCall.args[1].desc).toEqual("A Description!");
145
+ });
146
+ });
147
+ });
148
+ });
149
+ });
150
+
151
+ describe("#trackEvent", function () {
152
+ beforeEach(function () {
153
+ initialize();
154
+ });
155
+
156
+ it("should track the given string", function () {
157
+ bbm.trackEvent("Some Text");
158
+
159
+ expect(mixpanel.track).toHaveBeenCalledWith("Some Text", {});
160
+ });
161
+
162
+ it("should track the default extra information", function () {
163
+ bbm.trackEvent(null);
164
+
165
+ expect(mixpanel.track).toHaveBeenCalledWith(null, {});
166
+ });
167
+
168
+ it("should track the extra information", function () {
169
+ bbm.trackEvent(null, {extra: 'info', key: 'value'});
170
+
171
+ expect(mixpanel.track).toHaveBeenCalledWith(null, {extra: 'info', key: 'value'});
172
+ });
173
+ });
174
+
175
+ describe("#delegateEvents", function () {
176
+ var view,
177
+ delegate = function() {
178
+ bbm.delegateEvents.call(view);
179
+ };
180
+
181
+ beforeEach(function () {
182
+ initialize();
183
+ });
184
+
185
+ describe("when the view has no events", function () {
186
+ beforeEach(function () {
187
+ view = {};
188
+ });
189
+
190
+ it("should not error", function () {
191
+ expect(function() { delegate() }).not.toThrow();
192
+ });
193
+ });
194
+
195
+ describe("when the view has an events hash", function () {
196
+ beforeEach(function () {
197
+ view = {
198
+ $el: { on: function() {} },
199
+ undelegateEvents: function() {},
200
+ events: {
201
+ "click .some-class": "clickSomeClass"
202
+ }
203
+ };
204
+ });
205
+
206
+ it("should error (no method on the view)", function () {
207
+ expect(function() { delegate() }).toThrow("Method \"clickSomeClass\" does not exist");
208
+ });
209
+
210
+ describe("when the method exists", function () {
211
+ var method;
212
+
213
+ beforeEach(function () {
214
+ spyOn(bbm, 'wrapEvent').andCallThrough();
215
+
216
+ method = function() { return "hey" };
217
+ view.clickSomeClass = method;
218
+ });
219
+
220
+ it("should not error", function () {
221
+ expect(function() { delegate() }).not.toThrow();
222
+ });
223
+
224
+ it("should wrap the error", function () {
225
+ delegate();
226
+
227
+ expect(bbm.wrapEvent).toHaveBeenCalledWith(method);
228
+ });
229
+ });
230
+ });
231
+ });
232
+ });
@@ -0,0 +1,105 @@
1
+ Backbone.View.originalDelegateEvents = Backbone.View.prototype.delegateEvents;
2
+
3
+ Backbone.Mixpanel = (function() {
4
+ var self = {},
5
+ defaultOptions = {
6
+ token: null,
7
+ enabled: false,
8
+ eventDataAttr: 'event',
9
+ customData: [],
10
+ userInfo: {},
11
+ nameTag: ''
12
+ },
13
+ options = {};
14
+
15
+ // setup the Mixpanel environment
16
+ self.init = function(opts) {
17
+ _.extend(options, defaultOptions, opts || {});
18
+
19
+ if(!options.token) {
20
+ throw new Error("Backbone.Mixpanel.init requires the mixpanel token from your account.");
21
+ }
22
+
23
+ initialize();
24
+
25
+ return self;
26
+ };
27
+
28
+ // Returns a function that tracks to mixpanel and calls the original method
29
+ // method - (required) The method to handle an DOM event after tracking
30
+ self.wrapEvent = function(method, defaultDescription) {
31
+ if(!_.isFunction(method)) throw new Error("Wrapping requires a function to wrap");
32
+ if(!defaultDescription) throw new Error("Wrapping requires a default description");
33
+
34
+ return function(event) {
35
+ var data = {},
36
+ $target = $(event.currentTarget),
37
+ description = $target.data(options.eventDataAttr) || defaultDescription;
38
+
39
+ _(options.customData).each(function(key) {
40
+ var item = $target.data(key);
41
+
42
+ if(item) data[key] = item;
43
+ });
44
+
45
+ self.trackEvent(description, data);
46
+
47
+ method.apply(this, arguments);
48
+ }
49
+ };
50
+
51
+ // Track events to mixpanel when enabled and mixpanel is available
52
+ // desc - (required) The description of the event
53
+ // data - (optional) Any additional data included in the event
54
+ self.trackEvent = function(desc, data) {
55
+ data || (data = {});
56
+
57
+ // where the magic happens.
58
+ mixpanel.track(desc, data);
59
+ };
60
+
61
+ self.delegateEvents = function(events) {
62
+ if (!(events || (events = _.result(this, 'events')))) return;
63
+
64
+ for(var key in events) {
65
+ var method = events[key];
66
+ if(!_.isFunction(method)) method = this[method];
67
+ if(!method) throw new Error('Method "' + events[key] + '" does not exist');
68
+
69
+ var wr = self.wrapEvent(method, key);
70
+ events[key] = wr;
71
+
72
+ Backbone.View.originalDelegateEvents.call(this, events);
73
+ }
74
+ };
75
+
76
+ // Replace the delegateEvents function so that we can wrap each of the event
77
+ // handlers given via the events hash/function
78
+ Backbone.View.prototype.delegateEvents = self.delegateEvents;
79
+
80
+ return self;
81
+
82
+ // Setup the Mixpanel Javascript and initialize it with the Mixpanel token.
83
+ function initialize() {
84
+ window.mixpanel || (window.mixpanel = []);
85
+
86
+ var noop = function() {};
87
+
88
+ // Register the functions that we use internally to do nothing
89
+ if(!options.enabled) {
90
+ _.defaults(window.mixpanel, { init: noop, register: noop, name_tag: noop, track: noop });
91
+ }
92
+
93
+ if(options.enabled) loadMixpanel();
94
+
95
+ mixpanel.init(options.token);
96
+ mixpanel.register(options.userInfo);
97
+ mixpanel.name_tag(options.nameTag);
98
+ }
99
+
100
+ function loadMixpanel() {
101
+ (function(c,a){
102
+ window.mixpanel=a;var b,d,h,e;b=c.createElement('script');b.type='text/javascript';b.async=!0;b.src=('https:'===c.location.protocol?'https:':'http:')+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';d=c.getElementsByTagName('script')[0];d.parentNode.insertBefore(b,d);a._i=[];a.init=function(b,c,f){function d(a,b){var c=b.split('.');2==c.length&&(a=a[c[0]],b=c[1]);a[b]=function(){a.push([b].concat(Array.prototype.slice.call(arguments,0)))}}var g=a;'undefined'!==typeof f?g=a[f]=[]:f='mixpanel';g.people=g.people||[];h=['disable','track','track_pageview','track_links','track_forms','register','register_once','unregister','identify','alias','name_tag','set_config','people.set','people.increment','people.track_charge','people.append'];for(e=0;e<h.length;e++)d(g,h[e]);a._i.push([b,c,f])};a.__SV=1.2;
103
+ })(document,window.mixpanel);
104
+ }
105
+ }());
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: backbone-mixpanel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brian Norton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: ''
47
+ email:
48
+ - brian.nort@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .rspec
55
+ - Gemfile
56
+ - LICENSE.md
57
+ - README.md
58
+ - Rakefile
59
+ - backbone-mixpanel.gemspec
60
+ - lib/backbone-mixpanel.rb
61
+ - lib/backbone-mixpanel/engine.rb
62
+ - lib/backbone-mixpanel/version.rb
63
+ - spec/SpecRunner.html
64
+ - spec/lib/jasmine-1.3.1/MIT.LICENSE
65
+ - spec/lib/jasmine-1.3.1/jasmine-html.js
66
+ - spec/lib/jasmine-1.3.1/jasmine.css
67
+ - spec/lib/jasmine-1.3.1/jasmine.js
68
+ - spec/lib/javascripts/backbone.js
69
+ - spec/lib/javascripts/jquery.js
70
+ - spec/lib/javascripts/underscore.js
71
+ - spec/spec_helper.rb
72
+ - spec/vendor/assets/javascripts/backbone-mixpanel_spec.js
73
+ - vendor/assets/javascripts/backbone-mixpanel.js
74
+ homepage: ''
75
+ licenses: []
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 1.8.25
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: ''
98
+ test_files:
99
+ - spec/SpecRunner.html
100
+ - spec/lib/jasmine-1.3.1/MIT.LICENSE
101
+ - spec/lib/jasmine-1.3.1/jasmine-html.js
102
+ - spec/lib/jasmine-1.3.1/jasmine.css
103
+ - spec/lib/jasmine-1.3.1/jasmine.js
104
+ - spec/lib/javascripts/backbone.js
105
+ - spec/lib/javascripts/jquery.js
106
+ - spec/lib/javascripts/underscore.js
107
+ - spec/spec_helper.rb
108
+ - spec/vendor/assets/javascripts/backbone-mixpanel_spec.js
109
+ has_rdoc: