tapi 0.2.0

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.
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+ module TAPI
3
+ module V3
4
+ module Configurable
5
+
6
+ def self.included(klass)
7
+ klass.extend(ClassMethods)
8
+ end
9
+
10
+ def config
11
+ Thread.current[:tapi_config] ||= {}
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ def config
17
+ Thread.current[:tapi_config] ||= {}
18
+ end
19
+
20
+ def config=(config)
21
+ Thread.current[:tapi_config] = config
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ module TAPI
2
+
3
+ class Error < RuntimeError
4
+ attr_accessor :response_code, :request_url, :response_body
5
+
6
+ def to_s
7
+ "Status code: #{response_code}\nRequest url: #{request_url}\n\n\n#{response_body}"
8
+ end
9
+ end
10
+
11
+ class BadRequestError < Error
12
+ end
13
+
14
+ class NotFoundError < Error
15
+ end
16
+
17
+ class ExpiredError < Error
18
+ end
19
+
20
+ class MovedError < Error
21
+ end
22
+
23
+ class InternalServerError < Error
24
+ end
25
+
26
+ end
@@ -0,0 +1,124 @@
1
+ # -*- coding: utf-8 -*-
2
+ module TAPI
3
+ module V3
4
+ module Flights
5
+ class Search < TAPI::V3::GenericSearch
6
+
7
+ AIRPORTCODEEXPR = /^[\w]{3}$/
8
+
9
+ attr_accessor :origin, :destination, :comfort
10
+ attr_reader :leaves_on, :returns_on, :one_way, :adults, :children, :infants
11
+
12
+ def leaves_on=(date)
13
+ @leaves_on = coerce_date(date) rescue nil
14
+ end
15
+
16
+ def returns_on=(date)
17
+ @returns_on = coerce_date(date) rescue nil
18
+ end
19
+
20
+ def one_way=(value)
21
+ @one_way = value.to_s == 'true'
22
+ end
23
+
24
+ def adults=(value)
25
+ @adults = value.to_i
26
+ end
27
+
28
+ def children=(value)
29
+ @children = value.to_i
30
+ end
31
+
32
+ def infants=(value)
33
+ @infants = value.to_i
34
+ end
35
+
36
+ validates_numericality_of :adults, "Bitte wählen Sie die Anzahl der Erwachsenen."
37
+ validates_numericality_of :children, "Bitte wählen Sie die Anzahl der Kinder."
38
+ validates_numericality_of :infants, "Bitte wählen Sie die Anzahl der Babies."
39
+
40
+ validate do
41
+ if leaves_on and leaves_on < Date.today
42
+ add_error :leaves_on, "Abflug muss in der Zukunft liegen."
43
+ end
44
+
45
+ if returns_on and leaves_on and returns_on < leaves_on
46
+ add_error :returns_on, "Rückflug muss später als der Hinflug sein."
47
+ end
48
+
49
+ if leaves_on.nil?
50
+ add_error :leaves_on, "Bitte wählen Sie ein gültiges Abflugdatum aus."
51
+ end
52
+
53
+ if one_way.to_s == 'true' and not returns_on.to_s.empty?
54
+ add_error :returns_on, "Bitte wählen Sie entweder One-Way oder ein Rückflugdatum aus."
55
+ end
56
+
57
+ if one_way.to_s != 'true' and returns_on.to_s.empty?
58
+ add_error :returns_on, "Bitte wählen Sie ein gültiges Datum aus."
59
+ end
60
+
61
+ if adults.to_i < infants.to_i
62
+ add_error :passengers, "Die Anzahl der Erwachsenen muss mindestens gleich der Babies sein."
63
+ end
64
+
65
+ if adults.to_i < 1
66
+ add_error :passengers, "Es muss mindestens ein Erwachsener fliegen."
67
+ end
68
+
69
+ unless AIRPORTCODEEXPR =~ origin.to_s
70
+ add_error :origin, "Bitte wählen Sie einen Startflughafen."
71
+ end
72
+
73
+ unless AIRPORTCODEEXPR =~ destination.to_s
74
+ add_error :destination, "Bitte wählen Sie einen Zielflughafen."
75
+ end
76
+
77
+ unless /^(E|B|EB|BE)$/ =~ comfort
78
+ add_error :passengers, "Bitte geben Die einen gültigen Code für den Comfort an."
79
+ end
80
+
81
+ unless origin.to_s.empty? or destination.to_s.empty?
82
+ if origin == destination
83
+ add_error :destination, "Bitte wählen Sie unterschiedliche Start- und Zielflughäfen."
84
+ end
85
+ end
86
+ end
87
+
88
+ def item_path
89
+ 'flights'
90
+ end
91
+
92
+ def initialize(options = {})
93
+ @errors = {}
94
+ self.origin = options[:origin]
95
+ self.destination = options[:destination]
96
+ self.leaves_on = options[:leaves_on]
97
+ self.returns_on = options[:returns_on]
98
+ self.one_way = options[:one_way]
99
+ self.adults = options[:adults] || 1
100
+ self.children = options[:children] || 0
101
+ self.infants = options[:infants] || 0
102
+ self.comfort = (options[:comfort] || "E").upcase
103
+ end
104
+
105
+ def parameters
106
+ {
107
+ :key => config[:key],
108
+ :format => 'json',
109
+ :origin => origin,
110
+ :destination => destination,
111
+ :leaves_on => leaves_on.to_s,
112
+ :returns_on => returns_on.to_s,
113
+ :one_way => one_way,
114
+ :adults => adults,
115
+ :children => children,
116
+ :infants => infants,
117
+ :comfort => comfort
118
+ }
119
+ end
120
+
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,97 @@
1
+ # -*- coding: utf-8 -*-
2
+ module TAPI
3
+ module V3
4
+ class GenericSearch
5
+
6
+ extend Validations
7
+
8
+ attr_reader :client, :client_urls, :started_at, :start_time
9
+ attr_accessor :errors
10
+
11
+ def id
12
+ if @id
13
+ @id
14
+ elsif @client and @client.respond_to?(:id)
15
+ @client.id
16
+ end
17
+ end
18
+
19
+ def id=(id)
20
+ @id = id
21
+ end
22
+
23
+ include Configurable
24
+ include Utils
25
+
26
+ def add_error(name, message)
27
+ (errors[name] ||= []) << message
28
+ end
29
+
30
+ def valid?
31
+ errors.clear
32
+
33
+ self.class.inherited_validations.each do |validation|
34
+ instance_eval(&validation)
35
+ end
36
+
37
+ errors.empty?
38
+ end
39
+
40
+ def has_errors?
41
+ errors.any?
42
+ end
43
+
44
+ def reload
45
+ load_client
46
+ self
47
+ end
48
+
49
+ def api_url
50
+ port = config[:port] == 80 ? '' : ":#{config[:port]}"
51
+ "http://#{ config[:host] }#{ port }#{ config[:path] }"
52
+ end
53
+
54
+ def api_key
55
+ config[:key]
56
+ end
57
+
58
+ def post_url
59
+ api_url + '/' + item_path + '/searches.json'
60
+ end
61
+
62
+ def get(path, options={})
63
+ TAPI::V3::Client.new_from_get(api_url + path + '.json', options.merge(:key => api_key)).first
64
+ end
65
+
66
+ def start!
67
+ @started_at = Time.now
68
+ @client = TAPI::V3::Client.new_from_post(post_url, parameters).search
69
+ @start_time = Time.now - @started_at
70
+ @client_urls = @client.urls
71
+ end
72
+
73
+ def load_client
74
+ if @client_urls
75
+ client, etag = TAPI::V3::Client.new_from_get(@client_urls[:search_url], {}, @etag)
76
+ @client, @etag = client.search, etag unless @etag == etag
77
+ end
78
+ end
79
+
80
+ def restart!
81
+ if @client_urls
82
+ TAPI::V3::Client.new_from_post(@client_urls[:restart_url], {})
83
+ load_client
84
+ end
85
+ end
86
+
87
+ def method_missing(key, *args)
88
+ begin
89
+ @client.send(key, *args)
90
+ rescue NoMethodError
91
+ raise NoMethodError, "undefined method `#{key}' for #{self.class}", caller
92
+ end
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,213 @@
1
+ # -*- coding: utf-8 -*-
2
+ module TAPI
3
+ module V3
4
+ module Hotels
5
+ class Search < TAPI::V3::GenericSearch
6
+ attr_accessor :expired
7
+
8
+ attr_reader :arrival_date, :departure_date, :city_id, :region_id, :hotel_id
9
+ attr_reader :room_configuration, :single_rooms_count, :double_rooms_count
10
+
11
+ validates_numericality_of :single_rooms_count, "Bitte wählen Sie die Anzahl der Einzelzimmer."
12
+ validates_numericality_of :double_rooms_count, "Bitte wählen Sie die Anzahl der Doppelzimmer."
13
+
14
+ validates_presence_of :arrival_date, "Bitte wählen Sie ein gültiges Datum."
15
+ validates_presence_of :departure_date, "Bitte wählen Sie ein gültiges Datum."
16
+
17
+ validate do
18
+ if arrival_date and arrival_date < Date.today
19
+ add_error :arrival_date, "Das Anreisedatum darf nicht in der Vergangenheit liegen."
20
+ end
21
+
22
+ if single_rooms_count == 0 and double_rooms_count == 0
23
+ add_error :room_configuration, "Bitte mindestens ein Zimmer angeben."
24
+ end
25
+
26
+ if (single_rooms_count + double_rooms_count) > 3
27
+ add_error :room_configuration, "Bitte nicht mehr als 3 Zimmer angeben."
28
+ end
29
+
30
+ if arrival_date and departure_date and departure_date <= arrival_date
31
+ add_error :departure_date, "Das Abreisedatum muss mindestens einen Tag nach der Ankunft liegen."
32
+ end
33
+
34
+ if city_id.blank? and region_id.blank? and hotel_id.blank?
35
+ add_error :city_id, "Bitte geben Sie den gewünschten Aufenthaltsort an."
36
+ end
37
+
38
+ end
39
+
40
+ class << self
41
+
42
+ def parse_room_configuration(str)
43
+ rooms_strings = str.split('][').map {|r| r.gsub(/\]|\[/, '').split('|')}
44
+ raise ArgumentError, "#{str} is not a valid room configuration hash !" if rooms_strings.length > 3 || rooms_strings.empty?
45
+ room_collection = {}
46
+ rooms = rooms_strings.each_with_index do |guests, i|
47
+ room_collection[i + 1] ||= {}
48
+ guests.each do |g|
49
+ case g
50
+ when 'A'
51
+ room_collection[i + 1]['adults'] ||= 0
52
+ room_collection[i + 1]['adults'] += 1
53
+ when /\d/
54
+ room_collection[i + 1]['child'] ||= {}
55
+ last_key = room_collection[i + 1]['child'].keys.min || 0
56
+ room_collection[i + 1]['child'][last_key + 1] = g.to_i
57
+ else
58
+ raise ArgumentError, "#{str} is not a valid room configuration hash !"
59
+ end
60
+ end
61
+ end
62
+ room_collection
63
+ end
64
+
65
+ end # class
66
+
67
+ def initialize(options = {})
68
+ @errors = {}
69
+ @single_rooms_count = 0
70
+ @double_rooms_count = 0
71
+
72
+ self.arrival_date = options[:arrival_date]
73
+ self.departure_date = options[:departure_date]
74
+
75
+ self.city_id = options[:city_id]
76
+ self.region_id = options[:region_id]
77
+ self.hotel_id = options[:hotel_id]
78
+
79
+ if options[:room_configuration].blank?
80
+ self.single_rooms_count = options[:single_rooms_count] || 1
81
+ self.double_rooms_count = options[:double_rooms_count] || 0
82
+ else
83
+ self.room_configuration = options[:room_configuration]
84
+ end
85
+ end
86
+
87
+ def item_path
88
+ 'hotels'
89
+ end
90
+
91
+ def state
92
+ @client.status_detailed.state if @client
93
+ end
94
+
95
+ def location
96
+ case
97
+ when city_id then city
98
+ when region_id then region
99
+ when hotel_id then hotel
100
+ end
101
+ end
102
+
103
+ def location_type
104
+ case
105
+ when city_id then :city
106
+ when region_id then :region
107
+ when hotel_id then :hotel
108
+ end
109
+ end
110
+
111
+ def location_id
112
+ city_id || region_id || hotel_id
113
+ end
114
+
115
+ def arrival_date=(date)
116
+ @arrival_date = coerce_date(date)
117
+ rescue TypeError => e
118
+ Client.logger.debug e.message
119
+ Client.logger.debug e.backtrace.join("\n")
120
+ end
121
+
122
+ def departure_date=(date)
123
+ @departure_date = coerce_date(date)
124
+ rescue TypeError => e
125
+ Client.logger.debug e.message
126
+ Client.logger.debug e.backtrace.join("\n")
127
+ end
128
+
129
+ def city_id=(id)
130
+ @city_id = id.to_i unless id.to_s.empty?
131
+ end
132
+
133
+ def region_id=(id)
134
+ @region_id = id.to_i unless id.to_s.empty?
135
+ end
136
+
137
+ def hotel_id=(id)
138
+ @hotel_id = id.to_i unless id.to_s.empty?
139
+ end
140
+
141
+ def city
142
+ @city ||= get("/locations/cities/#{city_id}").city unless city_id.to_s.empty?
143
+ end
144
+
145
+ def region
146
+ @region ||= get("/locations/regions/#{region_id}").region unless region_id.to_s.empty?
147
+ end
148
+
149
+ def hotel
150
+ @hotel ||= get("/locations/hotels/#{hotel_id}").hotel unless hotel_id.to_s.empty?
151
+ end
152
+
153
+ def room_configuration=(config)
154
+ @room_configuration = config
155
+ rooms = Search.parse_room_configuration(config).values
156
+
157
+ @single_rooms_count = rooms.count { |room| room['adults'] == 1 }
158
+ @double_rooms_count = rooms.count { |room| room['adults'] == 2 }
159
+ end
160
+
161
+ def single_rooms_count=(count)
162
+ @single_rooms_count = Integer(count)
163
+ update_room_configuration!
164
+ end
165
+
166
+ def double_rooms_count=(count)
167
+ @double_rooms_count = Integer(count)
168
+ update_room_configuration!
169
+ end
170
+
171
+ def update_room_configuration!
172
+ @room_configuration = ('[A]' * single_rooms_count) + ('[A|A]' * double_rooms_count)
173
+ end
174
+
175
+ def to_param
176
+ {
177
+ :location_type => location_type,
178
+ :location_id => location_id,
179
+ :arrival_date => arrival_date.to_s,
180
+ :departure_date => departure_date.to_s,
181
+ :single_rooms_count => single_rooms_count,
182
+ :double_rooms_count => double_rooms_count
183
+ }
184
+ end
185
+
186
+ def parameters
187
+ {
188
+ :key => config[:key],
189
+ :format => 'json',
190
+ :city_id => city_id,
191
+ :region_id => region_id,
192
+ :hotel_id => hotel_id,
193
+ :arrival_date => arrival_date.to_s,
194
+ :departure_date => departure_date.to_s,
195
+ :room_configuration => room_configuration
196
+ }
197
+ end
198
+
199
+ # create a hotel search with the same parameters
200
+ # for a given hotel
201
+ def hotel_search(result_or_hotel_id)
202
+ hotel_id = result_or_hotel_id.is_a?(Fixnum) ? result_or_hotel_id : result_or_hotel_id.hotel_id
203
+ params = parameters
204
+ params.delete(:city_id)
205
+ params.delete(:region_id)
206
+ params[:hotel_id] = hotel_id
207
+ TAPI::V3::Hotels::Search.new(params)
208
+ end
209
+
210
+ end
211
+ end
212
+ end
213
+ end