hotel_beds 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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