hotel_beds 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/INTERNALS.md +6 -0
- data/README.md +55 -29
- data/lib/hotel_beds/client.rb +11 -3
- data/lib/hotel_beds/connection.rb +2 -3
- data/lib/hotel_beds/hotel_basket_add/envelope.rb +71 -0
- data/lib/hotel_beds/hotel_basket_add/operation.rb +36 -0
- data/lib/hotel_beds/hotel_basket_add/request.rb +49 -0
- data/lib/hotel_beds/hotel_basket_add/response.rb +66 -0
- data/lib/hotel_beds/hotel_search/envelope.rb +27 -18
- data/lib/hotel_beds/hotel_search/parser/errors.rb +49 -0
- data/lib/hotel_beds/hotel_search/parser/hotel.rb +67 -0
- data/lib/hotel_beds/hotel_search/parser/price.rb +38 -0
- data/lib/hotel_beds/hotel_search/parser/room.rb +46 -0
- data/lib/hotel_beds/hotel_search/parser/room_grouper.rb +101 -0
- data/lib/hotel_beds/hotel_search/request.rb +5 -23
- data/lib/hotel_beds/hotel_search/response.rb +16 -68
- data/lib/hotel_beds/model.rb +0 -1
- data/lib/hotel_beds/model/available_room.rb +9 -11
- data/lib/hotel_beds/model/cancellation_policy.rb +14 -0
- data/lib/hotel_beds/model/hotel.rb +4 -0
- data/lib/hotel_beds/model/purchase.rb +16 -0
- data/lib/hotel_beds/model/requested_room.rb +7 -0
- data/lib/hotel_beds/model/room.rb +31 -0
- data/lib/hotel_beds/model/search_result.rb +0 -5
- data/lib/hotel_beds/model/service.rb +18 -0
- data/lib/hotel_beds/version.rb +1 -1
- data/spec/features/adding_a_hotel_to_basket_spec.rb +58 -0
- data/spec/features/hotel_search_spec.rb +31 -9
- data/spec/lib/hotel_beds/hotel_search/parser/room_grouper_spec.rb +53 -0
- data/spec/spec_helper.rb +8 -0
- metadata +20 -5
- data/lib/hotel_beds/hotel_search/room_grouper.rb +0 -64
- data/spec/lib/hotel_beds/hotel_search/room_grouper_spec.rb +0 -45
data/lib/hotel_beds/model.rb
CHANGED
@@ -1,24 +1,22 @@
|
|
1
|
-
require "hotel_beds/model"
|
1
|
+
require "hotel_beds/model/room"
|
2
|
+
require "hotel_beds/model/cancellation_policy"
|
2
3
|
|
3
4
|
module HotelBeds
|
4
5
|
module Model
|
5
|
-
class AvailableRoom
|
6
|
-
include HotelBeds::Model
|
7
|
-
|
6
|
+
class AvailableRoom < Room
|
8
7
|
# attributes
|
9
8
|
attribute :id, Integer
|
10
|
-
attribute :
|
11
|
-
attribute :child_count, Integer
|
9
|
+
attribute :room_count, Integer
|
12
10
|
attribute :description, String
|
13
11
|
attribute :board, String
|
12
|
+
attribute :board_code, String
|
13
|
+
attribute :room_type_code, String
|
14
|
+
attribute :room_type_characteristic, String
|
14
15
|
attribute :price, BigDecimal
|
15
16
|
attribute :number_available, Integer
|
16
17
|
attribute :rates, Hash[Date => BigDecimal]
|
17
|
-
|
18
|
-
|
19
|
-
def <=>(other)
|
20
|
-
id == other.id && !id.nil?
|
21
|
-
end
|
18
|
+
attribute :cancellation_policies, Array[CancellationPolicy],
|
19
|
+
default: Array.new
|
22
20
|
end
|
23
21
|
end
|
24
22
|
end
|
@@ -8,12 +8,16 @@ module HotelBeds
|
|
8
8
|
|
9
9
|
# attributes
|
10
10
|
attribute :id, Integer
|
11
|
+
attribute :availability_token, String
|
11
12
|
attribute :name, String
|
12
13
|
attribute :images, Array[String]
|
13
14
|
attribute :stars, Integer
|
14
15
|
attribute :longitude, BigDecimal
|
15
16
|
attribute :latitude, BigDecimal
|
16
17
|
attribute :results, Array[SearchResult]
|
18
|
+
attribute :destination_code, String
|
19
|
+
attribute :contract_name, String
|
20
|
+
attribute :contract_incoming_office_code, String
|
17
21
|
end
|
18
22
|
end
|
19
23
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "hotel_beds/model"
|
2
|
+
require "hotel_beds/model/service"
|
3
|
+
|
4
|
+
module HotelBeds
|
5
|
+
module Model
|
6
|
+
class Purchase
|
7
|
+
include HotelBeds::Model
|
8
|
+
|
9
|
+
# attributes
|
10
|
+
attribute :id, String
|
11
|
+
attribute :services, Array[Service]
|
12
|
+
attribute :currency, String
|
13
|
+
attribute :amount, BigDecimal
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "hotel_beds/model"
|
2
|
+
require_relative "room"
|
3
|
+
|
4
|
+
module HotelBeds
|
5
|
+
module Model
|
6
|
+
class Room
|
7
|
+
include HotelBeds::Model
|
8
|
+
|
9
|
+
# attributes
|
10
|
+
attribute :adult_count, Integer, default: 0
|
11
|
+
attribute :child_count, Integer, default: 0
|
12
|
+
attribute :child_ages, Array[Integer], default: Array.new
|
13
|
+
|
14
|
+
# validation
|
15
|
+
validates :adult_count, :child_count, numericality: {
|
16
|
+
greater_than_or_equal_to: 0,
|
17
|
+
less_than_or_equal_to: 4,
|
18
|
+
only_integer: true,
|
19
|
+
}
|
20
|
+
validate do |room|
|
21
|
+
unless child_ages.compact.size == child_count
|
22
|
+
room.errors.add(:child_ages, "must match quantity of children")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def group_key
|
27
|
+
{ adult_count: adult_count, child_count: child_count }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -10,11 +10,6 @@ module HotelBeds
|
|
10
10
|
attribute :rooms, Array[AvailableRoom]
|
11
11
|
attribute :currency, String
|
12
12
|
|
13
|
-
# results are purely based upon their combination of rooms
|
14
|
-
def <=>(other)
|
15
|
-
rooms.map(&:id).sort == other.rooms.map(&:id).sort
|
16
|
-
end
|
17
|
-
|
18
13
|
# returns the total price of all the rooms collectively
|
19
14
|
def price
|
20
15
|
rooms.map(&:price).inject(BigDecimal.new("0.0"), :+)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "hotel_beds/model"
|
2
|
+
|
3
|
+
module HotelBeds
|
4
|
+
module Model
|
5
|
+
class Service
|
6
|
+
include HotelBeds::Model
|
7
|
+
|
8
|
+
# attributes
|
9
|
+
attribute :id, String
|
10
|
+
attribute :contract_name, String
|
11
|
+
attribute :contract_incoming_office_code, String
|
12
|
+
attribute :check_in_date, Date
|
13
|
+
attribute :check_out_date, Date
|
14
|
+
attribute :currency, String
|
15
|
+
attribute :amount, BigDecimal
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/hotel_beds/version.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
require "hotel_beds"
|
2
|
+
|
3
|
+
RSpec.describe "adding a hotel to the basket" do
|
4
|
+
describe "#response" do
|
5
|
+
before(:all) do
|
6
|
+
@client = HotelBeds::Client.new({
|
7
|
+
endpoint: :test,
|
8
|
+
username: ENV.fetch("HOTEL_BEDS_USERNAME"),
|
9
|
+
password: ENV.fetch("HOTEL_BEDS_PASSWORD"),
|
10
|
+
proxy: ENV.fetch("HOTEL_BEDS_PROXY", nil)
|
11
|
+
})
|
12
|
+
@check_in_date = Date.today + 28 + rand(10)
|
13
|
+
@check_out_date = @check_in_date + rand(3) + rand(2) + 1
|
14
|
+
@search_response = @client.perform_hotel_search({
|
15
|
+
check_in_date: @check_in_date,
|
16
|
+
check_out_date: @check_out_date,
|
17
|
+
rooms: [{ adult_count: 2 }],
|
18
|
+
destination: "SYD"
|
19
|
+
}).response
|
20
|
+
|
21
|
+
hotel = @search_response.hotels.first
|
22
|
+
result = hotel.results.first
|
23
|
+
|
24
|
+
@operation = @client.add_hotel_room_to_basket({
|
25
|
+
service: {
|
26
|
+
check_in_date: @check_in_date,
|
27
|
+
check_out_date: @check_out_date,
|
28
|
+
availability_token: hotel.availability_token,
|
29
|
+
hotel_code: hotel.id,
|
30
|
+
destination_code: hotel.destination_code,
|
31
|
+
contract_name: hotel.contract_name,
|
32
|
+
contract_incoming_office_code: hotel.contract_incoming_office_code,
|
33
|
+
rooms: result.rooms
|
34
|
+
}
|
35
|
+
})
|
36
|
+
if @operation.errors.any?
|
37
|
+
raise StandardError, @operation.errors.full_messages.join("\n")
|
38
|
+
end
|
39
|
+
@response = @operation.response
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:response) { @response }
|
43
|
+
|
44
|
+
subject { response }
|
45
|
+
|
46
|
+
it "should be a success" do
|
47
|
+
expect(subject).to be_success
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#purchase" do
|
51
|
+
subject { response.purchase }
|
52
|
+
|
53
|
+
it "should have a service" do
|
54
|
+
expect(subject.services).to_not be_empty
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -40,19 +40,41 @@ RSpec.describe "performing a hotel search" do
|
|
40
40
|
expect(subject.first).to be_kind_of(HotelBeds::Model::Hotel)
|
41
41
|
end
|
42
42
|
|
43
|
-
it "should
|
44
|
-
|
45
|
-
expect(
|
43
|
+
it "should only have one room per result" do
|
44
|
+
room_counts = subject.map { |h| h.results.map { |r| r.rooms.size } }
|
45
|
+
expect(room_counts.to_a.flatten.uniq).to eq([1])
|
46
46
|
end
|
47
47
|
|
48
|
-
it "should have
|
49
|
-
|
50
|
-
|
48
|
+
it "should have an availability token" do
|
49
|
+
subject.each do |hotel|
|
50
|
+
expect(hotel.availability_token).to be_present
|
51
|
+
end
|
51
52
|
end
|
52
53
|
|
53
|
-
it "should
|
54
|
-
|
55
|
-
|
54
|
+
it "should have a contract name" do
|
55
|
+
subject.each do |hotel|
|
56
|
+
expect(hotel.contract_name).to be_present
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should have a contract incoming office code" do
|
61
|
+
subject.each do |hotel|
|
62
|
+
expect(hotel.contract_incoming_office_code).to be_present
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#results" do
|
67
|
+
describe "#rooms" do
|
68
|
+
subject { response.hotels.first.results.first.rooms.first }
|
69
|
+
|
70
|
+
it "should parse the rates correctly" do
|
71
|
+
expect(subject.price).to eq(subject.rates.values.inject(:+))
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should have the same number of rates as nights requested" do
|
75
|
+
expect(subject.rates.size).to eq(check_out_date - check_in_date)
|
76
|
+
end
|
77
|
+
end
|
56
78
|
end
|
57
79
|
end
|
58
80
|
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "hotel_beds/hotel_search/parser/room_grouper"
|
2
|
+
require "hotel_beds/model/available_room"
|
3
|
+
|
4
|
+
RSpec.describe HotelBeds::HotelSearch::Parser::RoomGrouper do
|
5
|
+
subject { described_class.new(requested_rooms, response_rooms).results }
|
6
|
+
|
7
|
+
context "when asking for 3 rooms (2 x 2 adults & 1 x 1 adult, 1 child)" do
|
8
|
+
let(:requested_rooms) do
|
9
|
+
[
|
10
|
+
double(:requested_room, adult_count: 2, child_count: 0, child_ages: []),
|
11
|
+
double(:requested_room, adult_count: 2, child_count: 0, child_ages: []),
|
12
|
+
double(:requested_room, adult_count: 1, child_count: 1, child_ages: [rand(17)])
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:response_rooms) do
|
17
|
+
[
|
18
|
+
HotelBeds::Model::AvailableRoom.new(id: "room_1", adult_count: 2, child_count: 0, number_available: 1, room_count: 2),
|
19
|
+
HotelBeds::Model::AvailableRoom.new(id: "room_2", adult_count: 2, child_count: 0, number_available: 1, room_count: 2),
|
20
|
+
HotelBeds::Model::AvailableRoom.new(id: "room_3", adult_count: 1, child_count: 1, number_available: 2, room_count: 1),
|
21
|
+
HotelBeds::Model::AvailableRoom.new(id: "room_4", adult_count: 1, child_count: 1, number_available: 1, room_count: 1),
|
22
|
+
HotelBeds::Model::AvailableRoom.new(id: "room_5", adult_count: 2, child_count: 0, number_available: 2, room_count: 2)
|
23
|
+
]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return 6 results" do
|
27
|
+
expect(subject.size).to eq(6)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return results containing all three rooms" do
|
31
|
+
subject.each do |rooms|
|
32
|
+
expect(rooms.size).to eq(3)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when asking for 1 rooms (1 adult, 1 child)" do
|
38
|
+
let(:requested_rooms) do
|
39
|
+
[double(:requested_room, adult_count: 1, child_count: 1, child_ages: [rand(17)])]
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:response_rooms) do
|
43
|
+
[
|
44
|
+
HotelBeds::Model::AvailableRoom.new(id: "room_1", room_count: 1, adult_count: 1, child_count: 1, number_available: 2),
|
45
|
+
HotelBeds::Model::AvailableRoom.new(id: "room_2", room_count: 1, adult_count: 1, child_count: 1, number_available: 1)
|
46
|
+
]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should return 2 results" do
|
50
|
+
expect(subject.size).to eq(2)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,3 +5,11 @@ if ENV["CODECLIMATE_REPO_TOKEN"]
|
|
5
5
|
require "codeclimate-test-reporter"
|
6
6
|
CodeClimate::TestReporter.start
|
7
7
|
end
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.disable_monkey_patching!
|
11
|
+
|
12
|
+
config.mock_with :rspec do |mocks|
|
13
|
+
mocks.verify_doubled_constant_names = true
|
14
|
+
end
|
15
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hotel_beds
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Townsend
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: savon
|
@@ -174,23 +174,37 @@ files:
|
|
174
174
|
- lib/hotel_beds/client.rb
|
175
175
|
- lib/hotel_beds/configuration.rb
|
176
176
|
- lib/hotel_beds/connection.rb
|
177
|
+
- lib/hotel_beds/hotel_basket_add/envelope.rb
|
178
|
+
- lib/hotel_beds/hotel_basket_add/operation.rb
|
179
|
+
- lib/hotel_beds/hotel_basket_add/request.rb
|
180
|
+
- lib/hotel_beds/hotel_basket_add/response.rb
|
177
181
|
- lib/hotel_beds/hotel_search/envelope.rb
|
178
182
|
- lib/hotel_beds/hotel_search/operation.rb
|
183
|
+
- lib/hotel_beds/hotel_search/parser/errors.rb
|
184
|
+
- lib/hotel_beds/hotel_search/parser/hotel.rb
|
185
|
+
- lib/hotel_beds/hotel_search/parser/price.rb
|
186
|
+
- lib/hotel_beds/hotel_search/parser/room.rb
|
187
|
+
- lib/hotel_beds/hotel_search/parser/room_grouper.rb
|
179
188
|
- lib/hotel_beds/hotel_search/request.rb
|
180
189
|
- lib/hotel_beds/hotel_search/response.rb
|
181
|
-
- lib/hotel_beds/hotel_search/room_grouper.rb
|
182
190
|
- lib/hotel_beds/model.rb
|
183
191
|
- lib/hotel_beds/model/available_room.rb
|
192
|
+
- lib/hotel_beds/model/cancellation_policy.rb
|
184
193
|
- lib/hotel_beds/model/hotel.rb
|
194
|
+
- lib/hotel_beds/model/purchase.rb
|
195
|
+
- lib/hotel_beds/model/requested_room.rb
|
196
|
+
- lib/hotel_beds/model/room.rb
|
185
197
|
- lib/hotel_beds/model/search_result.rb
|
198
|
+
- lib/hotel_beds/model/service.rb
|
186
199
|
- lib/hotel_beds/version.rb
|
200
|
+
- spec/features/adding_a_hotel_to_basket_spec.rb
|
187
201
|
- spec/features/grouped_hotel_search_spec.rb
|
188
202
|
- spec/features/hotel_search_spec.rb
|
189
203
|
- spec/lib/hotel_beds/client_spec.rb
|
190
204
|
- spec/lib/hotel_beds/configuration_spec.rb
|
191
205
|
- spec/lib/hotel_beds/connection_spec.rb
|
206
|
+
- spec/lib/hotel_beds/hotel_search/parser/room_grouper_spec.rb
|
192
207
|
- spec/lib/hotel_beds/hotel_search/request_spec.rb
|
193
|
-
- spec/lib/hotel_beds/hotel_search/room_grouper_spec.rb
|
194
208
|
- spec/lib/hotel_beds/version_spec.rb
|
195
209
|
- spec/spec_helper.rb
|
196
210
|
homepage: https://www.hotelsindex.com/open-source
|
@@ -218,12 +232,13 @@ signing_key:
|
|
218
232
|
specification_version: 4
|
219
233
|
summary: Interfaces with the HotelBeds SOAP API to book hotel rooms
|
220
234
|
test_files:
|
235
|
+
- spec/features/adding_a_hotel_to_basket_spec.rb
|
221
236
|
- spec/features/grouped_hotel_search_spec.rb
|
222
237
|
- spec/features/hotel_search_spec.rb
|
223
238
|
- spec/lib/hotel_beds/client_spec.rb
|
224
239
|
- spec/lib/hotel_beds/configuration_spec.rb
|
225
240
|
- spec/lib/hotel_beds/connection_spec.rb
|
241
|
+
- spec/lib/hotel_beds/hotel_search/parser/room_grouper_spec.rb
|
226
242
|
- spec/lib/hotel_beds/hotel_search/request_spec.rb
|
227
|
-
- spec/lib/hotel_beds/hotel_search/room_grouper_spec.rb
|
228
243
|
- spec/lib/hotel_beds/version_spec.rb
|
229
244
|
- spec/spec_helper.rb
|
@@ -1,64 +0,0 @@
|
|
1
|
-
require "hotel_beds/model/search_result"
|
2
|
-
|
3
|
-
# see INTERNALS.md for comment symbols
|
4
|
-
module HotelBeds
|
5
|
-
module HotelSearch
|
6
|
-
class RoomGrouper < Struct.new(:requested_rooms, :response_rooms)
|
7
|
-
def results
|
8
|
-
if requested_rooms.size == 1
|
9
|
-
response_rooms.map { |room| [room] }
|
10
|
-
else
|
11
|
-
combined_available_rooms
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
# returns an array of room combinations for all rooms
|
17
|
-
def combined_available_rooms
|
18
|
-
room_groups = combine_available_rooms_by_occupants.values
|
19
|
-
head, *rest = room_groups
|
20
|
-
head.product(*rest).map { |rooms| rooms.flatten.sort_by(&:id) }.uniq
|
21
|
-
end
|
22
|
-
|
23
|
-
# returns a hash of OK => RSRG
|
24
|
-
def combine_available_rooms_by_occupants
|
25
|
-
group_requested_room_count_by_occupants.to_a.inject(Hash.new) do |result, (key, count)|
|
26
|
-
result[key] = group_rooms_by_occupants.fetch(key).combination(count).to_a
|
27
|
-
result
|
28
|
-
end
|
29
|
-
rescue KeyError => e
|
30
|
-
Hash.new
|
31
|
-
end
|
32
|
-
|
33
|
-
# returns a hash of OK => RC
|
34
|
-
def group_requested_room_count_by_occupants
|
35
|
-
requested_rooms.inject(Hash.new) do |result, room|
|
36
|
-
key = occupant_key(room)
|
37
|
-
result[key] ||= 0
|
38
|
-
result[key] += 1
|
39
|
-
result
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# returns a hash of OK => AR
|
44
|
-
def group_rooms_by_occupants
|
45
|
-
@frozen_rooms ||= response_rooms.inject(Hash.new) do |result, room|
|
46
|
-
key = occupant_key(room)
|
47
|
-
result[key] ||= Array.new
|
48
|
-
1.upto([room.number_available, 5].min) do |i|
|
49
|
-
result[key].push(room)
|
50
|
-
end
|
51
|
-
result
|
52
|
-
end.freeze
|
53
|
-
end
|
54
|
-
|
55
|
-
# returns an OK for a given room
|
56
|
-
def occupant_key(room)
|
57
|
-
{
|
58
|
-
adult_count: room.adult_count,
|
59
|
-
child_count: room.child_count
|
60
|
-
}
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|