beer_list 0.1.3 → 1.0.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.
- data/Gemfile.lock +5 -3
- data/README.md +69 -8
- data/beer_list.gemspec +4 -3
- data/lib/beer_list/cli.rb +48 -54
- data/lib/beer_list/establishments/acadia_cafe.rb +31 -0
- data/lib/beer_list/establishments/blue_nile.rb +55 -0
- data/lib/beer_list/establishments/busters_on28th.rb +54 -0
- data/lib/beer_list/establishments/establishment.rb +1 -6
- data/lib/beer_list/establishments/happy_gnome.rb +50 -0
- data/lib/beer_list/establishments/lowry_uptown.rb +31 -0
- data/lib/beer_list/establishments/mackenzie_pub.rb +26 -0
- data/lib/beer_list/establishments/macs_industrial.rb +33 -0
- data/lib/beer_list/establishments/muddy_pig.rb +15 -0
- data/lib/beer_list/establishments/new_bohemia.rb +52 -0
- data/lib/beer_list/establishments/stanleys_bar_room.rb +26 -0
- data/lib/beer_list/establishments/washington_square.rb +31 -0
- data/lib/beer_list/establishments.rb +3 -9
- data/lib/beer_list/exceptions.rb +0 -7
- data/lib/beer_list/leads/beer_advocate.rb +44 -0
- data/lib/beer_list/listable.rb +24 -0
- data/lib/beer_list/scraper.rb +9 -8
- data/lib/beer_list.rb +10 -4
- data/lib/generators/establishment_generator.rb +1 -1
- data/spec/lib/beer_list/establishments/establishment_spec.rb +5 -19
- data/spec/lib/beer_list_spec.rb +1 -0
- metadata +37 -8
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
beer_list (0.1.
|
5
|
-
mechanize (
|
4
|
+
beer_list (0.1.3)
|
5
|
+
mechanize (~> 2.6.0)
|
6
|
+
thor (~> 0.18.1)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
specs:
|
@@ -30,6 +31,7 @@ GEM
|
|
30
31
|
rspec-expectations (2.12.1)
|
31
32
|
diff-lcs (~> 1.1.3)
|
32
33
|
rspec-mocks (2.12.2)
|
34
|
+
thor (0.18.1)
|
33
35
|
unf (0.1.1)
|
34
36
|
unf_ext
|
35
37
|
unf_ext (0.0.6)
|
@@ -41,4 +43,4 @@ PLATFORMS
|
|
41
43
|
DEPENDENCIES
|
42
44
|
beer_list!
|
43
45
|
bundler (~> 1.3)
|
44
|
-
rspec
|
46
|
+
rspec (~> 2.12.0)
|
data/README.md
CHANGED
@@ -29,6 +29,22 @@ list.to_hash
|
|
29
29
|
list.to_json
|
30
30
|
```
|
31
31
|
|
32
|
+
### Establishments
|
33
|
+
|
34
|
+
Establishments peddle delicious beers. You're interested in which delicious beers a
|
35
|
+
given establishment is peddling. To reconcile this situation, you need one or more
|
36
|
+
BeerList::Establishments. BeerList ships with a limited number of establishments,
|
37
|
+
but its easy to [extend BeerList](#extending-beerlist-with-more-establishments) with more.
|
38
|
+
|
39
|
+
First, let's try one out of the box:
|
40
|
+
|
41
|
+
```
|
42
|
+
bulldog_northeast = BeerList::Establishments::BulldogNortheast.new
|
43
|
+
|
44
|
+
# Now get the list
|
45
|
+
bulldog_northeast.list
|
46
|
+
```
|
47
|
+
|
32
48
|
You may want to get lists for more than one establishment at a time. To do so, register
|
33
49
|
the desired establishments in BeerList.establishments:
|
34
50
|
|
@@ -47,23 +63,22 @@ BeerList.establishments
|
|
47
63
|
BeerList.lists
|
48
64
|
|
49
65
|
# As a hash
|
50
|
-
BeerList.
|
66
|
+
BeerList.lists_as_hash
|
51
67
|
|
52
68
|
# As JSON
|
53
|
-
BeerList.
|
69
|
+
BeerList.lists_as_json
|
54
70
|
```
|
55
71
|
|
56
72
|
The Lists will be memoized until the content of BeerList.establishments changes so that
|
57
|
-
|
73
|
+
the establishments don't have to be re-scraped each time the list is requested.
|
58
74
|
|
59
75
|
### Extending BeerList with More Establishments
|
60
76
|
|
61
|
-
BeerList
|
62
|
-
to easily create your own.
|
77
|
+
BeerList includes an executable to easily create your own establishments.
|
63
78
|
|
64
79
|
For example:
|
65
80
|
|
66
|
-
|
81
|
+
`$ beer_list establish Applebirds -u http://applebirds.com/beers -d path/to/establishments`
|
67
82
|
|
68
83
|
will create the following code in path/to/establishments/applebirds.rb
|
69
84
|
|
@@ -92,7 +107,7 @@ end
|
|
92
107
|
|
93
108
|
For all options you can pass to beer_list establish, run:
|
94
109
|
|
95
|
-
|
110
|
+
`$ beer_list help`
|
96
111
|
|
97
112
|
### Using Your Generated Establishments
|
98
113
|
|
@@ -116,8 +131,54 @@ BeerList.lists
|
|
116
131
|
|
117
132
|
```
|
118
133
|
|
119
|
-
See [Getting A List](
|
134
|
+
See [Getting A List](#getting-a-list) for more details.
|
120
135
|
|
121
136
|
AND...
|
122
137
|
|
123
138
|
Checkout [This link](http://mechanize.rubyforge.org/) for more on Mechanize
|
139
|
+
|
140
|
+
### Leads
|
141
|
+
|
142
|
+
If you're out of ideas on what establishments you may want lists for, fear not: BeerList can
|
143
|
+
give you some ideas.
|
144
|
+
|
145
|
+
```
|
146
|
+
# You might be interested in good beer bars located in California:
|
147
|
+
cali = BeerList::Leads::CA.new
|
148
|
+
|
149
|
+
# returns a BeerList::List of URLs for popular beer bars in California
|
150
|
+
cali.list
|
151
|
+
|
152
|
+
|
153
|
+
# Maybe you want Wisconsin:
|
154
|
+
wi = BeerList::Leads:WI.new
|
155
|
+
|
156
|
+
wi.list
|
157
|
+
|
158
|
+
```
|
159
|
+
|
160
|
+
So far, support for this feature is limited to the United States. Hopefully, I can expand
|
161
|
+
it in the not-too-distant future.
|
162
|
+
|
163
|
+
### CLI
|
164
|
+
|
165
|
+
In addition to the [establish](#extending-beerlist-with-more-establishments) command, which
|
166
|
+
generates Establishment files for you, BeerList also offers the `list` command. For example,
|
167
|
+
say you have the following two establishments a directory called ~/my_beer_lists:
|
168
|
+
|
169
|
+
```
|
170
|
+
BeerList::Establishments::Applebirds
|
171
|
+
BeerList::Establishments::Thursdays
|
172
|
+
```
|
173
|
+
|
174
|
+
You can get the beer lists for these places from the command line:
|
175
|
+
|
176
|
+
```
|
177
|
+
$ beer_list list applebirds -d ~/my_beer_lists
|
178
|
+
|
179
|
+
# or
|
180
|
+
$ beer_list list applebirds thursdays -d ~/my_beer_lists
|
181
|
+
|
182
|
+
# pass -j for JSON
|
183
|
+
$ beer_list list applebirds thursdays -j -d ~/my_beer_lists
|
184
|
+
```
|
data/beer_list.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = 'beer_list'
|
3
|
-
spec.version = '0.
|
3
|
+
spec.version = '1.0.0'
|
4
4
|
spec.authors = ['Dan Olson']
|
5
5
|
spec.email = ['olson_dan@yahoo.com']
|
6
6
|
spec.description = 'A utility for retrieving the beer list from various establishments'
|
@@ -13,7 +13,8 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.test_files = spec.files.grep(%r{^(spec)/})
|
14
14
|
spec.require_paths = ['lib']
|
15
15
|
|
16
|
-
spec.add_dependency 'mechanize', '2.6.0'
|
16
|
+
spec.add_dependency 'mechanize', '~> 2.6.0'
|
17
|
+
spec.add_dependency 'thor', '~> 0.18.1'
|
17
18
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
18
|
-
spec.add_development_dependency 'rspec'
|
19
|
+
spec.add_development_dependency 'rspec', '~> 2.12.0'
|
19
20
|
end
|
data/lib/beer_list/cli.rb
CHANGED
@@ -1,65 +1,59 @@
|
|
1
|
-
require '
|
1
|
+
require 'thor'
|
2
2
|
|
3
3
|
module BeerList
|
4
|
-
class CLI
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
opts.on("-u", "--url [URL]", "url of your establishment's beer list") do |url|
|
24
|
-
options[:url] = url
|
25
|
-
end
|
26
|
-
|
27
|
-
opts.on("-s", "--selector [SELECTOR]", "selector to use for scraping") do |s|
|
28
|
-
options[:selector] = s
|
29
|
-
end
|
4
|
+
class CLI < Thor
|
5
|
+
option :url,
|
6
|
+
aliases: '-u',
|
7
|
+
banner: "The URL of your establishment's beer list"
|
8
|
+
option :directory,
|
9
|
+
required: true,
|
10
|
+
aliases: '-d',
|
11
|
+
banner: 'The directory in which BeerList will put your establishments'
|
12
|
+
option :selector,
|
13
|
+
aliases: '-s',
|
14
|
+
banner: 'Optional selector to use for scraping'
|
15
|
+
|
16
|
+
desc 'establish ESTABLISHMENT', 'Generate a subclass of BeerList::Establishments::Establishment in the given directory'
|
17
|
+
def establish(klass)
|
18
|
+
# Support underscore and camelcase
|
19
|
+
klass = klass.split('_').map(&:capitalize).join if klass.match(/_/)
|
20
|
+
BeerList::EstablishmentGenerator.new(klass, options)
|
21
|
+
end
|
30
22
|
|
31
|
-
|
32
|
-
|
33
|
-
|
23
|
+
option :directory,
|
24
|
+
aliases: '-d',
|
25
|
+
banner: 'The directory where your establishments are stored'
|
26
|
+
option :json,
|
27
|
+
aliases: '-j',
|
28
|
+
type: :boolean,
|
29
|
+
banner: 'Format output as JSON'
|
30
|
+
|
31
|
+
desc 'list ESTABLISHMENTS', 'Retrieve the beer list at the given establishments'
|
32
|
+
def list(*establishments)
|
33
|
+
BeerList.establishments_dir = options[:directory]
|
34
|
+
BeerList.add_establishments *classify(establishments)
|
35
|
+
if options[:json]
|
36
|
+
puts BeerList.lists_as_json
|
37
|
+
else
|
38
|
+
BeerList.lists.each do |list|
|
39
|
+
puts '*' * (list.establishment.size + 10)
|
40
|
+
puts "**** #{list.establishment} ****"
|
41
|
+
puts '*' * (list.establishment.size + 10)
|
42
|
+
puts
|
43
|
+
puts list
|
44
|
+
puts
|
34
45
|
end
|
35
|
-
|
36
|
-
opts.parse! args
|
37
|
-
options
|
38
|
-
end
|
39
|
-
|
40
|
-
def unsupported_command
|
41
|
-
<<-WARN
|
42
|
-
Command: not supported. Must be one of the following:
|
43
|
-
#{COMMANDS.join(', ')}
|
44
|
-
WARN
|
45
|
-
end
|
46
|
-
|
47
|
-
def no_establishments_dir
|
48
|
-
<<-WARN
|
49
|
-
You must supply an establishments directory with -d or --directory
|
50
|
-
WARN
|
51
46
|
end
|
52
47
|
end
|
53
48
|
|
54
|
-
|
55
|
-
@args = args
|
56
|
-
end
|
49
|
+
private
|
57
50
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
51
|
+
def classify(establishments)
|
52
|
+
establishments.map do |est|
|
53
|
+
class_name = est.to_s.split('_').map(&:capitalize).join
|
54
|
+
klass = BeerList.send(:get_class_with_namespace, class_name)
|
55
|
+
klass.new
|
56
|
+
end
|
63
57
|
end
|
64
58
|
end
|
65
59
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class AcadiaCafe < Establishment
|
4
|
+
URL = 'http://acadiacafe.com/as_beer-menu-and-happy-hour-specials'
|
5
|
+
|
6
|
+
def get_list
|
7
|
+
base_list
|
8
|
+
having_price_and_abv
|
9
|
+
match_before_comma
|
10
|
+
end
|
11
|
+
|
12
|
+
def url
|
13
|
+
URL
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def base_list
|
19
|
+
@acadia = page.search('p').map(&:text)
|
20
|
+
end
|
21
|
+
|
22
|
+
def having_price_and_abv
|
23
|
+
@acadia = @acadia.select{ |str| str.match(/\d{1,2}\.\d{1,2}%+.*\$+/) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def match_before_comma
|
27
|
+
@acadia = @acadia.map{ |b| b.match(/,{1}/); $` }.reject(&:nil?).map(&:strip)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class BlueNile < Establishment
|
4
|
+
RESTAURANT_NAME = 'Blue Nile'
|
5
|
+
URL = 'http://www.bluenilempls.com/bar-b.html'
|
6
|
+
|
7
|
+
def get_list
|
8
|
+
base_list
|
9
|
+
trim
|
10
|
+
end
|
11
|
+
|
12
|
+
def url
|
13
|
+
URL
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def headers
|
19
|
+
@headers ||= remove_carraige_and_strip(page.search('.style5').map(&:text))
|
20
|
+
end
|
21
|
+
|
22
|
+
def base_list
|
23
|
+
@beers = remove_carraige_and_strip(page.search('p').map(&:text)).reject{ |b| headers.include? b }
|
24
|
+
remove_contact_info
|
25
|
+
end
|
26
|
+
|
27
|
+
def trim
|
28
|
+
trim_cost_and_origin
|
29
|
+
trim_volume
|
30
|
+
reject_non_word_entries
|
31
|
+
end
|
32
|
+
|
33
|
+
def trim_cost_and_origin
|
34
|
+
@beers = @beers.map{ |b| b.split('(').first }.map{ |b| b.split(' $').first }
|
35
|
+
end
|
36
|
+
|
37
|
+
def trim_volume
|
38
|
+
@beers = @beers.map{ |b| b.match(/,?\s?\d+\s?(ml|oz|0z)/) ? $`.strip : b.strip }
|
39
|
+
end
|
40
|
+
|
41
|
+
def reject_non_word_entries
|
42
|
+
@beers = @beers.reject{ |b| b.match /\A\W\z/ }
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove_contact_info
|
46
|
+
index = @beers.index(RESTAURANT_NAME)
|
47
|
+
@beers.pop @beers.size - index
|
48
|
+
end
|
49
|
+
|
50
|
+
def remove_carraige_and_strip(ary)
|
51
|
+
ary.map{ |b| b.split("\r").map(&:strip) }.flatten.reject(&:empty?)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class BustersOn28th < Establishment
|
4
|
+
URL = 'http://busterson28th.com/bottles/'
|
5
|
+
|
6
|
+
def get_list
|
7
|
+
base_list
|
8
|
+
split_on_newline
|
9
|
+
reject_headers
|
10
|
+
match_pre_vol
|
11
|
+
reject_nils
|
12
|
+
end
|
13
|
+
|
14
|
+
def url
|
15
|
+
URL
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def base_list
|
21
|
+
@busters = page.search('p').map(&:text)
|
22
|
+
end
|
23
|
+
|
24
|
+
def split_on_newline
|
25
|
+
@busters = @busters.map{ |beer| beer.split("\n") }.flatten
|
26
|
+
end
|
27
|
+
|
28
|
+
# First, select all entries that have a lowercase letter (headers do not)
|
29
|
+
# At that point, there are still some headers mixed with beer names.
|
30
|
+
# For example:
|
31
|
+
#
|
32
|
+
# STOUTSurly Darkness
|
33
|
+
#
|
34
|
+
# Match group 3 or more capital letters followed by one cap and one lower
|
35
|
+
# take the portion of the beer name after the match group
|
36
|
+
def reject_headers
|
37
|
+
@busters = @busters.select{ |beer| beer.match /[a-z]/ }
|
38
|
+
@busters = @busters.map do |beer|
|
39
|
+
beer.match(/([A-Z]{3,})[A-Z]{1}[a-z]{1}/) ? beer.split($1).last : beer
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def match_pre_vol
|
44
|
+
@busters = @busters.map do |beer|
|
45
|
+
beer.match(/\d{1,2}\.?\d*\s*oz/) ? $`.strip : nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def reject_nils
|
50
|
+
@busters = @busters.reject(&:nil?)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,12 +1,7 @@
|
|
1
1
|
module BeerList
|
2
2
|
module Establishments
|
3
3
|
class Establishment
|
4
|
-
|
5
|
-
|
6
|
-
def list
|
7
|
-
raise BeerList::NoScraperError unless @scraper
|
8
|
-
@list ||= BeerList::List.new establishment: short_class_name, array: get_list
|
9
|
-
end
|
4
|
+
include BeerList::Listable
|
10
5
|
|
11
6
|
def get_list
|
12
7
|
raise "#{__method__} is not implemented in #{self.class.name}"
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class HappyGnome < Establishment
|
4
|
+
attr_accessor :url
|
5
|
+
|
6
|
+
DRAFTS = 'http://thehappygnome.com/menus/drafts/'
|
7
|
+
BOTTLES = 'http://thehappygnome.com/menus/bottled-beers/'
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@url = DRAFTS
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_list
|
14
|
+
get_draft_list
|
15
|
+
get_bottle_list
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def get_the_list
|
21
|
+
base_list
|
22
|
+
match_before_paren
|
23
|
+
reject_empty
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_draft_list
|
27
|
+
@beers = []
|
28
|
+
@beers += get_the_list
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_bottle_list
|
32
|
+
self.url = BOTTLES
|
33
|
+
self.page = BeerList.scraper.visit self
|
34
|
+
@beers += get_the_list
|
35
|
+
end
|
36
|
+
|
37
|
+
def base_list
|
38
|
+
@happy_gnome = page.search('p').map(&:text)
|
39
|
+
end
|
40
|
+
|
41
|
+
def match_before_paren
|
42
|
+
@happy_gnome = @happy_gnome.map{ |b| b.match(/\(/); $` }
|
43
|
+
end
|
44
|
+
|
45
|
+
def reject_empty
|
46
|
+
@happy_gnome = @happy_gnome.reject(&:nil?).map(&:strip)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class LowryUptown < Establishment
|
4
|
+
URL = 'http://www.thelowryuptown.com/drink'
|
5
|
+
|
6
|
+
def get_list
|
7
|
+
base_list
|
8
|
+
match_before_abv
|
9
|
+
strip
|
10
|
+
end
|
11
|
+
|
12
|
+
def url
|
13
|
+
URL
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def base_list
|
19
|
+
@beers = page.search('div.third_col').first.search('p').map(&:text)
|
20
|
+
end
|
21
|
+
|
22
|
+
def match_before_abv
|
23
|
+
@beers = @beers.map{ |beer| beer.match(/\d+\.?\d*%/); $` }.compact
|
24
|
+
end
|
25
|
+
|
26
|
+
def strip
|
27
|
+
@beers = @beers.map{ |beer| beer.gsub(/\A\W*|\W*\z/, '') }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class MackenziePub < Establishment
|
4
|
+
URL = 'http://mackenziepub.com/drink/beer/'
|
5
|
+
|
6
|
+
def get_list
|
7
|
+
base_list
|
8
|
+
strip_leading_star
|
9
|
+
end
|
10
|
+
|
11
|
+
def url
|
12
|
+
URL
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def base_list
|
18
|
+
@beers = page.search('div.menu-container-cell-description h4').map(&:text)
|
19
|
+
end
|
20
|
+
|
21
|
+
def strip_leading_star
|
22
|
+
@beers = @beers.map{ |beer| beer.gsub(/\A\*/, '') }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class MacsIndustrial < Establishment
|
4
|
+
URL = 'http://www.macsindustrial.com/tap_page.php'
|
5
|
+
|
6
|
+
def get_list
|
7
|
+
build_list
|
8
|
+
end
|
9
|
+
|
10
|
+
def url
|
11
|
+
URL
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_list
|
17
|
+
breweries.zip(beers).map{ |b| b.join(' ') }
|
18
|
+
end
|
19
|
+
|
20
|
+
def base_list
|
21
|
+
@macs ||= page.search('tr:first-child td:last-child')
|
22
|
+
end
|
23
|
+
|
24
|
+
def breweries
|
25
|
+
base_list.search('a strong').children.map(&:text)
|
26
|
+
end
|
27
|
+
|
28
|
+
def beers
|
29
|
+
base_list.search('span strong').children.map(&:text).map{ |b| b[1..-1] }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class NewBohemia < Establishment
|
4
|
+
URL = 'http://www.newbohemiausa.com/bier/'
|
5
|
+
|
6
|
+
def get_list
|
7
|
+
base_list
|
8
|
+
strip
|
9
|
+
end
|
10
|
+
|
11
|
+
def url
|
12
|
+
URL
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def base_list
|
18
|
+
@beers = [left, right].inject([]) do |ary, collection|
|
19
|
+
ary += match_before_slash(collection).compact
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def strip
|
24
|
+
@beers = @beers.map{ |beer| beer.gsub(/\A\W*|\W*\z/, '') }
|
25
|
+
end
|
26
|
+
|
27
|
+
def left
|
28
|
+
split_on_newline(left_column).flatten
|
29
|
+
end
|
30
|
+
|
31
|
+
def right
|
32
|
+
right_column
|
33
|
+
end
|
34
|
+
|
35
|
+
def left_column
|
36
|
+
page.search('div.col-left p').map(&:text)
|
37
|
+
end
|
38
|
+
|
39
|
+
def right_column
|
40
|
+
page.search('div.col-right p span').map(&:text)
|
41
|
+
end
|
42
|
+
|
43
|
+
def split_on_newline(beers)
|
44
|
+
beers.inject([]) { |ary, string| ary << string.split("\n") }
|
45
|
+
end
|
46
|
+
|
47
|
+
def match_before_slash(beers)
|
48
|
+
beers.map{ |beer| beer.match(/\A([^\/]+)\/{1}/); $1 }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class StanleysBarRoom < Establishment
|
4
|
+
URL = 'http://www.stanleysbarroom.com/drinks/beer-list/'
|
5
|
+
|
6
|
+
def get_list
|
7
|
+
base_list
|
8
|
+
remove_blank
|
9
|
+
end
|
10
|
+
|
11
|
+
def url
|
12
|
+
URL
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def base_list
|
18
|
+
@beers = page.search('td ul li').map(&:text)
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove_blank
|
22
|
+
@beers = @beers.map(&:strip).reject{ |beer| beer.empty? }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Establishments
|
3
|
+
class WashingtonSquare < Establishment
|
4
|
+
URL = 'http://www.washingtonsquareonline.net/as_on-tap'
|
5
|
+
|
6
|
+
def get_list
|
7
|
+
base_list
|
8
|
+
remove_facebook_plug
|
9
|
+
remove_nbsp
|
10
|
+
end
|
11
|
+
|
12
|
+
def url
|
13
|
+
URL
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def base_list
|
19
|
+
@results = page.search('p strong').map(&:text)
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove_facebook_plug
|
23
|
+
@results.shift
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove_nbsp
|
27
|
+
@results = @results.map{ |b| b.gsub(/[[:space:]]{2,}/, '') }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,17 +1,11 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
-
|
3
1
|
module BeerList
|
4
2
|
module Establishments
|
5
|
-
require 'establishments/establishment'
|
3
|
+
require 'beer_list/establishments/establishment.rb'
|
6
4
|
|
7
|
-
Dir[File.dirname(__FILE__) + '/establishments/*.rb'].each
|
8
|
-
require f.split('.rb').first
|
9
|
-
end
|
5
|
+
Dir[File.dirname(__FILE__) + '/establishments/*.rb'].each { |f| require f }
|
10
6
|
|
11
7
|
if BeerList.establishments_dir
|
12
|
-
Dir[File.join(BeerList.establishments_dir, '*.rb')].each
|
13
|
-
require f.split('.rb').first
|
14
|
-
end
|
8
|
+
Dir[File.join(BeerList.establishments_dir, '*.rb')].each { |f| require f }
|
15
9
|
end
|
16
10
|
end
|
17
11
|
end
|
data/lib/beer_list/exceptions.rb
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Leads
|
3
|
+
class BeerAdvocate
|
4
|
+
include BeerList::Listable
|
5
|
+
|
6
|
+
BEERFLY = 'http://beeradvocate.com/beerfly/list'
|
7
|
+
LEAD_INDICATOR_TEXT = 'official website'
|
8
|
+
|
9
|
+
def links
|
10
|
+
@links ||= []
|
11
|
+
pages
|
12
|
+
end
|
13
|
+
alias :get_list :links
|
14
|
+
|
15
|
+
def url
|
16
|
+
"#{BEERFLY}?c_id=US&s_id=#{short_class_name}&bar=Y"
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def pages
|
22
|
+
@links += page.links_with(:text => LEAD_INDICATOR_TEXT).map(&:href)
|
23
|
+
|
24
|
+
if next_link = page.links_with(:text => /next/).first
|
25
|
+
self.page = next_link.click
|
26
|
+
pages
|
27
|
+
end
|
28
|
+
@links
|
29
|
+
end
|
30
|
+
|
31
|
+
def short_class_name
|
32
|
+
self.class.name.split('::').last
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
%w{
|
37
|
+
AL AK AZ AR CA CO CT DE FL GA HI ID IL IN IA KS KY
|
38
|
+
LA ME MD MA MI MN MS MO MT NE NV NH NJ NM NY NC ND
|
39
|
+
OH OK OR PA RI SC SD TN TX UT VT VA WA WV WI WY
|
40
|
+
}.each do |state|
|
41
|
+
const_set state, Class.new(BeerAdvocate)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module BeerList
|
2
|
+
module Listable
|
3
|
+
attr_accessor :page
|
4
|
+
|
5
|
+
def list
|
6
|
+
visit_page unless page
|
7
|
+
@list ||= BeerList::List.new establishment: short_class_name, array: get_list
|
8
|
+
end
|
9
|
+
|
10
|
+
def short_class_name
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_list
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def visit_page
|
21
|
+
BeerList.scraper.visit self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/beer_list/scraper.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
1
3
|
module BeerList
|
2
4
|
class Scraper
|
3
|
-
|
5
|
+
include Singleton
|
4
6
|
|
5
7
|
USER_AGENT = 'Mac Safari'
|
6
8
|
|
@@ -10,19 +12,18 @@ module BeerList
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def beer_list(establishment)
|
13
|
-
establishment
|
14
|
-
visit_site establishment
|
15
|
+
visit establishment
|
15
16
|
establishment.list
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
def visit_site(establishment)
|
21
|
-
establishment.page = agent.get(establishment.url)
|
19
|
+
def visit(visitable)
|
20
|
+
visitable.page = @agent.get(visitable.url)
|
22
21
|
end
|
23
22
|
|
23
|
+
private
|
24
|
+
|
24
25
|
def set_user_agent
|
25
|
-
agent.user_agent_alias = USER_AGENT
|
26
|
+
@agent.user_agent_alias = USER_AGENT
|
26
27
|
end
|
27
28
|
end
|
28
29
|
end
|
data/lib/beer_list.rb
CHANGED
@@ -8,6 +8,8 @@ module BeerList
|
|
8
8
|
require 'beer_list/cli'
|
9
9
|
require 'generators/establishment_generator'
|
10
10
|
require 'ext/string'
|
11
|
+
require 'beer_list/listable'
|
12
|
+
require 'beer_list/leads/beer_advocate'
|
11
13
|
autoload :Establishments, 'beer_list/establishments'
|
12
14
|
|
13
15
|
class << self
|
@@ -55,12 +57,12 @@ module BeerList
|
|
55
57
|
lists_as_hash.to_json
|
56
58
|
end
|
57
59
|
|
58
|
-
private
|
59
|
-
|
60
60
|
def scraper
|
61
|
-
@scraper ||= Scraper.
|
61
|
+
@scraper ||= Scraper.instance
|
62
62
|
end
|
63
63
|
|
64
|
+
private
|
65
|
+
|
64
66
|
def update_necessary?
|
65
67
|
!@lists || !establishments_eq_lists?
|
66
68
|
end
|
@@ -76,8 +78,12 @@ module BeerList
|
|
76
78
|
|
77
79
|
def method_missing(method, *args, &block)
|
78
80
|
class_name = method.to_s.split('_').map(&:capitalize).join
|
79
|
-
klass =
|
81
|
+
klass = get_class_with_namespace class_name
|
80
82
|
scraper.beer_list klass.new
|
81
83
|
end
|
84
|
+
|
85
|
+
def get_class_with_namespace(class_name)
|
86
|
+
['BeerList', 'Establishments', class_name].inject(Object){ |o, name| o.const_get(name) }
|
87
|
+
end
|
82
88
|
end
|
83
89
|
end
|
@@ -4,25 +4,13 @@ module BeerList
|
|
4
4
|
module Establishments
|
5
5
|
|
6
6
|
describe Establishment do
|
7
|
-
let(:establishment){ BeerList::Establishments::
|
7
|
+
let(:establishment){ BeerList::Establishments::MuddyWaters.new }
|
8
8
|
|
9
9
|
describe '#list' do
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
context 'when it has a scraper' do
|
17
|
-
|
18
|
-
before do
|
19
|
-
establishment.scraper = stub
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'returns a BeerList::List' do
|
23
|
-
establishment.stub(:get_list){ [] }
|
24
|
-
establishment.list.should be_an_instance_of BeerList::List
|
25
|
-
end
|
10
|
+
it 'returns a BeerList::List' do
|
11
|
+
establishment.stub(:visit_page)
|
12
|
+
establishment.stub(:get_list){ [] }
|
13
|
+
establishment.list.should be_an_instance_of BeerList::List
|
26
14
|
end
|
27
15
|
end
|
28
16
|
|
@@ -41,8 +29,6 @@ module BeerList
|
|
41
29
|
end
|
42
30
|
|
43
31
|
describe '#short_class_name' do
|
44
|
-
let(:establishment){ BeerList::Establishments::MuddyWaters.new }
|
45
|
-
|
46
32
|
it 'returns a usable name' do
|
47
33
|
establishment.short_class_name.should == 'MuddyWaters'
|
48
34
|
end
|
data/spec/lib/beer_list_spec.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beer_list
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,14 +9,14 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mechanize
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: 2.6.0
|
22
22
|
type: :runtime
|
@@ -24,9 +24,25 @@ dependencies:
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 2.6.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: thor
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.18.1
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.18.1
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: bundler
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -48,17 +64,17 @@ dependencies:
|
|
48
64
|
requirement: !ruby/object:Gem::Requirement
|
49
65
|
none: false
|
50
66
|
requirements:
|
51
|
-
- -
|
67
|
+
- - ~>
|
52
68
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
69
|
+
version: 2.12.0
|
54
70
|
type: :development
|
55
71
|
prerelease: false
|
56
72
|
version_requirements: !ruby/object:Gem::Requirement
|
57
73
|
none: false
|
58
74
|
requirements:
|
59
|
-
- -
|
75
|
+
- - ~>
|
60
76
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
77
|
+
version: 2.12.0
|
62
78
|
description: A utility for retrieving the beer list from various establishments
|
63
79
|
email:
|
64
80
|
- olson_dan@yahoo.com
|
@@ -78,17 +94,30 @@ files:
|
|
78
94
|
- lib/beer_list.rb
|
79
95
|
- lib/beer_list/cli.rb
|
80
96
|
- lib/beer_list/establishments.rb
|
97
|
+
- lib/beer_list/establishments/acadia_cafe.rb
|
98
|
+
- lib/beer_list/establishments/blue_nile.rb
|
81
99
|
- lib/beer_list/establishments/bulldog_lowertown.rb
|
82
100
|
- lib/beer_list/establishments/bulldog_northeast.rb
|
83
101
|
- lib/beer_list/establishments/bulldog_uptown.rb
|
102
|
+
- lib/beer_list/establishments/busters_on28th.rb
|
84
103
|
- lib/beer_list/establishments/edina_grill.rb
|
85
104
|
- lib/beer_list/establishments/establishment.rb
|
86
105
|
- lib/beer_list/establishments/groveland_tap.rb
|
106
|
+
- lib/beer_list/establishments/happy_gnome.rb
|
87
107
|
- lib/beer_list/establishments/longfellow_grill.rb
|
108
|
+
- lib/beer_list/establishments/lowry_uptown.rb
|
109
|
+
- lib/beer_list/establishments/mackenzie_pub.rb
|
110
|
+
- lib/beer_list/establishments/macs_industrial.rb
|
111
|
+
- lib/beer_list/establishments/muddy_pig.rb
|
88
112
|
- lib/beer_list/establishments/muddy_waters.rb
|
113
|
+
- lib/beer_list/establishments/new_bohemia.rb
|
114
|
+
- lib/beer_list/establishments/stanleys_bar_room.rb
|
89
115
|
- lib/beer_list/establishments/three_squares.rb
|
116
|
+
- lib/beer_list/establishments/washington_square.rb
|
90
117
|
- lib/beer_list/exceptions.rb
|
118
|
+
- lib/beer_list/leads/beer_advocate.rb
|
91
119
|
- lib/beer_list/list.rb
|
120
|
+
- lib/beer_list/listable.rb
|
92
121
|
- lib/beer_list/scraper.rb
|
93
122
|
- lib/ext/string.rb
|
94
123
|
- lib/generators/establishment_generator.rb
|