backbone-mixpanel 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: