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.
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