metry 2.0.5 → 2.1.0
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.
- data/History.txt +4 -0
- data/Manifest.txt +12 -6
- data/Rakefile +5 -0
- data/TODO +1 -6
- data/example/example.rb +1 -1
- data/features/app_tracking.feature +1 -2
- data/features/basic_tracking.feature +19 -38
- data/features/psycho/dashboard.feature +3 -11
- data/features/psycho/experiments.feature +28 -0
- data/features/psycho/visitor_tracking.feature +4 -4
- data/features/step_definitions/experiments.rb +8 -0
- data/features/step_definitions/psycho.rb +14 -0
- data/features/step_definitions/tracking.rb +56 -24
- data/features/support/env.rb +0 -1
- data/features/support/helpers.rb +6 -0
- data/lib/metry.rb +12 -5
- data/lib/metry/cohort.rb +19 -0
- data/lib/metry/event.rb +26 -0
- data/lib/metry/experiment.rb +36 -13
- data/lib/metry/goal.rb +18 -0
- data/lib/metry/psycho.rb +24 -93
- data/lib/metry/psycho/dashboard.erb +5 -5
- data/lib/metry/psycho/edit_goal.erb +1 -1
- data/lib/metry/psycho/experiment.erb +25 -0
- data/lib/metry/psycho/new_goal.erb +1 -1
- data/lib/metry/psycho/visitor.erb +4 -1
- data/lib/metry/psycho/visitors.erb +6 -0
- data/lib/metry/rack/tracking.rb +19 -18
- data/lib/metry/visitor.rb +30 -0
- data/radiant/example/features/metry.feature +21 -43
- data/radiant/example/features/step_definitions/tracking.rb +56 -24
- data/radiant/example/features/step_definitions/web.rb +0 -7
- data/radiant/example/features/support/env.rb +7 -3
- data/radiant/example/features/support/helpers.rb +6 -0
- data/radiant/extension/lib/metry_tags.rb +3 -1
- data/test/shared.rb +3 -1
- data/test/test_experiment.rb +104 -0
- metadata +45 -9
- data/features/psycho/goals.feature +0 -53
- data/features/step_definitions/goals.rb +0 -3
- data/lib/metry/psycho/goal.erb +0 -9
- data/lib/metry/storage.rb +0 -142
- data/radiant/example/features/step_definitions/experiments.rb +0 -12
- data/test/test_storage.rb +0 -25
@@ -1,53 +0,0 @@
|
|
1
|
-
Feature: Track Goals
|
2
|
-
|
3
|
-
Background:
|
4
|
-
Given an empty tracking database
|
5
|
-
|
6
|
-
Scenario: No goals
|
7
|
-
When I view "/admin/metry"
|
8
|
-
Then the page should have "#goals"
|
9
|
-
And the page should not have "#goals .goal"
|
10
|
-
|
11
|
-
Scenario: Create a goal
|
12
|
-
Given I view "/admin/metry"
|
13
|
-
And I follow "New Goal"
|
14
|
-
And I fill in "name" with "Cool"
|
15
|
-
And I fill in "path" with "/goal"
|
16
|
-
And I press "Create"
|
17
|
-
Then I should be on "/admin/metry/goals/1"
|
18
|
-
And I should see "Cool"
|
19
|
-
|
20
|
-
Scenario: Goal count
|
21
|
-
Given I add a goal named "My Goal" with path "/"
|
22
|
-
And I view "/"
|
23
|
-
When I view "/admin/metry"
|
24
|
-
Then I should see "My Goal: 1 visits"
|
25
|
-
|
26
|
-
Scenario: Goal Regexp
|
27
|
-
Given I add a goal named "Subpage" with path "/subpage/?"
|
28
|
-
When I view "/subpage"
|
29
|
-
And I view "/subpage/"
|
30
|
-
And I view "/admin/metry"
|
31
|
-
Then I should see "Subpage: 2 visits"
|
32
|
-
|
33
|
-
Scenario: View Goal Detail
|
34
|
-
Given I add a goal named "Root" with path "/"
|
35
|
-
And I view "/"
|
36
|
-
And I view "/"
|
37
|
-
When I view "/admin/metry"
|
38
|
-
And I follow "Root"
|
39
|
-
Then I should see "Root"
|
40
|
-
And I should see "Path: /"
|
41
|
-
And I should see "Visitor 1"
|
42
|
-
|
43
|
-
Scenario: Edit a Goal
|
44
|
-
Given I add a goal named "Bogus" with path "/"
|
45
|
-
When I view "/admin/metry"
|
46
|
-
And I follow "Bogus"
|
47
|
-
And I follow "Edit"
|
48
|
-
And I fill in "name" with "Right"
|
49
|
-
And I fill in "path" with "/subpage"
|
50
|
-
And I press "Save"
|
51
|
-
Then I should be on "/admin/metry/goals/1"
|
52
|
-
And I should see "Right"
|
53
|
-
And I should see "Path: /subpage"
|
data/lib/metry/psycho/goal.erb
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
<h1>Goal <%= @goal.name %></h1>
|
2
|
-
|
3
|
-
<p>Path: <%=h @goal.path %></p>
|
4
|
-
<p><a href="<%= url "/goals/#{@goal.id}/edit" %>">Edit</a></p>
|
5
|
-
<ol>
|
6
|
-
<% @goal.visitors.each do |visitor| %>
|
7
|
-
<li><a href="<%= url "/visitors/#{visitor.id}" %>">Visitor <%= visitor.id %></a></li>
|
8
|
-
<% end %>
|
9
|
-
</ol>
|
data/lib/metry/storage.rb
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
require 'mongo'
|
2
|
-
|
3
|
-
module Metry
|
4
|
-
class Storage
|
5
|
-
include XGen::Mongo::Driver
|
6
|
-
|
7
|
-
@predictable_keys = false
|
8
|
-
class << self
|
9
|
-
def predictable_keys?
|
10
|
-
@predictable_keys
|
11
|
-
end
|
12
|
-
|
13
|
-
def predictable_keys=(value)
|
14
|
-
@predictable_keys = value
|
15
|
-
Metry.init Metry.current.dbname if Metry.current
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
attr_reader :dbname
|
20
|
-
|
21
|
-
def initialize(dbname)
|
22
|
-
@dbname = dbname
|
23
|
-
options = {}
|
24
|
-
@key_factory = nil
|
25
|
-
if self.class.predictable_keys?
|
26
|
-
@key_factory = PredictableKeyFactory.new
|
27
|
-
options[:pk] = @key_factory
|
28
|
-
end
|
29
|
-
@db = Mongo.new('localhost').db(dbname, options)
|
30
|
-
end
|
31
|
-
|
32
|
-
def visitor(id)
|
33
|
-
@db.collection('visitors').find(prep_selector('_id' => id)).next_object
|
34
|
-
end
|
35
|
-
|
36
|
-
def visitor_count
|
37
|
-
@db.collection('visitors').count
|
38
|
-
end
|
39
|
-
|
40
|
-
def visitors
|
41
|
-
@db.collection('visitors').find
|
42
|
-
end
|
43
|
-
|
44
|
-
def new_visitor
|
45
|
-
visitor(@db.collection('visitors').insert({}))
|
46
|
-
end
|
47
|
-
|
48
|
-
def save_visitor(visitor)
|
49
|
-
@db.collection('visitors').repsert(prep_selector('_id' => visitor["_id"]), visitor)
|
50
|
-
end
|
51
|
-
|
52
|
-
def add_event(event)
|
53
|
-
@db.collection('events') << event
|
54
|
-
end
|
55
|
-
|
56
|
-
def event(id)
|
57
|
-
@db.collection('events').find(prep_selector('_id' => id)).next_object
|
58
|
-
end
|
59
|
-
|
60
|
-
def event_count
|
61
|
-
@db.collection('events').count
|
62
|
-
end
|
63
|
-
|
64
|
-
def events_for(visitor)
|
65
|
-
@db.collection('events').find('visitor' => visitor)
|
66
|
-
end
|
67
|
-
|
68
|
-
def last_events(count=1)
|
69
|
-
@db.collection('events').find({}, :limit => count, :sort => {'time' => 1}).to_a
|
70
|
-
end
|
71
|
-
|
72
|
-
def all_events(*find_options)
|
73
|
-
@db.collection('events').find(*find_options).to_a
|
74
|
-
end
|
75
|
-
|
76
|
-
def add_goal(goal)
|
77
|
-
goal(@db.collection('goals').insert(goal))
|
78
|
-
end
|
79
|
-
|
80
|
-
def goals
|
81
|
-
@db.collection('goals').find.to_a
|
82
|
-
end
|
83
|
-
|
84
|
-
def goal(id)
|
85
|
-
@db.collection('goals').find(prep_selector('_id' => id)).next_object
|
86
|
-
end
|
87
|
-
|
88
|
-
def save_goal(goal)
|
89
|
-
@db.collection('goals').repsert(prep_selector('_id' => goal['_id']), goal)
|
90
|
-
end
|
91
|
-
|
92
|
-
def clear
|
93
|
-
%w(visitors events goals).each{|e| @db.drop_collection(e)}
|
94
|
-
@key_factory.clear if @key_factory
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def prep_selector(selector)
|
100
|
-
if self.class.predictable_keys?
|
101
|
-
selector
|
102
|
-
elsif id = selector['_id']
|
103
|
-
case id
|
104
|
-
when String
|
105
|
-
begin
|
106
|
-
selector['_id'] = ObjectID.from_string(id)
|
107
|
-
rescue RuntimeError => e
|
108
|
-
raise unless e.message =~ /illegal objectid/i
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
selector
|
113
|
-
end
|
114
|
-
|
115
|
-
class PredictableKeyFactory
|
116
|
-
def initialize
|
117
|
-
clear
|
118
|
-
end
|
119
|
-
|
120
|
-
def create_pk(row)
|
121
|
-
return row if row['_id']
|
122
|
-
row.delete('_id') # in case it exists but the value is nil
|
123
|
-
row['_id'] ||= id_for(row).to_s
|
124
|
-
row
|
125
|
-
end
|
126
|
-
|
127
|
-
def id_for(row)
|
128
|
-
if row["event"]
|
129
|
-
@ids[:event] += 1
|
130
|
-
elsif row["name"] && row["path"]
|
131
|
-
@ids[:goal] += 1
|
132
|
-
else
|
133
|
-
@ids[:visitor] += 1
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def clear
|
138
|
-
@ids = {:event => 0, :goal => 0, :visitor => 0}
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
Then /^I should see the same "([^\"]*)" alternative (\d+) times$/ do |experiment, count|
|
2
|
-
events = Metry.current.last_events(count.to_i)
|
3
|
-
assert_equal count.to_i, events.size
|
4
|
-
seen = events.collect{|e| e["experiment.#{experiment}"]}.uniq
|
5
|
-
assert_equal 1, seen.size, "More than one seen in #{events.collect{|e| [e[:pk], e["visitor"], e["time"], e["experiment.#{experiment}"]]}.inspect}"
|
6
|
-
end
|
7
|
-
|
8
|
-
Then /^at least (\d+) should see alternative "([^\"]*)" of experiment "([^\"]*)"$/ do |count, alternative, experiment|
|
9
|
-
events = Metry.current.all_events
|
10
|
-
matching_count = events.select{|e| e["experiment:#{experiment}"] == alternative}.size
|
11
|
-
assert matching_count > count.to_i, "#{matching_count} is not great than #{count}"
|
12
|
-
end
|
data/test/test_storage.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/shared'
|
2
|
-
|
3
|
-
class TestTokyo < Test::Unit::TestCase
|
4
|
-
context "An empty database" do
|
5
|
-
setup do
|
6
|
-
@storage = Metry::Storage.new('test')
|
7
|
-
@storage.clear
|
8
|
-
end
|
9
|
-
|
10
|
-
should "be empty" do
|
11
|
-
assert_equal 0, @storage.event_count
|
12
|
-
end
|
13
|
-
|
14
|
-
should "handle object ids" do
|
15
|
-
v = @storage.new_visitor
|
16
|
-
assert_equal v, @storage.visitor(v['_id'].to_s)
|
17
|
-
end
|
18
|
-
|
19
|
-
should "handle bad object ids" do
|
20
|
-
assert_nothing_raised do
|
21
|
-
assert_nil @storage.visitor('1')
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|