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 +6 -0
- data/.rvmrc +1 -0
- data/Gemfile +5 -0
- data/Guardfile +16 -0
- data/LICENSE +22 -0
- data/README.md +54 -0
- data/RELEASE_NOTES.md +11 -0
- data/Rakefile +8 -0
- data/hungrytable.gemspec +37 -0
- data/lib/hungrytable/config.rb +26 -0
- data/lib/hungrytable/get_request.rb +15 -0
- data/lib/hungrytable/post_request.rb +15 -0
- data/lib/hungrytable/request.rb +17 -0
- data/lib/hungrytable/request_extensions.rb +21 -0
- data/lib/hungrytable/request_header.rb +109 -0
- data/lib/hungrytable/reservation_cancel.rb +30 -0
- data/lib/hungrytable/reservation_make.rb +51 -0
- data/lib/hungrytable/reservation_status.rb +0 -0
- data/lib/hungrytable/restaurant.rb +58 -0
- data/lib/hungrytable/restaurant_search.rb +78 -0
- data/lib/hungrytable/restaurant_slotlock.rb +49 -0
- data/lib/hungrytable/version.rb +3 -0
- data/lib/hungrytable.rb +73 -0
- data/test/restaurant_get_details_result.json +6 -0
- data/test/restaurant_search_result.json +7 -0
- data/test/test_helper.rb +18 -0
- data/test/unit/config_test.rb +43 -0
- data/test/unit/get_request_test.rb +0 -0
- data/test/unit/hungrytable/user_test.rb +28 -0
- 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 +39 -0
- data/test/user_login_result.json +6 -0
- metadata +258 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.2@hungrytable --create
|
data/Gemfile
ADDED
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
data/hungrytable.gemspec
ADDED
@@ -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
|
data/lib/hungrytable.rb
ADDED
@@ -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
|
+
|
data/test/test_helper.rb
ADDED
@@ -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
|
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
|