mixpal 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +23 -0
  3. data/.travis.yml +5 -0
  4. data/README.md +7 -11
  5. data/Rakefile +8 -1
  6. data/lib/mixpal.rb +7 -7
  7. data/lib/mixpal/event.rb +4 -4
  8. data/lib/mixpal/integration.rb +7 -7
  9. data/lib/mixpal/tracker.rb +29 -26
  10. data/lib/mixpal/user.rb +9 -7
  11. data/lib/mixpal/util.rb +3 -3
  12. data/lib/mixpal/version.rb +1 -1
  13. data/mixpanel_assistant.gemspec +1 -0
  14. data/spec/lib/mixpal/event_spec.rb +19 -17
  15. data/spec/lib/mixpal/tracker_spec.rb +127 -149
  16. data/spec/lib/mixpal/user_spec.rb +36 -30
  17. data/spec/lib/mixpal/util_spec.rb +15 -14
  18. data/spec/lib/mixpal_spec.rb +2 -2
  19. data/spec/spec_helper.rb +4 -6
  20. data/spec/support/matchers/element_matchers.rb +2 -2
  21. data/test_app/.gitignore +16 -0
  22. data/test_app/Gemfile +11 -0
  23. data/test_app/README.rdoc +28 -0
  24. data/test_app/Rakefile +6 -0
  25. data/test_app/app/assets/images/.keep +0 -0
  26. data/test_app/app/assets/javascripts/application.js +16 -0
  27. data/test_app/app/assets/stylesheets/application.css +15 -0
  28. data/test_app/app/controllers/application_controller.rb +12 -0
  29. data/test_app/app/controllers/concerns/.keep +0 -0
  30. data/test_app/app/controllers/redirects_controller.rb +10 -0
  31. data/test_app/app/helpers/application_helper.rb +2 -0
  32. data/test_app/app/mailers/.keep +0 -0
  33. data/test_app/app/models/.keep +0 -0
  34. data/test_app/app/models/concerns/.keep +0 -0
  35. data/test_app/app/views/layouts/application.html.erb +16 -0
  36. data/test_app/app/views/redirects/new.html.erb +1 -0
  37. data/test_app/bin/bundle +3 -0
  38. data/test_app/bin/rails +8 -0
  39. data/test_app/bin/rake +8 -0
  40. data/test_app/bin/spring +18 -0
  41. data/test_app/config.ru +4 -0
  42. data/test_app/config/application.rb +30 -0
  43. data/test_app/config/boot.rb +4 -0
  44. data/test_app/config/environment.rb +5 -0
  45. data/test_app/config/environments/development.rb +37 -0
  46. data/test_app/config/environments/production.rb +82 -0
  47. data/test_app/config/environments/test.rb +39 -0
  48. data/test_app/config/initializers/assets.rb +8 -0
  49. data/test_app/config/initializers/backtrace_silencers.rb +7 -0
  50. data/test_app/config/initializers/cookies_serializer.rb +3 -0
  51. data/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  52. data/test_app/config/initializers/inflections.rb +16 -0
  53. data/test_app/config/initializers/mime_types.rb +4 -0
  54. data/test_app/config/initializers/session_store.rb +3 -0
  55. data/test_app/config/initializers/wrap_parameters.rb +14 -0
  56. data/test_app/config/locales/en.yml +23 -0
  57. data/test_app/config/routes.rb +4 -0
  58. data/test_app/config/secrets.yml +22 -0
  59. data/test_app/db/seeds.rb +7 -0
  60. data/test_app/lib/assets/.keep +0 -0
  61. data/test_app/lib/tasks/.keep +0 -0
  62. data/test_app/log/.keep +0 -0
  63. data/test_app/public/404.html +67 -0
  64. data/test_app/public/422.html +67 -0
  65. data/test_app/public/500.html +66 -0
  66. data/test_app/public/favicon.ico +0 -0
  67. data/test_app/public/robots.txt +5 -0
  68. data/test_app/vendor/assets/javascripts/.keep +0 -0
  69. data/test_app/vendor/assets/stylesheets/.keep +0 -0
  70. metadata +67 -6
  71. data/spec/support/mock_rails.rb +0 -6
  72. data/spec/support/mock_storage.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f7b06bf4a86525182d6d44a19848532e62cc51a
4
- data.tar.gz: eb38535ae4ef5a101b61a8d30c0a4ed041c04b9d
3
+ metadata.gz: c04724d3a3e803dfd392c4635ee4480a41e959d4
4
+ data.tar.gz: 0ddb3d1b74618d3523cf32ed59ddbbbf39df457a
5
5
  SHA512:
6
- metadata.gz: 8d4079b4038dad025ca08dfaefcb1239abeacbedccb1c0c7553e1cdaa8cea64849cd03717d83cf2315ede897b85634a55d6950e6de299dbd61f3354f9d97a08d
7
- data.tar.gz: 883548ba1bb19454caa6052e2d812efe80f09680b22b1647034865f0c885042b462d1412a37ac152ca1c2786ee89e61ea827a721f6fce11331aab1346907380f
6
+ metadata.gz: 33668e11499b7a84380c2421d710213af257c858ed45cdc017c4e1aec0855277c73f65f818a323dc35ef520cc3484f8cbd320ff9dff3d9a0940dfebfbe25633f
7
+ data.tar.gz: 2ac2f904b86d599230c60b0d9a7f31327f03c3920ed60b2ce221ac1414992a556ed021951001852060bb3d88eabd38e0122697fa8d2a64a6f3d475568b47215f
@@ -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/**/*"
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.0.0
5
+ - 1.9.3
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 `Rails.cache` when
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
- #### Customizing the storage adapter
107
+ #### A note about `CookieStore` size limit
108
108
 
109
- You can specify a custom persistence storage adapter like so:
110
-
111
- ```ruby
112
- Mixpal::Tracker.storage = MyCustomAdapter.new
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
@@ -1 +1,8 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ RuboCop::RakeTask.new
7
+
8
+ task default: [:spec, :rubocop]
@@ -1,10 +1,10 @@
1
- require "mixpal/version"
2
- require "active_support/core_ext"
1
+ require 'mixpal/version'
2
+ require 'active_support/core_ext'
3
3
 
4
4
  module Mixpal
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"
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
@@ -14,13 +14,13 @@ module Mixpal
14
14
 
15
15
  def to_store
16
16
  {
17
- name: name,
18
- properties: properties,
17
+ 'name' => name,
18
+ 'properties' => properties
19
19
  }
20
20
  end
21
21
 
22
22
  def self.from_store(data)
23
- new(data[:name], data[:properties])
23
+ new(data['name'], data['properties'])
24
24
  end
25
25
  end
26
- end
26
+ end
@@ -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
- send(data[:object_method]).try(data[:attribute_method])
22
- end
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
@@ -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 = "mixpal"
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
- "".tap do |html|
33
- html << "<script type=\"text/javascript\">"
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 << "</script>"
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
- self.class.storage.write(STORAGE_KEY, to_store)
38
+ def store!(session)
39
+ session[STORAGE_KEY] = to_store
44
40
  end
45
41
 
46
- private
42
+ def restore!(session)
43
+ data = session[STORAGE_KEY] || {}
44
+
45
+ @alias_user = data['alias_user']
46
+ @identity ||= data['identity']
47
47
 
48
- def restore!
49
- data = self.class.storage.read(STORAGE_KEY) || {}
48
+ if data['events']
49
+ @events = data['events'].map { |e| Mixpal::Event.from_store(e) }
50
+ end
50
51
 
51
- @alias_user = data[:alias_user]
52
- @identity = data[:identity]
53
- @events = data[:events].map { |e| Mixpal::Event.from_store(e) } if data[:events]
54
- @user_updates = data[:user_updates].map { |u| Mixpal::User.from_store(u) } if data[:user_updates]
52
+ if data['user_updates']
53
+ @user_updates = data['user_updates']
54
+ .map { |u| Mixpal::User.from_store(u) }
55
+ end
55
56
 
56
- self.class.storage.delete(STORAGE_KEY)
57
+ session.delete(STORAGE_KEY)
57
58
  end
58
59
 
60
+ private
61
+
59
62
  def to_store
60
63
  {
61
- alias_user: @alias_user,
62
- identity: identity,
63
- events: events.map(&:to_store),
64
- user_updates: user_updates.map(&:to_store),
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
@@ -12,12 +12,12 @@ module Mixpal
12
12
 
13
13
  def to_store
14
14
  {
15
- properties: properties,
15
+ 'properties' => properties
16
16
  }
17
17
  end
18
18
 
19
19
  def self.from_store(data)
20
- new(data[:properties])
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[properties.map {|k, v| [mixpanel_special_properties_map[k] || k, v] }]
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: "$name",
38
- email: "$email",
39
- created_at: "$created",
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
@@ -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! { |k,v| v.nil? }
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(",").html_safe
10
+ end.join(',').html_safe
11
11
 
12
12
  "{#{contents}}"
13
13
  end
@@ -1,3 +1,3 @@
1
1
  module Mixpal
2
- VERSION = "0.0.5"
2
+ VERSION = '0.1.0'
3
3
  end
@@ -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 "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
3
  describe Mixpal::Event do
4
- let(:name) { "Event 1" }
5
- let(:properties) { { title: "Awesome Product" } }
4
+ let(:name) { 'Event 1' }
5
+ let(:properties) { { title: 'Awesome Product' } }
6
6
  subject { described_class.new(name, properties) }
7
7
 
8
- describe "#render" do
9
- it "delegates to Util for js_object composition" do
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 "outputs a call to track" do
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 "outputs an html safe string" do
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 "#to_store" do
25
- it "returns a hash with its data" do
24
+ describe '#to_store' do
25
+ it 'returns a hash with its data' do
26
26
  expect(subject.to_store).to eq(
27
- name: name,
28
- properties: properties,
27
+ 'name' => name,
28
+ 'properties' => properties
29
29
  )
30
30
  end
31
31
  end
32
32
 
33
- describe ".from_store" do
34
- let(:result) { described_class.from_store(name: name, properties: properties) }
33
+ describe '.from_store' do
34
+ let(:result) do
35
+ described_class.from_store('name' => name, 'properties' => properties)
36
+ end
35
37
 
36
- it "instantiates a new instance" do
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 "sets its name from the data" do
42
+ it 'sets its name from the data' do
41
43
  expect(result.name).to eq name
42
44
  end
43
45
 
44
- it "sets its properties from the data" do
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 "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
3
  describe Mixpal::Tracker do
4
4
  subject { Mixpal::Tracker.new }
5
- let(:identity) { "nick" }
5
+ let(:identity) { 'nick' }
6
6
  let(:subject_with_identity) { Mixpal::Tracker.new(identity: identity) }
7
7
 
8
- describe "custom storage adapter" do
9
- before do
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 "creates an empty set of user_updates" do
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 "with an :identity arg" do
17
+ context 'with an :identity arg' do
36
18
  subject { subject_with_identity }
37
19
 
38
- it "sets the identity" do
39
- expect(subject.identity).to eq "nick"
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 "#register_user" do
82
- it "sets the alias_user flag so we render the alias call" do
83
- subject.register_user(name: "Nick")
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 "delegates to #update_user for tracking user properties" do
88
- properties = { name: "Nick" }
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 "#update_user" do
95
- it "instantiates a new User object with properties" do
96
- properties = { name: "Nick" }
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 "adds the User to user_updates for rendering later" do
46
+ it 'adds the User to user_updates for rendering later' do
102
47
  expect do
103
- subject.update_user(name: "Nick")
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 "#track" do
111
- it "instantiates a new Event object with properties" do
112
- name = "Clicked Button"
113
- properties = { color: "Green" }
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 "adds the Event to events for rendering later" do
64
+ it 'adds the Event to events for rendering later' do
120
65
  expect do
121
- subject.track("Clicked Button", color: "Green")
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 "#render" do
129
- it "outputs script tag" do
130
- expect(subject.render).to have_tag("script")
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 "outputs an html safe string" do
78
+ it 'outputs an html safe string' do
134
79
  expect(subject.render).to be_html_safe
135
80
  end
136
81
 
137
- context "with an identity" do
82
+ context 'with an identity' do
138
83
  subject { subject_with_identity }
139
84
 
140
- it "outputs call to identify" do
85
+ it 'outputs call to identify' do
141
86
  expect(subject.render).to include "mixpanel.identify(\"#{identity}\");"
142
87
  end
143
88
 
144
- context "when user is being registered" do
145
- before { subject.register_user({ name: "Nick Giancola" }) }
89
+ context 'when user is being registered' do
90
+ before { subject.register_user(name: 'Nick Giancola') }
146
91
 
147
- it "outputs call to alias by identity" do
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 "with no registered user" do
154
- it "does not output call to alias" do
155
- expect(subject.render).not_to include "mixpanel.alias"
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 "without an identity" do
160
- it "does not output call to identify" do
161
- expect(subject.render).not_to include "mixpanel.indentify"
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 "with tracked events" do
110
+ context 'with tracked events' do
166
111
  before do
167
- subject.track("Event 1", { color: "Green" })
168
- subject.track("Event 2", { title: "Something Awesome" })
112
+ subject.track('Event 1', color: 'Green')
113
+ subject.track('Event 2', title: 'Something Awesome')
169
114
  end
170
115
 
171
- it "delegates render to the events" do
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 "joins each rendered event" do
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 "with user properties" do
127
+ context 'with user properties' do
183
128
  before do
184
- subject.update_user({ name: "Hank" })
185
- subject.update_user({ location: "Los Angeles" })
129
+ subject.update_user(name: 'Hank')
130
+ subject.update_user(location: 'Los Angeles')
186
131
  end
187
132
 
188
- it "delegates render to the users" do
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 "joins each rendered user" do
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 "#store!" do
201
- let(:storage) { described_class::storage }
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
- storage.should_receive(:write).with(
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
- context "when alias_user is set" do
218
- before { subject.register_user({}) }
219
-
220
- it "stores the alias_user property" do
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
- context "when identity is set" do
227
- subject { subject_with_identity }
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 "when events have been tracked" do
163
+ context 'when events have been tracked' do
236
164
  before do
237
- subject.track("Event 1", { color: "Green" })
238
- subject.track("Event 2", { title: "Something Awesome" })
165
+ subject.track('Event 1', color: 'Green')
166
+ subject.track('Event 2', title: 'Something Awesome')
239
167
  end
240
168
 
241
- it "delegates composition to the events" do
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 "stores the events' composed hashes in an array" do
174
+ it 'stores the events composed hashes in an array' do
175
+ subject.store!(session)
247
176
  storage_should_include(
248
- events: [subject.events[0].to_store, subject.events[1].to_store]
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 "when user properties have been updated" do
182
+ context 'when user properties have been updated' do
256
183
  before do
257
- subject.update_user({ name: "Hank" })
258
- subject.update_user({ location: "Los Angeles" })
184
+ subject.update_user(name: 'Hank')
185
+ subject.update_user(location: 'Los Angeles')
259
186
  end
260
187
 
261
- it "delegates composition to the users" do
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 "stores the users' composed hashes in an array" do
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
- subject.store!
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