rumble_bundle 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []