ratebeer 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -3
- data/lib/ratebeer.rb +2 -2
- data/lib/ratebeer/beer.rb +127 -166
- data/lib/ratebeer/beer/alias.rb +64 -0
- data/lib/ratebeer/brewery.rb +90 -163
- data/lib/ratebeer/brewery/beer_list.rb +101 -0
- data/lib/ratebeer/location.rb +16 -21
- data/lib/ratebeer/review.rb +5 -5
- data/lib/ratebeer/scraping.rb +7 -5
- data/lib/ratebeer/search.rb +5 -5
- data/lib/ratebeer/style.rb +4 -4
- data/lib/ratebeer/urls.rb +4 -3
- data/spec/lib/ratebeer/beer_spec.rb +7 -9
- data/spec/lib/ratebeer/brewery_spec.rb +7 -7
- data/spec/lib/ratebeer/country_spec.rb +1 -1
- data/spec/lib/ratebeer/region_spec.rb +1 -1
- data/spec/lib/ratebeer/review_spec.rb +2 -2
- data/spec/lib/ratebeer/search_spec.rb +5 -12
- data/spec/lib/ratebeer_spec.rb +2 -2
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf9d9f5539a98733d67f57195ea03c9517b82278
|
4
|
+
data.tar.gz: 79d95ea76346b5e93b1bb73ac27ca088c746d4f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6bcd1a25689ec578662d50f7bfafd271a525eb548f4eebb00724184c92ceb055a67346c5ca89f46a782d08b3c93f24c0ccda8d230edef609046df20e836d2eab
|
7
|
+
data.tar.gz: aeb41234258c933f21c410c77f9d3d96f8b38c3f3ca1f532624d27b2b4f51d456c88e29f3e2ad68e6b18dbc703ed357fab49631e296fb3e19d7fcb1262b9f7e9
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/lib/ratebeer.rb
CHANGED
@@ -13,7 +13,7 @@ module RateBeer
|
|
13
13
|
# @return [RateBeer::Beer] beer with passed ID#
|
14
14
|
#
|
15
15
|
def beer(id, name = nil)
|
16
|
-
Beer.new(id, name: name)
|
16
|
+
Beer::Beer.new(id, name: name)
|
17
17
|
end
|
18
18
|
|
19
19
|
# Create new brewery instance, using ID and name passed as arguments.
|
@@ -23,7 +23,7 @@ module RateBeer
|
|
23
23
|
# @return [RateBeer::Brewery] brewery with passed ID#
|
24
24
|
#
|
25
25
|
def brewery(id, name = nil)
|
26
|
-
Brewery.new(id, name: name)
|
26
|
+
Brewery::Brewery.new(id, name: name)
|
27
27
|
end
|
28
28
|
|
29
29
|
# Create new style instance, using ID and name passed as arguments.
|
data/lib/ratebeer/beer.rb
CHANGED
@@ -1,193 +1,154 @@
|
|
1
|
-
|
2
|
-
require_relative "review"
|
3
|
-
require_relative "style"
|
4
|
-
require_relative "scraping"
|
5
|
-
require_relative "urls"
|
1
|
+
# frozen_string_literal: true
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
[:name,
|
14
|
-
:brewery,
|
15
|
-
:style,
|
16
|
-
:glassware,
|
17
|
-
:availability,
|
18
|
-
:abv,
|
19
|
-
:calories,
|
20
|
-
:description,
|
21
|
-
:retired,
|
22
|
-
:rating]
|
23
|
-
end
|
24
|
-
|
25
|
-
include RateBeer::Scraping
|
26
|
-
include RateBeer::URLs
|
27
|
-
|
28
|
-
# CSS selector for the root element containing beer information.
|
29
|
-
ROOT_SELECTOR = '#container table'.freeze
|
30
|
-
|
31
|
-
# CSS selector for the beer information element.
|
32
|
-
INFO_SELECTOR = 'table'.freeze
|
3
|
+
require_relative 'beer/alias'
|
4
|
+
require_relative 'brewery'
|
5
|
+
require_relative 'review'
|
6
|
+
require_relative 'style'
|
7
|
+
require_relative 'scraping'
|
8
|
+
require_relative 'urls'
|
33
9
|
|
34
|
-
|
35
|
-
|
36
|
-
#
|
10
|
+
module RateBeer
|
11
|
+
module Beer
|
12
|
+
# The Beer class.
|
37
13
|
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# @param [hash] options Options hash for entity created
|
14
|
+
# This class represents one beer found on RateBeer.com, and provides
|
15
|
+
# functionality for obtaining information from the site.
|
41
16
|
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
17
|
+
# The key functionality is defined in the self.data_keys method, each key
|
18
|
+
# representing a piece of accessible data.
|
19
|
+
class Beer
|
20
|
+
# Each key represents an item of data accessible for each beer. The
|
21
|
+
# included scraping module defines dynamically a series of methods for
|
22
|
+
# accessing this data.
|
23
|
+
#
|
24
|
+
def self.data_keys
|
25
|
+
[:name,
|
26
|
+
:brewery,
|
27
|
+
:style,
|
28
|
+
:glassware,
|
29
|
+
:abv,
|
30
|
+
:description,
|
31
|
+
:retired,
|
32
|
+
:rating]
|
51
33
|
end
|
52
|
-
@doc
|
53
|
-
end
|
54
34
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
35
|
+
include RateBeer::Beer
|
36
|
+
include RateBeer::Scraping
|
37
|
+
include RateBeer::URLs
|
38
|
+
|
39
|
+
# CSS selector for the root element containing beer information.
|
40
|
+
ROOT_SELECTOR = '#container table'
|
41
|
+
|
42
|
+
# CSS selector for the beer information element.
|
43
|
+
INFO_SELECTOR = 'table'
|
44
|
+
|
45
|
+
# Create RateBeer::Beer instance.
|
46
|
+
#
|
47
|
+
# Requires the RateBeer ID# for the beer in question.
|
48
|
+
#
|
49
|
+
# @param [Integer, String] id ID# of beer to retrieve
|
50
|
+
# @param [String] name Name of the beer to which ID# relates if known
|
51
|
+
# @param [hash] options Options hash for entity created
|
52
|
+
#
|
53
|
+
def initialize(id, name: nil, **options)
|
54
|
+
super
|
55
|
+
end
|
62
56
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
57
|
+
def doc
|
58
|
+
unless instance_variable_defined?('@doc')
|
59
|
+
@doc = noko_doc(URI.join(BASE_URL, beer_url(id)))
|
60
|
+
validate_beer
|
61
|
+
scrape_name # Name must be scraped before any possible redirection.
|
62
|
+
@doc = redirect_if_alias(@doc) || @doc
|
63
|
+
end
|
64
|
+
@doc
|
65
|
+
end
|
68
66
|
|
69
|
-
|
67
|
+
def root
|
68
|
+
@root ||= doc.at_css(ROOT_SELECTOR)
|
69
|
+
end
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
#
|
74
|
-
# This method overwrites the value of @doc, so that this will scrape the
|
75
|
-
# details of the proper beer, and not the alias.
|
76
|
-
def redirect_if_aliased
|
77
|
-
# retrieve details of the proper beer instead.
|
78
|
-
alias_pattern = /Also known as(.|\n)*Proceed to the aliased beer\.{3}/
|
79
|
-
local_root = doc.at_css(ROOT_SELECTOR)
|
80
|
-
if local_root.css('tr')[1].css('div div').text =~ alias_pattern
|
81
|
-
scrape_name # Set the name to the original, non-aliased beer.
|
82
|
-
alias_node = local_root.css('tr')[1]
|
83
|
-
.css('div div')
|
84
|
-
.css('a')
|
85
|
-
.first
|
86
|
-
@alias_id = alias_node['href'].split('/').last.to_i
|
87
|
-
@doc = noko_doc(URI.join(BASE_URL, beer_url(@alias_id)))
|
71
|
+
def info_root
|
72
|
+
@info_root ||= root.at_css(INFO_SELECTOR)
|
88
73
|
end
|
89
|
-
end
|
90
74
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
75
|
+
# Return reviews of this beer.
|
76
|
+
#
|
77
|
+
def reviews(order: :most_recent, limit: 10)
|
78
|
+
Review.retrieve(self, order: order, limit: limit)
|
95
79
|
end
|
96
|
-
end
|
97
80
|
|
98
|
-
|
99
|
-
@name ||= fix_characters(doc.css('h1').text.strip)
|
100
|
-
end
|
81
|
+
private
|
101
82
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
83
|
+
def validate_beer
|
84
|
+
error_indicator = 'we didn\'t find this beer'
|
85
|
+
error_message = "Beer not found - #{id}"
|
86
|
+
raise PageNotFoundError, error_message if name == error_indicator
|
87
|
+
end
|
108
88
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
style_name = fix_characters(style_element.text)
|
113
|
-
@style = Style.new(style_id, name: style_name)
|
114
|
-
end
|
89
|
+
def scrape_name
|
90
|
+
@name ||= fix_characters(doc.css('h1').text.strip)
|
91
|
+
end
|
115
92
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
93
|
+
def scrape_brewery
|
94
|
+
brewery_element = doc.at_css("a[itemprop='brand']")
|
95
|
+
brewery_id = id_from_link(brewery_element)
|
96
|
+
brewery_name = fix_characters(brewery_element.text)
|
97
|
+
@brewery = Brewery::Brewery.new(brewery_id, name: brewery_name)
|
120
98
|
end
|
121
|
-
end
|
122
99
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
.map(&:text)
|
130
|
-
.reject(&:empty?)
|
131
|
-
.each_slice(2)
|
132
|
-
.to_a
|
133
|
-
.tap { |a| a.last.unshift('distribution') }
|
134
|
-
.map do |(k, v)|
|
135
|
-
[k =~ /bottl/ ? :bottling : symbolize_text(k), v]
|
136
|
-
end
|
137
|
-
@availability = raw_info.to_h.merge(seasonal: scrape_misc[:seasonal])
|
138
|
-
end
|
100
|
+
def scrape_style
|
101
|
+
style_element = doc.at_css("a[href^='/beerstyles']")
|
102
|
+
style_id = id_from_link(style_element)
|
103
|
+
style_name = fix_characters(style_element.text)
|
104
|
+
@style = Style.new(style_id, name: style_name)
|
105
|
+
end
|
139
106
|
|
140
|
-
|
141
|
-
|
142
|
-
|
107
|
+
def scrape_glassware
|
108
|
+
glassware_elements = doc.css("a[href^='/ShowGlassware.asp']")
|
109
|
+
@glassware = glassware_elements.map do |el|
|
110
|
+
[:id, :name].zip([el['href'].split('GWID=').last.to_i, el.text]).to_h
|
111
|
+
end
|
112
|
+
end
|
143
113
|
|
144
|
-
|
145
|
-
|
146
|
-
|
114
|
+
def scrape_abv
|
115
|
+
@abv = scrape_misc[:abv]
|
116
|
+
end
|
147
117
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
.children
|
152
|
-
.children
|
153
|
-
.map(&:text)
|
154
|
-
.map(&:strip)
|
155
|
-
.drop(1)
|
156
|
-
.reject(&:empty?)
|
157
|
-
.join("\n")
|
158
|
-
@description = fix_characters(@description)
|
159
|
-
end
|
118
|
+
def scrape_description
|
119
|
+
@description = fix_characters(doc.at_css('#_description3').text)
|
120
|
+
end
|
160
121
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
122
|
+
def scrape_retired
|
123
|
+
element = doc.at_css('span.beertitle2')
|
124
|
+
@retired = element && element.text.match?(/RETIRED/) || false
|
125
|
+
end
|
165
126
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
127
|
+
def scrape_rating
|
128
|
+
raw_rating = [:overall,
|
129
|
+
:style].zip(doc.css('#_aggregateRating6 div')
|
130
|
+
.select { |d| d['title'] =~ /This figure/ }
|
131
|
+
.map { |d| d['title'].split(':').first.to_f }).to_h
|
132
|
+
@rating = raw_rating.merge(ratings: scrape_misc[:ratings],
|
133
|
+
weighted_avg: scrape_misc[:weighted_avg],
|
134
|
+
mean: scrape_misc[:mean])
|
135
|
+
end
|
175
136
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
137
|
+
# Scrapes the miscellaneous information contained on the beer page.
|
138
|
+
#
|
139
|
+
# This information relates to various other specific types of information.
|
140
|
+
# As such, other scrapers rely on this method for information.
|
141
|
+
def scrape_misc
|
142
|
+
doc.at_css('.stats-container')
|
143
|
+
.children
|
144
|
+
.map(&:text)
|
145
|
+
.flat_map { |x| x.gsub(nbsp, ' ').strip.split(':') }
|
146
|
+
.map(&:strip)
|
147
|
+
.reject(&:empty?)
|
148
|
+
.each_slice(2)
|
149
|
+
.map { |(k, v)| [symbolize_text(k), v.to_f.zero? ? v : v.to_f] }
|
150
|
+
.to_h
|
151
|
+
end
|
191
152
|
end
|
192
153
|
end
|
193
154
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../urls'
|
4
|
+
|
5
|
+
module RateBeer
|
6
|
+
module Beer
|
7
|
+
# Redirects a beer if it is an alias.
|
8
|
+
#
|
9
|
+
def redirect_if_alias(doc)
|
10
|
+
Alias.new(doc).try_redirect
|
11
|
+
end
|
12
|
+
|
13
|
+
# The Alias class.
|
14
|
+
#
|
15
|
+
# RateBeer treats certain beers as aliases of another (e.g. Koenig Ludwig
|
16
|
+
# Weissbier - https://www.ratebeer.com/beer/konig-ludwig-weissbier/14945/)
|
17
|
+
# and provides a link to the "original" beer. This class is used to handle
|
18
|
+
# redirection where a beer is an alias.
|
19
|
+
#
|
20
|
+
class Alias
|
21
|
+
include RateBeer::URLs
|
22
|
+
|
23
|
+
# CSS selector for container with alias information.
|
24
|
+
ALIAS_SELECTOR = '.row.columns-container .col-sm-8'.freeze
|
25
|
+
|
26
|
+
# Create an Alias instance.
|
27
|
+
#
|
28
|
+
# The Alias class deals with handling beers which may be aliases of
|
29
|
+
# others, and so requires redirection to the "proper" beer's page.
|
30
|
+
#
|
31
|
+
# @param [Nokogiri::Doc] document representing a RateBeer.com beer page
|
32
|
+
#
|
33
|
+
def initialize(doc)
|
34
|
+
@doc = doc
|
35
|
+
end
|
36
|
+
|
37
|
+
# Redirects this beer to the "proper" beer page if it represents an alias
|
38
|
+
# of another beer.
|
39
|
+
#
|
40
|
+
# This method returns a new doc value if the beer is an alias, or nil if
|
41
|
+
# not.
|
42
|
+
def try_redirect
|
43
|
+
redirect_to_alias if aliased_beer?
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def aliased_beer?
|
49
|
+
alias_pattern = /Also known as(.|\n)*Proceed to the aliased beer\.{3}/
|
50
|
+
alias_container && alias_container.text =~ alias_pattern
|
51
|
+
end
|
52
|
+
|
53
|
+
def redirect_to_alias
|
54
|
+
alias_node = alias_container.at_css('a')
|
55
|
+
alias_id = alias_node['href'].split('/').last.to_i
|
56
|
+
noko_doc(URI.join(BASE_URL, beer_url(alias_id)))
|
57
|
+
end
|
58
|
+
|
59
|
+
def alias_container
|
60
|
+
@doc.at_css(ALIAS_SELECTOR)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/ratebeer/brewery.rb
CHANGED
@@ -2,187 +2,114 @@
|
|
2
2
|
|
3
3
|
require_relative 'scraping'
|
4
4
|
require_relative 'urls'
|
5
|
+
require_relative 'brewery/beer_list'
|
5
6
|
|
6
7
|
module RateBeer
|
7
8
|
# The brewery class represents one brewery found in RateBeer, with methods
|
8
9
|
# for accessing information found about the brewery on the site.
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
include RateBeer::Scraping
|
22
|
-
include RateBeer::URLs
|
23
|
-
|
24
|
-
attr_reader :established, :location
|
25
|
-
|
26
|
-
# CSS selector for the brewery information element.
|
27
|
-
INFO_SELECTOR = "div[itemtype='http://schema.org/LocalBusiness']".freeze
|
28
|
-
|
29
|
-
# Create RateBeer::Brewery instance.
|
30
|
-
#
|
31
|
-
# Requires the RateBeer ID# for the brewery in question. Optionally accepts
|
32
|
-
# a name parameter where the name is already known.
|
33
|
-
#
|
34
|
-
# @param [Integer, String] id ID# for the brewery
|
35
|
-
# @param [String] name The name of the specified brewery
|
36
|
-
# @param [hash] options Options hash for entity created
|
37
|
-
#
|
38
|
-
def initialize(id, name: nil, **options)
|
39
|
-
super
|
40
|
-
if options
|
41
|
-
@established = options[:established]
|
42
|
-
@location = options[:location]
|
43
|
-
@type = options[:type]
|
44
|
-
@status = options[:status]
|
10
|
+
module Brewery
|
11
|
+
class Brewery
|
12
|
+
# Each key represents an item of data accessible for each beer, and defines
|
13
|
+
# dynamically a series of methods for accessing this data.
|
14
|
+
#
|
15
|
+
def self.data_keys
|
16
|
+
[:name,
|
17
|
+
:type,
|
18
|
+
:address,
|
19
|
+
:telephone,
|
20
|
+
:beers]
|
45
21
|
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def doc
|
49
|
-
@doc ||= noko_doc(URI.join(BASE_URL, brewery_url(id)))
|
50
|
-
validate_brewery
|
51
|
-
@doc
|
52
|
-
end
|
53
|
-
|
54
|
-
def info_root
|
55
|
-
@info_root ||= doc.at_css(INFO_SELECTOR)
|
56
|
-
end
|
57
22
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
23
|
+
include RateBeer::Scraping
|
24
|
+
include RateBeer::URLs
|
25
|
+
|
26
|
+
attr_reader :established, :location
|
27
|
+
|
28
|
+
# CSS selector for the brewery information element.
|
29
|
+
INFO_SELECTOR = "div[itemtype='http://schema.org/LocalBusiness']".freeze
|
30
|
+
|
31
|
+
# Create RateBeer::Brewery instance.
|
32
|
+
#
|
33
|
+
# Requires the RateBeer ID# for the brewery in question. Optionally accepts
|
34
|
+
# a name parameter where the name is already known.
|
35
|
+
#
|
36
|
+
# @param [Integer, String] id ID# for the brewery
|
37
|
+
# @param [String] name The name of the specified brewery
|
38
|
+
# @param [hash] options Options hash for entity created
|
39
|
+
#
|
40
|
+
def initialize(id, name: nil, **options)
|
41
|
+
super
|
42
|
+
if options
|
43
|
+
@established = options[:established]
|
44
|
+
@location = options[:location]
|
45
|
+
@type = options[:type]
|
46
|
+
@status = options[:status]
|
47
|
+
end
|
68
48
|
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# Scrapes the brewery's name.
|
72
|
-
def scrape_name
|
73
|
-
@name = fix_characters(info_root.css('h1').first.text)
|
74
|
-
end
|
75
49
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
# Extracts one element of address details from a node contained within the
|
84
|
-
# address div.
|
85
|
-
def extract_address_element(node)
|
86
|
-
key = case node.attributes['itemprop'].value
|
87
|
-
when 'streetAddress' then :street
|
88
|
-
when 'addressLocality' then :city
|
89
|
-
when 'addressRegion' then :state
|
90
|
-
when 'addressCountry' then :country
|
91
|
-
when 'postalCode' then :postcode
|
92
|
-
else raise 'unrecognised attribute'
|
93
|
-
end
|
94
|
-
[key, node.text.strip]
|
95
|
-
end
|
50
|
+
def doc
|
51
|
+
@doc ||= noko_doc(URI.join(BASE_URL, brewery_url(id)))
|
52
|
+
validate_brewery
|
53
|
+
@doc
|
54
|
+
end
|
96
55
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
56
|
+
def info_root
|
57
|
+
@info_root ||= doc.at_css(INFO_SELECTOR)
|
58
|
+
end
|
101
59
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
60
|
+
private
|
61
|
+
|
62
|
+
# Validates whether the brewery with the given ID exists.
|
63
|
+
#
|
64
|
+
# Throws an exception if the brewery does not exist.
|
65
|
+
def validate_brewery
|
66
|
+
error_message = "This brewer, ID##{id}, is no longer in the database. "\
|
67
|
+
'RateBeer Home'
|
68
|
+
if @doc.at_css('body p').text == error_message
|
69
|
+
raise PageNotFoundError.new("Brewery not found - #{id}")
|
70
|
+
end
|
71
|
+
end
|
106
72
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
@beers = rows.map { |row| process_beer_row(row) }.reject(&:nil?)
|
112
|
-
end
|
73
|
+
# Scrapes the brewery's name.
|
74
|
+
def scrape_name
|
75
|
+
@name = fix_characters(info_root.css('h1').first.text)
|
76
|
+
end
|
113
77
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
# @param [String] brewer the client for whom this brewery brewed the beer,
|
121
|
-
# where the brewery is brewing for a different company/brewery
|
122
|
-
# @return [RateBeer::Beer] a beer object representing the scraped beer,
|
123
|
-
# containing scraped attributes
|
124
|
-
#
|
125
|
-
def process_beer_row(row)
|
126
|
-
beer = process_beer_name_cell(row.css('td').first)
|
127
|
-
beer[:abv] = row.css('td')[1].text.to_f
|
128
|
-
beer[:date_added] = Date.strptime(row.css('td')[2].text, '%m/%d/%Y')
|
129
|
-
Beer.new(id, beer.merge(process_rating_info(row)))
|
130
|
-
end
|
78
|
+
# Scrapes the brewery's address.
|
79
|
+
def scrape_address
|
80
|
+
address_root = info_root.css('div[itemprop="address"] b span')
|
81
|
+
address_details = address_root.map { |e| extract_address_element(e) }
|
82
|
+
@address = address_details.to_h
|
83
|
+
end
|
131
84
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
style: style,
|
146
|
-
retired: info && info.text =~ /retired/ || false }.merge(brewed_at_for)
|
147
|
-
end
|
85
|
+
# Extracts one element of address details from a node contained within the
|
86
|
+
# address div.
|
87
|
+
def extract_address_element(node)
|
88
|
+
key = case node.attributes['itemprop'].value
|
89
|
+
when 'streetAddress' then :street
|
90
|
+
when 'addressLocality' then :city
|
91
|
+
when 'addressRegion' then :state
|
92
|
+
when 'addressCountry' then :country
|
93
|
+
when 'postalCode' then :postcode
|
94
|
+
else raise 'unrecognised attribute'
|
95
|
+
end
|
96
|
+
[key, node.text.strip]
|
97
|
+
end
|
148
98
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
node_text = brewed_at_for_node.children.first.text
|
154
|
-
key = if node_text.include?('Brewed at')
|
155
|
-
:brewed_at
|
156
|
-
elsif node_text.include?('Brewed by/for')
|
157
|
-
:brewed_by_for
|
158
|
-
end
|
159
|
-
other_brewer_node = brewed_at_for_node.at_css('a')
|
160
|
-
{ key => Brewery.new(id_from_link(other_brewer_node),
|
161
|
-
name: other_brewer_node.text) }
|
162
|
-
end
|
99
|
+
# Scrapes the telephone number of the brewery.
|
100
|
+
def scrape_telephone
|
101
|
+
@telephone = info_root.at_css('span[itemprop="telephone"]')
|
102
|
+
end
|
163
103
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
n.children.any? { |c| c.name == 'span' }
|
104
|
+
# Scrapes the type of brewery.
|
105
|
+
def scrape_type
|
106
|
+
@type = info_root.css('div')[1]
|
168
107
|
end
|
169
|
-
name = style_node.text
|
170
|
-
id = id_from_link(style_node)
|
171
|
-
Style.new(id, name: name)
|
172
|
-
end
|
173
108
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
overall_rating: 5,
|
178
|
-
style_rating: 6,
|
179
|
-
num_ratings: 7 }
|
180
|
-
rating = cell_indices.map do |attr, i|
|
181
|
-
val = row.css('td')[i].text.gsub(nbsp, ' ').strip
|
182
|
-
conversion = attr == :avg_rating ? :to_f : :to_i
|
183
|
-
[attr, val.send(conversion)]
|
109
|
+
# Scrapes beers list for brewery.
|
110
|
+
def scrape_beers
|
111
|
+
@beers = BeerList.new(self).beers
|
184
112
|
end
|
185
|
-
rating.to_h
|
186
113
|
end
|
187
114
|
end
|
188
115
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../urls'
|
4
|
+
|
5
|
+
module RateBeer
|
6
|
+
module Brewery
|
7
|
+
# The BeerList class extracts a list of beers for a given brewery.
|
8
|
+
class BeerList
|
9
|
+
include RateBeer::Scraping
|
10
|
+
include RateBeer::URLs
|
11
|
+
|
12
|
+
attr_reader :brewery, :id
|
13
|
+
|
14
|
+
def initialize(brewery)
|
15
|
+
@brewery = brewery
|
16
|
+
@id = brewery.id
|
17
|
+
end
|
18
|
+
|
19
|
+
def beers
|
20
|
+
beers_doc = noko_doc(URI.join(BASE_URL, brewery_beers_url(id)))
|
21
|
+
rows = beers_doc.css('table#brewer-beer-table tbody tr')
|
22
|
+
@beers = rows.map { |row| process_beer_row(row) }.reject(&:nil?)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Process a row of data representing one beer brewed by/at a brewery.
|
28
|
+
#
|
29
|
+
# @param [Nokogiri::XML::Element] row HTML TR row wrapped as a Nokogiri
|
30
|
+
# element
|
31
|
+
# @param [String] location the location at which a brewery's beer is brewed
|
32
|
+
# where this location differs from the brewery's regular brewsite/venue
|
33
|
+
# @param [String] brewer the client for whom this brewery brewed the beer,
|
34
|
+
# where the brewery is brewing for a different company/brewery
|
35
|
+
# @return [RateBeer::Beer] a beer object representing the scraped beer,
|
36
|
+
# containing scraped attributes
|
37
|
+
#
|
38
|
+
def process_beer_row(row)
|
39
|
+
beer = process_beer_name_cell(row.css('td').first)
|
40
|
+
beer[:abv] = row.css('td')[1].text.to_f
|
41
|
+
beer[:date_added] = Date.strptime(row.css('td')[2].text, '%m/%d/%Y')
|
42
|
+
Beer::Beer.new(id, beer.merge(process_rating_info(row)))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Processes the cell containing the beer's name and other information.
|
46
|
+
#
|
47
|
+
# This cell contains information on the beer's name, its style, whether it
|
48
|
+
# is retired, and who is was brewed for or by.
|
49
|
+
def process_beer_name_cell(node)
|
50
|
+
beer_link = node.at_css('strong a')
|
51
|
+
name = fix_characters(beer_link.text)
|
52
|
+
id = id_from_link(beer_link)
|
53
|
+
info = node.at_css('em.real-small')
|
54
|
+
brewed_at_for = process_brewed_at_for(node)
|
55
|
+
style = process_style_info(node)
|
56
|
+
{ id: id,
|
57
|
+
name: name,
|
58
|
+
style: style,
|
59
|
+
retired: info && info.text =~ /retired/ || false }.merge(brewed_at_for)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Processes information on who the beer was brewed for or by, or at.
|
63
|
+
def process_brewed_at_for(node)
|
64
|
+
brewed_at_for_node = node.at_css('div.small em')
|
65
|
+
return {} if brewed_at_for_node.nil?
|
66
|
+
node_text = brewed_at_for_node.children.first.text
|
67
|
+
key = if node_text.include?('Brewed at')
|
68
|
+
:brewed_at
|
69
|
+
elsif node_text.include?('Brewed by/for')
|
70
|
+
:brewed_by_for
|
71
|
+
end
|
72
|
+
other_brewer_node = brewed_at_for_node.at_css('a')
|
73
|
+
{ key => Brewery.new(id_from_link(other_brewer_node),
|
74
|
+
name: other_brewer_node.text) }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Processes the style information contained within a beer name cell.
|
78
|
+
def process_style_info(node)
|
79
|
+
style_node = node.css('a').find do |n|
|
80
|
+
n.children.any? { |c| c.name == 'span' }
|
81
|
+
end
|
82
|
+
name = style_node.text
|
83
|
+
id = id_from_link(style_node)
|
84
|
+
Style.new(id, name: name)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Processes rating information from a beer row.
|
88
|
+
def process_rating_info(row)
|
89
|
+
cell_indices = { avg_rating: 4,
|
90
|
+
style_rating: 5,
|
91
|
+
num_ratings: 6 }
|
92
|
+
rating = cell_indices.map do |attr, i|
|
93
|
+
val = row.css('td')[i].text.gsub(nbsp, ' ').strip
|
94
|
+
conversion = attr == :avg_rating ? :to_f : :to_i
|
95
|
+
[attr, val.send(conversion)]
|
96
|
+
end
|
97
|
+
rating.to_h
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/ratebeer/location.rb
CHANGED
@@ -40,10 +40,6 @@ module RateBeer
|
|
40
40
|
@doc
|
41
41
|
end
|
42
42
|
|
43
|
-
def heading
|
44
|
-
@heading ||= doc.at_css('.col-lg-9')
|
45
|
-
end
|
46
|
-
|
47
43
|
private
|
48
44
|
|
49
45
|
def validate_location
|
@@ -53,20 +49,19 @@ module RateBeer
|
|
53
49
|
end
|
54
50
|
|
55
51
|
def scrape_name
|
56
|
-
@name =
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
52
|
+
@name = doc.at_css('h1')
|
53
|
+
.text
|
54
|
+
.split('Breweries')
|
55
|
+
.first
|
56
|
+
.strip
|
61
57
|
end
|
62
58
|
|
63
59
|
def scrape_num_breweries
|
64
|
-
@num_breweries =
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
.to_i
|
60
|
+
@num_breweries = doc.at_css('li.active')
|
61
|
+
.text
|
62
|
+
.split(' ')
|
63
|
+
.first
|
64
|
+
.to_i
|
70
65
|
end
|
71
66
|
|
72
67
|
def scrape_breweries
|
@@ -86,12 +81,12 @@ module RateBeer
|
|
86
81
|
.strip
|
87
82
|
type = cells[1].text.strip
|
88
83
|
established = status == 'Active' ? cells[3].text.to_i : nil
|
89
|
-
Brewery.new(id,
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
84
|
+
Brewery::Brewery.new(id,
|
85
|
+
name: name,
|
86
|
+
location: location,
|
87
|
+
type: type,
|
88
|
+
established: established,
|
89
|
+
status: status)
|
95
90
|
end
|
96
91
|
end
|
97
92
|
end
|
data/lib/ratebeer/review.rb
CHANGED
@@ -57,11 +57,11 @@ module RateBeer
|
|
57
57
|
# beer, up to the review_limit
|
58
58
|
#
|
59
59
|
def retrieve(beer, order: :most_recent, limit: 10)
|
60
|
-
if beer.is_a?(RateBeer::Beer)
|
60
|
+
if beer.is_a?(RateBeer::Beer::Beer)
|
61
61
|
beer_id = beer.id
|
62
62
|
elsif beer.is_a?(Integer)
|
63
63
|
beer_id = beer
|
64
|
-
beer = RateBeer::Beer.new(beer)
|
64
|
+
beer = RateBeer::Beer::Beer.new(beer)
|
65
65
|
else
|
66
66
|
raise "unknown beer value: #{beer}"
|
67
67
|
end
|
@@ -69,7 +69,7 @@ module RateBeer
|
|
69
69
|
reviews = num_pages(limit).times.flat_map do |page_number|
|
70
70
|
url = URI.join(BASE_URL, review_url(beer_id, url_suffix(order), page_number))
|
71
71
|
doc = RateBeer::Scraping.noko_doc(url)
|
72
|
-
root = doc.
|
72
|
+
root = doc.at_css('.reviews-container')
|
73
73
|
|
74
74
|
# All reviews are contained within the sole cell in the sole row of
|
75
75
|
# the selected table. Each review consists of rating information,
|
@@ -77,7 +77,7 @@ module RateBeer
|
|
77
77
|
#
|
78
78
|
# The components are contained within div, small, div tags
|
79
79
|
# respectively. We need to scrape these specifically.
|
80
|
-
root.
|
80
|
+
root.at_css('div div')
|
81
81
|
.children
|
82
82
|
.select { |x| x.name == 'div' || x.name == 'small' }
|
83
83
|
.map(&:text)
|
@@ -126,7 +126,7 @@ module RateBeer
|
|
126
126
|
@beer = if options[:beer].is_a?(RateBeer::Beer)
|
127
127
|
options[:beer]
|
128
128
|
elsif options[:beer].is_a?(Integer)
|
129
|
-
RateBeer::Beer.new(options[:beer])
|
129
|
+
RateBeer::Beer::Beer.new(options[:beer])
|
130
130
|
else
|
131
131
|
raise ArgumentError.new("incorrect beer parameter: #{options[:beer]}")
|
132
132
|
end
|
data/lib/ratebeer/scraping.rb
CHANGED
@@ -14,10 +14,12 @@ module RateBeer
|
|
14
14
|
|
15
15
|
# Run method on inclusion in class.
|
16
16
|
def self.included(base)
|
17
|
-
base.data_keys
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
if base.respond_to?(:data_keys)
|
18
|
+
base.data_keys.each do |attr|
|
19
|
+
define_method(attr) do
|
20
|
+
send("scrape_#{attr}") unless instance_variable_defined?("@#{attr}")
|
21
|
+
instance_variable_get("@#{attr}")
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
@@ -128,7 +130,7 @@ module RateBeer
|
|
128
130
|
# strings scraped from RateBeer.com
|
129
131
|
#
|
130
132
|
def fix_characters(string)
|
131
|
-
string = string.encode('UTF-8',
|
133
|
+
string = string.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
132
134
|
characters = { nbsp => " ",
|
133
135
|
"\u0093" => "ž",
|
134
136
|
"\u0092" => "'",
|
data/lib/ratebeer/search.rb
CHANGED
@@ -108,7 +108,7 @@ module RateBeer
|
|
108
108
|
def scrape_beers
|
109
109
|
unless instance_variable_defined?('@beers')
|
110
110
|
run_search
|
111
|
-
@beers = @beers.sort_by(&:id)
|
111
|
+
@beers = @beers && @beers.sort_by(&:id)
|
112
112
|
end
|
113
113
|
@beers
|
114
114
|
end
|
@@ -124,7 +124,7 @@ module RateBeer
|
|
124
124
|
# Generate parameters to use in POST request.
|
125
125
|
#
|
126
126
|
def post_params
|
127
|
-
{ '
|
127
|
+
{ 'beername' => @query }
|
128
128
|
end
|
129
129
|
|
130
130
|
# Process breweries table returned in search.
|
@@ -146,7 +146,7 @@ module RateBeer
|
|
146
146
|
end
|
147
147
|
result[:url] = row.at_css('a')['href']
|
148
148
|
result[:id] = result[:url].split('/').last.to_i
|
149
|
-
Brewery.new(result[:id], name: result[:name])
|
149
|
+
Brewery::Brewery.new(result[:id], name: result[:name])
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
@@ -183,14 +183,14 @@ module RateBeer
|
|
183
183
|
def process_beer_row(row)
|
184
184
|
result = [:id, :name, :score, :ratings, :url].zip([nil]).to_h
|
185
185
|
content = row.element_children.map { |x| fix_characters(x.text) }
|
186
|
-
result[:name] =
|
186
|
+
result[:name] = row.element_children.first.at_css('a').text
|
187
187
|
result[:score], result[:ratings] = content.values_at(3, 4)
|
188
188
|
.map do |n|
|
189
189
|
n.nil? || n.empty? ? nil : n.to_i
|
190
190
|
end
|
191
191
|
result[:url] = row.at_css('a')['href']
|
192
192
|
result[:id] = result[:url].split('/').last.to_i
|
193
|
-
b = Beer.new(result[:id], name: result[:name])
|
193
|
+
b = Beer::Beer.new(result[:id], name: result[:name])
|
194
194
|
b.brewery.name if @scrape_beer_brewers
|
195
195
|
b
|
196
196
|
end
|
data/lib/ratebeer/style.rb
CHANGED
@@ -35,9 +35,9 @@ module RateBeer
|
|
35
35
|
#
|
36
36
|
def all_styles(include_hidden = false)
|
37
37
|
doc = Scraping.noko_doc(URI.join(BASE_URL, '/beerstyles/'))
|
38
|
-
root = doc.at_css('div.container-fluid
|
38
|
+
root = doc.at_css('div.container-fluid')
|
39
39
|
|
40
|
-
categories = root.css('
|
40
|
+
categories = root.css('h3').map(&:text)
|
41
41
|
style_node = root.css('.styleGroup')
|
42
42
|
|
43
43
|
styles = style_node.flat_map.with_index do |list, i|
|
@@ -109,8 +109,8 @@ module RateBeer
|
|
109
109
|
@beers = beer_list.css('tr').drop(1).map do |row|
|
110
110
|
cells = row.css('td')
|
111
111
|
url = cells[1].at_css('a')['href']
|
112
|
-
[cells[0].text.to_i, Beer.new(url.split('/').last,
|
113
|
-
|
112
|
+
[cells[0].text.to_i, Beer::Beer.new(url.split('/').last,
|
113
|
+
name: fix_characters(cells[1].text))]
|
114
114
|
end.to_h
|
115
115
|
end
|
116
116
|
end
|
data/lib/ratebeer/urls.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module RateBeer
|
3
4
|
# This module contains URLs or URL patterns for use throughout the Gem.
|
4
5
|
#
|
5
6
|
module URLs
|
6
|
-
BASE_URL =
|
7
|
-
SEARCH_URL =
|
7
|
+
BASE_URL = 'https://www.ratebeer.com'
|
8
|
+
SEARCH_URL = '/search'
|
8
9
|
|
9
10
|
# Return URL to info page for beer with id
|
10
11
|
#
|
@@ -1,23 +1,23 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe RateBeer::Beer do
|
3
|
+
describe RateBeer::Beer::Beer do
|
4
4
|
before :all do
|
5
|
-
@valid = RateBeer
|
6
|
-
@retired = RateBeer
|
7
|
-
@with_name = RateBeer
|
5
|
+
@valid = RateBeer.beer(1411) # ID for Tennents Lager (sorry...)
|
6
|
+
@retired = RateBeer.beer(213_225) # ID for BrewDog Vice Bier
|
7
|
+
@with_name = RateBeer.beer(422, 'Stone IPA')
|
8
8
|
end
|
9
9
|
|
10
10
|
describe '#new' do
|
11
11
|
it 'creates a beer instance' do
|
12
|
-
expect(@valid).to be_a RateBeer::Beer
|
12
|
+
expect(@valid).to be_a RateBeer::Beer::Beer
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'requires an ID# as parameter' do
|
16
|
-
expect { RateBeer
|
16
|
+
expect { RateBeer.beer }.to raise_error(ArgumentError)
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'accepts a name parameter' do
|
20
|
-
expect(@with_name).to be_a RateBeer::Beer
|
20
|
+
expect(@with_name).to be_a RateBeer::Beer::Beer
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -46,9 +46,7 @@ describe RateBeer::Beer do
|
|
46
46
|
:url,
|
47
47
|
:style,
|
48
48
|
:glassware,
|
49
|
-
:availability,
|
50
49
|
:abv,
|
51
|
-
:calories,
|
52
50
|
:description,
|
53
51
|
:retired,
|
54
52
|
:rating)
|
@@ -2,21 +2,21 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe RateBeer::Brewery do
|
4
4
|
before :all do
|
5
|
-
@valid = RateBeer
|
6
|
-
@with_name = RateBeer
|
5
|
+
@valid = RateBeer.brewery(8534) # ID for BrewDog
|
6
|
+
@with_name = RateBeer.brewery(1069, 'Cantillon Brewery')
|
7
7
|
end
|
8
8
|
|
9
9
|
describe '#new' do
|
10
10
|
it 'creates a brewery instance' do
|
11
|
-
expect(@valid).to be_a RateBeer::Brewery
|
11
|
+
expect(@valid).to be_a RateBeer::Brewery::Brewery
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'requires an ID# as parameter' do
|
15
|
-
expect { RateBeer::Brewery.new }.to raise_error(ArgumentError)
|
15
|
+
expect { RateBeer::Brewery::Brewery.new }.to raise_error(ArgumentError)
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'accepts a name parameter' do
|
19
|
-
expect(@with_name).to be_a RateBeer::Brewery
|
19
|
+
expect(@with_name).to be_a RateBeer::Brewery::Brewery
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -36,7 +36,7 @@ describe RateBeer::Brewery do
|
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'returns an array of beer instances' do
|
39
|
-
@valid.beers.each { |b| expect(b).to be_a RateBeer::Beer }
|
39
|
+
@valid.beers.each { |b| expect(b).to be_a RateBeer::Beer::Beer }
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'returns a list of beers produced by brewery' do
|
@@ -48,7 +48,7 @@ describe RateBeer::Brewery do
|
|
48
48
|
162_521,
|
49
49
|
87_321,
|
50
50
|
118_987,
|
51
|
-
119_594].map { |id| RateBeer
|
51
|
+
119_594].map { |id| RateBeer.beer(id) }
|
52
52
|
expect(@valid.beers).to include(*beers)
|
53
53
|
end
|
54
54
|
end
|
@@ -39,7 +39,7 @@ describe RateBeer::Region do
|
|
39
39
|
it "returns a series of RateBeer::Brewery instances" do
|
40
40
|
expected_breweries = [25_211,
|
41
41
|
3132,
|
42
|
-
1097].map { |i| RateBeer::Brewery.new(i) }
|
42
|
+
1097].map { |i| RateBeer::Brewery::Brewery.new(i) }
|
43
43
|
expect(@region.breweries).to include(*expected_breweries)
|
44
44
|
end
|
45
45
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe RateBeer::Review do
|
4
4
|
before :all do
|
5
5
|
# Create Review instance with Beer instance
|
6
|
-
@beer = RateBeer
|
6
|
+
@beer = RateBeer.beer(135361) # BrewDog Punk IPA
|
7
7
|
@constructed_params = { beer: @beer,
|
8
8
|
reviewer: "Johnny Tester",
|
9
9
|
reviewer_rank: 1234,
|
@@ -43,7 +43,7 @@ describe RateBeer::Review do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
it "recognises a beer specified by ID#" do
|
46
|
-
expect(@reviews_by_id.first.beer).to eq RateBeer
|
46
|
+
expect(@reviews_by_id.first.beer).to eq RateBeer.beer(@beer_id)
|
47
47
|
end
|
48
48
|
|
49
49
|
it "retrieves reviews for beer specified by ID#" do
|
@@ -18,7 +18,7 @@ describe RateBeer::Search do
|
|
18
18
|
describe '#run_search' do
|
19
19
|
before :all do
|
20
20
|
@failed_search = RateBeer::Search.new('random param 1234').run_search
|
21
|
-
@successful_search = RateBeer::Search.new('
|
21
|
+
@successful_search = RateBeer::Search.new('dugges').run_search
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'executes a search using specified query parameter' do
|
@@ -45,22 +45,15 @@ describe RateBeer::Search do
|
|
45
45
|
it 'returns a list of specific beers matching the query parameter' do
|
46
46
|
beers = @successful_search.beers
|
47
47
|
names = beers.map(&:name)
|
48
|
-
expect(names).to include('
|
49
|
-
'
|
50
|
-
'
|
51
|
-
'Heineken Kylian',
|
52
|
-
'Heineken Oud Bruin',
|
53
|
-
'Heineken Tarwebok')
|
48
|
+
expect(names).to include('Dugges / Stillwater Tropic Thunder',
|
49
|
+
'Dugges Almost Imperial',
|
50
|
+
'Dugges Bärliner')
|
54
51
|
end
|
55
52
|
|
56
53
|
it 'returns a list of specific breweries matching the query parameter' do
|
57
54
|
breweries = @successful_search.breweries
|
58
55
|
names = breweries.map(&:name)
|
59
|
-
expect(names).to include('
|
60
|
-
'Heineken Italia',
|
61
|
-
'Al Ahram (Heineken)',
|
62
|
-
'Pivovar Corgon (Heineken)',
|
63
|
-
'Bralima (Heineken)')
|
56
|
+
expect(names).to include('Dugges Bryggeri')
|
64
57
|
end
|
65
58
|
end
|
66
59
|
end
|
data/spec/lib/ratebeer_spec.rb
CHANGED
@@ -4,14 +4,14 @@ describe RateBeer do
|
|
4
4
|
describe '.beer' do
|
5
5
|
it 'creates a new beer' do
|
6
6
|
beer = RateBeer.beer(1234, 'Magic Lager')
|
7
|
-
expect(beer).to eq RateBeer::Beer.new(1234, name: 'Magic Lager')
|
7
|
+
expect(beer).to eq RateBeer::Beer::Beer.new(1234, name: 'Magic Lager')
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
describe '.brewery' do
|
12
12
|
it 'creates a new brewery' do
|
13
13
|
brewery = RateBeer.brewery(456, 'Magic BrewCo')
|
14
|
-
expect(brewery).to eq RateBeer::Brewery.new(456, name: 'Magic BrewCo')
|
14
|
+
expect(brewery).to eq RateBeer::Brewery::Brewery.new(456, name: 'Magic BrewCo')
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ratebeer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Meakin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -38,7 +38,9 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
description:
|
41
|
+
description: |-
|
42
|
+
RateBeer provides a way to access information from \
|
43
|
+
\RateBeer.com.
|
42
44
|
email: dan@danmeakin.com
|
43
45
|
executables: []
|
44
46
|
extensions: []
|
@@ -52,7 +54,9 @@ files:
|
|
52
54
|
- bin/ratebeer
|
53
55
|
- lib/ratebeer.rb
|
54
56
|
- lib/ratebeer/beer.rb
|
57
|
+
- lib/ratebeer/beer/alias.rb
|
55
58
|
- lib/ratebeer/brewery.rb
|
59
|
+
- lib/ratebeer/brewery/beer_list.rb
|
56
60
|
- lib/ratebeer/country.rb
|
57
61
|
- lib/ratebeer/location.rb
|
58
62
|
- lib/ratebeer/region.rb
|
@@ -89,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
93
|
version: '0'
|
90
94
|
requirements: []
|
91
95
|
rubyforge_project:
|
92
|
-
rubygems_version: 2.
|
96
|
+
rubygems_version: 2.6.8
|
93
97
|
signing_key:
|
94
98
|
specification_version: 4
|
95
99
|
summary: Unofficial RateBeer API
|