hotel_beds 0.0.2 → 0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 305bb6ccb1f0f40a6a27186565f3ce5ddb853c76
4
- data.tar.gz: ff0b31e19ca3939f4da18e9dfc5a5b6608113486
3
+ metadata.gz: 55dc805389b4efeb9ef72bd55a2399ec361b8092
4
+ data.tar.gz: 8d2a36c3f58e9b573dfe41d30d81436157c71c4e
5
5
  SHA512:
6
- metadata.gz: cdee1089ee23fb3df5543a81626e4eeb73391b9524f35d0a98562e3df9ca041edb6893afbb4db422d7eb942fc40097fe0bbeaa9a7c1bd763fc94e1b30691828f
7
- data.tar.gz: 0966d2c596f0f6ac2d65a0621720ff6badaca68c84880c290dc3a4ada95f8d2c9bdc55be6eeee0ab6b0bec1318ba65a27c378b90d62c1154075bf69216bcb0fb
6
+ metadata.gz: c1c5da4a532371c2b08dda8ef38d6393b1954c8d81672e355ae8b9082e4a347d8ab4f43a2af05446295dbd16de7560f15df43c2f0751602b8ea82a85a8cac53a
7
+ data.tar.gz: bfc1dc68a2794149cb659e82211ca595f564f2bdeccc5bb49a729dea40e79fda2cc0c06bcfc32dac977c9133ca96b29c7af7d840917c2232675a2ec65967ffee
data/Gemfile CHANGED
@@ -1,2 +1,3 @@
1
1
  source "https://rubygems.org"
2
+ ruby "2.1.1"
2
3
  gemspec
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Code Climate](https://codeclimate.com/github/platformq/hotel_beds.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) [![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)
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
 
@@ -16,11 +16,24 @@ Manually, via command line:
16
16
 
17
17
  ## Usage
18
18
 
19
+ # create the connection to HotelBeds
19
20
  client = HotelBeds::Client.new(endpoint: :test, username: "user", password: "pass")
20
- search = HotelBeds::Model::Search.new(check_in_date: Date.today, check_out_date: Date.today + 1.day, rooms: [{ adult_count: 2 }], destination: "SYD")
21
- response = client.perform(search)
22
- puts response.results
21
+
22
+ # perform the search
23
+ search = client.perform_hotel_search({
24
+ check_in_date: Date.today,
25
+ check_out_date: Date.today + 1,
26
+ rooms: [{ adult_count: 2 }],
27
+ destination: "SYD"
28
+ })
29
+
30
+ # inspect the response
31
+ puts search.response.hotels
23
32
  # => [<HotelBeds::Model::Hotel>, <HotelBeds::Model::Hotel>]
33
+ puts search.response.total_pages
34
+ # => 10
35
+ puts search.response.current_page
36
+ # => 1
24
37
 
25
38
  ## Contributing
26
39
 
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "rspec", "~> 3.0.0"
28
28
  spec.add_development_dependency "guard-rspec", "~> 4.2.10"
29
29
  spec.add_development_dependency "dotenv", "~> 0.11.1"
30
+ spec.add_development_dependency "codeclimate-test-reporter"
30
31
  end
@@ -1,4 +1,2 @@
1
1
  require "hotel_beds/version"
2
2
  require "hotel_beds/client"
3
- require "hotel_beds/model/search"
4
- require "hotel_beds/operation/search"
@@ -1,5 +1,6 @@
1
1
  require "hotel_beds/configuration"
2
2
  require "hotel_beds/connection"
3
+ require "hotel_beds/hotel_search/operation"
3
4
 
4
5
  module HotelBeds
5
6
  class Client
@@ -12,8 +13,12 @@ module HotelBeds
12
13
  freeze
13
14
  end
14
15
 
15
- def perform(model)
16
- connection.perform(model.to_operation)
16
+ # builds and performs a hotel search, returning an operation object which
17
+ # contains both the request and response objects.
18
+ def perform_hotel_search(*args)
19
+ HotelSearch::Operation.new(*args).perform(
20
+ connection: connection
21
+ )
17
22
  end
18
23
  end
19
24
  end
@@ -1,4 +1,5 @@
1
1
  require "savon"
2
+ require "securerandom"
2
3
 
3
4
  module HotelBeds
4
5
  class Connection
@@ -11,21 +12,20 @@ module HotelBeds
11
12
  freeze
12
13
  end
13
14
 
14
- def perform(operation)
15
- message = operation.message
16
- message[operation.namespace] = message.fetch(operation.namespace).merge({
15
+ def call(method:, namespace:, data:)
16
+ message = { namespace => {
17
17
  :@xmlns => "http://www.hotelbeds.com/schemas/2005/06/messages",
18
18
  :"@xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
19
- :"@xsi:schemaLocation" => "http://www.hotelbeds.com/schemas/2005/06/messages #{operation.namespace}.xsd",
20
- :@echoToken => operation.echo_token,
21
- :@sessionId => operation.session_id,
19
+ :"@xsi:schemaLocation" => "http://www.hotelbeds.com/schemas/2005/06/messages #{namespace}.xsd",
20
+ :@echoToken => SecureRandom.hex[0..15],
21
+ :@sessionId => SecureRandom.hex[0..15],
22
22
  :Credentials => {
23
23
  User: configuration.username,
24
24
  Password: configuration.password
25
25
  }
26
- })
26
+ }.merge(data) }
27
27
  # send the call
28
- response = client.call(operation.method, {
28
+ response = client.call(method, {
29
29
  soap_action: "",
30
30
  attributes: {
31
31
  :"xmlns:hb" => "http://axis.frontend.hydra.hotelbeds.com",
@@ -33,8 +33,6 @@ module HotelBeds
33
33
  },
34
34
  message: message
35
35
  })
36
- # parse the response
37
- operation.parse_response(response)
38
36
  end
39
37
 
40
38
  private
@@ -1,33 +1,33 @@
1
- require "hotel_beds/operation/base"
2
- require "hotel_beds/response/search"
1
+ require "delegate"
3
2
 
4
3
  module HotelBeds
5
- module Operation
6
- class Search < Base
7
- def method
8
- :getHotelValuedAvail
9
- end
10
-
11
- def namespace
12
- :HotelValuedAvailRQ
13
- end
14
-
15
- def message
16
- { namespace => {
4
+ module HotelSearch
5
+ class Envelope < SimpleDelegator
6
+ def attributes
7
+ {
17
8
  PaginationData: pagination_data,
18
9
  Language: language,
19
10
  CheckInDate: check_in_date,
20
11
  CheckOutDate: check_out_date,
21
- Destination: destination,
22
12
  OccupancyList: occupancy_list
23
- } }
13
+ }.merge(Hash(destination)).merge(Hash(hotels)).merge(Hash(extra_params))
24
14
  end
25
15
 
26
- def parse_response(response)
27
- HotelBeds::Response::Search.new(response)
16
+ private
17
+ def extra_params
18
+ { ExtraParamList: {
19
+ ExtendedData: [{
20
+ :@type => "EXT_ADDITIONAL_PARAM",
21
+ :Name => "PARAM_KEY_PRICE_BREAKDOWN",
22
+ :Value => "Y"
23
+ }, {
24
+ :@type => "EXT_ORDER",
25
+ :Name => "ORDER_CONTRACT_PRICE",
26
+ :Value => "ASC"
27
+ }]
28
+ } }
28
29
  end
29
30
 
30
- private
31
31
  def pagination_data
32
32
  { :@pageNumber => Integer(page_number) }
33
33
  end
@@ -45,7 +45,21 @@ module HotelBeds
45
45
  end
46
46
 
47
47
  def destination
48
- { :@code => String(__getobj__.destination).upcase, :@type => "SIMPLE" }
48
+ if __getobj__.destination
49
+ { Destination: {
50
+ :@code => String(__getobj__.destination).upcase,
51
+ :@type => "SIMPLE"
52
+ } }
53
+ end
54
+ end
55
+
56
+ def hotels
57
+ if Array(__getobj__.hotels).any?
58
+ { HotelCodeList: {
59
+ :@withinResults => "Y",
60
+ :ProductCode => Array(__getobj__.hotels)
61
+ } }
62
+ end
49
63
  end
50
64
 
51
65
  def occupancy_list
@@ -56,7 +70,7 @@ module HotelBeds
56
70
  AdultCount: Integer(room.adult_count),
57
71
  ChildCount: Integer(room.child_count)
58
72
  },
59
- GuestList: room.child_ages.each { |age|
73
+ GuestList: Array(room.child_ages).each { |age|
60
74
  { Customer: { :@type => "CH", :Age => Integer(age) } }
61
75
  }
62
76
  } }
@@ -0,0 +1,36 @@
1
+ require_relative "request"
2
+ require_relative "response"
3
+ require_relative "envelope"
4
+
5
+ module HotelBeds
6
+ module HotelSearch
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(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: :getHotelValuedAvail,
30
+ namespace: :HotelValuedAvailRQ,
31
+ data: Envelope.new(request).attributes
32
+ })
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,19 +1,17 @@
1
- require "hotel_beds/model/base"
2
- require "virtus"
3
- require "active_model"
1
+ require "hotel_beds/model"
4
2
 
5
3
  module HotelBeds
6
- module Model
7
- class Search < Base
8
- class Room
4
+ module HotelSearch
5
+ class Request
6
+ class Room
7
+ include HotelBeds::Model
8
+
9
9
  # attributes
10
- include Virtus.model
11
10
  attribute :adult_count, Integer, default: 0
12
11
  attribute :child_count, Integer, default: 0
13
12
  attribute :child_ages, Array[Integer], default: Array.new
14
-
13
+
15
14
  # validation
16
- include ActiveModel::Validations
17
15
  validates :adult_count, :child_count, numericality: {
18
16
  greater_than_or_equal_to: 0,
19
17
  less_than_or_equal_to: 4,
@@ -25,6 +23,8 @@ module HotelBeds
25
23
  end
26
24
  end
27
25
  end
26
+
27
+ include HotelBeds::Model
28
28
 
29
29
  # attributes
30
30
  attribute :page_number, Integer, default: 1
@@ -32,6 +32,7 @@ module HotelBeds
32
32
  attribute :check_in_date, Date
33
33
  attribute :check_out_date, Date
34
34
  attribute :destination, String
35
+ attribute :hotels, Array[Integer]
35
36
  attribute :rooms, Array[Room]
36
37
 
37
38
  # validation
@@ -0,0 +1,90 @@
1
+ require "active_model/errors"
2
+ require "hotel_beds/model/hotel"
3
+
4
+ module HotelBeds
5
+ module HotelSearch
6
+ class Response
7
+ attr_accessor :headers, :body, :errors
8
+ private :headers=, :body=, :errors=
9
+
10
+ def initialize(response)
11
+ self.headers = response.header
12
+ self.body = Nokogiri::XML(response.body.fetch(:get_hotel_valued_avail))
13
+ self.errors = ActiveModel::Errors.new(self).tap do |errors|
14
+ if response.http_error?
15
+ errors.add(:base, "HTTP error")
16
+ elsif response.soap_fault?
17
+ errors.add(:base, "SOAP error")
18
+ elsif !response.success?
19
+ errors.add(:base, "Request failed")
20
+ end
21
+ end
22
+ freeze
23
+ end
24
+
25
+ def inspect
26
+ "<#{self.class.name} headers=#{headers.inspect} body=#{body.to_s}>"
27
+ end
28
+
29
+ def current_page
30
+ Integer(body.css("PaginationData").first.attr("currentPage"))
31
+ end
32
+
33
+ def total_pages
34
+ Integer(body.css("PaginationData").first.attr("totalPages"))
35
+ end
36
+
37
+ def hotels
38
+ body.css("ServiceHotel").lazy.map do |hotel|
39
+ HotelBeds::Model::Hotel.new({
40
+ id: hotel.css("HotelInfo Code").first.content,
41
+ name: hotel.css("HotelInfo Name").first.content,
42
+ images: hotel.css("HotelInfo ImageList Image Url").map(&:content),
43
+ latitude: hotel.css("HotelInfo Position").first.attr("latitude"),
44
+ longitude: hotel.css("HotelInfo Position").first.attr("longitude"),
45
+ results: parse_available_rooms(hotel.css("AvailableRoom"))
46
+ })
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def parse_available_rooms(rooms)
53
+ Array(rooms).map do |room|
54
+ {
55
+ adult_count: room.css("HotelOccupancy AdultCount").first.content,
56
+ child_count: room.css("HotelOccupancy ChildCount").first.content,
57
+ rooms: parse_hotel_rooms(room.css("HotelRoom"))
58
+ }
59
+ end
60
+ end
61
+
62
+ def parse_hotel_rooms(rooms)
63
+ Array(rooms).map do |room|
64
+ {
65
+ number_available: room.attr("availCount"),
66
+ id: room.attr("SHRUI"),
67
+ description: room.css("RoomType").first.content,
68
+ board: room.css("Board").first.content,
69
+ price: room.css("Price Amount").first.content,
70
+ currency: body.css("Currency").first.attribute("code").value,
71
+ rates: parse_price_list(room.css("PriceList Price"))
72
+ }
73
+ end
74
+ end
75
+
76
+ def parse_price_list(prices)
77
+ Array(prices).inject({}) do |result, price|
78
+ from = Date.parse(price.css("DateTimeFrom").first.attr("date"))
79
+ to = Date.parse(price.css("DateTimeTo").first.attr("date"))
80
+ dates = (from..to).to_a
81
+ amount = BigDecimal.new(price.css("Amount").first.content) / dates.size
82
+ dates.each do |date|
83
+ result.merge!(date => amount)
84
+ end
85
+ result
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,13 @@
1
+ require "virtus"
2
+ require "active_model"
3
+
4
+ module HotelBeds
5
+ module Model
6
+ def self.included(base)
7
+ base.class_eval do
8
+ include Virtus.model
9
+ include ActiveModel::Validations
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,12 +1,15 @@
1
- require "hotel_beds/model/hotel_room"
2
- require "virtus"
1
+ require "hotel_beds/model"
2
+ require_relative "hotel_room"
3
3
 
4
4
  module HotelBeds
5
5
  module Model
6
6
  class AvailableRoom
7
+ include HotelBeds::Model
8
+
7
9
  # attributes
8
- include Virtus.model
9
10
  attribute :rooms, Array[HotelRoom]
11
+ attribute :adult_count, Integer
12
+ attribute :child_count, Integer
10
13
  end
11
14
  end
12
15
  end
@@ -1,11 +1,12 @@
1
- require "hotel_beds/model/available_room"
2
- require "virtus"
1
+ require "hotel_beds/model"
2
+ require_relative "available_room"
3
3
 
4
4
  module HotelBeds
5
5
  module Model
6
6
  class Hotel
7
+ include HotelBeds::Model
8
+
7
9
  # attributes
8
- include Virtus.model
9
10
  attribute :id, Integer
10
11
  attribute :name, String
11
12
  attribute :images, Array[String]
@@ -1,15 +1,18 @@
1
- require "virtus"
1
+ require "hotel_beds/model"
2
2
 
3
3
  module HotelBeds
4
4
  module Model
5
5
  class HotelRoom
6
+ include HotelBeds::Model
7
+
6
8
  # attributes
7
- include Virtus.model
8
9
  attribute :id, Integer
9
10
  attribute :description, String
10
11
  attribute :board, String
11
12
  attribute :price, BigDecimal
12
13
  attribute :currency, String
14
+ attribute :number_available, Integer
15
+ attribute :rates, Hash[Date => BigDecimal]
13
16
  end
14
17
  end
15
18
  end
@@ -1,3 +1,3 @@
1
1
  module HotelBeds
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -1,50 +1,69 @@
1
1
  require "hotel_beds"
2
2
 
3
3
  RSpec.describe "performing a hotel search" do
4
- let(:check_in_date) { Date.today + 28 + rand(10) }
5
- let(:check_out_date) { check_in_date + rand(3) + 1 }
6
- let(:client) do
7
- HotelBeds::Client.new({
8
- endpoint: :test,
9
- username: ENV.fetch("HOTEL_BEDS_USERNAME"),
10
- password: ENV.fetch("HOTEL_BEDS_PASSWORD"),
11
- proxy: ENV.fetch("HOTEL_BEDS_PROXY", nil)
12
- })
13
- end
14
-
15
- let(:search) do
16
- HotelBeds::Model::Search.new({
17
- check_in_date: check_in_date,
18
- check_out_date: check_out_date,
19
- rooms: [{ adult_count: 2 }],
20
- destination: "SYD"
21
- })
22
- end
23
-
24
- let(:response) { client.perform(search) }
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) + 1
14
+ @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
+ end
25
21
 
26
- describe "#hotels" do
27
- subject { response.hotels }
22
+ let(:check_in_date) { @check_in_date }
23
+ let(:check_out_date) { @check_out_date }
24
+ let(:response) { @response }
28
25
 
29
- it "should return an array of the hotels available" do
30
- expect(subject.to_a).to be_kind_of(Array)
31
- expect(subject.first).to be_kind_of(HotelBeds::Model::Hotel)
26
+ describe "#errors" do
27
+ subject { response.errors }
28
+
29
+ it "should be empty" do
30
+ expect(subject).to be_empty
31
+ end
32
+ end
33
+
34
+ describe "#hotels" do
35
+ subject { response.hotels }
36
+
37
+ it "should return an array of the hotels available" do
38
+ expect(subject.to_a).to be_kind_of(Array)
39
+ expect(subject.first).to be_kind_of(HotelBeds::Model::Hotel)
40
+ end
41
+
42
+ it "should parse the rates correctly" do
43
+ room = subject.first.results.first.rooms.first
44
+ expect(room.price).to eq(room.rates.values.inject(:+))
45
+ end
46
+
47
+ it "should have the same number of rates as nights reuqested" do
48
+ room = subject.first.results.first.rooms.first
49
+ expect(room.rates.size).to eq(check_out_date - check_in_date)
50
+ end
32
51
  end
33
- end
34
52
 
35
- describe "#current_page" do
36
- subject { response.current_page }
53
+ describe "#current_page" do
54
+ subject { response.current_page }
37
55
 
38
- it "should return '1'" do
39
- expect(subject).to eq(1)
56
+ it "should return '1'" do
57
+ expect(subject).to eq(1)
58
+ end
40
59
  end
41
- end
42
60
 
43
- describe "#total_pages" do
44
- subject { response.total_pages }
61
+ describe "#total_pages" do
62
+ subject { response.total_pages }
45
63
 
46
- it "should be greater or equal to current page" do
47
- expect(subject).to be >= response.current_page
64
+ it "should be greater or equal to current page" do
65
+ expect(subject).to be >= response.current_page
66
+ end
48
67
  end
49
68
  end
50
69
  end
@@ -18,7 +18,7 @@ RSpec.describe HotelBeds::Connection do
18
18
  expect(subject).to be_frozen
19
19
  end
20
20
 
21
- it "should respond to #perform" do
22
- expect(subject).to respond_to(:perform)
21
+ it "should respond to #call" do
22
+ expect(subject).to respond_to(:call)
23
23
  end
24
24
  end
@@ -1,7 +1,7 @@
1
- require "hotel_beds/model/search"
1
+ require "hotel_beds/hotel_search/request"
2
2
  require "date"
3
3
 
4
- RSpec.describe HotelBeds::Model::Search do
4
+ RSpec.describe HotelBeds::HotelSearch::Request do
5
5
  let(:valid_attributes) do
6
6
  {
7
7
  page_number: 2,
@@ -1,5 +1,5 @@
1
1
  RSpec.describe HotelBeds::VERSION do
2
2
  example do
3
- expect(subject).to eq("0.0.1")
3
+ expect(subject).to match(/\A\d+\.\d+\.\d+(\.\d+)?\z/)
4
4
  end
5
5
  end
@@ -1,2 +1,7 @@
1
1
  require "dotenv"
2
2
  Dotenv.load
3
+
4
+ if ENV["CODECLIMATE_REPO_TOKEN"]
5
+ require "codeclimate-test-reporter"
6
+ CodeClimate::TestReporter.start
7
+ 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.0.2
4
+ version: 0.1.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-08 00:00:00.000000000 Z
11
+ date: 2014-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: savon
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: 0.11.1
139
+ - !ruby/object:Gem::Dependency
140
+ name: codeclimate-test-reporter
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
139
153
  description:
140
154
  email:
141
155
  - ryan@ryantownsend.co.uk
@@ -158,23 +172,20 @@ files:
158
172
  - lib/hotel_beds/client.rb
159
173
  - lib/hotel_beds/configuration.rb
160
174
  - lib/hotel_beds/connection.rb
175
+ - lib/hotel_beds/hotel_search/envelope.rb
176
+ - lib/hotel_beds/hotel_search/operation.rb
177
+ - lib/hotel_beds/hotel_search/request.rb
178
+ - lib/hotel_beds/hotel_search/response.rb
179
+ - lib/hotel_beds/model.rb
161
180
  - lib/hotel_beds/model/available_room.rb
162
- - lib/hotel_beds/model/base.rb
163
181
  - lib/hotel_beds/model/hotel.rb
164
182
  - lib/hotel_beds/model/hotel_room.rb
165
- - lib/hotel_beds/model/search.rb
166
- - lib/hotel_beds/model_invalid.rb
167
- - lib/hotel_beds/operation/base.rb
168
- - lib/hotel_beds/operation/search.rb
169
- - lib/hotel_beds/response/base.rb
170
- - lib/hotel_beds/response/search.rb
171
183
  - lib/hotel_beds/version.rb
172
184
  - spec/features/hotel_search_spec.rb
173
185
  - spec/lib/hotel_beds/client_spec.rb
174
186
  - spec/lib/hotel_beds/configuration_spec.rb
175
187
  - spec/lib/hotel_beds/connection_spec.rb
176
- - spec/lib/hotel_beds/model/search_spec.rb
177
- - spec/lib/hotel_beds/model_invalid_spec.rb
188
+ - spec/lib/hotel_beds/hotel_search/request_spec.rb
178
189
  - spec/lib/hotel_beds/version_spec.rb
179
190
  - spec/spec_helper.rb
180
191
  homepage: https://www.hotelsindex.com/open-source
@@ -206,7 +217,6 @@ test_files:
206
217
  - spec/lib/hotel_beds/client_spec.rb
207
218
  - spec/lib/hotel_beds/configuration_spec.rb
208
219
  - spec/lib/hotel_beds/connection_spec.rb
209
- - spec/lib/hotel_beds/model/search_spec.rb
210
- - spec/lib/hotel_beds/model_invalid_spec.rb
220
+ - spec/lib/hotel_beds/hotel_search/request_spec.rb
211
221
  - spec/lib/hotel_beds/version_spec.rb
212
222
  - spec/spec_helper.rb
@@ -1,35 +0,0 @@
1
- require "hotel_beds/model_invalid"
2
- require "securerandom"
3
- require "virtus"
4
- require "active_model"
5
-
6
- module HotelBeds
7
- module Model
8
- class Base
9
- # generator for random tokens
10
- RANDOM_TOKEN = -> (model, attr) { SecureRandom.hex[0..15] }
11
-
12
- # returns the operation for this model
13
- def self.operation_class
14
- HotelBeds::Operation.const_get(name.split("::").last)
15
- end
16
-
17
- # attributes
18
- include Virtus.model
19
- attribute :session_id, String, default: RANDOM_TOKEN, writer: :private
20
- attribute :echo_token, String, default: RANDOM_TOKEN, writer: :private
21
-
22
- # validation
23
- include ActiveModel::Validations
24
- validates :session_id, :echo_token, presence: true
25
-
26
- def to_operation
27
- if valid?
28
- self.class.operation_class.new(self)
29
- else
30
- raise HotelsBeds::ModelInvalid.new(self)
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,12 +0,0 @@
1
- module HotelBeds
2
- class ModelInvalid < StandardError
3
- attr_accessor :model
4
- private :model=
5
-
6
- def initialize(model)
7
- super("This #{model.class.name} model is invalid")
8
- self.model = model
9
- freeze
10
- end
11
- end
12
- end
@@ -1,29 +0,0 @@
1
- require "delegate"
2
- require "securerandom"
3
-
4
- module HotelBeds
5
- module Operation
6
- class Base < SimpleDelegator
7
- def method
8
- raise NotImplementedError, "#method should return a symbol for the \
9
- remote method to be called"
10
- end
11
-
12
- def namespace
13
- raise NotImplementedError, "#namespace should return a symbol for the \
14
- root element within the <message> element"
15
- end
16
-
17
- def message
18
- raise NotImplementedError, "#message should return a hash containing \
19
- the contents of the <message> element, the #namespace should be \
20
- included as a key"
21
- end
22
-
23
- def parse_response(response)
24
- raise NotImplementedError, "#parse_response should accept a SOAP \
25
- response and return a HotelBeds::Response::Base subclass object"
26
- end
27
- end
28
- end
29
- end
@@ -1,27 +0,0 @@
1
- require "forwardable"
2
- require "nokogiri"
3
-
4
- module HotelBeds
5
- module Response
6
- class Base
7
- # attributes
8
- attr_accessor :response, :body, :headers
9
- private :response, :response=, :body=, :headers=
10
-
11
- # delegation
12
- extend Forwardable
13
- def_delegators :response, :success?, :soap_fault?, :http_error?
14
-
15
- def initialize(response)
16
- self.response = response
17
- self.headers = response.header
18
- self.body = Nokogiri::XML(response.body.fetch(:get_hotel_valued_avail))
19
- freeze
20
- end
21
-
22
- def inspect
23
- "<#{self.class.name} headers=#{headers.inspect} body=#{body.inspect}>"
24
- end
25
- end
26
- end
27
- end
@@ -1,43 +0,0 @@
1
- require "hotel_beds/response/base"
2
- require "hotel_beds/model/hotel"
3
- require "hotel_beds/model/available_room"
4
- require "hotel_beds/model/hotel_room"
5
-
6
- module HotelBeds
7
- module Response
8
- class Search < Base
9
- def current_page
10
- Integer(body.css("PaginationData").attr("currentPage").value)
11
- end
12
-
13
- def total_pages
14
- Integer(body.css("PaginationData").attr("totalPages").value)
15
- end
16
-
17
- def hotels
18
- body.css("ServiceHotel").lazy.map do |hotel|
19
- HotelBeds::Model::Hotel.new({
20
- id: hotel.css("HotelInfo Code").first.content,
21
- name: hotel.css("HotelInfo Name").first.content,
22
- images: hotel.css("HotelInfo ImageList Image Url").map(&:content),
23
- latitude: hotel.css("HotelInfo Position").attr("latitude").value,
24
- longitude: hotel.css("HotelInfo Position").attr("longitude").value,
25
- results: hotel.css("AvailableRoom").map { |result|
26
- HotelBeds::Model::AvailableRoom.new({
27
- rooms: result.css("HotelRoom").map { |room|
28
- HotelBeds::Model::HotelRoom.new({
29
- id: room.attribute("SHRUI").value,
30
- description: room.css("RoomType").first.content,
31
- board: room.css("Board").first.content,
32
- price: room.css("Price Amount").first.content,
33
- currency: body.css("Currency").first.attribute("code").value
34
- })
35
- }
36
- })
37
- }
38
- })
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,22 +0,0 @@
1
- require "hotel_beds/model_invalid"
2
-
3
- RSpec.describe HotelBeds::ModelInvalid do
4
- let(:model) { Object.new }
5
- let(:instance) { described_class.new(model) }
6
-
7
- describe "an instance" do
8
- subject { instance }
9
-
10
- it "should be immutable" do
11
- expect(subject).to be_frozen
12
- end
13
- end
14
-
15
- describe "#model" do
16
- subject { instance.model }
17
-
18
- it "should return the given model" do
19
- expect(subject).to eq(model)
20
- end
21
- end
22
- end