flightstats-flex 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/README.md +252 -0
  2. data/lib/flightstats.rb +109 -0
  3. data/lib/flightstats/abstract_date.rb +6 -0
  4. data/lib/flightstats/actual_gate_departure.rb +4 -0
  5. data/lib/flightstats/actual_runway_departure.rb +4 -0
  6. data/lib/flightstats/airline.rb +10 -0
  7. data/lib/flightstats/airport.rb +88 -0
  8. data/lib/flightstats/airport_resources.rb +7 -0
  9. data/lib/flightstats/api.rb +73 -0
  10. data/lib/flightstats/api/errors.rb +120 -0
  11. data/lib/flightstats/api/net_http_adapter.rb +114 -0
  12. data/lib/flightstats/appendix.rb +6 -0
  13. data/lib/flightstats/arrival_date.rb +4 -0
  14. data/lib/flightstats/codeshare.rb +7 -0
  15. data/lib/flightstats/departure_date.rb +4 -0
  16. data/lib/flightstats/equipment.rb +10 -0
  17. data/lib/flightstats/estimated_gate_arrival.rb +4 -0
  18. data/lib/flightstats/estimated_gate_departure.rb +4 -0
  19. data/lib/flightstats/estimated_runway_arrival.rb +4 -0
  20. data/lib/flightstats/estimated_runway_departure.rb +4 -0
  21. data/lib/flightstats/extended_options.rb +6 -0
  22. data/lib/flightstats/flight.rb +61 -0
  23. data/lib/flightstats/flight_durations.rb +9 -0
  24. data/lib/flightstats/flight_equipment.rb +6 -0
  25. data/lib/flightstats/flight_id.rb +6 -0
  26. data/lib/flightstats/flight_leg.rb +25 -0
  27. data/lib/flightstats/flight_plan_planned_arrival.rb +4 -0
  28. data/lib/flightstats/flight_plan_planned_departure.rb +4 -0
  29. data/lib/flightstats/flight_status.rb +50 -0
  30. data/lib/flightstats/helper.rb +52 -0
  31. data/lib/flightstats/operational_times.rb +16 -0
  32. data/lib/flightstats/operator.rb +5 -0
  33. data/lib/flightstats/published_arrival.rb +4 -0
  34. data/lib/flightstats/published_departure.rb +4 -0
  35. data/lib/flightstats/resource.rb +108 -0
  36. data/lib/flightstats/schedule.rb +7 -0
  37. data/lib/flightstats/scheduled_gate_arrival.rb +4 -0
  38. data/lib/flightstats/scheduled_gate_departure.rb +4 -0
  39. data/lib/flightstats/version.rb +17 -0
  40. metadata +141 -0
@@ -0,0 +1,7 @@
1
+ module FlightStats
2
+ class AirportResources < Resource
3
+ attr_accessor :departure_gate,
4
+ :arrival_terminal,
5
+ :arrival_gate
6
+ end
7
+ end
@@ -0,0 +1,73 @@
1
+ module FlightStats
2
+ class API
3
+ require 'flightstats/api/errors'
4
+ require 'flightstats/resource'
5
+
6
+ @@base_uri = "https://api.flightstats.com"
7
+
8
+ class << self
9
+ # Additional HTTP headers sent with each API call
10
+ # @return [Hash{String => String}]
11
+ def headers
12
+ @headers ||= { 'Accept' => accept, 'User-Agent' => user_agent }
13
+ end
14
+
15
+ # @return [String, nil] Accept-Language header value
16
+ def accept_language
17
+ headers['Accept-Language']
18
+ end
19
+
20
+ # @param [String] language Accept-Language header value
21
+ def accept_language=(language)
22
+ headers['Accept-Language'] = language
23
+ end
24
+
25
+ # @return [Net::HTTPOK, Net::HTTPResponse]
26
+ # @raise [ResponseError] With a non-2xx status code.
27
+ def head uri, params = {}, options = {}
28
+ request :head, uri, { :params => params }.merge(options)
29
+ end
30
+
31
+ # @return [Net::HTTPOK, Net::HTTPResponse]
32
+ # @raise [ResponseError] With a non-2xx status code.
33
+ def get uri, params = {}, options = {}
34
+ request :get, uri, { :params => params }.merge(options)
35
+ end
36
+
37
+ # @return [Net::HTTPCreated, Net::HTTPResponse]
38
+ # @raise [ResponseError] With a non-2xx status code.
39
+ def post uri, body = nil, options = {}
40
+ request :post, uri, { :body => body.to_s }.merge(options)
41
+ end
42
+
43
+ # @return [Net::HTTPOK, Net::HTTPResponse]
44
+ # @raise [ResponseError] With a non-2xx status code.
45
+ def put uri, body = nil, options = {}
46
+ request :put, uri, { :body => body.to_s }.merge(options)
47
+ end
48
+
49
+ # @return [Net::HTTPNoContent, Net::HTTPResponse]
50
+ # @raise [ResponseError] With a non-2xx status code.
51
+ def delete uri, options = {}
52
+ request :delete, uri, options
53
+ end
54
+
55
+ # @return [URI::Generic]
56
+ def base_uri
57
+ URI.parse @@base_uri
58
+ end
59
+
60
+ # @return [String]
61
+ def user_agent
62
+ "FlightStats/#{FlightStats::Version}; #{RUBY_DESCRIPTION}"
63
+ end
64
+
65
+ private
66
+
67
+ def accept
68
+ 'application/json'
69
+ end
70
+ alias content_type accept
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,120 @@
1
+ require 'cgi'
2
+ require 'json'
3
+
4
+ module FlightStats
5
+ class API
6
+ # The superclass to all errors that occur when making an API request.
7
+ class ResponseError < Error
8
+ attr_reader :request
9
+ attr_reader :response
10
+
11
+ def initialize request, response
12
+ @request, @response = request, response
13
+ end
14
+
15
+ def code
16
+ response.code.to_i if response
17
+ end
18
+
19
+ def to_s
20
+ if description
21
+ return CGI.unescapeHTML [description, details].compact.join(' ')
22
+ end
23
+
24
+ return super unless code
25
+ "%d %s (%s %s)" % [
26
+ code, http_error, request.method, API.base_uri + request.path
27
+ ]
28
+ end
29
+
30
+ def message
31
+ json and json['error'] and json['error']['errorMessage']
32
+ end
33
+
34
+ def code
35
+ json and json['error'] and json['error']['errorCode']
36
+ end
37
+
38
+ private
39
+
40
+ def http_error
41
+ Helper.demodulize self.class.name.gsub(/([a-z])([A-Z])/, '\1 \2')
42
+ end
43
+
44
+ def json
45
+ return @json if defined? @json
46
+ @json = (JSON.parse(response.body) if response && !response.body.empty?)
47
+ end
48
+ end
49
+
50
+ # === 3xx Redirection
51
+ #
52
+ # Not an error, per se, but should result in one in the normal course of
53
+ # API interaction.
54
+ class Redirection < ResponseError
55
+ end
56
+
57
+ # === 4xx Client Error
58
+ #
59
+ # The superclass to all client errors (responses with status code 4xx).
60
+ class ClientError < ResponseError
61
+ end
62
+
63
+ # === 400 Bad Request
64
+ #
65
+ # Something was wrong with the request submitted, and it must be corrected
66
+ # before it can succeed. The response body will include details about the error.
67
+ class BadRequest < ClientError
68
+ end
69
+
70
+ # === 403 Forbidden
71
+ #
72
+ # Authorization failure; valid credentials were not provided.
73
+ class Forbidden < ClientError
74
+ end
75
+
76
+ # === 404 Not Found
77
+ #
78
+ # No resource was found at the specified URI.
79
+ class NotFound < ClientError
80
+ end
81
+
82
+ # === 405 Method Not Allowed
83
+ #
84
+ # The HTTP request specified a method (e.g. PUT, DELETE, OPTIONS, etc.)
85
+ # that is not supported by this resource.
86
+ class MethodNotAllowed < ClientError
87
+ end
88
+
89
+ # === 5xx Server Error
90
+ #
91
+ # The superclass to all server errors (responses with status code 5xx).
92
+ class ServerError < ResponseError
93
+ end
94
+
95
+ # === 500 Internal Server Error
96
+ #
97
+ # Unexpected server-side failure. Should this occur, please contact us
98
+ # for assistance. The response body will include a unique identifier
99
+ # that we can use to help locate the problem.
100
+ class InternalServerError < ServerError
101
+ end
102
+
103
+ # Error mapping by status code.
104
+ ERRORS = Hash.new { |hash, code|
105
+ unless hash.key? code
106
+ case code
107
+ when 400...500 then ClientError
108
+ when 500...600 then ServerError
109
+ else ResponseError
110
+ end
111
+ end
112
+ }.update(
113
+ 400 => BadRequest,
114
+ 403 => Forbidden,
115
+ 404 => NotFound,
116
+ 405 => MethodNotAllowed,
117
+ 500 => InternalServerError
118
+ ).freeze
119
+ end
120
+ end
@@ -0,0 +1,114 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'net/https'
4
+
5
+ module FlightStats
6
+ class API
7
+ module Net
8
+ module HTTPAdapter
9
+ # A hash of Net::HTTP settings configured before the request.
10
+ #
11
+ # @return [Hash]
12
+ def net_http
13
+ @net_http ||= {}
14
+ end
15
+
16
+ # Used to store any Net::HTTP settings.
17
+ #
18
+ # @example
19
+ # FlightStats::API.net_http = {
20
+ # :verify_mode => OpenSSL::SSL::VERIFY_PEER,
21
+ # :ca_path => "/etc/ssl/certs",
22
+ # :ca_file => "/opt/local/share/curl/curl-ca-bundle.crt"
23
+ # }
24
+ attr_writer :net_http
25
+
26
+ private
27
+
28
+ METHODS = {
29
+ :head => ::Net::HTTP::Head,
30
+ :get => ::Net::HTTP::Get,
31
+ :post => ::Net::HTTP::Post,
32
+ :put => ::Net::HTTP::Put,
33
+ :delete => ::Net::HTTP::Delete
34
+ }
35
+
36
+ def request method, uri, options = {}
37
+ head = headers.dup
38
+ head.update options[:head] if options[:head]
39
+ head.delete_if { |_, value| value.nil? }
40
+ uri = base_uri + uri
41
+
42
+ query_params = "?"
43
+ query_params += "#{CGI.escape 'appId'}=#{CGI.escape FlightStats.app_id.to_s}"
44
+ query_params += "&#{CGI.escape 'appKey'}=#{CGI.escape FlightStats.app_key.to_s}"
45
+ if options[:params] && !options[:params].empty?
46
+ pairs = options[:params].map { |key, value|
47
+ "#{CGI.escape key.to_s}=#{CGI.escape value.to_s}"
48
+ }
49
+ query_params += "&#{pairs.join '&'}"
50
+ end
51
+ uri += query_params
52
+
53
+ request = METHODS[method].new uri.request_uri, head
54
+ if options[:body]
55
+ request['Content-Type'] = content_type
56
+ request.body = options[:body]
57
+ end
58
+ if options[:etag]
59
+ request['If-None-Match'] = options[:etag]
60
+ end
61
+ if options[:format]
62
+ request['Accept'] = FORMATS[options[:format]]
63
+ end
64
+ if options[:locale]
65
+ request['Accept-Language'] = options[:locale]
66
+ end
67
+ http = ::Net::HTTP.new uri.host, uri.port
68
+ http.use_ssl = uri.scheme == 'https'
69
+ net_http.each_pair { |key, value| http.send "#{key}=", value }
70
+
71
+ if FlightStats.logger
72
+ FlightStats.log :info, "===> %s %s" % [request.method, uri]
73
+ headers = request.to_hash
74
+ headers['authorization'] &&= ['Basic [FILTERED]']
75
+ FlightStats.log :debug, headers.inspect
76
+ if request.body && !request.body.empty?
77
+ FlightStats.log :debug, request.body
78
+ end
79
+ start_time = Time.now
80
+ end
81
+
82
+ response = http.start { http.request request }
83
+ code = response.code.to_i
84
+
85
+ if FlightStats.logger
86
+ latency = (Time.now - start_time) * 1_000
87
+ level = case code
88
+ when 200...300 then :info
89
+ when 300...400 then :warn
90
+ when 400...500 then :error
91
+ else :fatal
92
+ end
93
+ FlightStats.log level, "<=== %d %s (%.1fms)" % [
94
+ code,
95
+ response.class.name[9, response.class.name.length].gsub(
96
+ /([a-z])([A-Z])/, '\1 \2'
97
+ ),
98
+ latency
99
+ ]
100
+ FlightStats.log :debug, response.to_hash.inspect
101
+ FlightStats.log :debug, response.body if response.body
102
+ end
103
+
104
+ case code
105
+ when 200...300 then response
106
+ else raise ERRORS[code].new request, response
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ extend Net::HTTPAdapter
113
+ end
114
+ end
@@ -0,0 +1,6 @@
1
+ module FlightStats
2
+ class Appendix < Resource
3
+ attr_accessor :airlines,
4
+ :airports
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ module FlightStats
2
+ class ArrivalDate < AbstractDate
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ module FlightStats
2
+ class Codeshare < Resource
3
+ attr_accessor :fs_code,
4
+ :flight_number,
5
+ :relationship
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module FlightStats
2
+ class DepartureDate < AbstractDate
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ module FlightStats
2
+ class Equipment < Resource
3
+ attr_accessor :iata,
4
+ :name,
5
+ :turbo_prop,
6
+ :jet,
7
+ :widebody,
8
+ :regional
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module FlightStats
2
+ class EstimatedGateArrival < AbstractDate
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module FlightStats
2
+ class EstimatedGateDeparture < AbstractDate
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module FlightStats
2
+ class EstimatedRunwayArrival < AbstractDate
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module FlightStats
2
+ class EstimatedRunwayDeparture < AbstractDate
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module FlightStats
2
+ class ExtendedOptions < Resource
3
+ attr_accessor :requested,
4
+ :interpreted
5
+ end
6
+ end
@@ -0,0 +1,61 @@
1
+ module FlightStats
2
+ class Flight < Resource
3
+ attr_accessor :departure_airport_fs_code,
4
+ :arrival_airport_fs_code,
5
+ :departure_date_from,
6
+ :departure_date_to,
7
+ :departure_days_of_week,
8
+ :arrival_date_adjustment,
9
+ :departure_time,
10
+ :arrival_time,
11
+ :distance_miles,
12
+ :flight_duration_minutes,
13
+ :layover_duration_minutes,
14
+ :flight_type,
15
+ :service_type,
16
+ :online,
17
+ :flight_legs
18
+
19
+ class << self
20
+ def direct_arriving_at(arrival_code, year, month, day, options = {})
21
+ from_response API.get("/flex/connections/rest/v1/json/direct/to/#{arrival_code}/arriving/#{year}/#{month}/#{day}", {}, options), 'flights'
22
+ end
23
+
24
+ def direct_departing_from(departure_code, year, month, day, options = {})
25
+ from_response API.get("/flex/connections/rest/v1/json/direct/from/#{departure_code}/departing/#{year}/#{month}/#{day}", {}, options), 'flights'
26
+ end
27
+
28
+ def direct_arriving_by_flight_number(carrier, flight_number, year, month, day, options = {})
29
+ from_response API.get("/flex/connections/rest/v1/json/direct/flight/#{carrier}/#{flight_number}/arriving/#{year}/#{month}/#{day}", {}, options), 'flights'
30
+ end
31
+
32
+ def direct_arriving_by_flight_number_and_location(carrier, flight_number, arrival_code, year, month, day, options = {})
33
+ from_response API.get("/flex/connections/rest/v1/json/direct/flight/#{carrier}/#{flight_number}/to/#{arrival_code}/arriving/#{year}/#{month}/#{day}", {}, options), 'flights'
34
+ end
35
+
36
+ def direct_departing_by_flight_number(carrier, flight_number, year, month, day, options = {})
37
+ from_response API.get("/flex/connections/rest/v1/json/direct/flight/#{carrier}/#{flight_number}/departing/#{year}/#{month}/#{day}", {}, options), 'flights'
38
+ end
39
+
40
+ def direct_departing_by_flight_number_and_location(carrier, flight_number, arrival_code, year, month, day, options = {})
41
+ from_response API.get("/flex/connections/rest/v1/json/direct/flight/#{carrier}/#{flight_number}/to/#{arrival_code}/departing/#{year}/#{month}/#{day}", {}, options), 'flights'
42
+ end
43
+
44
+ def direct_and_connecting_arriving(departure_code, arrival_code, year, month, day, options = {})
45
+ from_response API.get("/flex/connections/rest/v1/json/connecting/from/#{departure_code}/to/#{arrival_code}/arriving/#{year}/#{month}/#{day}", {}, options), 'flights'
46
+ end
47
+
48
+ def direct_and_connecting_departing(departure_code, arrival_code, year, month, day, options = {})
49
+ from_response API.get("/flex/connections/rest/v1/json/connecting/from/#{departure_code}/to/#{arrival_code}/departing/#{year}/#{month}/#{day}", {}, options), 'flights'
50
+ end
51
+ end
52
+
53
+ def to_s
54
+ str = "#{departure_airport_fs_code} - #{arrival_airport_fs_code} (#{distance_miles} miles): from #{departure_date_from} to #{departure_date_to}"
55
+ flight_legs.each do |leg|
56
+ str << "\n #{leg.to_s}"
57
+ end
58
+ str
59
+ end
60
+ end
61
+ end