flightstats-flex 0.1.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.
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