mixpal 0.0.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +24 -0
  3. data/.travis.yml +7 -0
  4. data/Dockerfile +6 -0
  5. data/README.md +51 -11
  6. data/Rakefile +10 -1
  7. data/docker-compose.yml +14 -0
  8. data/lib/mixpal.rb +27 -7
  9. data/lib/mixpal/event.rb +4 -4
  10. data/lib/mixpal/integration.rb +12 -8
  11. data/lib/mixpal/revenue.rb +32 -0
  12. data/lib/mixpal/tracker.rb +44 -27
  13. data/lib/mixpal/user.rb +9 -7
  14. data/lib/mixpal/util.rb +3 -3
  15. data/lib/mixpal/version.rb +1 -1
  16. data/mixpanel_assistant.gemspec +8 -7
  17. data/spec/lib/mixpal/event_spec.rb +20 -18
  18. data/spec/lib/mixpal/revenue_spec.rb +60 -0
  19. data/spec/lib/mixpal/tracker_spec.rb +181 -153
  20. data/spec/lib/mixpal/user_spec.rb +37 -31
  21. data/spec/lib/mixpal/util_spec.rb +15 -14
  22. data/spec/lib/mixpal_spec.rb +13 -2
  23. data/spec/spec_helper.rb +4 -7
  24. data/spec/support/custom_events_module.rb +5 -0
  25. data/spec/support/matchers/element_matchers.rb +2 -2
  26. data/test_app/.gitignore +18 -0
  27. data/test_app/Gemfile +11 -0
  28. data/test_app/README.rdoc +28 -0
  29. data/test_app/Rakefile +6 -0
  30. data/test_app/app/assets/images/.keep +0 -0
  31. data/test_app/app/assets/javascripts/application.js +16 -0
  32. data/test_app/app/assets/stylesheets/application.css +15 -0
  33. data/test_app/app/controllers/application_controller.rb +12 -0
  34. data/test_app/app/controllers/concerns/.keep +0 -0
  35. data/test_app/app/controllers/redirects_controller.rb +10 -0
  36. data/test_app/app/helpers/application_helper.rb +2 -0
  37. data/test_app/app/mailers/.keep +0 -0
  38. data/test_app/app/models/.keep +0 -0
  39. data/test_app/app/models/concerns/.keep +0 -0
  40. data/test_app/app/views/layouts/application.html.erb +16 -0
  41. data/test_app/app/views/redirects/new.html.erb +1 -0
  42. data/test_app/bin/bundle +3 -0
  43. data/test_app/bin/rails +8 -0
  44. data/test_app/bin/rake +8 -0
  45. data/test_app/bin/spring +18 -0
  46. data/test_app/config.ru +4 -0
  47. data/test_app/config/application.rb +30 -0
  48. data/test_app/config/boot.rb +4 -0
  49. data/test_app/config/environment.rb +5 -0
  50. data/test_app/config/environments/development.rb +37 -0
  51. data/test_app/config/environments/production.rb +82 -0
  52. data/test_app/config/environments/test.rb +39 -0
  53. data/test_app/config/initializers/assets.rb +8 -0
  54. data/test_app/config/initializers/backtrace_silencers.rb +7 -0
  55. data/test_app/config/initializers/cookies_serializer.rb +3 -0
  56. data/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  57. data/test_app/config/initializers/inflections.rb +16 -0
  58. data/test_app/config/initializers/mime_types.rb +4 -0
  59. data/test_app/config/initializers/session_store.rb +3 -0
  60. data/test_app/config/initializers/wrap_parameters.rb +14 -0
  61. data/test_app/config/locales/en.yml +23 -0
  62. data/test_app/config/routes.rb +4 -0
  63. data/test_app/config/secrets.yml +22 -0
  64. data/test_app/db/seeds.rb +7 -0
  65. data/test_app/lib/assets/.keep +0 -0
  66. data/test_app/lib/tasks/.keep +0 -0
  67. data/test_app/log/.keep +0 -0
  68. data/test_app/public/404.html +67 -0
  69. data/test_app/public/422.html +67 -0
  70. data/test_app/public/500.html +66 -0
  71. data/test_app/public/favicon.ico +0 -0
  72. data/test_app/public/robots.txt +5 -0
  73. data/test_app/vendor/assets/javascripts/.keep +0 -0
  74. data/test_app/vendor/assets/stylesheets/.keep +0 -0
  75. metadata +112 -44
  76. data/spec/support/mock_rails.rb +0 -6
  77. data/spec/support/mock_storage.rb +0 -19
@@ -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.4.0'
3
3
  end
@@ -6,7 +6,7 @@ require 'mixpal/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "mixpal"
8
8
  spec.version = Mixpal::VERSION
9
- spec.authors = ["patbenatar"]
9
+ spec.authors = ["patbenatar", "mikehmorrissey"]
10
10
  spec.email = ["nick@gophilosophie.com"]
11
11
  spec.description = "Use Mixpanel's JavaScript library from your backend with ease"
12
12
  spec.summary = "As the JavaScript library is Mixpanel's preferred method of usage, Mixpal aims to make it easier to work with from your Rails backend. Most notably it persists tracking data across redirects, perfect for handling events like user sign ups or form submissions."
@@ -18,14 +18,15 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "bundler", "~> 2"
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "pry"
24
- spec.add_development_dependency "rspec", "~> 2.14.0"
25
- spec.add_development_dependency "guard-rspec", "~> 3.0.3"
26
- spec.add_development_dependency "rb-fsevent", "~> 0.9.3"
27
- spec.add_development_dependency "awesome_print", "~> 1.1.0"
28
- spec.add_development_dependency "nokogiri", "~> 1.6.0"
24
+ spec.add_development_dependency "rubocop", "~> 0.91.0"
25
+ spec.add_development_dependency "rspec", "~> 3.9.0"
26
+ spec.add_development_dependency "guard-rspec", "~> 4.7.3"
27
+ spec.add_development_dependency "rb-fsevent", "~> 0.10.4"
28
+ spec.add_development_dependency "awesome_print", "~> 1.8.0"
29
+ spec.add_development_dependency "nokogiri", "~> 1"
29
30
 
30
31
  spec.add_development_dependency "actionpack", ">= 3.0"
31
32
 
@@ -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
10
- Mixpal::Util.should_receive(:hash_to_js_object_string).with(properties)
8
+ describe '#render' do
9
+ it 'delegates to Util for js_object composition' do
10
+ expect(Mixpal::Util).to 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
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mixpal::Revenue do
4
+ let(:properties) do
5
+ {
6
+ sku: 'SKU-1010'
7
+ }
8
+ end
9
+
10
+ let(:amount) { 50 }
11
+
12
+ let(:subject) { described_class.new(amount, properties) }
13
+
14
+ describe '#render' do
15
+ it 'delegates to Util for js_object composition' do
16
+ expect(Mixpal::Util).to receive(:hash_to_js_object_string).with(properties)
17
+ subject.render
18
+ end
19
+
20
+ it 'outputs a call to people.track_charge' do
21
+ js_object = Mixpal::Util.hash_to_js_object_string(properties)
22
+ expect(subject.render)
23
+ .to eq "mixpanel.people.track_charge(#{amount}, #{js_object});"
24
+ end
25
+
26
+ it 'outputs an html safe string' do
27
+ expect(subject.render).to be_html_safe
28
+ end
29
+ end
30
+
31
+ describe '#to_store' do
32
+ it 'returns a hash with its data' do
33
+ expect(subject.to_store).to eq(
34
+ 'amount' => amount,
35
+ 'properties' => properties
36
+ )
37
+ end
38
+ end
39
+
40
+ describe '#from_store' do
41
+ let(:result) do
42
+ described_class.from_store(
43
+ 'amount' => amount,
44
+ 'properties' => properties
45
+ )
46
+ end
47
+
48
+ it 'instantiates a new instance' do
49
+ expect(result).to be_an_instance_of(described_class)
50
+ end
51
+
52
+ it 'sets its amount from the data' do
53
+ expect(result.amount).to eq amount
54
+ end
55
+
56
+ it 'sets its properties from the data' do
57
+ expect(result.properties).to eq properties
58
+ end
59
+ end
60
+ end
@@ -1,278 +1,306 @@
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
8
+ context 'with configured helper module' do
9
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
10
+ Mixpal.configure do |config|
11
+ config.helper_module = CustomEventsModule
12
+ end
16
13
  end
17
14
 
18
- after { described_class.storage = @original_adapter }
19
-
20
- it "uses the specified adapter" do
21
- @adapter_instance.should_receive :write
22
- subject.store!
15
+ it 'exposes helper module methods on tracker instance' do
16
+ expect(subject.custom_event).to eq true
23
17
  end
24
18
  end
25
19
 
26
- describe "#initialize" do
27
- it "creates an empty set of events" do
20
+ describe '#initialize' do
21
+ it 'creates an empty set of events' do
28
22
  expect(subject.events).to eq []
29
23
  end
30
24
 
31
- it "creates an empty set of user_updates" do
25
+ it 'creates an empty set of user_updates' do
32
26
  expect(subject.user_updates).to eq []
33
27
  end
34
28
 
35
- context "with an :identity arg" do
36
- subject { subject_with_identity }
37
-
38
- it "sets the identity" do
39
- expect(subject.identity).to eq "nick"
40
- end
29
+ it 'creates an empty set of revenue_updates' do
30
+ expect(subject.revenue_updates).to eq []
41
31
  end
42
32
 
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") }
33
+ context 'with an :identity arg' do
34
+ subject { subject_with_identity }
73
35
 
74
- it "overrides anything from storage" do
75
- expect(subject.identity).to eq "Franky"
76
- end
36
+ it 'sets the identity' do
37
+ expect(subject.identity).to eq 'nick'
77
38
  end
78
39
  end
79
40
  end
80
41
 
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")
84
- expect(subject.alias_user).to be_true
42
+ describe '#register_user' do
43
+ it 'sets the alias_user flag so we render the alias call' do
44
+ subject.register_user(name: 'Nick')
45
+ expect(subject.alias_user).to be true
85
46
  end
86
47
 
87
- it "delegates to #update_user for tracking user properties" do
88
- properties = { name: "Nick" }
89
- subject.should_receive(:update_user).with(properties)
48
+ it 'delegates to #update_user for tracking user properties' do
49
+ properties = { name: 'Nick' }
50
+ expect(subject).to receive(:update_user).with(properties)
90
51
  subject.register_user(properties)
91
52
  end
92
53
  end
93
54
 
94
- describe "#update_user" do
95
- it "instantiates a new User object with properties" do
96
- properties = { name: "Nick" }
97
- Mixpal::User.should_receive(:new).with(properties)
55
+ describe '#update_user' do
56
+ it 'instantiates a new User object with properties' do
57
+ properties = { name: 'Nick' }
58
+ expect(Mixpal::User).to receive(:new).with(properties)
98
59
  subject.update_user(properties)
99
60
  end
100
61
 
101
- it "adds the User to user_updates for rendering later" do
62
+ it 'adds the User to user_updates for rendering later' do
102
63
  expect do
103
- subject.update_user(name: "Nick")
64
+ subject.update_user(name: 'Nick')
104
65
  end.to change(subject.user_updates, :size).by(1)
105
66
 
106
- subject.user_updates.first.should be_an_instance_of(Mixpal::User)
67
+ expect(subject.user_updates.first).to be_an_instance_of(Mixpal::User)
107
68
  end
108
69
  end
109
70
 
110
- describe "#track" do
111
- it "instantiates a new Event object with properties" do
112
- name = "Clicked Button"
113
- properties = { color: "Green" }
71
+ describe '#track' do
72
+ it 'instantiates a new Event object with properties' do
73
+ name = 'Clicked Button'
74
+ properties = { color: 'Green' }
114
75
 
115
- Mixpal::Event.should_receive(:new).with(name, properties)
76
+ expect(Mixpal::Event).to receive(:new).with(name, properties)
116
77
  subject.track(name, properties)
117
78
  end
118
79
 
119
- it "adds the Event to events for rendering later" do
80
+ it 'adds the Event to events for rendering later' do
120
81
  expect do
121
- subject.track("Clicked Button", color: "Green")
82
+ subject.track('Clicked Button', color: 'Green')
122
83
  end.to change(subject.events, :size).by(1)
123
84
 
124
- subject.events.first.should be_an_instance_of(Mixpal::Event)
85
+ expect(subject.events.first).to be_an_instance_of(Mixpal::Event)
86
+ end
87
+ end
88
+
89
+ describe '#track_charge' do
90
+ it 'instantiates a new Revenue object with properties' do
91
+ properties = { sku: 'SKU-1010' }
92
+ expect(Mixpal::Revenue).to receive(:new).with(50, properties)
93
+ subject.track_charge(50, properties)
94
+ end
95
+
96
+ it 'adds the Revenue to revenue_updates for rendering later' do
97
+ expect do
98
+ subject.track_charge(50, sku: 'SKU-1010')
99
+ end.to change(subject.revenue_updates, :size).by(1)
100
+
101
+ expect(subject.revenue_updates.first).to be_an_instance_of(Mixpal::Revenue)
125
102
  end
126
103
  end
127
104
 
128
- describe "#render" do
129
- it "outputs script tag" do
130
- expect(subject.render).to have_tag("script")
105
+ describe '#render' do
106
+ it 'outputs script tag' do
107
+ expect(subject.render).to have_tag('script')
131
108
  end
132
109
 
133
- it "outputs an html safe string" do
110
+ it 'outputs an html safe string' do
134
111
  expect(subject.render).to be_html_safe
135
112
  end
136
113
 
137
- context "with an identity" do
114
+ context 'with an identity' do
138
115
  subject { subject_with_identity }
139
116
 
140
- it "outputs call to identify" do
117
+ it 'outputs call to identify' do
141
118
  expect(subject.render).to include "mixpanel.identify(\"#{identity}\");"
142
119
  end
143
120
 
144
- context "when user is being registered" do
145
- before { subject.register_user({ name: "Nick Giancola" }) }
121
+ context 'when user is being registered' do
122
+ before { subject.register_user(name: 'Nick Giancola') }
146
123
 
147
- it "outputs call to alias by identity" do
124
+ it 'outputs call to alias by identity' do
148
125
  expect(subject.render).to include "mixpanel.alias(\"#{identity}\");"
149
126
  end
150
127
  end
151
128
  end
152
129
 
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"
130
+ context 'with no registered user' do
131
+ it 'does not output call to alias' do
132
+ expect(subject.render).not_to include 'mixpanel.alias'
156
133
  end
157
134
  end
158
135
 
159
- context "without an identity" do
160
- it "does not output call to identify" do
161
- expect(subject.render).not_to include "mixpanel.indentify"
136
+ context 'without an identity' do
137
+ it 'does not output call to identify' do
138
+ expect(subject.render).not_to include 'mixpanel.indentify'
162
139
  end
163
140
  end
164
141
 
165
- context "with tracked events" do
142
+ context 'with tracked events' do
166
143
  before do
167
- subject.track("Event 1", { color: "Green" })
168
- subject.track("Event 2", { title: "Something Awesome" })
144
+ subject.track('Event 1', color: 'Green')
145
+ subject.track('Event 2', title: 'Something Awesome')
169
146
  end
170
147
 
171
- it "delegates render to the events" do
172
- subject.events.each { |event| event.should_receive :render }
148
+ it 'delegates render to the events' do
149
+ subject.events.each { |event| expect(event).to receive :render }
173
150
  subject.render
174
151
  end
175
152
 
176
- it "joins each rendered event" do
153
+ it 'joins each rendered event' do
177
154
  joined = subject.events[0].render + subject.events[1].render
178
155
  expect(subject.render).to include joined
179
156
  end
180
157
  end
181
158
 
182
- context "with user properties" do
159
+ context 'with user properties' do
183
160
  before do
184
- subject.update_user({ name: "Hank" })
185
- subject.update_user({ location: "Los Angeles" })
161
+ subject.update_user(name: 'Hank')
162
+ subject.update_user(location: 'Los Angeles')
186
163
  end
187
164
 
188
- it "delegates render to the users" do
189
- subject.user_updates.each { |user| user.should_receive :render }
165
+ it 'delegates render to the users' do
166
+ subject.user_updates.each { |user| expect(user).to receive :render }
190
167
  subject.render
191
168
  end
192
169
 
193
- it "joins each rendered user" do
170
+ it 'joins each rendered user' do
194
171
  joined = subject.user_updates[0].render + subject.user_updates[1].render
195
172
  expect(subject.render).to include joined
196
173
  end
197
174
  end
198
- end
199
-
200
- describe "#store!" do
201
- let(:storage) { described_class::storage }
202
175
 
203
- after { MockRails.cache.delete(described_class::STORAGE_KEY) }
176
+ context 'with revenue' do
177
+ before do
178
+ subject.track_charge(50)
179
+ subject.track_charge(100, sku: 'SKU-1010')
180
+ end
204
181
 
205
- 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
182
+ it 'delegates render to the revenues' do
183
+ subject.revenue_updates.each { |r| expect(r).to receive :render }
184
+ subject.render
185
+ end
211
186
 
212
- it "writes to the storage adapter" do
213
- storage.should_receive(:write)
214
- subject.store!
187
+ it 'joins each rendered revenue' do
188
+ joined =
189
+ subject.revenue_updates[0].render + subject.revenue_updates[1].render
190
+ expect(subject.render).to include joined
191
+ end
215
192
  end
193
+ end
216
194
 
217
- context "when alias_user is set" do
218
- before { subject.register_user({}) }
195
+ describe '#store!' do
196
+ let(:session) { {} }
219
197
 
220
- it "stores the alias_user property" do
221
- storage_should_include(alias_user: true)
222
- subject.store!
223
- end
198
+ def expect_storage_to_include(hash_fragment)
199
+ expect(session[described_class::STORAGE_KEY]).to include hash_fragment
224
200
  end
225
201
 
226
- context "when identity is set" do
227
- subject { subject_with_identity }
202
+ it 'stores the alias_user property' do
203
+ subject.register_user({})
204
+ subject.store!(session)
205
+ expect_storage_to_include('alias_user' => true)
206
+ end
228
207
 
229
- it "stores the identity" do
230
- storage_should_include(identity: identity)
231
- subject.store!
232
- end
208
+ it 'stores the identity' do
209
+ subject_with_identity.store!(session)
210
+ expect_storage_to_include('identity' => identity)
233
211
  end
234
212
 
235
- context "when events have been tracked" do
213
+ context 'when events have been tracked' do
236
214
  before do
237
- subject.track("Event 1", { color: "Green" })
238
- subject.track("Event 2", { title: "Something Awesome" })
215
+ subject.track('Event 1', color: 'Green')
216
+ subject.track('Event 2', title: 'Something Awesome')
239
217
  end
240
218
 
241
- it "delegates composition to the events" do
242
- subject.events.each { |event| event.should_receive :to_store }
243
- subject.store!
219
+ it 'delegates composition to the events' do
220
+ subject.events.each { |event| expect(event).to receive :to_store }
221
+ subject.store!(session)
244
222
  end
245
223
 
246
- it "stores the events' composed hashes in an array" do
247
- storage_should_include(
248
- events: [subject.events[0].to_store, subject.events[1].to_store]
224
+ it 'stores the events composed hashes in an array' do
225
+ subject.store!(session)
226
+ expect_storage_to_include(
227
+ 'events' => [subject.events[0].to_store, subject.events[1].to_store]
249
228
  )
250
-
251
- subject.store!
252
229
  end
253
230
  end
254
231
 
255
- context "when user properties have been updated" do
232
+ context 'when user properties have been updated' do
256
233
  before do
257
- subject.update_user({ name: "Hank" })
258
- subject.update_user({ location: "Los Angeles" })
234
+ subject.update_user(name: 'Hank')
235
+ subject.update_user(location: 'Los Angeles')
259
236
  end
260
237
 
261
- it "delegates composition to the users" do
262
- subject.user_updates.each { |user| user.should_receive :to_store }
263
- subject.store!
238
+ it 'delegates composition to the users' do
239
+ subject.user_updates.each { |user| expect(user).to receive :to_store }
240
+ subject.store!(session)
264
241
  end
265
242
 
266
- it "stores the users' composed hashes in an array" do
267
- storage_should_include(
268
- user_updates: [
243
+ it 'stores the users composed hashes in an array' do
244
+ subject.store!(session)
245
+
246
+ expect_storage_to_include(
247
+ 'user_updates' => [
269
248
  subject.user_updates[0].to_store,
270
249
  subject.user_updates[1].to_store
271
250
  ]
272
251
  )
252
+ end
253
+ end
254
+ end
255
+
256
+ describe '#restore!' do
257
+ let(:old_tracker) { Mixpal::Tracker.new(identity: identity) }
258
+ let(:session) { {} }
259
+
260
+ before do
261
+ old_tracker.track 'Event 1'
262
+ old_tracker.register_user name: 'Nick Giancola'
263
+ old_tracker.store!(session)
264
+ end
265
+
266
+ it 'restores the alias_user property' do
267
+ subject.restore!(session)
268
+ expect(subject.alias_user).to eq true
269
+ end
270
+
271
+ it 'restores the events' do
272
+ subject.restore!(session)
273
+ expect(subject.events.size).to eq 1
274
+ end
275
+
276
+ it 'delegates event restoration to the Event class' do
277
+ expect(Mixpal::Event).to receive(:from_store)
278
+ .with(old_tracker.events.first.to_store)
279
+
280
+ subject.restore!(session)
281
+ end
282
+
283
+ it 'restores the events' do
284
+ subject.restore!(session)
285
+ expect(subject.events.size).to eq 1
286
+ end
287
+
288
+ context 'with a different identity (e.g. after login)' do
289
+ subject { Mixpal::Tracker.new(identity: "#{identity}-2") }
290
+
291
+ it 'does not override identity with value in storage' do
292
+ subject.restore!(session)
293
+ expect(subject.identity).to eq "#{identity}-2"
294
+ end
295
+ end
296
+
297
+ context 'with no identity (e.g. after logout)' do
298
+ subject { Mixpal::Tracker.new(identity: nil) }
273
299
 
274
- subject.store!
300
+ it 'overrides identity with value in storage' do
301
+ subject.restore!(session)
302
+ expect(subject.identity).to eq identity
275
303
  end
276
304
  end
277
305
  end
278
- end
306
+ end