meta_events 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +591 -0
- data/Rakefile +6 -0
- data/lib/meta_events/controller_methods.rb +43 -0
- data/lib/meta_events/definition/category.rb +78 -0
- data/lib/meta_events/definition/definition_set.rb +122 -0
- data/lib/meta_events/definition/event.rb +163 -0
- data/lib/meta_events/definition/version.rb +89 -0
- data/lib/meta_events/engine.rb +6 -0
- data/lib/meta_events/helpers.rb +114 -0
- data/lib/meta_events/railtie.rb +29 -0
- data/lib/meta_events/test_receiver.rb +37 -0
- data/lib/meta_events/tracker.rb +493 -0
- data/lib/meta_events/version.rb +3 -0
- data/lib/meta_events.rb +29 -0
- data/meta_events.gemspec +31 -0
- data/spec/meta_events/controller_methods_and_helpers_spec.rb +253 -0
- data/spec/meta_events/definition/category_spec.rb +102 -0
- data/spec/meta_events/definition/definition_set_spec.rb +142 -0
- data/spec/meta_events/definition/event_spec.rb +146 -0
- data/spec/meta_events/definition/version_spec.rb +93 -0
- data/spec/meta_events/tracker_spec.rb +466 -0
- data/vendor/assets/javascripts/meta_events.js.erb +154 -0
- metadata +154 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
describe ::MetaEvents::Definition::Version do
|
2
|
+
let(:definition_set) do
|
3
|
+
out = double("definition_set")
|
4
|
+
allow(out).to receive(:kind_of?).with(::MetaEvents::Definition::DefinitionSet).and_return(true)
|
5
|
+
allow(out).to receive(:global_events_prefix).with().and_return("gep")
|
6
|
+
out
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:klass) { ::MetaEvents::Definition::Version }
|
10
|
+
let(:instance) { klass.new(definition_set, 3, "2014-02-03") }
|
11
|
+
|
12
|
+
it "should require valid parameters for construction" do
|
13
|
+
expect { klass.new(double("not-a-definition-set"), 1, "2014-01-01") }.to raise_error(ArgumentError)
|
14
|
+
expect { klass.new(definition_set, "foo", "2014-01-01") }.to raise_error(ArgumentError)
|
15
|
+
|
16
|
+
# Ruby 1.8.x's Time.parse method will accept "foo" for Time.parse and just return Time.now --
|
17
|
+
# which is deeply unfortunate, but there's really no good way around it.
|
18
|
+
unless RUBY_VERSION =~ /^1\.8\./
|
19
|
+
expect { klass.new(definition_set, 1, "foo") }.to raise_error(ArgumentError)
|
20
|
+
end
|
21
|
+
|
22
|
+
expect { klass.new(definition_set, 1, nil) }.to raise_error
|
23
|
+
expect { klass.new(definition_set, 1, "2014-01-01", :foo => :bar) }.to raise_error(ArgumentError, /foo/i)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return the definition set, number, and introduction time" do
|
27
|
+
expect(instance.definition_set).to be(definition_set)
|
28
|
+
expect(instance.number).to eq(3)
|
29
|
+
expect(instance.introduced).to eq(Time.parse("2014-02-03"))
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should evaluate its block in its own context" do
|
33
|
+
expect_any_instance_of(klass).to receive(:foobar).once.with(:bonk)
|
34
|
+
klass.new(definition_set, 3, "2014-02-03") { foobar(:bonk) }
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return the prefix correctly" do
|
38
|
+
instance.prefix.should == "gep3_"
|
39
|
+
end
|
40
|
+
|
41
|
+
context "with one category" do
|
42
|
+
let(:category) do
|
43
|
+
out = double("category")
|
44
|
+
allow(out).to receive(:name).with().and_return(:quux)
|
45
|
+
out
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should be able to create a new category, and retrieve it" do
|
49
|
+
blk = lambda { :whatever }
|
50
|
+
expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, ' FooBar ', :bar => :baz, &blk).and_return(category)
|
51
|
+
instance.category(' FooBar ', :bar => :baz, &blk)
|
52
|
+
|
53
|
+
expect(instance.category_named(:quux)).to be(category)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should not allow creating duplicate categories" do
|
57
|
+
expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, :quux, { }).and_return(category)
|
58
|
+
instance.category(:quux)
|
59
|
+
|
60
|
+
category_2 = double("category-2")
|
61
|
+
allow(category_2).to receive(:name).with().and_return(:quux)
|
62
|
+
expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, :baz, { }).and_return(category_2)
|
63
|
+
expect { instance.category(:baz) }.to raise_error(ArgumentError, /baz/i)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should allow retrieving the category, and normalize the name" do
|
67
|
+
expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, :quux, { }).and_return(category)
|
68
|
+
instance.category(:quux)
|
69
|
+
|
70
|
+
instance.category_named(' QuuX ').should be(category)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should delegate to the category on #fetch_event" do
|
74
|
+
expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, :quux, { }).and_return(category)
|
75
|
+
instance.category(:quux)
|
76
|
+
|
77
|
+
expect(category).to receive(:event_named).once.with(:foobar).and_return(:bonk)
|
78
|
+
instance.fetch_event(:quux, :foobar).should == :bonk
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should return the #retired_at properly" do
|
83
|
+
expect(instance.retired_at).to be_nil
|
84
|
+
|
85
|
+
new_instance = klass.new(definition_set, 4, "2014-01-01", :retired_at => "2015-01-01")
|
86
|
+
expect(new_instance.retired_at).to eq(Time.parse("2015-01-01"))
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should turn itself into a string reasonably" do
|
90
|
+
expect(instance.to_s).to match(/Version/)
|
91
|
+
expect(instance.to_s).to match(/3/)
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,466 @@
|
|
1
|
+
require 'active_support/core_ext/numeric/time'
|
2
|
+
require 'ipaddr'
|
3
|
+
|
4
|
+
describe MetaEvents::Tracker do
|
5
|
+
subject(:klass) { MetaEvents::Tracker }
|
6
|
+
let(:definition_set) do
|
7
|
+
MetaEvents::Definition::DefinitionSet.new(:global_events_prefix => "xy") do
|
8
|
+
version 1, '2014-01-31' do
|
9
|
+
category :foo do
|
10
|
+
event :bar, '2014-01-31', 'this is bar'
|
11
|
+
event :baz, '2014-01-31', 'this is baz'
|
12
|
+
event :nolonger, '2014-01-31', 'should be retired', :retired_at => '2020-01-01'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
version 2, '2014-06-01' do
|
17
|
+
category :foo do
|
18
|
+
event :quux, '2014-01-31', 'this is quux'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:receiver_1) do
|
25
|
+
out = double("receiver_1")
|
26
|
+
@receiver_1_calls = [ ]
|
27
|
+
rc1c = @receiver_1_calls
|
28
|
+
allow(out).to receive(:track) { |*args| rc1c << args }
|
29
|
+
out
|
30
|
+
end
|
31
|
+
|
32
|
+
let(:receiver_2) do
|
33
|
+
out = double("receiver_2")
|
34
|
+
@receiver_2_calls = [ ]
|
35
|
+
rc2c = @receiver_2_calls
|
36
|
+
allow(out).to receive(:track) { |*args| rc2c << args }
|
37
|
+
out
|
38
|
+
end
|
39
|
+
|
40
|
+
def new_instance(*args)
|
41
|
+
out = klass.new(*args)
|
42
|
+
out.event_receivers << receiver_1
|
43
|
+
out
|
44
|
+
end
|
45
|
+
|
46
|
+
before :each do
|
47
|
+
::MetaEvents::Tracker.default_event_receivers = [ ]
|
48
|
+
@user = tep_object(:name => 'wilfred', :hometown => 'Fridley')
|
49
|
+
@distinct_id = rand(1_000_000_000)
|
50
|
+
@remote_ip = "128.12.34.56"
|
51
|
+
@instance = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1, :implicit_properties => { :user => @user } )
|
52
|
+
end
|
53
|
+
|
54
|
+
after :each do
|
55
|
+
expect(@receiver_1_calls).to eq([ ])
|
56
|
+
expect((@receiver_2_calls || [ ])).to eq([ ])
|
57
|
+
end
|
58
|
+
|
59
|
+
def expect_event(event_name, event_properties, options = { })
|
60
|
+
receiver_name = options[:receiver_name] || "receiver_1"
|
61
|
+
expected_distinct_id = if options.has_key?(:distinct_id) then options[:distinct_id] else @distinct_id end
|
62
|
+
|
63
|
+
calls = instance_variable_get("@#{receiver_name}_calls")
|
64
|
+
|
65
|
+
expect(calls.length).to be > 0
|
66
|
+
next_event = calls.shift
|
67
|
+
expect(next_event[0]).to eq(expected_distinct_id)
|
68
|
+
expect(next_event[1]).to eq(event_name)
|
69
|
+
expect(next_event[2]).to eq(event_properties)
|
70
|
+
end
|
71
|
+
|
72
|
+
def tep_object(props)
|
73
|
+
out = double("tep_object")
|
74
|
+
allow(out).to receive(:to_event_properties).with().and_return(props)
|
75
|
+
out
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#initialize" do
|
79
|
+
it "should validate its arguments" do
|
80
|
+
expect { new_instance(@distinct_id, nil, :version => 1, :definitions => definition_set, :foo => :bar) }.to raise_error(ArgumentError, /foo/i)
|
81
|
+
expect { new_instance(@distinct_id, "whatever", :version => 1, :definitions => definition_set) }.to raise_error(ArgumentError)
|
82
|
+
expect { new_instance(@distinct_id, /foobar/, :version => 1, :definitions => definition_set) }.to raise_error(ArgumentError, /foobar/i)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should allow a nil distinct_id" do
|
86
|
+
expect { new_instance(nil, nil, :definitions => definition_set, :version => 1) }.not_to raise_error
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should not allow an invalid distinct_id" do
|
90
|
+
expect { new_instance(/foobar/, nil, :definitions => definition_set, :version => 1) }.to raise_error(ArgumentError)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should be able to read definitions from a file" do
|
94
|
+
require 'tempfile'
|
95
|
+
|
96
|
+
f = Tempfile.new('tracker_events')
|
97
|
+
begin
|
98
|
+
f.puts <<-EOS
|
99
|
+
global_events_prefix :fb
|
100
|
+
|
101
|
+
version 1, '2014-02-19' do
|
102
|
+
category :baz do
|
103
|
+
event :quux, '2014-01-31', 'this is quux'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
EOS
|
107
|
+
f.close
|
108
|
+
|
109
|
+
the_instance = new_instance(@distinct_id, nil, :definitions => f.path, :version => 1)
|
110
|
+
expect { the_instance.event!(:baz, :quux, :foo => :bar) }.not_to raise_error
|
111
|
+
expect_event("fb1_baz_quux", { 'foo' => 'bar' })
|
112
|
+
expect { the_instance.event!(:baz, :foo, :foo => :bar) }.to raise_error
|
113
|
+
ensure
|
114
|
+
f.close
|
115
|
+
f.unlink
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#distinct_id" do
|
121
|
+
it "should allow reading the distinct ID" do
|
122
|
+
expect(@instance.distinct_id).to eq(@distinct_id)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should allow changing the distinct ID at runtime" do
|
126
|
+
i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
|
127
|
+
i.event!(:foo, :bar, { })
|
128
|
+
expect_event('xy1_foo_bar', { }, :distinct_id => @distinct_id)
|
129
|
+
i.distinct_id = '12345yoyoyo'
|
130
|
+
expect(i.distinct_id).to eq('12345yoyoyo')
|
131
|
+
i.event!(:foo, :bar, { })
|
132
|
+
expect_event('xy1_foo_bar', { }, :distinct_id => '12345yoyoyo')
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should allow changing the distinct ID to nil at runtime" do
|
136
|
+
i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
|
137
|
+
expect(i.distinct_id).to eq(@distinct_id)
|
138
|
+
i.distinct_id = nil
|
139
|
+
expect(i.distinct_id).to eq(nil)
|
140
|
+
i.event!(:foo, :bar, { })
|
141
|
+
expect_event('xy1_foo_bar', { }, :distinct_id => nil)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should not allow setting the distinct ID to an unsupported object" do
|
145
|
+
i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
|
146
|
+
expect { i.distinct_id = /foobar/ }.to raise_error(ArgumentError)
|
147
|
+
expect(i.distinct_id).to eq(@distinct_id)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "#track_event" do
|
152
|
+
it "should allow firing a valid event" do
|
153
|
+
i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
|
154
|
+
i.event!(:foo, :bar, { })
|
155
|
+
expect_event('xy1_foo_bar', { })
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "ip-address passing" do
|
159
|
+
it "should pass through an IPv4 String IP" do
|
160
|
+
i = new_instance(@distinct_id, "138.93.206.193", :definitions => definition_set, :version => 1)
|
161
|
+
i.event!(:foo, :bar, { })
|
162
|
+
expect_event('xy1_foo_bar', { 'ip' => "138.93.206.193" })
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should pass through an IPv6 String IP" do
|
166
|
+
i = new_instance(@distinct_id, "2607:f0d0:1002:0051:0000:0000:0000:0004", :definitions => definition_set, :version => 1)
|
167
|
+
i.event!(:foo, :bar, { })
|
168
|
+
expect_event('xy1_foo_bar', { 'ip' => "2607:f0d0:1002:51::4" })
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should pass through an Integer IP" do
|
172
|
+
i = new_instance(@distinct_id, 2321403585, :definitions => definition_set, :version => 1)
|
173
|
+
i.event!(:foo, :bar, { })
|
174
|
+
expect_event('xy1_foo_bar', { 'ip' => "138.93.206.193" })
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should pass through an IPv4 IPAddr" do
|
178
|
+
i = new_instance(@distinct_id, IPAddr.new("138.93.206.193"), :definitions => definition_set, :version => 1)
|
179
|
+
i.event!(:foo, :bar, { })
|
180
|
+
expect_event('xy1_foo_bar', { 'ip' => "138.93.206.193" })
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should pass through an IPv6 IPAddr" do
|
184
|
+
i = new_instance(@distinct_id, IPAddr.new("2607:f0d0:1002:0051:0000:0000:0000:0004"), :definitions => definition_set, :version => 1)
|
185
|
+
i.event!(:foo, :bar, { })
|
186
|
+
expect_event('xy1_foo_bar', { 'ip' => "2607:f0d0:1002:51::4" })
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should include the distinct_id set in the constructor" do
|
191
|
+
i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
|
192
|
+
i.event!(:foo, :bar, { })
|
193
|
+
expect_event('xy1_foo_bar', { }, :distinct_id => @distinct_id)
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should let you override the IP address on a per-event basis" do
|
197
|
+
i = new_instance(@distinct_id, "138.93.206.193", :definitions => definition_set, :version => 1)
|
198
|
+
i.event!(:foo, :bar, { 'ip' => '203.196.4.32' })
|
199
|
+
expect_event('xy1_foo_bar', { 'ip' => '203.196.4.32' }, :distinct_id => @distinct_id)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should allow overriding the distinct_id set in the constructor" do
|
203
|
+
i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
|
204
|
+
i.event!(:foo, :bar, { :distinct_id => 12345 })
|
205
|
+
expect_event('xy1_foo_bar', { }, :distinct_id => 12345)
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should allow a nil distinct_id" do
|
209
|
+
i = new_instance(nil, nil, :definitions => definition_set, :version => 1)
|
210
|
+
i.event!(:foo, :bar, { })
|
211
|
+
expect_event('xy1_foo_bar', { }, :distinct_id => nil)
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should allow a String distinct_id" do
|
215
|
+
i = new_instance("foobarfoobar", nil, :definitions => definition_set, :version => 1)
|
216
|
+
i.event!(:foo, :bar, { })
|
217
|
+
expect_event('xy1_foo_bar', { }, :distinct_id => 'foobarfoobar')
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should send the event to both receivers if asked" do
|
221
|
+
@instance.event_receivers = [ receiver_1, receiver_2 ]
|
222
|
+
@instance.event!(:foo, :bar, { })
|
223
|
+
|
224
|
+
expect_event("xy1_foo_bar", { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley' }, :receiver_name => :receiver_1)
|
225
|
+
expect_event("xy1_foo_bar", { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley' }, :receiver_name => :receiver_2)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should clone the class's default-receiver list on creation" do
|
229
|
+
expect(klass.default_event_receivers).to eq([ ])
|
230
|
+
|
231
|
+
begin
|
232
|
+
klass.default_event_receivers = [ receiver_2 ]
|
233
|
+
i = klass.new(@distinct_id, nil, :definitions => definition_set, :version => 1)
|
234
|
+
i.event!(:foo, :bar, { })
|
235
|
+
expect_event('xy1_foo_bar', { }, :receiver_name => :receiver_2)
|
236
|
+
ensure
|
237
|
+
klass.default_event_receivers = [ ]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should allow overriding the list of receivers in the constructor" do
|
242
|
+
i = klass.new(@distinct_id, nil, :definitions => definition_set, :version => 1, :event_receivers => [ receiver_2 ])
|
243
|
+
i.event!(:foo, :bar, { })
|
244
|
+
expect_event('xy1_foo_bar', { }, :receiver_name => :receiver_2)
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should use the class's list of default definitions by default" do
|
248
|
+
klass.default_definitions = MetaEvents::Definition::DefinitionSet.new(:global_events_prefix => "zz") do
|
249
|
+
version 1, '2014-01-31' do
|
250
|
+
category :marph do
|
251
|
+
event :bonk, '2014-01-31', 'this is bar'
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
i = klass.new(@distinct_id, nil, :version => 1, :event_receivers => receiver_1)
|
257
|
+
i.event!(:marph, :bonk)
|
258
|
+
expect_event('zz1_marph_bonk', { })
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should pick up the default version from the class" do
|
262
|
+
klass.default_version = 2
|
263
|
+
|
264
|
+
i = klass.new(@distinct_id, nil, :event_receivers => receiver_1, :definitions => definition_set)
|
265
|
+
i.event!(:foo, :quux)
|
266
|
+
expect_event("xy2_foo_quux", { })
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should allow firing a valid event, and include implicit properties" do
|
270
|
+
@instance.event!(:foo, :bar, { })
|
271
|
+
expect_event('xy1_foo_bar', { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley' })
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should not allow firing an event that doesn't exist" do
|
275
|
+
expect { @instance.event!(:foo, :quux, { }) }.to raise_error(ArgumentError, /quux/i)
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should not allow firing a category that doesn't exist" do
|
279
|
+
expect { @instance.event!(:bogus, :signed_up, { }) }.to raise_error(ArgumentError, /bogus/i)
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should include defined properties with the event" do
|
283
|
+
@instance.event!(:foo, :bar, { :awesomeness => 123, :foo => 'bar' })
|
284
|
+
expect_event('xy1_foo_bar', { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley', 'awesomeness' => 123, 'foo' => 'bar' })
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should fail if there's a circular reference" do
|
288
|
+
circular = { :foo => :bar }
|
289
|
+
circular[:baz] = circular
|
290
|
+
|
291
|
+
expect { @instance.event!(:foo, :bar, circular) }.to raise_error(/circular/i)
|
292
|
+
end
|
293
|
+
|
294
|
+
it "should pass through lots of different kinds of properties" do
|
295
|
+
the_time = Time.parse("2008-09-04 3:46:12 PM -08:00")
|
296
|
+
@instance.event!(:foo, :bar, {
|
297
|
+
:num1 => 42,
|
298
|
+
:num2 => 6.0221413e+23,
|
299
|
+
:true => true,
|
300
|
+
:false => false,
|
301
|
+
:string => 'foobar',
|
302
|
+
:symbol => :bazbar,
|
303
|
+
:time_interval => 3.months,
|
304
|
+
:pos_infinity => (+1.0/0.0),
|
305
|
+
:nan => (0.0/0.0),
|
306
|
+
:neg_infinity => (-1.0/0.0),
|
307
|
+
:time => the_time,
|
308
|
+
:stuff => [ "foo", :bar, 123, -9.45e+17, the_time, false, nil, true, " BoNk " ]
|
309
|
+
})
|
310
|
+
expect_event('xy1_foo_bar', {
|
311
|
+
'user_name' => 'wilfred',
|
312
|
+
'user_hometown' => 'Fridley',
|
313
|
+
'num1' => 42,
|
314
|
+
'num2' => 6.0221413e+23,
|
315
|
+
'true' => true,
|
316
|
+
'false' => false,
|
317
|
+
'string' => 'foobar',
|
318
|
+
'symbol' => 'bazbar',
|
319
|
+
'time_interval' => 7776000,
|
320
|
+
'pos_infinity' => "+infinity",
|
321
|
+
'nan' => 'NaN',
|
322
|
+
'neg_infinity' => "-infinity",
|
323
|
+
'time' => "2008-09-04T23:46:12",
|
324
|
+
'stuff' => [ "foo", "bar", 123, -9.45e+17, "2008-09-04T23:46:12", false, nil, true, "BoNk" ],
|
325
|
+
})
|
326
|
+
end
|
327
|
+
|
328
|
+
it "should let me override implicit properties with user-defined ones" do
|
329
|
+
@instance.event!(:foo, :bar, { :user_name => 'bongo' })
|
330
|
+
expect_event('xy1_foo_bar', { 'user_name' => 'bongo', 'user_hometown' => 'Fridley' })
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should allow nested properties in the event, and expand them out" do
|
334
|
+
@instance.event!(:foo, :bar, { :location => { :city => 'Edina', :zip => 55343 } })
|
335
|
+
expect_event('xy1_foo_bar', { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley', 'location_city' => 'Edina', 'location_zip' => 55343 })
|
336
|
+
end
|
337
|
+
|
338
|
+
it "should accept nested properties from #to_event_properties, and expand them out" do
|
339
|
+
@instance.event!(:foo, :bar, { :location => tep_object(:city => 'Edina', :zip => 55343 )})
|
340
|
+
expect_event('xy1_foo_bar', { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley', 'location_city' => 'Edina', 'location_zip' => 55343 })
|
341
|
+
end
|
342
|
+
|
343
|
+
it "should not allow firing a retired event" do
|
344
|
+
expect { @instance.event!(:foo, :nolonger, { }) }.to raise_error(::MetaEvents::Definition::DefinitionSet::RetiredEventError)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
describe "#effective_properties" do
|
349
|
+
it "should validate the event" do
|
350
|
+
expect { @instance.effective_properties(:foo, :whatever) }.to raise_error(ArgumentError)
|
351
|
+
expect { @instance.effective_properties(:foo, :nolonger) }.to raise_error(::MetaEvents::Definition::DefinitionSet::RetiredEventError)
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should include the fully-qualified event name" do
|
355
|
+
expect(@instance.effective_properties(:foo, :bar)[:event_name]).to eq('xy1_foo_bar')
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should include the distinct ID" do
|
359
|
+
expect(@instance.effective_properties(:foo, :bar)[:distinct_id]).to eq(@distinct_id)
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should include a distinct ID of nil if there is none" do
|
363
|
+
i = new_instance(nil, nil, :definitions => definition_set, :version => 1)
|
364
|
+
h = i.effective_properties(:foo, :bar)
|
365
|
+
expect(h.has_key?(:distinct_id)).to be_true
|
366
|
+
expect(h[:distinct_id]).to be_nil
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should include the set of implicit and explicit properties" do
|
370
|
+
expect(@instance.effective_properties(:foo, :bar)[:properties]).to eq('user_name' => 'wilfred', 'user_hometown' => 'Fridley')
|
371
|
+
props = @instance.effective_properties(:foo, :bar, :baz => { :a => 1, :b => 'hoo' }, :user_name => 'bongo')[:properties]
|
372
|
+
expect(props).to eq('user_name' => 'bongo', 'user_hometown' => 'Fridley', 'baz_a' => 1, 'baz_b' => 'hoo')
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should include no additional keys" do
|
376
|
+
expect(@instance.effective_properties(:foo, :bar).keys.sort_by(&:to_s)).to eq(%w{distinct_id event_name properties}.map(&:to_sym).sort_by(&:to_s))
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
describe "#merge_properties" do
|
381
|
+
def expand(hash)
|
382
|
+
out = { }
|
383
|
+
klass.merge_properties(out, hash, "", 0)
|
384
|
+
out
|
385
|
+
end
|
386
|
+
|
387
|
+
def expand_scalar(value)
|
388
|
+
expand({ 'foo' => value })['foo']
|
389
|
+
end
|
390
|
+
|
391
|
+
it "should return proper values for scalars" do
|
392
|
+
expect(expand_scalar('foo')).to eq('foo')
|
393
|
+
expect(expand_scalar(' FoO ')).to eq('FoO')
|
394
|
+
|
395
|
+
expect(expand_scalar(:foo)).to eq('foo')
|
396
|
+
expect(expand_scalar(:' FoOO ')).to eq('FoOO')
|
397
|
+
|
398
|
+
expect(expand_scalar(123)).to eq(123)
|
399
|
+
expect(expand_scalar(4.2867)).to eq(4.2867)
|
400
|
+
expect(expand_scalar(1234567890123456789012345678901234567890123456789012345678901234567890)).to eq(1234567890123456789012345678901234567890123456789012345678901234567890)
|
401
|
+
expect(expand_scalar(true)).to eq(true)
|
402
|
+
expect(expand_scalar(false)).to eq(false)
|
403
|
+
expect(expand_scalar(nil)).to eq(nil)
|
404
|
+
end
|
405
|
+
|
406
|
+
it "should raise an error for unknown data, including Hashes" do
|
407
|
+
expect { expand_scalar(/foobar/) }.to raise_error(ArgumentError)
|
408
|
+
expect { expand_scalar([ "a", "b", /foo/ ]) }.to raise_error(ArgumentError)
|
409
|
+
end
|
410
|
+
|
411
|
+
it "should stringify symbols on both keys and values" do
|
412
|
+
expect(expand({ :foo => :bar })).to eq({ 'foo' => 'bar' })
|
413
|
+
end
|
414
|
+
|
415
|
+
it "should correctly expand simple hashes" do
|
416
|
+
expect(expand({ :foo => 'bar' })).to eq({ 'foo' => 'bar' })
|
417
|
+
expect(expand({ :zzz => ' bonkO ', 'bar' => :' baZZ ' })).to eq({ 'zzz' => 'bonkO', 'bar' => 'baZZ' })
|
418
|
+
end
|
419
|
+
|
420
|
+
it "should recursively expand hashes" do
|
421
|
+
expect(expand({ :foo => { :bar => ' whatEVs '} })).to eq({ 'foo_bar' => 'whatEVs' })
|
422
|
+
expect { expand({ :foo => { :bar => [ 1, 2, /foo/ ] } }) }.to raise_error(ArgumentError)
|
423
|
+
end
|
424
|
+
|
425
|
+
it "should call #to_event_properties for any object, and recursively expand that" do
|
426
|
+
expect(expand(:baz => tep_object({ :foo => ' BaR '}))).to eq({ 'baz_foo' => 'BaR' })
|
427
|
+
expect(expand(:baz => tep_object({ :foo => { :bar => ' yo yo yo '} }))).to eq({ 'baz_foo_bar' => 'yo yo yo' })
|
428
|
+
|
429
|
+
subsidiary = tep_object({ :bar => :baz })
|
430
|
+
expect(expand(:baz => tep_object({ :foo => subsidiary }))).to eq({ 'baz_foo_bar' => 'baz' })
|
431
|
+
end
|
432
|
+
|
433
|
+
it "should raise if it detects a property-name conflict" do
|
434
|
+
expect { expand(:foo_bar => :quux1, :foo => { :bar => :quux }) }.to raise_error(MetaEvents::Tracker::PropertyCollisionError)
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
describe "#normalize_scalar_property_value" do
|
439
|
+
it "should return the correct results for scalars" do
|
440
|
+
infinity = (1.0 / 0.0)
|
441
|
+
nan = (0.0 / 0.0)
|
442
|
+
|
443
|
+
t = Time.parse("2008-09-04 3:46:12 PM -08:00")
|
444
|
+
{
|
445
|
+
nil => nil,
|
446
|
+
true => true,
|
447
|
+
false => false,
|
448
|
+
3 => 3,
|
449
|
+
42.5e+17 => 42.5e+17,
|
450
|
+
3.months => 7776000,
|
451
|
+
infinity => "+infinity",
|
452
|
+
nan => "NaN",
|
453
|
+
-infinity => "-infinity",
|
454
|
+
:foobar => 'foobar',
|
455
|
+
:' FooBar ' => 'FooBar',
|
456
|
+
' FooBar ' => 'FooBar',
|
457
|
+
t => "2008-09-04T23:46:12",
|
458
|
+
[ "foo", :bar, 123, -9.45e+17, t, false, nil, true, " BoNk " ] => [ "foo", "bar", 123, -9.45e+17, "2008-09-04T23:46:12", false, nil, true, "BoNk" ],
|
459
|
+
/foobar/ => :invalid_property_value,
|
460
|
+
Object.new => :invalid_property_value
|
461
|
+
}.each do |input, output|
|
462
|
+
expect(klass.normalize_scalar_property_value(input)).to eq(output)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|