rumble_bundle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 06ac207177a08474c3d5c241685bd4a3e6fcde1e
4
+ data.tar.gz: e14ee7a9be2aa91b10fb7b03955a7ef31289be2c
5
+ SHA512:
6
+ metadata.gz: 90978f729a1b41cb2ab29a7d7abcdf8b5eb39c022a0310d79436a60523d7149da86cc7eaf8555032b5b1fb5af43763804ddcbee55667ed465f0a9b772a92a43e
7
+ data.tar.gz: e0c9e1e7606c8db082936a80d17adad0196683ec412e3f4c97cfb4af1dfef394246b316e8f385cad230b21645bcc0dd4ef9cb4024717a4eb58230a10e1dce0c9
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /bin/console
@@ -0,0 +1,3 @@
1
+ # Contributor Code of Conduct
2
+
3
+ This project does not adhere to a code of conduct.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rumble_bundle.gemspec
4
+ gemspec
@@ -0,0 +1,29 @@
1
+ # RumbleBundle
2
+
3
+ This is a rudimentary info scraper and command line "browser" for the [Humble Bundle](https://www.humblebundle.com/) website, using plain Ruby and Nokogiri.
4
+
5
+ Upon firing up, it will scrape the Game Bundles, Book Bundles, and Mobiles Bundles tabs (along with any sub-tabs) for ongoing bundles. Once it's finished, you can query the scraped information via the command prompt.
6
+
7
+ Available information for each bundle includes: Name, Supported Charities, Donation Tiers and Included Products, Total MSRP, and URL.
8
+
9
+ Available products can also be filtered by tags like `linux`, or `drm-free`, or multiple at once (`windows linux drm-free`).
10
+
11
+ ## Installation
12
+
13
+ Install via:
14
+ ```
15
+ $ gem install rumble_bundle
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Run via:
21
+ ```
22
+ $ rumble_bundle
23
+ ```
24
+
25
+ Then follow the on-screen prompts. :)
26
+
27
+ ## Contributing
28
+
29
+ Bug reports and pull requests are welcome on GitHub at https://github.com/repromancer/rumble_bundle.
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
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 NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <https://unlicense.org>
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "./config/environment"
4
+
5
+ RumbleBundle::CLI.new.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,10 @@
1
+ require 'bundler/setup'
2
+ require 'open-uri'
3
+ require 'nokogiri'
4
+
5
+ require 'rumble_bundle'
6
+
7
+ require_relative '../lib/rumble_bundle/cli'
8
+ require_relative '../lib/rumble_bundle/scraper'
9
+ require_relative '../lib/rumble_bundle/bundle'
10
+ require_relative '../lib/rumble_bundle/product'
@@ -0,0 +1,4 @@
1
+ require "rumble_bundle/version"
2
+
3
+ module RumbleBundle
4
+ end
@@ -0,0 +1,29 @@
1
+ class RumbleBundle::Bundle
2
+
3
+ attr_accessor :name, :tiers, :products, :charities, :total_msrp, :url
4
+
5
+ @@all = []
6
+
7
+ def self.all
8
+ @@all
9
+ end
10
+
11
+ def initialize(data_hash)
12
+ data_hash.each{|key, val| self.send("#{key}=", val)}
13
+ self.class.all << self
14
+ end
15
+
16
+
17
+
18
+ def products=(array)
19
+
20
+ #associate incoming products with Bundle
21
+ @products = array.tap do |products|
22
+ products.each do |product|
23
+ product.bundle = self
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,161 @@
1
+ class RumbleBundle::CLI
2
+
3
+ def start
4
+ puts ""
5
+ puts "Fetching data from HumbleBundle.com..."
6
+ puts ""
7
+
8
+ RumbleBundle::Scraper.new.crawl_site
9
+
10
+ query
11
+
12
+ end
13
+
14
+
15
+ def query
16
+
17
+ bundles = RumbleBundle::Bundle.all
18
+
19
+ puts ""
20
+ puts "Enter a bundle's number to learn more, or enter 'help' for more commands."
21
+ puts "Enter 'quit' to leave the program."
22
+ puts ""
23
+
24
+ bundles.each.with_index(1) do |bundle, i|
25
+ puts "[#{i}] #{bundle.name}"
26
+ end
27
+
28
+ puts ""
29
+ print " "
30
+ input = gets.strip.downcase
31
+ puts ""
32
+
33
+ if input.to_i.between?(1, bundles.length)
34
+ display_bundle(bundles[input.to_i - 1])
35
+ elsif input.to_i < 0 || input.to_i > bundles.length
36
+ puts ""
37
+ puts "Sorry! Please enter a valid number or command."
38
+ query
39
+
40
+ else
41
+ case input
42
+ when 'help'
43
+ help
44
+ when 'quit'
45
+ quit
46
+ else
47
+ filter(input)
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ def filter(input)
54
+ filters = %w[drm-free steam-key windows mac linux android]
55
+ valid = filters & input.split
56
+ if valid.any?
57
+ filtered = RumbleBundle::Product.all.collect{|p| p}
58
+ valid.each do |filter|
59
+ case filter
60
+ when 'windows','mac','linux','android'
61
+ filtered.delete_if{|p| ! p.platforms.include?(filter.capitalize)}
62
+ when 'drm-free'
63
+ filtered.delete_if{|p| ! p.drm_free}
64
+ when 'steam-key'
65
+ filtered.delete_if{|p| ! p.steam_key}
66
+ end
67
+ end
68
+
69
+ if filtered.any?
70
+ puts ""
71
+ puts "Results for: #{valid}"
72
+ puts "---------------------------------------------------------------"
73
+ filtered.collect{|p| p.bundle }.uniq.each do |bundle|
74
+ puts "#{bundle.name} (#{bundle.url})"
75
+ puts ""
76
+ filtered.each{|p| display_product(p) if p.bundle == bundle}
77
+ puts ""
78
+ end
79
+ puts "---------------------------------------------------------------"
80
+ else
81
+ puts ""
82
+ puts "Sorry! No results for: #{valid}"
83
+ puts "---------------------------------------------------------------"
84
+ puts "---------------------------------------------------------------"
85
+ end
86
+
87
+ else
88
+ puts ""
89
+ puts "Sorry! Please enter a valid number or command."
90
+ query
91
+
92
+ end
93
+
94
+ query
95
+
96
+ end
97
+
98
+ def display_bundle(bundle)
99
+ puts "---------------------------------------------------------------"
100
+ puts "#{bundle.name} (#{bundle.url})"
101
+ puts "#{bundle.total_msrp}!"
102
+ puts ""
103
+ puts "Supports:"
104
+ bundle.charities.each do |c|
105
+ print " #{c}"
106
+ puts ","
107
+ end
108
+ puts " or a Charity of Your Choice"
109
+ puts "", ""
110
+ bundle.tiers.each do |tier|
111
+ puts tier, ""
112
+ bundle.products.each do |product|
113
+ if product.tier == tier
114
+ display_product(product)
115
+ end
116
+ end
117
+ puts ""
118
+ end
119
+ puts "---------------------------------------------------------------"
120
+
121
+ query
122
+ end
123
+
124
+ def display_product(product)
125
+ print " #{product.name}"
126
+ print " (#{product.platforms.join(", ")})" if product.platforms.any?
127
+ print " (DRM-Free!)" if product.drm_free
128
+ print " (w/Steam Key!)" if product.steam_key
129
+ puts ""
130
+ puts " #{product.subtitle}" if product.subtitle
131
+ puts ""
132
+ end
133
+
134
+ def help
135
+ puts <<~HEREDOC
136
+ ---------------------------------------------------------------
137
+ You can enter a filter to list all products matching that filter.
138
+ You can combine multiple filters in a space-separated list.
139
+
140
+ e.g. 'drm-free linux'
141
+
142
+ drm-free
143
+ steam-key
144
+ windows
145
+ mac
146
+ linux
147
+ android
148
+
149
+ Press any key to continue.
150
+ ---------------------------------------------------------------
151
+ HEREDOC
152
+ STDIN.getch
153
+ query
154
+ end
155
+
156
+ def quit
157
+ exit
158
+ end
159
+
160
+ end
161
+
@@ -0,0 +1,16 @@
1
+ class RumbleBundle::Product
2
+
3
+ attr_accessor :name, :subtitle, :bundle, :tier, :platforms, :drm_free, :steam_key
4
+
5
+ @@all = []
6
+
7
+ def self.all
8
+ @@all
9
+ end
10
+
11
+ def initialize(data_hash)
12
+ data_hash.each{|key, val| self.send("#{key}=", val)}
13
+ self.class.all << self
14
+ end
15
+
16
+ end
@@ -0,0 +1,128 @@
1
+ class RumbleBundle::Scraper
2
+
3
+ def initialize
4
+ @base_url = 'https://www.humblebundle.com'
5
+ @main_pages = [
6
+ 'https://www.humblebundle.com/',
7
+ 'https://www.humblebundle.com/books/',
8
+ 'https://www.humblebundle.com/mobile/']
9
+ end
10
+
11
+
12
+
13
+ def crawl_site
14
+
15
+ @main_pages.each do |page|
16
+ first_page = Nokogiri::HTML(open(page))
17
+ scrape_bundle(first_page, page)
18
+
19
+ if first_page.at_css(".js-highlight.subtab-button")
20
+ tab_bar = first_page.css(".js-highlight.subtab-button")
21
+ links = tab_bar.collect{|a| @base_url + a.attr("href") }
22
+
23
+ links.each do |link|
24
+ scrape_bundle(Nokogiri::HTML(open(link)), link)
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+
32
+
33
+ def scrape_bundle(html, url)
34
+
35
+ bundle = {
36
+ 'name' => '',
37
+ 'tiers' => [],
38
+ 'products' => [],
39
+ 'charities' => [],
40
+ 'total_msrp' => '',
41
+ 'url' => url
42
+ }
43
+
44
+ bundle['name'] = html.css("title").text.chomp("(pay what you want and help charity)").strip
45
+
46
+ bundle['charities'] = html.css(".charity-image-wrapper img").collect{|img| img.attr("alt")}
47
+
48
+ #for each tier in bundle
49
+ html.css(".main-content-row").each do |tier|
50
+
51
+ #add tier to Bundle @tiers array
52
+ tier_name = tier.css(".dd-header-headline").text.strip
53
+ if tier_name.downcase.include?("charity")
54
+ break
55
+ end
56
+
57
+ bundle['tiers'] << tier_name
58
+
59
+ #and instantiate products from tier
60
+ tier.css(".game-boxes").each do |box|
61
+ scrape_product(box, tier_name).tap do |product|
62
+ bundle['products'] << product
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ bundle['total_msrp'] = html.css('.hr-tagline-text').detect{|e| e.text.include?("worth")}.text.strip
69
+
70
+ RumbleBundle::Bundle.new(bundle)
71
+
72
+ end
73
+
74
+
75
+
76
+ def scrape_product(box, tier)
77
+
78
+ product = {
79
+ 'name' => '',
80
+ 'subtitle' => '',
81
+ 'bundle' => '',
82
+ 'tier' => '',
83
+ 'platforms' => [],
84
+ 'drm_free' => nil,
85
+ 'steam_key' => nil
86
+ }
87
+
88
+ product['name'] = box.css(".dd-image-box-caption").text.strip
89
+ product['tier'] = tier
90
+ product['subtitle'] = if box.at_css(".subtitle")
91
+ box.css(".subtitle .callout-msrp").remove
92
+ if box.css(".subtitle").text.strip != ""
93
+ box.css(".subtitle").text.strip
94
+ else
95
+ nil
96
+ end
97
+ end
98
+
99
+ product['platforms'] = Array.new.tap do |platforms|
100
+ if box.at_css(".dd-availability-icon > i.hb-android")
101
+ platforms << 'Android'
102
+ end
103
+
104
+ if box.at_css(".dd-availability-icon > i.hb-linux")
105
+ platforms << 'Linux'
106
+ end
107
+
108
+ if box.at_css(".dd-availability-icon > i.hb-windows")
109
+ platforms << 'Windows'
110
+ end
111
+
112
+ if box.at_css(".dd-availability-icon > i.hb-osx")
113
+ platforms << 'Mac'
114
+ end
115
+
116
+ end
117
+
118
+ product['drm_free'] = box.at_css(".dd-availability-icon > i.hb-drmfree") ?
119
+ true : false
120
+
121
+ product['steam_key'] = box.at_css(".dd-availability-icon > i.hb-steam") ?
122
+ true : false
123
+
124
+ RumbleBundle::Product.new(product)
125
+
126
+ end
127
+
128
+ end
@@ -0,0 +1,3 @@
1
+ module RumbleBundle
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "rumble_bundle/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rumble_bundle"
8
+ spec.version = RumbleBundle::VERSION
9
+ spec.authors = ["Noah Summers"]
10
+ spec.email = ["connect@repromancer.me"]
11
+
12
+ spec.summary = %q{Simple command line reader for the Humble Bundle website.}
13
+
14
+ spec.description = %q{This is a rudimentary info scraper and command line "browser" for the Humble Bundle website, using plain Ruby and Nokogiri.
15
+
16
+ Upon firing up, it will scrape the Game Bundles, Book Bundles, and Mobiles Bundles tabs (along with any sub-tabs) for ongoing bundles. Once it's finished, you can query the scraped information via the command prompt.
17
+
18
+ Available information for each bundle includes: Name, Supported Charities, Donation Tiers and Included Products, Total MSRP, and URL.
19
+
20
+ Available products can also be filtered by tags like linux, or drm-free, or multiple at once (windows linux drm-free).}
21
+
22
+ spec.homepage = "https://github.com/repromancer/rumble_bundle"
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
25
+ f.match(%r{^(test|spec|features)/})
26
+ end
27
+ spec.bindir = "bin"
28
+ spec.executables << 'rumble_bundle'
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_development_dependency "bundler", "~> 1.15"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "pry"
34
+
35
+ spec.add_dependency "nokogiri", "~> 1.7"
36
+ end
data/spec.md ADDED
@@ -0,0 +1,12 @@
1
+ # Specifications for the CLI Assessment
2
+
3
+ Specs:
4
+ - [x] Have a CLI for interfacing with the application
5
+ > All user interaction occurs through `RumbleBundle::CLI`, beginning with `#start`, looping through `#query`, and ending with `#quit`.
6
+
7
+ - [x] Pull data from an external source
8
+ > Except for the `@base_url` and `@main_pages` used as a starting point by `RumbleBundle::Scraper` (unpredictable sub-urls are fetched during scraping), all data comes from [the Humble Bundle website](http://humblebundle.com/).
9
+
10
+ - [x] Implement both list and detail views
11
+ > Once the site has been scraped, a list of all bundles on sale is presented; the user can then select a bundle to see a detailed view of its content.
12
+ > The user can also filter available products by entering a space-separated list of one or more tags such as `linux` or `drm-free`; search results are sorted by bundle.
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rumble_bundle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Noah Summers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nokogiri
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ description: |-
70
+ This is a rudimentary info scraper and command line "browser" for the Humble Bundle website, using plain Ruby and Nokogiri.
71
+
72
+ Upon firing up, it will scrape the Game Bundles, Book Bundles, and Mobiles Bundles tabs (along with any sub-tabs) for ongoing bundles. Once it's finished, you can query the scraped information via the command prompt.
73
+
74
+ Available information for each bundle includes: Name, Supported Charities, Donation Tiers and Included Products, Total MSRP, and URL.
75
+
76
+ Available products can also be filtered by tags like linux, or drm-free, or multiple at once (windows linux drm-free).
77
+ email:
78
+ - connect@repromancer.me
79
+ executables:
80
+ - rumble_bundle
81
+ extensions: []
82
+ extra_rdoc_files: []
83
+ files:
84
+ - ".gitignore"
85
+ - CODE_OF_CONDUCT.md
86
+ - Gemfile
87
+ - README.md
88
+ - Rakefile
89
+ - UNLICENSE.md
90
+ - bin/console
91
+ - bin/rumble_bundle
92
+ - bin/setup
93
+ - config/environment.rb
94
+ - lib/rumble_bundle.rb
95
+ - lib/rumble_bundle/bundle.rb
96
+ - lib/rumble_bundle/cli.rb
97
+ - lib/rumble_bundle/product.rb
98
+ - lib/rumble_bundle/scraper.rb
99
+ - lib/rumble_bundle/version.rb
100
+ - rumble_bundle.gemspec
101
+ - spec.md
102
+ homepage: https://github.com/repromancer/rumble_bundle
103
+ licenses: []
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.6.11
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Simple command line reader for the Humble Bundle website.
125
+ test_files: []