suitcase 1.2.12 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -1
- data/README.md +54 -11
- data/examples/redis_adapter.rb +29 -0
- data/lib/suitcase/cache.rb +19 -4
- data/lib/suitcase/car_rental.rb +51 -0
- data/lib/suitcase/configuration.rb +40 -0
- data/lib/suitcase/hotel/helpers.rb +131 -0
- data/lib/suitcase/hotel/hotel.rb +1 -0
- data/lib/suitcase/version.rb +1 -1
- data/lib/suitcase.rb +10 -4
- data/spec/car_rental_spec.rb +20 -0
- data/spec/helpers_spec.rb +25 -4
- data/spec/keys.rb +36 -0
- metadata +52 -22
- data/lib/suitcase/helpers.rb +0 -44
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -6,30 +6,73 @@ Ruby library that utilizes the EAN (Expedia.com) API for locating available hote
|
|
6
6
|
Installation
|
7
7
|
------------
|
8
8
|
|
9
|
-
|
9
|
+
Suitcase is a Ruby gem, meaning it can be installed via a simple `gem install suitcase`. It can also be added to a project's `Gemfile` with the following line: `gem 'suitcase'` (or `gem 'suitcase', git: "git://github.com/thoughtfusion/suitcase.git", branch: "development"` for the latest updates).
|
10
10
|
|
11
11
|
Usage
|
12
12
|
-----
|
13
13
|
|
14
|
+
### Hotels
|
15
|
+
|
14
16
|
First, configure the library:
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
```ruby
|
19
|
+
Suitcase.configure do |config|
|
20
|
+
config.hotel_api_key = "..." # set the Hotel API key from developer.ean.com
|
21
|
+
config.hotel_cid = "..." # set the CID from developer.ean.com
|
22
|
+
config.cache = Hash.new # set the caching mechanism (see below)
|
23
|
+
end
|
24
|
+
```
|
19
25
|
|
20
|
-
|
26
|
+
Full example:
|
27
|
+
```ruby
|
28
|
+
# Find Hotels in Boston
|
29
|
+
hotels = Suitcase::Hotel.find(location: "Boston, MA")
|
30
|
+
# Pick a specific hotel
|
31
|
+
hotel = hotels[1]
|
32
|
+
# Get the rooms for a specific date
|
33
|
+
rooms = hotel.rooms(arrival: "7/1/2013", departure: "7/8/2013", rooms: [{ adults: 1, children_ages: [2, 3] }, { adults: 1, children_ages: [4] }])
|
34
|
+
# Find a payment option that is compatible with USD
|
35
|
+
payment_option = Suitcase::PaymentOption.find(currency_code: "USD")
|
36
|
+
# Pick a specific room
|
37
|
+
room = rooms.first
|
38
|
+
# Set the bed type on each of the rooms to be ordered
|
39
|
+
room.rooms.each { |r| r[:bed_type] = room.bed_types.first }
|
40
|
+
# Reserve the room, with the reservation_hash described on 'User flow'
|
41
|
+
room.reserve!(reservation_hash)
|
42
|
+
```
|
21
43
|
|
22
|
-
|
23
|
-
room = hotels.first.rooms(arrival: "2/19/2012", departure: "2/26/2012", rooms: [{ children: 1, ages: [8] }, { children: 1, ages: [12] }] # Check the availability of two rooms at that hotel with 1 child in each room of ages 8 and 9
|
24
|
-
room.reserve!(info) # See wiki page "User flow" for options
|
44
|
+
#### Caching requests
|
25
45
|
|
26
|
-
|
46
|
+
You can setup a cache to store all API requests that do not contain secure information (i.e. anything but booking requests). A cache needs to be able store deeply nested Hashes and have a method called [] to access them. An example of setting the cache is given above. Check the `examples/hash_adapter.rb` for a trivial example of the required methods. A Redis adapter should be coming soon.
|
27
47
|
|
28
|
-
#### Requests
|
29
48
|
|
30
|
-
|
49
|
+
### Car rentals
|
50
|
+
|
51
|
+
Add the required configuration options:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# Or add to your existing configure block
|
55
|
+
Suitcase.configure do |config|
|
56
|
+
config.hotwire_api_key = "..." # set the Hotwire API key
|
57
|
+
config.hotwire_linkshare_id = "..." # optionally set the Hotwire linkshare ID
|
58
|
+
end
|
59
|
+
```
|
31
60
|
|
61
|
+
Example usage:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
# Find all rental cars from the specified dates/times at LAX
|
65
|
+
rentals = Suitcase::CarRental.find(destination: "LAX", start_date: "7/14/2012", end_date: "7/21/2012", pickup_time: "6:30", dropoff_time: "11:30")
|
66
|
+
# => [#<Suitcase::CarRental ...>, ...]
|
67
|
+
```
|
32
68
|
|
33
69
|
Contributing
|
34
70
|
------------
|
71
|
+
|
72
|
+
### Running the specs
|
73
|
+
|
74
|
+
To set up for the specs, you need to edit the file `spec/keys.rb` with the proper information. Currently, testing reservations is unsupported. You can run the specs with the default rake task by running `rake` from the command line.
|
75
|
+
|
76
|
+
### Pull requests/issues
|
77
|
+
|
35
78
|
Please submit any useful pull requests through GitHub. If you find any bugs, please report them with the issue tracker! Thanks.
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class RedisAdapter
|
2
|
+
def initialize
|
3
|
+
uri = URI.parse(ENV["REDIS_URI"])
|
4
|
+
@redis = Redis.new(:host => uri.host,
|
5
|
+
:port => uri.port,
|
6
|
+
:password => uri.password)
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](key)
|
10
|
+
stored = @redis.hgetall("ean_cache:#{key}")
|
11
|
+
stored.inject({}) {|mem, (k, v)|
|
12
|
+
mem[JSON.parse(k)] = JSON.parse(v)
|
13
|
+
mem
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(key, value)
|
18
|
+
value.each_pair do |params, response|
|
19
|
+
@redis.hset("ean_cache:#{key}", params.to_json, response.to_json)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def keys
|
24
|
+
@redis.keys("ean_cache:*").collect {|key|
|
25
|
+
key.match(/ean_cache:(.*)/)[1] rescue nil
|
26
|
+
}.compact!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/lib/suitcase/cache.rb
CHANGED
@@ -11,12 +11,16 @@ module Suitcase
|
|
11
11
|
params.delete(param)
|
12
12
|
end
|
13
13
|
params.delete("currencyCode") unless action == :paymentInfo
|
14
|
+
|
15
|
+
string_params = keys_to_strings(params)
|
16
|
+
|
14
17
|
@store[action] ||= {}
|
15
|
-
@store[action][
|
18
|
+
@store[action] = @store[action].merge(string_params => response)
|
16
19
|
end
|
17
20
|
|
18
21
|
def get_query(action, params)
|
19
|
-
|
22
|
+
string_params = keys_to_strings(params)
|
23
|
+
@store[action] ? @store[action][string_params] : nil
|
20
24
|
end
|
21
25
|
|
22
26
|
def keys
|
@@ -24,11 +28,22 @@ module Suitcase
|
|
24
28
|
end
|
25
29
|
|
26
30
|
def cached?(action, params)
|
27
|
-
|
31
|
+
string_params = keys_to_strings(params)
|
32
|
+
@store[action] && @store[action][string_params]
|
28
33
|
end
|
29
34
|
|
30
35
|
def undefine_query(action, params)
|
31
|
-
|
36
|
+
string_params = keys_to_strings(params)
|
37
|
+
@store[action].delete(string_params) if @store[action]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def keys_to_strings(hash)
|
43
|
+
hash.inject({}) {|memo, (k, v)|
|
44
|
+
memo[k.to_s] = v
|
45
|
+
memo
|
46
|
+
}
|
32
47
|
end
|
33
48
|
end
|
34
49
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Suitcase
|
2
|
+
class CarRental
|
3
|
+
attr_accessor :seating, :type_name, :type_code, :possible_features, :possible_models
|
4
|
+
|
5
|
+
def initialize(data)
|
6
|
+
@seating = data["CarTypeSeating"]
|
7
|
+
@type_name = data["CarTypeName"]
|
8
|
+
@type_code = data["CarTypeCode"]
|
9
|
+
@possible_features = data["PossibleFeatures"].split(", ")
|
10
|
+
@possible_models = data["PossibleModels"].split(", ")
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def find(info)
|
15
|
+
parsed = parse_json(build_url(info))
|
16
|
+
parse_errors(parsed)
|
17
|
+
parsed["MetaData"]["CarMetaData"]["CarTypes"].map { |data| CarRental.new(data) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def build_url(info)
|
21
|
+
base_url = "http://api.hotwire.com/v1/search/car"
|
22
|
+
info["apikey"] = Configuration.hotwire_api_key
|
23
|
+
info["format"] = "JSON"
|
24
|
+
if Configuration.use_hotwire_linkshare_id?
|
25
|
+
info["linkshareid"] = Configuration.hotwire_linkshare_id
|
26
|
+
end
|
27
|
+
info["dest"] = info.delete(:destination)
|
28
|
+
info["startdate"] = info.delete(:start_date)
|
29
|
+
info["enddate"] = info.delete(:end_date)
|
30
|
+
info["pickuptime"] = info.delete(:pickup_time)
|
31
|
+
info["dropofftime"] = info.delete(:dropoff_time)
|
32
|
+
base_url += "?" + parameterize(info)
|
33
|
+
URI.parse(URI.escape(base_url))
|
34
|
+
end
|
35
|
+
|
36
|
+
def parameterize(info)
|
37
|
+
info.map { |key, value| "#{key}=#{value}" }.join("&")
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_json(uri)
|
41
|
+
response = Net::HTTP.get_response(uri)
|
42
|
+
raise "Data not valid." if response.code != "200"
|
43
|
+
JSON.parse(response.body)
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_errors(parsed)
|
47
|
+
parsed["Errors"].each { |e| raise e } if parsed["Errors"] && !parsed["Errors"].empty?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,4 +1,8 @@
|
|
1
1
|
module Suitcase
|
2
|
+
def self.configure(&block)
|
3
|
+
block.call(Configuration)
|
4
|
+
end
|
5
|
+
|
2
6
|
class Configuration
|
3
7
|
def self.cache=(store)
|
4
8
|
@@cache = Suitcase::Cache.new(store)
|
@@ -27,5 +31,41 @@ module Suitcase
|
|
27
31
|
def self.hotel_cid
|
28
32
|
@@hotel_cid if defined? @@hotel_cid
|
29
33
|
end
|
34
|
+
|
35
|
+
def self.hotel_shared_secret=(secret)
|
36
|
+
@@hotel_shared_secret = secret
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.hotel_shared_secret
|
40
|
+
@@hotel_shared_secret if defined? @@hotel_shared_secret
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.use_signature_auth=(choice)
|
44
|
+
@@use_signature_authentication = choice
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.use_signature_auth?
|
48
|
+
defined? @@use_signature_auth
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.hotwire_api_key=(api_key)
|
52
|
+
@@hotwire_api_key = api_key
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.hotwire_api_key
|
56
|
+
@@hotwire_api_key if defined? @@hotwire_api_key
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.hotwire_linkshare_id=(id)
|
60
|
+
@@hotwire_linkshare_id = id
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.hotwire_linkshare_id
|
64
|
+
@@hotwire_linkshare_id if use_hotwire_lnikshare_id?
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.use_hotwire_linkshare_id?
|
68
|
+
defined? @@hotwire_linkshare_id
|
69
|
+
end
|
30
70
|
end
|
31
71
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module Suitcase
|
2
|
+
# Internal: Various methods for doing things that many files need to in the
|
3
|
+
# library.
|
4
|
+
#
|
5
|
+
# Examples
|
6
|
+
#
|
7
|
+
# parameterize(something: "else", another: "thing")
|
8
|
+
# # => "something=else&another=thing"
|
9
|
+
module Helpers
|
10
|
+
# Internal: Defaults for the builder options to Helpers#url.
|
11
|
+
URL_DEFAULTS = {
|
12
|
+
include_key: true,
|
13
|
+
include_cid: true,
|
14
|
+
secure: false,
|
15
|
+
as_form: false,
|
16
|
+
session: Session.new
|
17
|
+
}
|
18
|
+
|
19
|
+
# Internal: Parameterize a Hash.
|
20
|
+
#
|
21
|
+
# hash - The Hash to be parameterized.
|
22
|
+
#
|
23
|
+
# Examples
|
24
|
+
#
|
25
|
+
# parameterize(something: "else", another: "thing")
|
26
|
+
# # => "something=else&another=thing"
|
27
|
+
#
|
28
|
+
# Returns a parameterized String.
|
29
|
+
def parameterize(hash)
|
30
|
+
hash.map { |key, value| "#{key}=#{value}" }.join "&"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Internal: Build an API URL.
|
34
|
+
#
|
35
|
+
# builder - A Hash with the following required keys:
|
36
|
+
# :method - The API method to be put in the URL.
|
37
|
+
# :params - The params to be put in the URL.
|
38
|
+
# And the following optional ones:
|
39
|
+
# :include_key - Whether to include the API key or not.
|
40
|
+
# :include_cid - Whether to include the API cid or not.
|
41
|
+
# :secure - Whether or not for the request to be sent over
|
42
|
+
# HTTPS.
|
43
|
+
# :as_form - Whether or not to include the parameters in the
|
44
|
+
# URL.
|
45
|
+
# :session - The Session associated with the request.
|
46
|
+
#
|
47
|
+
# Examples
|
48
|
+
#
|
49
|
+
# url(secure: true, as_form: true, method: "myMethod", params: {})
|
50
|
+
# # => #<URI::HTTPS URL:https://book.api.ean.com/.../rs/hotel/v3/myMethod>
|
51
|
+
def url(builder)
|
52
|
+
builder = URL_DEFAULTS.merge(builder)
|
53
|
+
builder[:session] ||= URL_DEFAULTS[:session]
|
54
|
+
method, params = builder[:method], builder[:params]
|
55
|
+
params["apiKey"] = Configuration.hotel_api_key if builder[:include_key]
|
56
|
+
params["cid"] = (Configuration.hotel_cid ||= 55505) if builder[:include_cid]
|
57
|
+
params["sig"] = generate_signature if Configuration.use_signature_auth?
|
58
|
+
|
59
|
+
url = main_url(builder[:secure]) + method.to_s + (builder[:as_form] ? "" : "?")
|
60
|
+
|
61
|
+
params.merge!(build_session_params(builder[:session]))
|
62
|
+
url += parameterize(params) unless builder[:as_form]
|
63
|
+
URI.parse(URI.escape(url))
|
64
|
+
end
|
65
|
+
|
66
|
+
# Internal: Get the root URL for the Hotels API.
|
67
|
+
#
|
68
|
+
# secure - Whether or not the URL should be HTTPS or not.
|
69
|
+
#
|
70
|
+
# Returns the URL.
|
71
|
+
def main_url(secure)
|
72
|
+
url = "http#{secure ? "s" : ""}://#{secure ? "book." : ""}"
|
73
|
+
url += "api.ean.com/ean-services/rs/hotel/v3/"
|
74
|
+
url
|
75
|
+
end
|
76
|
+
|
77
|
+
# Internal: Parse the JSON response at the given URL.
|
78
|
+
#
|
79
|
+
# uri - The URI to parse the JSON from.
|
80
|
+
#
|
81
|
+
# Returns the parsed JSON.
|
82
|
+
def parse_response(uri)
|
83
|
+
JSON.parse(Net::HTTP.get_response(uri).body)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Internal: Get the base URL based on a Hash of info.
|
87
|
+
#
|
88
|
+
# info - A Hash with only one required key:
|
89
|
+
# :booking - Whether or not it is a booking request.
|
90
|
+
#
|
91
|
+
# Returns the base URL.
|
92
|
+
def base_url(info)
|
93
|
+
main_url(info[:booking])
|
94
|
+
end
|
95
|
+
|
96
|
+
# Internal: Build a Hash of params from a Session.
|
97
|
+
#
|
98
|
+
# session - The Session to generate params from.
|
99
|
+
#
|
100
|
+
# Returns the Hash of params.
|
101
|
+
def build_session_params(session)
|
102
|
+
session_info = {}
|
103
|
+
session_info["customerSessionId"] = session.id if session.id
|
104
|
+
session_info["customerIpAddress"] = session.ip_address if session.ip_address
|
105
|
+
session_info["locale"] = session.locale if session.locale
|
106
|
+
session_info["currencyCode"] = session.currency_code if session.currency_code
|
107
|
+
session_info["customerUserAgent"] = session.user_agent if session.user_agent
|
108
|
+
session_info
|
109
|
+
end
|
110
|
+
|
111
|
+
# Internal: Update the Session's ID with the parsed JSON.
|
112
|
+
#
|
113
|
+
# parsed - The parsed JSON to fetch the ID from.
|
114
|
+
# session - The Session to update.
|
115
|
+
#
|
116
|
+
# Returns nothing.
|
117
|
+
def update_session(parsed, session)
|
118
|
+
session ||= Suitcase::Session.new
|
119
|
+
session.id = parsed[parsed.keys.first]["customerSessionId"] if parsed[parsed.keys.first]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Internal: Generate a digital signature to authenticate with.
|
123
|
+
#
|
124
|
+
# Returns the generated signature.
|
125
|
+
def generate_signature
|
126
|
+
Digest::MD5.hexdigest(Configuration.hotel_api_key +
|
127
|
+
Configuration.hotel_shared_secret +
|
128
|
+
Time.now.to_i.to_s)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/suitcase/hotel/hotel.rb
CHANGED
@@ -145,6 +145,7 @@ module Suitcase
|
|
145
145
|
parsed_info[:location_description] = summary["locationDescription"]
|
146
146
|
parsed_info[:number_of_rooms] = parsed["HotelInformationResponse"]["HotelDetails"]["numberOfRooms"] if parsed["HotelInformationResponse"]
|
147
147
|
parsed_info[:number_of_floors] = parsed["HotelInformationResponse"]["HotelDetails"]["numberOfFloors"] if parsed["HotelInformationResponse"]
|
148
|
+
parsed_info[:short_description] = summary["shortDescription"]
|
148
149
|
parsed_info
|
149
150
|
end
|
150
151
|
|
data/lib/suitcase/version.rb
CHANGED
data/lib/suitcase.rb
CHANGED
@@ -1,15 +1,19 @@
|
|
1
1
|
require 'patron'
|
2
2
|
require 'json'
|
3
3
|
require 'net/http'
|
4
|
+
require 'digest/md5'
|
4
5
|
require 'date/format'
|
5
6
|
require 'time'
|
6
|
-
|
7
|
-
require 'suitcase/core_ext/string'
|
7
|
+
|
8
8
|
require 'suitcase/version'
|
9
|
+
|
10
|
+
require 'suitcase/core_ext/string'
|
11
|
+
|
12
|
+
require 'suitcase/session'
|
9
13
|
require 'suitcase/configuration'
|
10
|
-
# require 'suitcase/session_cache'
|
11
14
|
require 'suitcase/cache'
|
12
|
-
|
15
|
+
|
16
|
+
require 'suitcase/hotel/helpers'
|
13
17
|
require 'suitcase/hotel/location'
|
14
18
|
require 'suitcase/hotel/amenity'
|
15
19
|
require 'suitcase/hotel/reservation'
|
@@ -18,3 +22,5 @@ require 'suitcase/hotel/room'
|
|
18
22
|
require 'suitcase/hotel/payment_option'
|
19
23
|
require 'suitcase/hotel/hotel'
|
20
24
|
require 'suitcase/hotel/image'
|
25
|
+
|
26
|
+
require 'suitcase/car_rental'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Suitcase::CarRental do
|
4
|
+
before :each do
|
5
|
+
@rentals = Suitcase::CarRental.find(destination: "Seattle", start_date: "07/04/2012", end_date: "07/11/2012", pickup_time: "07:30", dropoff_time: "07:30")
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.find' do
|
9
|
+
it 'returns an Array of CarRentals' do
|
10
|
+
@rentals.class.should eq Array
|
11
|
+
@rentals.first.class.should eq Suitcase::CarRental
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
subject { @rentals.first }
|
16
|
+
|
17
|
+
[:seating, :type_name, :type_code, :possible_features, :possible_models].each do |method|
|
18
|
+
it { should respond_to method }
|
19
|
+
end
|
20
|
+
end
|
data/spec/helpers_spec.rb
CHANGED
@@ -5,10 +5,22 @@ describe Suitcase::Helpers do
|
|
5
5
|
class Dummy; extend Suitcase::Helpers; end
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
describe "#url" do
|
9
|
+
it "should return a URI with the correct base URL" do
|
10
|
+
returned = Dummy.url(:method => "action", :params => {})
|
11
|
+
returned.should be_a(URI)
|
12
|
+
returned.host.should match(/api.ean.com/)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when using digital signature authentication" do
|
16
|
+
it "should add a 'sig' parameter" do
|
17
|
+
Suitcase::Configuration.stub(:use_signature_auth?) { true }
|
18
|
+
Dummy.stub(:generate_signature) { "test" }
|
19
|
+
|
20
|
+
returned = Dummy.url(:method => "action", :params => {})
|
21
|
+
returned.query.should match(/sig=test/)
|
22
|
+
end
|
23
|
+
end
|
12
24
|
end
|
13
25
|
|
14
26
|
it "#parse_response should raise an exception when passed an invalid URI" do
|
@@ -16,4 +28,13 @@ describe Suitcase::Helpers do
|
|
16
28
|
Dummy.parse_response(URI.parse "http://google.com")
|
17
29
|
end.should raise_error
|
18
30
|
end
|
31
|
+
|
32
|
+
it "#generate_signature should return the encrypted api key, shared secret, and timestamp" do
|
33
|
+
Suitcase::Configuration.stub(:hotel_api_key) { "abc" }
|
34
|
+
Suitcase::Configuration.stub(:hotel_shared_secret) { "123" }
|
35
|
+
Time.stub(:now) { "10" }
|
36
|
+
|
37
|
+
returned = Dummy.generate_signature
|
38
|
+
returned.should == Digest::MD5.hexdigest("abc12310")
|
39
|
+
end
|
19
40
|
end
|
data/spec/keys.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Suitcase::Configuration.hotel_api_key = "your_api_key_here"
|
2
|
+
# Suitcase::Configuration.hotel_cid = "your_cid_here"
|
3
|
+
# Suitcase::Configuration.hotel_shared_secret = "your_shared_secret_here"
|
4
|
+
# Suitcase::Configuration.use_signature_auth = true
|
5
|
+
|
6
|
+
# Or configure with a block:
|
7
|
+
|
8
|
+
Suitcase.configure do |config|
|
9
|
+
config.hotel_api_key = "your_api_key_here"
|
10
|
+
config.hotel_cid = "your_cid_here"
|
11
|
+
# config.hotel_shared_secret = "none"
|
12
|
+
# config.use_signature_auth = false
|
13
|
+
end
|
14
|
+
|
15
|
+
module Keys
|
16
|
+
SUITCASE_PAYMENT_OPTION = Suitcase::PaymentOption.find(currency_code: "USD")[0]
|
17
|
+
CREDIT_CARD_NUMBER_TESTING = "1234123412341234"
|
18
|
+
CREDIT_CARD_CVV_TESTING = "123"
|
19
|
+
CREDIT_CARD_EXPIRATION_DATE_TESTING = "3/14"
|
20
|
+
|
21
|
+
VALID_RESERVATION_INFO = {
|
22
|
+
email: "testemail@gmail.com",
|
23
|
+
first_name: "Test",
|
24
|
+
last_name: "User",
|
25
|
+
home_phone: "1231231234",
|
26
|
+
payment_option: SUITCASE_PAYMENT_OPTION,
|
27
|
+
credit_card_number: CREDIT_CARD_NUMBER_TESTING,
|
28
|
+
credit_card_verification_code: "123",
|
29
|
+
credit_card_expiration_date: CREDIT_CARD_EXPIRATION_DATE_TESTING,
|
30
|
+
address1: "1 Some Place",
|
31
|
+
city: "Boston",
|
32
|
+
province: "MA",
|
33
|
+
country: "US",
|
34
|
+
postal_code: "01234",
|
35
|
+
}
|
36
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: suitcase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: rake
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
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
47
|
name: factory_girl
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
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
63
|
name: pry
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
@@ -54,10 +69,15 @@ dependencies:
|
|
54
69
|
version: '0'
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
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: json
|
60
|
-
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: :runtime
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
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
95
|
name: patron
|
71
|
-
requirement:
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
72
97
|
none: false
|
73
98
|
requirements:
|
74
99
|
- - ! '>='
|
@@ -76,7 +101,12 @@ dependencies:
|
|
76
101
|
version: '0'
|
77
102
|
type: :runtime
|
78
103
|
prerelease: false
|
79
|
-
version_requirements:
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
80
110
|
description: Ruby library that utilizes the EAN (Expedia.com) API for locating available
|
81
111
|
hotels (and maybe rental cars and flights someday, too).
|
82
112
|
email:
|
@@ -92,14 +122,16 @@ files:
|
|
92
122
|
- README.md
|
93
123
|
- Rakefile
|
94
124
|
- examples/hash_adapter.rb
|
125
|
+
- examples/redis_adapter.rb
|
95
126
|
- lib/suitcase.rb
|
96
127
|
- lib/suitcase/airport_codes.rb
|
97
128
|
- lib/suitcase/cache.rb
|
129
|
+
- lib/suitcase/car_rental.rb
|
98
130
|
- lib/suitcase/configuration.rb
|
99
131
|
- lib/suitcase/core_ext/string.rb
|
100
132
|
- lib/suitcase/country_codes.rb
|
101
|
-
- lib/suitcase/helpers.rb
|
102
133
|
- lib/suitcase/hotel/amenity.rb
|
134
|
+
- lib/suitcase/hotel/helpers.rb
|
103
135
|
- lib/suitcase/hotel/hotel.rb
|
104
136
|
- lib/suitcase/hotel/image.rb
|
105
137
|
- lib/suitcase/hotel/location.rb
|
@@ -110,10 +142,12 @@ files:
|
|
110
142
|
- lib/suitcase/session.rb
|
111
143
|
- lib/suitcase/version.rb
|
112
144
|
- spec/caching_spec.rb
|
145
|
+
- spec/car_rental_spec.rb
|
113
146
|
- spec/helpers_spec.rb
|
114
147
|
- spec/hotel_location_spec.rb
|
115
148
|
- spec/hotels_spec.rb
|
116
149
|
- spec/images_spec.rb
|
150
|
+
- spec/keys.rb
|
117
151
|
- spec/payment_options_spec.rb
|
118
152
|
- spec/reservation_spec.rb
|
119
153
|
- spec/rooms_spec.rb
|
@@ -132,30 +166,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
132
166
|
- - ! '>='
|
133
167
|
- !ruby/object:Gem::Version
|
134
168
|
version: '0'
|
135
|
-
segments:
|
136
|
-
- 0
|
137
|
-
hash: 1832860835282660065
|
138
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
170
|
none: false
|
140
171
|
requirements:
|
141
172
|
- - ! '>='
|
142
173
|
- !ruby/object:Gem::Version
|
143
174
|
version: '0'
|
144
|
-
segments:
|
145
|
-
- 0
|
146
|
-
hash: 1832860835282660065
|
147
175
|
requirements: []
|
148
176
|
rubyforge_project: suitcase
|
149
|
-
rubygems_version: 1.8.
|
177
|
+
rubygems_version: 1.8.21
|
150
178
|
signing_key:
|
151
179
|
specification_version: 3
|
152
180
|
summary: Locates available hotels with the Expedia API
|
153
181
|
test_files:
|
154
182
|
- spec/caching_spec.rb
|
183
|
+
- spec/car_rental_spec.rb
|
155
184
|
- spec/helpers_spec.rb
|
156
185
|
- spec/hotel_location_spec.rb
|
157
186
|
- spec/hotels_spec.rb
|
158
187
|
- spec/images_spec.rb
|
188
|
+
- spec/keys.rb
|
159
189
|
- spec/payment_options_spec.rb
|
160
190
|
- spec/reservation_spec.rb
|
161
191
|
- spec/rooms_spec.rb
|
data/lib/suitcase/helpers.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
module Suitcase
|
2
|
-
module Helpers
|
3
|
-
def url(builder)
|
4
|
-
builder[:include_key] ||= true
|
5
|
-
builder[:include_cid] ||= true
|
6
|
-
builder[:secure] ||= false
|
7
|
-
builder[:as_form] ||= false
|
8
|
-
builder[:session] ||= Suitcase::Session.new
|
9
|
-
method, params, include_key, include_cid = builder[:method], builder[:params], builder[:include_key], builder[:include_cid]
|
10
|
-
params["apiKey"] = Configuration.hotel_api_key if include_key
|
11
|
-
params["cid"] = (Configuration.hotel_cid ||= 55505) if include_cid
|
12
|
-
url = main_url(builder[:secure]) + method.to_s + (builder[:as_form] ? "" : "?")
|
13
|
-
session_info = {}
|
14
|
-
session_info["customerSessionId"] = builder[:session].id if builder[:session].id
|
15
|
-
session_info["customerIpAddress"] = builder[:session].ip_address if builder[:session].ip_address
|
16
|
-
session_info["locale"] = builder[:session].locale if builder[:session].locale
|
17
|
-
session_info["currencyCode"] = builder[:session].currency_code if builder[:session].currency_code
|
18
|
-
session_info["customerUserAgent"] = builder[:session].user_agent if builder[:session].user_agent
|
19
|
-
url += params.merge(session_info).map { |key, value| "#{key}=#{value}"}.join("&") unless builder[:as_form]
|
20
|
-
URI.parse(URI.escape(url))
|
21
|
-
end
|
22
|
-
|
23
|
-
def main_url(secure)
|
24
|
-
"http#{secure ? "s" : ""}://#{secure ? "book." : ""}api.ean.com/ean-services/rs/hotel/v3/"
|
25
|
-
end
|
26
|
-
|
27
|
-
def parse_response(uri)
|
28
|
-
JSON.parse(Net::HTTP.get_response(uri).body)
|
29
|
-
end
|
30
|
-
|
31
|
-
def base_url(info)
|
32
|
-
if info[:booking]
|
33
|
-
URI.parse main_url(true)
|
34
|
-
else
|
35
|
-
URI.parse main_url(false)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def update_session(parsed, session)
|
40
|
-
session ||= Suitcase::Session.new
|
41
|
-
session.id = parsed[parsed.keys.first]["customerSessionId"] if parsed[parsed.keys.first]
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|