suitcase 1.2.12 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|