mixpal 0.0.5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +23 -0
- data/.travis.yml +5 -0
- data/README.md +7 -11
- data/Rakefile +8 -1
- data/lib/mixpal.rb +7 -7
- data/lib/mixpal/event.rb +4 -4
- data/lib/mixpal/integration.rb +7 -7
- data/lib/mixpal/tracker.rb +29 -26
- data/lib/mixpal/user.rb +9 -7
- data/lib/mixpal/util.rb +3 -3
- data/lib/mixpal/version.rb +1 -1
- data/mixpanel_assistant.gemspec +1 -0
- data/spec/lib/mixpal/event_spec.rb +19 -17
- data/spec/lib/mixpal/tracker_spec.rb +127 -149
- data/spec/lib/mixpal/user_spec.rb +36 -30
- data/spec/lib/mixpal/util_spec.rb +15 -14
- data/spec/lib/mixpal_spec.rb +2 -2
- data/spec/spec_helper.rb +4 -6
- data/spec/support/matchers/element_matchers.rb +2 -2
- data/test_app/.gitignore +16 -0
- data/test_app/Gemfile +11 -0
- data/test_app/README.rdoc +28 -0
- data/test_app/Rakefile +6 -0
- data/test_app/app/assets/images/.keep +0 -0
- data/test_app/app/assets/javascripts/application.js +16 -0
- data/test_app/app/assets/stylesheets/application.css +15 -0
- data/test_app/app/controllers/application_controller.rb +12 -0
- data/test_app/app/controllers/concerns/.keep +0 -0
- data/test_app/app/controllers/redirects_controller.rb +10 -0
- data/test_app/app/helpers/application_helper.rb +2 -0
- data/test_app/app/mailers/.keep +0 -0
- data/test_app/app/models/.keep +0 -0
- data/test_app/app/models/concerns/.keep +0 -0
- data/test_app/app/views/layouts/application.html.erb +16 -0
- data/test_app/app/views/redirects/new.html.erb +1 -0
- data/test_app/bin/bundle +3 -0
- data/test_app/bin/rails +8 -0
- data/test_app/bin/rake +8 -0
- data/test_app/bin/spring +18 -0
- data/test_app/config.ru +4 -0
- data/test_app/config/application.rb +30 -0
- data/test_app/config/boot.rb +4 -0
- data/test_app/config/environment.rb +5 -0
- data/test_app/config/environments/development.rb +37 -0
- data/test_app/config/environments/production.rb +82 -0
- data/test_app/config/environments/test.rb +39 -0
- data/test_app/config/initializers/assets.rb +8 -0
- data/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test_app/config/initializers/cookies_serializer.rb +3 -0
- data/test_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/test_app/config/initializers/inflections.rb +16 -0
- data/test_app/config/initializers/mime_types.rb +4 -0
- data/test_app/config/initializers/session_store.rb +3 -0
- data/test_app/config/initializers/wrap_parameters.rb +14 -0
- data/test_app/config/locales/en.yml +23 -0
- data/test_app/config/routes.rb +4 -0
- data/test_app/config/secrets.yml +22 -0
- data/test_app/db/seeds.rb +7 -0
- data/test_app/lib/assets/.keep +0 -0
- data/test_app/lib/tasks/.keep +0 -0
- data/test_app/log/.keep +0 -0
- data/test_app/public/404.html +67 -0
- data/test_app/public/422.html +67 -0
- data/test_app/public/500.html +66 -0
- data/test_app/public/favicon.ico +0 -0
- data/test_app/public/robots.txt +5 -0
- data/test_app/vendor/assets/javascripts/.keep +0 -0
- data/test_app/vendor/assets/stylesheets/.keep +0 -0
- metadata +67 -6
- data/spec/support/mock_rails.rb +0 -6
- data/spec/support/mock_storage.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c04724d3a3e803dfd392c4635ee4480a41e959d4
|
4
|
+
data.tar.gz: 0ddb3d1b74618d3523cf32ed59ddbbbf39df457a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33668e11499b7a84380c2421d710213af257c858ed45cdc017c4e1aec0855277c73f65f818a323dc35ef520cc3484f8cbd320ff9dff3d9a0940dfebfbe25633f
|
7
|
+
data.tar.gz: 2ac2f904b86d599230c60b0d9a7f31327f03c3920ed60b2ce221ac1414992a556ed021951001852060bb3d88eabd38e0122697fa8d2a64a6f3d475568b47215f
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Encoding:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Documentation:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
ClassAndModuleChildren:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
ClassLength:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
MethodLength:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
DoubleNegation:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
AllCops:
|
20
|
+
Include:
|
21
|
+
- "Rakefile"
|
22
|
+
Exclude:
|
23
|
+
- "test_app/**/*"
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Mixpal
|
1
|
+
# Mixpal [![Build Status](https://travis-ci.org/patbenatar/mixpal.svg?branch=master)](https://travis-ci.org/patbenatar/mixpal) [![Code Climate](https://codeclimate.com/github/patbenatar/mixpal/badges/gpa.svg)](https://codeclimate.com/github/patbenatar/mixpal)
|
2
2
|
|
3
3
|
As the JavaScript library is Mixpanel's preferred method of usage,
|
4
4
|
Mixpal aims to make it easier to work with from your Rails backend.
|
@@ -80,7 +80,7 @@ As with `register_user`, this method will also identify "special properties".
|
|
80
80
|
|
81
81
|
### Persistance Across Redirects
|
82
82
|
|
83
|
-
Mixpal stores any tracked events or user data in
|
83
|
+
Mixpal stores any tracked events or user data in the session when
|
84
84
|
it detects a redirect so it can output the appropriate Mixpanel JS integration
|
85
85
|
code to the client on the following render. This enables us to do cool things
|
86
86
|
like:
|
@@ -104,16 +104,12 @@ class UsersController < ActionController::Base
|
|
104
104
|
end
|
105
105
|
```
|
106
106
|
|
107
|
-
####
|
107
|
+
#### A note about `CookieStore` size limit
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
```
|
114
|
-
|
115
|
-
Storage adapters must implement the following API: `write(key, value)`,
|
116
|
-
`read(key)`, and `delete(key)`.
|
109
|
+
When using Rails' default `ActionDispatch::Session::CookieStore`, a 4K cookie
|
110
|
+
size limit is enforced. This cookie is shared by anything using the session.
|
111
|
+
If you anticipate tracking many events or large data sets to Mixpal,
|
112
|
+
[consider a different session store](http://guides.rubyonrails.org/action_controller_overview.html#session).
|
117
113
|
|
118
114
|
## Contributing
|
119
115
|
|
data/Rakefile
CHANGED
data/lib/mixpal.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'mixpal/version'
|
2
|
+
require 'active_support/core_ext'
|
3
3
|
|
4
4
|
module Mixpal
|
5
|
-
autoload :Util,
|
6
|
-
autoload :Tracker,
|
7
|
-
autoload :Event,
|
8
|
-
autoload :User,
|
9
|
-
autoload :Integration,
|
5
|
+
autoload :Util, 'mixpal/util'
|
6
|
+
autoload :Tracker, 'mixpal/tracker'
|
7
|
+
autoload :Event, 'mixpal/event'
|
8
|
+
autoload :User, 'mixpal/user'
|
9
|
+
autoload :Integration, 'mixpal/integration'
|
10
10
|
end
|
data/lib/mixpal/event.rb
CHANGED
@@ -14,13 +14,13 @@ module Mixpal
|
|
14
14
|
|
15
15
|
def to_store
|
16
16
|
{
|
17
|
-
name
|
18
|
-
properties
|
17
|
+
'name' => name,
|
18
|
+
'properties' => properties
|
19
19
|
}
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.from_store(data)
|
23
|
-
new(data[
|
23
|
+
new(data['name'], data['properties'])
|
24
24
|
end
|
25
25
|
end
|
26
|
-
end
|
26
|
+
end
|
data/lib/mixpal/integration.rb
CHANGED
@@ -10,25 +10,25 @@ module Mixpal
|
|
10
10
|
def self.mixpanel_identity(object_method, attribute_method)
|
11
11
|
self.mixpanel_identity_data = {
|
12
12
|
object_method: object_method,
|
13
|
-
attribute_method: attribute_method
|
13
|
+
attribute_method: attribute_method
|
14
14
|
}
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def mixpanel
|
19
19
|
@mixpanel ||= begin
|
20
|
-
identity = if data = self.class.mixpanel_identity_data
|
21
|
-
|
22
|
-
|
20
|
+
identity = if (data = self.class.mixpanel_identity_data)
|
21
|
+
send(data[:object_method]).try(data[:attribute_method])
|
22
|
+
end
|
23
23
|
|
24
|
-
Mixpal::Tracker.new(identity: identity)
|
24
|
+
Mixpal::Tracker.new(identity: identity).tap { |t| t.restore!(session) }
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
private
|
29
29
|
|
30
30
|
def store_mixpanel_if_redirecting
|
31
|
-
mixpanel.store! if status == 302
|
31
|
+
mixpanel.store!(session) if status == 302
|
32
32
|
end
|
33
33
|
end
|
34
|
-
end
|
34
|
+
end
|
data/lib/mixpal/tracker.rb
CHANGED
@@ -2,16 +2,12 @@ module Mixpal
|
|
2
2
|
class Tracker
|
3
3
|
attr_reader :events, :user_updates, :identity, :alias_user
|
4
4
|
|
5
|
-
STORAGE_KEY =
|
6
|
-
class_attribute :storage
|
7
|
-
self.storage = Rails.cache
|
5
|
+
STORAGE_KEY = 'mixpal'
|
8
6
|
|
9
|
-
def initialize(args={})
|
7
|
+
def initialize(args = {})
|
10
8
|
@events = []
|
11
9
|
@user_updates = []
|
12
10
|
|
13
|
-
restore!
|
14
|
-
|
15
11
|
@identity = args[:identity]
|
16
12
|
end
|
17
13
|
|
@@ -24,44 +20,51 @@ module Mixpal
|
|
24
20
|
user_updates << Mixpal::User.new(properties)
|
25
21
|
end
|
26
22
|
|
27
|
-
def track(name, properties={})
|
23
|
+
def track(name, properties = {})
|
28
24
|
events << Mixpal::Event.new(name, properties)
|
29
25
|
end
|
30
26
|
|
31
27
|
def render
|
32
|
-
|
33
|
-
html <<
|
28
|
+
''.tap do |html|
|
29
|
+
html << '<script type="text/javascript">'
|
34
30
|
html << "mixpanel.alias(\"#{identity}\");" if alias_user
|
35
31
|
html << "mixpanel.identify(\"#{identity}\");" if identity
|
36
|
-
html << events.map(&:render).join(
|
37
|
-
html << user_updates.map(&:render).join(
|
38
|
-
html <<
|
32
|
+
html << events.map(&:render).join('')
|
33
|
+
html << user_updates.map(&:render).join('')
|
34
|
+
html << '</script>'
|
39
35
|
end.html_safe
|
40
36
|
end
|
41
37
|
|
42
|
-
def store!
|
43
|
-
|
38
|
+
def store!(session)
|
39
|
+
session[STORAGE_KEY] = to_store
|
44
40
|
end
|
45
41
|
|
46
|
-
|
42
|
+
def restore!(session)
|
43
|
+
data = session[STORAGE_KEY] || {}
|
44
|
+
|
45
|
+
@alias_user = data['alias_user']
|
46
|
+
@identity ||= data['identity']
|
47
47
|
|
48
|
-
|
49
|
-
|
48
|
+
if data['events']
|
49
|
+
@events = data['events'].map { |e| Mixpal::Event.from_store(e) }
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
if data['user_updates']
|
53
|
+
@user_updates = data['user_updates']
|
54
|
+
.map { |u| Mixpal::User.from_store(u) }
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
+
session.delete(STORAGE_KEY)
|
57
58
|
end
|
58
59
|
|
60
|
+
private
|
61
|
+
|
59
62
|
def to_store
|
60
63
|
{
|
61
|
-
alias_user
|
62
|
-
identity
|
63
|
-
events
|
64
|
-
user_updates
|
64
|
+
'alias_user' => alias_user,
|
65
|
+
'identity' => identity,
|
66
|
+
'events' => events.map(&:to_store),
|
67
|
+
'user_updates' => user_updates.map(&:to_store)
|
65
68
|
}
|
66
69
|
end
|
67
70
|
end
|
data/lib/mixpal/user.rb
CHANGED
@@ -12,12 +12,12 @@ module Mixpal
|
|
12
12
|
|
13
13
|
def to_store
|
14
14
|
{
|
15
|
-
properties
|
15
|
+
'properties' => properties
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.from_store(data)
|
20
|
-
new(data[
|
20
|
+
new(data['properties'])
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
@@ -29,15 +29,17 @@ module Mixpal
|
|
29
29
|
# Isolate special properties and rename their keys to align with
|
30
30
|
# Mixpanel's naming.
|
31
31
|
def properties_for_mixpanel
|
32
|
-
Hash[
|
32
|
+
Hash[
|
33
|
+
properties.map { |k, v| [mixpanel_special_properties_map[k] || k, v] }
|
34
|
+
]
|
33
35
|
end
|
34
36
|
|
35
37
|
def mixpanel_special_properties_map
|
36
38
|
{
|
37
|
-
name:
|
38
|
-
email:
|
39
|
-
created_at:
|
39
|
+
name: '$name',
|
40
|
+
email: '$email',
|
41
|
+
created_at: '$created'
|
40
42
|
}.with_indifferent_access
|
41
43
|
end
|
42
44
|
end
|
43
|
-
end
|
45
|
+
end
|
data/lib/mixpal/util.rb
CHANGED
@@ -2,12 +2,12 @@ module Mixpal
|
|
2
2
|
module Util
|
3
3
|
class << self
|
4
4
|
def hash_to_js_object_string(hash)
|
5
|
-
hash.reject! { |
|
5
|
+
hash.reject! { |_, v| v.nil? }
|
6
6
|
|
7
|
-
contents = hash.map do |k,v|
|
7
|
+
contents = hash.map do |k, v|
|
8
8
|
js_value = v.is_a?(String) || v.is_a?(Time) ? "\"#{v}\"" : v
|
9
9
|
"\"#{k}\": #{js_value}"
|
10
|
-
end.join(
|
10
|
+
end.join(',').html_safe
|
11
11
|
|
12
12
|
"{#{contents}}"
|
13
13
|
end
|
data/lib/mixpal/version.rb
CHANGED
data/mixpanel_assistant.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "pry"
|
24
|
+
spec.add_development_dependency "rubocop", "~> 0.24.1"
|
24
25
|
spec.add_development_dependency "rspec", "~> 2.14.0"
|
25
26
|
spec.add_development_dependency "guard-rspec", "~> 3.0.3"
|
26
27
|
spec.add_development_dependency "rb-fsevent", "~> 0.9.3"
|
@@ -1,48 +1,50 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Mixpal::Event do
|
4
|
-
let(:name) {
|
5
|
-
let(:properties) { { title:
|
4
|
+
let(:name) { 'Event 1' }
|
5
|
+
let(:properties) { { title: 'Awesome Product' } }
|
6
6
|
subject { described_class.new(name, properties) }
|
7
7
|
|
8
|
-
describe
|
9
|
-
it
|
8
|
+
describe '#render' do
|
9
|
+
it 'delegates to Util for js_object composition' do
|
10
10
|
Mixpal::Util.should_receive(:hash_to_js_object_string).with(properties)
|
11
11
|
subject.render
|
12
12
|
end
|
13
13
|
|
14
|
-
it
|
14
|
+
it 'outputs a call to track' do
|
15
15
|
js_object = Mixpal::Util.hash_to_js_object_string(properties)
|
16
16
|
expect(subject.render).to eq "mixpanel.track(\"#{name}\", #{js_object});"
|
17
17
|
end
|
18
18
|
|
19
|
-
it
|
19
|
+
it 'outputs an html safe string' do
|
20
20
|
expect(subject.render).to be_html_safe
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
describe
|
25
|
-
it
|
24
|
+
describe '#to_store' do
|
25
|
+
it 'returns a hash with its data' do
|
26
26
|
expect(subject.to_store).to eq(
|
27
|
-
name
|
28
|
-
properties
|
27
|
+
'name' => name,
|
28
|
+
'properties' => properties
|
29
29
|
)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
describe
|
34
|
-
let(:result)
|
33
|
+
describe '.from_store' do
|
34
|
+
let(:result) do
|
35
|
+
described_class.from_store('name' => name, 'properties' => properties)
|
36
|
+
end
|
35
37
|
|
36
|
-
it
|
38
|
+
it 'instantiates a new instance' do
|
37
39
|
expect(result).to be_an_instance_of(described_class)
|
38
40
|
end
|
39
41
|
|
40
|
-
it
|
42
|
+
it 'sets its name from the data' do
|
41
43
|
expect(result.name).to eq name
|
42
44
|
end
|
43
45
|
|
44
|
-
it
|
46
|
+
it 'sets its properties from the data' do
|
45
47
|
expect(result.properties).to eq properties
|
46
48
|
end
|
47
49
|
end
|
48
|
-
end
|
50
|
+
end
|
@@ -1,278 +1,256 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Mixpal::Tracker do
|
4
4
|
subject { Mixpal::Tracker.new }
|
5
|
-
let(:identity) {
|
5
|
+
let(:identity) { 'nick' }
|
6
6
|
let(:subject_with_identity) { Mixpal::Tracker.new(identity: identity) }
|
7
7
|
|
8
|
-
describe
|
9
|
-
|
10
|
-
@original_adapter = described_class.storage
|
11
|
-
|
12
|
-
MyCustomStorageAdapter = Class.new(MockStorage)
|
13
|
-
@adapter_instance = MyCustomStorageAdapter.new
|
14
|
-
|
15
|
-
described_class.storage = @adapter_instance
|
16
|
-
end
|
17
|
-
|
18
|
-
after { described_class.storage = @original_adapter }
|
19
|
-
|
20
|
-
it "uses the specified adapter" do
|
21
|
-
@adapter_instance.should_receive :write
|
22
|
-
subject.store!
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "#initialize" do
|
27
|
-
it "creates an empty set of events" do
|
8
|
+
describe '#initialize' do
|
9
|
+
it 'creates an empty set of events' do
|
28
10
|
expect(subject.events).to eq []
|
29
11
|
end
|
30
12
|
|
31
|
-
it
|
13
|
+
it 'creates an empty set of user_updates' do
|
32
14
|
expect(subject.user_updates).to eq []
|
33
15
|
end
|
34
16
|
|
35
|
-
context
|
17
|
+
context 'with an :identity arg' do
|
36
18
|
subject { subject_with_identity }
|
37
19
|
|
38
|
-
it
|
39
|
-
expect(subject.identity).to eq
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context "when data exists in storage" do
|
44
|
-
let(:old_tracker) { Mixpal::Tracker.new(identity: identity) }
|
45
|
-
|
46
|
-
before do
|
47
|
-
old_tracker.track "Event 1"
|
48
|
-
old_tracker.register_user name: "Nick Giancola"
|
49
|
-
old_tracker.store!
|
50
|
-
end
|
51
|
-
|
52
|
-
it "restores the alias_user property" do
|
53
|
-
expect(subject.alias_user).to eq true
|
54
|
-
end
|
55
|
-
|
56
|
-
it "restores the events" do
|
57
|
-
expect(subject.events.size).to eq 1
|
58
|
-
end
|
59
|
-
|
60
|
-
it "delegates event restoration to the Event class" do
|
61
|
-
Mixpal::Event.should_receive(:from_store).
|
62
|
-
with(old_tracker.events.first.to_store)
|
63
|
-
|
64
|
-
subject
|
65
|
-
end
|
66
|
-
|
67
|
-
it "restores the events" do
|
68
|
-
expect(subject.events.size).to eq 1
|
69
|
-
end
|
70
|
-
|
71
|
-
context "when initialized with an identity" do
|
72
|
-
subject { Mixpal::Tracker.new(identity: "Franky") }
|
73
|
-
|
74
|
-
it "overrides anything from storage" do
|
75
|
-
expect(subject.identity).to eq "Franky"
|
76
|
-
end
|
20
|
+
it 'sets the identity' do
|
21
|
+
expect(subject.identity).to eq 'nick'
|
77
22
|
end
|
78
23
|
end
|
79
24
|
end
|
80
25
|
|
81
|
-
describe
|
82
|
-
it
|
83
|
-
subject.register_user(name:
|
26
|
+
describe '#register_user' do
|
27
|
+
it 'sets the alias_user flag so we render the alias call' do
|
28
|
+
subject.register_user(name: 'Nick')
|
84
29
|
expect(subject.alias_user).to be_true
|
85
30
|
end
|
86
31
|
|
87
|
-
it
|
88
|
-
properties = { name:
|
32
|
+
it 'delegates to #update_user for tracking user properties' do
|
33
|
+
properties = { name: 'Nick' }
|
89
34
|
subject.should_receive(:update_user).with(properties)
|
90
35
|
subject.register_user(properties)
|
91
36
|
end
|
92
37
|
end
|
93
38
|
|
94
|
-
describe
|
95
|
-
it
|
96
|
-
properties = { name:
|
39
|
+
describe '#update_user' do
|
40
|
+
it 'instantiates a new User object with properties' do
|
41
|
+
properties = { name: 'Nick' }
|
97
42
|
Mixpal::User.should_receive(:new).with(properties)
|
98
43
|
subject.update_user(properties)
|
99
44
|
end
|
100
45
|
|
101
|
-
it
|
46
|
+
it 'adds the User to user_updates for rendering later' do
|
102
47
|
expect do
|
103
|
-
subject.update_user(name:
|
48
|
+
subject.update_user(name: 'Nick')
|
104
49
|
end.to change(subject.user_updates, :size).by(1)
|
105
50
|
|
106
51
|
subject.user_updates.first.should be_an_instance_of(Mixpal::User)
|
107
52
|
end
|
108
53
|
end
|
109
54
|
|
110
|
-
describe
|
111
|
-
it
|
112
|
-
name =
|
113
|
-
properties = { color:
|
55
|
+
describe '#track' do
|
56
|
+
it 'instantiates a new Event object with properties' do
|
57
|
+
name = 'Clicked Button'
|
58
|
+
properties = { color: 'Green' }
|
114
59
|
|
115
60
|
Mixpal::Event.should_receive(:new).with(name, properties)
|
116
61
|
subject.track(name, properties)
|
117
62
|
end
|
118
63
|
|
119
|
-
it
|
64
|
+
it 'adds the Event to events for rendering later' do
|
120
65
|
expect do
|
121
|
-
subject.track(
|
66
|
+
subject.track('Clicked Button', color: 'Green')
|
122
67
|
end.to change(subject.events, :size).by(1)
|
123
68
|
|
124
69
|
subject.events.first.should be_an_instance_of(Mixpal::Event)
|
125
70
|
end
|
126
71
|
end
|
127
72
|
|
128
|
-
describe
|
129
|
-
it
|
130
|
-
expect(subject.render).to have_tag(
|
73
|
+
describe '#render' do
|
74
|
+
it 'outputs script tag' do
|
75
|
+
expect(subject.render).to have_tag('script')
|
131
76
|
end
|
132
77
|
|
133
|
-
it
|
78
|
+
it 'outputs an html safe string' do
|
134
79
|
expect(subject.render).to be_html_safe
|
135
80
|
end
|
136
81
|
|
137
|
-
context
|
82
|
+
context 'with an identity' do
|
138
83
|
subject { subject_with_identity }
|
139
84
|
|
140
|
-
it
|
85
|
+
it 'outputs call to identify' do
|
141
86
|
expect(subject.render).to include "mixpanel.identify(\"#{identity}\");"
|
142
87
|
end
|
143
88
|
|
144
|
-
context
|
145
|
-
before { subject.register_user(
|
89
|
+
context 'when user is being registered' do
|
90
|
+
before { subject.register_user(name: 'Nick Giancola') }
|
146
91
|
|
147
|
-
it
|
92
|
+
it 'outputs call to alias by identity' do
|
148
93
|
expect(subject.render).to include "mixpanel.alias(\"#{identity}\");"
|
149
94
|
end
|
150
95
|
end
|
151
96
|
end
|
152
97
|
|
153
|
-
context
|
154
|
-
it
|
155
|
-
expect(subject.render).not_to include
|
98
|
+
context 'with no registered user' do
|
99
|
+
it 'does not output call to alias' do
|
100
|
+
expect(subject.render).not_to include 'mixpanel.alias'
|
156
101
|
end
|
157
102
|
end
|
158
103
|
|
159
|
-
context
|
160
|
-
it
|
161
|
-
expect(subject.render).not_to include
|
104
|
+
context 'without an identity' do
|
105
|
+
it 'does not output call to identify' do
|
106
|
+
expect(subject.render).not_to include 'mixpanel.indentify'
|
162
107
|
end
|
163
108
|
end
|
164
109
|
|
165
|
-
context
|
110
|
+
context 'with tracked events' do
|
166
111
|
before do
|
167
|
-
subject.track(
|
168
|
-
subject.track(
|
112
|
+
subject.track('Event 1', color: 'Green')
|
113
|
+
subject.track('Event 2', title: 'Something Awesome')
|
169
114
|
end
|
170
115
|
|
171
|
-
it
|
116
|
+
it 'delegates render to the events' do
|
172
117
|
subject.events.each { |event| event.should_receive :render }
|
173
118
|
subject.render
|
174
119
|
end
|
175
120
|
|
176
|
-
it
|
121
|
+
it 'joins each rendered event' do
|
177
122
|
joined = subject.events[0].render + subject.events[1].render
|
178
123
|
expect(subject.render).to include joined
|
179
124
|
end
|
180
125
|
end
|
181
126
|
|
182
|
-
context
|
127
|
+
context 'with user properties' do
|
183
128
|
before do
|
184
|
-
subject.update_user(
|
185
|
-
subject.update_user(
|
129
|
+
subject.update_user(name: 'Hank')
|
130
|
+
subject.update_user(location: 'Los Angeles')
|
186
131
|
end
|
187
132
|
|
188
|
-
it
|
133
|
+
it 'delegates render to the users' do
|
189
134
|
subject.user_updates.each { |user| user.should_receive :render }
|
190
135
|
subject.render
|
191
136
|
end
|
192
137
|
|
193
|
-
it
|
138
|
+
it 'joins each rendered user' do
|
194
139
|
joined = subject.user_updates[0].render + subject.user_updates[1].render
|
195
140
|
expect(subject.render).to include joined
|
196
141
|
end
|
197
142
|
end
|
198
143
|
end
|
199
144
|
|
200
|
-
describe
|
201
|
-
let(:
|
202
|
-
|
203
|
-
after { MockRails.cache.delete(described_class::STORAGE_KEY) }
|
145
|
+
describe '#store!' do
|
146
|
+
let(:session) { {} }
|
204
147
|
|
205
148
|
def storage_should_include(hash_fragment)
|
206
|
-
|
207
|
-
described_class::STORAGE_KEY,
|
208
|
-
hash_including(hash_fragment)
|
209
|
-
)
|
210
|
-
end
|
211
|
-
|
212
|
-
it "writes to the storage adapter" do
|
213
|
-
storage.should_receive(:write)
|
214
|
-
subject.store!
|
149
|
+
expect(session[described_class::STORAGE_KEY]).to include hash_fragment
|
215
150
|
end
|
216
151
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
storage_should_include(alias_user: true)
|
222
|
-
subject.store!
|
223
|
-
end
|
152
|
+
it 'stores the alias_user property' do
|
153
|
+
subject.register_user({})
|
154
|
+
subject.store!(session)
|
155
|
+
storage_should_include('alias_user' => true)
|
224
156
|
end
|
225
157
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
it "stores the identity" do
|
230
|
-
storage_should_include(identity: identity)
|
231
|
-
subject.store!
|
232
|
-
end
|
158
|
+
it 'stores the identity' do
|
159
|
+
subject_with_identity.store!(session)
|
160
|
+
storage_should_include('identity' => identity)
|
233
161
|
end
|
234
162
|
|
235
|
-
context
|
163
|
+
context 'when events have been tracked' do
|
236
164
|
before do
|
237
|
-
subject.track(
|
238
|
-
subject.track(
|
165
|
+
subject.track('Event 1', color: 'Green')
|
166
|
+
subject.track('Event 2', title: 'Something Awesome')
|
239
167
|
end
|
240
168
|
|
241
|
-
it
|
169
|
+
it 'delegates composition to the events' do
|
242
170
|
subject.events.each { |event| event.should_receive :to_store }
|
243
|
-
subject.store!
|
171
|
+
subject.store!(session)
|
244
172
|
end
|
245
173
|
|
246
|
-
it
|
174
|
+
it 'stores the events composed hashes in an array' do
|
175
|
+
subject.store!(session)
|
247
176
|
storage_should_include(
|
248
|
-
events
|
177
|
+
'events' => [subject.events[0].to_store, subject.events[1].to_store]
|
249
178
|
)
|
250
|
-
|
251
|
-
subject.store!
|
252
179
|
end
|
253
180
|
end
|
254
181
|
|
255
|
-
context
|
182
|
+
context 'when user properties have been updated' do
|
256
183
|
before do
|
257
|
-
subject.update_user(
|
258
|
-
subject.update_user(
|
184
|
+
subject.update_user(name: 'Hank')
|
185
|
+
subject.update_user(location: 'Los Angeles')
|
259
186
|
end
|
260
187
|
|
261
|
-
it
|
188
|
+
it 'delegates composition to the users' do
|
262
189
|
subject.user_updates.each { |user| user.should_receive :to_store }
|
263
|
-
subject.store!
|
190
|
+
subject.store!(session)
|
264
191
|
end
|
265
192
|
|
266
|
-
it
|
193
|
+
it 'stores the users composed hashes in an array' do
|
194
|
+
subject.store!(session)
|
195
|
+
|
267
196
|
storage_should_include(
|
268
|
-
user_updates
|
197
|
+
'user_updates' => [
|
269
198
|
subject.user_updates[0].to_store,
|
270
199
|
subject.user_updates[1].to_store
|
271
200
|
]
|
272
201
|
)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe '#restore!' do
|
207
|
+
let(:old_tracker) { Mixpal::Tracker.new(identity: identity) }
|
208
|
+
let(:session) { {} }
|
209
|
+
|
210
|
+
before do
|
211
|
+
old_tracker.track 'Event 1'
|
212
|
+
old_tracker.register_user name: 'Nick Giancola'
|
213
|
+
old_tracker.store!(session)
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'restores the alias_user property' do
|
217
|
+
subject.restore!(session)
|
218
|
+
expect(subject.alias_user).to eq true
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'restores the events' do
|
222
|
+
subject.restore!(session)
|
223
|
+
expect(subject.events.size).to eq 1
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'delegates event restoration to the Event class' do
|
227
|
+
Mixpal::Event.should_receive(:from_store)
|
228
|
+
.with(old_tracker.events.first.to_store)
|
229
|
+
|
230
|
+
subject.restore!(session)
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'restores the events' do
|
234
|
+
subject.restore!(session)
|
235
|
+
expect(subject.events.size).to eq 1
|
236
|
+
end
|
237
|
+
|
238
|
+
context 'with a different identity (e.g. after login)' do
|
239
|
+
subject { Mixpal::Tracker.new(identity: "#{identity}-2") }
|
240
|
+
|
241
|
+
it 'does not override identity with value in storage' do
|
242
|
+
subject.restore!(session)
|
243
|
+
expect(subject.identity).to eq "#{identity}-2"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context 'with no identity (e.g. after logout)' do
|
248
|
+
subject { Mixpal::Tracker.new(identity: nil) }
|
273
249
|
|
274
|
-
|
250
|
+
it 'overrides identity with value in storage' do
|
251
|
+
subject.restore!(session)
|
252
|
+
expect(subject.identity).to eq identity
|
275
253
|
end
|
276
254
|
end
|
277
255
|
end
|
278
|
-
end
|
256
|
+
end
|