snowfinch-collector 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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