hungrytable-pjc 0.0.7

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/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.sw*
6
+ .open_table
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2@hungrytable --create
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in hungrytable.gemspec
4
+ gemspec
5
+
data/Guardfile ADDED
@@ -0,0 +1,16 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ notification :libnotify
5
+
6
+ guard 'minitest' do
7
+ # with Minitest::Unit
8
+ watch(%r|^test/(.*)_test\.rb|)
9
+ watch(%r|^lib/(.*)/([^/]+)\.rb|) { |m| "test/unit/#{m[1]}/#{m[2]}_test.rb" }
10
+ watch(%r|^test/test_helper\.rb|) { "test" }
11
+
12
+ # with Minitest::Spec
13
+ # watch(%r|^spec/(.*)_spec\.rb|)
14
+ # watch(%r|^lib/(.*)\.rb|) { |m| "spec/#{m[1]}_spec.rb" }
15
+ # watch(%r|^spec/spec_helper\.rb|) { "spec" }
16
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 David Chapmap
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Hungrytable
2
+
3
+ The purpose of this gem is to interact with the [OpenTable](http://www.opentable.com) REST API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'hungrytable'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install hungrytable
18
+
19
+ ## Usage
20
+
21
+ You need to set some environment variable for this gem to work properly:
22
+
23
+ Observe:
24
+
25
+ # In ~/.bashrc
26
+ export OT_PARTNER_ID=<YOUR OPENTABLE PARTNER ID>
27
+ export OT_PARTNER_AUTH=<YOUR OPENTABLE PARTNER AUTH> # For XML feed only...
28
+ export OT_OAUTH_KEY=<YOUR OPENTABLE OAUTH KEY>
29
+ export OT_OAUTH_SECRET=<YOUR OPENTABLE OAUTH SECRET KEY>
30
+
31
+ ## Example Run
32
+
33
+ $ restaurant = Hungrytable::Restaurant.new(82591)
34
+
35
+ > #<Hungrytable::Restaurant:0x0000000032e4098 ... >
36
+
37
+ $ search = Hungrytable::RestaurantSearch.new(restaurant, {date_time: 5.days.from_now, party_size: 3})
38
+
39
+ > #<Hungrytable::RestaurantSearch:0x00000003143388 ... >
40
+
41
+ $ slotlock = Hungrytable::RestaurantSlotlock.new(search)
42
+
43
+ > #<Hungrytable::RestaurantSlotlock:0x00000002973a68 ... >
44
+
45
+ $ reservation = Hungrytable::ReservationMake.new(slotlock, {email_address: 'foo@bar.com', firstname: 'Mike', lastname: 'Jones', phone: '2813308004'})
46
+
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
data/RELEASE_NOTES.md ADDED
@@ -0,0 +1,11 @@
1
+ # Hungrytable - Ruby API Client for the OpenTable REST API
2
+
3
+ ## v0.0.1 (05/11/2012)
4
+
5
+ * Get relevant details about an individual restaurant.
6
+ * Search for availability at an individual restaurant.
7
+
8
+ ### Known Issues
9
+
10
+ OpenTable's API currently returns search results as an unsupported transaction,
11
+ but it is clearly marked as a thing in their API document.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'lib'
6
+ t.libs << 'test'
7
+ t.pattern = 'test/**/*_test.rb'
8
+ end
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "hungrytable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "hungrytable-pjc"
7
+ s.version = Hungrytable::VERSION
8
+ s.authors = ["David Chapman", "Nicholas Fine"]
9
+ s.email = ["david@isotope11.com", 'nicholas.fine@gmail.com']
10
+ s.homepage = "http://www.isotope11.com/"
11
+ s.summary = %q{Gem to interact with OpenTable's REST API}
12
+ s.description = %q{Gem to interact with OpenTable's REST API}
13
+
14
+ s.rubyforge_project = "hungrytable-pjc"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+
25
+ s.add_runtime_dependency 'json', '~> 1.7.1'
26
+ s.add_runtime_dependency 'activesupport'
27
+ s.add_runtime_dependency 'i18n'
28
+ s.add_runtime_dependency 'curb'
29
+
30
+ s.add_development_dependency 'rake'
31
+ s.add_development_dependency 'minitest'
32
+ s.add_development_dependency 'minitest-reporters'
33
+ s.add_development_dependency 'mocha'
34
+ s.add_development_dependency 'pry'
35
+ s.add_development_dependency 'webmock'
36
+ end
37
+
@@ -0,0 +1,26 @@
1
+ module Hungrytable
2
+ module Config
3
+ extend self
4
+
5
+ def partner_id
6
+ ENV['OT_PARTNER_ID'] || config_error('OT_PARTNER_ID')
7
+ end
8
+
9
+ def oauth_key
10
+ ENV['OT_OAUTH_KEY'] || config_error('OT_OAUTH_KEY')
11
+ end
12
+
13
+ def oauth_secret
14
+ ENV['OT_OAUTH_SECRET'] || config_error('OT_OAUTH_SECRET')
15
+ end
16
+
17
+ def base_url
18
+ 'https://secure.opentable.com/api/otapi_v2.ashx'
19
+ end
20
+
21
+ private
22
+ def config_error var
23
+ raise HungrytableError, "ENV variable #{var} must be set."
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ module Hungrytable
2
+ class GetRequest < Request
3
+ private
4
+ def auth_header
5
+ Hungrytable::RequestHeader.new(:get, @uri, {}, {})
6
+ end
7
+
8
+ def make_request
9
+ Curl::Easy.perform(@uri) do |curl|
10
+ curl.headers['Authorization'] = auth_header
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Hungrytable
2
+ class PostRequest < Request
3
+ private
4
+ def auth_header
5
+ Hungrytable::RequestHeader.new(:post, @uri, {}, {})
6
+ end
7
+
8
+ def make_request
9
+ Curl::Easy.http_post(@uri, @params.to_query) do |curl|
10
+ curl.headers['Authorization'] = auth_header
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module Hungrytable
2
+ class Request
3
+ def initialize uri, params={}
4
+ @uri = Hungrytable::Config.base_url << uri
5
+ @params = params
6
+ end
7
+
8
+ def parsed_response
9
+ JSON.parse(response)
10
+ end
11
+
12
+ private
13
+ def response
14
+ @response ||= make_request.body_str
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module Hungrytable
2
+ module RequestExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ private
6
+ def request
7
+ @request ||= @requester.new(request_uri, params)
8
+ end
9
+
10
+ def ensure_required_opts
11
+ required_opts.each do |key|
12
+ raise ArgumentError, "options must include a value for #{key}" unless opts.has_key?(key)
13
+ end
14
+ end
15
+
16
+ # Will be overwritten in objects that send post requests
17
+ def params
18
+ {}
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,109 @@
1
+ # Modified from simple_oauth (https://github.com/laserlemon/simple_oauth)
2
+ module Hungrytable
3
+ class RequestHeader
4
+
5
+ ATTRIBUTE_KEYS = %w(consumer_key nonce signature_method timestamp token version).map(&:to_sym)
6
+
7
+ def self.default_options
8
+ {
9
+ :nonce => OpenSSL::Random.random_bytes(16).unpack('H*')[0],
10
+ :signature_method => 'HMAC-SHA1',
11
+ :timestamp => Time.now.to_i.to_s,
12
+ :version => '1.0',
13
+ :consumer_key => Hungrytable::Config.oauth_key,
14
+ :consumer_secret => Hungrytable::Config.oauth_secret,
15
+ :token => ''
16
+ }
17
+ end
18
+
19
+ def self.encode(value)
20
+ URI.encode(value.to_s, /[^a-z0-9\-\.\_\~]/i)
21
+ end
22
+
23
+ def self.decode(value)
24
+ URI.decode(value.to_s)
25
+ end
26
+
27
+ attr_reader :method, :params, :options
28
+
29
+ def initialize(method, url, params, oauth = {})
30
+ @method = method.to_s.upcase
31
+ @uri = URI.parse(url.to_s)
32
+ @uri.scheme = @uri.scheme.downcase
33
+ @uri.normalize!
34
+ @uri.fragment = nil
35
+ @params = params
36
+ @options = self.class.default_options.merge(oauth)
37
+ end
38
+
39
+ def url
40
+ uri = @uri.dup
41
+ uri.query = nil
42
+ uri.to_s
43
+ end
44
+
45
+ def to_s
46
+ %Q(OAuth realm="http://www.opentable.com/", #{normalized_attributes})
47
+ end
48
+
49
+ def valid?(secrets = {})
50
+ original_options = options.dup
51
+ options.merge!(secrets)
52
+ valid = options[:signature] == signature
53
+ options.replace(original_options)
54
+ valid
55
+ end
56
+
57
+ def signed_attributes
58
+ attributes.merge(:oauth_signature => signature)
59
+ end
60
+
61
+ private
62
+
63
+ def normalized_attributes
64
+ signed_attributes.sort_by{|k,v| k.to_s }.map{|k,v| %(#{k}="#{self.class.encode(v)}") }.join(', ')
65
+ end
66
+
67
+ def attributes
68
+ ATTRIBUTE_KEYS.inject({}){|a,k| options.key?(k) ? a.merge(:"oauth_#{k}" => options[k]) : a }
69
+ end
70
+
71
+ def signature
72
+ send(options[:signature_method].downcase.tr('-', '_') + '_signature')
73
+ end
74
+
75
+ def hmac_sha1_signature
76
+ Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, secret, signature_base)).chomp.gsub(/\n/, '')
77
+ end
78
+
79
+ def secret
80
+ options.values_at(:consumer_secret, :token_secret).map{|v| self.class.encode(v) }.join('&')
81
+ end
82
+ alias_method :plaintext_signature, :secret
83
+
84
+ def signature_base
85
+ [method, url, normalized_params].map{|v| self.class.encode(v) }.join('&')
86
+ end
87
+
88
+ def normalized_params
89
+ signature_params.map{|p| p.map{|v| self.class.encode(v) } }.sort.map{|p| p.join('=') }.join('&')
90
+ end
91
+
92
+ def signature_params
93
+ attributes.to_a + params.to_a + url_params
94
+ end
95
+
96
+ def url_params
97
+ CGI.parse(@uri.query || '').inject([]){|p,(k,vs)| p + vs.sort.map{|v| [k, v] } }
98
+ end
99
+
100
+ def rsa_sha1_signature
101
+ Base64.encode64(private_key.sign(OpenSSL::Digest::SHA1.new, signature_base)).chomp.gsub(/\n/, '')
102
+ end
103
+
104
+ def private_key
105
+ OpenSSL::PKey::RSA.new(options[:consumer_secret])
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,30 @@
1
+ module Hungrytable
2
+ class ReservationCancel
3
+ include RequestExtensions
4
+
5
+ attr_reader :opts
6
+
7
+ def initialize opts={}
8
+ @opts = opts
9
+ ensure_required_opts
10
+ @requester = opts[:requester] || GetRequest
11
+ end
12
+
13
+ def successful?
14
+ details["ns:ErrorID"] == "0"
15
+ end
16
+
17
+ private
18
+ def request_uri
19
+ "/reservation/?pid=#{Config.partner_id}&rid=#{opts[:restaurant_id]}&conf=#{opts[:confirmation_number]}&email=#{CGI.escape(opts[:email_address])}"
20
+ end
21
+
22
+ def details
23
+ request.parsed_response["Results"]
24
+ end
25
+
26
+ def required_opts
27
+ %w(email_address confirmation_number restaurant_id).map(&:to_sym)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ module Hungrytable
2
+ class ReservationMake
3
+ include RequestExtensions
4
+
5
+ attr_reader :restaurant_slotlock, :opts
6
+
7
+ def initialize restaurant_slotlock, opts={}
8
+ @opts = opts
9
+ ensure_required_opts
10
+ @requester = opts[:requester] || PostRequest
11
+ @restaurant_slotlock = restaurant_slotlock
12
+ end
13
+
14
+ def successful?
15
+ details["ns:ErrorID"] == "0"
16
+ end
17
+
18
+ def confirmation_number
19
+ return nil unless successful?
20
+ details["ns:ConfirmationNumber"]
21
+ end
22
+
23
+ private
24
+ def required_opts
25
+ %w(email_address firstname lastname phone).map(&:to_sym)
26
+ end
27
+
28
+ def default_options
29
+ {
30
+ 'OTannouncementOption' => '0',
31
+ 'RestaurantEmailOption' => '0',
32
+ 'firsttimediner' => '0',
33
+ 'specialinstructions' => 'Have a great time',
34
+ 'slotlockid' => restaurant_slotlock.slotlock_id
35
+ }.merge(restaurant_slotlock.params)
36
+ end
37
+
38
+ def params
39
+ default_options.merge(opts)
40
+ end
41
+
42
+ def request_uri
43
+ "/reservation/?pid=#{Config.partner_id}&st=0"
44
+ end
45
+
46
+ def details
47
+ request.parsed_response["MakeResults"]
48
+ end
49
+
50
+ end
51
+ end
File without changes
@@ -0,0 +1,58 @@
1
+ module Hungrytable
2
+ class Restaurant
3
+ include RequestExtensions
4
+
5
+ def initialize restaurant_id, opts={}
6
+ @requester = opts[:requester] || GetRequest
7
+ @restaurant_id = restaurant_id
8
+ end
9
+
10
+ def id
11
+ @restaurant_id
12
+ end
13
+
14
+ def valid?
15
+ error_ID == "0"
16
+ end
17
+
18
+ def method_missing meth, *args, &blk
19
+ if %w(
20
+ address
21
+ city
22
+ error_ID
23
+ error_message
24
+ image_link
25
+ latitude
26
+ longitude
27
+ metro_name
28
+ neighborhood_name
29
+ parking
30
+ parking_details
31
+ phone
32
+ postal_code
33
+ price_range
34
+ primary_food_type
35
+ restaurant_description
36
+ restaurant_ID
37
+ restaurant_name
38
+ state
39
+ url
40
+ ).map(&:to_sym).include?(meth)
41
+ return details["ns:#{meth.to_s.camelize.gsub("Id","ID")}"]
42
+ end
43
+ super
44
+ end
45
+
46
+ private
47
+
48
+ def request_uri
49
+ "/restaurant/?pid=#{Config.partner_id}&rid=#{id}"
50
+ end
51
+
52
+ # @return [Hash]
53
+ def details
54
+ request.parsed_response["RestaurantDetailsResults"]
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,78 @@
1
+ module Hungrytable
2
+ class RestaurantSearch
3
+ include RequestExtensions
4
+
5
+ attr_reader :restaurant, :opts
6
+
7
+ def initialize restaurant, opts={}
8
+ @opts = opts
9
+ ensure_required_opts
10
+ @requester = opts[:requester] || GetRequest
11
+ @restaurant = restaurant
12
+ end
13
+
14
+ def valid?
15
+ error_ID == "0"
16
+ end
17
+
18
+ def method_missing meth, *args, &blk
19
+ if %w(
20
+ cuisine_type
21
+ early_security_ID
22
+ early_time
23
+ error_ID
24
+ error_message
25
+ exact_security_ID
26
+ exact_time
27
+ later_security_ID
28
+ later_time
29
+ latitude
30
+ longitude
31
+ neighborhood_name
32
+ restaurant_name
33
+ results_key
34
+ no_times_message
35
+ ).map(&:to_sym).include?(meth)
36
+ return details["ns:#{meth.to_s.camelize.gsub("Id","ID")}"]
37
+ end
38
+ super
39
+ end
40
+
41
+ def ideal_security_id
42
+ return exact_security_ID unless exact_security_ID.nil?
43
+ return early_security_ID unless early_security_ID.nil?
44
+ return later_security_ID unless later_security_ID.nil?
45
+ nil
46
+ end
47
+
48
+ def ideal_time
49
+ return exact_time unless exact_time.nil?
50
+ return early_time unless early_time.nil?
51
+ return later_time unless later_time.nil?
52
+ nil
53
+ end
54
+
55
+ def party_size
56
+ opts[:party_size]
57
+ end
58
+
59
+ private
60
+ def required_opts
61
+ %w(date_time party_size).map(&:to_sym)
62
+ end
63
+
64
+ def encoded_date_time
65
+ #URI.encode(opts[:date_time].strftime("%m/%d/%Y %I:%M %p"), /[^a-z0-9\-\.\_\~]/i)
66
+ URI.encode(opts[:date_time].strftime("%Y-%m-%dT%H:%M"), /[^a-z0-9\-\.\_\~]/i)
67
+ end
68
+
69
+ def request_uri
70
+ "/table/?pid=#{Config.partner_id}&rid=#{restaurant.id}&dt=#{encoded_date_time}&ps=#{party_size}"
71
+ end
72
+
73
+ def details
74
+ request.parsed_response["SearchResults"]
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,49 @@
1
+ module Hungrytable
2
+ class RestaurantSlotlock
3
+ include RequestExtensions
4
+
5
+ attr_reader :restaurant_search
6
+
7
+ def initialize restaurant_search, requester=PostRequest
8
+ @restaurant_search = restaurant_search
9
+ @requester = requester
10
+ end
11
+
12
+ def successful?
13
+ details["ns:ErrorID"] == "0"
14
+ end
15
+
16
+ def errors
17
+ details["ns:ErrorMessage"]
18
+ end
19
+
20
+ def slotlock_id
21
+ return nil unless successful?
22
+ details["ns:SlotLockID"]
23
+ end
24
+
25
+ def params
26
+ {
27
+ 'RID' => restaurant.id,
28
+ 'datetime' => restaurant_search.ideal_time,
29
+ 'partysize' => restaurant_search.party_size,
30
+ 'timesecurityID' => restaurant_search.ideal_security_id,
31
+ 'resultskey' => restaurant_search.results_key
32
+ }
33
+ end
34
+
35
+ private
36
+ def request_uri
37
+ "/slotlock/?pid=#{Config.partner_id}&st=0"
38
+ end
39
+
40
+ def restaurant
41
+ restaurant_search.restaurant
42
+ end
43
+
44
+ def details
45
+ request.parsed_response["SlotLockResults"]
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module Hungrytable
2
+ VERSION = "0.0.7"
3
+ end
@@ -0,0 +1,73 @@
1
+ require 'uri'
2
+ require 'json'
3
+ require 'openssl'
4
+ require 'base64'
5
+ require 'cgi'
6
+ require 'curb'
7
+ require 'active_support/core_ext'
8
+ require 'hungrytable/version'
9
+ require 'hungrytable/config'
10
+ require 'hungrytable/request_extensions'
11
+ require 'hungrytable/request_header'
12
+ require 'hungrytable/request'
13
+ require 'hungrytable/get_request'
14
+ require 'hungrytable/post_request'
15
+ require 'hungrytable/restaurant'
16
+ require 'hungrytable/restaurant_search'
17
+ require 'hungrytable/restaurant_slotlock'
18
+ require 'hungrytable/reservation_make'
19
+ require 'hungrytable/reservation_status'
20
+ require 'hungrytable/reservation_cancel'
21
+
22
+ module Hungrytable
23
+ class HungrytableError < StandardError;end
24
+
25
+ def error_codes
26
+ {
27
+ 152 => { message: 'The email address is not valid. Please try again.', name: 'VALIDEMAIL' },
28
+ 160 => { message: "We're sorry but we were unable to complete you request. Our technical team has been notified of the problem and will resolve it shortly. Thank you.", name: "GENERALERROR" },
29
+ 197 => { message: "You must select at least one restaurant to search.", name: "PROVIDERESTAURANT" },
30
+ 202 => { message: "The time you selected has already passed. You may wish to check that your computer clock is correct.", name: "PASSEDTIME" },
31
+ 274 => { message: "Your phone number must be numeric.", name: "NUMERICPHONE" },
32
+ 279 => { message: "Your IP address is not listed as an OpenTable Partner IP.", name: "INVALIDIP" },
33
+ 280 => { message: "Key authentication failure", name: "AUTHENTICATEFAIL" },
34
+ 281 => { message: "The phone length for the number provided was not a valid length for the country code.", name: "INVALIDPHONELENGTH" },
35
+ 282 => { message: "We are currently unable to connect to the restaurant to complete this action. Please try again later.", name: "ERBERROR" },
36
+ 283 => { message: "The time you have chosen for your reservation is no longer available.", name: "RESERVATIONNOTAVAIL" },
37
+ 285 => { message: "Credit Card transactions are not allowed via OT Web Services.", name: "CCNOTALLOWED" },
38
+ 286 => { message: "Large parties are not allowed via OT Web Services.", name: "LARGEPARTYNOTALLOWED" },
39
+ 287 => { message: "The restaurant is currently offline.", name: "RESTOFFLINE" },
40
+ 288 => { message: "The restaurant is currently unreachable.", name: "RESTUNREACHABLE" },
41
+ 289 => { message: "Cancel transaction failed.", name: "CANCELFAIL" },
42
+ 294 => { message: "You have not provided enough information to perform the search.", name: "INSUFFICIENTINFORMATION" },
43
+ 295 => { message: "No restaurants were found in your search. Please try again.", name: "NORESTAURANTSRETURNED" },
44
+ 296 => { message: "Your search produced no times.", name: "NOTIMESMESSAGE" },
45
+ 298 => { message: "The confirmation number is invalid.", name: "VALIDCONFIRMNUMBER" },
46
+ 299 => { message: "The reservation status is not available on reservations older than 30 days.", name: "VALIDSTATUSDATE" },
47
+ 301 => { message: "The user for the reservation request already has a reservation within 2 hours of the requested time.", name: "VALIDDUPRESERVATION" },
48
+ 302 => { message: "No Reservation Activity found.", name: "NORESOHISTORYAVAILABLE" },
49
+ 303 => { message: "Invalid User Email. Please re-enter your UserEmail.", name: "INVALIDUSEREMAIL" },
50
+ 304 => { message: "Invalid User Password. Please re-enter your password.", name: "INVALIDUSERPASSWORD" },
51
+ 305 => { message: "Unable to authenticate user to login. Please try again.", name: "USERAUTHENTICATIONFAIL" },
52
+ 306 => { message: "The entered area to search is too large please limit your search to 20 miles.", name: "SEARCHAREATOOLARGE" },
53
+ 307 => { message: "The email address(es) you have provided are not in the correct format. Please try again.", name: "INVALIDEMAILADDRESSES" },
54
+ 308 => { message: "Your reservation has already been cancelled, please hit the Reload button on your profile to refresh your page", name: "ALREADYCANCELLED" },
55
+ 309 => { message: "You must use SSL for this request.", name: "SSLCONNECTIONREQUIRED" },
56
+ 310 => { message: "The points requested for this time slot are not available.", name: "INVALIDPOINTREQUEST" },
57
+ 311 => { message: "The user account is deactivated.", name: "ACCTDEACTIVATED" },
58
+ 313 => { message: "The requested Restaurant was not found.", name: "INVALIDRESTAURANTID" },
59
+ 314 => { message: "We already have an account registered to <email address>", name: "MATCHACCOUNT" },
60
+ 315 => { message: "A default metro must be provided for a use", name: "PROVIDEMETRO" },
61
+ 316 => { message: "The password must be a minimum of 6 characters.", name: "MALFORMEDPASSWORD" },
62
+ 317 => { message: "The phone length for the number provided was not a valid length for the country code.", name: "INVALIDPHONE" },
63
+ 318 => { message: "Please enter a valid country id for the phone number.", name: "PROVIDEPHONECOUNTRY" },
64
+ 319 => { message: "Please enter a valid country id for the mobile phone number.", name: "PROVIDEMOBILEPHONECOUNTRY" },
65
+ 321 => { message: "We're sorry, but we could not complete your reservation request because an account cannot have more than two confirmed reservations into the same restaurant for the same day.", name: "VALIDTOOMANYSAMEREST" },
66
+ 322 => { message: "The time slot for this reservation is no longer available.", name: "SLOTLOCKUNAVAILABLE" },
67
+ 323 => { message: "The service you are trying to access is currently unavailable, please try again later.", name: "SERVICEUNAVAILABLE" },
68
+ 324 => { message: "The service experienced a timeout. Please try again later.", name: "SERVICETIMEOUT" },
69
+ 325 => { message: "Invalid number of lookback days, the most you can look back is 8 days.", name: "INVALIDLOOKBACKDAYS" },
70
+ 326 => { message: "This service is currently disabled. Please try again later.", name: "CURRENTLYDISABLED" }
71
+ }
72
+ end
73
+ end
@@ -0,0 +1,6 @@
1
+ HTTP/1.1 200 OK
2
+ Status: 200
3
+ Vary: Accept-Encoding
4
+ Content-Type: application/json
5
+
6
+ { "RestaurantDetailsResults": {"ns:Address": null, "ns:City": "799 Market Street", "ns:ErrorID": "0", "ns:ErrorMessage": null, "ns:ImageLink": null, "ns:Latitude": null, "ns:Longitude": null, "ns:MenuURL": null, "ns:MetroID": "1", "ns:MetroName": "Demoland", "ns:NeighborhoodName": "Devland", "ns:Parking": "No", "ns:ParkingDetails": null, "ns:Phone": "4157897485", "ns:PostalCode": "94103", "ns:PriceRange": "$30 and under", "ns:PrimaryFoodType": "American", "ns:ReserveUrl": "1759B23C", "ns:RestaurantDescription": "Coming Soon!", "ns:RestaurantID": "82591", "ns:RestaurantName": "OTConnect-Partner-Rest1", "ns:State": "CA", "ns:Url": null, "xmlns:ns": "com.opentable.webservices.v2" }}
@@ -0,0 +1,7 @@
1
+ HTTP/1.1 200 OK
2
+ Status: 200
3
+ Vary: Accept-Encoding
4
+ Content-Type: application/json
5
+
6
+ {"SearchResults": {"ns:CuisineType": "American", "ns:EarlyPoints": "100", "ns:EarlySecurityID": "1113043844", "ns:EarlyTime": "5/16/2012 1:45:00 PM", "ns:ErrorID": "0", "ns:ErrorMessage": null, "ns:ExactPoints": "100", "ns:ExactSecurityID": "1113043836", "ns:ExactTime": "5/16/2012 2:00:00 PM", "ns:ImageExists": "0", "ns:Latitude": null, "ns:Longitude": null, "ns:NeighborhoodName": "Devland", "ns:Price": "$$", "ns:RestaurantID": "82591", "ns:RestaurantName": "OTConnect-Partner-Rest1", "ns:ResultsKey": "z0OlOTkH2NbY%2bq7bjf7Lxw%3d%3d", "xmlns:ns": "com.opentable.webservices.v2" }}
7
+
@@ -0,0 +1,18 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/reporters'
3
+
4
+ require 'ostruct'
5
+ require 'pry'
6
+ require 'webmock'
7
+ include WebMock::API
8
+
9
+
10
+ # Load support files
11
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
12
+
13
+ # Set up minitest
14
+ MiniTest::Unit.runner = MiniTest::SuiteRunner.new
15
+ MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new
16
+
17
+ require 'mocha'
18
+ require File.expand_path("../../lib/hungrytable.rb", __FILE__)
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ describe Hungrytable::Config do
4
+ describe 'when the correct ENV vars are not set' do
5
+ before do
6
+ ENV['OT_PARTNER_ID'] = nil
7
+ ENV['OT_OAUTH_KEY'] = nil
8
+ ENV['OT_OAUTH_SECRET'] = nil
9
+ end
10
+
11
+ it 'raises a HungrytableError' do
12
+ [:partner_id, :oauth_key, :oauth_secret].each do |meth|
13
+ -> {Hungrytable::Config.send meth}.must_raise Hungrytable::HungrytableError
14
+ end
15
+ end
16
+ end
17
+
18
+ describe 'when the ENV vars are set' do
19
+ before do
20
+ ENV['OT_PARTNER_ID'] = 'foo'
21
+ ENV['OT_OAUTH_KEY'] = 'bar'
22
+ ENV['OT_OAUTH_SECRET'] = 'baz'
23
+ end
24
+
25
+ describe '.partner_id' do
26
+ it 'returns the value for OT_PARTNER_ID' do
27
+ Hungrytable::Config.partner_id.must_equal 'foo'
28
+ end
29
+ end
30
+
31
+ describe '.oauth_key' do
32
+ it 'returns the value for OT_OAUTH_KEY' do
33
+ Hungrytable::Config.oauth_key.must_equal 'bar'
34
+ end
35
+ end
36
+
37
+ describe '.oauth_secret' do
38
+ it 'returns the value for OT_OAUTH_SECRET' do
39
+ Hungrytable::Config.oauth_secret.must_equal 'baz'
40
+ end
41
+ end
42
+ end
43
+ end
File without changes
@@ -0,0 +1,28 @@
1
+ =begin
2
+ require 'test_helper'
3
+
4
+ describe Hungrytable::User do
5
+ subject { Hungrytable::User }
6
+
7
+ it 'should be a class' do
8
+ subject.class.must_be_kind_of Class
9
+ end
10
+
11
+ %w{ login }.each do |meth|
12
+ it "should respond to #{meth}" do
13
+ assert subject.new.respond_to?(meth.to_sym), true
14
+ end
15
+ end
16
+
17
+ describe 'users' do
18
+ before do
19
+ stub_request(:get, /.*secure.opentable.com\/api\/otapi_v2.ashx\/user.*/).to_return(File.new('./test/user_login_result.json'), :status => 200)
20
+ @user = subject.new
21
+ end
22
+ it 'should be able to login' do
23
+ response = @user.login({ email: 'bob@webservices.com', password: 'password' })
24
+ response.must_be_kind_of Hash
25
+ end
26
+ end
27
+ end
28
+ =end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,39 @@
1
+ require 'test_helper'
2
+ require 'pry'
3
+
4
+ describe Hungrytable::Restaurant do
5
+ before do
6
+ @mock_requester = mock('GetRequest')
7
+ @mock_request = mock('get')
8
+ @mock_requester.expects(:new).returns(@mock_request)
9
+ @mock_request.stubs(:parsed_response).returns({"RestaurantDetailsResults" => {}})
10
+ ENV['OT_PARTNER_ID'] = 'foo'
11
+ @restaurant = Hungrytable::Restaurant.new(1, requester: @mock_requester)
12
+ end
13
+ %w(
14
+ address
15
+ city
16
+ error_ID
17
+ error_message
18
+ image_link
19
+ latitude
20
+ longitude
21
+ metro_name
22
+ neighborhood_name
23
+ parking
24
+ parking_details
25
+ phone
26
+ postal_code
27
+ price_range
28
+ primary_food_type
29
+ restaurant_description
30
+ restaurant_ID
31
+ restaurant_name
32
+ state
33
+ url
34
+ ).map(&:to_sym).each do |meth|
35
+ it "responds to #{meth.to_s} via method_missing" do
36
+ @restaurant.send(meth).must_equal nil
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,6 @@
1
+ HTTP/1.1 200 OK
2
+ Status: 200
3
+ Vary: Accept-Encoding
4
+ Content-Type: application/json
5
+
6
+ { "LoginResults": {"ns:ErrorID": "303", "ns:ErrorMessage": "Invalid User Email. Please re-enter your UserEmail.", "ns:IsAdmin": "0", "xmlns:ns": "com.opentable.webservices.v2" }}
metadata ADDED
@@ -0,0 +1,258 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hungrytable-pjc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Chapman
9
+ - Nicholas Fine
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-12-17 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 1.7.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: 1.7.1
31
+ - !ruby/object:Gem::Dependency
32
+ name: activesupport
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: i18n
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: curb
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ - !ruby/object:Gem::Dependency
80
+ name: rake
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :development
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: minitest
97
+ requirement: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: minitest-reporters
113
+ requirement: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ - !ruby/object:Gem::Dependency
128
+ name: mocha
129
+ requirement: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ - !ruby/object:Gem::Dependency
144
+ name: pry
145
+ requirement: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ! '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ type: :development
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ! '>='
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ - !ruby/object:Gem::Dependency
160
+ name: webmock
161
+ requirement: !ruby/object:Gem::Requirement
162
+ none: false
163
+ requirements:
164
+ - - ! '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ type: :development
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ! '>='
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ description: Gem to interact with OpenTable's REST API
176
+ email:
177
+ - david@isotope11.com
178
+ - nicholas.fine@gmail.com
179
+ executables: []
180
+ extensions: []
181
+ extra_rdoc_files: []
182
+ files:
183
+ - .gitignore
184
+ - .rvmrc
185
+ - Gemfile
186
+ - Guardfile
187
+ - LICENSE
188
+ - README.md
189
+ - RELEASE_NOTES.md
190
+ - Rakefile
191
+ - hungrytable.gemspec
192
+ - lib/hungrytable.rb
193
+ - lib/hungrytable/config.rb
194
+ - lib/hungrytable/get_request.rb
195
+ - lib/hungrytable/post_request.rb
196
+ - lib/hungrytable/request.rb
197
+ - lib/hungrytable/request_extensions.rb
198
+ - lib/hungrytable/request_header.rb
199
+ - lib/hungrytable/reservation_cancel.rb
200
+ - lib/hungrytable/reservation_make.rb
201
+ - lib/hungrytable/reservation_status.rb
202
+ - lib/hungrytable/restaurant.rb
203
+ - lib/hungrytable/restaurant_search.rb
204
+ - lib/hungrytable/restaurant_slotlock.rb
205
+ - lib/hungrytable/version.rb
206
+ - test/restaurant_get_details_result.json
207
+ - test/restaurant_search_result.json
208
+ - test/test_helper.rb
209
+ - test/unit/config_test.rb
210
+ - test/unit/get_request_test.rb
211
+ - test/unit/hungrytable/user_test.rb
212
+ - test/unit/post_request_test.rb
213
+ - test/unit/request_test.rb
214
+ - test/unit/reservation_cancel_test.rb
215
+ - test/unit/reservation_make_test.rb
216
+ - test/unit/restaurant_search_test.rb
217
+ - test/unit/restaurant_slotlock_test.rb
218
+ - test/unit/restaurant_test.rb
219
+ - test/user_login_result.json
220
+ homepage: http://www.isotope11.com/
221
+ licenses: []
222
+ post_install_message:
223
+ rdoc_options: []
224
+ require_paths:
225
+ - lib
226
+ required_ruby_version: !ruby/object:Gem::Requirement
227
+ none: false
228
+ requirements:
229
+ - - ! '>='
230
+ - !ruby/object:Gem::Version
231
+ version: '0'
232
+ required_rubygems_version: !ruby/object:Gem::Requirement
233
+ none: false
234
+ requirements:
235
+ - - ! '>='
236
+ - !ruby/object:Gem::Version
237
+ version: '0'
238
+ requirements: []
239
+ rubyforge_project: hungrytable-pjc
240
+ rubygems_version: 1.8.24
241
+ signing_key:
242
+ specification_version: 3
243
+ summary: Gem to interact with OpenTable's REST API
244
+ test_files:
245
+ - test/restaurant_get_details_result.json
246
+ - test/restaurant_search_result.json
247
+ - test/test_helper.rb
248
+ - test/unit/config_test.rb
249
+ - test/unit/get_request_test.rb
250
+ - test/unit/hungrytable/user_test.rb
251
+ - test/unit/post_request_test.rb
252
+ - test/unit/request_test.rb
253
+ - test/unit/reservation_cancel_test.rb
254
+ - test/unit/reservation_make_test.rb
255
+ - test/unit/restaurant_search_test.rb
256
+ - test/unit/restaurant_slotlock_test.rb
257
+ - test/unit/restaurant_test.rb
258
+ - test/user_login_result.json