pwnalytics_client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.project ADDED
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>pwnalytics_client</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>com.aptana.ide.core.unifiedBuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ </buildSpec>
14
+ <natures>
15
+ <nature>com.aptana.ruby.core.rubynature</nature>
16
+ </natures>
17
+ </projectDescription>
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem "hashie", ">= 1.0.0"
6
+ gem "json", ">= 1.0.0"
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem "rspec", "~> 2.5.0"
12
+ gem "bundler", "~> 1.0.0"
13
+ gem "jeweler", "~> 1.5.2"
14
+ gem "rcov", ">= 0"
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ git (1.2.5)
6
+ hashie (1.0.0)
7
+ jeweler (1.5.2)
8
+ bundler (~> 1.0.0)
9
+ git (>= 1.2.5)
10
+ rake
11
+ json (1.5.1)
12
+ rake (0.8.7)
13
+ rcov (0.9.9)
14
+ rspec (2.5.0)
15
+ rspec-core (~> 2.5.0)
16
+ rspec-expectations (~> 2.5.0)
17
+ rspec-mocks (~> 2.5.0)
18
+ rspec-core (2.5.1)
19
+ rspec-expectations (2.5.0)
20
+ diff-lcs (~> 1.1.2)
21
+ rspec-mocks (2.5.0)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ bundler (~> 1.0.0)
28
+ hashie (>= 1.0.0)
29
+ jeweler (~> 1.5.2)
30
+ json (>= 1.0.0)
31
+ rcov
32
+ rspec (~> 2.5.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Victor Costan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,71 @@
1
+ = pwnalytics_client
2
+
3
+ Ruby gem providing a client for the Pwnalytics REST API.
4
+
5
+ == Usage
6
+
7
+ First, create a PwnalyticsClient pointing to your Pwnalytics instance:
8
+
9
+ client = PwnalyticsClient.new :host => 'localhost', :port => 3000,
10
+ :user => 'config', :password => 'vars'
11
+
12
+ Or, if you're using SSL:
13
+
14
+ client = PwnalyticsClient.new :host => 'localhost', :port => 3000,
15
+ :user => 'config', :password => 'vars',
16
+ :ssl => true,
17
+ :net_http => { :ca_path => '/etc/ssl/certs' }
18
+
19
+ Get the site that you want data for:
20
+
21
+ site = client.site('AA123456')
22
+
23
+ Or, if you don't know the site's UID, you can list all of them.
24
+
25
+ sites = client.sites
26
+
27
+ Finally, get the logged events for that site:
28
+
29
+ # Get everything.
30
+ events = site.events
31
+
32
+ # Restrict query to events with certain names.
33
+ events = site.events :names => ['page', 'unload']
34
+
35
+ Events have the same data as in the JSON output.
36
+
37
+ event.name # "page"
38
+ event.ip # "127.0.0.1"
39
+ event.browser_ua # "Mozilla/5.0 ..."
40
+ event.url # <URI::HTTP:0x7f6750505078 URL:http://localhost>
41
+ event.ref # <URI::HTTP:0x7f6750505078 URL:http://www.google.com>
42
+ event.data.your_property # "your_value"
43
+ event.pixels.document.width # 1680
44
+
45
+ The RSpec tests cover the entire API.
46
+
47
+ == Testing
48
+
49
+ Clone the Pwnalytics repository and run the Rails server locally.
50
+
51
+ git clone git://github.com/pwnall/pwnalytics.git
52
+ cd pwnalytics
53
+ bundle
54
+ rake db:create db:migrate db:seed
55
+ rails s
56
+
57
+ The specs assume a local server for integration testing.
58
+
59
+ == Contributing to pwnalytics_client
60
+
61
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
62
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
63
+ * Fork the project
64
+ * Start a feature/bugfix branch
65
+ * Commit and push until you are happy with your contribution
66
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
67
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
68
+
69
+ == Copyright
70
+
71
+ Copyright (c) 2011 Victor Costan. See LICENSE.txt for further details.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts 'Run `bundle install` to install missing gems'
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = 'pwnalytics_client'
16
+ gem.homepage = 'http://github.com/pwnall/pwnalytics_client'
17
+ gem.license = 'MIT'
18
+ gem.summary = %Q{Client library for pulling data from a Pwnalytics server}
19
+ gem.description = %Q{Client library for pulling data from a Pwnalytics server}
20
+ gem.email = 'victor@costan.us'
21
+ gem.authors = ['Victor Costan']
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ gem.add_runtime_dependency 'json', '>= 1.0.0'
25
+ gem.add_runtime_dependency 'hashie', '>= 1.0.0'
26
+
27
+ gem.add_development_dependency 'rspec', '~> 2.5.0'
28
+ gem.add_development_dependency 'bundler', '~> 1.0.0'
29
+ gem.add_development_dependency 'jeweler', '~> 1.5.2'
30
+ gem.add_development_dependency 'rcov', '>= 0'
31
+ end
32
+ Jeweler::RubygemsDotOrgTasks.new
33
+
34
+ require 'rspec/core'
35
+ require 'rspec/core/rake_task'
36
+ RSpec::Core::RakeTask.new(:spec) do |spec|
37
+ spec.pattern = FileList['spec/**/*_spec.rb']
38
+ end
39
+
40
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
41
+ spec.pattern = 'spec/**/*_spec.rb'
42
+ spec.rcov = true
43
+ end
44
+
45
+ task :default => :spec
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ''
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "pwnalytics_client #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,85 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'uri'
5
+
6
+
7
+ # Information about connecting to a Pwnalytics server.
8
+ class PwnalyticsClient
9
+ # New client connection to a Pwnalytics server.
10
+ #
11
+ # The options argument supports the following keys:
12
+ # :host:: the server hostname (e.g. "ticks.pwnb.us")
13
+ # :port:: the server port (defaults to 80 for http, 443 for https)
14
+ # :user:: the account username (e.g. "config")
15
+ # :password:: the account passowrd (e.g. "vars")
16
+ # :ssl:: if true, https will be used; make sure to populate the relevant
17
+ # net_http options (at the very least, you need ca_file or ca_path)
18
+ # :net_http:: properties to be set on the Net::HTTP connection object
19
+ def initialize(options)
20
+ unless @host = options[:host]
21
+ raise InvalidArgumentError, 'Missing server hostname'
22
+ end
23
+ unless @user = options[:user]
24
+ raise InvalidArgumentError, 'Missing account username'
25
+ end
26
+ unless @password = options[:password]
27
+ raise InvalidArgumentError, 'Missing account password'
28
+ end
29
+ @ssl = options[:ssl] ? true : false
30
+ @port = options[:port] || (@ssl ? 443 : 80)
31
+ @net_http = options[:net_http] || {}
32
+ end
33
+
34
+ # Pwnalytics server hostname.
35
+ attr_reader :host
36
+
37
+ # Pwnalytics server port.
38
+ attr_reader :port
39
+
40
+ # True if the connection to the Pwnalytics server is secured with SSL.
41
+ attr_reader :ssl
42
+
43
+ # Issues a low-level request to the Pwnalytics server, parses the JSON.
44
+ #
45
+ # Args:
46
+ # request:: the low-level request, e.g. '/web_properties.json'
47
+ #
48
+ # Returns the parsed JSON, as a Hash or Array.
49
+ #
50
+ # Raises IOError if the server's response indicates HTTP error.
51
+ def request(request)
52
+ http_request = Net::HTTP::Get.new request
53
+ http_request.initialize_http_header 'User-Agent' => 'Pwnalytics Client Gem'
54
+ http_request.basic_auth @user, @password
55
+
56
+ http = Net::HTTP.new @host, @port
57
+ if @ssl
58
+ http.use_ssl = true
59
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
60
+ end
61
+ @net_http.each { |key, value| http.send :"#{key}=", value }
62
+ response = http.request http_request
63
+ unless response.kind_of? Net::HTTPSuccess
64
+ raise IOError, "Unhappy HTTP response: #{response}\n"
65
+ end
66
+
67
+ JSON.parse response.body
68
+ end
69
+
70
+ # Returns an array of all Sites on this server.
71
+ def sites
72
+ request('/web_properties.json').map do |site|
73
+ Site.new self, site['uid'], site['name']
74
+ end
75
+ end
76
+
77
+ # Returns the Site with the given UID.
78
+ #
79
+ # This method does a server request, to get additional property data.
80
+ def property(uid)
81
+ request('/web_property/#{uid}.json').map do |site|
82
+ Site.new self, site['uid'], site['name']
83
+ end
84
+ end
85
+ end # class PwnalyticsClient
@@ -0,0 +1,62 @@
1
+ require 'hashie/mash'
2
+
3
+ # :nodoc: namespace
4
+ class PwnalyticsClient
5
+
6
+ # Information about an event logged on a Pwnalytics server.
7
+ class Event
8
+ # New wrapper for a site on a Pwnalytics server.
9
+ #
10
+ # This is called automatically by PwnalyticsClient in #sites and #site.
11
+ #
12
+ # Args:
13
+ # site:: the PwnalyticsClient::Site that the event occured on
14
+ # json_data:: a Hash containing parsed JSON data representing the event
15
+ def initialize(site, json_data)
16
+ @site = site
17
+
18
+ @name = json_data['name']
19
+ @url = self.class.parse_url json_data['page']
20
+ @ref = self.class.parse_url json_data['referrer']
21
+ if json_data['browser'] && json_data['browser']['time']
22
+ @time = Time.at(json_data['browser']['time'] / 1000.0)
23
+ else
24
+ @time = nil
25
+ end
26
+ @ip = json_data['ip']
27
+ @browser_ua = json_data['browser'] && json_data['browser']['ua']
28
+ @pixels = Hashie::Mash.new json_data['pixels']
29
+ @data = Hashie::Mash.new json_data['data']
30
+ end
31
+
32
+ # PwnalyticsClient::Site that the event occured on.
33
+ attr_reader :site
34
+
35
+ # Event name used for filtering in queries.
36
+ attr_reader :name
37
+ # URL of the page where the event was triggerred.
38
+ attr_reader :url
39
+ # Referer URL for the page where the event was triggerred.
40
+ attr_reader :ref
41
+ # When the event was triggerred.
42
+ attr_reader :time
43
+ # IP of the computer where the event was triggered (e.g., "127.0.0.1").
44
+ attr_reader :ip
45
+ # User-Agent string of the browser where the event was triggered.
46
+ attr_reader :browser_ua
47
+ # Screen metrics: screen size, window size and position.
48
+ attr_reader :pixels
49
+ # User-defined data associated with the event.
50
+ attr_reader :data
51
+
52
+ # Parses an URL out of a Pwnalytics server response.
53
+ def self.parse_url(json_data)
54
+ if json_data && json_data['url'] && json_data['url'] != 'null'
55
+ URI.parse json_data['url']
56
+ else
57
+ nil
58
+ end
59
+ end
60
+ end # class PwnalyticsClient::Event
61
+
62
+ end # namespace PwnalyticsClient
@@ -0,0 +1,43 @@
1
+ require 'cgi'
2
+
3
+ # :nodoc: namespace
4
+ class PwnalyticsClient
5
+
6
+ # Information about a site on a Pwnalytics server.
7
+ class Site
8
+ # New wrapper for a site on a Pwnalytics server.
9
+ #
10
+ # This is called automatically by PwnalyticsClient in #sites and #site.
11
+ #
12
+ # Args:
13
+ # client:: the PwnalyticsClient connection to be used for queries
14
+ # uid:: site's Pwnalytics UID
15
+ # name:: site's user-friendly name (optional)
16
+ def initialize(client, uid, name = 'unknown')
17
+ @client = client
18
+ @uid = uid
19
+ @name = name
20
+ end
21
+
22
+ # PwnalyticsClient connection used for queries.
23
+ attr_reader :client
24
+ # Pwnalytics UID for the site.
25
+ attr_reader :uid
26
+ # User-friendly name for the site
27
+ attr_reader :name
28
+
29
+ # Queries the Pwnalytics server for events matching some criteria.
30
+ #
31
+ # Options supports the following keys:
32
+ # :names:: only get events whose names are contained in this array
33
+ def events(options = {})
34
+ request = "/web_properties/#{@uid}/events.json?"
35
+ if options[:names]
36
+ request << options[:names].
37
+ map { |name| 'names%5B%5D=' + CGI.escape(name) }.join('&')
38
+ end
39
+ @client.request(request).map { |data| Event.new self, data }
40
+ end
41
+ end # class PwnalyticsClient::Site
42
+
43
+ end # namespace PwnalyticsClient
@@ -0,0 +1,3 @@
1
+ require 'pwnalytics_client/client.rb'
2
+ require 'pwnalytics_client/event.rb'
3
+ require 'pwnalytics_client/site.rb'
@@ -0,0 +1,98 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{pwnalytics_client}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Victor Costan"]
12
+ s.date = %q{2011-04-13}
13
+ s.description = %q{Client library for pulling data from a Pwnalytics server}
14
+ s.email = %q{victor@costan.us}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".project",
22
+ ".rspec",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "lib/pwnalytics_client.rb",
30
+ "lib/pwnalytics_client/client.rb",
31
+ "lib/pwnalytics_client/event.rb",
32
+ "lib/pwnalytics_client/site.rb",
33
+ "pwnalytics_client.gemspec",
34
+ "spec/lib/pwnalytics_client/client_spec.rb",
35
+ "spec/lib/pwnalytics_client/event_spec.rb",
36
+ "spec/lib/pwnalytics_client/site_spec.rb",
37
+ "spec/spec_helper.rb",
38
+ "spec/support/local_server.rb"
39
+ ]
40
+ s.homepage = %q{http://github.com/pwnall/pwnalytics_client}
41
+ s.licenses = ["MIT"]
42
+ s.require_paths = ["lib"]
43
+ s.rubygems_version = %q{1.7.2}
44
+ s.summary = %q{Client library for pulling data from a Pwnalytics server}
45
+ s.test_files = [
46
+ "spec/lib/pwnalytics_client/client_spec.rb",
47
+ "spec/lib/pwnalytics_client/event_spec.rb",
48
+ "spec/lib/pwnalytics_client/site_spec.rb",
49
+ "spec/spec_helper.rb",
50
+ "spec/support/local_server.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_runtime_dependency(%q<hashie>, [">= 1.0.0"])
58
+ s.add_runtime_dependency(%q<json>, [">= 1.0.0"])
59
+ s.add_development_dependency(%q<rspec>, ["~> 2.5.0"])
60
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
61
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
62
+ s.add_development_dependency(%q<rcov>, [">= 0"])
63
+ s.add_runtime_dependency(%q<json>, [">= 1.0.0"])
64
+ s.add_runtime_dependency(%q<hashie>, [">= 1.0.0"])
65
+ s.add_development_dependency(%q<rspec>, ["~> 2.5.0"])
66
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
67
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
68
+ s.add_development_dependency(%q<rcov>, [">= 0"])
69
+ else
70
+ s.add_dependency(%q<hashie>, [">= 1.0.0"])
71
+ s.add_dependency(%q<json>, [">= 1.0.0"])
72
+ s.add_dependency(%q<rspec>, ["~> 2.5.0"])
73
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
74
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
75
+ s.add_dependency(%q<rcov>, [">= 0"])
76
+ s.add_dependency(%q<json>, [">= 1.0.0"])
77
+ s.add_dependency(%q<hashie>, [">= 1.0.0"])
78
+ s.add_dependency(%q<rspec>, ["~> 2.5.0"])
79
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
80
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
81
+ s.add_dependency(%q<rcov>, [">= 0"])
82
+ end
83
+ else
84
+ s.add_dependency(%q<hashie>, [">= 1.0.0"])
85
+ s.add_dependency(%q<json>, [">= 1.0.0"])
86
+ s.add_dependency(%q<rspec>, ["~> 2.5.0"])
87
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
88
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
89
+ s.add_dependency(%q<rcov>, [">= 0"])
90
+ s.add_dependency(%q<json>, [">= 1.0.0"])
91
+ s.add_dependency(%q<hashie>, [">= 1.0.0"])
92
+ s.add_dependency(%q<rspec>, ["~> 2.5.0"])
93
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
94
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
95
+ s.add_dependency(%q<rcov>, [">= 0"])
96
+ end
97
+ end
98
+
@@ -0,0 +1,79 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
2
+
3
+ describe PwnalyticsClient do
4
+ describe 'with good data' do
5
+ let(:client) { PwnalyticsClient.new integration_server_data }
6
+
7
+ describe 'request with good data' do
8
+ let(:response) { client.request '/web_properties.json' }
9
+
10
+
11
+ it 'should return an array' do
12
+ response.should_not be_empty
13
+ end
14
+
15
+ let(:js_test) { response.find { |prop| prop['uid'] == 'AA123456' }}
16
+
17
+ it 'should return the test property' do
18
+ js_test.should_not be_nil
19
+ js_test['name'].should == 'Pwnalytics itself'
20
+ end
21
+ end
22
+
23
+ it 'should report the server hostname' do
24
+ client.host.should == integration_server_data[:host]
25
+ end
26
+ it 'should report the server port' do
27
+ client.port.should == integration_server_data[:port]
28
+ end
29
+ it 'should report whether it uses SSL' do
30
+ client.ssl.should == integration_server_data[:ssl]
31
+ end
32
+ end
33
+
34
+ describe 'with bad credentials' do
35
+ let(:client) do
36
+ PwnalyticsClient.new integration_server_data.merge(:user => 'nouser')
37
+ end
38
+
39
+ it 'should raise an exception on request with good data' do
40
+ lambda {
41
+ client.request '/web_properties.json'
42
+ }.should raise_error
43
+ end
44
+ end
45
+
46
+ describe 'with a bad server' do
47
+ let(:client) do
48
+ PwnalyticsClient.new integration_server_data.merge(:host => 'nohost')
49
+ end
50
+
51
+ it 'should raise an exception on request with good data' do
52
+ lambda {
53
+ client.request '/web_properties.json'
54
+ }.should raise_error
55
+ end
56
+ end
57
+
58
+ describe 'sites' do
59
+ let(:client) { PwnalyticsClient.new integration_server_data }
60
+
61
+ let(:result) do
62
+ client.should_receive(:request).with('/web_properties.json').
63
+ and_return([{'uid' => 'AA123456', 'name' => 'Pwnalytics itself'},
64
+ {'uid' => 'CDCDCDCD', 'name' => 'Test Property'}])
65
+ client.sites
66
+ end
67
+
68
+ it 'should map all items in the response' do
69
+ result.should have(2).sites
70
+ end
71
+
72
+ let(:js_test) { result.first }
73
+ it 'should map the test site correctly' do
74
+ js_test.client.should == client
75
+ js_test.uid.should == 'AA123456'
76
+ js_test.name.should == 'Pwnalytics itself'
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
2
+
3
+ describe PwnalyticsClient::Event do
4
+ let(:json_data) do
5
+ {'name' => 'page', 'referrer' => {'url' => 'http://google.com/'},'data' => {'pixie' => 'dust'},'pixels' => {'screen' => {'height' => 1440,'width' => 4240},'document' => {'height' => 863,'width' => 1680},'window' => {'x' => 2215,'y' => 181}},'id' => 1,'visitor' => {'uid' => '12f49459540.11faa9b963f5c4f0'},'page' => {'url' => 'http://localhost:3000/'},'ip' => '127.0.0.1', 'browser' => {'ua' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.204 Chrome/10.0.648.204 Safari/534.16', 'time' => 1302682242437}}
6
+ end
7
+ let(:client) { PwnalyticsClient.new integration_server_data }
8
+ let(:site) { PwnalyticsClient::Site.new client, 'AA', 'Test'}
9
+ let(:event) { PwnalyticsClient::Event.new site, json_data }
10
+
11
+ it 'should report the Pwnalytics site' do
12
+ event.site.should == site
13
+ end
14
+
15
+ it 'should parse out the name' do
16
+ event.name.should == 'page'
17
+ end
18
+
19
+ it 'should parse out the page URL' do
20
+ event.url.should == URI.parse('http://localhost:3000/')
21
+ end
22
+
23
+ it 'should parse out the referrer URL' do
24
+ event.ref.should == URI.parse('http://google.com/')
25
+ end
26
+
27
+ it 'should parse out null URLs' do
28
+ PwnalyticsClient::Event.new(site, json_data.
29
+ merge('page' => { 'url' => 'null'})).url.should == nil
30
+ PwnalyticsClient::Event.new(site, json_data.
31
+ merge('page' => {})).url.should == nil
32
+ PwnalyticsClient::Event.new(site, json_data.
33
+ merge('page' => nil)).url.should == nil
34
+ end
35
+
36
+ it 'should parse out the time' do
37
+ event.time.gmtime.to_s.should == "Wed Apr 13 08:10:42 UTC 2011"
38
+ end
39
+
40
+ it 'should parse out the IP' do
41
+ event.ip.should == '127.0.0.1'
42
+ end
43
+
44
+ it 'should parse out the User-Agent string' do
45
+ event.browser_ua.should match(/WebKit/)
46
+ end
47
+
48
+ it 'should parse screen metric data' do
49
+ event.pixels.screen.width.should == 4240
50
+ event.pixels.screen.height.should == 1440
51
+ event.pixels.document.width.should == 1680
52
+ event.pixels.document.height.should == 863
53
+ event.pixels.window.x.should == 2215
54
+ event.pixels.window.y.should == 181
55
+ end
56
+
57
+ it 'should parse out additional data' do
58
+ event.data.should eq({'pixie' => 'dust'})
59
+ end
60
+
61
+ it 'should have syntactic sugar on the additional data' do
62
+ event.data.pixie.should == 'dust'
63
+ end
64
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
2
+
3
+ describe PwnalyticsClient::Site do
4
+ let(:client) { PwnalyticsClient.new integration_server_data }
5
+ let(:site) { PwnalyticsClient::Site.new client, 'AA', 'Test'}
6
+
7
+ describe 'events' do
8
+ it 'should request all events by default' do
9
+ client.should_receive(:request).
10
+ with('/web_properties/AA/events.json?').and_return({})
11
+ site.events.should be_empty
12
+ end
13
+
14
+ it 'should encode name filter if given' do
15
+ client.should_receive(:request).
16
+ with('/web_properties/AA/events.json?names%5B%5D=AB&names%5B%5D=C+D').
17
+ and_return({})
18
+ site.events(:names => ['AB', 'C D']).should be_empty
19
+ end
20
+
21
+ let(:result) do
22
+ client.should_receive(:request).with('/web_properties/AA/events.json?').
23
+ and_return([{'name' => 'page', 'referrer' => {'url' => 'null'},'data' => {},'pixels' => {'screen' => {'height' => 1440,'width' => 4240},'document' => {'height' => 863,'width' => 1680},'window' => {'x' => 2215,'y' => 181}},'id' => 1,'visitor' => {'uid' => '12f49459540.11faa9b963f5c4f0'},'page' => {'url' => 'http://localhost:3000/'},'ip' => '127.0.0.1', 'browser' => {'ua' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.204 Chrome/10.0.648.204 Safari/534.16', 'time' => 1302682242437}},
24
+ {'name' => 'unload', 'referrer' => {'url' => 'null'},'data' => {},'pixels' => {'screen' => {'height' => 1440,'width' => 4240},'document' => {'height' => 863,'width' => 1680},'window' => {'x' => 2215,'y' => 181}},'id' => 2,'visitor' => {'uid' => '12f49459540.11faa9b963f5c4f0'},'page' => {'url' => 'http://localhost:3000/'},'ip' => '127.0.0.1', 'browser' => {'ua' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.648.204 Chrome/10.0.648.204 Safari/534.16', 'time' => 1302682245565}}])
25
+ site.events
26
+ end
27
+
28
+ it 'should map all results to events' do
29
+ result.should have(2).events
30
+ end
31
+
32
+ it 'should map events correctly' do
33
+ result.first.name.should == 'page'
34
+ result.first.site.should == site
35
+ result.last.name.should == 'unload'
36
+ result.last.site.should == site
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'pwnalytics_client'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,5 @@
1
+ # Connection details for the local server used in tests.
2
+ def integration_server_data
3
+ { :host => 'localhost', :port => 3000, :ssl => false,
4
+ :user => 'config', :password => 'vars' }
5
+ end
metadata ADDED
@@ -0,0 +1,275 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pwnalytics_client
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Victor Costan
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-13 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ requirement: &id001 !ruby/object:Gem::Requirement
22
+ none: false
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ hash: 23
27
+ segments:
28
+ - 1
29
+ - 0
30
+ - 0
31
+ version: 1.0.0
32
+ version_requirements: *id001
33
+ name: hashie
34
+ prerelease: false
35
+ type: :runtime
36
+ - !ruby/object:Gem::Dependency
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 23
43
+ segments:
44
+ - 1
45
+ - 0
46
+ - 0
47
+ version: 1.0.0
48
+ version_requirements: *id002
49
+ name: json
50
+ prerelease: false
51
+ type: :runtime
52
+ - !ruby/object:Gem::Dependency
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ hash: 27
59
+ segments:
60
+ - 2
61
+ - 5
62
+ - 0
63
+ version: 2.5.0
64
+ version_requirements: *id003
65
+ name: rspec
66
+ prerelease: false
67
+ type: :development
68
+ - !ruby/object:Gem::Dependency
69
+ requirement: &id004 !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ~>
73
+ - !ruby/object:Gem::Version
74
+ hash: 23
75
+ segments:
76
+ - 1
77
+ - 0
78
+ - 0
79
+ version: 1.0.0
80
+ version_requirements: *id004
81
+ name: bundler
82
+ prerelease: false
83
+ type: :development
84
+ - !ruby/object:Gem::Dependency
85
+ requirement: &id005 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ hash: 7
91
+ segments:
92
+ - 1
93
+ - 5
94
+ - 2
95
+ version: 1.5.2
96
+ version_requirements: *id005
97
+ name: jeweler
98
+ prerelease: false
99
+ type: :development
100
+ - !ruby/object:Gem::Dependency
101
+ requirement: &id006 !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 3
107
+ segments:
108
+ - 0
109
+ version: "0"
110
+ version_requirements: *id006
111
+ name: rcov
112
+ prerelease: false
113
+ type: :development
114
+ - !ruby/object:Gem::Dependency
115
+ requirement: &id007 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ hash: 23
121
+ segments:
122
+ - 1
123
+ - 0
124
+ - 0
125
+ version: 1.0.0
126
+ version_requirements: *id007
127
+ name: json
128
+ prerelease: false
129
+ type: :runtime
130
+ - !ruby/object:Gem::Dependency
131
+ requirement: &id008 !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ hash: 23
137
+ segments:
138
+ - 1
139
+ - 0
140
+ - 0
141
+ version: 1.0.0
142
+ version_requirements: *id008
143
+ name: hashie
144
+ prerelease: false
145
+ type: :runtime
146
+ - !ruby/object:Gem::Dependency
147
+ requirement: &id009 !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ~>
151
+ - !ruby/object:Gem::Version
152
+ hash: 27
153
+ segments:
154
+ - 2
155
+ - 5
156
+ - 0
157
+ version: 2.5.0
158
+ version_requirements: *id009
159
+ name: rspec
160
+ prerelease: false
161
+ type: :development
162
+ - !ruby/object:Gem::Dependency
163
+ requirement: &id010 !ruby/object:Gem::Requirement
164
+ none: false
165
+ requirements:
166
+ - - ~>
167
+ - !ruby/object:Gem::Version
168
+ hash: 23
169
+ segments:
170
+ - 1
171
+ - 0
172
+ - 0
173
+ version: 1.0.0
174
+ version_requirements: *id010
175
+ name: bundler
176
+ prerelease: false
177
+ type: :development
178
+ - !ruby/object:Gem::Dependency
179
+ requirement: &id011 !ruby/object:Gem::Requirement
180
+ none: false
181
+ requirements:
182
+ - - ~>
183
+ - !ruby/object:Gem::Version
184
+ hash: 7
185
+ segments:
186
+ - 1
187
+ - 5
188
+ - 2
189
+ version: 1.5.2
190
+ version_requirements: *id011
191
+ name: jeweler
192
+ prerelease: false
193
+ type: :development
194
+ - !ruby/object:Gem::Dependency
195
+ requirement: &id012 !ruby/object:Gem::Requirement
196
+ none: false
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ hash: 3
201
+ segments:
202
+ - 0
203
+ version: "0"
204
+ version_requirements: *id012
205
+ name: rcov
206
+ prerelease: false
207
+ type: :development
208
+ description: Client library for pulling data from a Pwnalytics server
209
+ email: victor@costan.us
210
+ executables: []
211
+
212
+ extensions: []
213
+
214
+ extra_rdoc_files:
215
+ - LICENSE.txt
216
+ - README.rdoc
217
+ files:
218
+ - .document
219
+ - .project
220
+ - .rspec
221
+ - Gemfile
222
+ - Gemfile.lock
223
+ - LICENSE.txt
224
+ - README.rdoc
225
+ - Rakefile
226
+ - VERSION
227
+ - lib/pwnalytics_client.rb
228
+ - lib/pwnalytics_client/client.rb
229
+ - lib/pwnalytics_client/event.rb
230
+ - lib/pwnalytics_client/site.rb
231
+ - pwnalytics_client.gemspec
232
+ - spec/lib/pwnalytics_client/client_spec.rb
233
+ - spec/lib/pwnalytics_client/event_spec.rb
234
+ - spec/lib/pwnalytics_client/site_spec.rb
235
+ - spec/spec_helper.rb
236
+ - spec/support/local_server.rb
237
+ homepage: http://github.com/pwnall/pwnalytics_client
238
+ licenses:
239
+ - MIT
240
+ post_install_message:
241
+ rdoc_options: []
242
+
243
+ require_paths:
244
+ - lib
245
+ required_ruby_version: !ruby/object:Gem::Requirement
246
+ none: false
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ hash: 3
251
+ segments:
252
+ - 0
253
+ version: "0"
254
+ required_rubygems_version: !ruby/object:Gem::Requirement
255
+ none: false
256
+ requirements:
257
+ - - ">="
258
+ - !ruby/object:Gem::Version
259
+ hash: 3
260
+ segments:
261
+ - 0
262
+ version: "0"
263
+ requirements: []
264
+
265
+ rubyforge_project:
266
+ rubygems_version: 1.7.2
267
+ signing_key:
268
+ specification_version: 3
269
+ summary: Client library for pulling data from a Pwnalytics server
270
+ test_files:
271
+ - spec/lib/pwnalytics_client/client_spec.rb
272
+ - spec/lib/pwnalytics_client/event_spec.rb
273
+ - spec/lib/pwnalytics_client/site_spec.rb
274
+ - spec/spec_helper.rb
275
+ - spec/support/local_server.rb