hungrytable 0.0.1 → 0.0.2

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.
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
-