ratebeer 0.0.8 → 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.
@@ -1,6 +1,6 @@
1
- require_relative "brewery"
2
- require_relative "style"
3
- require_relative "urls"
1
+ require_relative 'brewery'
2
+ require_relative 'style'
3
+ require_relative 'urls'
4
4
 
5
5
  module RateBeer
6
6
  class Location
@@ -25,41 +25,52 @@ module RateBeer
25
25
  #
26
26
  def initialize(id, location_type: nil, name: nil, **options)
27
27
  super
28
- if location_type.nil? || !([:country, :region].include?(location_type))
29
- raise ArgumentError.new("location_type must be supplied and must be "\
30
- "either country or region")
28
+ if location_type.nil? || ![:country, :region].include?(location_type)
29
+ raise ArgumentError.new('location_type must be supplied and must be '\
30
+ 'either country or region')
31
31
  end
32
32
  @location_type = location_type
33
33
  end
34
34
 
35
+ def doc
36
+ unless instance_variable_defined?('@doc')
37
+ @doc = noko_doc(url)
38
+ validate_location
39
+ end
40
+ @doc
41
+ end
42
+
43
+ def heading
44
+ @heading ||= doc.at_css('.col-lg-9')
45
+ end
46
+
35
47
  private
36
48
 
37
- # Retrive details about this location from the website.
38
- #
39
- # This method stores the retrived details in instance variables
40
- # of the location instance.
41
- #
42
- def retrieve_details
43
- doc = noko_doc(url)
44
- heading = doc.css('.col-lg-9').first
45
- brewery_info = doc.css('#tabs table')
49
+ def validate_location
50
+ if @doc.at_css('h1').text.include? 'n/a'
51
+ raise PageNotFoundError.new("#{self.class.name} not found - #{id}")
52
+ end
53
+ end
46
54
 
55
+ def scrape_name
47
56
  @name = heading.at_css('h1')
48
57
  .text
49
58
  .split('Breweries')
50
59
  .first
51
60
  .strip
52
- if @name == "n/a" || @name == "RateBeer Robot Oops!"
53
- raise PageNotFoundError.new("#{self.class.name} not found - #{id}")
54
- end
61
+ end
55
62
 
63
+ def scrape_num_breweries
56
64
  @num_breweries = heading.at_css('li.active')
57
65
  .text
58
66
  .scan(/Active \((\d*)\)/)
59
67
  .first
60
68
  .first
61
69
  .to_i
70
+ end
62
71
 
72
+ def scrape_breweries
73
+ brewery_info = doc.css('#tabs table')
63
74
  @breweries = brewery_info.flat_map.with_index do |tbl, i|
64
75
  status = i == 0 ? 'Active' : 'Out of Business'
65
76
 
@@ -83,7 +94,6 @@ module RateBeer
83
94
  status: status)
84
95
  end
85
96
  end
86
- nil
87
97
  end
88
98
 
89
99
  # Return URL for page containing information on this location.
@@ -97,7 +107,7 @@ module RateBeer
97
107
  when :region
98
108
  URI.join(BASE_URL, region_url(id))
99
109
  else
100
- raise "invalid location type: #{@location_type.to_s}"
110
+ raise "invalid location type: #{@location_type}"
101
111
  end
102
112
  end
103
113
  end
@@ -16,9 +16,7 @@ module RateBeer
16
16
  def self.included(base)
17
17
  base.data_keys.each do |attr|
18
18
  define_method(attr) do
19
- unless instance_variable_defined?("@#{attr}")
20
- retrieve_details
21
- end
19
+ send("scrape_#{attr}") unless instance_variable_defined?("@#{attr}")
22
20
  instance_variable_get("@#{attr}")
23
21
  end
24
22
  end
@@ -60,6 +58,11 @@ module RateBeer
60
58
  end
61
59
  end
62
60
 
61
+ # Extracts an ID# from an a element containing a link to an entity.
62
+ def id_from_link(node)
63
+ node.attribute('href').value.split('/').last.to_i
64
+ end
65
+
63
66
  # Return full details of the scraped entity in a Hash.
64
67
  #
65
68
  def full_details
@@ -6,7 +6,6 @@ require_relative 'scraping'
6
6
  require_relative 'urls'
7
7
 
8
8
  module RateBeer
9
-
10
9
  # Stop I18N from enforcing locale, to avoid error message
11
10
  I18n.enforce_available_locales = false
12
11
 
@@ -29,7 +28,7 @@ module RateBeer
29
28
  # a search.
30
29
  #
31
30
  def search(query)
32
- s = self.new(query)
31
+ s = new(query)
33
32
  { beers: s.beers,
34
33
  breweries: s.breweries }
35
34
  end
@@ -41,8 +40,9 @@ module RateBeer
41
40
  #
42
41
  # @param [String] query Term to use to search RateBeer
43
42
  #
44
- def initialize(query)
43
+ def initialize(query, scrape_beer_brewers = false)
45
44
  self.query = query
45
+ @scrape_breweries = scrape_beer_brewers
46
46
  end
47
47
 
48
48
  # Setter for query instance variable.
@@ -52,6 +52,10 @@ module RateBeer
52
52
  @query = fix_query_param(qry)
53
53
  end
54
54
 
55
+ def ==(other)
56
+ query == other.query
57
+ end
58
+
55
59
  def inspect
56
60
  num_beers = @beers && @beers.count || 0
57
61
  num_breweries = @breweries && @breweries.count || 0
@@ -71,7 +75,6 @@ module RateBeer
71
75
  #
72
76
  def run_search
73
77
  @beers, @breweries = nil
74
- doc = post_request(URI.join(BASE_URL, SEARCH_URL), post_params)
75
78
  tables = doc.css('h2').map(&:text).zip(doc.css('table'))
76
79
  beers, breweries = nil
77
80
  tables.each do |(heading, table)|
@@ -84,24 +87,44 @@ module RateBeer
84
87
  end
85
88
 
86
89
  # RateBeer is inconsistent with searching for IPAs. If IPA is in the name
87
- # of the beer, replace IPA with India Pale Ale, and add the additional
90
+ # of the beer, replace IPA with India Pale Ale, and add the additional
88
91
  # results to these results.
89
- if query.downcase.include?(" ipa")
90
- alt_query = query.downcase.gsub(" ipa", " india pale ale")
92
+ if query.downcase.include?(' ipa')
93
+ alt_query = query.downcase.gsub(' ipa', ' india pale ale')
91
94
  extra_beers = self.class.new(alt_query).run_search.beers
92
95
  @beers = ((@beers || []) + (extra_beers || [])).uniq
93
96
  end
94
- return self
97
+ self
95
98
  end
96
99
 
97
100
  alias retrieve_details run_search
98
101
 
99
102
  private
103
+
104
+ def doc
105
+ @doc ||= post_request(URI.join(BASE_URL, SEARCH_URL), post_params)
106
+ end
107
+
108
+ def scrape_beers
109
+ unless instance_variable_defined?('@beers')
110
+ run_search
111
+ @beers = @beers.sort_by(&:id)
112
+ end
113
+ @beers
114
+ end
115
+
116
+ def scrape_breweries
117
+ unless instance_variable_defined?('@breweries')
118
+ run_search
119
+ @breweries = @breweries.sort_by(&:id)
120
+ end
121
+ @breweries
122
+ end
100
123
 
101
124
  # Generate parameters to use in POST request.
102
125
  #
103
126
  def post_params
104
- { "BeerName" => @query }
127
+ { 'BeerName' => @query }
105
128
  end
106
129
 
107
130
  # Process breweries table returned in search.
@@ -118,9 +141,9 @@ module RateBeer
118
141
  def process_breweries_table(table)
119
142
  table.css('tr').map do |row|
120
143
  result = [:id, :name, :location, :url].zip([nil]).to_h
121
- result[:name], result[:location] = row.element_children.map { |x|
122
- fix_characters(x.text)
123
- }
144
+ result[:name], result[:location] = row.element_children.map do |x|
145
+ fix_characters(x.text)
146
+ end
124
147
  result[:url] = row.at_css('a')['href']
125
148
  result[:id] = result[:url].split('/').last.to_i
126
149
  Brewery.new(result[:id], name: result[:name])
@@ -168,7 +191,7 @@ module RateBeer
168
191
  result[:url] = row.at_css('a')['href']
169
192
  result[:id] = result[:url].split('/').last.to_i
170
193
  b = Beer.new(result[:id], name: result[:name])
171
- b.brewery.name
194
+ b.brewery.name if @scrape_beer_brewers
172
195
  b
173
196
  end
174
197
 
@@ -24,16 +24,16 @@ module RateBeer
24
24
 
25
25
  # Scrape all styles.
26
26
  #
27
- # RateBeer provides a styles landing page, with links through to info on
28
- # each style listed thereon. This method scrapes style info with links
27
+ # RateBeer provides a styles landing page, with links through to info on
28
+ # each style listed thereon. This method scrapes style info with links
29
29
  # to the more detailed pages.
30
30
  #
31
- # @param [Boolean] hidden_styles Flag for whether to include hidden
31
+ # @param [Boolean] hidden_styles Flag for whether to include hidden
32
32
  # styles.
33
- # @return [Array<RateBeer::Style>] List of styles with links etc. to
33
+ # @return [Array<RateBeer::Style>] List of styles with links etc. to
34
34
  # detailed pages
35
35
  #
36
- def all_styles(include_hidden=false)
36
+ def all_styles(include_hidden = false)
37
37
  doc = Scraping.noko_doc(URI.join(BASE_URL, '/beerstyles/'))
38
38
  root = doc.at_css('div.container-fluid table')
39
39
 
@@ -41,15 +41,15 @@ module RateBeer
41
41
  style_node = root.css('.styleGroup')
42
42
 
43
43
  styles = style_node.flat_map.with_index do |list, i|
44
- list.css('a').map do |x|
44
+ list.css('a').map do |x|
45
45
  category = categories[i]
46
- Style.new(x['href'].split('/').last.to_i, name: x.text).tap { |s|
47
- s.category = category
48
- }
46
+ Style.new(x['href'].split('/').last.to_i, name: x.text).tap do |s|
47
+ s.category = category
48
+ end
49
49
  end
50
50
  end
51
51
  if include_hidden
52
- styles += hidden_styles
52
+ styles + hidden_styles
53
53
  else
54
54
  styles
55
55
  end
@@ -57,13 +57,13 @@ module RateBeer
57
57
 
58
58
  # Scrape hidden style information
59
59
  #
60
- # RateBeer has a number of styles not accessible from the "beerstyles"
60
+ # RateBeer has a number of styles not accessible from the "beerstyles"
61
61
  # landing page. This method scrapes these.
62
62
  #
63
63
  # @return [Array<Hash>] List of hidden styles
64
64
  #
65
65
  def hidden_styles
66
- hidden_ids = [40, 41, 57, 59, 66, 67, 68, 69, 70,
66
+ hidden_ids = [40, 41, 57, 59, 66, 67, 68, 69, 70,
67
67
  75, 83, 99, 104, 106, 116, 119, 120]
68
68
  hidden_ids.map do |id|
69
69
  Style.new(id)
@@ -73,32 +73,45 @@ module RateBeer
73
73
 
74
74
  private
75
75
 
76
- # Retrieve details about this style from the website.
77
- #
78
- # This method stores the retrieved details in instance variables of
79
- # this style instance.
80
- #
81
- def retrieve_details
82
- doc = noko_doc(URI.join(BASE_URL, style_url(id)))
83
- root = doc.at_css('.container-fluid')
84
- beer_list = noko_doc(URI.join(BASE_URL, style_beers_url(id)))
85
-
86
- if !root.nil?
87
- @name = root.at_css('h1').text.strip
88
- else
89
- raise PageNotFoundError.new("style not found - ##{id}")
76
+ def doc
77
+ unless instance_variable_defined?('@doc')
78
+ @doc = noko_doc(URI.join(BASE_URL, style_url(id)))
79
+ validate_style
90
80
  end
81
+ @doc
82
+ end
83
+
84
+ def validate_style
85
+ raise PageNotFoundError.new("style not found - ##{id}") if root.nil?
86
+ end
87
+
88
+ def root
89
+ @root ||= doc.at_css('.container-fluid')
90
+ end
91
91
 
92
+ def beer_list
93
+ @beer_list = noko_doc(URI.join(BASE_URL, style_beers_url(id)))
94
+ end
95
+
96
+ def scrape_name
97
+ @name = root.at_css('h1').text.strip
98
+ end
99
+
100
+ def scrape_description
92
101
  @description = root.at_css('#styleDescription').text
102
+ end
103
+
104
+ def scrape_glassware
93
105
  @glassware = root.css('.glassblurb').map { |x| x.text.strip }
106
+ end
94
107
 
108
+ def scrape_beers
95
109
  @beers = beer_list.css('tr').drop(1).map do |row|
96
110
  cells = row.css('td')
97
111
  url = cells[1].at_css('a')['href']
98
- [cells[0].text.to_i, Beer.new(url.split('/').last,
112
+ [cells[0].text.to_i, Beer.new(url.split('/').last,
99
113
  name: fix_characters(cells[1].text))]
100
114
  end.to_h
101
- nil
102
115
  end
103
116
  end
104
117
  end
data/lib/ratebeer/urls.rb CHANGED
@@ -19,11 +19,15 @@ module RateBeer
19
19
  end
20
20
 
21
21
  # Return URL to info page for brewery with id
22
- #
23
22
  def brewery_url(id)
24
23
  "/brewers/a/#{id}/"
25
24
  end
26
25
 
26
+ # Return URL to beer listing page for brewery with id.
27
+ def brewery_beers_url(id)
28
+ "/Ratings/Beer/ShowBrewerBeers.asp?BrewerID=#{id}"
29
+ end
30
+
27
31
  # Return URL to info page for country with id
28
32
  def country_url(id)
29
33
  "/breweries/a/0/#{id}/"
@@ -3,43 +3,43 @@ require 'spec_helper'
3
3
  describe RateBeer::Beer do
4
4
  before :all do
5
5
  @valid = RateBeer::Beer.new(1411) # ID for Tennents Lager (sorry...)
6
- @retired = RateBeer::Beer.new(213225) # ID for BrewDog Vice Bier
7
- @with_name = RateBeer::Beer.new(422, name: "Stone IPA")
6
+ @retired = RateBeer::Beer.new(213_225) # ID for BrewDog Vice Bier
7
+ @with_name = RateBeer::Beer.new(422, name: 'Stone IPA')
8
8
  end
9
9
 
10
- describe "#new" do
11
- it "creates a beer instance" do
10
+ describe '#new' do
11
+ it 'creates a beer instance' do
12
12
  expect(@valid).to be_a RateBeer::Beer
13
13
  end
14
14
 
15
- it "requires an ID# as parameter" do
15
+ it 'requires an ID# as parameter' do
16
16
  expect { RateBeer::Beer.new }.to raise_error(ArgumentError)
17
17
  end
18
18
 
19
- it "accepts a name parameter" do
19
+ it 'accepts a name parameter' do
20
20
  expect(@with_name).to be_a RateBeer::Beer
21
21
  end
22
22
  end
23
23
 
24
- describe "#name" do
25
- it "retrieves name details from RateBeer if not present" do
26
- expect(@valid.name).to eq "Tennents Lager"
24
+ describe '#name' do
25
+ it 'retrieves name details from RateBeer if not present' do
26
+ expect(@valid.name).to eq 'Tennents Lager'
27
27
  end
28
28
 
29
- it "uses name details if passed as parameter" do
30
- expect(@with_name.name).to eq "Stone IPA"
29
+ it 'uses name details if passed as parameter' do
30
+ expect(@with_name.name).to eq 'Stone IPA'
31
31
  end
32
32
  end
33
33
 
34
- describe "#retired" do
35
- it "states that beer is retired when this is the case" do
34
+ describe '#retired' do
35
+ it 'states that beer is retired when this is the case' do
36
36
  expect(@valid.retired).to be false
37
37
  expect(@retired.retired).to be true
38
38
  end
39
39
  end
40
40
 
41
- describe "#full_details" do
42
- it "returns full information about the beer" do
41
+ describe '#full_details' do
42
+ it 'returns full information about the beer' do
43
43
  expect(@valid.full_details).to include(:id,
44
44
  :name,
45
45
  :brewery,
@@ -3,51 +3,58 @@ require 'spec_helper'
3
3
  describe RateBeer::Brewery do
4
4
  before :all do
5
5
  @valid = RateBeer::Brewery.new(8534) # ID for BrewDog
6
- @with_name = RateBeer::Brewery.new(1069, name: "Cantillon Brewery")
6
+ @with_name = RateBeer::Brewery.new(1069, name: 'Cantillon Brewery')
7
7
  end
8
8
 
9
- describe "#new" do
10
- it "creates a brewery instance" do
9
+ describe '#new' do
10
+ it 'creates a brewery instance' do
11
11
  expect(@valid).to be_a RateBeer::Brewery
12
12
  end
13
13
 
14
- it "requires an ID# as parameter" do
14
+ it 'requires an ID# as parameter' do
15
15
  expect { RateBeer::Brewery.new }.to raise_error(ArgumentError)
16
16
  end
17
17
 
18
- it "accepts a name parameter" do
18
+ it 'accepts a name parameter' do
19
19
  expect(@with_name).to be_a RateBeer::Brewery
20
20
  end
21
21
  end
22
22
 
23
- describe "#name" do
24
- it "retrieves name from RateBeer if not passed as parameter" do
25
- expect(@valid.name).to eq "BrewDog"
23
+ describe '#name' do
24
+ it 'retrieves name from RateBeer if not passed as parameter' do
25
+ expect(@valid.name).to eq 'BrewDog'
26
26
  end
27
27
 
28
- it "uses name details if passed as parameter" do
29
- expect(@with_name.name).to eq "Cantillon Brewery"
28
+ it 'uses name details if passed as parameter' do
29
+ expect(@with_name.name).to eq 'Cantillon Brewery'
30
30
  end
31
31
  end
32
32
 
33
- describe "#beers" do
34
- it "returns a non-empty array of beers produced by the brewery" do
33
+ describe '#beers' do
34
+ it 'returns a non-empty array of beers produced by the brewery' do
35
35
  expect(@valid.beers).to_not be_empty
36
36
  end
37
37
 
38
- it "returns an array of beer instances" do
38
+ it 'returns an array of beer instances' do
39
39
  @valid.beers.each { |b| expect(b).to be_a RateBeer::Beer }
40
40
  end
41
41
 
42
- it "returns a list of beers produced by brewery" do
43
- beers = [215065, 98242, 172237, 76701, 178585, 162521, 87321, 118987,
44
- 119594].map { |id| RateBeer::Beer.new(id) }
42
+ it 'returns a list of beers produced by brewery' do
43
+ beers = [215_065,
44
+ 98_242,
45
+ 172_237,
46
+ 76_701,
47
+ 178_585,
48
+ 162_521,
49
+ 87_321,
50
+ 118_987,
51
+ 119_594].map { |id| RateBeer::Beer.new(id) }
45
52
  expect(@valid.beers).to include(*beers)
46
53
  end
47
54
  end
48
55
 
49
- describe "#full_details" do
50
- it "returns full information about the brewery" do
56
+ describe '#full_details' do
57
+ it 'returns full information about the brewery' do
51
58
  expect(@valid.full_details).to include(:id,
52
59
  :name,
53
60
  :url,