hungrytable 0.0.8 → 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.
- checksums.yaml +7 -0
- data/README.md +299 -22
- data/RELEASE_NOTES.md +16 -0
- data/lib/hungrytable/api_health_monitor.rb +106 -0
- data/lib/hungrytable/circuit_breaker.rb +177 -0
- data/lib/hungrytable/config.rb +38 -16
- data/lib/hungrytable/enhanced_errors.rb +127 -0
- data/lib/hungrytable/errors.rb +36 -0
- data/lib/hungrytable/get_request.rb +17 -6
- data/lib/hungrytable/post_request.rb +17 -6
- data/lib/hungrytable/request.rb +42 -5
- data/lib/hungrytable/request_extensions.rb +17 -4
- data/lib/hungrytable/request_header.rb +25 -25
- data/lib/hungrytable/reservation_cancel.rb +19 -5
- data/lib/hungrytable/reservation_make.rb +30 -9
- data/lib/hungrytable/reservation_status.rb +56 -0
- data/lib/hungrytable/restaurant.rb +43 -31
- data/lib/hungrytable/restaurant_search.rb +63 -34
- data/lib/hungrytable/restaurant_slotlock.rb +32 -10
- data/lib/hungrytable/version.rb +3 -1
- data/lib/hungrytable.rb +117 -65
- metadata +43 -179
- data/.gitignore +0 -6
- data/.rvmrc +0 -1
- data/Gemfile +0 -5
- data/Guardfile +0 -16
- data/Rakefile +0 -8
- data/hungrytable.gemspec +0 -37
- data/test/restaurant_get_details_result.json +0 -6
- data/test/restaurant_search_result.json +0 -7
- data/test/test_helper.rb +0 -18
- data/test/unit/config_test.rb +0 -43
- data/test/unit/get_request_test.rb +0 -0
- data/test/unit/hungrytable/user_test.rb +0 -28
- data/test/unit/post_request_test.rb +0 -0
- data/test/unit/request_test.rb +0 -0
- data/test/unit/reservation_cancel_test.rb +0 -0
- data/test/unit/reservation_make_test.rb +0 -0
- data/test/unit/restaurant_search_test.rb +0 -0
- data/test/unit/restaurant_slotlock_test.rb +0 -0
- data/test/unit/restaurant_test.rb +0 -39
- data/test/user_login_result.json +0 -6
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hungrytable
|
|
4
|
+
# Checks the status of an existing reservation
|
|
5
|
+
class ReservationStatus
|
|
6
|
+
include RequestExtensions
|
|
7
|
+
|
|
8
|
+
attr_reader :opts
|
|
9
|
+
|
|
10
|
+
def initialize(opts = {})
|
|
11
|
+
@opts = opts
|
|
12
|
+
ensure_required_opts
|
|
13
|
+
@requester = opts[:requester] || GetRequest
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [Boolean] true if the status request was successful
|
|
17
|
+
def successful?
|
|
18
|
+
details['ns:ErrorID'].to_s == '0'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [String] the current status of the reservation
|
|
22
|
+
def status
|
|
23
|
+
return nil unless successful?
|
|
24
|
+
|
|
25
|
+
details['ns:ReservationStatus']
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Hash] all reservation details
|
|
29
|
+
def reservation_details
|
|
30
|
+
return nil unless successful?
|
|
31
|
+
|
|
32
|
+
{
|
|
33
|
+
status: details['ns:ReservationStatus'],
|
|
34
|
+
restaurant_name: details['ns:RestaurantName'],
|
|
35
|
+
date_time: details['ns:DateTime'],
|
|
36
|
+
party_size: details['ns:PartySize'],
|
|
37
|
+
confirmation_number: opts[:confirmation_number]
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def request_uri
|
|
44
|
+
"/reservationstatus/?pid=#{Config.partner_id}&rid=#{opts[:restaurant_id]}&" \
|
|
45
|
+
"conf=#{CGI.escape(opts[:confirmation_number].to_s)}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def details
|
|
49
|
+
@details ||= request.parsed_response['StatusResults'] || {}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def required_opts
|
|
53
|
+
%i[confirmation_number restaurant_id]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -1,46 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Hungrytable
|
|
4
|
+
# Represents a restaurant and its details from the OpenTable API
|
|
2
5
|
class Restaurant
|
|
3
6
|
include RequestExtensions
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
# Attributes that map directly to API response fields
|
|
9
|
+
ATTRIBUTES = %i[
|
|
10
|
+
address
|
|
11
|
+
city
|
|
12
|
+
error_ID
|
|
13
|
+
error_message
|
|
14
|
+
image_link
|
|
15
|
+
latitude
|
|
16
|
+
longitude
|
|
17
|
+
metro_name
|
|
18
|
+
neighborhood_name
|
|
19
|
+
parking
|
|
20
|
+
parking_details
|
|
21
|
+
phone
|
|
22
|
+
postal_code
|
|
23
|
+
price_range
|
|
24
|
+
primary_food_type
|
|
25
|
+
restaurant_description
|
|
26
|
+
restaurant_ID
|
|
27
|
+
restaurant_name
|
|
28
|
+
state
|
|
29
|
+
url
|
|
30
|
+
].freeze
|
|
31
|
+
|
|
32
|
+
attr_reader :restaurant_id
|
|
33
|
+
|
|
34
|
+
def initialize(restaurant_id, opts = {})
|
|
35
|
+
@requester = opts[:requester] || GetRequest
|
|
7
36
|
@restaurant_id = restaurant_id
|
|
8
37
|
end
|
|
9
38
|
|
|
39
|
+
# Alias for consistency
|
|
10
40
|
def id
|
|
11
41
|
@restaurant_id
|
|
12
42
|
end
|
|
13
43
|
|
|
44
|
+
# Check if the restaurant query was valid
|
|
45
|
+
# @return [Boolean] true if no errors
|
|
14
46
|
def valid?
|
|
15
|
-
error_ID ==
|
|
47
|
+
error_ID.to_s == '0'
|
|
16
48
|
end
|
|
17
49
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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")}"]
|
|
50
|
+
# Dynamically define getter methods for all attributes
|
|
51
|
+
ATTRIBUTES.each do |attr|
|
|
52
|
+
define_method(attr) do
|
|
53
|
+
# Convert Ruby snake_case to API camelCase, handling special case of ID
|
|
54
|
+
api_key = "ns:#{attr.to_s.camelize.gsub('Id', 'ID')}"
|
|
55
|
+
details[api_key]
|
|
42
56
|
end
|
|
43
|
-
super
|
|
44
57
|
end
|
|
45
58
|
|
|
46
59
|
private
|
|
@@ -49,10 +62,9 @@ module Hungrytable
|
|
|
49
62
|
"/restaurant/?pid=#{Config.partner_id}&rid=#{id}"
|
|
50
63
|
end
|
|
51
64
|
|
|
52
|
-
# @return [Hash]
|
|
65
|
+
# @return [Hash] restaurant details from API response
|
|
53
66
|
def details
|
|
54
|
-
request.parsed_response[
|
|
67
|
+
@details ||= request.parsed_response['RestaurantDetailsResults'] || {}
|
|
55
68
|
end
|
|
56
|
-
|
|
57
69
|
end
|
|
58
70
|
end
|
|
@@ -1,77 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Hungrytable
|
|
4
|
+
# Searches for available reservation times at a restaurant
|
|
2
5
|
class RestaurantSearch
|
|
3
6
|
include RequestExtensions
|
|
4
7
|
|
|
8
|
+
# Attributes that map directly to API response fields
|
|
9
|
+
ATTRIBUTES = %i[
|
|
10
|
+
cuisine_type
|
|
11
|
+
early_security_ID
|
|
12
|
+
early_time
|
|
13
|
+
error_ID
|
|
14
|
+
error_message
|
|
15
|
+
exact_security_ID
|
|
16
|
+
exact_time
|
|
17
|
+
later_security_ID
|
|
18
|
+
later_time
|
|
19
|
+
latitude
|
|
20
|
+
longitude
|
|
21
|
+
neighborhood_name
|
|
22
|
+
restaurant_name
|
|
23
|
+
results_key
|
|
24
|
+
no_times_message
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
5
27
|
attr_reader :restaurant, :opts
|
|
6
28
|
|
|
7
|
-
def initialize
|
|
29
|
+
def initialize(restaurant, opts = {})
|
|
8
30
|
@opts = opts
|
|
9
31
|
ensure_required_opts
|
|
10
|
-
|
|
32
|
+
validate_date_time_type
|
|
33
|
+
validate_party_size
|
|
34
|
+
@requester = opts[:requester] || GetRequest
|
|
11
35
|
@restaurant = restaurant
|
|
12
36
|
end
|
|
13
37
|
|
|
38
|
+
# Check if the search was valid
|
|
39
|
+
# @return [Boolean] true if no errors
|
|
14
40
|
def valid?
|
|
15
|
-
error_ID ==
|
|
41
|
+
error_ID.to_s == '0'
|
|
16
42
|
end
|
|
17
43
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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")}"]
|
|
44
|
+
# Dynamically define getter methods for all attributes
|
|
45
|
+
ATTRIBUTES.each do |attr|
|
|
46
|
+
define_method(attr) do
|
|
47
|
+
# Convert Ruby snake_case to API camelCase, handling special case of ID
|
|
48
|
+
api_key = "ns:#{attr.to_s.camelize.gsub('Id', 'ID')}"
|
|
49
|
+
details[api_key]
|
|
37
50
|
end
|
|
38
|
-
super
|
|
39
51
|
end
|
|
40
52
|
|
|
53
|
+
# Get the best available security ID
|
|
54
|
+
# @return [String, nil] the ideal security ID for slotlock
|
|
41
55
|
def ideal_security_id
|
|
42
|
-
|
|
43
|
-
return early_security_ID unless early_security_ID.nil?
|
|
44
|
-
return later_security_ID unless later_security_ID.nil?
|
|
45
|
-
nil
|
|
56
|
+
exact_security_ID || early_security_ID || later_security_ID
|
|
46
57
|
end
|
|
47
58
|
|
|
59
|
+
# Get the best available time
|
|
60
|
+
# @return [String, nil] the ideal time for reservation
|
|
48
61
|
def ideal_time
|
|
49
|
-
|
|
50
|
-
return early_time unless early_time.nil?
|
|
51
|
-
return later_time unless later_time.nil?
|
|
52
|
-
nil
|
|
62
|
+
exact_time || early_time || later_time
|
|
53
63
|
end
|
|
54
64
|
|
|
65
|
+
# Get the party size for this search
|
|
66
|
+
# @return [Integer] number of people
|
|
55
67
|
def party_size
|
|
56
68
|
opts[:party_size]
|
|
57
69
|
end
|
|
58
70
|
|
|
59
71
|
private
|
|
72
|
+
|
|
60
73
|
def required_opts
|
|
61
|
-
%
|
|
74
|
+
%i[date_time party_size]
|
|
62
75
|
end
|
|
63
76
|
|
|
64
77
|
def encoded_date_time
|
|
65
|
-
|
|
78
|
+
# Use CGI.escape instead of deprecated URI.encode
|
|
79
|
+
CGI.escape(opts[:date_time].strftime('%m/%d/%Y %I:%M %p'))
|
|
66
80
|
end
|
|
67
81
|
|
|
68
82
|
def request_uri
|
|
69
83
|
"/table/?pid=#{Config.partner_id}&rid=#{restaurant.id}&dt=#{encoded_date_time}&ps=#{party_size}"
|
|
70
84
|
end
|
|
71
85
|
|
|
86
|
+
# @return [Hash] search results from API response
|
|
72
87
|
def details
|
|
73
|
-
request.parsed_response[
|
|
88
|
+
@details ||= request.parsed_response['SearchResults'] || {}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def validate_date_time_type
|
|
92
|
+
return if opts[:date_time].respond_to?(:strftime)
|
|
93
|
+
|
|
94
|
+
raise ValidationError,
|
|
95
|
+
"date_time must be a Time or DateTime object, got #{opts[:date_time].class}"
|
|
74
96
|
end
|
|
75
97
|
|
|
98
|
+
def validate_party_size
|
|
99
|
+
ps = opts[:party_size]
|
|
100
|
+
return if ps.is_a?(Integer) && ps.positive? && ps <= 20
|
|
101
|
+
|
|
102
|
+
raise ValidationError,
|
|
103
|
+
"party_size must be a positive integer between 1 and 20, got #{ps.inspect}"
|
|
104
|
+
end
|
|
76
105
|
end
|
|
77
106
|
end
|
|
@@ -1,38 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Hungrytable
|
|
4
|
+
# Locks a reservation time slot before making a reservation
|
|
2
5
|
class RestaurantSlotlock
|
|
3
6
|
include RequestExtensions
|
|
4
7
|
|
|
5
8
|
attr_reader :restaurant_search
|
|
6
9
|
|
|
7
|
-
def initialize
|
|
10
|
+
def initialize(restaurant_search, requester = PostRequest)
|
|
8
11
|
@restaurant_search = restaurant_search
|
|
9
|
-
@requester
|
|
12
|
+
@requester = requester
|
|
13
|
+
validate_search_has_results
|
|
10
14
|
end
|
|
11
15
|
|
|
16
|
+
# Check if the slotlock was successful
|
|
17
|
+
# @return [Boolean] true if no errors
|
|
12
18
|
def successful?
|
|
13
|
-
details[
|
|
19
|
+
details['ns:ErrorID'].to_s == '0'
|
|
14
20
|
end
|
|
15
21
|
|
|
22
|
+
# Get error messages if slotlock failed
|
|
23
|
+
# @return [String, nil] error message or nil
|
|
16
24
|
def errors
|
|
17
|
-
details[
|
|
25
|
+
details['ns:ErrorMessage']
|
|
18
26
|
end
|
|
19
27
|
|
|
28
|
+
# Get the slotlock ID needed for making a reservation
|
|
29
|
+
# @return [String, nil] slotlock ID or nil if unsuccessful
|
|
20
30
|
def slotlock_id
|
|
21
31
|
return nil unless successful?
|
|
22
|
-
|
|
32
|
+
|
|
33
|
+
details['ns:SlotLockID']
|
|
23
34
|
end
|
|
24
35
|
|
|
36
|
+
# Parameters to send with the slotlock request
|
|
37
|
+
# @return [Hash] request parameters
|
|
25
38
|
def params
|
|
26
39
|
{
|
|
27
|
-
'RID'
|
|
28
|
-
'datetime'
|
|
29
|
-
'partysize'
|
|
40
|
+
'RID' => restaurant.id,
|
|
41
|
+
'datetime' => restaurant_search.ideal_time,
|
|
42
|
+
'partysize' => restaurant_search.party_size,
|
|
30
43
|
'timesecurityID' => restaurant_search.ideal_security_id,
|
|
31
|
-
'resultskey'
|
|
44
|
+
'resultskey' => restaurant_search.results_key
|
|
32
45
|
}
|
|
33
46
|
end
|
|
34
47
|
|
|
35
48
|
private
|
|
49
|
+
|
|
36
50
|
def request_uri
|
|
37
51
|
"/slotlock/?pid=#{Config.partner_id}&st=0"
|
|
38
52
|
end
|
|
@@ -41,9 +55,17 @@ module Hungrytable
|
|
|
41
55
|
restaurant_search.restaurant
|
|
42
56
|
end
|
|
43
57
|
|
|
58
|
+
# @return [Hash] slotlock results from API response
|
|
44
59
|
def details
|
|
45
|
-
request.parsed_response[
|
|
60
|
+
@details ||= request.parsed_response['SlotLockResults'] || {}
|
|
46
61
|
end
|
|
47
62
|
|
|
63
|
+
def validate_search_has_results
|
|
64
|
+
if restaurant_search.ideal_security_id.nil? ||
|
|
65
|
+
restaurant_search.ideal_time.nil? ||
|
|
66
|
+
restaurant_search.results_key.nil?
|
|
67
|
+
raise ValidationError, 'Cannot create slotlock: no available times found in restaurant search'
|
|
68
|
+
end
|
|
69
|
+
end
|
|
48
70
|
end
|
|
49
71
|
end
|
data/lib/hungrytable/version.rb
CHANGED
data/lib/hungrytable.rb
CHANGED
|
@@ -1,73 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'uri'
|
|
2
4
|
require 'json'
|
|
3
|
-
require 'openssl'
|
|
4
|
-
require 'base64'
|
|
5
5
|
require 'cgi'
|
|
6
|
-
require '
|
|
7
|
-
require '
|
|
8
|
-
require '
|
|
9
|
-
require '
|
|
10
|
-
require '
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
require 'http'
|
|
7
|
+
require 'oauth'
|
|
8
|
+
require 'active_support'
|
|
9
|
+
require 'active_support/core_ext/string/inflections'
|
|
10
|
+
require 'active_support/concern'
|
|
11
|
+
|
|
12
|
+
require_relative 'hungrytable/version'
|
|
13
|
+
require_relative 'hungrytable/errors'
|
|
14
|
+
require_relative 'hungrytable/config'
|
|
15
|
+
|
|
16
|
+
# Safety features
|
|
17
|
+
require_relative 'hungrytable/circuit_breaker'
|
|
18
|
+
require_relative 'hungrytable/api_health_monitor'
|
|
19
|
+
require_relative 'hungrytable/enhanced_errors'
|
|
20
|
+
|
|
21
|
+
# Core functionality
|
|
22
|
+
require_relative 'hungrytable/request_extensions'
|
|
23
|
+
require_relative 'hungrytable/request_header'
|
|
24
|
+
require_relative 'hungrytable/request'
|
|
25
|
+
require_relative 'hungrytable/get_request'
|
|
26
|
+
require_relative 'hungrytable/post_request'
|
|
27
|
+
require_relative 'hungrytable/restaurant'
|
|
28
|
+
require_relative 'hungrytable/restaurant_search'
|
|
29
|
+
require_relative 'hungrytable/restaurant_slotlock'
|
|
30
|
+
require_relative 'hungrytable/reservation_make'
|
|
31
|
+
require_relative 'hungrytable/reservation_status'
|
|
32
|
+
require_relative 'hungrytable/reservation_cancel'
|
|
21
33
|
|
|
22
34
|
module Hungrytable
|
|
23
|
-
|
|
35
|
+
# OpenTable API error codes
|
|
36
|
+
# @return [Hash] mapping of error codes to error details
|
|
37
|
+
ERROR_CODES = {
|
|
38
|
+
152 => { message: 'The email address is not valid. Please try again.', name: 'VALIDEMAIL' },
|
|
39
|
+
160 => {
|
|
40
|
+
message: "We're sorry but we were unable to complete you request. Our technical team has been notified " \
|
|
41
|
+
'of the problem and will resolve it shortly. Thank you.',
|
|
42
|
+
name: 'GENERALERROR'
|
|
43
|
+
},
|
|
44
|
+
197 => { message: 'You must select at least one restaurant to search.', name: 'PROVIDERESTAURANT' },
|
|
45
|
+
202 => {
|
|
46
|
+
message: 'The time you selected has already passed. You may wish to check that your computer clock ' \
|
|
47
|
+
'is correct.',
|
|
48
|
+
name: 'PASSEDTIME'
|
|
49
|
+
},
|
|
50
|
+
274 => { message: 'Your phone number must be numeric.', name: 'NUMERICPHONE' },
|
|
51
|
+
279 => { message: 'Your IP address is not listed as an OpenTable Partner IP.', name: 'INVALIDIP' },
|
|
52
|
+
280 => { message: 'Key authentication failure', name: 'AUTHENTICATEFAIL' },
|
|
53
|
+
281 => { message: 'The phone length for the number provided was not a valid length for the country code.',
|
|
54
|
+
name: 'INVALIDPHONELENGTH' },
|
|
55
|
+
282 => {
|
|
56
|
+
message: 'We are currently unable to connect to the restaurant to complete this action. ' \
|
|
57
|
+
'Please try again later.',
|
|
58
|
+
name: 'ERBERROR'
|
|
59
|
+
},
|
|
60
|
+
283 => { message: 'The time you have chosen for your reservation is no longer available.',
|
|
61
|
+
name: 'RESERVATIONNOTAVAIL' },
|
|
62
|
+
285 => { message: 'Credit Card transactions are not allowed via OT Web Services.', name: 'CCNOTALLOWED' },
|
|
63
|
+
286 => { message: 'Large parties are not allowed via OT Web Services.', name: 'LARGEPARTYNOTALLOWED' },
|
|
64
|
+
287 => { message: 'The restaurant is currently offline.', name: 'RESTOFFLINE' },
|
|
65
|
+
288 => { message: 'The restaurant is currently unreachable.', name: 'RESTUNREACHABLE' },
|
|
66
|
+
289 => { message: 'Cancel transaction failed.', name: 'CANCELFAIL' },
|
|
67
|
+
294 => { message: 'You have not provided enough information to perform the search.',
|
|
68
|
+
name: 'INSUFFICIENTINFORMATION' },
|
|
69
|
+
295 => { message: 'No restaurants were found in your search. Please try again.', name: 'NORESTAURANTSRETURNED' },
|
|
70
|
+
296 => { message: 'Your search produced no times.', name: 'NOTIMESMESSAGE' },
|
|
71
|
+
298 => { message: 'The confirmation number is invalid.', name: 'VALIDCONFIRMNUMBER' },
|
|
72
|
+
299 => { message: 'The reservation status is not available on reservations older than 30 days.',
|
|
73
|
+
name: 'VALIDSTATUSDATE' },
|
|
74
|
+
301 => {
|
|
75
|
+
message: 'The user for the reservation request already has a reservation within 2 hours ' \
|
|
76
|
+
'of the requested time.',
|
|
77
|
+
name: 'VALIDDUPRESERVATION'
|
|
78
|
+
},
|
|
79
|
+
302 => { message: 'No Reservation Activity found.', name: 'NORESOHISTORYAVAILABLE' },
|
|
80
|
+
303 => { message: 'Invalid User Email. Please re-enter your UserEmail.', name: 'INVALIDUSEREMAIL' },
|
|
81
|
+
304 => { message: 'Invalid User Password. Please re-enter your password.', name: 'INVALIDUSERPASSWORD' },
|
|
82
|
+
305 => { message: 'Unable to authenticate user to login. Please try again.', name: 'USERAUTHENTICATIONFAIL' },
|
|
83
|
+
306 => { message: 'The entered area to search is too large please limit your search to 20 miles.',
|
|
84
|
+
name: 'SEARCHAREATOOLARGE' },
|
|
85
|
+
307 => { message: 'The email address(es) you have provided are not in the correct format. Please try again.',
|
|
86
|
+
name: 'INVALIDEMAILADDRESSES' },
|
|
87
|
+
308 => {
|
|
88
|
+
message: 'Your reservation has already been cancelled, please hit the Reload button on your profile ' \
|
|
89
|
+
'to refresh your page',
|
|
90
|
+
name: 'ALREADYCANCELLED'
|
|
91
|
+
},
|
|
92
|
+
309 => { message: 'You must use SSL for this request.', name: 'SSLCONNECTIONREQUIRED' },
|
|
93
|
+
310 => { message: 'The points requested for this time slot are not available.', name: 'INVALIDPOINTREQUEST' },
|
|
94
|
+
311 => { message: 'The user account is deactivated.', name: 'ACCTDEACTIVATED' },
|
|
95
|
+
313 => { message: 'The requested Restaurant was not found.', name: 'INVALIDRESTAURANTID' },
|
|
96
|
+
314 => { message: 'We already have an account registered to <email address>', name: 'MATCHACCOUNT' },
|
|
97
|
+
315 => { message: 'A default metro must be provided for a use', name: 'PROVIDEMETRO' },
|
|
98
|
+
316 => { message: 'The password must be a minimum of 6 characters.', name: 'MALFORMEDPASSWORD' },
|
|
99
|
+
317 => { message: 'The phone length for the number provided was not a valid length for the country code.',
|
|
100
|
+
name: 'INVALIDPHONE' },
|
|
101
|
+
318 => { message: 'Please enter a valid country id for the phone number.', name: 'PROVIDEPHONECOUNTRY' },
|
|
102
|
+
319 => { message: 'Please enter a valid country id for the mobile phone number.',
|
|
103
|
+
name: 'PROVIDEMOBILEPHONECOUNTRY' },
|
|
104
|
+
321 => {
|
|
105
|
+
message: "We're sorry, but we could not complete your reservation request because an account cannot " \
|
|
106
|
+
'have more than two confirmed reservations into the same restaurant for the same day.',
|
|
107
|
+
name: 'VALIDTOOMANYSAMEREST'
|
|
108
|
+
},
|
|
109
|
+
322 => { message: 'The time slot for this reservation is no longer available.', name: 'SLOTLOCKUNAVAILABLE' },
|
|
110
|
+
323 => { message: 'The service you are trying to access is currently unavailable, please try again later.',
|
|
111
|
+
name: 'SERVICEUNAVAILABLE' },
|
|
112
|
+
324 => { message: 'The service experienced a timeout. Please try again later.', name: 'SERVICETIMEOUT' },
|
|
113
|
+
325 => { message: 'Invalid number of lookback days, the most you can look back is 8 days.',
|
|
114
|
+
name: 'INVALIDLOOKBACKDAYS' },
|
|
115
|
+
326 => { message: 'This service is currently disabled. Please try again later.', name: 'CURRENTLYDISABLED' }
|
|
116
|
+
}.freeze
|
|
24
117
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
}
|
|
118
|
+
class << self
|
|
119
|
+
# Configure the gem programmatically
|
|
120
|
+
# @yield [Config] configuration object
|
|
121
|
+
def configure
|
|
122
|
+
yield Config if block_given?
|
|
123
|
+
end
|
|
72
124
|
end
|
|
73
125
|
end
|