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 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