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 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.
@@ -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
@@ -0,0 +1,9 @@
1
+ module Suitcase
2
+ class Amenity
3
+ attr_accessor :id, :description
4
+
5
+ def initialize(info)
6
+ @id, @description = info[:id], info[:description]
7
+ end
8
+ end
9
+ end
@@ -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
- if params[:amenities]
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
- return parsed["HotelInformationResponse"]["HotelImages"]["HotelImage"].map { |image_data| Suitcase::Image.new(image_data) } if parsed["HotelInformationResponse"] && parsed["HotelInformationResponse"]["HotelImages"] && parsed["HotelInformationResponse"]["HotelImages"]["HotelImage"]
91
- return [Suitcase::Image.new("thumbnailURL" => "http://images.travelnow.com" + parsed["thumbNailUrl"])] unless parsed["thumbnailUrl"].nil? or parsed["thumbNailUrl"].empty?
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
- # Bleghh. so ugly. #needsfixing
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
@@ -1,3 +1,3 @@
1
1
  module Suitcase
2
- VERSION = "1.2.0.pre"
2
+ VERSION = "1.2.0"
3
3
  end
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.pre
5
- prerelease: 6
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-06 00:00:00.000000000 Z
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: &16614460 !ruby/object:Gem::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: *16614460
24
+ version_requirements: *17296680
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &16613580 !ruby/object:Gem::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: *16613580
35
+ version_requirements: *17294880
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: factory_girl
38
- requirement: &16612360 !ruby/object:Gem::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: *16612360
46
+ version_requirements: *17293140
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: json
49
- requirement: &16611740 !ruby/object:Gem::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: *16611740
57
+ version_requirements: *17308720
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: patron
60
- requirement: &16611300 !ruby/object:Gem::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: *16611300
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: 3770284806370543423
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: 1.3.1
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