rumble_bundle 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e0cbcb6413466b3ddec4ca3814ceb129224bee6
4
- data.tar.gz: 14e09388a179ac90ff1d7df78f261875e5ad618c
3
+ metadata.gz: ab912ac3d3ab1fa15544e0d5dde4421d1a9fb30c
4
+ data.tar.gz: '0479427cfff6e9e85a85d04917aaa9718f93c863'
5
5
  SHA512:
6
- metadata.gz: d89e2671e1b6577fbe99bec9951534b548852e381c97e29ccfed36e39067f1dcd4107b1775b0d15fdc9b609a7c1eecdb69b2e46df6b72379d11dfa5baabcc119
7
- data.tar.gz: 88ee8a990f923acf5826737c2eea0d44520ee42a01d96b0fb08d1fb636e396132ce4f104377910801712341dfe1566bc1edc608d3fa7f620e592228d2b5a488e
6
+ metadata.gz: 9675a0ac23f75668bbf7ec75f61d13635b7d224d4cd240f93240c129f9c3ddb7033bf8eb7d1b6218c75d513a72f8f12bf157b95002d32718262b99918aaa1941
7
+ data.tar.gz: 54f374f52c02574af06e8e06ea3649d5dc82173f68e9b8e3213168d8677d6ca6a789c0210e24003a782607a2141c28bbd53cbfee66a7533efa37681a7fec2820
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in rumble_bundle.gemspec
4
3
  gemspec
data/README.md CHANGED
@@ -27,3 +27,9 @@ Then follow the on-screen prompts. :)
27
27
  ## Contributing
28
28
 
29
29
  Bug reports and pull requests are welcome on GitHub at https://github.com/repromancer/rumble_bundle.
30
+
31
+ ## License
32
+
33
+ This is free and unencumbered software released into the public domain.
34
+
35
+ For more information, please refer to the included UNLICENSE.md or <https://unlicense.org>
data/bin/setup CHANGED
@@ -3,6 +3,4 @@ set -euo pipefail
3
3
  IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
6
+ bundle install
@@ -4,7 +4,8 @@ require 'nokogiri'
4
4
 
5
5
  require 'rumble_bundle'
6
6
 
7
- require_relative '../lib/rumble_bundle/cli'
8
- require_relative '../lib/rumble_bundle/scraper'
9
7
  require_relative '../lib/rumble_bundle/bundle'
10
- require_relative '../lib/rumble_bundle/product'
8
+ require_relative '../lib/rumble_bundle/tier'
9
+ require_relative '../lib/rumble_bundle/product'
10
+ require_relative '../lib/rumble_bundle/scraper'
11
+ require_relative '../lib/rumble_bundle/cli'
File without changes
@@ -1,6 +1,6 @@
1
1
  class RumbleBundle::Bundle
2
2
 
3
- attr_accessor :name, :tiers, :products, :charities, :total_msrp, :url
3
+ attr_accessor :name, :url, :tiers, :charities, :total_msrp
4
4
 
5
5
  @@all = []
6
6
 
@@ -8,22 +8,13 @@ class RumbleBundle::Bundle
8
8
  @@all
9
9
  end
10
10
 
11
- def initialize(data_hash)
12
- data_hash.each{|key, val| self.send("#{key}=", val)}
11
+ def initialize
13
12
  self.class.all << self
14
13
  end
15
14
 
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
-
15
+ def products
16
+ # Leverage Bundle#tiers to expose a flat array of a Bundle's Products.
17
+ self.tiers.collect{|t| t.products}.flatten
27
18
  end
28
19
 
29
20
  end
@@ -1,5 +1,6 @@
1
1
  class RumbleBundle::CLI
2
2
 
3
+ # Scrape site and allow user to begin browsing.
3
4
  def start
4
5
  puts ""
5
6
  puts "Fetching data from HumbleBundle.com..."
@@ -12,6 +13,7 @@ class RumbleBundle::CLI
12
13
  end
13
14
 
14
15
 
16
+
15
17
  def query
16
18
 
17
19
  bundles = RumbleBundle::Bundle.all
@@ -21,22 +23,28 @@ class RumbleBundle::CLI
21
23
  puts "Enter 'quit' to leave the program."
22
24
  puts ""
23
25
 
26
+ # List Bundles in numbered order
24
27
  bundles.each.with_index(1) do |bundle, i|
25
28
  puts "[#{i}] #{bundle.name}"
26
29
  end
27
30
 
31
+ # Get user input
28
32
  puts ""
29
33
  print " "
30
34
  input = gets.strip.downcase
31
35
  puts ""
32
36
 
37
+ # If input is a valid number, display the corresponding Bundle
33
38
  if input.to_i.between?(1, bundles.length)
34
39
  display_bundle(bundles[input.to_i - 1])
40
+
41
+ # If input is an invalid number, restart #query
35
42
  elsif input.to_i < 0 || input.to_i > bundles.length
36
43
  puts ""
37
44
  puts "Sorry! Please enter a valid number or command."
38
45
  query
39
46
 
47
+ # Check for 'help' or 'quit' commands, else pass input to #filter
40
48
  else
41
49
  case input
42
50
  when 'help'
@@ -50,9 +58,14 @@ class RumbleBundle::CLI
50
58
 
51
59
  end
52
60
 
61
+
62
+
53
63
  def filter(input)
64
+ # Compare input to recognized tags
54
65
  filters = %w[drm-free steam-key windows mac linux android]
55
66
  valid = filters & input.split
67
+
68
+ # If input is valid, collect an array of all Products, remove any that do not match search tags, then display results in a formatted list
56
69
  if valid.any?
57
70
  filtered = RumbleBundle::Product.all.collect{|p| p}
58
71
  valid.each do |filter|
@@ -73,8 +86,13 @@ class RumbleBundle::CLI
73
86
  filtered.collect{|p| p.bundle }.uniq.each do |bundle|
74
87
  puts "#{bundle.name} (#{bundle.url})"
75
88
  puts ""
76
- filtered.each{|p| display_product(p) if p.bundle == bundle}
77
- puts ""
89
+ bundle.tiers.each do |tier|
90
+ if filtered.detect{|p| p.tier == tier}
91
+ puts " #{tier.description}", ""
92
+ filtered.each{|p| display_product(p) if p.tier == tier}
93
+ puts ""
94
+ end
95
+ end
78
96
  end
79
97
  puts "---------------------------------------------------------------"
80
98
  else
@@ -95,25 +113,24 @@ class RumbleBundle::CLI
95
113
 
96
114
  end
97
115
 
116
+
117
+
118
+ # Display formatted information for given Bundle
98
119
  def display_bundle(bundle)
99
120
  puts "---------------------------------------------------------------"
100
121
  puts "#{bundle.name} (#{bundle.url})"
101
122
  puts "#{bundle.total_msrp}!"
102
123
  puts ""
103
- puts "Supports:"
124
+ puts " Supports:"
104
125
  bundle.charities.each do |c|
105
- print " #{c}"
126
+ print " #{c}"
106
127
  puts ","
107
128
  end
108
- puts " or a Charity of Your Choice"
129
+ puts " or a Charity of Your Choice"
109
130
  puts "", ""
110
131
  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
132
+ puts " #{tier.description}", ""
133
+ tier.products.each{|p| display_product(p)}
117
134
  puts ""
118
135
  end
119
136
  puts "---------------------------------------------------------------"
@@ -122,15 +139,16 @@ class RumbleBundle::CLI
122
139
  end
123
140
 
124
141
  def display_product(product)
125
- print " #{product.name}"
142
+ print " #{product.name}"
126
143
  print " (#{product.platforms.join(", ")})" if product.platforms.any?
127
144
  print " (DRM-Free!)" if product.drm_free
128
145
  print " (w/Steam Key!)" if product.steam_key
129
146
  puts ""
130
- puts " #{product.subtitle}" if product.subtitle
147
+ puts " #{product.subtitle}" if product.subtitle
131
148
  puts ""
132
149
  end
133
150
 
151
+ # Display help
134
152
  def help
135
153
  puts <<~HEREDOC
136
154
  ---------------------------------------------------------------
@@ -153,6 +171,7 @@ class RumbleBundle::CLI
153
171
  query
154
172
  end
155
173
 
174
+ # Quit
156
175
  def quit
157
176
  exit
158
177
  end
@@ -8,8 +8,7 @@ class RumbleBundle::Product
8
8
  @@all
9
9
  end
10
10
 
11
- def initialize(data_hash)
12
- data_hash.each{|key, val| self.send("#{key}=", val)}
11
+ def initialize
13
12
  self.class.all << self
14
13
  end
15
14
 
@@ -1,6 +1,7 @@
1
1
  class RumbleBundle::Scraper
2
2
 
3
3
  def initialize
4
+ # Set URLs used as starting points for scraping
4
5
  @base_url = 'https://www.humblebundle.com'
5
6
  @main_pages = [
6
7
  'https://www.humblebundle.com/',
@@ -11,15 +12,18 @@ class RumbleBundle::Scraper
11
12
 
12
13
 
13
14
  def crawl_site
14
-
15
+ # For each main page
15
16
  @main_pages.each do |page|
17
+ # scrape the bundle from that page
16
18
  first_page = Nokogiri::HTML(open(page))
17
19
  scrape_bundle(first_page, page)
18
20
 
21
+ # and if there are any sub-pages
19
22
  if first_page.at_css(".js-highlight.subtab-button")
20
23
  tab_bar = first_page.css(".js-highlight.subtab-button")
21
24
  links = tab_bar.collect{|a| @base_url + a.attr("href") }
22
25
 
26
+ # scrape them for bundles too
23
27
  links.each do |link|
24
28
  scrape_bundle(Nokogiri::HTML(open(link)), link)
25
29
  end
@@ -32,96 +36,96 @@ class RumbleBundle::Scraper
32
36
 
33
37
  def scrape_bundle(html, url)
34
38
 
35
- bundle = {
36
- 'name' => '',
37
- 'tiers' => [],
38
- 'products' => [],
39
- 'charities' => [],
40
- 'total_msrp' => '',
41
- 'url' => url
42
- }
39
+ RumbleBundle::Bundle.new.tap do |bundle|
43
40
 
44
- bundle['name'] = html.css("title").text.chomp("(pay what you want and help charity)").strip
41
+ bundle.url = url
45
42
 
46
- bundle['charities'] = html.css(".charity-image-wrapper img").collect{|img| img.attr("alt")}
43
+ # Scrape Bundle metadata
47
44
 
48
- #for each tier in bundle
49
- html.css(".main-content-row").each do |tier|
45
+ bundle.name = html.css("title").text.chomp("(pay what you want and help charity)").strip
50
46
 
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
47
+ bundle.charities = html.css(".charity-image-wrapper img").collect{|img| img.attr("alt")}
56
48
 
57
- bundle['tiers'] << tier_name
49
+ bundle.total_msrp = html.css('.hr-tagline-text').detect{|e| e.text.include?("worth")}.text.strip
58
50
 
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
51
 
66
- end
52
+ bundle.tiers = Array.new.tap do |bundle_tiers|
53
+
54
+ # For each 'main content row' on the page, instantiate a Tier with its Products and add that Tier to the Bundle's @tiers array
55
+ html.css(".main-content-row").each do |row|
56
+
57
+ row_title = row.css(".dd-header-headline").text.strip
67
58
 
68
- bundle['total_msrp'] = html.css('.hr-tagline-text').detect{|e| e.text.include?("worth")}.text.strip
59
+ # Stop iterating if scraper has reached a row which is not a Tier
60
+ if row_title.downcase.include?("charity")
61
+ break
62
+ end
69
63
 
70
- RumbleBundle::Bundle.new(bundle)
64
+ bundle_tiers << RumbleBundle::Tier.new.tap do |tier|
65
+ tier.description = row_title
66
+ tier.bundle = bundle
67
+ tier.products = Array.new.tap do |tier_products|
68
+ row.css(".game-boxes").each do |box|
69
+ scrape_product(box, bundle, tier).tap do |product|
70
+ tier_products << product
71
+ end
72
+ end
73
+ end
74
+ end
71
75
 
76
+ end
77
+
78
+ end
79
+
80
+ end
72
81
  end
73
82
 
74
83
 
84
+ # Self-explanatory
85
+ def scrape_product(box, bundle, tier)
75
86
 
76
- def scrape_product(box, tier)
87
+ RumbleBundle::Product.new.tap do |product|
77
88
 
78
- product = {
79
- 'name' => '',
80
- 'subtitle' => '',
81
- 'bundle' => '',
82
- 'tier' => '',
83
- 'platforms' => [],
84
- 'drm_free' => nil,
85
- 'steam_key' => nil
86
- }
89
+ product.bundle = bundle
90
+ product.tier = tier
87
91
 
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
92
+ product.name = box.css(".dd-image-box-caption").text.strip
98
93
 
99
- product['platforms'] = Array.new.tap do |platforms|
100
- if box.at_css(".dd-availability-icon > i.hb-android")
101
- platforms << 'Android'
94
+ product.subtitle = if box.at_css(".subtitle")
95
+ box.css(".subtitle .callout-msrp").remove
96
+ if box.css(".subtitle").text.strip != ""
97
+ box.css(".subtitle").text.strip
98
+ else
99
+ nil
100
+ end
102
101
  end
103
102
 
104
- if box.at_css(".dd-availability-icon > i.hb-linux")
105
- platforms << 'Linux'
106
- end
103
+ product.platforms = Array.new.tap do |platforms|
104
+ if box.at_css(".dd-availability-icon > i.hb-android")
105
+ platforms << 'Android'
106
+ end
107
107
 
108
- if box.at_css(".dd-availability-icon > i.hb-windows")
109
- platforms << 'Windows'
110
- end
108
+ if box.at_css(".dd-availability-icon > i.hb-linux")
109
+ platforms << 'Linux'
110
+ end
111
111
 
112
- if box.at_css(".dd-availability-icon > i.hb-osx")
113
- platforms << 'Mac'
114
- end
112
+ if box.at_css(".dd-availability-icon > i.hb-windows")
113
+ platforms << 'Windows'
114
+ end
115
115
 
116
- end
116
+ if box.at_css(".dd-availability-icon > i.hb-osx")
117
+ platforms << 'Mac'
118
+ end
117
119
 
118
- product['drm_free'] = box.at_css(".dd-availability-icon > i.hb-drmfree") ?
119
- true : false
120
+ end
121
+
122
+ product.drm_free = box.at_css(".dd-availability-icon > i.hb-drmfree") ?
123
+ true : false
120
124
 
121
- product['steam_key'] = box.at_css(".dd-availability-icon > i.hb-steam") ?
122
- true : false
125
+ product.steam_key = box.at_css(".dd-availability-icon > i.hb-steam") ?
126
+ true : false
123
127
 
124
- RumbleBundle::Product.new(product)
128
+ end
125
129
 
126
130
  end
127
131
 
@@ -0,0 +1,3 @@
1
+ class RumbleBundle::Tier
2
+ attr_accessor :bundle, :products, :description
3
+ end
@@ -1,3 +1,3 @@
1
1
  module RumbleBundle
2
- VERSION = "0.1.1"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
25
25
  f.match(%r{^(test|spec|features)/})
26
26
  end
27
- spec.bindir = "bin"
27
+ spec.bindir = "exe"
28
28
  spec.executables << 'rumble_bundle'
29
29
  spec.require_paths = ["lib"]
30
30
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rumble_bundle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Summers
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2017-06-30 00:00:00.000000000 Z
11
+ date: 2017-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -88,14 +88,15 @@ files:
88
88
  - Rakefile
89
89
  - UNLICENSE.md
90
90
  - bin/console
91
- - bin/rumble_bundle
92
91
  - bin/setup
93
92
  - config/environment.rb
93
+ - exe/rumble_bundle
94
94
  - lib/rumble_bundle.rb
95
95
  - lib/rumble_bundle/bundle.rb
96
96
  - lib/rumble_bundle/cli.rb
97
97
  - lib/rumble_bundle/product.rb
98
98
  - lib/rumble_bundle/scraper.rb
99
+ - lib/rumble_bundle/tier.rb
99
100
  - lib/rumble_bundle/version.rb
100
101
  - rumble_bundle.gemspec
101
102
  - spec.md