vapor 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +15 -19
- data/.rspec +2 -0
- data/.rvmrc +48 -0
- data/Gemfile +4 -0
- data/{LICENSE → LICENSE.txt} +4 -2
- data/README.md +29 -0
- data/Rakefile +1 -53
- data/lib/vapor.rb +12 -0
- data/lib/vapor/api.rb +44 -0
- data/lib/vapor/crawler.rb +17 -0
- data/lib/vapor/game.rb +15 -0
- data/lib/vapor/games_list.rb +20 -0
- data/lib/vapor/user.rb +40 -0
- data/lib/vapor/version.rb +3 -0
- data/spec/cassettes/Vapor_API/fetches_player_summaries_for_an_user.yml +87 -0
- data/spec/cassettes/Vapor_Crawler/_games_for/fetches_a_games_list_for_an_user.yml +1306 -0
- data/spec/cassettes/Vapor_Game/finds_the_correct_id.yml +1107 -0
- data/spec/cassettes/Vapor_Game/finds_the_correct_name.yml +1107 -0
- data/spec/cassettes/Vapor_Game/finds_the_correct_time_spent_in_hours.yml +1133 -0
- data/spec/cassettes/Vapor_GamesList/creates_game_objects.yml +1113 -0
- data/spec/cassettes/Vapor_GamesList/has_the_right_amount_of_games.yml +1107 -0
- data/spec/cassettes/Vapor_User/fetches_the_right_Steam_Id_using_the_users_webpage.yml +57 -0
- data/spec/cassettes/Vapor_User/games/fetches_the_right_amount_of_games.yml +1195 -0
- data/spec/cassettes/Vapor_User/profile_info/has_a_profile_url.yml +40 -0
- data/spec/cassettes/Vapor_User/raises_a_valid_error_when_Steam_API_is_not_available.yml +57 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/vapor/api_spec.rb +13 -0
- data/spec/vapor/crawler_spec.rb +15 -0
- data/spec/vapor/game_spec.rb +19 -0
- data/spec/vapor/games_list_spec.rb +13 -0
- data/spec/vapor/user_spec.rb +26 -0
- data/vapor.gemspec +28 -0
- metadata +213 -62
- data/.document +0 -5
- data/README.rdoc +0 -17
- data/VERSION +0 -1
- data/test/helper.rb +0 -10
- data/test/test_vapor.rb +0 -7
data/.gitignore
CHANGED
@@ -1,21 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
*~
|
10
|
-
\#*
|
11
|
-
.\#*
|
12
|
-
|
13
|
-
## VIM
|
14
|
-
*.swp
|
15
|
-
|
16
|
-
## PROJECT::GENERAL
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
17
9
|
coverage
|
18
|
-
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
19
12
|
pkg
|
20
|
-
|
21
|
-
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
|
7
|
+
# Only full ruby name is supported here, for short names use:
|
8
|
+
# echo "rvm use 1.9.3" > .rvmrc
|
9
|
+
environment_id="ruby-1.9.3-p327@vapor"
|
10
|
+
|
11
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
12
|
+
# rvmrc_rvm_version="1.16.5 (latest)" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
|
18
|
+
# First we attempt to load the desired environment directly from the environment
|
19
|
+
# file. This is very fast and efficient compared to running through the entire
|
20
|
+
# CLI and selector. If you want feedback on which environment was used then
|
21
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
22
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
|
23
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
24
|
+
then
|
25
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
26
|
+
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
|
27
|
+
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
|
28
|
+
else
|
29
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
30
|
+
rvm --create "$environment_id" || {
|
31
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
32
|
+
return 1
|
33
|
+
}
|
34
|
+
fi
|
35
|
+
|
36
|
+
# If you use bundler, this might be useful to you:
|
37
|
+
# if [[ -s Gemfile ]] && {
|
38
|
+
# ! builtin command -v bundle >/dev/null ||
|
39
|
+
# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
|
40
|
+
# }
|
41
|
+
# then
|
42
|
+
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
43
|
+
# gem install bundler
|
44
|
+
# fi
|
45
|
+
# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
|
46
|
+
# then
|
47
|
+
# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
|
48
|
+
# fi
|
data/Gemfile
ADDED
data/{LICENSE → LICENSE.txt}
RENAMED
@@ -1,4 +1,6 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2012 Pedro Nascimento
|
2
|
+
|
3
|
+
MIT License
|
2
4
|
|
3
5
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
6
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
21
|
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.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Vapor
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'vapor'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install vapor
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
CHANGED
@@ -1,53 +1 @@
|
|
1
|
-
require
|
2
|
-
require 'rake'
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'jeweler'
|
6
|
-
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = "vapor"
|
8
|
-
gem.summary = %Q{Minimalist Ruby library inspired in vapor.js}
|
9
|
-
gem.description = %Q{All awesomest in one library}
|
10
|
-
gem.email = "guilleiguaran@gmail.com"
|
11
|
-
gem.homepage = "http://github.com/guilleiguaran/vapor"
|
12
|
-
gem.authors = ["Guillermo Iguaran"]
|
13
|
-
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
-
end
|
16
|
-
Jeweler::GemcutterTasks.new
|
17
|
-
rescue LoadError
|
18
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
-
end
|
20
|
-
|
21
|
-
require 'rake/testtask'
|
22
|
-
Rake::TestTask.new(:test) do |test|
|
23
|
-
test.libs << 'lib' << 'test'
|
24
|
-
test.pattern = 'test/**/test_*.rb'
|
25
|
-
test.verbose = true
|
26
|
-
end
|
27
|
-
|
28
|
-
begin
|
29
|
-
require 'rcov/rcovtask'
|
30
|
-
Rcov::RcovTask.new do |test|
|
31
|
-
test.libs << 'test'
|
32
|
-
test.pattern = 'test/**/test_*.rb'
|
33
|
-
test.verbose = true
|
34
|
-
end
|
35
|
-
rescue LoadError
|
36
|
-
task :rcov do
|
37
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
task :test => :check_dependencies
|
42
|
-
|
43
|
-
task :default => :test
|
44
|
-
|
45
|
-
require 'rake/rdoctask'
|
46
|
-
Rake::RDocTask.new do |rdoc|
|
47
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
-
|
49
|
-
rdoc.rdoc_dir = 'rdoc'
|
50
|
-
rdoc.title = "vapor #{version}"
|
51
|
-
rdoc.rdoc_files.include('README*')
|
52
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
-
end
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/vapor.rb
CHANGED
data/lib/vapor/api.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'json'
|
3
|
+
module Vapor
|
4
|
+
class API
|
5
|
+
class << self
|
6
|
+
def key
|
7
|
+
@key ||= ENV['STEAM_API_KEY']
|
8
|
+
end
|
9
|
+
end
|
10
|
+
BASE_URL="http://api.steampowered.com"
|
11
|
+
attr_reader :client
|
12
|
+
|
13
|
+
def player_summaries_for(user)
|
14
|
+
fetch('ISteamUser/GetPlayerSummaries', version: 'v0002', steamids: user.steam_id.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
def client
|
18
|
+
@client ||= HTTPClient.new
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def fetch(url, options)
|
23
|
+
version = options.delete :version
|
24
|
+
options.merge! key: self.class.key
|
25
|
+
response = client.get("#{BASE_URL}/#{url}/#{version}/?#{normalize_params options}")
|
26
|
+
parse_and_extract_response response
|
27
|
+
end
|
28
|
+
|
29
|
+
def normalize_params(params)
|
30
|
+
query = ""
|
31
|
+
params.each {|k, v| query += "#{k}=#{v}&"}
|
32
|
+
query
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_and_extract_response(response)
|
36
|
+
JSON.parse(response.body)["response"]["players"].first.symbolize_keys
|
37
|
+
end
|
38
|
+
end
|
39
|
+
class APINotAvailableError < StandardError
|
40
|
+
def initialize
|
41
|
+
super("Steam API is currently unavailable")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
module Vapor
|
3
|
+
class Crawler
|
4
|
+
def games_for(user)
|
5
|
+
doc = fetch("#{user.profile_url}games")
|
6
|
+
doc.gamesList.games.elements
|
7
|
+
end
|
8
|
+
private
|
9
|
+
def fetch(url)
|
10
|
+
doc = client.get("#{url}?xml=1").body
|
11
|
+
Nokogiri::Slop(doc)
|
12
|
+
end
|
13
|
+
def client
|
14
|
+
@client ||= HTTPClient.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/vapor/game.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Vapor
|
2
|
+
class Game
|
3
|
+
attr_reader :id, :name, :url, :time_spent_in_hours
|
4
|
+
def initialize(xml_node)
|
5
|
+
parse_xml(xml_node)
|
6
|
+
end
|
7
|
+
private
|
8
|
+
def parse_xml(xml_node)
|
9
|
+
@id = xml_node.search('appID').text.to_i
|
10
|
+
@name = xml_node.search('name').text
|
11
|
+
@url = xml_node.search('storeLink').text
|
12
|
+
@time_spent_in_hours = xml_node.search('hoursOnRecord').text.to_f
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Vapor
|
2
|
+
class GamesList
|
3
|
+
def initialize(user)
|
4
|
+
@user = user
|
5
|
+
end
|
6
|
+
|
7
|
+
def games
|
8
|
+
parse_games(fetch_games)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def fetch_games
|
13
|
+
Crawler.new.games_for(@user)
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse_games(xml_nodes)
|
17
|
+
@games ||= xml_nodes.map{|xml| Game.new(xml)}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/vapor/user.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'httpclient'
|
2
|
+
require 'rexml/document'
|
3
|
+
module Vapor
|
4
|
+
class User
|
5
|
+
attr_reader :steam_id
|
6
|
+
|
7
|
+
def initialize(steam_id)
|
8
|
+
if steam_id.is_a? Integer || steam_id =~ /\A\d+\Z/
|
9
|
+
@steam_id = steam_id
|
10
|
+
else
|
11
|
+
@steam_id = fetch_real_id steam_id
|
12
|
+
end
|
13
|
+
@steam_id = @steam_id.to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
def profile_url
|
17
|
+
@profile_url ||= player_info[:profileurl]
|
18
|
+
end
|
19
|
+
|
20
|
+
def games
|
21
|
+
@games ||= GamesList.new(self).games
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def player_info
|
27
|
+
@player_info ||= Vapor.api.player_summaries_for(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def fetch_real_id(username)
|
31
|
+
user_page = client.get("http://steamcommunity.com/id/#{username}?xml=1")
|
32
|
+
raise APINotAvailableError if user_page.status == 503
|
33
|
+
REXML::Document.new(user_page.body).elements['profile/steamID64'].text
|
34
|
+
end
|
35
|
+
|
36
|
+
def client
|
37
|
+
Vapor.api.client
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=8BDCBAAA1E46E207931AA2950110AEB2&steamids=76561198021477729
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers: {}
|
10
|
+
response:
|
11
|
+
status:
|
12
|
+
code: 200
|
13
|
+
message: !binary |-
|
14
|
+
T0s=
|
15
|
+
headers:
|
16
|
+
!binary "RGF0ZQ==":
|
17
|
+
- !binary |-
|
18
|
+
VHVlLCAyNyBOb3YgMjAxMiAwNjoxNjozMyBHTVQ=
|
19
|
+
!binary "RXhwaXJlcw==":
|
20
|
+
- !binary |-
|
21
|
+
VHVlLCAyNyBOb3YgMjAxMiAwNjoxNjozMyBHTVQ=
|
22
|
+
!binary "Q29udGVudC1UeXBl":
|
23
|
+
- !binary |-
|
24
|
+
YXBwbGljYXRpb24vanNvbjsgY2hhcnNldD1VVEYtOA==
|
25
|
+
!binary "Q29udGVudC1MZW5ndGg=":
|
26
|
+
- !binary |-
|
27
|
+
ODE0
|
28
|
+
body:
|
29
|
+
encoding: US-ASCII
|
30
|
+
string: ! "{\n\t\"response\": {\n\t\t\"players\": [\n\t\t\t{\n\t\t\t\t\"steamid\":
|
31
|
+
\"76561198021477729\",\n\t\t\t\t\"communityvisibilitystate\": 3,\n\t\t\t\t\"profilestate\":
|
32
|
+
1,\n\t\t\t\t\"personaname\": \"Lunks\",\n\t\t\t\t\"lastlogoff\": 1353928831,\n\t\t\t\t\"profileurl\":
|
33
|
+
\"http://steamcommunity.com/id/pedronascimento/\",\n\t\t\t\t\"avatar\": \"http://media.steampowered.com/steamcommunity/public/images/avatars/4c/4c76064be169987500e4c7e228aabe3e33e1df47.jpg\",\n\t\t\t\t\"avatarmedium\":
|
34
|
+
\"http://media.steampowered.com/steamcommunity/public/images/avatars/4c/4c76064be169987500e4c7e228aabe3e33e1df47_medium.jpg\",\n\t\t\t\t\"avatarfull\":
|
35
|
+
\"http://media.steampowered.com/steamcommunity/public/images/avatars/4c/4c76064be169987500e4c7e228aabe3e33e1df47_full.jpg\",\n\t\t\t\t\"personastate\":
|
36
|
+
1,\n\t\t\t\t\"primaryclanid\": \"103582791429521408\",\n\t\t\t\t\"timecreated\":
|
37
|
+
1266769126,\n\t\t\t\t\"loccountrycode\": \"BR\"\n\t\t\t}\n\t\t]\n\t\t\n\t}\n}"
|
38
|
+
http_version:
|
39
|
+
recorded_at: Tue, 27 Nov 2012 06:16:25 GMT
|
40
|
+
- request:
|
41
|
+
method: get
|
42
|
+
uri: http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=&steamids=76561198021477729
|
43
|
+
body:
|
44
|
+
encoding: US-ASCII
|
45
|
+
string: ''
|
46
|
+
headers: {}
|
47
|
+
response:
|
48
|
+
status:
|
49
|
+
code: 401
|
50
|
+
message: !binary |-
|
51
|
+
VW5hdXRob3JpemVk
|
52
|
+
headers:
|
53
|
+
!binary "Q29udGVudC1MZW5ndGg=":
|
54
|
+
- !binary |-
|
55
|
+
OTg=
|
56
|
+
!binary "Q29udGVudC1UeXBl":
|
57
|
+
- !binary |-
|
58
|
+
dGV4dC9odG1sOyBjaGFyc2V0PVVURjg=
|
59
|
+
!binary "RGF0ZQ==":
|
60
|
+
- !binary |-
|
61
|
+
TW9uLCAwMyBEZWMgMjAxMiAxNjo1NzoxMyBHTVQ=
|
62
|
+
!binary "RXhwaXJlcw==":
|
63
|
+
- !binary |-
|
64
|
+
TW9uLCAyNiBKdWwgMTk5NyAwNTowMDowMCBHTVQ=
|
65
|
+
!binary "Q2FjaGUtQ29udHJvbA==":
|
66
|
+
- !binary |-
|
67
|
+
bm8tY2FjaGUsbXVzdC1yZXZhbGlkYXRl
|
68
|
+
body:
|
69
|
+
encoding: US-ASCII
|
70
|
+
string: ! '<html>
|
71
|
+
|
72
|
+
<head>
|
73
|
+
|
74
|
+
<title>401 Unauthorized</title>
|
75
|
+
|
76
|
+
</head>
|
77
|
+
|
78
|
+
<body>
|
79
|
+
|
80
|
+
<h1>Unauthorized</h1>
|
81
|
+
|
82
|
+
</body>
|
83
|
+
|
84
|
+
</html>'
|
85
|
+
http_version:
|
86
|
+
recorded_at: Mon, 03 Dec 2012 16:57:11 GMT
|
87
|
+
recorded_with: VCR 2.3.0
|