Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit ea80a60fb55596a46c01c05aed883f7571661759 @gabebw committed Aug 15, 2010
@@ -0,0 +1,2 @@
+1.0.0 / 2010-08-14
+* Birthday!
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2010 Gabe Berke-Williams
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,65 @@
+= Restaurant Week Boston
+This is just a silly little gem I made that parses the Restaurant Week
+Boston (http://www.restaurantweekboston.com/) website and lets you mark which
+ones you want to go to for lunch, dinner, or lunch and dinner.
+
+= Installation
+<tt>gem install restaurant_week_boston</tt>
+
+= Usage
+Use the +restaurant_week_boston+ command line script.
+Use -a/--all, -l/--lunch, and -d/--dinner options to mark restaurants that
+you want to go to for lunch *and* dinner, lunch, or dinner, respectively.
+
+So if you want to go to Bond and Aquitaine Bis for lunch and dinner, you'd
+do
+
+<tt>$ restaurant_week_boston -a 'aquitaine bis',bond</tt>
+
+=== More involved demo
+
+<tt>$ restaurant_week_boston -a 'aquitaine bis' -l bond,asana -d bergamot,clink</tt>
+ Getting doc...done.
+ Parsing doc...done.
+
+ === LUNCH ===
+ Name: Asana (short-name: asana)
+ Meals: Lunch, Dinner
+ Neighborhood: Back Bay
+ Phone: 617-535-8800
+ Lunch Menu: http://www.restaurantweekboston.com/fetch/asana/lunch/
+ Dinner Menu: http://www.restaurantweekboston.com/fetch/asana/dinner/
+ Map: http://www.restaurantweekboston.com/map/back-bay/asana/#topOfMap
+ --------------------------------------------------------------------------------
+ Name: Bond (short-name: bond)
+ Meals: Lunch, Dinner
+ Neighborhood: Downtown
+ Phone: 617-451-1900
+ Lunch Menu: http://www.restaurantweekboston.com/fetch/bond/lunch/
+ Dinner Menu: http://www.restaurantweekboston.com/fetch/bond/dinner/
+ Map: http://www.restaurantweekboston.com/map/downtown/bond/#topOfMap
+
+ === DINNER ===
+ Name: Bergamot (short-name: bergamot)
+ Meals: Dinner
+ Neighborhood: Somerville
+ Phone: <no phone given>
+ Dinner Menu: http://www.restaurantweekboston.com/fetch/bergamot/dinner/
+ Map: http://www.restaurantweekboston.com/map/somerville/bergamot/#topOfMap
+ --------------------------------------------------------------------------------
+ Name: Clink. at the Liberty Hotel (short-name: clink)
+ Meals: Lunch, Dinner
+ Neighborhood: Beacon Hill
+ Phone: 617-224-4004
+ Lunch Menu: http://www.restaurantweekboston.com/fetch/clink/lunch/
+ Dinner Menu: http://www.restaurantweekboston.com/fetch/clink/dinner/
+ Map: http://www.restaurantweekboston.com/map/beacon-hill/clink/#topOfMap
+
+ === LUNCH OR DINNER ===
+ Name: Aquitaine Bis (short-name: aquitaine-bis)
+ Meals: Lunch, Dinner
+ Neighborhood: Chestnut Hill
+ Phone: 617-734-8400
+ Lunch Menu: http://www.restaurantweekboston.com/fetch/aquitaine-bis/lunch/
+ Dinner Menu: http://www.restaurantweekboston.com/fetch/aquitaine-bis/dinner/
+ Map: http://www.restaurantweekboston.com/map/chestnut-hill/aquitaine-bis/#topOfMap
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+
+require 'restaurant_week_boston'
+runner = RestaurantWeekBoston::Runner.new(ARGV)
+runner.run()
@@ -0,0 +1,5 @@
+require 'rubygems'
+require 'restaurant_week_boston/scraper'
+require 'restaurant_week_boston/marker'
+require 'restaurant_week_boston/restaurant'
+require 'restaurant_week_boston/runner'
@@ -0,0 +1,63 @@
+require 'restaurant_week_boston/scraper'
+
+module RestaurantWeekBoston
+ # Uses a given Scraper and marks restaurants as good for lunch, dinner, any,
+ # etc.
+ class Marker
+ # +opts+ is a hash with keys for +:lunch+, +:dinner+, and +:any+.
+ # The values of each of these keys should be an array suitable for passing
+ # to Scraper#special_find().
+ def initialize(scraper, opts)
+ @scraper = scraper
+ @opts = opts
+ end
+
+ # Print out restaurants that are good for lunch.
+ def lunch
+ unless @lunch
+ if @opts.key?(:lunch)
+ @lunch = @scraper.special_find(@opts[:lunch])
+ else
+ @lunch = '[no lunch options specified]'
+ end
+ end
+ puts "=== LUNCH ==="
+ puts @lunch
+ end
+
+ # Print out restaurants that are good for dinner.
+ def dinner
+ unless @dinner
+ if @opts.key?(:dinner)
+ @dinner = @scraper.special_find(@opts[:dinner])
+ else
+ @dinner = '[no dinner options specified]'
+ end
+ end
+ puts "=== DINNER ==="
+ puts @dinner
+ end
+
+ # Print out restaurants that are good for lunch or dinner.
+ def any
+ unless @any
+ if @opts.key?(:any)
+ @any = @scraper.special_find(@opts[:any])
+ else
+ @any = '[no "lunch or dinner" options specified]'
+ end
+ end
+ puts "=== LUNCH OR DINNER ==="
+ puts @any
+ end
+
+ # Print lunch(), dinner(), and any(), with newlines between them.
+ def all
+ lunch()
+ puts
+ dinner()
+ puts
+ any()
+ end
+ end
+end
@@ -0,0 +1,134 @@
+module RestaurantWeekBoston
+ # A Restaurant has the following properties:
+ # * name ("224 Boston Street")
+ # * short-name ("224-boston-street", used internally by RWB site)
+ # * meals: %w{lunch dinner}, %w{lunch}, %w{dinner}
+ # * neighborhood: "back-bay", etc
+ # * phone: "617-555-1234"
+ # * menu: use menu_link_for("lunch" || "dinner")
+ # * map_link: a link to the RWB map page for that Restaurant
+ class Restaurant
+ include Comparable
+
+ # +entry+ is a Nokogiri::XML::Node from the RWB web site.
+ def initialize(entry)
+ @entry = entry
+ end
+
+
+ # Compares this Restaurant's name to +other+'s name, and returns -1, 0, 1
+ # as appropriate.
+ def <=>(other)
+ return name <=> other.name
+ end
+
+
+ # Generate a pretty representation of the Restaurant.
+ def to_s
+ # back-bay -> "Back Bay"
+ pretty_neighborhood = neighborhood.split('-').map{|x| x.capitalize }.join(' ')
+
+ s = "Name: #{name} (short-name: #{short_name})"
+ s += "\n"
+ s += "Meals: #{meals.map(&:capitalize).join(', ')}"
+ s += "\n"
+ s += "Neighborhood: #{pretty_neighborhood}"
+ s += "\n"
+ s += "Phone: #{phone}"
+ @meals.each do |meal|
+ s += "\n"
+ s += "%s Menu: #{menu_link_for(meal)}" % [meal.capitalize]
+ end
+ s += "\n"
+ s += "Map: #{map_link}"
+ s
+ end
+
+
+ # Returns "<Restaurant: #{restaurant-name}>"
+ def inspect
+ "<Restaurant: #{name}>"
+ end
+
+ # Returns true if this Restaurant offers lunch.
+ def offers_lunch?
+ meals.include?('lunch')
+ end
+
+ # Returns true if this Restaurant offers dinner.
+ def offers_dinner?
+ meals.include?('dinner')
+ end
+
+ # Return the short name (e.g. '224-boston-street' for "224 Boston Street").
+ def short_name
+ @short_name ||= @entry[:id].sub('restaurantID-', '')
+ end
+
+ # Return the full name of this Restaurant, e.g. "224 Boston Street".
+ def name
+ unless @name
+ # UGLY, but it's not wrapped in a tag so there it is.
+ # split: ["224", "Boston", "Street", "[", "map", "]"]
+ split = @entry.css('h4')[0].text.strip.split(/\s+/)
+ # Remove [[", "map", "]"]
+ split.slice!(-3, 3)
+ @name = split.join(' ')
+ end
+ @name
+ end
+
+ # Return a 0-2 element array containing the meals offered by this
+ # Restaurant: "lunch" and/or "dinner".
+ def meals
+ unless @meals
+ @meals = []
+ lunch = ! @entry.css('a.lunchMenuButton').empty?
+ dinner = ! @entry.css('a.dinnerMenuButton').empty?
+ @meals << 'lunch' if lunch
+ @meals << 'dinner' if dinner
+ end
+ @meals
+ end
+
+ # Return the neighborhood this Restaurant is in (e.g. "back-bay").
+ def neighborhood
+ unless @neighborhood
+ link = @entry.css('a[@href*="neighborhood"]').first
+ @neighborhood = link.attributes['href'].value.sub('/?neighborhood=', '')
+ end
+ @neighborhood
+ end
+
+
+ # Return this Restaurant's phone number, or "<no phone given>" is none is
+ # provided.
+ def phone
+ unless @phone
+ # UGLY, but it's not wrapped in a tag so there it is.
+ phone = @entry.css('.restaurantInfoBasic > p').children[6].to_s
+ if phone == '<br>'
+ @phone = '<no phone given>'
+ else
+ @phone = phone
+ end
+ end
+ @phone
+ end
+
+ # +meal+ should be either "lunch" or "dinner"
+ def menu_link_for(meal)
+ if meal == 'lunch'
+ @lunch_menu_link ||= "http://www.restaurantweekboston.com/fetch/#{short_name}/lunch/"
+ elsif meal == 'dinner'
+ @dinner_menu_link ||= "http://www.restaurantweekboston.com/fetch/#{short_name}/dinner/"
+ end
+ end
+
+
+ # Return the URL to the map of this Restaurant at the RWB web site.
+ def map_link
+ @map_link ||= "http://www.restaurantweekboston.com/map/#{neighborhood}/#{short_name}/#topOfMap"
+ end
+ end
+end
@@ -0,0 +1,55 @@
+require 'optparse'
+require 'restaurant_week_boston/scraper'
+require 'restaurant_week_boston/marker'
+
+module RestaurantWeekBoston
+ # Used in the executable script, +restaurant_week_boston+.
+ class Runner
+ # Pass in ARGV.
+ def initialize(argv)
+ options = {}
+ optparse = OptionParser.new do |opts|
+ opts.banner = "Usage: #{$0} [options] restaurant1, restaurant2, ..."
+
+ options[:meal] = :any
+ opts.on('-m', '--meal MEAL', 'Only get restaurants that offer this meal (lunch/dinner/both/any)') do |m|
+ options[:meal] = m
+ end
+
+ opts.on('-l', '--lunch RESTO1,RESTO2', 'Mark RESTAURANTS as good for lunch') do |lunches|
+ options[:lunch] = lunches.split(',')
+ end
+
+ opts.on('-d', '--dinner RESTO1,RESTO2', 'Mark RESTAURANTS as good for dinner') do |dinners|
+ options[:dinner] = dinners.split(',')
+ end
+
+ opts.on('-a', '--any RESTO1,RESTO2', 'Mark RESTAURANTS as good for any meal') do |any|
+ options[:any] = any.split(',')
+ end
+
+ options[:neighborhood] = :all # NOT any!
+ opts.on('-n', '--neighborhood HOOD',
+ 'Only get restaurants in this neighborhood (dorchester, back-bay, etc)') do |n|
+ n = :all if n == 'any' # :any makes it choke
+ options[:neighborhood] = n
+ end
+
+ opts.on('-h', '--help', 'Display this help') do
+ puts opts
+ return
+ end
+ end
+ optparse.parse!(argv)
+ @options = options
+ end
+
+ def run
+ scraper = Scraper.new(:meal => @options[:meal],
+ :neighborhood => @options[:neighborhood])
+ marker = Marker.new(scraper, @options)
+
+ puts marker.all
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit ea80a60

Please sign in to comment.