metry 2.0.5 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +12 -6
  3. data/Rakefile +5 -0
  4. data/TODO +1 -6
  5. data/example/example.rb +1 -1
  6. data/features/app_tracking.feature +1 -2
  7. data/features/basic_tracking.feature +19 -38
  8. data/features/psycho/dashboard.feature +3 -11
  9. data/features/psycho/experiments.feature +28 -0
  10. data/features/psycho/visitor_tracking.feature +4 -4
  11. data/features/step_definitions/experiments.rb +8 -0
  12. data/features/step_definitions/psycho.rb +14 -0
  13. data/features/step_definitions/tracking.rb +56 -24
  14. data/features/support/env.rb +0 -1
  15. data/features/support/helpers.rb +6 -0
  16. data/lib/metry.rb +12 -5
  17. data/lib/metry/cohort.rb +19 -0
  18. data/lib/metry/event.rb +26 -0
  19. data/lib/metry/experiment.rb +36 -13
  20. data/lib/metry/goal.rb +18 -0
  21. data/lib/metry/psycho.rb +24 -93
  22. data/lib/metry/psycho/dashboard.erb +5 -5
  23. data/lib/metry/psycho/edit_goal.erb +1 -1
  24. data/lib/metry/psycho/experiment.erb +25 -0
  25. data/lib/metry/psycho/new_goal.erb +1 -1
  26. data/lib/metry/psycho/visitor.erb +4 -1
  27. data/lib/metry/psycho/visitors.erb +6 -0
  28. data/lib/metry/rack/tracking.rb +19 -18
  29. data/lib/metry/visitor.rb +30 -0
  30. data/radiant/example/features/metry.feature +21 -43
  31. data/radiant/example/features/step_definitions/tracking.rb +56 -24
  32. data/radiant/example/features/step_definitions/web.rb +0 -7
  33. data/radiant/example/features/support/env.rb +7 -3
  34. data/radiant/example/features/support/helpers.rb +6 -0
  35. data/radiant/extension/lib/metry_tags.rb +3 -1
  36. data/test/shared.rb +3 -1
  37. data/test/test_experiment.rb +104 -0
  38. metadata +45 -9
  39. data/features/psycho/goals.feature +0 -53
  40. data/features/step_definitions/goals.rb +0 -3
  41. data/lib/metry/psycho/goal.erb +0 -9
  42. data/lib/metry/storage.rb +0 -142
  43. data/radiant/example/features/step_definitions/experiments.rb +0 -12
  44. 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"
@@ -1,3 +0,0 @@
1
- Given /^I add a goal named "([^\"]*)" with path "([^\"]*)"$/ do |name, path|
2
- Metry.current.add_goal('name' => name, 'path' => path)
3
- end
@@ -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>
@@ -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
@@ -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