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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd639a1b56be28955272c107b23928fb1470eaae
4
- data.tar.gz: 60b24a0c3dbe096a6e885929bc23bece6e8a20c9
3
+ metadata.gz: 305bb6ccb1f0f40a6a27186565f3ce5ddb853c76
4
+ data.tar.gz: ff0b31e19ca3939f4da18e9dfc5a5b6608113486
5
5
  SHA512:
6
- metadata.gz: d3900025320c746d53587e4c07492c4308b19231ef959639de7ce449ecc6dfc0bf83e2788b0019dba1fae1bee6fdadad9a47ff6af25634cd7aa878497adc7629
7
- data.tar.gz: ca32909e9ab69f31c10bd330516db2ffb0e6d01d29d5e9a6e71fd9250eff6773b23fc1577f05a7bfa717c567d63f7205a253faaf66a6f88a42792583d9df15b3
6
+ metadata.gz: cdee1089ee23fb3df5543a81626e4eeb73391b9524f35d0a98562e3df9ca041edb6893afbb4db422d7eb942fc40097fe0bbeaa9a7c1bd763fc94e1b30691828f
7
+ data.tar.gz: 0966d2c596f0f6ac2d65a0621720ff6badaca68c84880c290dc3a4ada95f8d2c9bdc55be6eeee0ab6b0bec1318ba65a27c378b90d62c1154075bf69216bcb0fb
data/.env.sample ADDED
@@ -0,0 +1,3 @@
1
+ HOTEL_BEDS_USERNAME=username
2
+ HOTEL_BEDS_PASSWORD=password
3
+ HOTEL_BEDS_PROXY=https://optional.example.com/
data/.gitignore CHANGED
@@ -20,4 +20,5 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
- **.DS_Store
23
+ **.DS_Store
24
+ .env
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --order rand
3
+ --fail-fast
4
+ --require spec_helper
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :rspec, all_after_pass: true, all_on_start: true do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch("spec/spec_helper.rb") { "spec" }
5
+ end
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
- $ gem install hotel_beds
15
+ gem install hotel_beds
14
16
 
15
17
  ## Usage
16
18
 
17
- TODO: Write usage instructions here
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
@@ -1,5 +1,4 @@
1
1
  require "hotel_beds/version"
2
-
3
- module HotelBeds
4
- # Your code goes here...
5
- end
2
+ require "hotel_beds/client"
3
+ require "hotel_beds/model/search"
4
+ require "hotel_beds/operation/search"
@@ -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,12 @@
1
+ require "hotel_beds/model/hotel_room"
2
+ require "virtus"
3
+
4
+ module HotelBeds
5
+ module Model
6
+ class AvailableRoom
7
+ # attributes
8
+ include Virtus.model
9
+ attribute :rooms, Array[HotelRoom]
10
+ end
11
+ end
12
+ 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,12 @@
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module HotelBeds
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -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
@@ -0,0 +1,5 @@
1
+ RSpec.describe HotelBeds::VERSION do
2
+ example do
3
+ expect(subject).to eq("0.0.1")
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require "dotenv"
2
+ Dotenv.load
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.1
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-06-09 00:00:00.000000000 Z
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: '0'
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: '0'
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