Ruby client for the OpenTable REST API. Supports restaurant search, availability lookup, reservations, and booking management.
- Fetch restaurant details
- Search available reservation times
- Lock time slots before booking
- Create and cancel reservations
- Check reservation status
- Requires Ruby 3.0+
- No
method_missing- all methods explicitly defined - Error classes for different failure types
Add this line to your application's Gemfile:
gem 'hungrytable'And then execute:
$ bundle installOr install it yourself as:
$ gem install hungrytableSet the following environment variables (recommended for production):
export OT_PARTNER_ID=<YOUR OPENTABLE PARTNER ID>
export OT_OAUTH_KEY=<YOUR OPENTABLE OAUTH KEY>
export OT_OAUTH_SECRET=<YOUR OPENTABLE OAUTH SECRET KEY>Alternatively, configure programmatically in your code:
Hungrytable.configure do |config|
config.partner_id = 'your_partner_id'
config.oauth_key = 'your_oauth_key'
config.oauth_secret = 'your_oauth_secret'
endrestaurant = Hungrytable::Restaurant.new(82591)
if restaurant.valid?
puts restaurant.restaurant_name
puts restaurant.address
puts restaurant.phone
puts restaurant.primary_food_type
puts restaurant.price_range
else
puts "Error: #{restaurant.error_message}"
endAvailable restaurant attributes:
restaurant_name,restaurant_IDaddress,city,state,postal_codephone,urllatitude,longitudeneighborhood_name,metro_nameprimary_food_type,price_rangerestaurant_descriptionparking,parking_detailsimage_link
restaurant = Hungrytable::Restaurant.new(82591)
# Search for a table for 4 people, 5 days from now at 7pm
search_time = 5.days.from_now.change(hour: 19, min: 0)
search = Hungrytable::RestaurantSearch.new(
restaurant,
date_time: search_time,
party_size: 4
)
if search.valid?
puts "Exact time: #{search.exact_time}" if search.exact_time
puts "Early time: #{search.early_time}" if search.early_time
puts "Later time: #{search.later_time}" if search.later_time
puts "Best time: #{search.ideal_time}"
else
puts "No availability: #{search.error_message}"
endslotlock = Hungrytable::RestaurantSlotlock.new(search)
if slotlock.successful?
puts "Slot locked! ID: #{slotlock.slotlock_id}"
else
puts "Failed to lock slot: #{slotlock.errors}"
endreservation = Hungrytable::ReservationMake.new(
slotlock,
email_address: 'john.doe@example.com',
firstname: 'John',
lastname: 'Doe',
phone: '5551234567',
specialinstructions: 'Window seat please'
)
if reservation.successful?
puts "Reservation confirmed! Number: #{reservation.confirmation_number}"
else
puts "Reservation failed: #{reservation.error_message}"
endstatus = Hungrytable::ReservationStatus.new(
restaurant_id: 82591,
confirmation_number: 'ABC123XYZ'
)
if status.successful?
puts "Status: #{status.status}"
details = status.reservation_details
puts "Restaurant: #{details[:restaurant_name]}"
puts "Date/Time: #{details[:date_time]}"
puts "Party Size: #{details[:party_size]}"
endcancel = Hungrytable::ReservationCancel.new(
email_address: 'john.doe@example.com',
confirmation_number: 'ABC123XYZ',
restaurant_id: 82591
)
if cancel.successful?
puts "Reservation cancelled successfully"
else
puts "Cancellation failed: #{cancel.error_message}"
endrequire 'hungrytable'
# Configure
Hungrytable.configure do |config|
config.partner_id = ENV['OT_PARTNER_ID']
config.oauth_key = ENV['OT_OAUTH_KEY']
config.oauth_secret = ENV['OT_OAUTH_SECRET']
end
# 1. Get restaurant details
restaurant = Hungrytable::Restaurant.new(82591)
raise "Restaurant not found" unless restaurant.valid?
puts "Booking at: #{restaurant.restaurant_name}"
# 2. Search for availability
search_time = 3.days.from_now.change(hour: 19, min: 0)
search = Hungrytable::RestaurantSearch.new(
restaurant,
date_time: search_time,
party_size: 2
)
raise "No availability" unless search.valid?
puts "Found time slot: #{search.ideal_time}"
# 3. Lock the slot
slotlock = Hungrytable::RestaurantSlotlock.new(search)
raise "Could not lock slot: #{slotlock.errors}" unless slotlock.successful?
# 4. Make the reservation
reservation = Hungrytable::ReservationMake.new(
slotlock,
email_address: 'diner@example.com',
firstname: 'Jane',
lastname: 'Smith',
phone: '5551234567'
)
if reservation.successful?
puts "Success! Confirmation: #{reservation.confirmation_number}"
else
puts "Failed: #{reservation.error_message}"
endAvailable error classes:
begin
restaurant = Hungrytable::Restaurant.new(invalid_id)
rescue Hungrytable::ConfigurationError => e
puts "Configuration error: #{e.message}"
rescue Hungrytable::HTTPError => e
puts "HTTP error: #{e.message}"
rescue Hungrytable::APIError => e
puts "API error #{e.error_code}: #{e.error_message}"
rescue Hungrytable::MissingRequiredFieldError => e
puts "Missing required field: #{e.message}"
endError hierarchy:
Hungrytable::Error(base class)ConfigurationError- Missing or invalid configurationHTTPError- HTTP-level errorsNotFoundError- 404 errorsUnauthorizedError- 401 authentication errorsServerError- 5xx server errors
APIError- OpenTable API errorsValidationError- Input validation errorsMissingRequiredFieldError- Missing required parameters
After checking out the repo, run bundle install to install dependencies. Then, run rake test to run the tests.
bundle exec rake testbundle exec rubocopbundle exec rubocop -A- Ruby >= 3.0.0
- OpenTable Partner API credentials
Runtime:
activesupport(>= 6.0)http(~> 5.0)oauth(~> 1.1)
Development:
minitest(~> 5.20) - Testing frameworkwebmock(~> 3.20) - HTTP mocking for testsrubocop(~> 1.60) - Code quality and stylemocha(~> 2.1) - Mocking and stubbing
- Fork it (https://github.com/dchapman1988/hungrytable/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Please ensure:
- All tests pass (
bundle exec rake test) - RuboCop is clean (
bundle exec rubocop) - New features have tests
- Code follows existing style
The gem is available as open source under the terms of the MIT License.
Active:
- David Chapman (@dchapman1988)
Inactive:
- Nicholas Fine (@yrgoldteeth)
- Ryan T. Hosford (@rthbound)
See RELEASE_NOTES.md for version history.