sc2_achievements 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "nokogiri"
4
+
5
+ group :development do
6
+ gem "rspec"
7
+ gem "vcr"
8
+ gem "webmock"
9
+ gem "bundler"
10
+ gem "jeweler"
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.3.2)
5
+ crack (0.3.1)
6
+ diff-lcs (1.1.3)
7
+ git (1.2.5)
8
+ jeweler (1.8.4)
9
+ bundler (~> 1.0)
10
+ git (>= 1.2.5)
11
+ rake
12
+ rdoc
13
+ json (1.7.4)
14
+ nokogiri (1.5.5)
15
+ rake (0.9.2.2)
16
+ rdoc (3.12)
17
+ json (~> 1.4)
18
+ rspec (2.11.0)
19
+ rspec-core (~> 2.11.0)
20
+ rspec-expectations (~> 2.11.0)
21
+ rspec-mocks (~> 2.11.0)
22
+ rspec-core (2.11.1)
23
+ rspec-expectations (2.11.2)
24
+ diff-lcs (~> 1.1.3)
25
+ rspec-mocks (2.11.1)
26
+ vcr (2.2.5)
27
+ webmock (1.8.10)
28
+ addressable (>= 2.2.7)
29
+ crack (>= 0.1.7)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ bundler
36
+ jeweler
37
+ nokogiri
38
+ rspec
39
+ vcr
40
+ webmock
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012
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.md ADDED
@@ -0,0 +1,45 @@
1
+ Starcraft II Achievements
2
+ =========================
3
+
4
+ Download all the achievements of a SC2 player.
5
+
6
+ This library downloads all the achievements from a player's public profile,
7
+ extracts the relevant data and then convert it all to an array, with the
8
+ most recent earned achievement first and then ordered by date.
9
+
10
+ I'm planning to use this as a part of a bigger system, so that's why I haven't
11
+ built-in some sort of cache.
12
+
13
+ ## INSTALLATION
14
+
15
+ Everything is nicely packed as a gem, so all you have to do is:
16
+
17
+ ```
18
+ $ gem install sc2_achievements
19
+ ```
20
+
21
+ ## USAGE
22
+
23
+ ```
24
+ >> require 'sc2_achievements'
25
+ => true
26
+ >> achievements = SC2Achievements.for('/3396700/1/Tato')
27
+ => [{:date=>"2012-09-20", :points=>10, :title=>"That\342\200\231s Teamwork", ... }]
28
+ ```
29
+
30
+ ## TODO
31
+
32
+ I've opened a couple of tickets with some stuff that needs to be done, in case
33
+ anyone wanna help out.
34
+
35
+ ## CONTRIBUTE
36
+
37
+ 1. Fork it.
38
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
39
+ 3. Commit your changes (`git commit -am 'Added some feature'`).
40
+ 4. Push to the branch (`git push origin my-new-feature`).
41
+ 5. Create a new pull request.
42
+
43
+ ## COPYRIGHT
44
+
45
+ Copyright (c) 2012 Eduardo Grajeda Blandón. See LICENSE.txt for further details.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "sc2_achievements"
18
+ gem.homepage = "http://github.com/egrajeda/sc2_achievements"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Download all the achievements of a SC2 player.}
21
+ gem.description = %Q{This library downloads all the achievements from a Starcraft II player's public profile and package them as an array.}
22
+ gem.email = "tatofoo@gmail.com"
23
+ gem.authors = ["Eduardo Grajeda Blandón"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rdoc/task'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "sc2_achievements #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,60 @@
1
+ require 'sc2_achievements/page'
2
+
3
+ module SC2Achievements
4
+ class CategoryPage < Page
5
+ def self.get_achievements_for(user_path, category)
6
+ page = fetch_page_of user_path, :category => category
7
+ page.css('.achievement.earned', '.series-tiles .series-tile:not(.tile-locked)').inject({}) do |achievements, achievement|
8
+ achievements[key_for(achievement)] = {
9
+ :title => title_of(achievement),
10
+ :description => description_of(achievement),
11
+ :category => category_of(achievement),
12
+ :points => points_of(achievement),
13
+ :date => date_of(achievement) }
14
+ achievements
15
+ end
16
+ end
17
+
18
+ private
19
+ # See Homepage.key_for
20
+ def self.key_for(achievement)
21
+ title_of(achievement)
22
+ end
23
+
24
+ def self.title_of(achievement)
25
+ if achievement.attr('class') =~ /series-tile/
26
+ text_of(achievement, achievement.attr('data-tooltip') + ' .tooltip-title')
27
+ else
28
+ text_of(achievement, '.desc span')
29
+ end
30
+ end
31
+
32
+ def self.description_of(achievement)
33
+ if achievement.attr('class') =~ /series-tile/
34
+ text_of(achievement, achievement.attr('data-tooltip'))
35
+ else
36
+ text_of(achievement, '.desc')
37
+ end
38
+ end
39
+
40
+ def self.category_of(achievement)
41
+ wrapper = achievement.ancestors('#profile-wrapper')
42
+ text_of(wrapper, '#profile-menu .active a')
43
+ end
44
+
45
+ def self.points_of(achievement)
46
+ if achievement.attr('class') =~ /series-tile/
47
+ text_of(achievement, '.series-badge').to_i
48
+ else
49
+ text_of(achievement, '.meta span').to_i
50
+ end
51
+ end
52
+
53
+ def self.date_of(achievement)
54
+ if achievement.attr('class') =~ /series-tile/
55
+ achievement = achievement.ancestors('.achievement')
56
+ end
57
+ date_from(achievement, '.meta')
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,36 @@
1
+ require 'date'
2
+ require 'sc2_achievements/page'
3
+
4
+ module SC2Achievements
5
+ class Homepage < Page
6
+ def self.get_achievements_for(user_path)
7
+ page = fetch_page_of user_path
8
+ page.css('#recent-achievements a').each_with_index.inject({}) do |achievements, (achievement, index)|
9
+ achievements[key_for(achievement)] = {
10
+ :title => title_of(achievement),
11
+ :description => description_of(achievement),
12
+ :date => date_of(achievement),
13
+ :recentness => index + 1 }
14
+ achievements
15
+ end
16
+ end
17
+
18
+ private
19
+ # TODO: in the future we might use a real ID, checksum, etc.
20
+ def self.key_for(achievement)
21
+ title_of(achievement)
22
+ end
23
+
24
+ def self.title_of(achievement)
25
+ text_of(achievement)
26
+ end
27
+
28
+ def self.description_of(achievement)
29
+ text_of(achievement.parent, achievement.attr('data-tooltip'))
30
+ end
31
+
32
+ def self.date_of(achievement)
33
+ date_from(achievement, 'span')
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,67 @@
1
+ require 'open-uri'
2
+ begin
3
+ require 'nokogiri'
4
+ rescue LoadError
5
+ require 'rubygems'
6
+ require 'nokogiri'
7
+ end
8
+
9
+ module SC2Achievements
10
+ class Page
11
+ def self.get_achievements_for(user_path, options = {})
12
+ if options.has_key? :category
13
+ CategoryPage.get_achievements_for user_path, options[:category]
14
+ else
15
+ Homepage.get_achievements_for user_path
16
+ end
17
+ end
18
+
19
+ def self.get_categories_for(user_path, options = {})
20
+ page = fetch_page_of user_path, options
21
+ page.css('#profile-menu a[href*=category]').collect do |achievement|
22
+ achievement.attr('href')[/category\/(.*)/, 1]
23
+ end
24
+ end
25
+
26
+ protected
27
+ def self.fetch_page_of(user_path, options = {})
28
+ path = path_for user_path, options
29
+ if cache_exists? path
30
+ page = @@cache[path]
31
+ else
32
+ page = fetch_page path
33
+ end
34
+ @@cache[path] = page
35
+ end
36
+
37
+ def self.cache_exists?(path)
38
+ @@cache ||= {}
39
+ @@cache.has_key? path
40
+ end
41
+
42
+ def self.fetch_page(path)
43
+ html = open(path).read
44
+ Nokogiri::HTML(html)
45
+ end
46
+
47
+ def self.path_for(user_path, options = {})
48
+ if options.has_key? :category
49
+ "http://battle.net/sc2/en/profile#{user_path}/achievements/category/#{options[:category]}"
50
+ else
51
+ "http://battle.net/sc2/en/profile#{user_path}/achievements/"
52
+ end
53
+ end
54
+
55
+ def self.text_of(current_node, css_selector=nil)
56
+ if css_selector
57
+ current_node = current_node.css(css_selector)
58
+ end
59
+ current_node.xpath('text()').text.strip
60
+ end
61
+
62
+ def self.date_from(current_node, css_selector=nil)
63
+ date = text_of(current_node, css_selector)
64
+ Date.strptime(date, '%m/%d/%Y').strftime('%Y-%m-%d')
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,32 @@
1
+ require 'sc2_achievements/homepage'
2
+ require 'sc2_achievements/category_page'
3
+
4
+ module SC2Achievements
5
+ def self.for(user_path)
6
+ achievements = get_achievements_for user_path
7
+ achievements.sort_by do |title, achievement|
8
+ [achievement[:date], -(achievement[:recentness] || 7)]
9
+ end.reverse.collect do |title, achievement|
10
+ achievement.delete :recentness
11
+ achievement
12
+ end
13
+ end
14
+
15
+ private
16
+ def self.get_achievements_for(user_path)
17
+ achievements = Page.get_achievements_for user_path
18
+ Page.get_categories_for(user_path).inject(achievements) do |achievements, category|
19
+ add_subcategories_achievements_to(achievements, user_path, category)
20
+ end
21
+ end
22
+
23
+ def self.add_subcategories_achievements_to(achievements, user_path, category)
24
+ Page.get_categories_for(user_path, :category => category).inject(achievements) do |achievements, category|
25
+ category_achievements = Page.get_achievements_for(user_path, :category => category)
26
+ achievements.merge(category_achievements) do |title, old_achievement, new_achievement|
27
+ old_achievement.merge(new_achievement)
28
+ end
29
+ end
30
+ end
31
+
32
+ end