suitcase 1.2.0.pre → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +7 -0
- data/lib/suitcase.rb +3 -0
- data/lib/suitcase/configuration.rb +12 -1
- data/lib/suitcase/hotel/amenity.rb +9 -0
- data/lib/suitcase/hotel/hotel.rb +54 -13
- data/lib/suitcase/session.rb +34 -0
- data/lib/suitcase/session_cache.rb +27 -0
- data/lib/suitcase/version.rb +1 -1
- metadata +22 -16
data/README.md
CHANGED
@@ -16,6 +16,7 @@ First, configure the library:
|
|
16
16
|
Suitcase::Configuration.hotel_api_key = "..." # set the Hotel API key from developer.ean.com
|
17
17
|
Suitcase::Configuration.cid = "..." # set the CID from developer.ean.com
|
18
18
|
Suitcase::Configuration.cache = Hash.new # set the caching mechanism (see below)
|
19
|
+
Suitcase::Configuration.session_cache = Hash.new # set the session caching store (see below)
|
19
20
|
|
20
21
|
Find nearby hotels:
|
21
22
|
|
@@ -25,8 +26,14 @@ Find nearby hotels:
|
|
25
26
|
|
26
27
|
### Caching
|
27
28
|
|
29
|
+
#### Requests
|
30
|
+
|
28
31
|
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.
|
29
32
|
|
33
|
+
#### Sessions
|
34
|
+
|
35
|
+
You **must** also set up a cache to store user sessions. A session object simply stores the internal ID used by Expedia, the user agent, the IP address of the user, the user's locale, and the user's currency code. The store is configured similarly to the query store, and have the same requirements.
|
36
|
+
|
30
37
|
Contributing
|
31
38
|
------------
|
32
39
|
Please submit any useful pull requests through GitHub. If you find any bugs, please report them with the issue tracker! Thanks.
|
data/lib/suitcase.rb
CHANGED
@@ -3,11 +3,14 @@ require 'json'
|
|
3
3
|
require 'net/http'
|
4
4
|
require 'date/format'
|
5
5
|
require 'time'
|
6
|
+
require 'suitcase/session'
|
6
7
|
require 'suitcase/core_ext/string'
|
7
8
|
require 'suitcase/version'
|
8
9
|
require 'suitcase/configuration'
|
10
|
+
require 'suitcase/session_cache'
|
9
11
|
require 'suitcase/cache'
|
10
12
|
require 'suitcase/helpers'
|
13
|
+
require 'suitcase/hotel/amenity'
|
11
14
|
require 'suitcase/hotel/reservation'
|
12
15
|
require 'suitcase/hotel/nightly_rate'
|
13
16
|
require 'suitcase/hotel/room'
|
@@ -6,7 +6,6 @@ module Suitcase
|
|
6
6
|
|
7
7
|
def self.cache
|
8
8
|
return @@cache if cache?
|
9
|
-
nil
|
10
9
|
end
|
11
10
|
|
12
11
|
def self.cache?
|
@@ -28,5 +27,17 @@ module Suitcase
|
|
28
27
|
def self.hotel_cid
|
29
28
|
@@hotel_cid if defined? @@hotel_cid
|
30
29
|
end
|
30
|
+
|
31
|
+
def self.session_cache=(store)
|
32
|
+
@@session_cache = Suitcase::SessionCache.new(store)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.session_cache
|
36
|
+
return @@session_cache if session_cache?
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.session_cache?
|
40
|
+
defined? @@session_cache
|
41
|
+
end
|
31
42
|
end
|
32
43
|
end
|
data/lib/suitcase/hotel/hotel.rb
CHANGED
@@ -5,6 +5,7 @@ module Suitcase
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
+
# Used for organizing Bed options
|
8
9
|
class BedType
|
9
10
|
attr_accessor :id, :description
|
10
11
|
|
@@ -13,6 +14,10 @@ module Suitcase
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
17
|
+
# A Class representing a Hotel that stores information
|
18
|
+
# about the hotel. It provides methods for checking
|
19
|
+
# room availability, fetching images and just general
|
20
|
+
# information providing.
|
16
21
|
class Hotel
|
17
22
|
extend Suitcase::Helpers
|
18
23
|
|
@@ -26,14 +31,25 @@ module Suitcase
|
|
26
31
|
wheelchair_accessible: 8,
|
27
32
|
kitchen: 9 }
|
28
33
|
|
29
|
-
attr_accessor :id, :name, :address, :city, :min_rate, :max_rate, :amenities, :country_code, :high_rate, :low_rate, :longitude, :latitude, :rating, :postal_code, :supplier_type, :images, :nightly_rate_total
|
34
|
+
attr_accessor :id, :name, :address, :city, :province, :min_rate, :max_rate, :amenities, :country_code, :high_rate, :low_rate, :longitude, :latitude, :rating, :postal_code, :supplier_type, :images, :nightly_rate_total, :airport_code, :property_category, :confidence_rating, :amenity_mask, :location_description, :short_description, :hotel_in_destination, :proximity_distance
|
30
35
|
|
36
|
+
# Public: Initialize a new hotel
|
37
|
+
#
|
38
|
+
# info - a Hash of the options listed in attr_accesor.
|
39
|
+
#
|
40
|
+
# Returns a new Hotel object with the passed-in attributes.
|
31
41
|
def initialize(info)
|
32
42
|
info.each do |k, v|
|
33
43
|
send (k.to_s + "=").to_sym, v
|
34
44
|
end
|
35
45
|
end
|
36
46
|
|
47
|
+
# Public: Find a Hotel based on known information
|
48
|
+
#
|
49
|
+
# info - a Hash of known information
|
50
|
+
#
|
51
|
+
# Returns a single Hotel if an id is passed in, otherwise
|
52
|
+
# an Array of Hotels.
|
37
53
|
def self.find(info)
|
38
54
|
if info[:id]
|
39
55
|
find_by_id(info[:id])
|
@@ -42,6 +58,12 @@ module Suitcase
|
|
42
58
|
end
|
43
59
|
end
|
44
60
|
|
61
|
+
# Public: Find a Hotel by it's id.
|
62
|
+
#
|
63
|
+
# id - an Integer or String representation of the Hotel's
|
64
|
+
# id.
|
65
|
+
#
|
66
|
+
# Returns a single Hotel object.
|
45
67
|
def self.find_by_id(id)
|
46
68
|
params = { hotelId: id }
|
47
69
|
if Configuration.cache? and Configuration.cache.cached?(:info, params)
|
@@ -55,44 +77,57 @@ module Suitcase
|
|
55
77
|
Hotel.new(hotel_data)
|
56
78
|
end
|
57
79
|
|
80
|
+
# Public: Find a hotel by info other than it's id.
|
81
|
+
#
|
82
|
+
# info - a Hash of options described in the Hotel
|
83
|
+
# accessors, excluding the id.
|
84
|
+
#
|
85
|
+
# Returns an Array of Hotels.
|
58
86
|
def self.find_by_info(info)
|
59
87
|
params = info
|
60
88
|
params["numberOfResults"] = params[:results] ? params[:results] : 10
|
61
89
|
params.delete(:results)
|
62
90
|
params["destinationString"] = params[:location]
|
63
91
|
params.delete(:location)
|
64
|
-
|
65
|
-
params[:amenities].inject("") { |old, new| old + AMENITIES[new].to_s + "," }
|
66
|
-
amenities =~ /^(.+),$/
|
67
|
-
amenities = $1
|
68
|
-
end
|
92
|
+
amenities = params[:amenities] ? params[:amenities].map { |amenity| old + AMENITIES[amenity] }.join(",") : nil
|
69
93
|
params["minRate"] = params[:min_rate] if params[:min_rate]
|
70
94
|
params["maxRate"] = params[:max_rate] if params[:max_rate]
|
71
|
-
params[:amenities] = amenities
|
95
|
+
params[:amenities] = amenities if amenities
|
72
96
|
hotels = []
|
73
97
|
parsed = parse_response(url(:list, params))
|
74
98
|
handle_errors(parsed)
|
75
99
|
split(parsed).each do |hotel_data|
|
76
100
|
hotels.push Hotel.new(parse_information(hotel_data))
|
77
101
|
end
|
78
|
-
hotels
|
102
|
+
params[:results] ? hotels[0..params[:results]-1] : hotels
|
79
103
|
end
|
80
104
|
|
105
|
+
# Public: Parse the information returned by a search request
|
106
|
+
#
|
107
|
+
# parsed - a Hash representing the parsed JSON
|
108
|
+
#
|
109
|
+
# Returns a reformatted Hash with the specified accessors.
|
81
110
|
def self.parse_information(parsed)
|
82
111
|
handle_errors(parsed)
|
83
112
|
summary = parsed["hotelId"] ? parsed : parsed["HotelInformationResponse"]["HotelSummary"]
|
84
|
-
parsed_info = { id: summary["hotelId"], name: summary["name"], address: summary["address1"], city: summary["city"], postal_code: summary["postalCode"], country_code: summary["countryCode"], rating: summary["hotelRating"], high_rate: summary["highRate"], low_rate: summary["lowRate"], latitude: summary["latitude"].to_f, longitude: summary["longitude"].to_f }
|
113
|
+
parsed_info = { id: summary["hotelId"], name: summary["name"], address: summary["address1"], city: summary["city"], postal_code: summary["postalCode"], country_code: summary["countryCode"], rating: summary["hotelRating"], high_rate: summary["highRate"], low_rate: summary["lowRate"], latitude: summary["latitude"].to_f, longitude: summary["longitude"].to_f, province: summary["stateProvinceCode"], airport_code: summary["airportCode"], property_category: summary["propertyCategory"].to_i, proximity_distance: summary["proximityDistance"].to_s + summary["proximityUnit"].to_s }
|
114
|
+
parsed_info[:amenities] = parsed["HotelInformationResponse"]["PropertyAmenities"]["PropertyAmenity"].map { |x| Amenity.new(id: x["amenityId"], description: x["amenity"]) } if parsed["HotelInformationResponse"]
|
85
115
|
parsed_info[:images] = images(parsed) if images(parsed)
|
86
116
|
parsed_info
|
87
117
|
end
|
88
118
|
|
119
|
+
# Public: Get images from a parsed object
|
120
|
+
#
|
121
|
+
# parsed - a Hash representing the parsed JSON
|
122
|
+
#
|
123
|
+
# Returns an Array of Suitcase::Images.
|
89
124
|
def self.images(parsed)
|
90
|
-
|
91
|
-
|
92
|
-
return []
|
125
|
+
images = parsed["HotelInformationResponse"]["HotelImages"]["HotelImage"].map { |image_data| Suitcase::Image.new(image_data) } if parsed["HotelInformationResponse"] && parsed["HotelInformationResponse"]["HotelImages"] && parsed["HotelInformationResponse"]["HotelImages"]["HotelImage"]
|
126
|
+
images = [Suitcase::Image.new("thumbnailURL" => "http://images.travelnow.com" + parsed["thumbNailUrl"])] unless parsed["thumbNailUrl"].nil? or parsed["thumbNailUrl"].empty?
|
127
|
+
return images ? images : []
|
93
128
|
end
|
94
129
|
|
95
|
-
#
|
130
|
+
# Handle the errors from the response.
|
96
131
|
def self.handle_errors(info)
|
97
132
|
if info["HotelRoomAvailabilityResponse"] && info["HotelRoomAvailabilityResponse"]["EanWsError"]
|
98
133
|
message = info["HotelRoomAvailabilityResponse"]["EanWsError"]["presentationMessage"]
|
@@ -114,6 +149,12 @@ module Suitcase
|
|
114
149
|
first_image.thumbnail_url if first_image
|
115
150
|
end
|
116
151
|
|
152
|
+
# Public: Fetch possible rooms from a Hotel.
|
153
|
+
#
|
154
|
+
# info - a Hash of options described as the accessors in
|
155
|
+
# the Suitcase::Room class
|
156
|
+
#
|
157
|
+
# Returns an Array of Suitcase::Rooms.
|
117
158
|
def rooms(info)
|
118
159
|
params = { rooms: [{adults: 1, children_ages: []}] }.merge(info)
|
119
160
|
params[:rooms].each_with_index do |room, n|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Suitcase
|
2
|
+
class Session
|
3
|
+
attr_accessor :locale, :currency_code, :session_id, :ip_address, :user_agent
|
4
|
+
|
5
|
+
# Public: Instantiate a new session
|
6
|
+
#
|
7
|
+
# info - a Hash of the attributes:
|
8
|
+
# - locale: the user's locale
|
9
|
+
# - currency_code: the user's currency code
|
10
|
+
# - session_id: the session_id
|
11
|
+
# - ip_address: the user's IP address (request.remote_ip)
|
12
|
+
# - user_agent: the user's user agent
|
13
|
+
def initialize(info={})
|
14
|
+
%w(locale currency_code session_id ip_address user_agent).each do |attr|
|
15
|
+
send (attr + "=").to_sym, info[attr.to_sym]
|
16
|
+
end
|
17
|
+
Suitcase::Configuration.session_cache.push self
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete(session_id)
|
21
|
+
Session.store.delete Session.store.find { |session| session.session_id == session_id }
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_or_create(info)
|
25
|
+
session = Session.store.find { |session| session.session_id == session_id }
|
26
|
+
session = Session.new(info) unless session
|
27
|
+
session
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.store
|
31
|
+
Suitcase::Configuration.session_store
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Suitcase
|
2
|
+
class SessionCache
|
3
|
+
def initialize(store)
|
4
|
+
@store = store
|
5
|
+
end
|
6
|
+
|
7
|
+
def save_session(session)
|
8
|
+
@store.push session
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_session(session_id)
|
12
|
+
@store.find { |session| session.session_id == session_id }
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete_session(session_id)
|
16
|
+
@store.delete get_session(session_id)
|
17
|
+
end
|
18
|
+
|
19
|
+
def count
|
20
|
+
@store.count
|
21
|
+
end
|
22
|
+
|
23
|
+
def push(item)
|
24
|
+
@store.push item
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/suitcase/version.rb
CHANGED
metadata
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: suitcase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.0
|
5
|
-
prerelease:
|
4
|
+
version: 1.2.0
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Walter Nelson
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &17296680 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *17296680
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
requirement: &
|
27
|
+
requirement: &17294880 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *17294880
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: factory_girl
|
38
|
-
requirement: &
|
38
|
+
requirement: &17293140 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *17293140
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: json
|
49
|
-
requirement: &
|
49
|
+
requirement: &17308720 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *17308720
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: patron
|
60
|
-
requirement: &
|
60
|
+
requirement: &17307560 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *17307560
|
69
69
|
description: Suitcase utilizes the EAN (Expedia.com) API for locating info about hotels,
|
70
70
|
rental cars, and flights.
|
71
71
|
email:
|
@@ -87,12 +87,15 @@ files:
|
|
87
87
|
- lib/suitcase/core_ext/string.rb
|
88
88
|
- lib/suitcase/country_codes.rb
|
89
89
|
- lib/suitcase/helpers.rb
|
90
|
+
- lib/suitcase/hotel/amenity.rb
|
90
91
|
- lib/suitcase/hotel/hotel.rb
|
91
92
|
- lib/suitcase/hotel/image.rb
|
92
93
|
- lib/suitcase/hotel/nightly_rate.rb
|
93
94
|
- lib/suitcase/hotel/payment_option.rb
|
94
95
|
- lib/suitcase/hotel/reservation.rb
|
95
96
|
- lib/suitcase/hotel/room.rb
|
97
|
+
- lib/suitcase/session.rb
|
98
|
+
- lib/suitcase/session_cache.rb
|
96
99
|
- lib/suitcase/version.rb
|
97
100
|
- spec/caching_spec.rb
|
98
101
|
- spec/helpers_spec.rb
|
@@ -117,13 +120,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
120
|
version: '0'
|
118
121
|
segments:
|
119
122
|
- 0
|
120
|
-
hash:
|
123
|
+
hash: 2063899818244561137
|
121
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
125
|
none: false
|
123
126
|
requirements:
|
124
|
-
- - ! '
|
127
|
+
- - ! '>='
|
125
128
|
- !ruby/object:Gem::Version
|
126
|
-
version:
|
129
|
+
version: '0'
|
130
|
+
segments:
|
131
|
+
- 0
|
132
|
+
hash: 2063899818244561137
|
127
133
|
requirements: []
|
128
134
|
rubyforge_project: suitcase
|
129
135
|
rubygems_version: 1.8.10
|