hotel_beds 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Code Climate](https://codeclimate.com/github/platformq/hotel_beds.png)](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
|