snowfinch-collector 0.5.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.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem "autotest-standalone"
7
+ gem "rack-test", :require => "rack/test"
8
+ gem "rspec"
9
+ gem "steak"
10
+ gem "timecop"
11
+ end
@@ -0,0 +1,2 @@
1
+ Snowfinch Collector
2
+ ===================
@@ -0,0 +1,5 @@
1
+ require "bundler"
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require "rspec/core/rake_task"
5
+ RSpec::Core::RakeTask.new
@@ -0,0 +1,59 @@
1
+ require "base64"
2
+ require "mongo"
3
+ require "rack"
4
+ require "rack/request"
5
+ require "uri"
6
+ require "digest/sha1"
7
+ require "radix62"
8
+ require "snowfinch/collector/click"
9
+
10
+ module Snowfinch
11
+ module Collector
12
+
13
+ EMPTY_GIF = "R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
14
+ RESPONSE = [Base64.decode64(EMPTY_GIF)]
15
+ HEADERS = { "Content-Type" => "image/gif" }
16
+
17
+ OK = [200, HEADERS, RESPONSE]
18
+ BAD_REQUEST = [400, {}, []]
19
+
20
+ def self.call(env)
21
+ params = Rack::Request.new(env).params
22
+
23
+ click = Click.new :token => params["token"],
24
+ :uri => params["uri"],
25
+ :uuid => params["uuid"],
26
+ :referrer => params["referrer"]
27
+
28
+ if click.save
29
+ OK
30
+ else
31
+ BAD_REQUEST
32
+ end
33
+ #rescue
34
+ #BAD_REQUEST
35
+ end
36
+
37
+ def self.db
38
+ @db ||= Mongo::Connection.new.db("snowfinch")
39
+ end
40
+
41
+ def self.db=(database)
42
+ @db = database
43
+ end
44
+
45
+ def self.sanitize_uri(uri)
46
+ uri = uri.sub(/^https?:\/\/(www\.)?/, "http://")
47
+ uri = URI.parse(uri)
48
+ uri.path = uri.path.sub(/(.)\/$/, '\1')
49
+ uri.query = nil
50
+ uri.fragment = nil
51
+ uri = uri.to_s
52
+ end
53
+
54
+ def self.hash_uri(uri)
55
+ Digest::SHA1.hexdigest(uri).to_i(16).encode62
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,168 @@
1
+ require "bson"
2
+ require "cgi"
3
+ require "time"
4
+ require "tzinfo"
5
+ require "uri"
6
+
7
+ module Snowfinch
8
+ module Collector
9
+ class Click
10
+
11
+ def initialize(attributes={})
12
+ @attributes = attributes
13
+ end
14
+
15
+ def save
16
+ if uri && uuid
17
+ update_site_count
18
+ update_page_count
19
+ update_visit
20
+ update_visitor
21
+ update_sensors
22
+ true
23
+ else
24
+ false
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def site_id
31
+ @site_id ||= BSON::ObjectId(@attributes[:token])
32
+ end
33
+
34
+ def uuid
35
+ @uuid ||= @attributes[:uuid]
36
+ end
37
+
38
+ def uri
39
+ @uri ||= begin
40
+ uri = @attributes[:uri]
41
+ uri && Snowfinch::Collector.sanitize_uri(uri)
42
+ end
43
+ end
44
+
45
+ def uri_hash
46
+ @uri_hash ||= Snowfinch::Collector.hash_uri(uri)
47
+ end
48
+
49
+ def referrer
50
+ @referrer ||= @attributes[:referrer]
51
+ end
52
+
53
+ def site
54
+ @site ||= Snowfinch::Collector.db["sites"].find_one(site_id)
55
+ end
56
+
57
+ def time
58
+ @time ||= TZInfo::Timezone.get(site["tz"]).current_period_and_time.first
59
+ end
60
+
61
+ def sensors
62
+ @sensors ||= site["sensors"]
63
+ end
64
+
65
+ def matching_sensors
66
+ matching_query_sensors + matching_host_sensors
67
+ end
68
+
69
+ def matching_query_sensors
70
+ query_string = URI.parse(@attributes[:uri]).query
71
+
72
+ if query_string && sensors
73
+ query_parts = CGI.parse(query_string)
74
+
75
+ matches = sensors.find_all do |sensor|
76
+ if sensor["type"] == "query"
77
+ query_parts.any? do |key, value|
78
+ sensor["key"] == key && value.include?(sensor["value"])
79
+ end
80
+ end
81
+ end
82
+
83
+ matches.map { |sensor| sensor["id"] }
84
+ else
85
+ []
86
+ end
87
+ end
88
+
89
+ def matching_host_sensors
90
+ if referrer && !referrer.empty? && sensors
91
+ referrer_host = URI.parse(referrer).host
92
+
93
+ matches = sensors.find_all do |sensor|
94
+ if sensor["type"] == "referrer"
95
+ sensor["hosts"].any? do |sensor_host|
96
+ referrer_host =~ /^(\S+\.)?#{sensor_host}$/
97
+ end
98
+ end
99
+ end
100
+
101
+ matches.map { |sensor| sensor["id"] }
102
+ else
103
+ []
104
+ end
105
+ end
106
+
107
+ def hourly_increment
108
+ @hourly_increment ||= {
109
+ "c" => 1,
110
+ "#{time.mon}.c" => 1,
111
+ "#{time.mon}.#{time.day}.c" => 1,
112
+ "#{time.mon}.#{time.day}.#{time.hour}.c" => 1
113
+ }
114
+ end
115
+
116
+ def update_site_count
117
+ Snowfinch::Collector.db["site_counts"].update(
118
+ { "s" => site_id, "y" => time.year },
119
+ { :$inc => hourly_increment },
120
+ { :upsert => true }
121
+ )
122
+ end
123
+
124
+ def update_page_count
125
+ Snowfinch::Collector.db["page_counts"].update(
126
+ { "s" => site_id, "u" => uri, "y" => time.year, "h" => uri_hash },
127
+ { :$inc => hourly_increment },
128
+ { :upsert => true }
129
+ )
130
+ end
131
+
132
+ def update_visit
133
+ Snowfinch::Collector.db["visits"].update(
134
+ {
135
+ "s" => site_id,
136
+ "v" => uuid,
137
+ "h" => { :$gt => (Time.now.to_i - 15 * 60) }
138
+ },
139
+ {
140
+ :$addToSet => { "p" => uri_hash },
141
+ :$set => { "h" => Time.now.to_i },
142
+ :$inc => { "c" => 1 }
143
+ },
144
+ { :upsert => true }
145
+ )
146
+ end
147
+
148
+ def update_visitor
149
+ Snowfinch::Collector.db["visitors"].update(
150
+ { "s" => site_id, "u" => uuid, "d" => time.to_date.to_s },
151
+ { :$inc => { "c" => 1 } },
152
+ { :upsert => 1 }
153
+ )
154
+ end
155
+
156
+ def update_sensors
157
+ matching_sensors.each do |id|
158
+ Snowfinch::Collector.db["sensor_counts"].update(
159
+ { "s" => site_id, "y" => time.year, "id" => id },
160
+ { :$inc => hourly_increment },
161
+ { :upsert => true }
162
+ )
163
+ end
164
+ end
165
+
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,5 @@
1
+ module Snowfinch
2
+ module Collector
3
+ VERSION = "0.5.0"
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "snowfinch/collector/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "snowfinch-collector"
7
+ s.version = Snowfinch::Collector::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Joao Carlos"]
10
+ s.email = ["mail@joao-carlos.com"]
11
+ s.homepage = "https://github.com/jcxplorer/snowfinch"
12
+ s.summary = %q{Snowfinch collector}
13
+ s.description = %q{Collector gem for snowfinch.}
14
+
15
+ s.rubyforge_project = "snowfinch-collector"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "mongo"
23
+ s.add_dependency "bson_ext"
24
+ s.add_dependency "rack"
25
+ s.add_dependency "radix62"
26
+ s.add_dependency "tzinfo"
27
+ end
@@ -0,0 +1,304 @@
1
+ require "spec_helper"
2
+
3
+ feature "Data collection" do
4
+
5
+ let(:homepage) { "http://snowfinch.net/" }
6
+
7
+ let :sites do
8
+ Snowfinch::Collector.db["sites"].find.to_a
9
+ end
10
+
11
+ let :site_counts do
12
+ Snowfinch::Collector.db["site_counts"].find.to_a
13
+ end
14
+
15
+ let :page_counts do
16
+ Snowfinch::Collector.db["page_counts"].find({}, :sort => "_id").to_a
17
+ end
18
+
19
+ let :sensor_counts do
20
+ Snowfinch::Collector.db["sensor_counts"].find({}, :sort => "_id").to_a
21
+ end
22
+
23
+ let :visits do
24
+ Snowfinch::Collector.db["visits"].find({}, :sort => "_id").to_a
25
+ end
26
+
27
+ let :visitors do
28
+ Snowfinch::Collector.db["visitors"].find({}, :sort => "_id").to_a
29
+ end
30
+
31
+ scenario "Multiple pageviews at different times" do
32
+ freeze_utc_time(2011, 2, 12, 7)
33
+ get path(:token => token, :uri => "http://snowfinch.net/posts")
34
+
35
+ freeze_utc_time(2011, 6, 4, 10)
36
+ get path(:token => token, :uri => "http://snowfinch.net/archive")
37
+
38
+ freeze_utc_time(2011, 6, 4, 15)
39
+ get path(:token => token, :uri => "http://snowfinch.net/")
40
+
41
+ freeze_utc_time(2011, 6, 4, 15)
42
+ get path(:token => token, :uri => "http://snowfinch.net/posts")
43
+
44
+ freeze_utc_time(2011, 6, 4, 15)
45
+ get path(:token => token, :uri => "http://snowfinch.net/posts")
46
+
47
+ freeze_utc_time(2012, 1, 1, 4)
48
+ get path(:token => token, :uri => "http://snowfinch.net/")
49
+
50
+ site_counts.count.should == 2
51
+ page_counts.count.should == 4
52
+
53
+ (site_counts + page_counts).each do |doc|
54
+ doc["s"].should == BSON::ObjectId(token)
55
+ end
56
+
57
+ site_counts[0]["y"].should == 2011
58
+ site_counts[0]["c"].should == 5
59
+ site_counts[0]["2"]["c"].should == 1
60
+ site_counts[0]["2"]["12"]["c"].should == 1
61
+ site_counts[0]["2"]["12"]["9"]["c"].should == 1
62
+ site_counts[0]["6"]["c"].should == 4
63
+ site_counts[0]["6"]["4"]["c"].should == 4
64
+ site_counts[0]["6"]["4"]["13"]["c"].should == 1
65
+ site_counts[0]["6"]["4"]["18"]["c"].should == 3
66
+
67
+ site_counts[1]["y"].should == 2012
68
+ site_counts[1]["c"].should == 1
69
+ site_counts[1]["1"]["c"].should == 1
70
+ site_counts[1]["1"]["1"]["c"].should == 1
71
+ site_counts[1]["1"]["1"]["6"]["c"].should == 1
72
+
73
+ page_counts[0]["u"].should == "http://snowfinch.net/posts"
74
+ page_counts[0]["h"].should == "rmT7mgGiXq6Hw8d6oApVW1AohTW"
75
+ page_counts[0]["y"].should == 2011
76
+ page_counts[0]["c"].should == 3
77
+ page_counts[0]["2"]["c"].should == 1
78
+ page_counts[0]["2"]["12"]["c"].should == 1
79
+ page_counts[0]["2"]["12"]["9"]["c"].should == 1
80
+ page_counts[0]["6"]["c"].should == 2
81
+ page_counts[0]["6"]["4"]["c"].should == 2
82
+ page_counts[0]["6"]["4"]["18"]["c"].should == 2
83
+
84
+ page_counts[1]["u"].should == "http://snowfinch.net/archive"
85
+ page_counts[1]["h"].should == "61EDJAzzvZOSNlIsLEaD3GRau2K"
86
+ page_counts[1]["y"].should == 2011
87
+ page_counts[1]["c"].should == 1
88
+ page_counts[1]["6"]["c"].should == 1
89
+ page_counts[1]["6"]["4"]["c"].should == 1
90
+ page_counts[1]["6"]["4"]
91
+ page_counts[1]["6"]["4"]["13"]["c"].should == 1
92
+
93
+ page_counts[2]["u"].should == "http://snowfinch.net/"
94
+ page_counts[2]["h"].should == "acrfzPFTC4qJH0SLjgo4611dj2h"
95
+ page_counts[2]["y"].should == 2011
96
+ page_counts[2]["c"].should == 1
97
+ page_counts[2]["6"]["c"].should == 1
98
+ page_counts[2]["6"]["4"]["c"].should == 1
99
+ page_counts[2]["6"]["4"]["18"]["c"].should == 1
100
+
101
+ page_counts[3]["u"].should == "http://snowfinch.net/"
102
+ page_counts[3]["h"].should == "acrfzPFTC4qJH0SLjgo4611dj2h"
103
+ page_counts[3]["y"].should == 2012
104
+ page_counts[3]["c"].should == 1
105
+ page_counts[3]["1"]["c"].should == 1
106
+ page_counts[3]["1"]["1"]["c"].should == 1
107
+ page_counts[3]["1"]["1"]["6"]["c"].should == 1
108
+ end
109
+
110
+ scenario "Multiple visits" do
111
+ archive = "http://snowfinch.net/archive"
112
+
113
+ freeze_utc_time(2011, 1, 1, 10, 0)
114
+ get path(:uuid => "A", :token => token, :uri => homepage)
115
+ get path(:uuid => "B", :token => token, :uri => homepage)
116
+
117
+ freeze_utc_time(2011, 1, 1, 10, 10)
118
+ get path(:uuid => "A", :token => token, :uri => homepage)
119
+
120
+ freeze_utc_time(2011, 1, 1, 10, 15)
121
+ get path(:uuid => "B", :token => token, :uri => homepage)
122
+
123
+ freeze_utc_time(2011, 1, 1, 10, 30)
124
+ get path(:uuid => "C", :token => token, :uri => homepage)
125
+
126
+ freeze_utc_time(2011, 1, 1, 10, 44, 59)
127
+ get path(:uuid => "C", :token => token, :uri => archive)
128
+
129
+ freeze_utc_time(2011, 1, 1, 10, 55)
130
+ get path(:uuid => "A", :token => token, :uri => homepage)
131
+
132
+ freeze_utc_time(2011, 1, 1, 11, 5)
133
+ get path(:uuid => "A", :token => token, :uri => homepage)
134
+
135
+ visits.count.should == 5
136
+ visits.each { |doc| doc["s"].should == BSON::ObjectId(token) }
137
+
138
+ visits[0]["p"].should == [Snowfinch::Collector.hash_uri(homepage)]
139
+ visits[0]["v"].should == "A"
140
+ visits[0]["h"].should == Time.utc(2011, 1, 1, 10, 10).to_i
141
+ visits[0]["c"].should == 2
142
+
143
+ visits[1]["p"].should == [Snowfinch::Collector.hash_uri(homepage)]
144
+ visits[1]["v"].should == "B"
145
+ visits[1]["h"].should == Time.utc(2011, 1, 1, 10, 0).to_i
146
+ visits[1]["c"].should == 1
147
+
148
+ visits[2]["p"].should == [Snowfinch::Collector.hash_uri(homepage)]
149
+ visits[2]["v"].should == "B"
150
+ visits[2]["h"].should == Time.utc(2011, 1, 1, 10, 15).to_i
151
+ visits[2]["c"].should == 1
152
+
153
+ pages = [homepage, archive].map { |uri| Snowfinch::Collector.hash_uri(uri) }
154
+ visits[3]["p"].should == pages
155
+ visits[3]["v"].should == "C"
156
+ visits[3]["h"].should == Time.utc(2011, 1, 1, 10, 44, 59).to_i
157
+ visits[3]["c"].should == 2
158
+
159
+ visits[4]["p"].should == [Snowfinch::Collector.hash_uri(homepage)]
160
+ visits[4]["v"].should == "A"
161
+ visits[4]["h"].should == Time.utc(2011, 1, 1, 11, 5).to_i
162
+ visits[4]["c"].should == 2
163
+ end
164
+
165
+ scenario "Multiple visitors" do
166
+ freeze_utc_time(2011, 1, 1, 23, 0)
167
+ get path(:token => token, :uuid => "A", :uri => homepage)
168
+
169
+ freeze_utc_time(2011, 1, 2)
170
+ get path(:token => token, :uuid => "A", :uri => homepage)
171
+
172
+ freeze_utc_time(2011, 1, 2)
173
+ get path(:token => token, :uuid => "B", :uri => homepage)
174
+
175
+ freeze_utc_time(2011, 1, 3)
176
+ get path(:token => token, :uuid => "A", :uri => homepage)
177
+
178
+ visitors.count.should == 3
179
+ visitors
180
+
181
+ visitors[0]["d"].should == "2011-01-02"
182
+ visitors[0]["u"].should == "A"
183
+ visitors[0]["c"].should == 2
184
+
185
+ visitors[1]["d"].should == "2011-01-02"
186
+ visitors[1]["u"].should == "B"
187
+ visitors[1]["c"].should == 1
188
+
189
+ visitors[2]["d"].should == "2011-01-03"
190
+ visitors[2]["u"].should == "A"
191
+ visitors[2]["c"].should == 1
192
+ end
193
+
194
+ scenario "Entries matching a query based sensor" do
195
+ campaign_uri = "http://snowfinch.net/?campaign=rr"
196
+ both_sensors_uri = "http://snowfinch.net/?campaign=rr&from=email"
197
+
198
+ campaign_sensor = {
199
+ "id" => 12,
200
+ "type" => "query",
201
+ "key" => "campaign",
202
+ "value" => "rr"
203
+ }
204
+
205
+ email_sensor = {
206
+ "id" => 24,
207
+ "type" => "query",
208
+ "key" => "from",
209
+ "value" => "email"
210
+ }
211
+
212
+ Snowfinch::Collector.db["sites"].update(
213
+ { "_id" => BSON::ObjectId(token) },
214
+ { :$set => { "sensors" => [campaign_sensor, email_sensor] } }
215
+ )
216
+
217
+ freeze_utc_time(2011, 11, 11, 20)
218
+ get path(:token => token, :uri => campaign_uri)
219
+
220
+ freeze_utc_time(2011, 11, 11, 20)
221
+ get path(:token => token, :uri => campaign_uri)
222
+
223
+ freeze_utc_time(2011, 11, 11, 21)
224
+ get path(:token => token, :uri => both_sensors_uri)
225
+
226
+ freeze_utc_time(2011, 11, 11, 21)
227
+ get path(:token => token, :uri => homepage)
228
+
229
+ sensor_counts.count.should == 2
230
+
231
+ sensor_counts[0]["s"].should == BSON::ObjectId(token)
232
+ sensor_counts[0]["id"].should == 12
233
+ sensor_counts[0]["y"].should == 2011
234
+ sensor_counts[0]["c"].should == 3
235
+ sensor_counts[0]["11"]["c"].should == 3
236
+ sensor_counts[0]["11"]["11"]["c"].should == 3
237
+ sensor_counts[0]["11"]["11"]["22"]["c"].should == 2
238
+ sensor_counts[0]["11"]["11"]["23"]["c"].should == 1
239
+
240
+ sensor_counts[1]["s"].should == BSON::ObjectId(token)
241
+ sensor_counts[1]["id"].should == 24
242
+ sensor_counts[1]["y"].should == 2011
243
+ sensor_counts[1]["c"].should == 1
244
+ sensor_counts[1]["11"]["c"].should == 1
245
+ sensor_counts[1]["11"]["11"]["c"].should == 1
246
+ sensor_counts[1]["11"]["11"]["23"]["c"].should == 1
247
+ end
248
+
249
+ scenario "Entries matching a referrer based sensor" do
250
+ facebook_referrer = "http://www.facebook.com/l.php"
251
+ twitter_referrer = "http://twitter.com/jcxplorer"
252
+ search_referrer = "http://duckduckgo.com/post.html"
253
+
254
+ social_sensor = {
255
+ "id" => 33,
256
+ "type" => "referrer",
257
+ "hosts" => ["facebook.com", "twitter.com"]
258
+ }
259
+
260
+ facebook_sensor = {
261
+ "id" => 46,
262
+ "type" => "referrer",
263
+ "hosts" => ["facebook.com"]
264
+ }
265
+
266
+ Snowfinch::Collector.db["sites"].update(
267
+ { "_id" => BSON::ObjectId(token) },
268
+ { :$set => { "sensors" => [social_sensor, facebook_sensor] } }
269
+ )
270
+
271
+ freeze_utc_time(2011, 8, 2, 5)
272
+ get path(:token => token, :uri => homepage, :referrer => twitter_referrer)
273
+
274
+ freeze_utc_time(2011, 8, 2, 5)
275
+ get path(:token => token, :uri => homepage, :referrer => search_referrer)
276
+
277
+ freeze_utc_time(2011, 8, 10, 12)
278
+ get path(:token => token, :uri => homepage, :referrer => facebook_referrer)
279
+
280
+ freeze_utc_time(2011, 8, 10, 12)
281
+ get path(:token => token, :uri => homepage, :referrer => twitter_referrer)
282
+
283
+ sensor_counts.count.should == 2
284
+
285
+ sensor_counts[0]["s"].should == BSON::ObjectId(token)
286
+ sensor_counts[0]["id"].should == 33
287
+ sensor_counts[0]["y"].should == 2011
288
+ sensor_counts[0]["c"].should == 3
289
+ sensor_counts[0]["8"]["c"].should == 3
290
+ sensor_counts[0]["8"]["2"]["c"].should == 1
291
+ sensor_counts[0]["8"]["2"]["8"]["c"].should == 1
292
+ sensor_counts[0]["8"]["10"]["c"].should == 2
293
+ sensor_counts[0]["8"]["10"]["15"]["c"].should == 2
294
+
295
+ sensor_counts[1]["s"].should == BSON::ObjectId(token)
296
+ sensor_counts[1]["id"].should == 46
297
+ sensor_counts[1]["y"].should == 2011
298
+ sensor_counts[1]["c"].should == 1
299
+ sensor_counts[1]["8"]["c"].should == 1
300
+ sensor_counts[1]["8"]["10"]["c"].should == 1
301
+ sensor_counts[1]["8"]["10"]["15"]["c"].should == 1
302
+ end
303
+
304
+ end
@@ -0,0 +1,30 @@
1
+ require "spec_helper"
2
+
3
+ feature "Response" do
4
+
5
+ scenario "GET request" do
6
+ get path(:token => token, :uri => "http://snowfinch.net/", :uuid => "CAFE")
7
+
8
+ empty_gif = "R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
9
+
10
+ last_response.status.should == 200
11
+ last_response.headers["Content-Type"].should == "image/gif"
12
+ Base64.encode64(last_response.body).strip.should == empty_gif
13
+ end
14
+
15
+ scenario "Invalid requests" do
16
+ get path_without_defaults
17
+ last_response.status.should == 400
18
+
19
+ get path_without_defaults(:token => token)
20
+ last_response.status.should == 400
21
+
22
+ get path_without_defaults(:token => token, :uri => "http://snowfinch.net/")
23
+ last_response.status.should == 400
24
+
25
+ get path_without_defaults(:token => token,
26
+ :uuid => "c2f6b003-e7e3-4bac-b69d-8b3d54ebab62")
27
+ last_response.status.should == 400
28
+ end
29
+
30
+ end
@@ -0,0 +1,76 @@
1
+ require "spec_helper"
2
+
3
+ describe Snowfinch::Collector do
4
+
5
+ describe ".db", :database => false do
6
+ context "default" do
7
+ it "returns a database 'snowfinch' with a default connection" do
8
+ connection = mock("Mongo::Connection")
9
+ database = mock("Mongo::Database")
10
+ Mongo::Connection.should_receive(:new).with(no_args).
11
+ and_return(connection)
12
+ connection.should_receive(:db).with("snowfinch").and_return(database)
13
+
14
+ Snowfinch::Collector.db.should == database
15
+ end
16
+
17
+ it "returns the same object accross calls" do
18
+ first_id = Snowfinch::Collector.db.object_id
19
+ second_id = Snowfinch::Collector.db.object_id
20
+
21
+ first_id.should == second_id
22
+ end
23
+ end
24
+ end
25
+
26
+ describe ".db=", :database => false do
27
+ it "sets the Mongo::Database object to be used" do
28
+ database = Mongo::Connection.new.db("snowfinch")
29
+ Snowfinch::Collector.db = database
30
+ Snowfinch::Collector.db.should == database
31
+ end
32
+ end
33
+
34
+ describe ".sanitize_uri" do
35
+ it "turns https into http" do
36
+ original_uri = "https://snowfinch.net/posts"
37
+ expected_uri = "http://snowfinch.net/posts"
38
+ Snowfinch::Collector.sanitize_uri(original_uri).should == expected_uri
39
+ end
40
+
41
+ it "removes www." do
42
+ original_uri = "http://www.snowfinch.net/archive"
43
+ expected_uri = "http://snowfinch.net/archive"
44
+ Snowfinch::Collector.sanitize_uri(original_uri).should == expected_uri
45
+ end
46
+
47
+ it "removes a slash at the end of the path" do
48
+ original_uri = "http://snowfinch.net/archive/"
49
+ expected_uri = "http://snowfinch.net/archive"
50
+ Snowfinch::Collector.sanitize_uri(original_uri).should == expected_uri
51
+ end
52
+
53
+ it "removes the query part" do
54
+ original_uri = "http://snowfinch.net/?source=google"
55
+ expected_uri = "http://snowfinch.net/"
56
+ Snowfinch::Collector.sanitize_uri(original_uri).should == expected_uri
57
+ end
58
+
59
+ it "removes the fragment part" do
60
+ original_uri = "http://snowfinch.net/about#contact"
61
+ expected_uri = "http://snowfinch.net/about"
62
+ Snowfinch::Collector.sanitize_uri(original_uri).should == expected_uri
63
+ end
64
+ end
65
+
66
+ describe ".hash_uri" do
67
+ it "returns a SHA1 hash of the URI in base 62" do
68
+ uri = "http://snowfinch.net/about"
69
+ Snowfinch::Collector.hash_uri(uri).should == "jjvMHRNTpBvTWe5Nm0YIjHufcdA"
70
+
71
+ uri = "http://snowfinch.net/"
72
+ Snowfinch::Collector.hash_uri(uri).should == "acrfzPFTC4qJH0SLjgo4611dj2h"
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,27 @@
1
+ require "bundler"
2
+ Bundler.require(:test)
3
+
4
+ require "snowfinch/collector"
5
+
6
+ Dir[File.expand_path("../support/**/*.rb", __FILE__)].each { |f| require f }
7
+
8
+ RSpec.configure do |config|
9
+ config.include Rack::Test::Methods
10
+
11
+ config.before(:each) do
12
+ Snowfinch::Collector.db = nil
13
+
14
+ unless example.metadata[:database] == false
15
+ Snowfinch::Collector.db = Mongo::Connection.new.db("snowfinch_test")
16
+ Snowfinch::Collector.db.collections.each do |collection|
17
+ collection.drop unless collection.name.match(/^system\./)
18
+ end
19
+
20
+ Snowfinch::Collector.db["sites"].insert({ "tz" => "Europe/Helsinki" })
21
+ end
22
+ end
23
+
24
+ config.after(:suite) do
25
+ Timecop.return
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ require "cgi"
2
+
3
+ def app
4
+ Snowfinch::Collector
5
+ end
6
+
7
+ def path_without_defaults(parts={})
8
+ "/?" + parts.each.map { |k,v| k.to_s + "=" + CGI.escape(v) }.join("&")
9
+ end
10
+
11
+ def path(parts={})
12
+ parts[:token] ||= token
13
+ parts[:uri] ||= "http://snowfinch.net/"
14
+ parts[:uuid] ||= "8e41cb1f-72f1-4ed5-bb29-84151737cc0a"
15
+ path_without_defaults(parts)
16
+ end
17
+
18
+ def token
19
+ Snowfinch::Collector.db["sites"].find_one["_id"].to_s
20
+ end
21
+
22
+ def freeze_utc_time(*args)
23
+ Timecop.freeze(Time.utc(*args))
24
+ end
@@ -0,0 +1,7 @@
1
+ class Mongo::Collection
2
+
3
+ def last
4
+ find_one({}, { :sort => [:$natural, :desc] })
5
+ end
6
+
7
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snowfinch-collector
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joao Carlos
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-05-05 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongo
16
+ requirement: &2158673220 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2158673220
25
+ - !ruby/object:Gem::Dependency
26
+ name: bson_ext
27
+ requirement: &2158672800 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2158672800
36
+ - !ruby/object:Gem::Dependency
37
+ name: rack
38
+ requirement: &2158672380 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2158672380
47
+ - !ruby/object:Gem::Dependency
48
+ name: radix62
49
+ requirement: &2158671940 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *2158671940
58
+ - !ruby/object:Gem::Dependency
59
+ name: tzinfo
60
+ requirement: &2158671480 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *2158671480
69
+ description: Collector gem for snowfinch.
70
+ email:
71
+ - mail@joao-carlos.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - .rspec
78
+ - Gemfile
79
+ - README.md
80
+ - Rakefile
81
+ - lib/snowfinch/collector.rb
82
+ - lib/snowfinch/collector/click.rb
83
+ - lib/snowfinch/collector/version.rb
84
+ - snowfinch-collector.gemspec
85
+ - spec/acceptance/data_collection_spec.rb
86
+ - spec/acceptance/response_spec.rb
87
+ - spec/snowfinch/collector_spec.rb
88
+ - spec/spec_helper.rb
89
+ - spec/support/helpers.rb
90
+ - spec/support/mongo_extensions.rb
91
+ homepage: https://github.com/jcxplorer/snowfinch
92
+ licenses: []
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project: snowfinch-collector
111
+ rubygems_version: 1.8.0
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Snowfinch collector
115
+ test_files:
116
+ - spec/acceptance/data_collection_spec.rb
117
+ - spec/acceptance/response_spec.rb
118
+ - spec/snowfinch/collector_spec.rb
119
+ - spec/spec_helper.rb
120
+ - spec/support/helpers.rb
121
+ - spec/support/mongo_extensions.rb