metry 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/History.txt +6 -0
  2. data/Manifest.txt +14 -69
  3. data/TODO +2 -3
  4. data/cucumber.yml +1 -0
  5. data/example/example.rb +1 -1
  6. data/features/psycho/dashboard.feature +17 -0
  7. data/features/psycho/goals.feature +24 -0
  8. data/features/psycho/visitor_tracking.feature +32 -0
  9. data/features/step_definitions/goals.rb +3 -0
  10. data/features/step_definitions/tracking.rb +1 -1
  11. data/features/step_definitions/web.rb +14 -2
  12. data/features/support/env.rb +4 -0
  13. data/lib/metry.rb +4 -6
  14. data/lib/metry/experiment.rb +6 -8
  15. data/lib/metry/psycho.rb +112 -0
  16. data/lib/metry/psycho/dashboard.erb +14 -0
  17. data/lib/metry/psycho/layout.erb +8 -0
  18. data/lib/metry/psycho/new_goal.erb +9 -0
  19. data/lib/metry/psycho/visitor.erb +7 -0
  20. data/lib/metry/rack/tracking.rb +17 -13
  21. data/lib/metry/storage.rb +116 -0
  22. data/radiant/example/features/metry.feature +8 -8
  23. data/radiant/example/features/psycho.feature +13 -0
  24. data/radiant/example/features/step_definitions/experiments.rb +2 -2
  25. data/radiant/example/features/step_definitions/tracking.rb +1 -1
  26. data/radiant/example/features/support/env.rb +3 -0
  27. data/radiant/extension/lib/metry_authenticator.rb +30 -0
  28. data/radiant/extension/lib/metry_tags.rb +2 -1
  29. data/radiant/extension/metry_extension.rb +1 -1
  30. data/test/{test_tokyo.rb → test_storage.rb} +3 -3
  31. metadata +17 -72
  32. data/lib/metry/memory.rb +0 -27
  33. data/lib/metry/tokyo.rb +0 -181
  34. data/vendor/rufus-tokyo/CHANGELOG.txt +0 -112
  35. data/vendor/rufus-tokyo/CREDITS.txt +0 -27
  36. data/vendor/rufus-tokyo/LICENSE.txt +0 -21
  37. data/vendor/rufus-tokyo/README.txt +0 -310
  38. data/vendor/rufus-tokyo/Rakefile +0 -118
  39. data/vendor/rufus-tokyo/TODO.txt +0 -25
  40. data/vendor/rufus-tokyo/doc/decision_table.numbers +0 -0
  41. data/vendor/rufus-tokyo/doc/rdoc-style.css +0 -320
  42. data/vendor/rufus-tokyo/lib/rufus-edo.rb +0 -3
  43. data/vendor/rufus-tokyo/lib/rufus-tokyo.rb +0 -3
  44. data/vendor/rufus-tokyo/lib/rufus/edo.rb +0 -39
  45. data/vendor/rufus-tokyo/lib/rufus/edo/README.txt +0 -106
  46. data/vendor/rufus-tokyo/lib/rufus/edo/cabcore.rb +0 -333
  47. data/vendor/rufus-tokyo/lib/rufus/edo/cabinet/abstract.rb +0 -219
  48. data/vendor/rufus-tokyo/lib/rufus/edo/cabinet/table.rb +0 -159
  49. data/vendor/rufus-tokyo/lib/rufus/edo/error.rb +0 -36
  50. data/vendor/rufus-tokyo/lib/rufus/edo/ntyrant.rb +0 -4
  51. data/vendor/rufus-tokyo/lib/rufus/edo/ntyrant/abstract.rb +0 -137
  52. data/vendor/rufus-tokyo/lib/rufus/edo/ntyrant/table.rb +0 -141
  53. data/vendor/rufus-tokyo/lib/rufus/edo/tabcore.rb +0 -567
  54. data/vendor/rufus-tokyo/lib/rufus/tokyo.rb +0 -58
  55. data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/abstract.rb +0 -568
  56. data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/lib.rb +0 -230
  57. data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/table.rb +0 -753
  58. data/vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/util.rb +0 -425
  59. data/vendor/rufus-tokyo/lib/rufus/tokyo/config.rb +0 -161
  60. data/vendor/rufus-tokyo/lib/rufus/tokyo/dystopia.rb +0 -43
  61. data/vendor/rufus-tokyo/lib/rufus/tokyo/dystopia/lib.rb +0 -65
  62. data/vendor/rufus-tokyo/lib/rufus/tokyo/dystopia/words.rb +0 -71
  63. data/vendor/rufus-tokyo/lib/rufus/tokyo/hmethods.rb +0 -111
  64. data/vendor/rufus-tokyo/lib/rufus/tokyo/query.rb +0 -102
  65. data/vendor/rufus-tokyo/lib/rufus/tokyo/transactions.rb +0 -74
  66. data/vendor/rufus-tokyo/lib/rufus/tokyo/ttcommons.rb +0 -59
  67. data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant.rb +0 -35
  68. data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/abstract.rb +0 -146
  69. data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/lib.rb +0 -153
  70. data/vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/table.rb +0 -162
  71. data/vendor/rufus-tokyo/rufus-tokyo.gemspec +0 -25
  72. data/vendor/rufus-tokyo/spec/cabinet_spec.rb +0 -472
  73. data/vendor/rufus-tokyo/spec/cabinetconfig_spec.rb +0 -82
  74. data/vendor/rufus-tokyo/spec/edo_cabinet_spec.rb +0 -447
  75. data/vendor/rufus-tokyo/spec/edo_ntyrant_spec.rb +0 -299
  76. data/vendor/rufus-tokyo/spec/edo_ntyrant_table_spec.rb +0 -462
  77. data/vendor/rufus-tokyo/spec/edo_table_spec.rb +0 -560
  78. data/vendor/rufus-tokyo/spec/hmethods_spec.rb +0 -44
  79. data/vendor/rufus-tokyo/spec/incr.lua +0 -20
  80. data/vendor/rufus-tokyo/spec/spec.rb +0 -9
  81. data/vendor/rufus-tokyo/spec/spec_base.rb +0 -23
  82. data/vendor/rufus-tokyo/spec/start_tyrants.sh +0 -26
  83. data/vendor/rufus-tokyo/spec/stop_tyrants.sh +0 -9
  84. data/vendor/rufus-tokyo/spec/table_spec.rb +0 -567
  85. data/vendor/rufus-tokyo/spec/tyrant_spec.rb +0 -309
  86. data/vendor/rufus-tokyo/spec/tyrant_table_spec.rb +0 -479
  87. data/vendor/rufus-tokyo/spec/util_list_spec.rb +0 -200
  88. data/vendor/rufus-tokyo/spec/util_map_spec.rb +0 -132
  89. data/vendor/rufus-tokyo/tasks/dev.rb +0 -70
  90. data/vendor/rufus-tokyo/test/bm0.rb +0 -353
  91. data/vendor/rufus-tokyo/test/bm1_compression.rb +0 -54
  92. data/vendor/rufus-tokyo/test/con0.rb +0 -30
  93. data/vendor/rufus-tokyo/test/mem.rb +0 -49
  94. data/vendor/rufus-tokyo/test/mem1.rb +0 -44
  95. data/vendor/rufus-tokyo/test/readme0.rb +0 -17
  96. data/vendor/rufus-tokyo/test/readme1.rb +0 -21
  97. data/vendor/rufus-tokyo/test/readme2.rb +0 -15
  98. data/vendor/rufus-tokyo/test/readme3.rb +0 -24
  99. data/vendor/rufus-tokyo/test/readmes_test.sh +0 -17
data/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ === 2.0.0 / 2009-08-07
2
+
3
+ * Switched to MongoDB for persistence.
4
+ * Added basic goal support to Psycho.
5
+ * Ignored events generated by Psycho.
6
+
1
7
  === 1.2.0 / 2009-07-30
2
8
 
3
9
  * Added Psycho for viewing Metry results.
data/Manifest.txt CHANGED
@@ -3,18 +3,27 @@ Manifest.txt
3
3
  README.txt
4
4
  Rakefile
5
5
  TODO
6
+ cucumber.yml
6
7
  example/example.rb
7
8
  features/app_tracking.feature
8
9
  features/basic_tracking.feature
10
+ features/psycho/dashboard.feature
11
+ features/psycho/goals.feature
12
+ features/psycho/visitor_tracking.feature
9
13
  features/sample_application.feature
14
+ features/step_definitions/goals.rb
10
15
  features/step_definitions/tracking.rb
11
16
  features/step_definitions/web.rb
12
17
  features/support/env.rb
13
18
  lib/metry.rb
14
19
  lib/metry/experiment.rb
15
- lib/metry/memory.rb
20
+ lib/metry/psycho.rb
21
+ lib/metry/psycho/dashboard.erb
22
+ lib/metry/psycho/layout.erb
23
+ lib/metry/psycho/new_goal.erb
24
+ lib/metry/psycho/visitor.erb
16
25
  lib/metry/rack/tracking.rb
17
- lib/metry/tokyo.rb
26
+ lib/metry/storage.rb
18
27
  radiant/example/CHANGELOG
19
28
  radiant/example/CONTRIBUTORS
20
29
  radiant/example/INSTALL
@@ -31,6 +40,7 @@ radiant/example/config/environments/test.rb
31
40
  radiant/example/config/routes.rb
32
41
  radiant/example/db/schema.rb
33
42
  radiant/example/features/metry.feature
43
+ radiant/example/features/psycho.feature
34
44
  radiant/example/features/step_definitions/experiments.rb
35
45
  radiant/example/features/step_definitions/radiant.rb
36
46
  radiant/example/features/step_definitions/tracking.rb
@@ -97,77 +107,12 @@ radiant/example/script/spec_server
97
107
  radiant/example/script/version
98
108
  radiant/extension/README
99
109
  radiant/extension/Rakefile
110
+ radiant/extension/lib/metry_authenticator.rb
100
111
  radiant/extension/lib/metry_tags.rb
101
112
  radiant/extension/lib/tasks/metry_extension_tasks.rake
102
113
  radiant/extension/metry_extension.rb
103
114
  test/shared.rb
104
- test/test_tokyo.rb
105
- vendor/rufus-tokyo/CHANGELOG.txt
106
- vendor/rufus-tokyo/CREDITS.txt
107
- vendor/rufus-tokyo/LICENSE.txt
108
- vendor/rufus-tokyo/README.txt
109
- vendor/rufus-tokyo/Rakefile
110
- vendor/rufus-tokyo/TODO.txt
111
- vendor/rufus-tokyo/doc/decision_table.numbers
112
- vendor/rufus-tokyo/doc/rdoc-style.css
113
- vendor/rufus-tokyo/lib/rufus-edo.rb
114
- vendor/rufus-tokyo/lib/rufus-tokyo.rb
115
- vendor/rufus-tokyo/lib/rufus/edo.rb
116
- vendor/rufus-tokyo/lib/rufus/edo/README.txt
117
- vendor/rufus-tokyo/lib/rufus/edo/cabcore.rb
118
- vendor/rufus-tokyo/lib/rufus/edo/cabinet/abstract.rb
119
- vendor/rufus-tokyo/lib/rufus/edo/cabinet/table.rb
120
- vendor/rufus-tokyo/lib/rufus/edo/error.rb
121
- vendor/rufus-tokyo/lib/rufus/edo/ntyrant.rb
122
- vendor/rufus-tokyo/lib/rufus/edo/ntyrant/abstract.rb
123
- vendor/rufus-tokyo/lib/rufus/edo/ntyrant/table.rb
124
- vendor/rufus-tokyo/lib/rufus/edo/tabcore.rb
125
- vendor/rufus-tokyo/lib/rufus/tokyo.rb
126
- vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/abstract.rb
127
- vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/lib.rb
128
- vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/table.rb
129
- vendor/rufus-tokyo/lib/rufus/tokyo/cabinet/util.rb
130
- vendor/rufus-tokyo/lib/rufus/tokyo/config.rb
131
- vendor/rufus-tokyo/lib/rufus/tokyo/dystopia.rb
132
- vendor/rufus-tokyo/lib/rufus/tokyo/dystopia/lib.rb
133
- vendor/rufus-tokyo/lib/rufus/tokyo/dystopia/words.rb
134
- vendor/rufus-tokyo/lib/rufus/tokyo/hmethods.rb
135
- vendor/rufus-tokyo/lib/rufus/tokyo/query.rb
136
- vendor/rufus-tokyo/lib/rufus/tokyo/transactions.rb
137
- vendor/rufus-tokyo/lib/rufus/tokyo/ttcommons.rb
138
- vendor/rufus-tokyo/lib/rufus/tokyo/tyrant.rb
139
- vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/abstract.rb
140
- vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/lib.rb
141
- vendor/rufus-tokyo/lib/rufus/tokyo/tyrant/table.rb
142
- vendor/rufus-tokyo/rufus-tokyo.gemspec
143
- vendor/rufus-tokyo/spec/cabinet_spec.rb
144
- vendor/rufus-tokyo/spec/cabinetconfig_spec.rb
145
- vendor/rufus-tokyo/spec/edo_cabinet_spec.rb
146
- vendor/rufus-tokyo/spec/edo_ntyrant_spec.rb
147
- vendor/rufus-tokyo/spec/edo_ntyrant_table_spec.rb
148
- vendor/rufus-tokyo/spec/edo_table_spec.rb
149
- vendor/rufus-tokyo/spec/hmethods_spec.rb
150
- vendor/rufus-tokyo/spec/incr.lua
151
- vendor/rufus-tokyo/spec/spec.rb
152
- vendor/rufus-tokyo/spec/spec_base.rb
153
- vendor/rufus-tokyo/spec/start_tyrants.sh
154
- vendor/rufus-tokyo/spec/stop_tyrants.sh
155
- vendor/rufus-tokyo/spec/table_spec.rb
156
- vendor/rufus-tokyo/spec/tyrant_spec.rb
157
- vendor/rufus-tokyo/spec/tyrant_table_spec.rb
158
- vendor/rufus-tokyo/spec/util_list_spec.rb
159
- vendor/rufus-tokyo/spec/util_map_spec.rb
160
- vendor/rufus-tokyo/tasks/dev.rb
161
- vendor/rufus-tokyo/test/bm0.rb
162
- vendor/rufus-tokyo/test/bm1_compression.rb
163
- vendor/rufus-tokyo/test/con0.rb
164
- vendor/rufus-tokyo/test/mem.rb
165
- vendor/rufus-tokyo/test/mem1.rb
166
- vendor/rufus-tokyo/test/readme0.rb
167
- vendor/rufus-tokyo/test/readme1.rb
168
- vendor/rufus-tokyo/test/readme2.rb
169
- vendor/rufus-tokyo/test/readme3.rb
170
- vendor/rufus-tokyo/test/readmes_test.sh
115
+ test/test_storage.rb
171
116
  vendor/webrat/.document
172
117
  vendor/webrat/History.txt
173
118
  vendor/webrat/MIT-LICENSE.txt
data/TODO CHANGED
@@ -1,6 +1,5 @@
1
- Switch to MongoDB
2
- Add ability to track goals in Psycho
3
- Allow ignoring requests in Metry, and have Psycho requests ignored
1
+ Show goal conversion for each experiment
2
+
4
3
  Switch to submodules for vendor'd libs
5
4
 
6
5
  More unit tests
data/cucumber.yml ADDED
@@ -0,0 +1 @@
1
+ default: --require features/support/ --require features/step_definitions/
data/example/example.rb CHANGED
@@ -4,7 +4,7 @@ require 'sinatra'
4
4
  require File.dirname(__FILE__) + '/../lib/metry'
5
5
 
6
6
  configure do
7
- Metry.init(ENV["USE_MEMORY"] ? Metry::Memory.new : Metry::Tokyo.new("tracking"))
7
+ Metry.init("tracking")
8
8
  use Metry::Rack::Tracking
9
9
  use Metry::Psycho, {:path => "/admin/metry"}
10
10
  end
@@ -0,0 +1,17 @@
1
+ Feature: Psycho Dashboard
2
+
3
+ Scenario: Basics
4
+ Given an empty tracking database
5
+ And I add a goal named "sweet" with path "/"
6
+ And I am a new visitor
7
+ And I view "/"
8
+ And I view "/subpage"
9
+ And I am a new visitor
10
+ And I view "/"
11
+ When I view "/admin/metry"
12
+ Then I should see "Goals"
13
+ And I should see "sweet: 2 visits"
14
+ And I should see "Recent Visitors"
15
+ And I should see "Visitor 1"
16
+ And I should see "Visitor 2"
17
+
@@ -0,0 +1,24 @@
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 goal"
15
+ And I fill in "path" with "/goal"
16
+ And I press "Create"
17
+ Then I should be on "/admin/metry/"
18
+ And the page should have "#goals .goal"
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"
@@ -0,0 +1,32 @@
1
+ Feature: Psycho Visitor Tracking
2
+ In order to see how visitors are using my site
3
+ As a site admin
4
+ I want to see visitors and drill down into their behavior
5
+
6
+ Background:
7
+ Given an empty tracking database
8
+ And I am a new visitor
9
+
10
+ Scenario: See list of visitors
11
+ Given I view "/"
12
+ And I am a new visitor
13
+ And I view "/"
14
+ When I view "/admin/metry"
15
+ Then I should see "Visitor 1"
16
+ And I should see "Visitor 2"
17
+
18
+ Scenario: View visitor detail
19
+ Given I view "/"
20
+ And I view "/subpage"
21
+ And I am a new visitor
22
+ And I view "/"
23
+ When I view "/admin/metry"
24
+ And I follow "Visitor 1"
25
+ Then I should see "/"
26
+ And I should see "/subpage"
27
+
28
+ Scenario: Psycho visits should not be tracked
29
+ Given I view "/admin/metry"
30
+ And I view "/admin/metry"
31
+ When I view "/admin/metry"
32
+ Then I should not see "Visitor 1"
@@ -0,0 +1,3 @@
1
+ Given /^I add a goal named "([^\"]*)" with path "([^\"]*)"$/ do |name, path|
2
+ Metry.current.add_goal('name' => name, 'path' => path)
3
+ end
@@ -11,7 +11,7 @@ Then /^there should be (\d+) visitors$/ do |visitor_count|
11
11
  end
12
12
 
13
13
  When /^there should be a tracking event "(\d+)":$/ do |id, table|
14
- event = Metry.current[id]
14
+ event = Metry.current.event(id)
15
15
  assert event, "Unable to lookup event #{id}."
16
16
  table.hashes.each do |hash|
17
17
  expected = hash["value"]
@@ -11,7 +11,19 @@ When /^I (post|put) to "([^\"]*)":$/ do |meth, path, params|
11
11
  end
12
12
 
13
13
  Then /^I should see "([^\"]*)"$/ do |string|
14
- assert_match(string, last_response.body)
14
+ assert_contain(string)
15
+ end
16
+
17
+ Then /^I should not see "([^\"]*)"$/ do |string|
18
+ assert_not_contain(string)
19
+ end
20
+
21
+ Then /^the page should have "([^\"]*)"$/ do |selector|
22
+ assert_have_selector selector
23
+ end
24
+
25
+ Then /^the page should not have "([^\"]*)"$/ do |selector|
26
+ assert_have_no_selector selector
15
27
  end
16
28
 
17
29
  When /^I follow "([^\"]*)"$/ do |link|
@@ -27,5 +39,5 @@ When /^I press "([^\"]*)"$/ do |button|
27
39
  end
28
40
 
29
41
  Then /^I should be on "([^\"]*)"$/ do |path|
30
- assert_equal path, last_response.path
42
+ assert_equal path, current_url
31
43
  end
@@ -3,6 +3,7 @@ World(Test::Unit::Assertions)
3
3
 
4
4
  require 'rack/test'
5
5
 
6
+ $: << File.dirname(__FILE__) + "/../../lib"
6
7
  $: << File.dirname(__FILE__) + "/../../vendor/webrat/lib"
7
8
  require 'webrat'
8
9
  require 'logger'
@@ -14,6 +15,9 @@ World(Rack::Test::Methods)
14
15
  World(Webrat::Methods)
15
16
  World(Webrat::Matchers)
16
17
 
18
+ require 'metry'
19
+ Metry::Storage.predictable_keys = true
20
+
17
21
  require File.dirname(__FILE__) + '/../../example/example.rb'
18
22
  def app
19
23
  Sinatra::Application
data/lib/metry.rb CHANGED
@@ -1,17 +1,15 @@
1
1
  $: << File.dirname(__FILE__)
2
- $: << File.dirname(__FILE__) + '/../vendor/rufus-tokyo/lib'
3
2
 
3
+ require 'metry/storage'
4
4
  require 'metry/rack/tracking'
5
- require 'metry/memory'
6
- require 'metry/tokyo'
7
5
  require 'metry/experiment'
8
6
  require 'metry/psycho'
9
7
 
10
8
  module Metry
11
- VERSION = '1.2.0'
9
+ VERSION = '2.0.0'
12
10
 
13
- def self.init(storage)
14
- @storage = storage
11
+ def self.init(dbname)
12
+ @storage = Storage.new(dbname)
15
13
  end
16
14
 
17
15
  def self.current
@@ -2,21 +2,19 @@ module Metry
2
2
  class Experiment
3
3
  METHODS = {
4
4
  "rand" => proc{|list, visitor| list.sort_by{rand}.first},
5
- "mod_visitor" => proc{|list, visitor| list[(visitor.to_i-1)%list.size]},
5
+ "mod_visitor" => proc{|list, visitor| list[(visitor['_id'].to_i-1)%list.size]}, # mod_visitor will only work with predictable_keys = true
6
6
  }
7
7
 
8
- def initialize(name, event)
9
- @key = "experiment.#{name}"
8
+ def initialize(name, event, visitor)
9
+ @key = "experiment:#{name}"
10
10
  @event = event
11
- @visitor = event["visitor"]
12
- @storage = Metry.current
11
+ @visitor = visitor
13
12
  end
14
13
 
15
14
  def choose(options, method=nil)
16
- visitor_hash = @storage.visitor(@visitor)
17
- unless choice = visitor_hash[@key]
15
+ unless choice = @visitor[@key]
18
16
  choice = METHODS[method || "rand"][options.keys, @visitor]
19
- @storage.save_visitor(@visitor, visitor_hash.merge(@key => choice))
17
+ @visitor[@key] = choice
20
18
  end
21
19
  @event[@key] = choice
22
20
  options[choice]
@@ -0,0 +1,112 @@
1
+ require 'sinatra/base'
2
+
3
+ module Metry
4
+ class Psycho < Sinatra::Base
5
+ def initialize(app, options={})
6
+ super(app)
7
+ @options = options
8
+ path = options[:path] || "/psycho"
9
+ auth = options[:authorize] || proc{true}
10
+ on_deny = options[:on_deny] || proc{[403, {"Content-Type" => "text/plain"}, "You must log in."]}
11
+
12
+ self.class.class_eval do
13
+ set :views, File.dirname(__FILE__) + '/psycho'
14
+
15
+ before do
16
+ if unescape(@request.path_info) =~ /^#{path}/
17
+ @request.env["metry.ignore"] = "true"
18
+ if !auth.call(env)
19
+ reply = on_deny.call(env)
20
+ status reply.first
21
+ response.header.merge!(reply[1])
22
+ halt reply.last
23
+ end
24
+ end
25
+ end
26
+
27
+ get "#{path}/?" do
28
+ @visitors = Visitor.all
29
+ @goals = Goal.all
30
+ erb :dashboard
31
+ end
32
+
33
+ get "#{path}/visitor/:id" do
34
+ @visitor = Visitor.find(params["id"])
35
+ erb :visitor
36
+ end
37
+
38
+ post "#{path}/goals" do
39
+ Goal.create!(params[:name], params[:path])
40
+ redirect url('/')
41
+ end
42
+
43
+ get "#{path}/goals/new" do
44
+ erb :new_goal
45
+ end
46
+
47
+ helpers do
48
+ define_method(:url) do |url|
49
+ "#{path}#{url}"
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ class Goal
56
+ def self.create!(name, path)
57
+ Metry.current.add_goal('name' => name, 'path' => path)
58
+ end
59
+
60
+ def self.all
61
+ Metry.current.goals.collect{|e| new(e)}
62
+ end
63
+
64
+ attr_reader :name, :path
65
+
66
+ def initialize(hash)
67
+ @name = hash['name']
68
+ @path = hash['path']
69
+ end
70
+
71
+ def visits
72
+ Metry.current.all_events({'path' => @path}).size
73
+ end
74
+ end
75
+
76
+ class Visitor
77
+ def self.all
78
+ Metry.current.visitors.collect{|e| new(e)}.select{|e| e.has_events?}
79
+ end
80
+
81
+ def self.find(id)
82
+ new(Metry.current.visitor(id))
83
+ end
84
+
85
+ def initialize(visitor)
86
+ @visitor = visitor
87
+ end
88
+
89
+ def id
90
+ @visitor['_id']
91
+ end
92
+
93
+ def events
94
+ Metry.current.events_for(id).collect{|e| Event.new(e)}
95
+ end
96
+
97
+ def has_events?
98
+ !events.empty?
99
+ end
100
+ end
101
+
102
+ class Event
103
+ def initialize(event)
104
+ @event = event
105
+ end
106
+
107
+ def path
108
+ @event["path"]
109
+ end
110
+ end
111
+ end
112
+ end