suitcase 1.0.1 → 1.1.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/README.md CHANGED
@@ -21,14 +21,14 @@ First, include the module in your code:
21
21
  Find nearby hotels:
22
22
 
23
23
  Hotel::API_KEY = "your_api_key_here"
24
- Hotel.near('Boston, MA', 10) # Returns 10 closest hotels to Boston, MA
25
- Hotel.near('Metropoliton Museum of Art') # Returns 50 closest hotels to the Met Museum in New York
24
+ Hotel.near(:location => 'Boston, MA', :results => 10) # Returns 10 closest hotels to Boston, MA
25
+ Hotel.near(:location => 'Metropoliton Museum of Art') # Returns 50 closest hotels to the Met Museum in New York
26
26
 
27
27
  Find available airline flights (not yet implemented):
28
28
 
29
29
  Flight:API_KEY = "your_api_key_here"
30
- Flight.available('June 23 2012', :from => "BOS", :to => "LHR") # Return all flights flying from Boston to London (Heathrow) on June 23, 2012
30
+ Flight.available(:date => 'June 23 2012', :departs => "BOS", :arrives => "LHR") # Return all flights flying from Boston to London (Heathrow) on June 23, 2012
31
31
 
32
32
  Contributing
33
33
  ------------
34
- Please submit any useful pull requests through GitHub. If you find any bugs, please report them! Thanks.
34
+ Please submit any useful pull requests through GitHub. If you find any bugs, please report them with the issue tracker! Thanks.
@@ -0,0 +1,28 @@
1
+ Feature: Hotels
2
+ As a developer
3
+ In order to use the EAN API in my application
4
+ I need to be able to manipulate hotel information
5
+
6
+ Scenario Outline: Listing available hotels
7
+ Given I have an API key
8
+ When I submit a request to find <number_of_results> hotels in <location>
9
+ Then I want to receive <number_of_results> hotels with their respective information
10
+
11
+ Examples:
12
+
13
+ | number_of_results | location |
14
+ | 10 | Boston, USA |
15
+ | 8 | Dublin, Ireland |
16
+ | 1 | London, UK |
17
+
18
+ Scenario Outline: Requesting more information about a given hotel
19
+ Given I have an API key
20
+ When I submit a request to find more information about a hotel with id <id>
21
+ Then I want to receive more information about that hotel
22
+
23
+ Examples:
24
+
25
+ | id |
26
+ | 252897 |
27
+ | 273655 |
28
+ | 174272 |
@@ -0,0 +1,21 @@
1
+ require 'suitcase'
2
+
3
+ Given /^I have an API key$/ do
4
+ Suitcase::Hotel::API_KEY = "3kx2vhr7sjrgmsyddb2cg47j"
5
+ end
6
+
7
+ When /^I submit a request to find (\d+) hotels in (.+)$/ do |results, location|
8
+ @hotels = Suitcase::Hotel.find(:near => location, :results => results.to_i)
9
+ end
10
+
11
+ Then /^I want to receive (\d+) hotels with their respective information$/ do |results|
12
+ @hotels.count.should eq(results.to_i)
13
+ end
14
+
15
+ When /^I submit a request to find more information about a hotel with id (\d+)$/ do |id|
16
+ @hotel = Suitcase::Hotel.find_by_id(id)
17
+ end
18
+
19
+ Then /^I want to receive more information about that hotel$/ do
20
+ @hotel.should_not be_nil
21
+ end
@@ -0,0 +1,151 @@
1
+ module Suitcase
2
+ class PaymentOption
3
+ attr_accessor :code, :name
4
+
5
+ def initialize(code, name)
6
+ @code = code
7
+ @name = name
8
+ end
9
+ end
10
+
11
+ class Room
12
+ attr_accessor :rate_key, :hotel_id, :supplier_type
13
+
14
+ def initialize(rate_key, hotel_id, supplier_type)
15
+ @rate_key = rate_key
16
+ @hotel_id = hotel_id
17
+ @supplier_type = supplier_type
18
+ end
19
+
20
+ def reserve!(info)
21
+ params = info
22
+ params["hotelId"] = @id
23
+ params["arrivalDate"] = info[:arrival]
24
+ params["departureDate"] = info[:departure]
25
+ params.delete(:arrival)
26
+ params.delete(:departure)
27
+ params["supplierType"] = supplier_type
28
+ params["rateKey"] = @rate_key
29
+ params["rateTypeCode"] = info[:room_type_code]
30
+ params["rateCode"] = info[:rate_code]
31
+ params.delete(:rate_code)
32
+ p Hotel.hit(Hotel.url(:res, true, true, params))
33
+ end
34
+ end
35
+
36
+ class Hotel
37
+ AMENITIES = { pool: 1,
38
+ fitness_center: 2,
39
+ restaurant: 3,
40
+ children_activities: 4,
41
+ breakfast: 5,
42
+ meeting_facilities: 6,
43
+ pets: 7,
44
+ wheelchair_accessible: 8,
45
+ kitchen: 9 }
46
+
47
+ attr_accessor :id, :name, :address, :city, :min_rate, :max_rate, :amenities, :country_code, :high_rate, :low_rate, :longitude, :latitude, :rating, :postal_code, :supplier_type
48
+
49
+ def initialize(info)
50
+ info.each do |k, v|
51
+ send (k.to_s + "=").to_sym, v
52
+ end
53
+ end
54
+
55
+ def self.url(action, include_key, include_cid, params)
56
+ url = "http://api.ean.com/ean-services/rs/hotel/v3/" + action.to_s + "?"
57
+ include_key ? params["apiKey"] = Suitcase::Hotel::API_KEY : nil
58
+ include_cid ? params["cid"] = "55505" : nil
59
+ params.each do |k, v|
60
+ url += k.to_s + "=" + v.to_s + "&"
61
+ end
62
+ if url =~ /^(.+)&$/
63
+ url = $1
64
+ end
65
+ URI.parse URI.escape(url)
66
+ end
67
+
68
+ def self.find(info)
69
+ if info[:id]
70
+ find_by_id(info[:id])
71
+ else
72
+ find_by_info(info)
73
+ end
74
+ end
75
+
76
+ def self.find_by_id(id)
77
+ Hotel.new(parse_hotel_information(hit(url(:info, true, true, { hotelId: id }))))
78
+ end
79
+
80
+ def self.find_by_info(info)
81
+ params = info
82
+ params["numberOfResults"] = params[:results] ? params[:results] : 10
83
+ params.delete(:results)
84
+ params["destinationString"] = params[:location]
85
+ params.delete(:location)
86
+ if params[:amenities]
87
+ amenities = ""
88
+ params[:amenities].each do |amenity|
89
+ amenities += AMENITIES[amenity].to_s + ","
90
+ end
91
+ if amenities =~ /^(.+),$/
92
+ amenities = $1
93
+ end
94
+ end
95
+ params["minRate"] = params[:min_rate] if params[:min_rate]
96
+ params["maxRate"] = params[:max_rate] if params[:max_rate]
97
+ params[:amenities] = amenities
98
+ hotels = []
99
+ split(hit(url(:list, true, true, params))).each do |hotel_data|
100
+ hotels.push Hotel.new(parse_hotel_information(hotel_data.to_json))
101
+ end
102
+ hotels
103
+ end
104
+
105
+ def self.hit(url)
106
+ Net::HTTP.get_response(url).body
107
+ end
108
+
109
+ def self.parse_hotel_information(json)
110
+ parsed = JSON.parse json
111
+ summary = parsed["hotelId"] ? parsed : parsed["HotelInformationResponse"]["HotelSummary"]
112
+ { 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
+ end
114
+
115
+ def self.split(data)
116
+ parsed = JSON.parse(data)
117
+ hotels = parsed["HotelListResponse"]["HotelList"]
118
+ hotels["HotelSummary"]
119
+ end
120
+
121
+ def rooms(info)
122
+ params = info
123
+ params[:rooms].each_with_index do |room, n|
124
+ params["room#{n+1}"] = (room[:children] == 0 ? "" : room[:children].to_s + ",").to_s + room[:ages].join(",").to_s
125
+ end
126
+ params["arrivalDate"] = info[:arrival]
127
+ params["departureDate"] = info[:departure]
128
+ params.delete(:arrival)
129
+ params.delete(:departure)
130
+ params["hotelId"] = @id
131
+ parsed = JSON.parse(Hotel.hit(Hotel.url(:avail, true, true, params)))
132
+ hotel_id = parsed["HotelRoomAvailabilityResponse"]["hotelId"]
133
+ rate_key = parsed["HotelRoomAvailabilityResponse"]["rateKey"]
134
+ supplier_type = parsed["HotelRoomAvailabilityResponse"]["supplierType"]
135
+ Room.new(rate_key, hotel_id, supplier_type)
136
+ end
137
+
138
+ def payment_options
139
+ options = []
140
+ types_raw = JSON.parse Hotel.hit(Hotel.url(:paymentInfo, true, true, {}))
141
+ types_raw["HotelPaymentResponse"].each do |raw|
142
+ p raw
143
+ types = raw[0] != "PaymentType" ? [] : raw[1]
144
+ types.each do |type|
145
+ options.push PaymentOption.new(type["code"], type["name"])
146
+ end
147
+ end
148
+ options
149
+ end
150
+ end
151
+ end
@@ -1,3 +1,3 @@
1
1
  module Suitcase
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/suitcase.rb CHANGED
@@ -1,7 +1,4 @@
1
+ require 'json'
2
+ require 'net/http'
1
3
  require 'suitcase/version'
2
- require 'suitcase/hotel/hotel'
3
- require 'suitcase/air/air'
4
-
5
- module Suitcase
6
-
7
- end
4
+ require 'suitcase/hotel'
data/spec/api_key.rb ADDED
@@ -0,0 +1 @@
1
+ HOTEL_API_KEY = "88m3ugkarmfzcq3z4syayymt"
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Suitcase::DataHelpers do
4
+ before :each do
5
+ class Hotel
6
+ API_KEY = HOTEL_API_KEY
7
+ extend Suitcase::DataHelpers
8
+ end
9
+ end
10
+
11
+ it "#url should return a valid url" do
12
+ URI.parse(Hotel.url(:hotel, :list, true, { location: "London, UK" })).should be_a(URI)
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Suitcase::Hotel do
4
+ it "::find should return an Array if no ID is passed" do
5
+ Suitcase::Hotel.find(destination: "Boston, US").should be_a(Array)
6
+ end
7
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,2 @@
1
1
  require File.dirname(__FILE__) + '/../lib/suitcase'
2
2
  require 'api_key'
3
-
4
- Suitcase::Hotel::API_KEY = HOTEL_API_KEY
data/suitcase.gemspec CHANGED
@@ -19,6 +19,8 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ["lib"]
20
20
 
21
21
  s.add_development_dependency "rspec"
22
+ s.add_development_dependency "cucumber"
23
+ s.add_development_dependency "rake"
22
24
  s.add_runtime_dependency "json"
23
25
  s.add_runtime_dependency "nokogiri"
24
26
  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.0.1
4
+ version: 1.1.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: 2011-11-05 00:00:00.000000000Z
12
+ date: 2011-12-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &11887140 !ruby/object:Gem::Requirement
16
+ requirement: &20325340 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,32 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *11887140
24
+ version_requirements: *20325340
25
+ - !ruby/object:Gem::Dependency
26
+ name: cucumber
27
+ requirement: &20324640 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *20324640
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &20324160 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *20324160
25
47
  - !ruby/object:Gem::Dependency
26
48
  name: json
27
- requirement: &11886720 !ruby/object:Gem::Requirement
49
+ requirement: &20323700 !ruby/object:Gem::Requirement
28
50
  none: false
29
51
  requirements:
30
52
  - - ! '>='
@@ -32,10 +54,10 @@ dependencies:
32
54
  version: '0'
33
55
  type: :runtime
34
56
  prerelease: false
35
- version_requirements: *11886720
57
+ version_requirements: *20323700
36
58
  - !ruby/object:Gem::Dependency
37
59
  name: nokogiri
38
- requirement: &11886300 !ruby/object:Gem::Requirement
60
+ requirement: &20323260 !ruby/object:Gem::Requirement
39
61
  none: false
40
62
  requirements:
41
63
  - - ! '>='
@@ -43,7 +65,7 @@ dependencies:
43
65
  version: '0'
44
66
  type: :runtime
45
67
  prerelease: false
46
- version_requirements: *11886300
68
+ version_requirements: *20323260
47
69
  description: Suitcase utilizes the EAN API for locating available hotels, rental cars,
48
70
  and flights.
49
71
  email:
@@ -56,13 +78,16 @@ files:
56
78
  - Gemfile
57
79
  - README.md
58
80
  - Rakefile
81
+ - features/hotels.feature
82
+ - features/step_definitions/hotels_steps.rb
59
83
  - lib/suitcase.rb
60
- - lib/suitcase/air/air.rb
61
84
  - lib/suitcase/airport_codes.rb
62
85
  - lib/suitcase/country_codes.rb
63
- - lib/suitcase/hotel/hotel.rb
86
+ - lib/suitcase/hotel.rb
64
87
  - lib/suitcase/version.rb
65
- - spec/hotel_spec.rb
88
+ - spec/api_key.rb
89
+ - spec/data_helpers_spec.rb
90
+ - spec/hotels_spec.rb
66
91
  - spec/spec_helper.rb
67
92
  - suitcase.gemspec
68
93
  homepage: http://github.com/thoughtfusion/suitcase
@@ -77,16 +102,28 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
102
  - - ! '>='
78
103
  - !ruby/object:Gem::Version
79
104
  version: '0'
105
+ segments:
106
+ - 0
107
+ hash: -1859999531224729447
80
108
  required_rubygems_version: !ruby/object:Gem::Requirement
81
109
  none: false
82
110
  requirements:
83
111
  - - ! '>='
84
112
  - !ruby/object:Gem::Version
85
113
  version: '0'
114
+ segments:
115
+ - 0
116
+ hash: -1859999531224729447
86
117
  requirements: []
87
118
  rubyforge_project: suitcase
88
- rubygems_version: 1.8.6
119
+ rubygems_version: 1.8.10
89
120
  signing_key:
90
121
  specification_version: 3
91
122
  summary: Locates available hotels, rental cars, and flights
92
- test_files: []
123
+ test_files:
124
+ - features/hotels.feature
125
+ - features/step_definitions/hotels_steps.rb
126
+ - spec/api_key.rb
127
+ - spec/data_helpers_spec.rb
128
+ - spec/hotels_spec.rb
129
+ - spec/spec_helper.rb
@@ -1,86 +0,0 @@
1
- require 'open-uri'
2
- require 'json'
3
- require 'nokogiri'
4
- require File.dirname(__FILE__) + '/../airport_codes'
5
-
6
- module Suitcase
7
- class Flight
8
- attr_accessor :flights, :key, :origin, :destination, :departure, :arrival, :adults, :children, :seniors, :fare, :direct, :round_trip, :currency, :search_window, :results, :airline, :airline_code, :price, :segments, :outgoing
9
-
10
- def to_s
11
- puts <<EOS
12
- ----------
13
- FLIGHT:
14
- airline: #{airline} (#{airline_code})
15
- origin: #{origin}
16
- destination: #{destination}
17
- departure: #{departure}
18
- arrival: #{arrival}
19
- price: #{price} (#{currency})
20
- key: #{key}
21
- ----------
22
- EOS
23
- end
24
-
25
- def self.available(data)
26
- origin_city = data[:from]
27
- destination_city = data[:to]
28
- departure_date_time = data[:departs]
29
- arrival_date_time = data[:arrives]
30
- adult_passengers = data[:adults] ? data[:adults] : 0
31
- child_passengers = data[:children] ? data[:children].inject("") { |result, element| result + "C" + (element < 10 ? "0" : "") + element.to_s + (data[:children].last == element ? "" : ",") } : "" # :children => [2, 9, 11] (ages of children) should yield "C02,C09,C11"
32
- senior_passengers = data[:seniors] ? data[:seniors] : 0
33
- fare_class = data[:fare] ? data[:fare] : "Y" # F: first class; Y: coach; B: business
34
- direct_flight = data[:direct_only] ? data[:direct_only] : false
35
- round_trip = data[:round_trip] ? data[:round_trip] : "O"
36
- currency = data[:currency] ? data[:currency] : "USD"
37
- search_window = data[:search_window] ? data[:search_window] : 2
38
- number_of_results = data[:results] ? data[:results] : 50
39
- xml_format = <<EOS
40
- <AirSessionRequest method="getAirAvailability">
41
- <AirAvailabilityQuery>
42
- <originCityCode>#{origin_city}</originCityCode>
43
- <destinationCityCode>#{destination_city}</destinationCityCode>
44
- <departureDateTime>#{departure_date_time}</departureDateTime>
45
- <returnDateTime>#{arrival_date_time}</returnDateTime>
46
- <fareClass>#{fare_class}</fareClass>
47
- <tripType>#{round_trip}</tripType>
48
- <Passengers>
49
- <adultPassengers>#{adult_passengers}</adultPassengers>
50
- <seniorPassengers>#{senior_passengers}</seniorPassengers>
51
- <childCodes>#{child_passengers}</childCodes>
52
- </Passengers>
53
- </AirAvailabilityQuery>
54
- </AirSessionRequest>
55
- EOS
56
- uri = URI.escape("http://api.ean.com/ean-services/rs/air/200919/xmlinterface.jsp?cid=#{CID}&resType=air&intfc=ws&apiKey=#{API_KEY}&xml=#{xml_format}")
57
- xml = Nokogiri::XML(open(uri))
58
- segments = []
59
- xml.xpath('//Segment').each do |segment|
60
- f = Flight.new
61
- f.key = segment.xpath("@key")
62
- f.origin = segment.xpath("//originCity") + segment.xpath("//originStateProvince") + segment.xpath("//originCountry")
63
- f.destination = segment.xpath("//destinationCity")
64
- f.airline = segment.xpath("//airline")
65
- f.airline_code = segment.xpath("//airlineCode")
66
- f.departure = segment.xpath("//departureDateTime")
67
- f.arrival = segment.xpath("//arrivalDateTime")
68
- segments.push(f)
69
- end
70
- flights = []
71
- xml.xpath("//AirAvailabilityReply").each do |aar|
72
- f = Flight.new
73
- f.segments = []
74
- f.price = aar.xpath("//nativeTotalPrice")
75
- f.currency = aar.xpath("//displayCurrencyCode")
76
- aar.xpath("//FlightSegment").each do |subseg|
77
- fn = segments.find { |x| x.key == subseg.xpath("//segmentKey") }
78
- f.segments.push(fn)
79
- f.segments.compact!
80
- end
81
- flights.push(f)
82
- end
83
- flights
84
- end
85
- end
86
- end
@@ -1,49 +0,0 @@
1
- require 'net/http'
2
- require 'uri'
3
- require 'json'
4
- require File.dirname(__FILE__) + '/../country_codes'
5
-
6
- module Suitcase
7
- class Hotel
8
- attr_accessor :id, :name, :address, :city, :postal_code, :country, :airport_code, :rating, :confidence_rating, :description, :high_rate, :low_rate, :tripadvisor_rating, :currency_code, :latitude, :longitude
9
-
10
- def self.find(hash)
11
- hotels = []
12
- json = JSON.parse Net::HTTP.get_response(URI.parse(URI.escape("http://api.ean.com/ean-services/rs/hotel/v3/list?apiKey=#{Suitcase::Hotel::API_KEY}&city=#{hash[:near]}&numberOfResults=#{hash[:results]}"))).body
13
- if json["HotelListResponse"]["HotelList"]
14
- json["HotelListResponse"]["HotelList"]["HotelSummary"].each do |hotel_data|
15
- h = Hotel.new
16
- h.id = hotel_data["hotelId"]
17
- h.name = hotel_data["name"]
18
- h.address = hotel_data["address1"]
19
- h.city = hotel_data["city"]
20
- h.postal_code = hotel_data["postalCode"]
21
- h.country = COUNTRY_CODES[hotel_data["countryCode"]]
22
- h.airport_code = hotel_data["airportCode"]
23
- h.rating = hotel_data["hotelRating"]
24
- h.confidence_rating = hotel_data["confidenceRating"]
25
- h.tripadvisor_rating = hotel_data["tripAdvisorRating"]
26
- h.currency_code = hotel_data["rateCurrencyCode"]
27
- h.latitude = hotel_data["latitude"]
28
- h.longitude = hotel_data["longitude"]
29
- h.high_rate = hotel_data["highRate"]
30
- h.low_rate = hotel_data["lowRate"]
31
- hotels.push(h)
32
- end
33
- hotels[0..hash[:results]-1]
34
- else
35
- if json["HotelListResponse"]["EanWsError"]
36
- raise "An error occured. Check data."
37
- end
38
- end
39
- end
40
-
41
- def geolocation
42
- latitude + ", " + longitude
43
- end
44
-
45
- def complete_address
46
- address + " " + city + ", " + country + " " + postal_code
47
- end
48
- end
49
- end
data/spec/hotel_spec.rb DELETED
@@ -1,23 +0,0 @@
1
- require 'spec_helper'
2
-
3
- include Suitcase
4
-
5
- describe Hotel do
6
- before(:each) do
7
- @attr = { :near => "Boston", :results => 50 }
8
- end
9
-
10
- describe "find method" do
11
- it "should return the correct number of results" do
12
- Hotel.find(@attr).count.should eq(50)
13
- end
14
-
15
- it "should return an array of hotels" do
16
- Hotel.find(@attr).class.should eq(Array)
17
- end
18
-
19
- it "should return all hotels" do
20
- Hotel.find(@attr).map { |x| x.class }.should eq([Hotel] * 50)
21
- end
22
- end
23
- end