gamercard 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format nested
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use --create 1.9.3@gamercard_gem
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gamercard.gemspec
4
+ gemspec
5
+
6
+ # Guard & Devtools
7
+ gem 'guard', '~> 0.8.8'
8
+ # MacOS notifications:
9
+ gem 'rb-fsevent', :require => ('rb-fsevent' if RUBY_PLATFORM =~ /darwin/i)
10
+ gem 'growl', :require => ('growl' if RUBY_PLATFORM =~ /darwin/i)
11
+ gem 'guard-bundler'
12
+ gem 'guard-rspec'
@@ -0,0 +1,10 @@
1
+ guard 'bundler' do
2
+ watch('Gemfile')
3
+ watch(/^.+\.gemspec/)
4
+ end
5
+
6
+ guard 'rspec', :version => 2, :cli => '--color --format nested', :all_on_start => true, :all_after_pass => true do
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ watch(%r{^spec/.+_spec\.rb$})
9
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Matt Wilson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,134 @@
1
+ # Gamercard
2
+
3
+ Retrieves and parses an Xbox Live Gamercard for a player, providing a hash of the relevant data about the player or the raw HTML.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'gamercard'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install gamercard
18
+
19
+ ## Usage
20
+
21
+ Returns a hash suitable for JSON serialization.
22
+
23
+ ```ruby
24
+ resp = Gamercard.get('laikal1')
25
+ pp resp
26
+ # =>
27
+ {"gamertag"=>"laikal1",
28
+ "link"=>"http://live.xbox.com/en-US/Profile?Gamertag=laikal1",
29
+ "gamerpic"=>"http://avatar.xboxlive.com/avatar/laikal1/avatarpic-l.png",
30
+ "gamerscore"=>49646,
31
+ "location"=>"Ann Arbor, MI",
32
+ "motto"=>"Complexify!",
33
+ "name"=>"Nyarlathotep (1 bucket)",
34
+ "bio"=>"Design is how it works.",
35
+ "reputation"=>4.25,
36
+ "recent_games"=>
37
+ [{"comparison_url"=>
38
+ "http://live.xbox.com/en-US/Activity/Details?titleId=1161890110&compareTo=laikal1",
39
+ "image"=>
40
+ "http://tiles.xbox.com/tiles/9a/SL/0mdsb2JhbA9ECgQNGwEfV1wmL2ljb24vMC84MDAwIAAAAAAAAP2kpOo=.jpg",
41
+ "title"=>"KoA: Reckoning",
42
+ "last_played"=>"3/29/2012",
43
+ "earned_gamerscore"=>10,
44
+ "available_gamerscore"=>1300,
45
+ "earned_achievements"=>1,
46
+ "available_achievements"=>61,
47
+ "percentage_complete"=>"1%"},
48
+ {"comparison_url"=>
49
+ "http://live.xbox.com/en-US/Activity/Details?titleId=1161890141&compareTo=laikal1",
50
+ "image"=>
51
+ "http://tiles.xbox.com/tiles/np/G2/12dsb2JhbA9ECgQNGwEfV1onL2ljb24vMC84MDAwIAAAAAAAAPiZkYE=.jpg",
52
+ "title"=>"Mass Effect 3",
53
+ "last_played"=>"3/28/2012",
54
+ "earned_gamerscore"=>1025,
55
+ "available_gamerscore"=>1050,
56
+ "earned_achievements"=>51,
57
+ "available_achievements"=>52,
58
+ "percentage_complete"=>"98%"},
59
+ {"comparison_url"=>
60
+ "http://live.xbox.com/en-US/Activity/Details?titleId=1161890083&compareTo=laikal1",
61
+ "image"=>
62
+ "http://tiles.xbox.com/tiles/WQ/nz/0Wdsb2JhbA9ECgQNGwEfV11QL2ljb24vMC84MDAwIAAAAAAAAP7cCUY=.jpg",
63
+ "title"=>"Syndicate",
64
+ "last_played"=>"3/11/2012",
65
+ "earned_gamerscore"=>530,
66
+ "available_gamerscore"=>1000,
67
+ "earned_achievements"=>33,
68
+ "available_achievements"=>50,
69
+ "percentage_complete"=>"66%"},
70
+ {"comparison_url"=>
71
+ "http://live.xbox.com/en-US/Activity/Details?titleId=1414793176&compareTo=laikal1",
72
+ "image"=>
73
+ "http://tiles.xbox.com/tiles/fc/qf/0Wdsb2JhbA9ECgUMGgQfWStbL2ljb24vMC84MDAwIAAAAAAAAP6wymI=.jpg",
74
+ "title"=>"BioShock",
75
+ "last_played"=>"2/25/2012",
76
+ "earned_gamerscore"=>1100,
77
+ "available_gamerscore"=>1100,
78
+ "earned_achievements"=>51,
79
+ "available_achievements"=>51,
80
+ "percentage_complete"=>"100%"},
81
+ {"comparison_url"=>
82
+ "http://live.xbox.com/en-US/Activity/Details?titleId=1112737766&compareTo=laikal1",
83
+ "image"=>
84
+ "http://tiles.xbox.com/tiles/qR/QI/1Gdsb2JhbA9ECgQKGgMfWSpVL2ljb24vMC84MDAwIAAAAAAAAPsnFLY=.jpg",
85
+ "title"=>"Skyrim",
86
+ "last_played"=>"2/14/2012",
87
+ "earned_gamerscore"=>910,
88
+ "available_gamerscore"=>1000,
89
+ "earned_achievements"=>47,
90
+ "available_achievements"=>50,
91
+ "percentage_complete"=>"94%"}]}
92
+ ```
93
+
94
+ ### Configuration
95
+
96
+ Gamercard uses [Faraday](https://github.com/technoweenie/faraday), so you can use any http backend that Faraday supports. It assumes [Typhoeus](https://github.com/dbalatero/typhoeus) by default. Tell Gamercard what http library you are using by:
97
+
98
+ ```ruby
99
+ Gamercard.adapter = :excon # (or :typhoeus, w/e)
100
+ ```
101
+
102
+ A block-style configurator is also available:
103
+
104
+ ```ruby
105
+ Gamercard.configure do |c|
106
+ c.adapter = :typhoeus
107
+ c.user_agent = "My Awesome Client"
108
+ end
109
+ ```
110
+
111
+ ## CHANGELOG
112
+
113
+ * v0.0.1 Hello World
114
+ * Returns all the basic gamercard data, sans status. Status can be had from a public site, though, so that is next!
115
+
116
+ ## Thanks to
117
+
118
+ This project builds on work done by the following people and projects:
119
+
120
+ * [barisbalic](https://github.com/barisbalic)'s [gamertag](https://github.com/barisbalic/gamertag), which depends on unfortunately unreliable 3rd-party proxies to Microsoft's service. This gem provides less data, but scrapes it directly from http://live.xbox.com – Straight from the horse's mouth, if you will.
121
+
122
+ So: thanks a ton to him.
123
+
124
+ ## Note on Patches/Pull Requests
125
+
126
+ * Fork the project.
127
+ * Make your feature addition or bug fix.
128
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
129
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
130
+ * Send me a pull request. Bonus points for topic branches.
131
+
132
+ ## Copyright
133
+
134
+ Copyright (c) 2011 Matt Wilson. See LICENSE for details.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/gamercard/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.authors = ["Matt Wilson"]
6
+ s.email = ["mhw@hypomodern.com"]
7
+ s.description = %q{A Simple & Straightforward XBox Live Gamercard parser.}
8
+ s.summary = %q{A Simple & Straightforward XBox Live Gamercard parser.}
9
+ s.homepage = "https://github.com/hypomodern/gamercard"
10
+
11
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ s.name = "gamercard"
15
+ s.require_paths = ["lib"]
16
+ s.version = Gamercard::VERSION
17
+
18
+ s.add_development_dependency 'rake'
19
+ s.add_development_dependency 'rspec'
20
+ s.add_development_dependency 'typhoeus'
21
+ s.add_development_dependency 'vcr'
22
+ s.add_development_dependency 'fakeweb'
23
+
24
+ s.add_dependency 'nokogiri'
25
+ s.add_dependency 'faraday', '>= 0.8.0.rc2'
26
+ end
@@ -0,0 +1,12 @@
1
+ require "gamercard/version"
2
+ require "gamercard/configuration"
3
+ require "gamercard/client"
4
+ require "gamercard/card_parser"
5
+
6
+ module Gamercard
7
+ extend Configuration
8
+
9
+ def self.get gamertag
10
+ Client.new(gamertag).fetch
11
+ end
12
+ end
@@ -0,0 +1,69 @@
1
+ require 'nokogiri'
2
+
3
+ module Gamercard
4
+ class CardParser
5
+ attr_accessor :raw_response
6
+
7
+ def initialize http_response
8
+ self.raw_response = http_response
9
+ end
10
+
11
+ def self.parse http_response
12
+ new(http_response).parse
13
+ end
14
+
15
+ def parse
16
+ doc = Nokogiri::HTML(raw_response.body)
17
+ gamertag = doc.css("#Gamertag").first
18
+ {
19
+ 'gamertag' => gamertag.content,
20
+ 'link' => gamertag.attribute("href").value,
21
+ 'gamerpic' => doc.css("#Gamerpic").first.attribute("src").value,
22
+ 'gamerscore' => doc.css("#Gamerscore").first.content.to_i,
23
+ 'location' => doc.css("#Location").first.content,
24
+ 'motto' => doc.css("#Motto").first.content,
25
+ 'name' => doc.css("#Name").first.content,
26
+ 'bio' => doc.css("#Bio").first.content,
27
+ 'reputation' => count_reputation(doc),
28
+ 'recent_games' => summarize_games(doc)
29
+ }
30
+ end
31
+
32
+ def count_reputation doc
33
+ rep = doc.css('.RepContainer')
34
+ counts = {
35
+ 1.0 => rep.css('.Full').size,
36
+ 0.75 => rep.css('.ThreeQuarter').size,
37
+ 0.5 => rep.css('.Half').size,
38
+ 0.25 => rep.css('.Quarter').size
39
+ }
40
+ counts.inject(0) do |rep, (value, count)|
41
+ rep += count * value
42
+ rep
43
+ end
44
+ end
45
+
46
+ def summarize_games doc
47
+ doc.css('#PlayedGames li').inject([]) do |array, game_node|
48
+ array.push summarize(game_node)
49
+ array
50
+ end
51
+ end
52
+
53
+ def summarize game
54
+ link = game.css('a').first
55
+ {
56
+ 'comparison_url' => link.attribute('href').value,
57
+ 'image' => link.css('img').first.attribute('src').value,
58
+ 'title' => link.css('.Title').first.content,
59
+ 'last_played' => link.css('.LastPlayed').first.content,
60
+ 'earned_gamerscore' => link.css('.EarnedGamerscore').first.content.to_i,
61
+ 'available_gamerscore' => link.css('.AvailableGamerscore').first.content.to_i,
62
+ 'earned_achievements' => link.css('.EarnedAchievements').first.content.to_i,
63
+ 'available_achievements' => link.css('.AvailableAchievements').first.content.to_i,
64
+ 'percentage_complete' => link.css('.PercentageComplete').first.content
65
+ }
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,45 @@
1
+ require 'faraday'
2
+
3
+ module Gamercard
4
+ class Client
5
+ attr_accessor :gamertag
6
+
7
+ GAMERCARD_SERVICE_URL = "http://gamercard.xbox.com"
8
+
9
+ def initialize gamertag
10
+ self.gamertag = gamertag
11
+ end
12
+
13
+ def fetch
14
+ response = fetch_with_redirect
15
+ if response.success?
16
+ parse response
17
+ else
18
+ response
19
+ end
20
+ end
21
+
22
+ def fetch_with_redirect
23
+ resp = connection.get(build_url)
24
+ if resp.status == 302 && resp['location']
25
+ resp = connection.get(resp['location'])
26
+ end
27
+ resp
28
+ end
29
+
30
+ def connection
31
+ @connection ||= Faraday.new(:url => GAMERCARD_SERVICE_URL) do |b|
32
+ b.adapter Gamercard.adapter
33
+ end
34
+ end
35
+
36
+ def build_url
37
+ "#{gamertag}.card"
38
+ end
39
+
40
+ def parse response
41
+ CardParser.parse response
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ module Gamercard
2
+ module Configuration
3
+ # Set a custom User-Agent string (default: Gamercard Gem #{version})
4
+ attr_accessor :user_agent
5
+
6
+ # Select a different http library for Faraday to use (default: Typhoeus)
7
+ attr_accessor :adapter
8
+
9
+ # Yield self to be able to configure Gamercard
10
+ #
11
+ # Example:
12
+ #
13
+ # Gamercard.configure do |c|
14
+ # c.user_agent = "MyCoolApp Gamercard Client v2.51"
15
+ # c.adapter = :excon
16
+ # end
17
+ def configure
18
+ yield self
19
+ end
20
+
21
+ def user_agent
22
+ @user_agent || "Gamercard Gem #{Gamercard::VERSION}"
23
+ end
24
+
25
+ def adapter
26
+ @adapter || :typhoeus
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Gamercard
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,215 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://gamercard.xbox.com/laikal1.card
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers: {}
10
+ response:
11
+ status:
12
+ code: 302
13
+ message:
14
+ headers:
15
+ !binary "Q2FjaGUtQ29udHJvbA==":
16
+ - !binary |-
17
+ cHJpdmF0ZQ==
18
+ !binary "TG9jYXRpb24=":
19
+ - !binary |-
20
+ aHR0cDovL2dhbWVyY2FyZC54Ym94LmNvbTo4MC9lbi1VUy9sYWlrYWwxLmNh
21
+ cmQ=
22
+ !binary "WC1Db250ZW50LVR5cGUtT3B0aW9ucw==":
23
+ - !binary |-
24
+ bm9zbmlmZg==
25
+ !binary "Q29udGVudC1MZW5ndGg=":
26
+ - !binary |-
27
+ MA==
28
+ !binary "RGF0ZQ==":
29
+ - !binary |-
30
+ U3VuLCAwMSBBcHIgMjAxMiAyMjo1MjowMCBHTVQ=
31
+ !binary "Q29ubmVjdGlvbg==":
32
+ - !binary |-
33
+ a2VlcC1hbGl2ZQ==
34
+ body:
35
+ encoding: ASCII-8BIT
36
+ string: !binary ""
37
+ http_version:
38
+ recorded_at: Sun, 01 Apr 2012 22:52:00 GMT
39
+ - request:
40
+ method: get
41
+ uri: http://gamercard.xbox.com/en-US/laikal1.card
42
+ body:
43
+ encoding: US-ASCII
44
+ string: ''
45
+ headers: {}
46
+ response:
47
+ status:
48
+ code: 200
49
+ message:
50
+ headers:
51
+ !binary "Q2FjaGUtQ29udHJvbA==":
52
+ - !binary |-
53
+ cHJpdmF0ZQ==
54
+ !binary "Q29udGVudC1UeXBl":
55
+ - !binary |-
56
+ dGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04
57
+ !binary "WC1Db250ZW50LVR5cGUtT3B0aW9ucw==":
58
+ - !binary |-
59
+ bm9zbmlmZg==
60
+ !binary "VmFyeQ==":
61
+ - !binary |-
62
+ QWNjZXB0LUVuY29kaW5n
63
+ !binary "Q29udGVudC1FbmNvZGluZw==":
64
+ - !binary |-
65
+ Z3ppcA==
66
+ !binary "Q29udGVudC1MZW5ndGg=":
67
+ - !binary |-
68
+ MTIwNQ==
69
+ !binary "RGF0ZQ==":
70
+ - !binary |-
71
+ U3VuLCAwMSBBcHIgMjAxMiAyMjo1MjowMSBHTVQ=
72
+ !binary "Q29ubmVjdGlvbg==":
73
+ - !binary |-
74
+ a2VlcC1hbGl2ZQ==
75
+ body:
76
+ encoding: ASCII-8BIT
77
+ string: !binary |-
78
+ DQo8IURPQ1RZUEUgaHRtbCBQVUJMSUMgIi0vL1czQy8vRFREIFhIVE1MIDEu
79
+ MCBUcmFuc2l0aW9uYWwvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIveGh0
80
+ bWwxL0RURC94aHRtbDEtdHJhbnNpdGlvbmFsLmR0ZCI+DQo8aHRtbCB4bWxu
81
+ cz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgbGFuZz0iZW5nIj4N
82
+ CiAgICA8aGVhZD4NCiAgICAgICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiB0
83
+ eXBlPSJ0ZXh0L2NzcyIgaHJlZj0iL0NvbnRlbnQvR2FtZXJjYXJkL2RlZmF1
84
+ bHQvZ2FtZXJjYXJkLmNzcyIgLz4NCiAgICAgICAgPG1ldGEgbmFtZT0icm9i
85
+ b3RzIiBjb250ZW50PSJub2luZGV4LG5vZm9sbG93IiAvPg0KICAgICAgICA8
86
+ bWV0YSBuYW1lPSJjYXRlZ29yeSIgY29udGVudD0iR2FtZXJjYXJkIiAvPg0K
87
+ ICAgICAgICA8YmFzZSB0YXJnZXQ9Il90b3AiIC8+DQogICAgICAgIDx0aXRs
88
+ ZT5sYWlrYWwxPC90aXRsZT4NCiAgICA8L2hlYWQ+DQogICAgPGJvZHk+DQog
89
+ ICAgICAgIDxkaXYgY2xhc3M9IlhiY0dhbWVyY2FyZCBHb2xkIE1hbGUgIj4N
90
+ CiAgICAgICAgICAgIA0KICAgICAgICAgICAgPGEgaWQ9IkdhbWVydGFnIiBo
91
+ cmVmPSJodHRwOi8vbGl2ZS54Ym94LmNvbS9lbi1VUy9Qcm9maWxlP0dhbWVy
92
+ dGFnPWxhaWthbDEiPmxhaWthbDE8L2E+DQogICAgICAgICAgICA8YSBocmVm
93
+ PSJodHRwOi8vbGl2ZS54Ym94LmNvbS9lbi1VUy9Qcm9maWxlP0dhbWVydGFn
94
+ PWxhaWthbDEiPg0KICAgICAgICAgICAgICAgIDxpbWcgaWQ9IkdhbWVycGlj
95
+ IiBzcmM9Imh0dHA6Ly9hdmF0YXIueGJveGxpdmUuY29tL2F2YXRhci9sYWlr
96
+ YWwxL2F2YXRhcnBpYy1sLnBuZyIgYWx0PSJsYWlrYWwxIiB0aXRsZT0ibGFp
97
+ a2FsMSIvPg0KICAgICAgICAgICAgPC9hPg0KICAgICAgICAgICAgPGRpdiBj
98
+ bGFzcz0iUmVwQ29udGFpbmVyIj4NCiAgICAgICAgICAgICAgICA8bGFiZWw+
99
+ UmVwPC9sYWJlbD4NCiAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAg
100
+ ICAgICAgPGRpdiBjbGFzcz0iU3RhciBGdWxsIj48L2Rpdj4NCiAgICAgICAg
101
+ ICAgICAgICAgICAgPGRpdiBjbGFzcz0iU3RhciBGdWxsIj48L2Rpdj4NCiAg
102
+ ICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iU3RhciBGdWxsIj48L2Rp
103
+ dj4NCiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz0iU3RhciBGdWxs
104
+ Ij48L2Rpdj48ZGl2IGNsYXNzPSJTdGFyIFF1YXJ0ZXIiPjwvZGl2Pg0KICAg
105
+ ICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICA8ZGl2IGNsYXNzPSJHYW1l
106
+ cnNjb3JlQ29udGFpbmVyIj4NCiAgICAgICAgICAgICAgICA8bGFiZWw+PC9s
107
+ YWJlbD4NCiAgICAgICAgICAgICAgICA8ZGl2IGlkPSJHYW1lcnNjb3JlIj40
108
+ OTY0NjwvZGl2Pg0KICAgICAgICAgICAgPC9kaXY+DQogICAgICAgICAgICA8
109
+ ZGl2IGlkPSJMb2NhdGlvbiI+QW5uIEFyYm9yLCBNSTwvZGl2Pg0KICAgICAg
110
+ ICAgICAgPGRpdiBpZD0iTW90dG8iPkNvbXBsZXhpZnkhPC9kaXY+DQogICAg
111
+ ICAgICAgICA8ZGl2IGlkPSJOYW1lIj5OeWFybGF0aG90ZXAgKDEgYnVja2V0
112
+ KTwvZGl2Pg0KICAgICAgICAgICAgPGRpdiBpZD0iQmlvIj5EZXNpZ24gaXMg
113
+ aG93IGl0IHdvcmtzLjwvZGl2Pg0KDQogICAgICAgICAgICANCiAgICAgICAg
114
+ ICAgIDxvbCBpZD0iUGxheWVkR2FtZXMiID4NCiAgICAgICAgICAgIA0KICAg
115
+ ICAgICAgICAgICAgIDxsaSA+DQogICAgICAgICAgICAgICAgICAgIA0KICAg
116
+ ICAgICAgICAgICAgICAgICA8YSBocmVmPSJodHRwOi8vbGl2ZS54Ym94LmNv
117
+ bS9lbi1VUy9BY3Rpdml0eS9EZXRhaWxzP3RpdGxlSWQ9MTE2MTg5MDExMCZh
118
+ bXA7Y29tcGFyZVRvPWxhaWthbDEiPg0KICAgICAgICAgICAgICAgICAgICAg
119
+ ICA8aW1nIHNyYz0iaHR0cDovL3RpbGVzLnhib3guY29tL3RpbGVzLzlhL1NM
120
+ LzBtZHNiMkpoYkE5RUNnUU5Hd0VmVjF3bUwybGpiMjR2TUM4NE1EQXdJQUFB
121
+ QUFBQUFQMmtwT289LmpwZyIgYWx0PSJLb0E6IFJlY2tvbmluZyIgdGl0bGU9
122
+ IktvQTogUmVja29uaW5nIiAvPg0KICAgICAgICAgICAgICAgICAgICAgICA8
123
+ c3BhbiBjbGFzcz0iVGl0bGUiPktvQTogUmVja29uaW5nPC9zcGFuPg0KICAg
124
+ ICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iTGFzdFBsYXllZCI+
125
+ My8yOS8yMDEyPC9zcGFuPg0KICAgICAgICAgICAgICAgICAgICAgICA8c3Bh
126
+ biBjbGFzcz0iRWFybmVkR2FtZXJzY29yZSI+MTA8L3NwYW4+DQogICAgICAg
127
+ ICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJBdmFpbGFibGVHYW1lcnNj
128
+ b3JlIj4xMzAwPC9zcGFuPg0KICAgICAgICAgICAgICAgICAgICAgICA8c3Bh
129
+ biBjbGFzcz0iRWFybmVkQWNoaWV2ZW1lbnRzIj4xPC9zcGFuPg0KICAgICAg
130
+ ICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iQXZhaWxhYmxlQWNoaWV2
131
+ ZW1lbnRzIj42MTwvc3Bhbj4NCiAgICAgICAgICAgICAgICAgICAgICAgPHNw
132
+ YW4gY2xhc3M9IlBlcmNlbnRhZ2VDb21wbGV0ZSI+MSU8L3NwYW4+DQogICAg
133
+ ICAgICAgICAgICAgICAgIDwvYT4NCiAgICAgICAgICAgICAgICA8L2xpPg0K
134
+ ICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgPGxpID4NCiAgICAgICAg
135
+ ICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgIDxhIGhyZWY9Imh0
136
+ dHA6Ly9saXZlLnhib3guY29tL2VuLVVTL0FjdGl2aXR5L0RldGFpbHM/dGl0
137
+ bGVJZD0xMTYxODkwMTQxJmFtcDtjb21wYXJlVG89bGFpa2FsMSI+DQogICAg
138
+ ICAgICAgICAgICAgICAgICAgIDxpbWcgc3JjPSJodHRwOi8vdGlsZXMueGJv
139
+ eC5jb20vdGlsZXMvbnAvRzIvMTJkc2IySmhiQTlFQ2dRTkd3RWZWMW9uTDJs
140
+ amIyNHZNQzg0TURBd0lBQUFBQUFBQVBpWmtZRT0uanBnIiBhbHQ9Ik1hc3Mg
141
+ RWZmZWN0IDMiIHRpdGxlPSJNYXNzIEVmZmVjdCAzIiAvPg0KICAgICAgICAg
142
+ ICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iVGl0bGUiPk1hc3MgRWZmZWN0
143
+ IDM8L3NwYW4+DQogICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNz
144
+ PSJMYXN0UGxheWVkIj4zLzI4LzIwMTI8L3NwYW4+DQogICAgICAgICAgICAg
145
+ ICAgICAgICAgIDxzcGFuIGNsYXNzPSJFYXJuZWRHYW1lcnNjb3JlIj4xMDI1
146
+ PC9zcGFuPg0KICAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0i
147
+ QXZhaWxhYmxlR2FtZXJzY29yZSI+MTA1MDwvc3Bhbj4NCiAgICAgICAgICAg
148
+ ICAgICAgICAgICAgPHNwYW4gY2xhc3M9IkVhcm5lZEFjaGlldmVtZW50cyI+
149
+ NTE8L3NwYW4+DQogICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNz
150
+ PSJBdmFpbGFibGVBY2hpZXZlbWVudHMiPjUyPC9zcGFuPg0KICAgICAgICAg
151
+ ICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iUGVyY2VudGFnZUNvbXBsZXRl
152
+ Ij45OCU8L3NwYW4+DQogICAgICAgICAgICAgICAgICAgIDwvYT4NCiAgICAg
153
+ ICAgICAgICAgICA8L2xpPg0KICAgICAgICAgICAgDQogICAgICAgICAgICAg
154
+ ICAgPGxpID4NCiAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAg
155
+ ICAgICAgIDxhIGhyZWY9Imh0dHA6Ly9saXZlLnhib3guY29tL2VuLVVTL0Fj
156
+ dGl2aXR5L0RldGFpbHM/dGl0bGVJZD0xMTYxODkwMDgzJmFtcDtjb21wYXJl
157
+ VG89bGFpa2FsMSI+DQogICAgICAgICAgICAgICAgICAgICAgIDxpbWcgc3Jj
158
+ PSJodHRwOi8vdGlsZXMueGJveC5jb20vdGlsZXMvV1EvbnovMFdkc2IySmhi
159
+ QTlFQ2dRTkd3RWZWMTFRTDJsamIyNHZNQzg0TURBd0lBQUFBQUFBQVA3Y0NV
160
+ WT0uanBnIiBhbHQ9IlN5bmRpY2F0ZSIgdGl0bGU9IlN5bmRpY2F0ZSIgLz4N
161
+ CiAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9IlRpdGxlIj5T
162
+ eW5kaWNhdGU8L3NwYW4+DQogICAgICAgICAgICAgICAgICAgICAgIDxzcGFu
163
+ IGNsYXNzPSJMYXN0UGxheWVkIj4zLzExLzIwMTI8L3NwYW4+DQogICAgICAg
164
+ ICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJFYXJuZWRHYW1lcnNjb3Jl
165
+ Ij41MzA8L3NwYW4+DQogICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNs
166
+ YXNzPSJBdmFpbGFibGVHYW1lcnNjb3JlIj4xMDAwPC9zcGFuPg0KICAgICAg
167
+ ICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iRWFybmVkQWNoaWV2ZW1l
168
+ bnRzIj4zMzwvc3Bhbj4NCiAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4g
169
+ Y2xhc3M9IkF2YWlsYWJsZUFjaGlldmVtZW50cyI+NTA8L3NwYW4+DQogICAg
170
+ ICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPSJQZXJjZW50YWdlQ29t
171
+ cGxldGUiPjY2JTwvc3Bhbj4NCiAgICAgICAgICAgICAgICAgICAgPC9hPg0K
172
+ ICAgICAgICAgICAgICAgIDwvbGk+DQogICAgICAgICAgICANCiAgICAgICAg
173
+ ICAgICAgICA8bGkgY2xhc3M9IkNvbXBsZXRlIiA+DQogICAgICAgICAgICAg
174
+ ICAgICAgIA0KICAgICAgICAgICAgICAgICAgICA8YSBocmVmPSJodHRwOi8v
175
+ bGl2ZS54Ym94LmNvbS9lbi1VUy9BY3Rpdml0eS9EZXRhaWxzP3RpdGxlSWQ9
176
+ MTQxNDc5MzE3NiZhbXA7Y29tcGFyZVRvPWxhaWthbDEiPg0KICAgICAgICAg
177
+ ICAgICAgICAgICAgICA8aW1nIHNyYz0iaHR0cDovL3RpbGVzLnhib3guY29t
178
+ L3RpbGVzL2ZjL3FmLzBXZHNiMkpoYkE5RUNnVU1HZ1FmV1N0YkwybGpiMjR2
179
+ TUM4NE1EQXdJQUFBQUFBQUFQNnd5bUk9LmpwZyIgYWx0PSJCaW9TaG9jayIg
180
+ dGl0bGU9IkJpb1Nob2NrIiAvPg0KICAgICAgICAgICAgICAgICAgICAgICA8
181
+ c3BhbiBjbGFzcz0iVGl0bGUiPkJpb1Nob2NrPC9zcGFuPg0KICAgICAgICAg
182
+ ICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iTGFzdFBsYXllZCI+Mi8yNS8y
183
+ MDEyPC9zcGFuPg0KICAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFz
184
+ cz0iRWFybmVkR2FtZXJzY29yZSI+MTEwMDwvc3Bhbj4NCiAgICAgICAgICAg
185
+ ICAgICAgICAgICAgPHNwYW4gY2xhc3M9IkF2YWlsYWJsZUdhbWVyc2NvcmUi
186
+ PjExMDA8L3NwYW4+DQogICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNs
187
+ YXNzPSJFYXJuZWRBY2hpZXZlbWVudHMiPjUxPC9zcGFuPg0KICAgICAgICAg
188
+ ICAgICAgICAgICAgICA8c3BhbiBjbGFzcz0iQXZhaWxhYmxlQWNoaWV2ZW1l
189
+ bnRzIj41MTwvc3Bhbj4NCiAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4g
190
+ Y2xhc3M9IlBlcmNlbnRhZ2VDb21wbGV0ZSI+MTAwJTwvc3Bhbj4NCiAgICAg
191
+ ICAgICAgICAgICAgICAgPC9hPg0KICAgICAgICAgICAgICAgIDwvbGk+DQog
192
+ ICAgICAgICAgICANCiAgICAgICAgICAgICAgICA8bGkgPg0KICAgICAgICAg
193
+ ICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgPGEgaHJlZj0iaHR0
194
+ cDovL2xpdmUueGJveC5jb20vZW4tVVMvQWN0aXZpdHkvRGV0YWlscz90aXRs
195
+ ZUlkPTExMTI3Mzc3NjYmYW1wO2NvbXBhcmVUbz1sYWlrYWwxIj4NCiAgICAg
196
+ ICAgICAgICAgICAgICAgICAgPGltZyBzcmM9Imh0dHA6Ly90aWxlcy54Ym94
197
+ LmNvbS90aWxlcy9xUi9RSS8xR2RzYjJKaGJBOUVDZ1FLR2dNZldTcFZMMmxq
198
+ YjI0dk1DODRNREF3SUFBQUFBQUFBUHNuRkxZPS5qcGciIGFsdD0iU2t5cmlt
199
+ IiB0aXRsZT0iU2t5cmltIiAvPg0KICAgICAgICAgICAgICAgICAgICAgICA8
200
+ c3BhbiBjbGFzcz0iVGl0bGUiPlNreXJpbTwvc3Bhbj4NCiAgICAgICAgICAg
201
+ ICAgICAgICAgICAgPHNwYW4gY2xhc3M9Ikxhc3RQbGF5ZWQiPjIvMTQvMjAx
202
+ Mjwvc3Bhbj4NCiAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9
203
+ IkVhcm5lZEdhbWVyc2NvcmUiPjkxMDwvc3Bhbj4NCiAgICAgICAgICAgICAg
204
+ ICAgICAgICAgPHNwYW4gY2xhc3M9IkF2YWlsYWJsZUdhbWVyc2NvcmUiPjEw
205
+ MDA8L3NwYW4+DQogICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNz
206
+ PSJFYXJuZWRBY2hpZXZlbWVudHMiPjQ3PC9zcGFuPg0KICAgICAgICAgICAg
207
+ ICAgICAgICAgICA8c3BhbiBjbGFzcz0iQXZhaWxhYmxlQWNoaWV2ZW1lbnRz
208
+ Ij41MDwvc3Bhbj4NCiAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xh
209
+ c3M9IlBlcmNlbnRhZ2VDb21wbGV0ZSI+OTQlPC9zcGFuPg0KICAgICAgICAg
210
+ ICAgICAgICAgICA8L2E+DQogICAgICAgICAgICAgICAgPC9saT4NCiAgICAg
211
+ ICAgICAgIA0KICAgICAgICAgICAgPC9vbD4NCiAgICAgICAgPC9kaXY+DQog
212
+ ICAgICAgIA0KICAgIDwvYm9keT4NCjwvaHRtbD4=
213
+ http_version:
214
+ recorded_at: Sun, 01 Apr 2012 22:52:01 GMT
215
+ recorded_with: VCR 2.0.0
@@ -0,0 +1,113 @@
1
+
2
+
3
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="eng">
5
+ <head>
6
+ <link rel="stylesheet" type="text/css" href="/Content/Gamercard/default/gamercard.css" />
7
+ <meta name="robots" content="noindex,nofollow" />
8
+ <meta name="category" content="Gamercard" />
9
+ <base target="_top" />
10
+ <title>laikal1</title>
11
+ </head>
12
+ <body>
13
+ <div class="XbcGamercard Gold Male ">
14
+
15
+ <a id="Gamertag" href="http://live.xbox.com/en-US/Profile?Gamertag=laikal1">laikal1</a>
16
+ <a href="http://live.xbox.com/en-US/Profile?Gamertag=laikal1">
17
+ <img id="Gamerpic" src="http://avatar.xboxlive.com/avatar/laikal1/avatarpic-l.png" alt="laikal1" title="laikal1"/>
18
+ </a>
19
+ <div class="RepContainer">
20
+ <label>Rep</label>
21
+
22
+ <div class="Star Full"></div>
23
+ <div class="Star Full"></div>
24
+ <div class="Star Full"></div>
25
+ <div class="Star Full"></div><div class="Star Quarter"></div>
26
+ </div>
27
+ <div class="GamerscoreContainer">
28
+ <label></label>
29
+ <div id="Gamerscore">49646</div>
30
+ </div>
31
+ <div id="Location">Ann Arbor, MI</div>
32
+ <div id="Motto">Complexify!</div>
33
+ <div id="Name">Nyarlathotep (1 bucket)</div>
34
+ <div id="Bio">Design is how it works.</div>
35
+
36
+
37
+ <ol id="PlayedGames" >
38
+
39
+ <li >
40
+
41
+ <a href="http://live.xbox.com/en-US/Activity/Details?titleId=1161890110&amp;compareTo=laikal1">
42
+ <img src="http://tiles.xbox.com/tiles/9a/SL/0mdsb2JhbA9ECgQNGwEfV1wmL2ljb24vMC84MDAwIAAAAAAAAP2kpOo=.jpg" alt="KoA: Reckoning" title="KoA: Reckoning" />
43
+ <span class="Title">KoA: Reckoning</span>
44
+ <span class="LastPlayed">3/28/2012</span>
45
+ <span class="EarnedGamerscore">10</span>
46
+ <span class="AvailableGamerscore">1300</span>
47
+ <span class="EarnedAchievements">1</span>
48
+ <span class="AvailableAchievements">61</span>
49
+ <span class="PercentageComplete">1%</span>
50
+ </a>
51
+ </li>
52
+
53
+ <li >
54
+
55
+ <a href="http://live.xbox.com/en-US/Activity/Details?titleId=1161890141&amp;compareTo=laikal1">
56
+ <img src="http://tiles.xbox.com/tiles/np/G2/12dsb2JhbA9ECgQNGwEfV1onL2ljb24vMC84MDAwIAAAAAAAAPiZkYE=.jpg" alt="Mass Effect 3" title="Mass Effect 3" />
57
+ <span class="Title">Mass Effect 3</span>
58
+ <span class="LastPlayed">3/27/2012</span>
59
+ <span class="EarnedGamerscore">1025</span>
60
+ <span class="AvailableGamerscore">1050</span>
61
+ <span class="EarnedAchievements">51</span>
62
+ <span class="AvailableAchievements">52</span>
63
+ <span class="PercentageComplete">98%</span>
64
+ </a>
65
+ </li>
66
+
67
+ <li >
68
+
69
+ <a href="http://live.xbox.com/en-US/Activity/Details?titleId=1161890083&amp;compareTo=laikal1">
70
+ <img src="http://tiles.xbox.com/tiles/WQ/nz/0Wdsb2JhbA9ECgQNGwEfV11QL2ljb24vMC84MDAwIAAAAAAAAP7cCUY=.jpg" alt="Syndicate" title="Syndicate" />
71
+ <span class="Title">Syndicate</span>
72
+ <span class="LastPlayed">3/11/2012</span>
73
+ <span class="EarnedGamerscore">530</span>
74
+ <span class="AvailableGamerscore">1000</span>
75
+ <span class="EarnedAchievements">33</span>
76
+ <span class="AvailableAchievements">50</span>
77
+ <span class="PercentageComplete">66%</span>
78
+ </a>
79
+ </li>
80
+
81
+ <li class="Complete" >
82
+
83
+ <a href="http://live.xbox.com/en-US/Activity/Details?titleId=1414793176&amp;compareTo=laikal1">
84
+ <img src="http://tiles.xbox.com/tiles/fc/qf/0Wdsb2JhbA9ECgUMGgQfWStbL2ljb24vMC84MDAwIAAAAAAAAP6wymI=.jpg" alt="BioShock" title="BioShock" />
85
+ <span class="Title">BioShock</span>
86
+ <span class="LastPlayed">2/24/2012</span>
87
+ <span class="EarnedGamerscore">1100</span>
88
+ <span class="AvailableGamerscore">1100</span>
89
+ <span class="EarnedAchievements">51</span>
90
+ <span class="AvailableAchievements">51</span>
91
+ <span class="PercentageComplete">100%</span>
92
+ </a>
93
+ </li>
94
+
95
+ <li >
96
+
97
+ <a href="http://live.xbox.com/en-US/Activity/Details?titleId=1112737766&amp;compareTo=laikal1">
98
+ <img src="http://tiles.xbox.com/tiles/qR/QI/1Gdsb2JhbA9ECgQKGgMfWSpVL2ljb24vMC84MDAwIAAAAAAAAPsnFLY=.jpg" alt="Skyrim" title="Skyrim" />
99
+ <span class="Title">Skyrim</span>
100
+ <span class="LastPlayed">2/14/2012</span>
101
+ <span class="EarnedGamerscore">910</span>
102
+ <span class="AvailableGamerscore">1000</span>
103
+ <span class="EarnedAchievements">47</span>
104
+ <span class="AvailableAchievements">50</span>
105
+ <span class="PercentageComplete">94%</span>
106
+ </a>
107
+ </li>
108
+
109
+ </ol>
110
+ </div>
111
+
112
+ </body>
113
+ </html>
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ module Gamercard
4
+ describe "CardParser" do
5
+ let(:mock_response) { mock(:body => File.read("spec/fixtures/laikal1.html")) }
6
+ let(:parser) { CardParser.new mock_response }
7
+ let(:doc) { Nokogiri::HTML(mock_response.body) }
8
+
9
+ def nokogirize html
10
+ Nokogiri::HTML(html)
11
+ end
12
+
13
+ describe ".parse" do
14
+ it "returns a hash representing a parsed gamercard" do
15
+ result = CardParser.parse mock_response
16
+ result['gamertag'].should == 'laikal1'
17
+ result['gamerpic'].should == 'http://avatar.xboxlive.com/avatar/laikal1/avatarpic-l.png'
18
+ result['gamerscore'].should == 49646
19
+ end
20
+ end
21
+
22
+ describe "attributes" do
23
+ it "takes one required parameter, viz the http response from the card service" do
24
+ parser = CardParser.new mock_response
25
+ parser.raw_response.should == mock_response
26
+ end
27
+ end
28
+
29
+ describe "#parse" do
30
+ it "chops the html up neatly" do
31
+ result = parser.parse
32
+ result['gamertag'].should == 'laikal1'
33
+ result['gamerpic'].should == 'http://avatar.xboxlive.com/avatar/laikal1/avatarpic-l.png'
34
+ result['gamerscore'].should == 49646
35
+ result['recent_games'].size.should == 5
36
+ end
37
+ end
38
+
39
+ describe "#count_reputation" do
40
+ it "returns laikal1's rep as 4.25" do
41
+ count = parser.count_reputation(doc).should == 4.25
42
+ end
43
+ it "counts the number of Full, Half, ThreeQuarter, and Quarter .Star elements in the .RepContainer" do
44
+ doc1 = <<-DOC1
45
+ <div class="RepContainer">
46
+ <label>Rep</label>
47
+ <div class="Star Full"></div>
48
+ <div class="Star Full"></div>
49
+ <div class="Star Full"></div>
50
+ <div class="Star ThreeQuarter"></div>
51
+ <div class="Star Empty"></div>
52
+ </div>
53
+ DOC1
54
+ parser.count_reputation(nokogirize(doc1)).should == 3.75
55
+
56
+ doc2 = <<-DOC2
57
+ <div class="RepContainer">
58
+ <label>Rep</label>
59
+ <div class="Star Full"></div>
60
+ <div class="Star Full"></div>
61
+ <div class="Star Half"></div>
62
+ <div class="Star Empty"></div>
63
+ <div class="Star Empty"></div>
64
+ </div>
65
+ DOC2
66
+ parser.count_reputation(nokogirize(doc2)).should == 2.5
67
+
68
+ doc3 = <<-DOC3
69
+ <div class="RepContainer">
70
+ <label>Rep</label>
71
+ <div class="Star Full"></div>
72
+ <div class="Star Empty"></div>
73
+ <div class="Star Half"></div>
74
+ <div class="Star Quarter"></div>
75
+ <div class="Star Empty"></div>
76
+ </div>
77
+ DOC3
78
+ parser.count_reputation(nokogirize(doc3)).should == 1.75
79
+ end
80
+ end
81
+
82
+ describe "#summarize_games" do
83
+ it "returns an array of game summaries" do
84
+ games = parser.summarize_games(doc)
85
+ games.should be_a_kind_of(Array)
86
+ games.size.should == 5
87
+ end
88
+ end
89
+
90
+ describe "#summarize" do
91
+ it "returns a hash of parsed values" do
92
+ game = <<-DOC
93
+ <li >
94
+ <a href="http://live.xbox.com/en-US/Activity/Details?titleId=1161890110&amp;compareTo=laikal1">
95
+ <img src="http://tiles.xbox.com/tiles/9a/SL/0mdsb2JhbA9ECgQNGwEfV1wmL2ljb24vMC84MDAwIAAAAAAAAP2kpOo=.jpg" alt="KoA: Reckoning" title="KoA: Reckoning" />
96
+ <span class="Title">KoA: Reckoning</span>
97
+ <span class="LastPlayed">3/28/2012</span>
98
+ <span class="EarnedGamerscore">10</span>
99
+ <span class="AvailableGamerscore">1300</span>
100
+ <span class="EarnedAchievements">1</span>
101
+ <span class="AvailableAchievements">61</span>
102
+ <span class="PercentageComplete">1%</span>
103
+ </a>
104
+ </li>
105
+ DOC
106
+ result = parser.summarize(nokogirize(game))
107
+ result.should == {
108
+ "comparison_url" => "http://live.xbox.com/en-US/Activity/Details?titleId=1161890110&compareTo=laikal1",
109
+ "image" => "http://tiles.xbox.com/tiles/9a/SL/0mdsb2JhbA9ECgQNGwEfV1wmL2ljb24vMC84MDAwIAAAAAAAAP2kpOo=.jpg",
110
+ "title" => "KoA: Reckoning",
111
+ "last_played" => "3/28/2012",
112
+ "earned_gamerscore" => 10,
113
+ "available_gamerscore" => 1300,
114
+ "earned_achievements" => 1,
115
+ "available_achievements" => 61,
116
+ "percentage_complete" => "1%"
117
+ }
118
+ end
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ module Gamercard
4
+ describe "Client" do
5
+ let(:client) { Client.new("laikal1") }
6
+
7
+ it "should be loaded correctly" do
8
+ lambda { Gamercard::Client }.should_not raise_error
9
+ end
10
+
11
+ it "takes the gamertag as the only input parameter" do
12
+ client.gamertag.should == "laikal1"
13
+ end
14
+
15
+ describe "#fetch" do
16
+ it "should return a hash of useful values" do
17
+ VCR.use_cassette('fetch_laikal1', :record => :new_episodes) do
18
+ response = client.fetch
19
+ response['gamertag'].should == 'laikal1'
20
+ response['gamerpic'].should == 'http://avatar.xboxlive.com/avatar/laikal1/avatarpic-l.png'
21
+ response['gamerscore'].should == 49646
22
+ response['recent_games'].size.should == 5
23
+ end
24
+ end
25
+ it "should return the raw response if there is a problem with the response" do
26
+ client.stub!(:connection).and_return(
27
+ Faraday.new do |builder|
28
+ builder.adapter :test do |stub|
29
+ stub.get('laikal1.card') {[ 500, {}, 'Oh Noes! I blew it up!' ]}
30
+ end
31
+ end
32
+ )
33
+ VCR.turned_off do
34
+ response = client.fetch
35
+ response.status.should == 500
36
+ response.body.should == 'Oh Noes! I blew it up!'
37
+ end
38
+ end
39
+ end
40
+
41
+ describe "#fetch_with_redirect" do
42
+ it "follows the redirect like a boss" do
43
+ VCR.use_cassette('fetch_laikal1', :record => :new_episodes) do
44
+ response = client.fetch_with_redirect
45
+ response.status.should == 200
46
+ response.body[0, 16].should == "\r\n<!DOCTYPE html"
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#connection" do
52
+ it "returns a Faraday connection, using the configured http library, to the gamercard url" do
53
+ conn = client.send(:connection)
54
+ conn.host.should == Client::GAMERCARD_SERVICE_URL.sub("http://", "")
55
+ conn.builder.handlers.should include(Faraday::Adapter::Typhoeus)
56
+ end
57
+ end
58
+
59
+ describe "#build_url" do
60
+ it "returns the portion of the URL that generates the card for the given gamertag" do
61
+ client.build_url.should == "laikal1.card"
62
+ end
63
+ end
64
+
65
+ describe "#parse" do
66
+ it "delegates to a CardParser object" do
67
+ CardParser.should_receive(:parse).with("foo").and_return({ "gamertag" => "laikal1" })
68
+ client.parse "foo"
69
+ end
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Gamercard do
4
+ it "should be configurable" do
5
+ Gamercard.should respond_to(:configure)
6
+ end
7
+
8
+ it "has a sensible default for .adapter" do
9
+ Gamercard.adapter.should == :typhoeus
10
+ end
11
+
12
+ describe "#get" do
13
+ it "is a shortcut to Client#fetch" do
14
+ Gamercard::Client.should_receive(:new).and_return(mock(:fetch => "OK."))
15
+ Gamercard.get("foo").should == "OK."
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'gamercard' # and any other gems you need
5
+ require 'vcr'
6
+
7
+ RSpec.configure do |c|
8
+ c.extend VCR::RSpec::Macros
9
+ end
10
+
11
+ VCR.configure do |c|
12
+ c.cassette_library_dir = 'spec/fixtures'
13
+ c.hook_into :faraday
14
+ end
15
+
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ RSpec.configure do |config|
18
+ config.treat_symbols_as_metadata_keys_with_true_values = true
19
+ # config.run_all_when_everything_filtered = true
20
+ # config.filter_run :focus
21
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gamercard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matt Wilson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &2152009940 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2152009940
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &2152009460 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2152009460
36
+ - !ruby/object:Gem::Dependency
37
+ name: typhoeus
38
+ requirement: &2152008860 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2152008860
47
+ - !ruby/object:Gem::Dependency
48
+ name: vcr
49
+ requirement: &2152008300 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2152008300
58
+ - !ruby/object:Gem::Dependency
59
+ name: fakeweb
60
+ requirement: &2152007700 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2152007700
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: &2152007120 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *2152007120
80
+ - !ruby/object:Gem::Dependency
81
+ name: faraday
82
+ requirement: &2152006420 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: 0.8.0.rc2
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: *2152006420
91
+ description: A Simple & Straightforward XBox Live Gamercard parser.
92
+ email:
93
+ - mhw@hypomodern.com
94
+ executables: []
95
+ extensions: []
96
+ extra_rdoc_files: []
97
+ files:
98
+ - .gitignore
99
+ - .rspec
100
+ - .rvmrc
101
+ - Gemfile
102
+ - Guardfile
103
+ - LICENSE
104
+ - README.md
105
+ - Rakefile
106
+ - gamercard.gemspec
107
+ - lib/gamercard.rb
108
+ - lib/gamercard/card_parser.rb
109
+ - lib/gamercard/client.rb
110
+ - lib/gamercard/configuration.rb
111
+ - lib/gamercard/version.rb
112
+ - spec/fixtures/fetch_laikal1.yml
113
+ - spec/fixtures/laikal1.html
114
+ - spec/gamercard/card_parser_spec.rb
115
+ - spec/gamercard/client_spec.rb
116
+ - spec/gamercard_spec.rb
117
+ - spec/spec_helper.rb
118
+ homepage: https://github.com/hypomodern/gamercard
119
+ licenses: []
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 1.8.17
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: A Simple & Straightforward XBox Live Gamercard parser.
142
+ test_files:
143
+ - spec/fixtures/fetch_laikal1.yml
144
+ - spec/fixtures/laikal1.html
145
+ - spec/gamercard/card_parser_spec.rb
146
+ - spec/gamercard/client_spec.rb
147
+ - spec/gamercard_spec.rb
148
+ - spec/spec_helper.rb