hotel_beds 0.0.1 → 0.0.2
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/.env.sample +3 -0
- data/.gitignore +2 -1
- data/.rspec +4 -0
- data/Guardfile +5 -0
- data/README.md +10 -4
- data/hotel_beds.gemspec +9 -1
- data/lib/hotel_beds.rb +3 -4
- data/lib/hotel_beds/client.rb +19 -0
- data/lib/hotel_beds/configuration.rb +33 -0
- data/lib/hotel_beds/connection.rb +75 -0
- data/lib/hotel_beds/model/available_room.rb +12 -0
- data/lib/hotel_beds/model/base.rb +35 -0
- data/lib/hotel_beds/model/hotel.rb +18 -0
- data/lib/hotel_beds/model/hotel_room.rb +15 -0
- data/lib/hotel_beds/model/search.rb +54 -0
- data/lib/hotel_beds/model_invalid.rb +12 -0
- data/lib/hotel_beds/operation/base.rb +29 -0
- data/lib/hotel_beds/operation/search.rb +67 -0
- data/lib/hotel_beds/response/base.rb +27 -0
- data/lib/hotel_beds/response/search.rb +43 -0
- data/lib/hotel_beds/version.rb +1 -1
- data/spec/features/hotel_search_spec.rb +50 -0
- data/spec/lib/hotel_beds/client_spec.rb +27 -0
- data/spec/lib/hotel_beds/configuration_spec.rb +89 -0
- data/spec/lib/hotel_beds/connection_spec.rb +24 -0
- data/spec/lib/hotel_beds/model/search_spec.rb +35 -0
- data/spec/lib/hotel_beds/model_invalid_spec.rb +22 -0
- data/spec/lib/hotel_beds/version_spec.rb +5 -0
- data/spec/spec_helper.rb +2 -0
- metadata +137 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 305bb6ccb1f0f40a6a27186565f3ce5ddb853c76
|
4
|
+
data.tar.gz: ff0b31e19ca3939f4da18e9dfc5a5b6608113486
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cdee1089ee23fb3df5543a81626e4eeb73391b9524f35d0a98562e3df9ca041edb6893afbb4db422d7eb942fc40097fe0bbeaa9a7c1bd763fc94e1b30691828f
|
7
|
+
data.tar.gz: 0966d2c596f0f6ac2d65a0621720ff6badaca68c84880c290dc3a4ada95f8d2c9bdc55be6eeee0ab6b0bec1318ba65a27c378b90d62c1154075bf69216bcb0fb
|
data/.env.sample
ADDED
data/.gitignore
CHANGED
data/.rspec
ADDED
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -1,20 +1,26 @@
|
|
1
|
+
[](https://codeclimate.com/github/platformq/hotel_beds)
|
2
|
+
|
1
3
|
The `hotel_beds` gem interfaces with the [HotelBeds.com](http://www.hotelbeds.com/) SOAP API to search for and book hotel rooms.
|
2
4
|
|
3
5
|
## Installation
|
4
6
|
|
5
7
|
I'm sure you know how to install Ruby gems by now...
|
6
8
|
|
7
|
-
Gemfile:
|
9
|
+
In your Gemfile, before a `bundle install`, add:
|
8
10
|
|
9
11
|
gem "hotel_beds", "~> 0.0.1"
|
10
12
|
|
11
|
-
Manually:
|
13
|
+
Manually, via command line:
|
12
14
|
|
13
|
-
|
15
|
+
gem install hotel_beds
|
14
16
|
|
15
17
|
## Usage
|
16
18
|
|
17
|
-
|
19
|
+
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
|
23
|
+
# => [<HotelBeds::Model::Hotel>, <HotelBeds::Model::Hotel>]
|
18
24
|
|
19
25
|
## Contributing
|
20
26
|
|
data/hotel_beds.gemspec
CHANGED
@@ -17,6 +17,14 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
+
spec.add_dependency "savon", "~> 2.5.1"
|
21
|
+
spec.add_dependency "virtus", "~> 1.0.2"
|
22
|
+
spec.add_dependency "activemodel", "~> 4.1.1"
|
23
|
+
spec.add_dependency "nokogiri", "~> 1.6.2.1"
|
24
|
+
|
20
25
|
spec.add_development_dependency "bundler", "~> 1.6"
|
21
|
-
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.3.2"
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.0.0"
|
28
|
+
spec.add_development_dependency "guard-rspec", "~> 4.2.10"
|
29
|
+
spec.add_development_dependency "dotenv", "~> 0.11.1"
|
22
30
|
end
|
data/lib/hotel_beds.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "hotel_beds/configuration"
|
2
|
+
require "hotel_beds/connection"
|
3
|
+
|
4
|
+
module HotelBeds
|
5
|
+
class Client
|
6
|
+
attr_accessor :configuration, :connection
|
7
|
+
private :configuration=, :connection, :connection=
|
8
|
+
|
9
|
+
def initialize(**config)
|
10
|
+
self.configuration = Configuration.new(**config)
|
11
|
+
self.connection = Connection.new(configuration)
|
12
|
+
freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform(model)
|
16
|
+
connection.perform(model.to_operation)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module HotelBeds
|
2
|
+
class Configuration
|
3
|
+
def self.endpoints
|
4
|
+
path = "appservices/ws/FrontendService"
|
5
|
+
{
|
6
|
+
test: "http://testapi.interface-xml.com/#{path}".freeze,
|
7
|
+
live: "http://api.interface-xml.com/#{path}".freeze,
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :endpoint, :username, :password, :proxy, :request_timeout,
|
12
|
+
:response_timeout, :enable_logging
|
13
|
+
|
14
|
+
def initialize(endpoint: :test, username:, password:, proxy: nil, request_timeout: 5, response_timeout: 30, enable_logging: false)
|
15
|
+
self.endpoint = self.class.endpoints.fetch(endpoint, endpoint)
|
16
|
+
self.username = username
|
17
|
+
self.password = password
|
18
|
+
self.proxy = proxy
|
19
|
+
self.request_timeout = Integer(request_timeout)
|
20
|
+
self.response_timeout = Integer(response_timeout)
|
21
|
+
self.enable_logging = enable_logging
|
22
|
+
freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
def enable_logging?
|
26
|
+
!!enable_logging
|
27
|
+
end
|
28
|
+
|
29
|
+
def proxy?
|
30
|
+
!!(proxy && !proxy.empty?)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "savon"
|
2
|
+
|
3
|
+
module HotelBeds
|
4
|
+
class Connection
|
5
|
+
attr_accessor :client, :configuration
|
6
|
+
private :client, :client=, :configuration=
|
7
|
+
|
8
|
+
def initialize(configuration)
|
9
|
+
self.configuration = configuration
|
10
|
+
self.client = initialize_client
|
11
|
+
freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform(operation)
|
15
|
+
message = operation.message
|
16
|
+
message[operation.namespace] = message.fetch(operation.namespace).merge({
|
17
|
+
:@xmlns => "http://www.hotelbeds.com/schemas/2005/06/messages",
|
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,
|
22
|
+
:Credentials => {
|
23
|
+
User: configuration.username,
|
24
|
+
Password: configuration.password
|
25
|
+
}
|
26
|
+
})
|
27
|
+
# send the call
|
28
|
+
response = client.call(operation.method, {
|
29
|
+
soap_action: "",
|
30
|
+
attributes: {
|
31
|
+
:"xmlns:hb" => "http://axis.frontend.hydra.hotelbeds.com",
|
32
|
+
:"xsi:type" => "xsd:string",
|
33
|
+
},
|
34
|
+
message: message
|
35
|
+
})
|
36
|
+
# parse the response
|
37
|
+
operation.parse_response(response)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def initialize_client
|
42
|
+
Savon::Client.new do |config|
|
43
|
+
# http requests
|
44
|
+
config.endpoint(configuration.endpoint)
|
45
|
+
config.proxy(configuration.proxy) if configuration.proxy?
|
46
|
+
config.headers "Accept-Encoding" => "gzip, deflate"
|
47
|
+
|
48
|
+
# soap specifics
|
49
|
+
config.env_namespace :soapenv
|
50
|
+
config.namespace "http://schemas.xmlsoap.org/soap/envelope/"
|
51
|
+
config.namespaces({
|
52
|
+
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
|
53
|
+
"soapenv:encodingStyle" => "http://schemas.xmlsoap.org/soap/encoding/"
|
54
|
+
})
|
55
|
+
config.namespace_identifier :hb
|
56
|
+
config.convert_request_keys_to :none
|
57
|
+
|
58
|
+
# request timeout
|
59
|
+
config.open_timeout(configuration.request_timeout)
|
60
|
+
config.read_timeout(configuration.response_timeout)
|
61
|
+
|
62
|
+
# request building
|
63
|
+
config.encoding "UTF-8"
|
64
|
+
|
65
|
+
# logging
|
66
|
+
if configuration.enable_logging?
|
67
|
+
config.log true
|
68
|
+
config.log_level :debug
|
69
|
+
config.pretty_print_xml true
|
70
|
+
config.filters [:password]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,35 @@
|
|
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
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "hotel_beds/model/available_room"
|
2
|
+
require "virtus"
|
3
|
+
|
4
|
+
module HotelBeds
|
5
|
+
module Model
|
6
|
+
class Hotel
|
7
|
+
# attributes
|
8
|
+
include Virtus.model
|
9
|
+
attribute :id, Integer
|
10
|
+
attribute :name, String
|
11
|
+
attribute :images, Array[String]
|
12
|
+
attribute :stars, Integer
|
13
|
+
attribute :longitude, BigDecimal
|
14
|
+
attribute :latitude, BigDecimal
|
15
|
+
attribute :results, Array[AvailableRoom]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "virtus"
|
2
|
+
|
3
|
+
module HotelBeds
|
4
|
+
module Model
|
5
|
+
class HotelRoom
|
6
|
+
# attributes
|
7
|
+
include Virtus.model
|
8
|
+
attribute :id, Integer
|
9
|
+
attribute :description, String
|
10
|
+
attribute :board, String
|
11
|
+
attribute :price, BigDecimal
|
12
|
+
attribute :currency, String
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "hotel_beds/model/base"
|
2
|
+
require "virtus"
|
3
|
+
require "active_model"
|
4
|
+
|
5
|
+
module HotelBeds
|
6
|
+
module Model
|
7
|
+
class Search < Base
|
8
|
+
class Room
|
9
|
+
# attributes
|
10
|
+
include Virtus.model
|
11
|
+
attribute :adult_count, Integer, default: 0
|
12
|
+
attribute :child_count, Integer, default: 0
|
13
|
+
attribute :child_ages, Array[Integer], default: Array.new
|
14
|
+
|
15
|
+
# validation
|
16
|
+
include ActiveModel::Validations
|
17
|
+
validates :adult_count, :child_count, numericality: {
|
18
|
+
greater_than_or_equal_to: 0,
|
19
|
+
less_than_or_equal_to: 4,
|
20
|
+
only_integer: true,
|
21
|
+
}
|
22
|
+
validate do |room|
|
23
|
+
unless child_ages.compact.size == child_count
|
24
|
+
room.errors.add(:child_ages, "must match quantity of children")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# attributes
|
30
|
+
attribute :page_number, Integer, default: 1
|
31
|
+
attribute :language, String, default: "ENG"
|
32
|
+
attribute :check_in_date, Date
|
33
|
+
attribute :check_out_date, Date
|
34
|
+
attribute :destination, String
|
35
|
+
attribute :rooms, Array[Room]
|
36
|
+
|
37
|
+
# validation
|
38
|
+
validates :language, :destination, length: { is: 3 }
|
39
|
+
validates :check_in_date, :check_out_date, presence: true
|
40
|
+
validates :rooms, length: { minimum: 1, maximum: 5 }
|
41
|
+
validates :page_number, numericality: {
|
42
|
+
greater_than: 0, only_integer: true
|
43
|
+
}
|
44
|
+
validate do |search|
|
45
|
+
unless (1..5).cover?(search.rooms.size)
|
46
|
+
search.errors.add(:rooms, "quantity must be between 1 and 5")
|
47
|
+
end
|
48
|
+
unless search.rooms.all?(&:valid?)
|
49
|
+
search.errors.add(:rooms, "are invalid")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,29 @@
|
|
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
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "hotel_beds/operation/base"
|
2
|
+
require "hotel_beds/response/search"
|
3
|
+
|
4
|
+
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 => {
|
17
|
+
PaginationData: pagination_data,
|
18
|
+
Language: language,
|
19
|
+
CheckInDate: check_in_date,
|
20
|
+
CheckOutDate: check_out_date,
|
21
|
+
Destination: destination,
|
22
|
+
OccupancyList: occupancy_list
|
23
|
+
} }
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_response(response)
|
27
|
+
HotelBeds::Response::Search.new(response)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def pagination_data
|
32
|
+
{ :@pageNumber => Integer(page_number) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def language
|
36
|
+
String(__getobj__.language).upcase
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_in_date
|
40
|
+
{ :@date => __getobj__.check_in_date.strftime("%Y%m%d") }
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_out_date
|
44
|
+
{ :@date => __getobj__.check_out_date.strftime("%Y%m%d") }
|
45
|
+
end
|
46
|
+
|
47
|
+
def destination
|
48
|
+
{ :@code => String(__getobj__.destination).upcase, :@type => "SIMPLE" }
|
49
|
+
end
|
50
|
+
|
51
|
+
def occupancy_list
|
52
|
+
Array(rooms).map do |room|
|
53
|
+
{ HotelOccupancy: {
|
54
|
+
RoomCount: Integer(room.adult_count) + Integer(room.child_count),
|
55
|
+
Occupancy: {
|
56
|
+
AdultCount: Integer(room.adult_count),
|
57
|
+
ChildCount: Integer(room.child_count)
|
58
|
+
},
|
59
|
+
GuestList: room.child_ages.each { |age|
|
60
|
+
{ Customer: { :@type => "CH", :Age => Integer(age) } }
|
61
|
+
}
|
62
|
+
} }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,27 @@
|
|
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
|
@@ -0,0 +1,43 @@
|
|
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
|
data/lib/hotel_beds/version.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "hotel_beds"
|
2
|
+
|
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) }
|
25
|
+
|
26
|
+
describe "#hotels" do
|
27
|
+
subject { response.hotels }
|
28
|
+
|
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)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#current_page" do
|
36
|
+
subject { response.current_page }
|
37
|
+
|
38
|
+
it "should return '1'" do
|
39
|
+
expect(subject).to eq(1)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#total_pages" do
|
44
|
+
subject { response.total_pages }
|
45
|
+
|
46
|
+
it "should be greater or equal to current page" do
|
47
|
+
expect(subject).to be >= response.current_page
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "hotel_beds/client"
|
2
|
+
|
3
|
+
RSpec.describe HotelBeds::Client do
|
4
|
+
let(:username) { "username" }
|
5
|
+
let(:password) { "password" }
|
6
|
+
|
7
|
+
subject do
|
8
|
+
described_class.new(
|
9
|
+
username: "username",
|
10
|
+
password: "password"
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "an instance" do
|
15
|
+
it "should be immutable" do
|
16
|
+
expect(subject).to be_frozen
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#configuration" do
|
21
|
+
%w(username password endpoint proxy).each do |key|
|
22
|
+
it "should respond to ##{key}" do
|
23
|
+
expect(subject.configuration).to respond_to(key)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "hotel_beds/configuration"
|
2
|
+
|
3
|
+
RSpec.describe HotelBeds::Configuration do
|
4
|
+
let(:endpoint) { "http://example.com/api" }
|
5
|
+
let(:username) { "test" }
|
6
|
+
let(:password) { "passW0rd" }
|
7
|
+
let(:proxy) { "http://example.com/test" }
|
8
|
+
|
9
|
+
subject do
|
10
|
+
described_class.new(
|
11
|
+
endpoint: endpoint,
|
12
|
+
username: username,
|
13
|
+
password: password,
|
14
|
+
proxy: proxy
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "an instance" do
|
19
|
+
it "should be immutable" do
|
20
|
+
expect(subject).to be_frozen
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#endpoint" do
|
25
|
+
context "with a URL string" do
|
26
|
+
it "should return the given endpoint" do
|
27
|
+
expect(subject.endpoint).to eq(endpoint)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
described_class.endpoints.each do |key,value|
|
32
|
+
context "with a :#{key} key" do
|
33
|
+
let(:endpoint) { key }
|
34
|
+
|
35
|
+
it "should return the defined endpoint" do
|
36
|
+
expect(subject.endpoint).to eq(value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#username" do
|
43
|
+
it "should return the given username" do
|
44
|
+
expect(subject.username).to eq(username)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#password" do
|
49
|
+
it "should return the given password" do
|
50
|
+
expect(subject.password).to eq(password)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#proxy" do
|
55
|
+
it "should return the given proxy" do
|
56
|
+
expect(subject.proxy).to eq(proxy)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#proxy?" do
|
61
|
+
context "when proxy is specified" do
|
62
|
+
let(:proxy) { "http://example.com/proxy" }
|
63
|
+
|
64
|
+
it "should return true when a proxy is specified" do
|
65
|
+
expect(subject.proxy?).to eq(true)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when no proxy is specified" do
|
70
|
+
let(:proxy) { nil }
|
71
|
+
|
72
|
+
it "should return false" do
|
73
|
+
expect(subject.proxy?).to eq(false)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#request_timeout" do
|
79
|
+
it "should default to 5 seconds" do
|
80
|
+
expect(subject.request_timeout).to eq(5)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#response_timeout" do
|
85
|
+
it "should default to 30 seconds" do
|
86
|
+
expect(subject.response_timeout).to eq(30)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "hotel_beds/connection"
|
2
|
+
|
3
|
+
RSpec.describe HotelBeds::Connection do
|
4
|
+
let(:configuration) do
|
5
|
+
instance_double("HotelBeds::Configuration", {
|
6
|
+
endpoint: "http://www.example.com:2345/api",
|
7
|
+
response_timeout: 10,
|
8
|
+
request_timeout: 2,
|
9
|
+
proxy: "http://example.com:9090/proxy",
|
10
|
+
proxy?: true,
|
11
|
+
enable_logging?: false
|
12
|
+
})
|
13
|
+
end
|
14
|
+
|
15
|
+
subject { described_class.new(configuration) }
|
16
|
+
|
17
|
+
it "should be immutable" do
|
18
|
+
expect(subject).to be_frozen
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should respond to #perform" do
|
22
|
+
expect(subject).to respond_to(:perform)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "hotel_beds/model/search"
|
2
|
+
require "date"
|
3
|
+
|
4
|
+
RSpec.describe HotelBeds::Model::Search do
|
5
|
+
let(:valid_attributes) do
|
6
|
+
{
|
7
|
+
page_number: 2,
|
8
|
+
language: "FRA",
|
9
|
+
check_in_date: Date.today,
|
10
|
+
check_out_date: Date.today + 2,
|
11
|
+
destination: "SYD",
|
12
|
+
rooms: [{
|
13
|
+
adult_count: 2,
|
14
|
+
child_count: 1,
|
15
|
+
child_ages: [3]
|
16
|
+
}]
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "a valid instance" do
|
21
|
+
subject { described_class.new(valid_attributes) }
|
22
|
+
|
23
|
+
it "should be valid" do
|
24
|
+
expect(subject).to be_valid
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "an instance missing rooms" do
|
29
|
+
subject { described_class.new(valid_attributes.merge(rooms: [])) }
|
30
|
+
|
31
|
+
it "should not be valid" do
|
32
|
+
expect(subject).to_not be_valid
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
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
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,15 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hotel_beds
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
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-
|
11
|
+
date: 2014-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: savon
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.5.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.5.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: virtus
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.2
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activemodel
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 4.1.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 4.1.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: nokogiri
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.6.2.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.6.2.1
|
13
69
|
- !ruby/object:Gem::Dependency
|
14
70
|
name: bundler
|
15
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -28,16 +84,58 @@ dependencies:
|
|
28
84
|
name: rake
|
29
85
|
requirement: !ruby/object:Gem::Requirement
|
30
86
|
requirements:
|
31
|
-
- - "
|
87
|
+
- - "~>"
|
32
88
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
89
|
+
version: 10.3.2
|
34
90
|
type: :development
|
35
91
|
prerelease: false
|
36
92
|
version_requirements: !ruby/object:Gem::Requirement
|
37
93
|
requirements:
|
38
|
-
- - "
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 10.3.2
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 3.0.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 3.0.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: guard-rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 4.2.10
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 4.2.10
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: dotenv
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.11.1
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
39
137
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
138
|
+
version: 0.11.1
|
41
139
|
description:
|
42
140
|
email:
|
43
141
|
- ryan@ryantownsend.co.uk
|
@@ -45,16 +143,40 @@ executables: []
|
|
45
143
|
extensions: []
|
46
144
|
extra_rdoc_files: []
|
47
145
|
files:
|
146
|
+
- ".env.sample"
|
48
147
|
- ".gitignore"
|
148
|
+
- ".rspec"
|
49
149
|
- ".ruby-gemset"
|
50
150
|
- ".ruby-version"
|
51
151
|
- Gemfile
|
152
|
+
- Guardfile
|
52
153
|
- LICENSE.txt
|
53
154
|
- README.md
|
54
155
|
- Rakefile
|
55
156
|
- hotel_beds.gemspec
|
56
157
|
- lib/hotel_beds.rb
|
158
|
+
- lib/hotel_beds/client.rb
|
159
|
+
- lib/hotel_beds/configuration.rb
|
160
|
+
- lib/hotel_beds/connection.rb
|
161
|
+
- lib/hotel_beds/model/available_room.rb
|
162
|
+
- lib/hotel_beds/model/base.rb
|
163
|
+
- lib/hotel_beds/model/hotel.rb
|
164
|
+
- 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
|
57
171
|
- lib/hotel_beds/version.rb
|
172
|
+
- spec/features/hotel_search_spec.rb
|
173
|
+
- spec/lib/hotel_beds/client_spec.rb
|
174
|
+
- spec/lib/hotel_beds/configuration_spec.rb
|
175
|
+
- 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
|
178
|
+
- spec/lib/hotel_beds/version_spec.rb
|
179
|
+
- spec/spec_helper.rb
|
58
180
|
homepage: https://www.hotelsindex.com/open-source
|
59
181
|
licenses:
|
60
182
|
- MIT
|
@@ -79,4 +201,12 @@ rubygems_version: 2.2.2
|
|
79
201
|
signing_key:
|
80
202
|
specification_version: 4
|
81
203
|
summary: Interfaces with the HotelBeds SOAP API to book hotel rooms
|
82
|
-
test_files:
|
204
|
+
test_files:
|
205
|
+
- spec/features/hotel_search_spec.rb
|
206
|
+
- spec/lib/hotel_beds/client_spec.rb
|
207
|
+
- spec/lib/hotel_beds/configuration_spec.rb
|
208
|
+
- 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
|
211
|
+
- spec/lib/hotel_beds/version_spec.rb
|
212
|
+
- spec/spec_helper.rb
|