hotel_beds 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -44
  3. data/lib/hotel_beds/basket_remove/envelope.rb +16 -0
  4. data/lib/hotel_beds/basket_remove/operation.rb +36 -0
  5. data/lib/hotel_beds/basket_remove/request.rb +19 -0
  6. data/lib/hotel_beds/basket_remove/response.rb +31 -0
  7. data/lib/hotel_beds/client.rb +21 -0
  8. data/lib/hotel_beds/hotel_basket_add/request.rb +1 -1
  9. data/lib/hotel_beds/hotel_basket_add/response.rb +4 -35
  10. data/lib/hotel_beds/hotel_search/envelope.rb +10 -9
  11. data/lib/hotel_beds/hotel_search/request.rb +6 -4
  12. data/lib/hotel_beds/hotel_search/response.rb +4 -9
  13. data/lib/hotel_beds/model/available_room.rb +22 -1
  14. data/lib/hotel_beds/model/cancellation_policy.rb +0 -1
  15. data/lib/hotel_beds/model/comment.rb +13 -0
  16. data/lib/hotel_beds/model/contract.rb +15 -0
  17. data/lib/hotel_beds/model/customer.rb +21 -0
  18. data/lib/hotel_beds/model/destination.rb +13 -0
  19. data/lib/hotel_beds/model/hotel.rb +12 -7
  20. data/lib/hotel_beds/model/price.rb +18 -0
  21. data/lib/hotel_beds/model/purchase.rb +7 -2
  22. data/lib/hotel_beds/model/room.rb +0 -1
  23. data/lib/hotel_beds/model/service.rb +6 -4
  24. data/lib/hotel_beds/parser.rb +94 -0
  25. data/lib/hotel_beds/parser/available_room.rb +37 -0
  26. data/lib/hotel_beds/parser/cancellation_policy.rb +13 -0
  27. data/lib/hotel_beds/parser/comment.rb +13 -0
  28. data/lib/hotel_beds/parser/contract.rb +17 -0
  29. data/lib/hotel_beds/parser/customer.rb +18 -0
  30. data/lib/hotel_beds/parser/destination.rb +13 -0
  31. data/lib/hotel_beds/parser/errors.rb +39 -0
  32. data/lib/hotel_beds/parser/hotel.rb +27 -0
  33. data/lib/hotel_beds/parser/price.rb +14 -0
  34. data/lib/hotel_beds/parser/purchase.rb +24 -0
  35. data/lib/hotel_beds/parser/room_grouper.rb +104 -0
  36. data/lib/hotel_beds/parser/service.rb +21 -0
  37. data/lib/hotel_beds/purchase_confirm/envelope.rb +42 -0
  38. data/lib/hotel_beds/purchase_confirm/operation.rb +36 -0
  39. data/lib/hotel_beds/purchase_confirm/request.rb +72 -0
  40. data/lib/hotel_beds/purchase_confirm/response.rb +31 -0
  41. data/lib/hotel_beds/purchase_flush/envelope.rb +15 -0
  42. data/lib/hotel_beds/purchase_flush/operation.rb +36 -0
  43. data/lib/hotel_beds/purchase_flush/request.rb +17 -0
  44. data/lib/hotel_beds/purchase_flush/response.rb +26 -0
  45. data/lib/hotel_beds/version.rb +1 -1
  46. data/spec/features/flushing_the_basket_spec.rb +63 -0
  47. data/spec/features/ordering_a_hotel_room_spec.rb +96 -0
  48. data/spec/features/removing_a_basket_item_spec.rb +74 -0
  49. data/spec/lib/hotel_beds/hotel_search/request_spec.rb +4 -4
  50. data/spec/lib/hotel_beds/{hotel_search/parser → parser}/room_grouper_spec.rb +3 -3
  51. data/spec/lib/hotel_beds/parser/service_spec.rb +44 -0
  52. metadata +42 -15
  53. data/lib/hotel_beds/hotel_search/parser/errors.rb +0 -49
  54. data/lib/hotel_beds/hotel_search/parser/hotel.rb +0 -71
  55. data/lib/hotel_beds/hotel_search/parser/price.rb +0 -38
  56. data/lib/hotel_beds/hotel_search/parser/room.rb +0 -61
  57. data/lib/hotel_beds/hotel_search/parser/room_grouper.rb +0 -101
  58. data/spec/features/adding_a_hotel_to_basket_spec.rb +0 -58
  59. data/spec/features/grouped_hotel_search_spec.rb +0 -67
  60. data/spec/features/hotel_search_spec.rb +0 -113
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee35be3ac5b6c786cf81e423804de3fb925c32a7
4
- data.tar.gz: b9f28db962220a6b808fc2c5d3c7fed476d3fedb
3
+ metadata.gz: 7c3bea539610e13aca6d457ae561d8c62c5b52d0
4
+ data.tar.gz: 3b8365b3312998149cfa8bb1ede6a90aa5360525
5
5
  SHA512:
6
- metadata.gz: a5a5fd5bd3cc26f982de37ba7f34fedb03a9b0c43f841146e43d81d31b11a2cac08d5baa04ba691cc110b500662420810963ceda565a2e5a6a222146dafa9066
7
- data.tar.gz: 6ad26f49b7bb40ac1ac51ad4f6aec8dfaf39fa205ef92bc4e2e6cfad74eba043c2ff02427b959da7b44009a3dc4748c8d254e11b9547ec002cab532c3dbccde6
6
+ metadata.gz: 756489f73cc0a066d6771508404a01c01710a75fd32f73664d44e8a9f0d73f87d48db06fa626a64a0c6e3eb5c74ac74bf0b6bda0944f1b1b5f03058cb72117b7
7
+ data.tar.gz: 0fda52e2b50755e68809154ae0094239c94ae2c63c488686acfa550fd2c39920fb45187ad05e2e9018c49a7418bf1e95b2b9ae81eccab2fb8836f725bb644d85
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Codeship Status](https://www.codeship.io/projects/808271e0-e973-0131-1052-5240ebfefa5a/status)](https://www.codeship.io/projects/26188) [![Gem Version](https://badge.fury.io/rb/hotel_beds.svg)](https://rubygems.org/gems/hotel_beds) [![Code Climate](https://codeclimate.com/github/platformq/hotel_beds.png)](https://codeclimate.com/github/platformq/hotel_beds) [![Code Climate](https://codeclimate.com/github/platformq/hotel_beds/coverage.png)](https://codeclimate.com/github/platformq/hotel_beds)
1
+ [![Codeship Status](https://www.codeship.io/projects/808271e0-e973-0131-1052-5240ebfefa5a/status)](https://www.codeship.io/projects/26188) [![Gem Version](https://badge.fury.io/rb/hotel_beds.svg)](https://rubygems.org/gems/hotel_beds) [![Code Climate](https://codeclimate.com/github/platformq/hotel_beds/badges/gpa.svg)](https://codeclimate.com/github/platformq/hotel_beds) [![Code Climate](https://codeclimate.com/github/platformq/hotel_beds/badges/coverage.svg)](https://codeclimate.com/github/platformq/hotel_beds)
2
2
 
3
3
  The `hotel_beds` gem interfaces with the [HotelBeds.com](http://www.hotelbeds.com/) SOAP API to search for and book hotel rooms.
4
4
 
@@ -18,6 +18,8 @@ Manually, via command line:
18
18
 
19
19
  ## Usage
20
20
 
21
+ It's best to check the [feature specs](https://github.com/platformq/hotel_beds/tree/master/spec/features) to see up-to-date examples, but a basic search example is below:
22
+
21
23
  ```ruby
22
24
  # create the connection to HotelBeds
23
25
  client = HotelBeds::Client.new(endpoint: :test, username: "user", password: "pass")
@@ -27,7 +29,7 @@ search = client.perform_hotel_search({
27
29
  check_in_date: Date.today,
28
30
  check_out_date: Date.today + 1,
29
31
  rooms: [{ adult_count: 2 }],
30
- destination: "SYD"
32
+ destination_code: "SYD"
31
33
  })
32
34
 
33
35
  # inspect the response
@@ -37,48 +39,6 @@ puts search.response.total_pages
37
39
  # => 10
38
40
  puts search.response.current_page
39
41
  # => 1
40
-
41
- # place a booking
42
- booking = client.perform_hotel_booking({
43
- room_ids: [search.response.hotels.first.results.first.id],
44
- people: [
45
- { title: "Mr", name: "David Smith", type: :adult },
46
- { title: "Mrs", name: "Jane Smith", type: :adult }
47
- ],
48
- address: {
49
- line_1: "123 Some Street",
50
- city: "Townsville",
51
- state: "New Statestown",
52
- postcode: "NS1 1AB"
53
- country: "UK"
54
- },
55
- phone_number: "+44 1234 567 890",
56
- email: "david.smith@example.com"
57
- })
58
-
59
- # inspect the response
60
- puts booking.response.reference
61
- # => "ABC-123"
62
- ```
63
-
64
- ### Options
65
-
66
- 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.
67
-
68
- Example:
69
-
70
- ```ruby
71
- # perform the search
72
- search = client.perform_hotel_search({
73
- check_in_date: Date.today,
74
- check_out_date: Date.today + 1,
75
- rooms: [
76
- { adult_count: 2 },
77
- { adult_count: 1, child_count: 1, child_ages: [7] }
78
- ],
79
- destination: "SYD",
80
- group_results: true
81
- })
82
42
  ```
83
43
 
84
44
  ## Contributing
@@ -0,0 +1,16 @@
1
+ require "delegate"
2
+
3
+ module HotelBeds
4
+ module BasketRemove
5
+ class Envelope < SimpleDelegator
6
+ def attributes
7
+ {
8
+ :Language => language,
9
+ :@SPUI => service_id,
10
+ :@version => "2013/12",
11
+ :@purchaseToken => purchase_token
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ require_relative "request"
2
+ require_relative "response"
3
+ require_relative "envelope"
4
+
5
+ module HotelBeds
6
+ module BasketRemove
7
+ class Operation
8
+ attr_accessor :request, :response, :errors
9
+ private :request=, :response=, :errors=
10
+
11
+ def initialize(*args)
12
+ self.request = Request.new(*args)
13
+ end
14
+
15
+ def perform(connection:)
16
+ if request.valid?
17
+ self.response = Response.new(request, retrieve(connection))
18
+ self.errors = response.errors
19
+ else
20
+ self.errors = request.errors
21
+ end
22
+ freeze
23
+ self
24
+ end
25
+
26
+ private
27
+ def retrieve(connection)
28
+ connection.call({
29
+ method: :serviceRemove,
30
+ namespace: :ServiceRemoveRQ,
31
+ data: Envelope.new(request).attributes
32
+ })
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ require "hotel_beds/model"
2
+
3
+ module HotelBeds
4
+ module BasketRemove
5
+ class Request
6
+ include HotelBeds::Model
7
+
8
+ # attributes
9
+ attribute :language, String, default: "ENG"
10
+ attribute :purchase_token, String
11
+ attribute :service_id, String
12
+
13
+
14
+ # validation
15
+ validates :language, length: { is: 3 }
16
+ validates :service_id, :purchase_token, presence: true
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ require "hotel_beds/parser/errors"
2
+ require "hotel_beds/parser/purchase"
3
+
4
+ module HotelBeds
5
+ module BasketRemove
6
+ class Response
7
+ attr_accessor :request, :headers, :body, :errors
8
+ private :request=, :headers=, :body=, :errors=
9
+
10
+ def initialize(request, response)
11
+ self.request = request
12
+ self.headers = response.header
13
+ self.body = Nokogiri::XML(response.body.fetch(:service_remove))
14
+ self.errors = HotelBeds::Parser::Errors.new(response).to_model(self)
15
+ freeze
16
+ end
17
+
18
+ def inspect
19
+ "<#{self.class.name} errors=#{errors.inspect} headers=#{headers.inspect} body=#{body.to_s}>"
20
+ end
21
+
22
+ def success?
23
+ errors.empty?
24
+ end
25
+
26
+ def purchase
27
+ HotelBeds::Parser::Purchase.new(body.at_css("Purchase")).to_model
28
+ end
29
+ end
30
+ end
31
+ end
@@ -2,6 +2,9 @@ require "hotel_beds/configuration"
2
2
  require "hotel_beds/connection"
3
3
  require "hotel_beds/hotel_search/operation"
4
4
  require "hotel_beds/hotel_basket_add/operation"
5
+ require "hotel_beds/purchase_confirm/operation"
6
+ require "hotel_beds/purchase_flush/operation"
7
+ require "hotel_beds/basket_remove/operation"
5
8
 
6
9
  module HotelBeds
7
10
  class Client
@@ -28,5 +31,23 @@ module HotelBeds
28
31
  connection: connection
29
32
  )
30
33
  end
34
+
35
+ def confirm_purchase(*args)
36
+ PurchaseConfirm::Operation.new(*args).perform(
37
+ connection: connection
38
+ )
39
+ end
40
+
41
+ def flush_purchase(*args)
42
+ PurchaseFlush::Operation.new(*args).perform(
43
+ connection: connection
44
+ )
45
+ end
46
+
47
+ def remove_service_from_basket(*args)
48
+ BasketRemove::Operation.new(*args).perform(
49
+ connection: connection
50
+ )
51
+ end
31
52
  end
32
53
  end
@@ -19,7 +19,7 @@ module HotelBeds
19
19
  attribute :rooms, Array[HotelBeds::Model::AvailableRoom]
20
20
 
21
21
  # validation
22
- validates :destination_code, length: { is: 3 }
22
+ validates :destination_code, length: { is: 3, allow_blank: false }
23
23
  validates :rooms, length: { minimum: 1, maximum: 5 }
24
24
  validates :session_id, :availability_token, :contract_name,
25
25
  :contract_incoming_office_code, :check_in_date, :check_out_date,
@@ -1,5 +1,5 @@
1
- require "active_model/errors"
2
- require "hotel_beds/model/purchase"
1
+ require "hotel_beds/parser/errors"
2
+ require "hotel_beds/parser/purchase"
3
3
 
4
4
  module HotelBeds
5
5
  module HotelBasketAdd
@@ -11,22 +11,7 @@ module HotelBeds
11
11
  self.request = request
12
12
  self.headers = response.header
13
13
  self.body = Nokogiri::XML(response.body.fetch(:service_add))
14
- self.errors = ActiveModel::Errors.new(self).tap do |errors|
15
- if response.http_error?
16
- errors.add(:base, "HTTP error")
17
- elsif response.soap_fault?
18
- errors.add(:base, "SOAP error")
19
- elsif !response.success?
20
- errors.add(:base, "Request failed")
21
- end
22
-
23
- body.css("ErrorList Error").each do |error|
24
- errors.add(:base, [
25
- (sm = error.at_css("Message")) && sm.content,
26
- (dm = error.at_css("DetailedMessage")) && dm.content
27
- ].compact.join("\n"))
28
- end
29
- end
14
+ self.errors = HotelBeds::Parser::Errors.new(response).to_model(self)
30
15
  freeze
31
16
  end
32
17
 
@@ -43,23 +28,7 @@ module HotelBeds
43
28
  end
44
29
 
45
30
  def purchase
46
- purchase = body.at_css("Purchase")
47
- HotelBeds::Model::Purchase.new({
48
- id: purchase.attr("purchaseToken"),
49
- currency: purchase.at_css("Currency").attr("code"),
50
- amount: purchase.at_css("TotalPrice").content,
51
- services: purchase.css("ServiceList Service").map { |service|
52
- {
53
- id: service.attr("SPUI"),
54
- contract_name: service.at_css("Contract Name").content,
55
- contract_incoming_office_code: service.at_css("Contract IncomingOffice").attr("code"),
56
- check_in_date: Date.parse(service.at_css("DateFrom").attr("date")),
57
- check_out_date: Date.parse(service.at_css("DateTo").attr("date")),
58
- currency: service.at_css("Currency").attr("code"),
59
- amount: service.at_css("TotalAmount").content
60
- }
61
- }
62
- })
31
+ HotelBeds::Parser::Purchase.new(body.at_css("Purchase")).to_model
63
32
  end
64
33
  end
65
34
  end
@@ -30,7 +30,10 @@ module HotelBeds
30
30
  end
31
31
 
32
32
  def pagination_data
33
- { :@pageNumber => Integer(page_number) }
33
+ {
34
+ :@pageNumber => Integer(page_number),
35
+ :@itemsPerPage => Integer(items_per_page)
36
+ }
34
37
  end
35
38
 
36
39
  def language
@@ -46,19 +49,17 @@ module HotelBeds
46
49
  end
47
50
 
48
51
  def destination
49
- if __getobj__.destination
50
- { Destination: {
51
- :@code => String(__getobj__.destination).upcase,
52
- :@type => "SIMPLE"
53
- } }
54
- end
52
+ { Destination: {
53
+ :@code => String(__getobj__.destination_code).upcase,
54
+ :@type => "SIMPLE"
55
+ } }
55
56
  end
56
57
 
57
58
  def hotels
58
- if Array(__getobj__.hotels).any?
59
+ if Array(__getobj__.hotel_codes).any?
59
60
  { HotelCodeList: {
60
61
  :@withinResults => "Y",
61
- :ProductCode => Array(__getobj__.hotels)
62
+ :ProductCode => Array(__getobj__.hotel_codes)
62
63
  } }
63
64
  end
64
65
  end
@@ -10,16 +10,18 @@ module HotelBeds
10
10
  # attributes
11
11
  attribute :session_id, String, default: SecureRandom.hex[0..15]
12
12
  attribute :page_number, Integer, default: 1
13
+ attribute :items_per_page, Integer, default: 50
13
14
  attribute :language, String, default: "ENG"
14
15
  attribute :check_in_date, Date
15
16
  attribute :check_out_date, Date
16
- attribute :destination, String
17
- attribute :hotels, Array[Integer]
17
+ attribute :destination_code, String
18
+ attribute :hotel_codes, Array[Integer]
18
19
  attribute :rooms, Array[HotelBeds::Model::RequestedRoom]
19
- attribute :group_results, Virtus::Attribute::Boolean, default: false
20
20
 
21
21
  # validation
22
- validates :language, :destination, length: { is: 3 }
22
+ validates :language, :destination_code, length: {
23
+ is: 3, allow_blank: false
24
+ }
23
25
  validates :session_id, :check_in_date, :check_out_date, presence: true
24
26
  validates :rooms, length: { minimum: 1, maximum: 5 }
25
27
  validates :page_number, numericality: {
@@ -1,6 +1,5 @@
1
- require "hotel_beds/model/hotel"
2
- require "hotel_beds/hotel_search/parser/errors"
3
- require "hotel_beds/hotel_search/parser/hotel"
1
+ require "hotel_beds/parser/errors"
2
+ require "hotel_beds/parser/hotel"
4
3
 
5
4
  module HotelBeds
6
5
  module HotelSearch
@@ -12,7 +11,7 @@ module HotelBeds
12
11
  self.request = request
13
12
  self.headers = response.header
14
13
  self.body = Nokogiri::XML(response.body.fetch(:get_hotel_valued_avail))
15
- self.errors = HotelBeds::HotelSearch::Parser::Errors.call(response, body)
14
+ self.errors = HotelBeds::Parser::Errors.new(response).to_model(self)
16
15
  freeze
17
16
  end
18
17
 
@@ -46,11 +45,7 @@ module HotelBeds
46
45
 
47
46
  def hotels
48
47
  body.css("ServiceHotel").lazy.map do |hotel|
49
- HotelBeds::HotelSearch::Parser::Hotel.call(
50
- hotel: hotel,
51
- currency: currency,
52
- request: request
53
- )
48
+ HotelBeds::Parser::Hotel.new(hotel).to_model
54
49
  end
55
50
  end
56
51
 
@@ -1,5 +1,7 @@
1
1
  require "hotel_beds/model/room"
2
+ require "hotel_beds/model/price"
2
3
  require "hotel_beds/model/cancellation_policy"
4
+ require "hotel_beds/model/customer"
3
5
 
4
6
  module HotelBeds
5
7
  module Model
@@ -15,8 +17,27 @@ module HotelBeds
15
17
  attribute :price, BigDecimal
16
18
  attribute :number_available, Integer
17
19
  attribute :rates, Hash[Date => BigDecimal]
18
- attribute :cancellation_policies, Array[CancellationPolicy],
20
+ attribute :cancellation_policies, Array[HotelBeds::Model::CancellationPolicy],
19
21
  default: Array.new
22
+ attribute :customers, Array[HotelBeds::Model::Customer],
23
+ default: Array.new
24
+
25
+ def rates=(values)
26
+ if values.kind_of?(Array)
27
+ prices = values.map do |attrs|
28
+ HotelBeds::Model::Price.new(attrs)
29
+ end
30
+ hash = prices.inject(Hash.new) do |result, price|
31
+ price.dates.each do |date|
32
+ result.merge!(date => price.amount)
33
+ end
34
+ result
35
+ end
36
+ super(hash)
37
+ else
38
+ super
39
+ end
40
+ end
20
41
  end
21
42
  end
22
43
  end
@@ -8,7 +8,6 @@ module HotelBeds
8
8
  # attributes
9
9
  attribute :amount, BigDecimal
10
10
  attribute :from, Time
11
- attribute :to, Date
12
11
  end
13
12
  end
14
13
  end
@@ -0,0 +1,13 @@
1
+ require "hotel_beds/model"
2
+
3
+ module HotelBeds
4
+ module Model
5
+ class Comment
6
+ include HotelBeds::Model
7
+
8
+ # attributes
9
+ attribute :type, String
10
+ attribute :content, String
11
+ end
12
+ end
13
+ end