hungrytable 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +1 -0
  3. data/Guardfile +16 -0
  4. data/README.md +16 -0
  5. data/hungrytable.gemspec +8 -7
  6. data/lib/hungrytable/config.rb +26 -0
  7. data/lib/hungrytable/get_request.rb +15 -0
  8. data/lib/hungrytable/post_request.rb +15 -0
  9. data/lib/hungrytable/request.rb +17 -0
  10. data/lib/hungrytable/request_extensions.rb +21 -0
  11. data/lib/hungrytable/request_header.rb +109 -0
  12. data/lib/hungrytable/reservation_cancel.rb +30 -0
  13. data/lib/hungrytable/reservation_make.rb +51 -0
  14. data/lib/hungrytable/reservation_status.rb +0 -0
  15. data/lib/hungrytable/restaurant.rb +50 -27
  16. data/lib/hungrytable/restaurant_search.rb +76 -0
  17. data/lib/hungrytable/restaurant_slotlock.rb +45 -0
  18. data/lib/hungrytable/version.rb +1 -1
  19. data/lib/hungrytable.rb +21 -6
  20. data/test/restaurant_get_details_result.json +6 -0
  21. data/test/restaurant_search_result.json +7 -0
  22. data/test/test_helper.rb +4 -6
  23. data/test/unit/config_test.rb +43 -0
  24. data/test/unit/get_request_test.rb +0 -0
  25. data/test/unit/hungrytable/restaurant_test.rb +11 -3
  26. data/test/unit/hungrytable/user_test.rb +28 -0
  27. data/test/unit/post_request_test.rb +0 -0
  28. data/test/unit/request_test.rb +0 -0
  29. data/test/unit/reservation_cancel_test.rb +0 -0
  30. data/test/unit/reservation_make_test.rb +0 -0
  31. data/test/unit/restaurant_search_test.rb +0 -0
  32. data/test/unit/restaurant_slotlock_test.rb +0 -0
  33. data/test/unit/restaurant_test.rb +39 -0
  34. data/test/user_login_result.json +6 -0
  35. metadata +127 -43
  36. data/lib/hungrytable/base.rb +0 -26
  37. data/lib/hungrytable/oauth_patch.rb +0 -19
  38. data/test/hungrytable_test.rb +0 -8
  39. data/test/support/minitest_junit.rb +0 -81
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  *.sw*
6
+ .open_table
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in hungrytable.gemspec
4
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/README.md CHANGED
@@ -28,6 +28,22 @@ Observe:
28
28
  export OT_OAUTH_KEY=<YOUR OPENTABLE OAUTH KEY>
29
29
  export OT_OAUTH_SECRET=<YOUR OPENTABLE OAUTH SECRET KEY>
30
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
+
31
47
 
32
48
  ## Contributing
33
49
 
data/hungrytable.gemspec CHANGED
@@ -22,15 +22,16 @@ Gem::Specification.new do |s|
22
22
  # s.add_development_dependency "rspec"
23
23
  # s.add_runtime_dependency "rest-client"
24
24
 
25
- s.add_runtime_dependency 'oauth', '~> 0.4.5'
26
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'
27
29
 
28
- s.add_development_dependency "minitest"
29
- s.add_development_dependency "minitest-reporters"
30
- s.add_development_dependency "rake"
31
- s.add_development_dependency "simplecov"
32
- s.add_development_dependency 'simplecov-rcov'
33
- s.add_development_dependency 'rcov', '0.9.11'
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
34
  s.add_development_dependency 'pry'
35
+ s.add_development_dependency 'webmock'
35
36
  end
36
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
@@ -1,35 +1,58 @@
1
1
  module Hungrytable
2
- class Restaurant < ::Hungrytable::Base
3
- def initialize
4
- validate_environment_variables
5
-
6
- @consumer = OAuth::Consumer.new(
7
- ENV['OT_OAUTH_KEY'],
8
- ENV['OT_OAUTH_SECRET'],
9
- realm: 'http://www.opentable.com/',
10
- site: 'https://secure.opentable.com/api/otapi_v2.ashx',
11
- request_token_path: '',
12
- authorize_path: '',
13
- access_token_path: '',
14
- oauth_token: '',
15
- oauth_version: '1.0',
16
- http_method: :get
17
- )
18
-
19
- @access_token = OAuth::AccessToken.new(@consumer)
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
20
12
  end
21
13
 
22
- def get_details(restaurant_id)
23
- uri = "/restaurant/?pid=#{ENV['OT_PARTNER_ID']}&rid=#{restaurant_id}"
24
- response = @access_token.get(uri)
25
- parse_json(response.body)
14
+ def valid?
15
+ error_ID == "0"
26
16
  end
27
17
 
28
- def search(options)
29
- parse_uri_options(:restaurant_id, :date_time, :party_size, options)
30
- uri = "/table/?pid=#{ENV['OT_PARTNER_ID']}&st=0&rid=#{@restaurant_id}&dt=#{@date_time}&ps=#{@party_size}"
31
- response = @access_token.get(uri)
32
- parse_json(response.body)
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}"]
42
+ end
43
+ super
33
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
+
34
57
  end
35
58
  end
@@ -0,0 +1,76 @@
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
+ ).map(&:to_sym).include?(meth)
35
+ return details["ns:#{meth.to_s.camelize}"]
36
+ end
37
+ super
38
+ end
39
+
40
+ def ideal_security_id
41
+ return exact_security_ID unless exact_security_ID.nil?
42
+ return early_security_ID unless early_security_ID.nil?
43
+ return later_security_ID unless later_security_ID.nil?
44
+ nil
45
+ end
46
+
47
+ def ideal_time
48
+ return exact_time unless exact_time.nil?
49
+ return early_time unless early_time.nil?
50
+ return later_time unless later_time.nil?
51
+ nil
52
+ end
53
+
54
+ def party_size
55
+ opts[:party_size]
56
+ end
57
+
58
+ private
59
+ def required_opts
60
+ %w(date_time party_size).map(&:to_sym)
61
+ end
62
+
63
+ def encoded_date_time
64
+ URI.encode(opts[:date_time].strftime("%m/%d/%Y %I:%M %p"), /[^a-z0-9\-\.\_\~]/i)
65
+ end
66
+
67
+ def request_uri
68
+ "/table/?pid=#{Config.partner_id}&rid=#{restaurant.id}&dt=#{encoded_date_time}&ps=#{party_size}"
69
+ end
70
+
71
+ def details
72
+ request.parsed_response["SearchResults"]
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,45 @@
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 slotlock_id
17
+ return nil unless successful?
18
+ details["ns:SlotLockID"]
19
+ end
20
+
21
+ def params
22
+ {
23
+ 'RID' => restaurant.id,
24
+ 'datetime' => restaurant_search.ideal_time,
25
+ 'partysize' => restaurant_search.party_size,
26
+ 'timesecurityID' => restaurant_search.ideal_security_id,
27
+ 'resultskey' => restaurant_search.results_key
28
+ }
29
+ end
30
+
31
+ private
32
+ def request_uri
33
+ "/slotlock/?pid=#{Config.partner_id}&st=0"
34
+ end
35
+
36
+ def restaurant
37
+ restaurant_search.restaurant
38
+ end
39
+
40
+ def details
41
+ request.parsed_response["SlotLockResults"]
42
+ end
43
+
44
+ end
45
+ end
@@ -1,3 +1,3 @@
1
1
  module Hungrytable
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/hungrytable.rb CHANGED
@@ -1,9 +1,24 @@
1
- require "uri"
2
- require "json"
3
- require "hungrytable/base"
4
- require "hungrytable/version"
5
- require "hungrytable/oauth_patch"
6
- require "hungrytable/restaurant"
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'
7
21
 
8
22
  module Hungrytable
23
+ class HungrytableError < StandardError;end
9
24
  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 CHANGED
@@ -1,13 +1,11 @@
1
- # Set up simplecov
2
- require 'simplecov'
3
- require 'simplecov-rcov'
4
- SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
5
-
6
1
  require 'minitest/autorun'
7
2
  require 'minitest/reporters'
8
3
 
9
4
  require 'ostruct'
10
5
  require 'pry'
6
+ require 'webmock'
7
+ include WebMock::API
8
+
11
9
 
12
10
  # Load support files
13
11
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
@@ -19,5 +17,5 @@ if ENV['JENKINS']
19
17
  MiniTest::Unit.runner.reporters << MiniTest::Reporters::JUnitReporter.new
20
18
  end
21
19
 
22
- # Load the app
20
+ require 'mocha'
23
21
  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
@@ -13,21 +13,29 @@ describe Hungrytable::Restaurant do
13
13
  end
14
14
  end
15
15
 
16
- describe 'when getting details' do
16
+ describe '#get_details' do
17
17
  before do
18
18
  @restaurant = subject.new
19
+ stub_request(:get, /.*secure.opentable.com\/api\/otapi_v2.ashx\/restaurant.*/).to_return(File.new('./test/restaurant_get_details_result.json'))
19
20
  end
20
-
21
+
21
22
  it 'should return relevant details about an individual restaurant' do
22
23
  response = @restaurant.get_details(82591)
23
24
  response.must_be_kind_of Hash
24
25
  end
26
+ end
25
27
 
28
+ describe '#search' do
29
+ before do
30
+ @restaurant = subject.new
31
+ stub_request(:get, /.*secure.opentable.com\/api\/otapi_v2.ashx\/table.*/).to_return(File.new('./test/restaurant_search_result.json'), :status => 200)
32
+ end
33
+
26
34
  it 'should look for availability at an inidividual restaurant' do
27
35
  date_time = Time.now.strftime "%-m/#{Time.now.day + 1}/%Y %-l:%M %p"
28
36
  response = @restaurant.search({ restaurant_id: 82591, date_time: date_time, party_size: 2 })
29
37
  response.must_be_kind_of Hash
30
38
  end
31
-
32
39
  end
40
+
33
41
  end
@@ -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 CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hungrytable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,55 +9,75 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-11 00:00:00.000000000Z
12
+ date: 2012-07-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: oauth
16
- requirement: &6926620 !ruby/object:Gem::Requirement
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.4.5
21
+ version: 1.7.1
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *6926620
25
- - !ruby/object:Gem::Dependency
26
- name: json
27
- requirement: &6925700 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
28
25
  none: false
29
26
  requirements:
30
27
  - - ~>
31
28
  - !ruby/object:Gem::Version
32
29
  version: 1.7.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: activesupport
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *6925700
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
- name: minitest
38
- requirement: &6924760 !ruby/object:Gem::Requirement
47
+ name: i18n
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
42
52
  - !ruby/object:Gem::Version
43
53
  version: '0'
44
- type: :development
54
+ type: :runtime
45
55
  prerelease: false
46
- version_requirements: *6924760
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  - !ruby/object:Gem::Dependency
48
- name: minitest-reporters
49
- requirement: &6923840 !ruby/object:Gem::Requirement
63
+ name: curb
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
53
68
  - !ruby/object:Gem::Version
54
69
  version: '0'
55
- type: :development
70
+ type: :runtime
56
71
  prerelease: false
57
- version_requirements: *6923840
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: rake
60
- requirement: &6922980 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ! '>='
@@ -65,10 +85,15 @@ dependencies:
65
85
  version: '0'
66
86
  type: :development
67
87
  prerelease: false
68
- version_requirements: *6922980
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
69
94
  - !ruby/object:Gem::Dependency
70
- name: simplecov
71
- requirement: &6922200 !ruby/object:Gem::Requirement
95
+ name: minitest
96
+ requirement: !ruby/object:Gem::Requirement
72
97
  none: false
73
98
  requirements:
74
99
  - - ! '>='
@@ -76,10 +101,15 @@ dependencies:
76
101
  version: '0'
77
102
  type: :development
78
103
  prerelease: false
79
- version_requirements: *6922200
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
80
110
  - !ruby/object:Gem::Dependency
81
- name: simplecov-rcov
82
- requirement: &6921360 !ruby/object:Gem::Requirement
111
+ name: minitest-reporters
112
+ requirement: !ruby/object:Gem::Requirement
83
113
  none: false
84
114
  requirements:
85
115
  - - ! '>='
@@ -87,21 +117,31 @@ dependencies:
87
117
  version: '0'
88
118
  type: :development
89
119
  prerelease: false
90
- version_requirements: *6921360
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
91
126
  - !ruby/object:Gem::Dependency
92
- name: rcov
93
- requirement: &6854660 !ruby/object:Gem::Requirement
127
+ name: mocha
128
+ requirement: !ruby/object:Gem::Requirement
94
129
  none: false
95
130
  requirements:
96
- - - =
131
+ - - ! '>='
97
132
  - !ruby/object:Gem::Version
98
- version: 0.9.11
133
+ version: '0'
99
134
  type: :development
100
135
  prerelease: false
101
- version_requirements: *6854660
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
102
142
  - !ruby/object:Gem::Dependency
103
143
  name: pry
104
- requirement: &6852260 !ruby/object:Gem::Requirement
144
+ requirement: !ruby/object:Gem::Requirement
105
145
  none: false
106
146
  requirements:
107
147
  - - ! '>='
@@ -109,7 +149,28 @@ dependencies:
109
149
  version: '0'
110
150
  type: :development
111
151
  prerelease: false
112
- version_requirements: *6852260
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: webmock
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
113
174
  description: Gem to interact with OpenTable's REST API
114
175
  email:
115
176
  - david@isotope11.com
@@ -120,20 +181,41 @@ files:
120
181
  - .gitignore
121
182
  - .rvmrc
122
183
  - Gemfile
184
+ - Guardfile
123
185
  - LICENSE
124
186
  - README.md
125
187
  - RELEASE_NOTES.md
126
188
  - Rakefile
127
189
  - hungrytable.gemspec
128
190
  - lib/hungrytable.rb
129
- - lib/hungrytable/base.rb
130
- - lib/hungrytable/oauth_patch.rb
191
+ - lib/hungrytable/config.rb
192
+ - lib/hungrytable/get_request.rb
193
+ - lib/hungrytable/post_request.rb
194
+ - lib/hungrytable/request.rb
195
+ - lib/hungrytable/request_extensions.rb
196
+ - lib/hungrytable/request_header.rb
197
+ - lib/hungrytable/reservation_cancel.rb
198
+ - lib/hungrytable/reservation_make.rb
199
+ - lib/hungrytable/reservation_status.rb
131
200
  - lib/hungrytable/restaurant.rb
201
+ - lib/hungrytable/restaurant_search.rb
202
+ - lib/hungrytable/restaurant_slotlock.rb
132
203
  - lib/hungrytable/version.rb
133
- - test/hungrytable_test.rb
134
- - test/support/minitest_junit.rb
204
+ - test/restaurant_get_details_result.json
205
+ - test/restaurant_search_result.json
135
206
  - test/test_helper.rb
207
+ - test/unit/config_test.rb
208
+ - test/unit/get_request_test.rb
136
209
  - test/unit/hungrytable/restaurant_test.rb
210
+ - test/unit/hungrytable/user_test.rb
211
+ - test/unit/post_request_test.rb
212
+ - test/unit/request_test.rb
213
+ - test/unit/reservation_cancel_test.rb
214
+ - test/unit/reservation_make_test.rb
215
+ - test/unit/restaurant_search_test.rb
216
+ - test/unit/restaurant_slotlock_test.rb
217
+ - test/unit/restaurant_test.rb
218
+ - test/user_login_result.json
137
219
  homepage: http://www.isotope11.com/
138
220
  licenses: []
139
221
  post_install_message:
@@ -146,20 +228,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
146
228
  - - ! '>='
147
229
  - !ruby/object:Gem::Version
148
230
  version: '0'
231
+ segments:
232
+ - 0
233
+ hash: -1516024462257141481
149
234
  required_rubygems_version: !ruby/object:Gem::Requirement
150
235
  none: false
151
236
  requirements:
152
237
  - - ! '>='
153
238
  - !ruby/object:Gem::Version
154
239
  version: '0'
240
+ segments:
241
+ - 0
242
+ hash: -1516024462257141481
155
243
  requirements: []
156
244
  rubyforge_project: hungrytable
157
- rubygems_version: 1.8.10
245
+ rubygems_version: 1.8.24
158
246
  signing_key:
159
247
  specification_version: 3
160
248
  summary: Gem to interact with OpenTable's REST API
161
- test_files:
162
- - test/hungrytable_test.rb
163
- - test/support/minitest_junit.rb
164
- - test/test_helper.rb
165
- - test/unit/hungrytable/restaurant_test.rb
249
+ test_files: []
@@ -1,26 +0,0 @@
1
- module Hungrytable
2
- class Base
3
- def validate_environment_variables
4
- raise "Please set the environment variable OT_OAUTH_KEY to your OpenTable OAuth Table." unless ENV['OT_OAUTH_KEY']
5
- raise "Please set the environment variable OT_OAUTH_SECRET to your OpenTable OAuth Secret Key." unless ENV['OT_OAUTH_SECRET']
6
- raise "Please set the environment variable OT_PARTNER_ID to your OpentTable Partner ID." unless ENV['OT_PARTNER_ID']
7
- end
8
-
9
- def parse_uri_option(option_name, options)
10
- val = options[option_name.to_sym]
11
- raise "Missing required option: #{option_name}" if val.nil?
12
- if val.is_a?(String)
13
- val = CGI.escape(val).gsub(/[+]/, '%20')
14
- end
15
- instance_variable_set("@#{option_name}", val)
16
- end
17
-
18
- def parse_uri_options(*option_names, options)
19
- option_names.each { |option_name| parse_uri_option(option_name, options) }
20
- end
21
-
22
- def parse_json(string)
23
- JSON.parse(string)
24
- end
25
- end
26
- end
@@ -1,19 +0,0 @@
1
- require 'oauth'
2
- module OAuth::Client
3
- class Helper
4
- def oauth_parameters
5
- {
6
- 'oauth_body_hash' => options[:body_hash],
7
- 'oauth_callback' => options[:oauth_callback],
8
- 'oauth_consumer_key' => options[:consumer].key,
9
- 'oauth_token' => options[:token] ? options[:token].token : '',
10
- 'oauth_signature_method' => options[:signature_method],
11
- 'oauth_timestamp' => timestamp,
12
- 'oauth_nonce' => nonce,
13
- 'oauth_verifier' => options[:oauth_verifier],
14
- 'oauth_version' => (options[:oauth_version] || '1.0'),
15
- 'oauth_session_handle' => options[:oauth_session_handle]
16
- }.reject { |k,v| v.to_s == "" && k.to_s != 'oauth_token' }
17
- end
18
- end
19
- end
@@ -1,8 +0,0 @@
1
- require 'test_helper'
2
-
3
- describe Hungrytable do
4
- subject { Hungrytable }
5
- it 'should be a class' do
6
- subject.class.must_equal Module
7
- end
8
- end
@@ -1,81 +0,0 @@
1
- require 'ansi'
2
-
3
- module MiniTest
4
- module Reporters
5
- # A reporter identical to the standard MiniTest reporter.
6
- #
7
- # Based upon Ryan Davis of Seattle.rb's MiniTest (MIT License).
8
- #
9
- # @see https://github.com/seattlerb/minitest MiniTest
10
- class JUnitReporter
11
- include MiniTest::Reporter
12
-
13
- def before_suites(suites, type)
14
- end
15
-
16
- def before_test(suite, test)
17
- end
18
-
19
- def pass(suite, test, test_runner)
20
- end
21
-
22
- def skip(suite, test, test_runner)
23
- end
24
-
25
- def failure(suite, test, test_runner)
26
- end
27
-
28
- def error(suite, test, test_runner)
29
- end
30
-
31
- def after_suites(suites, type)
32
- time = Time.now - runner.start_time
33
- i = 0
34
- runner.report.each do |suite, tests|
35
- File.open("#{suite.to_s.gsub(" ", '_').downcase}.xml", 'w') do |out|
36
- out.puts '<?xml version="1.0" encoding="UTF-8" ?>'
37
- out.puts "<testsuite errors=\"#{runner.errors}\" failures=\"#{runner.failures}\" tests=\"#{runner.test_count}\" time=\"#{time}\" timestamp=\"#{runner.start_time.strftime('%FT%R')}\">"
38
- tests.each do |test, test_runner|
39
- out.print "<testcase classname=\"#{suite}\" name=\"#{test}\" time=\"0\">"
40
- message = message_for(test_runner)
41
- out.puts "\n#{message}" if message
42
- out.puts '</testcase>'
43
- end
44
- out.puts '</testsuite>'
45
- end
46
- end
47
- end
48
-
49
- private
50
- def location(exception)
51
- last_before_assertion = ''
52
-
53
- exception.backtrace.reverse_each do |s|
54
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
55
- last_before_assertion = s
56
- end
57
-
58
- last_before_assertion.sub(/:in .*$/, '')
59
- end
60
-
61
- def message_for(test_runner)
62
- suite = test_runner.suite
63
- test = test_runner.test
64
- e = test_runner.exception
65
-
66
- case test_runner.result
67
- when :pass then nil
68
- when :skip then "<skipped message=\"\"/>"
69
- when :failure then "<failure message=\"#{e.message}\" type=\"failure\">#{location(e)}:\n#{e.message}\n</failure>"
70
- when :error then "<error message=\"#{e.message}\" type=\"error\">#{location(e)}:\n#{e.message}\n</error>"
71
- end
72
- end
73
-
74
- def status
75
- '%d tests, %d assertions, %d failures, %d errors, %d skips' %
76
- [runner.test_count, runner.assertion_count, runner.failures, runner.errors, runner.skips]
77
- end
78
- end
79
- end
80
- end
81
-