hotel_beds 0.1.3 → 0.2.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.
- checksums.yaml +4 -4
- data/.tm_properties +5 -0
- data/INTERNALS.md +9 -0
- data/README.md +20 -2
- data/lib/hotel_beds/hotel_search/envelope.rb +16 -14
- data/lib/hotel_beds/hotel_search/operation.rb +5 -5
- data/lib/hotel_beds/hotel_search/request.rb +6 -5
- data/lib/hotel_beds/hotel_search/response.rb +31 -26
- data/lib/hotel_beds/hotel_search/room_grouper.rb +64 -0
- data/lib/hotel_beds/model.rb +1 -0
- data/lib/hotel_beds/model/available_room.rb +13 -3
- data/lib/hotel_beds/model/hotel.rb +3 -3
- data/lib/hotel_beds/model/search_result.rb +19 -0
- data/lib/hotel_beds/version.rb +1 -1
- data/spec/features/grouped_hotel_search_spec.rb +67 -0
- data/spec/features/hotel_search_spec.rb +11 -6
- data/spec/lib/hotel_beds/hotel_search/room_grouper_spec.rb +45 -0
- metadata +10 -3
- data/lib/hotel_beds/model/hotel_room.rb +0 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f4e1d67927848975b80e7a3dc3cd80654105e485
|
|
4
|
+
data.tar.gz: 65aadd0fb3f316987e34d5aa83dbe4c3d50319a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7a795daa686af484ce5bede7955a7133dddeece24b065f4da8c0d372c03671c9e3f3d311ed52b872bccda910524b5d4a8295d9320fcb5e14619ea2d6c45c5a6c
|
|
7
|
+
data.tar.gz: 5ba1daeadd74a320a985378997bd8f0cc9f670fd5f78051404dc0adf4fee4b8a29af12293d6e3ce66af91f6e9c56a68a21f66850fdd98f7d72e9354630d503fa
|
data/.tm_properties
ADDED
data/INTERNALS.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Grouping results
|
|
2
|
+
|
|
3
|
+
The process we use to group results is as follows:
|
|
4
|
+
|
|
5
|
+
1) Group the available rooms (AR) by occupants (OK)
|
|
6
|
+
2) Expand the rooms (AR) in the array to illustrate the availability of each room (limit to a max of 5, as we don't support searches of more than 5 rooms)
|
|
7
|
+
3) Group the count of each requested room (RC) by occupants (OK)
|
|
8
|
+
4) For each group, determine all combinations of the available rooms (AR) for the given count (RC), this gives us the search results per room group (RSRG)
|
|
9
|
+
5) Find all combinations of the search results per room group, giving us unique search results (SR)
|
data/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Manually, via command line:
|
|
|
18
18
|
|
|
19
19
|
# create the connection to HotelBeds
|
|
20
20
|
client = HotelBeds::Client.new(endpoint: :test, username: "user", password: "pass")
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
# perform the search
|
|
23
23
|
search = client.perform_hotel_search({
|
|
24
24
|
check_in_date: Date.today,
|
|
@@ -26,7 +26,7 @@ Manually, via command line:
|
|
|
26
26
|
rooms: [{ adult_count: 2 }],
|
|
27
27
|
destination: "SYD"
|
|
28
28
|
})
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
# inspect the response
|
|
31
31
|
puts search.response.hotels
|
|
32
32
|
# => [<HotelBeds::Model::Hotel>, <HotelBeds::Model::Hotel>]
|
|
@@ -35,6 +35,24 @@ Manually, via command line:
|
|
|
35
35
|
puts search.response.current_page
|
|
36
36
|
# => 1
|
|
37
37
|
|
|
38
|
+
### Options
|
|
39
|
+
|
|
40
|
+
The HotelBeds API will return individual rooms, rather than being grouped by what you searched for (e.g. 2 rooms, 1 with 2 adults, 1 with 1 adult and 1 child). To fix this issue, you can enable result grouping by adding `group_results: true` to the `perform_hotel_search` call.
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
|
|
44
|
+
# perform the search
|
|
45
|
+
search = client.perform_hotel_search({
|
|
46
|
+
check_in_date: Date.today,
|
|
47
|
+
check_out_date: Date.today + 1,
|
|
48
|
+
rooms: [
|
|
49
|
+
{ adult_count: 2 },
|
|
50
|
+
{ adult_count: 1, child_count: 1, child_ages: [7] }
|
|
51
|
+
],
|
|
52
|
+
destination: "SYD",
|
|
53
|
+
group_results: true
|
|
54
|
+
})
|
|
55
|
+
|
|
38
56
|
## Contributing
|
|
39
57
|
|
|
40
58
|
1. [Fork it](https://github.com/platformq/hotel_beds/fork)
|
|
@@ -12,7 +12,7 @@ module HotelBeds
|
|
|
12
12
|
OccupancyList: occupancy_list
|
|
13
13
|
}.merge(Hash(destination)).merge(Hash(hotels)).merge(Hash(extra_params))
|
|
14
14
|
end
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
private
|
|
17
17
|
def extra_params
|
|
18
18
|
{ ExtraParamList: {
|
|
@@ -27,7 +27,7 @@ module HotelBeds
|
|
|
27
27
|
}]
|
|
28
28
|
} }
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
def pagination_data
|
|
32
32
|
{ :@pageNumber => Integer(page_number) }
|
|
33
33
|
end
|
|
@@ -52,7 +52,7 @@ module HotelBeds
|
|
|
52
52
|
} }
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
def hotels
|
|
57
57
|
if Array(__getobj__.hotels).any?
|
|
58
58
|
{ HotelCodeList: {
|
|
@@ -61,20 +61,22 @@ module HotelBeds
|
|
|
61
61
|
} }
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
def occupancy_list
|
|
66
|
-
Array(rooms).map
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
{ HotelOccupancy: Array(rooms).map { |room|
|
|
67
|
+
guest_list = if room.child_count > 0
|
|
68
|
+
{ GuestList: { Customer: Array(room.child_ages).map { |age|
|
|
69
|
+
{ :@type => "CH", :Age => Integer(age) }
|
|
70
|
+
} } }
|
|
71
|
+
end
|
|
72
|
+
{
|
|
73
|
+
RoomCount: 1,
|
|
74
|
+
Occupancy: (guest_list || Hash.new).merge({
|
|
70
75
|
AdultCount: Integer(room.adult_count),
|
|
71
76
|
ChildCount: Integer(room.child_count)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
} }
|
|
77
|
-
end
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
} }
|
|
78
80
|
end
|
|
79
81
|
end
|
|
80
82
|
end
|
|
@@ -7,14 +7,14 @@ module HotelBeds
|
|
|
7
7
|
class Operation
|
|
8
8
|
attr_accessor :request, :response, :errors
|
|
9
9
|
private :request=, :response=, :errors=
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
def initialize(*args)
|
|
12
12
|
self.request = Request.new(*args)
|
|
13
13
|
end
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
def perform(connection:)
|
|
16
16
|
if request.valid?
|
|
17
|
-
self.response = Response.new(retrieve(connection))
|
|
17
|
+
self.response = Response.new(request, retrieve(connection))
|
|
18
18
|
self.errors = response.errors
|
|
19
19
|
else
|
|
20
20
|
self.errors = request.errors
|
|
@@ -22,11 +22,11 @@ module HotelBeds
|
|
|
22
22
|
freeze
|
|
23
23
|
self
|
|
24
24
|
end
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
private
|
|
27
27
|
def retrieve(connection)
|
|
28
28
|
connection.call({
|
|
29
|
-
method: :getHotelValuedAvail,
|
|
29
|
+
method: :getHotelValuedAvail,
|
|
30
30
|
namespace: :HotelValuedAvailRQ,
|
|
31
31
|
data: Envelope.new(request).attributes
|
|
32
32
|
})
|
|
@@ -3,14 +3,14 @@ require "hotel_beds/model"
|
|
|
3
3
|
module HotelBeds
|
|
4
4
|
module HotelSearch
|
|
5
5
|
class Request
|
|
6
|
-
class Room
|
|
6
|
+
class Room
|
|
7
7
|
include HotelBeds::Model
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
# attributes
|
|
10
10
|
attribute :adult_count, Integer, default: 0
|
|
11
11
|
attribute :child_count, Integer, default: 0
|
|
12
12
|
attribute :child_ages, Array[Integer], default: Array.new
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
# validation
|
|
15
15
|
validates :adult_count, :child_count, numericality: {
|
|
16
16
|
greater_than_or_equal_to: 0,
|
|
@@ -23,7 +23,7 @@ module HotelBeds
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
include HotelBeds::Model
|
|
28
28
|
|
|
29
29
|
# attributes
|
|
@@ -34,7 +34,8 @@ module HotelBeds
|
|
|
34
34
|
attribute :destination, String
|
|
35
35
|
attribute :hotels, Array[Integer]
|
|
36
36
|
attribute :rooms, Array[Room]
|
|
37
|
-
|
|
37
|
+
attribute :group_results, Virtus::Attribute::Boolean, default: false
|
|
38
|
+
|
|
38
39
|
# validation
|
|
39
40
|
validates :language, :destination, length: { is: 3 }
|
|
40
41
|
validates :check_in_date, :check_out_date, presence: true
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
require "active_model/errors"
|
|
2
2
|
require "hotel_beds/model/hotel"
|
|
3
|
+
require "hotel_beds/model/available_room"
|
|
4
|
+
require_relative "room_grouper"
|
|
3
5
|
|
|
4
6
|
module HotelBeds
|
|
5
7
|
module HotelSearch
|
|
6
8
|
class Response
|
|
7
|
-
attr_accessor :headers, :body, :errors
|
|
8
|
-
private :headers=, :body=, :errors=
|
|
9
|
-
|
|
10
|
-
def initialize(response)
|
|
9
|
+
attr_accessor :request, :headers, :body, :errors
|
|
10
|
+
private :request=, :headers=, :body=, :errors=
|
|
11
|
+
|
|
12
|
+
def initialize(request, response)
|
|
13
|
+
self.request = request
|
|
11
14
|
self.headers = response.header
|
|
12
15
|
self.body = Nokogiri::XML(response.body.fetch(:get_hotel_valued_avail))
|
|
13
16
|
self.errors = ActiveModel::Errors.new(self).tap do |errors|
|
|
@@ -28,11 +31,11 @@ module HotelBeds
|
|
|
28
31
|
end
|
|
29
32
|
freeze
|
|
30
33
|
end
|
|
31
|
-
|
|
34
|
+
|
|
32
35
|
def inspect
|
|
33
36
|
"<#{self.class.name} errors=#{errors.inspect} headers=#{headers.inspect} body=#{body.to_s}>"
|
|
34
37
|
end
|
|
35
|
-
|
|
38
|
+
|
|
36
39
|
def current_page
|
|
37
40
|
if data = pagination_data
|
|
38
41
|
Integer(data.attr("currentPage"))
|
|
@@ -40,7 +43,7 @@ module HotelBeds
|
|
|
40
43
|
0
|
|
41
44
|
end
|
|
42
45
|
end
|
|
43
|
-
|
|
46
|
+
|
|
44
47
|
def total_pages
|
|
45
48
|
if data = pagination_data
|
|
46
49
|
Integer(data.attr("totalPages"))
|
|
@@ -48,7 +51,7 @@ module HotelBeds
|
|
|
48
51
|
0
|
|
49
52
|
end
|
|
50
53
|
end
|
|
51
|
-
|
|
54
|
+
|
|
52
55
|
def hotels
|
|
53
56
|
body.css("ServiceHotel").lazy.map do |hotel|
|
|
54
57
|
HotelBeds::Model::Hotel.new({
|
|
@@ -57,37 +60,39 @@ module HotelBeds
|
|
|
57
60
|
images: hotel.css("HotelInfo ImageList Image Url").map(&:content),
|
|
58
61
|
latitude: hotel.at_css("HotelInfo Position").attr("latitude"),
|
|
59
62
|
longitude: hotel.at_css("HotelInfo Position").attr("longitude"),
|
|
60
|
-
results:
|
|
63
|
+
results: generate_results(hotel.css("AvailableRoom"))
|
|
61
64
|
})
|
|
62
65
|
end
|
|
63
66
|
end
|
|
64
|
-
|
|
67
|
+
|
|
65
68
|
private
|
|
66
69
|
def pagination_data
|
|
67
70
|
body.at_css("PaginationData")
|
|
68
71
|
end
|
|
69
|
-
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
|
|
73
|
+
def generate_results(rooms)
|
|
74
|
+
parsed_rooms = parse_available_rooms(rooms)
|
|
75
|
+
if request.group_results?
|
|
76
|
+
parsed_rooms = RoomGrouper.new(request.rooms, parsed_rooms).results
|
|
77
|
+
end
|
|
78
|
+
parsed_rooms.map do |rooms|
|
|
79
|
+
HotelBeds::Model::SearchResult.new(rooms: Array(rooms))
|
|
77
80
|
end
|
|
78
81
|
end
|
|
79
82
|
|
|
80
|
-
def
|
|
83
|
+
def parse_available_rooms(rooms)
|
|
81
84
|
Array(rooms).map do |room|
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
HotelBeds::Model::AvailableRoom.new({
|
|
86
|
+
id: room.at_css("HotelRoom").attr("SHRUI"),
|
|
87
|
+
adult_count: room.at_css("HotelOccupancy AdultCount").content,
|
|
88
|
+
child_count: room.at_css("HotelOccupancy ChildCount").content,
|
|
89
|
+
number_available: room.at_css("HotelRoom").attr("availCount"),
|
|
90
|
+
description: room.at_css("HotelRoom RoomType").content,
|
|
91
|
+
board: room.at_css("HotelRoom Board").content,
|
|
88
92
|
currency: body.at_css("Currency").attr("code"),
|
|
93
|
+
price: ((room.at_css("HotelRoom") > "Price") > "Amount").first.content,
|
|
89
94
|
rates: parse_price_list(room.css("Price PriceList Price"))
|
|
90
|
-
}
|
|
95
|
+
})
|
|
91
96
|
end
|
|
92
97
|
end
|
|
93
98
|
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
data/lib/hotel_beds/model.rb
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
require "hotel_beds/model"
|
|
2
|
-
require_relative "hotel_room"
|
|
3
2
|
|
|
4
3
|
module HotelBeds
|
|
5
4
|
module Model
|
|
6
5
|
class AvailableRoom
|
|
7
6
|
include HotelBeds::Model
|
|
8
|
-
|
|
7
|
+
|
|
9
8
|
# attributes
|
|
10
|
-
attribute :
|
|
9
|
+
attribute :id, Integer
|
|
11
10
|
attribute :adult_count, Integer
|
|
12
11
|
attribute :child_count, Integer
|
|
12
|
+
attribute :description, String
|
|
13
|
+
attribute :board, String
|
|
14
|
+
attribute :price, BigDecimal
|
|
15
|
+
attribute :currency, String
|
|
16
|
+
attribute :number_available, Integer
|
|
17
|
+
attribute :rates, Hash[Date => BigDecimal]
|
|
18
|
+
|
|
19
|
+
# compare based on id
|
|
20
|
+
def <=>(other)
|
|
21
|
+
id == other.id && !id.nil?
|
|
22
|
+
end
|
|
13
23
|
end
|
|
14
24
|
end
|
|
15
25
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require "hotel_beds/model"
|
|
2
|
-
require_relative "
|
|
2
|
+
require_relative "search_result"
|
|
3
3
|
|
|
4
4
|
module HotelBeds
|
|
5
5
|
module Model
|
|
@@ -13,7 +13,7 @@ module HotelBeds
|
|
|
13
13
|
attribute :stars, Integer
|
|
14
14
|
attribute :longitude, BigDecimal
|
|
15
15
|
attribute :latitude, BigDecimal
|
|
16
|
-
attribute :results, Array[
|
|
16
|
+
attribute :results, Array[SearchResult]
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
|
-
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require "hotel_beds/model"
|
|
2
|
+
require_relative "available_room"
|
|
3
|
+
|
|
4
|
+
module HotelBeds
|
|
5
|
+
module Model
|
|
6
|
+
class SearchResult
|
|
7
|
+
include HotelBeds::Model
|
|
8
|
+
|
|
9
|
+
# attributes
|
|
10
|
+
attribute :rooms, Array[AvailableRoom]
|
|
11
|
+
attribute :currency, String
|
|
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
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/hotel_beds/version.rb
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require "hotel_beds"
|
|
2
|
+
|
|
3
|
+
RSpec.describe "performing a grouped hotel search" 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
|
+
@operation = @client.perform_hotel_search({
|
|
15
|
+
check_in_date: @check_in_date,
|
|
16
|
+
check_out_date: @check_out_date,
|
|
17
|
+
rooms: [
|
|
18
|
+
{ adult_count: 2 },
|
|
19
|
+
{ adult_count: 1, child_count: 1, child_ages: [rand(17)] }
|
|
20
|
+
],
|
|
21
|
+
destination: "SYD",
|
|
22
|
+
group_results: true
|
|
23
|
+
})
|
|
24
|
+
if @operation.errors.any?
|
|
25
|
+
raise StandardError, @operation.errors.full_messages.join("\n")
|
|
26
|
+
end
|
|
27
|
+
@response = @operation.response
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
let(:check_in_date) { @check_in_date }
|
|
31
|
+
let(:check_out_date) { @check_out_date }
|
|
32
|
+
let(:response) { @response }
|
|
33
|
+
|
|
34
|
+
describe "#errors" do
|
|
35
|
+
subject { response.errors }
|
|
36
|
+
|
|
37
|
+
it "should be empty" do
|
|
38
|
+
expect(subject).to be_empty
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "#hotels" do
|
|
43
|
+
subject { response.hotels }
|
|
44
|
+
|
|
45
|
+
it "should have room counts to match the searched room count" do
|
|
46
|
+
room_counts = subject.map { |h| h.results.map { |r| r.rooms.size } }
|
|
47
|
+
expect(room_counts.to_a.flatten.uniq).to eq([response.request.rooms.size])
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe "#current_page" do
|
|
52
|
+
subject { response.current_page }
|
|
53
|
+
|
|
54
|
+
it "should return '1'" do
|
|
55
|
+
expect(subject).to eq(1)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe "#total_pages" do
|
|
60
|
+
subject { response.total_pages }
|
|
61
|
+
|
|
62
|
+
it "should be greater or equal to current page" do
|
|
63
|
+
expect(subject).to be >= response.current_page
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -25,12 +25,12 @@ RSpec.describe "performing a hotel search" do
|
|
|
25
25
|
|
|
26
26
|
describe "#errors" do
|
|
27
27
|
subject { response.errors }
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
it "should be empty" do
|
|
30
30
|
expect(subject).to be_empty
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
describe "#hotels" do
|
|
35
35
|
subject { response.hotels }
|
|
36
36
|
|
|
@@ -39,18 +39,23 @@ RSpec.describe "performing a hotel search" do
|
|
|
39
39
|
expect(subject.to_a).to_not be_empty
|
|
40
40
|
expect(subject.first).to be_kind_of(HotelBeds::Model::Hotel)
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
it "should parse the rates correctly" do
|
|
44
44
|
room = subject.first.results.first.rooms.first
|
|
45
45
|
expect(room.price).to eq(room.rates.values.inject(:+))
|
|
46
46
|
end
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
it "should have the same number of rates as nights requested" do
|
|
49
49
|
room = subject.first.results.first.rooms.first
|
|
50
50
|
expect(room.rates.size).to eq(check_out_date - check_in_date)
|
|
51
51
|
end
|
|
52
|
+
|
|
53
|
+
it "should only have one room per result" do
|
|
54
|
+
room_counts = subject.map { |h| h.results.map { |r| r.rooms.size } }
|
|
55
|
+
expect(room_counts.to_a.flatten.uniq).to eq([1])
|
|
56
|
+
end
|
|
52
57
|
end
|
|
53
|
-
|
|
58
|
+
|
|
54
59
|
describe "#current_page" do
|
|
55
60
|
subject { response.current_page }
|
|
56
61
|
|
|
@@ -58,7 +63,7 @@ RSpec.describe "performing a hotel search" do
|
|
|
58
63
|
expect(subject).to eq(1)
|
|
59
64
|
end
|
|
60
65
|
end
|
|
61
|
-
|
|
66
|
+
|
|
62
67
|
describe "#total_pages" do
|
|
63
68
|
subject { response.total_pages }
|
|
64
69
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require "hotel_beds/hotel_search/room_grouper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe HotelBeds::HotelSearch::RoomGrouper do
|
|
4
|
+
subject { described_class.new(requested_rooms, response_rooms).results }
|
|
5
|
+
|
|
6
|
+
context "when asking for 2 rooms (2 adults & 1 adult, 1 child)" do
|
|
7
|
+
let(:requested_rooms) do
|
|
8
|
+
[
|
|
9
|
+
double(:requested_room, adult_count: 2, child_count: 0),
|
|
10
|
+
double(:requested_room, adult_count: 1, child_count: 1)
|
|
11
|
+
]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
let(:response_rooms) do
|
|
15
|
+
[
|
|
16
|
+
double(:response_room_1, id: "room_1", adult_count: 2, child_count: 0, number_available: 1),
|
|
17
|
+
double(:response_room_2, id: "room_2", adult_count: 2, child_count: 0, number_available: 1),
|
|
18
|
+
double(:response_room_3, id: "room_3", adult_count: 1, child_count: 1, number_available: 2),
|
|
19
|
+
double(:response_room_4, id: "room_4", adult_count: 1, child_count: 1, number_available: 1),
|
|
20
|
+
double(:response_room_5, id: "room_5", adult_count: 2, child_count: 0, number_available: 2)
|
|
21
|
+
]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should return 6 results" do
|
|
25
|
+
expect(subject.size).to eq(6)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
context "when asking for 1 rooms (1 adult, 1 child)" do
|
|
30
|
+
let(:requested_rooms) do
|
|
31
|
+
[double(:requested_room, adult_count: 1, child_count: 1)]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
let(:response_rooms) do
|
|
35
|
+
[
|
|
36
|
+
double(:response_room_1, id: "room_1", adult_count: 1, child_count: 1, number_available: 2),
|
|
37
|
+
double(:response_room_2, id: "room_2", adult_count: 1, child_count: 1, number_available: 1)
|
|
38
|
+
]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should return 2 results" do
|
|
42
|
+
expect(subject.size).to eq(2)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
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.2.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-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: savon
|
|
@@ -162,8 +162,10 @@ files:
|
|
|
162
162
|
- ".rspec"
|
|
163
163
|
- ".ruby-gemset"
|
|
164
164
|
- ".ruby-version"
|
|
165
|
+
- ".tm_properties"
|
|
165
166
|
- Gemfile
|
|
166
167
|
- Guardfile
|
|
168
|
+
- INTERNALS.md
|
|
167
169
|
- LICENSE.txt
|
|
168
170
|
- README.md
|
|
169
171
|
- Rakefile
|
|
@@ -176,16 +178,19 @@ files:
|
|
|
176
178
|
- lib/hotel_beds/hotel_search/operation.rb
|
|
177
179
|
- lib/hotel_beds/hotel_search/request.rb
|
|
178
180
|
- lib/hotel_beds/hotel_search/response.rb
|
|
181
|
+
- lib/hotel_beds/hotel_search/room_grouper.rb
|
|
179
182
|
- lib/hotel_beds/model.rb
|
|
180
183
|
- lib/hotel_beds/model/available_room.rb
|
|
181
184
|
- lib/hotel_beds/model/hotel.rb
|
|
182
|
-
- lib/hotel_beds/model/
|
|
185
|
+
- lib/hotel_beds/model/search_result.rb
|
|
183
186
|
- lib/hotel_beds/version.rb
|
|
187
|
+
- spec/features/grouped_hotel_search_spec.rb
|
|
184
188
|
- spec/features/hotel_search_spec.rb
|
|
185
189
|
- spec/lib/hotel_beds/client_spec.rb
|
|
186
190
|
- spec/lib/hotel_beds/configuration_spec.rb
|
|
187
191
|
- spec/lib/hotel_beds/connection_spec.rb
|
|
188
192
|
- spec/lib/hotel_beds/hotel_search/request_spec.rb
|
|
193
|
+
- spec/lib/hotel_beds/hotel_search/room_grouper_spec.rb
|
|
189
194
|
- spec/lib/hotel_beds/version_spec.rb
|
|
190
195
|
- spec/spec_helper.rb
|
|
191
196
|
homepage: https://www.hotelsindex.com/open-source
|
|
@@ -213,10 +218,12 @@ signing_key:
|
|
|
213
218
|
specification_version: 4
|
|
214
219
|
summary: Interfaces with the HotelBeds SOAP API to book hotel rooms
|
|
215
220
|
test_files:
|
|
221
|
+
- spec/features/grouped_hotel_search_spec.rb
|
|
216
222
|
- spec/features/hotel_search_spec.rb
|
|
217
223
|
- spec/lib/hotel_beds/client_spec.rb
|
|
218
224
|
- spec/lib/hotel_beds/configuration_spec.rb
|
|
219
225
|
- spec/lib/hotel_beds/connection_spec.rb
|
|
220
226
|
- spec/lib/hotel_beds/hotel_search/request_spec.rb
|
|
227
|
+
- spec/lib/hotel_beds/hotel_search/room_grouper_spec.rb
|
|
221
228
|
- spec/lib/hotel_beds/version_spec.rb
|
|
222
229
|
- spec/spec_helper.rb
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
require "hotel_beds/model"
|
|
2
|
-
|
|
3
|
-
module HotelBeds
|
|
4
|
-
module Model
|
|
5
|
-
class HotelRoom
|
|
6
|
-
include HotelBeds::Model
|
|
7
|
-
|
|
8
|
-
# attributes
|
|
9
|
-
attribute :id, Integer
|
|
10
|
-
attribute :description, String
|
|
11
|
-
attribute :board, String
|
|
12
|
-
attribute :price, BigDecimal
|
|
13
|
-
attribute :currency, String
|
|
14
|
-
attribute :number_available, Integer
|
|
15
|
-
attribute :rates, Hash[Date => BigDecimal]
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|