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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/INTERNALS.md +6 -0
  3. data/README.md +55 -29
  4. data/lib/hotel_beds/client.rb +11 -3
  5. data/lib/hotel_beds/connection.rb +2 -3
  6. data/lib/hotel_beds/hotel_basket_add/envelope.rb +71 -0
  7. data/lib/hotel_beds/hotel_basket_add/operation.rb +36 -0
  8. data/lib/hotel_beds/hotel_basket_add/request.rb +49 -0
  9. data/lib/hotel_beds/hotel_basket_add/response.rb +66 -0
  10. data/lib/hotel_beds/hotel_search/envelope.rb +27 -18
  11. data/lib/hotel_beds/hotel_search/parser/errors.rb +49 -0
  12. data/lib/hotel_beds/hotel_search/parser/hotel.rb +67 -0
  13. data/lib/hotel_beds/hotel_search/parser/price.rb +38 -0
  14. data/lib/hotel_beds/hotel_search/parser/room.rb +46 -0
  15. data/lib/hotel_beds/hotel_search/parser/room_grouper.rb +101 -0
  16. data/lib/hotel_beds/hotel_search/request.rb +5 -23
  17. data/lib/hotel_beds/hotel_search/response.rb +16 -68
  18. data/lib/hotel_beds/model.rb +0 -1
  19. data/lib/hotel_beds/model/available_room.rb +9 -11
  20. data/lib/hotel_beds/model/cancellation_policy.rb +14 -0
  21. data/lib/hotel_beds/model/hotel.rb +4 -0
  22. data/lib/hotel_beds/model/purchase.rb +16 -0
  23. data/lib/hotel_beds/model/requested_room.rb +7 -0
  24. data/lib/hotel_beds/model/room.rb +31 -0
  25. data/lib/hotel_beds/model/search_result.rb +0 -5
  26. data/lib/hotel_beds/model/service.rb +18 -0
  27. data/lib/hotel_beds/version.rb +1 -1
  28. data/spec/features/adding_a_hotel_to_basket_spec.rb +58 -0
  29. data/spec/features/hotel_search_spec.rb +31 -9
  30. data/spec/lib/hotel_beds/hotel_search/parser/room_grouper_spec.rb +53 -0
  31. data/spec/spec_helper.rb +8 -0
  32. metadata +20 -5
  33. data/lib/hotel_beds/hotel_search/room_grouper.rb +0 -64
  34. data/spec/lib/hotel_beds/hotel_search/room_grouper_spec.rb +0 -45
@@ -5,7 +5,6 @@ module HotelBeds
5
5
  module Model
6
6
  def self.included(base)
7
7
  base.class_eval do
8
- include Comparable
9
8
  include Virtus.model
10
9
  include ActiveModel::Validations
11
10
  end
@@ -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 :adult_count, Integer
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
- # compare based on id
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
@@ -0,0 +1,14 @@
1
+ require "hotel_beds/model"
2
+
3
+ module HotelBeds
4
+ module Model
5
+ class CancellationPolicy
6
+ include HotelBeds::Model
7
+
8
+ # attributes
9
+ attribute :amount, BigDecimal
10
+ attribute :from, Time
11
+ attribute :to, Date
12
+ end
13
+ end
14
+ 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,7 @@
1
+ require "hotel_beds/model/room"
2
+
3
+ module HotelBeds
4
+ module Model
5
+ class RequestedRoom < Room; end
6
+ end
7
+ 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
@@ -1,3 +1,3 @@
1
1
  module HotelBeds
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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 parse the rates correctly" do
44
- room = subject.first.results.first.rooms.first
45
- expect(room.price).to eq(room.rates.values.inject(:+))
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 the same number of rates as nights requested" do
49
- room = subject.first.results.first.rooms.first
50
- expect(room.rates.size).to eq(check_out_date - check_in_date)
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 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])
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.2.1
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-22 00:00:00.000000000 Z
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