metry 1.2.0 → 2.0.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 (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