planefinder 0.0.1

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,67 @@
1
+ require "httparty"
2
+ require "json"
3
+ require "uri"
4
+ require "geokit"
5
+ require_relative "./planefinder/version"
6
+ require_relative "./planefinder/airplane_category"
7
+ require_relative "./planefinder/airplane_make"
8
+ require_relative "./planefinder/airplane_model"
9
+ require_relative "./planefinder/airplane_listing"
10
+ require_relative "./planefinder/airplane_geocoder"
11
+
12
+ module Planefinder
13
+ include HTTParty
14
+ base_uri 'www.trade-a-plane.com'
15
+
16
+ @@urls = {}
17
+ @@urls[:airplane_categories] = "/app_ajax/get_categories?listing_type_id=1"
18
+ @@urls[:airplane_makes_for_category] = "/app_ajax/get_aircraft_makes?category_id=%s&make_type_id=1"
19
+ @@urls[:airplane_models_for_category_and_make] = "/app_ajax/get_aircraft_models?category_id=%s&make_id=%s"
20
+ @@urls[:search_by_model_make_category] = "/app_ajax/search?s-type=aircraft&model=%s&make=%s&category=%s"
21
+
22
+ def self.valid_args?(*args)
23
+ args.all? { |a| a.to_i > 0 }
24
+ end
25
+
26
+ def self.get_categories
27
+ response = self.get(@@urls[:airplane_categories])
28
+ categories = []
29
+ JSON.parse(response.body).each do |cat|
30
+ next if(cat.has_key?('special_use_counts'))
31
+ categories << AirplaneCategory.new(cat)
32
+ end
33
+ categories
34
+ end
35
+
36
+ def self.get_makes_for_category(category)
37
+ response = self.get(@@urls[:airplane_makes_for_category] % category.id)
38
+ makes = []
39
+ JSON.parse(response.body).each do |make|
40
+ makes << AirplaneMake.new(make, category)
41
+ end
42
+ makes
43
+ end
44
+
45
+ def self.get_models_for_category_and_make(category, make)
46
+ response = self.get(@@urls[:airplane_models_for_category_and_make] % [category.id, make.id])
47
+ models = []
48
+ JSON.parse(response.body).each do |model|
49
+ models << AirplaneModel.new(model, category, make)
50
+ end
51
+ models
52
+ end
53
+
54
+ def self.search_by_model_make_category(model, make, cat)
55
+ response = self.get(@@urls[:search_by_model_make_category] % [model, make, cat].map { |s| URI.escape(s.name) })
56
+ listings = []
57
+ JSON.parse(response.body).each do |listing|
58
+ listings << AirplaneListing.new(listing)
59
+ end
60
+ listings
61
+ end
62
+
63
+ class << Planefinder
64
+ alias_method :get_makes, :get_makes_for_category
65
+ alias_method :get_models, :get_models_for_category_and_make
66
+ end
67
+ end
@@ -0,0 +1,30 @@
1
+ module Planefinder
2
+ # Contains categories like "Single Engine Piston" or "Jet" or "Turbine Helicopter"
3
+ class AirplaneCategory
4
+ attr_reader :count, :id, :name, :sort_priority
5
+
6
+ def initialize(json_category)
7
+ validate_category(json_category)
8
+ @count = json_category['count'].to_i
9
+ @id = json_category['id'].to_i
10
+ @name = json_category['name'].to_s
11
+ @sort_priority = json_category['sort_priority'].to_i
12
+ end
13
+
14
+ def validate_category(cat)
15
+ throw "Category must have count, id, name, and sort_priority fields" unless
16
+ ['count', 'id', 'name', 'sort_priority'].all? { |field| cat.include?(field) }
17
+ end
18
+
19
+ def ==(other)
20
+ @count == other.count &&
21
+ @id == other.id &&
22
+ @name == other.name &&
23
+ @sort_priority == other.sort_priority
24
+ end
25
+
26
+ def get_makes
27
+ Planefinder.get_makes_for_category(self)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,66 @@
1
+ require "geokit"
2
+ require "sequel"
3
+
4
+ module Geokit
5
+ module Geocoders
6
+ __define_accessors
7
+
8
+ # Replace name 'External' (below) with the name of your custom geocoder class
9
+ # and use :external to specify this geocoder in your list of geocoders.
10
+ class AirplaneGeocoder < Geocoder
11
+ private
12
+ def self.do_geocode(address, options = {})
13
+ record = case address
14
+ when /,/
15
+ city, state = address.split(',').map(&:strip)
16
+ self.lookup_by_city_state(city, state)
17
+ when /^\d{5}$/
18
+ self.lookup_by_zip(address)
19
+ when /[A-Z]{2}/
20
+ self.lookup_by_state(address)
21
+ else
22
+ self.lookup_by_phone(address) if self.us_phone_number?(address)
23
+ end
24
+ record ? LatLng.new(record[:latitude], record[:longitude]) : LatLng.new
25
+ end
26
+
27
+ def self.db
28
+ File.join(File.dirname(__FILE__), "../../db/geocoding.db")
29
+ end
30
+
31
+ def self.lookup_by_city_state(city, state)
32
+ Sequel.sqlite(self.db) do |db|
33
+ db[:zip_codes].first(:city => city, :state => state)
34
+ end
35
+ end
36
+
37
+ def self.lookup_by_state(state)
38
+ Sequel.sqlite(self.db) do |db|
39
+ db[:states].first(:abbreviation => state)
40
+ end
41
+ end
42
+
43
+ def self.lookup_by_zip(zip)
44
+ Sequel.sqlite(self.db) do |db|
45
+ db[:zip_codes].first(:zip => zip)
46
+ end
47
+ end
48
+
49
+ def self.lookup_by_phone(phone)
50
+ area_code = self.sanitize_phone(phone)[0, 3]
51
+ record = Sequel.sqlite(self.db) do |db|
52
+ db[:zip_codes].where(Sequel.like(:area_codes, area_code)).first
53
+ end
54
+ record ? self.lookup_by_state(record[:state]) : nil
55
+ end
56
+
57
+ def self.sanitize_phone(str)
58
+ str.gsub(/[+( ).-]/, '')[/\d{10}$/]
59
+ end
60
+
61
+ def self.us_phone_number?(str)
62
+ self.sanitize_phone(str) =~ /^\d{10}$/
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,68 @@
1
+ module Planefinder
2
+ # Contains an airplane listing, including contact info, total engine time, etc.
3
+ class AirplaneListing
4
+ attr_reader :properties
5
+
6
+ PREFERRED_PROPERTY_ORDER = ['zipcode', 'city', 'state', 'home_phone', 'work_phone', 'fax',
7
+ 'aff_zip', 'aff_city', 'aff_state', 'aff_home_phone', 'aff_work_phone', 'aff_fax']
8
+
9
+ def initialize(listing_hash)
10
+ # replace empty strings with nil
11
+ @properties = listing_hash.inject({}) do |h, (k,v)|
12
+ v == "" ? h[k] = nil : h[k] = v
13
+ h
14
+ end
15
+ define_attributes(@properties)
16
+ end
17
+
18
+ def metaclass
19
+ class << self
20
+ self
21
+ end
22
+ end
23
+
24
+ def define_attributes(hash)
25
+ hash.each do |k, v|
26
+ next if instance_variable_defined?("@#{k}") || self.class.method_defined?(k.to_sym)
27
+ metaclass.send :attr_reader, k
28
+ instance_variable_set("@#{k}".to_sym, v)
29
+ end
30
+ end
31
+
32
+ def location
33
+ Geokit::Geocoders::AirplaneGeocoder.geocode(location_text) if location_type
34
+ end
35
+
36
+ def location_type
37
+ PREFERRED_PROPERTY_ORDER.each do |p|
38
+ if p =~ /city/
39
+ city = p
40
+ state = p.gsub('city', 'state')
41
+ return "#{city}, #{state}" if @properties[city] and @properties[state]
42
+ else
43
+ return p if @properties[p]
44
+ end
45
+ end
46
+ nil
47
+ end
48
+
49
+ def location_text
50
+ # the type might be a "city, state", but otherwise treat it like a single value
51
+ types = location_type.split(", ")
52
+ types.length == 1 ? @properties[types[0]] : "#{@properties[types[0]]}, #{@properties[types[1]]}"
53
+ end
54
+
55
+ def location_description
56
+ location_type + ": " + location_text
57
+ end
58
+
59
+ def [](property_name)
60
+ property_name = property_name.to_s if property_name.class == Symbol
61
+ @properties[property_name]
62
+ end
63
+
64
+ def to_s
65
+ @properties.inspect
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,24 @@
1
+ module Planefinder
2
+ # Contains aircraft makes like "Cessna" or "Robinson"
3
+ class AirplaneMake
4
+ attr_reader :count, :id, :name, :category
5
+
6
+ def initialize(json, category)
7
+ @count = json['count'].to_i
8
+ @id = json['id'].to_i
9
+ @name = json['name'].to_s
10
+ @category = category
11
+ end
12
+
13
+ def ==(other)
14
+ @count == other.count &&
15
+ @id == other.id &&
16
+ @name == other.name &&
17
+ @category == other.category
18
+ end
19
+
20
+ def get_models
21
+ Planefinder.get_models_for_category_and_make(@category, self)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ module Planefinder
2
+ # Contains aircraft models like "DA20" (Diamond) or "172" (Cessna)
3
+ class AirplaneModel
4
+ attr_reader :count, :id, :model_group, :name, :category, :make
5
+
6
+ def initialize(json, category, make)
7
+ @count = json['count'].to_i
8
+ @id = json['id'].to_i
9
+ @model_group = json['model_group'].to_s
10
+ @name = json['name'].to_s
11
+ @category = category
12
+ @make = make
13
+ end
14
+
15
+ def ==(other)
16
+ @count == other.count &&
17
+ @id == other.id &&
18
+ @model_group == other.model_group &&
19
+ @name == other.name &&
20
+ @category == other.category &&
21
+ @make == other.make
22
+ end
23
+
24
+ def get_listings
25
+ Planefinder.search_by_model_make_category(self, @make, @category)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Planefinder
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'planefinder/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "planefinder"
8
+ spec.version = Planefinder::VERSION
9
+ spec.authors = ["Dave Ceddia"]
10
+ spec.email = ["dceddia@gmail.com"]
11
+ spec.description = %q{Find airplanes for sale and their approximate locations}
12
+ spec.summary = %q{Find airplanes for sale and narrow results by location}
13
+ spec.homepage = "https://github.com/dceddia/planefinder"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "httparty"
22
+ spec.add_dependency "geokit"
23
+ spec.add_dependency "sequel"
24
+ spec.add_dependency "sqlite3"
25
+
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "fakeweb"
30
+ end
@@ -0,0 +1 @@
1
+ [{"sort_priority":"1","count":"2429","name":"Single Engine Piston","id":"1"},{"sort_priority":"2","count":"599","name":"Multi Engine Piston","id":"2"},{"sort_priority":"3","count":"350","name":"TurboProp","id":"3"},{"sort_priority":"4","count":"370","name":"Jet","id":"4"},{"sort_priority":"5","count":"110","name":"Piston Helicopter","id":"100"},{"sort_priority":"6","count":"145","name":"Turbine Helicopter","id":"101"},{"sort_priority":"7","count":"6","name":"Ultralight","id":"6"},{"sort_priority":"8","count":"3","name":"Glider / Sailplane","id":"7"},{"sort_priority":"10","count":"3","name":"Rotary Wing","id":"9"},{"special_use_counts":{"rebuildable":"24","antique":"234","agricultural":"40","aerobatic":"77","light_sport":"106","amphibious":"170","warbird":"94","homebuilt":"129","utility":"50"}}]
@@ -0,0 +1 @@
1
+ [{"home_phone":"6514506200","overhaul_4_type":null,"serial_number":"40.766","aff_name":"Exclusive Aviation","company_state":"MN","overhaul_3_type":null,"suppress_url":"f","aff_country":"USA","aff_city":"St Paul","email":"sales@exclusiveaviation.com","url":"www.exclusiveaviation.com","category":"Single Engine Piston","overhaul_2_time":null,"flight_rules":"","aff_state":"MN","model_group":"DA40 Series","prop_4_time":null,"description":"1980 Diamond DA40XL, 870 TT A&E, G1000 Avionics Suite, Active Traffic, GFC700 autopilot, LR tanks, complete logs.","overhaul_2_type":null,"registration_number":"N866DS","prop_2_time":null,"seller_id":"66538","hot_section_2_time":null,"seller_category":"Dealer","aff_email":"gary@exclusiveaviation.com","aff_work_phone":"","total_seats":null,"overhaul_1_time":"870","add_date":"2012-07-20 08:19:03","aerobatic":"f","category_id":"1","fax":"6514506201","utility":"f","hot_section_1_time":null,"hot_section_3_time":null,"country":"USA","prop_1_time":null,"overhaul_1_type":"","update_date":"12/6/2012","warbird":"f","total_time":"870","year":"2007","model":"DA40XL","company_name":"Exclusive Aviation","price_extension":null,"state":"MN","suppress_address":"f","work_phone":"","rebuildable":"f","year_interior":null,"aff_fax":"","sale_status":"For Sale","end_date":"2012-07-20 00:00:00","price":"215000.00","listing_id":"1581835","agricultural":"f","prop_3_time":null,"aff_contact_name":"Gary Newhard - Piston Sales","rating":"1","aff_addr1":"607 Eaton St","listing_type_id":"1","city":"","light_sport":"f","company_country":"USA","show_serial":"t","aff_addr2":"","overhaul_3_time":null,"start_date":"2012-07-20 00:00:00","suppress_phone_2":"f","amphibious":"f","make":"Diamond","suppress_phone":"f","suppress_email":"f","zipcode":"","company_city":"St Paul","aff_home_phone":"9522507817","aff_zip":"55107","overhaul_4_time":null,"hot_section_4_time":null,"homebuilt":"f","suppress_name":"f","year_painted":null,"condition":"Used","modify_date":"2012-12-06 12:07:38"},{"home_phone":"619-227-7932","overhaul_4_type":null,"serial_number":"40.720","aff_name":null,"company_state":"CA","overhaul_3_type":null,"suppress_url":"f","aff_country":null,"aff_city":null,"email":"tlancemurray@mac.com","url":"","category":"Single Engine Piston","overhaul_2_time":null,"flight_rules":"IFR","aff_state":null,"model_group":"DA40 Series","prop_4_time":null,"description":"Beautiful 2007 Diamond DA40XL. NO DAMAGE HISTORY This is the model that included all the options including the GFC-700 autopilot and long range tanks. You will not find a better Diamond DA40XL for the price. I am not a broker/dealer. Principals only unless you have a buyer and no leaseback requests.","overhaul_2_type":null,"registration_number":"N720LM","prop_2_time":null,"seller_id":"71087","hot_section_2_time":null,"seller_category":"Transient","aff_email":null,"aff_work_phone":null,"total_seats":"4","overhaul_1_time":null,"add_date":"2013-05-10 01:16:50","aerobatic":"f","category_id":"1","fax":"","utility":"t","hot_section_1_time":null,"hot_section_3_time":null,"country":"USA","prop_1_time":null,"overhaul_1_type":null,"update_date":"5/28/2013","warbird":"f","total_time":"1650","year":"2007","model":"DA40XL","company_name":"Lance Murray Aviation LLC","price_extension":null,"state":"CA","suppress_address":"t","work_phone":"","rebuildable":"f","year_interior":"2012","aff_fax":null,"sale_status":"For Sale","end_date":"2013-06-10 00:00:00","price":"189500.00","listing_id":"1654691","agricultural":"f","prop_3_time":null,"aff_contact_name":null,"rating":"1","aff_addr1":null,"listing_type_id":"1","city":"San Diego","light_sport":"f","company_country":"USA","show_serial":"t","aff_addr2":null,"overhaul_3_time":null,"start_date":"2013-05-10 00:00:00","suppress_phone_2":"f","amphibious":"f","make":"Diamond","suppress_phone":"f","suppress_email":"f","zipcode":"","company_city":"San Marcos","aff_home_phone":null,"aff_zip":null,"overhaul_4_time":null,"hot_section_4_time":null,"homebuilt":"f","suppress_name":"t","year_painted":"2007","condition":"Used","modify_date":"2013-05-28 15:22:29"},{"home_phone":"8592339399","overhaul_4_type":"","serial_number":"","aff_name":null,"company_state":"KY","overhaul_3_type":"","suppress_url":"f","aff_country":null,"aff_city":null,"email":"sales@airmart.com","url":"http://www.airmart.com","category":"Single Engine Piston","overhaul_2_time":null,"flight_rules":"","aff_state":null,"model_group":"DA40 Series","prop_4_time":null,"description":"2007 DA40 XL, N893DS, 566 Snew on engine, airframe and prop, Integrated GFC700 AP, Garmin G100 PFD, P&I 8, NDH, Ann due 1/14.","overhaul_2_type":"","registration_number":"N893DS","prop_2_time":null,"seller_id":"48217","hot_section_2_time":null,"seller_category":"Dealer","aff_email":null,"aff_work_phone":null,"total_seats":null,"overhaul_1_time":"566","add_date":"2013-05-13 15:08:41","aerobatic":"f","category_id":"1","fax":"8592230222","utility":"f","hot_section_1_time":null,"hot_section_3_time":null,"country":"USA","prop_1_time":"566","overhaul_1_type":"SNEW","update_date":"5/24/2013","warbird":"f","total_time":"566","year":"2007","model":"DA40XL","company_name":"Airmart","price_extension":"Trades Considered","state":"KY","suppress_address":"f","work_phone":"8595339399","rebuildable":"f","year_interior":null,"aff_fax":null,"sale_status":"For Sale","end_date":"2013-07-19 00:00:00","price":"209000.00","listing_id":"1654806","agricultural":"f","prop_3_time":null,"aff_contact_name":null,"rating":"1","aff_addr1":null,"listing_type_id":"1","city":"","light_sport":"f","company_country":"USA","show_serial":"t","aff_addr2":null,"overhaul_3_time":null,"start_date":"2013-05-13 00:00:00","suppress_phone_2":"f","amphibious":"f","make":"Diamond","suppress_phone":"f","suppress_email":"f","zipcode":"","company_city":"Lexington","aff_home_phone":null,"aff_zip":null,"overhaul_4_time":null,"hot_section_4_time":null,"homebuilt":"f","suppress_name":"f","year_painted":null,"condition":"Used","modify_date":"2013-05-24 14:38:22"}]
@@ -0,0 +1 @@
1
+ [{"count":"1","model_group":"DA20 Series","name":"DA20-C1 Evolution","id":"4138"},{"count":"1","model_group":"DA20 Series","name":"DA20-C1 Katana","id":"4139"},{"count":"5","model_group":"DA40 Series","name":"DA40","id":"4140"},{"count":"2","model_group":"DA40 Series","name":"DA40 XLS","id":"4143"},{"count":"3","model_group":"DA40 Series","name":"DA40XL","id":"4146"},{"count":"1","model_group":"HK36 Series","name":"HK36 TTS","id":"4558"}]
@@ -0,0 +1 @@
1
+ [{"count":"8","name":"AERO","id":"5"},{"count":"13","name":"Airbus","id":"29"},{"count":"1","name":"Astra/Gulfstream","id":"50"},{"count":"11","name":"Beechcraft","id":"69"},{"count":"32","name":"Boeing","id":"77"},{"count":"1","name":"Bombardier","id":"81"},{"count":"10","name":"Bombardier/Challenger","id":"82"},{"count":"101","name":"Cessna","id":"114"},{"count":"25","name":"Dassault","id":"151"},{"count":"1","name":"Douglas","id":"158"},{"count":"4","name":"Eclipse","id":"167"},{"count":"8","name":"Embraer","id":"170"},{"count":"3","name":"Fairchild Dornier","id":"188"},{"count":"1","name":"Folland","id":"206"},{"count":"2","name":"Fouga","id":"208"},{"count":"14","name":"Gulfstream","id":"234"},{"count":"41","name":"Hawker","id":"242"},{"count":"47","name":"Learjet","id":"287"},{"count":"1","name":"Lockheed","id":"294"},{"count":"7","name":"McDonnell Douglas","id":"307"},{"count":"4","name":"Mikoyan","id":"317"},{"count":"5","name":"Mitsubishi","id":"323"},{"count":"1","name":"Morane-Saulnier","id":"329"},{"count":"1","name":"North American","id":"350"},{"count":"1","name":"PZL","id":"383"},{"count":"11","name":"Sabre","id":"420"},{"count":"1","name":"Soko","id":"448"},{"count":"11","name":"Westwind","id":"533"}]
@@ -0,0 +1 @@
1
+ [{"count":"4","name":"Bell","id":"70"},{"count":"10","name":"Hiller","id":"248"},{"count":"1","name":"Hughes","id":"255"},{"count":"79","name":"Robinson","id":"406"},{"count":"2","name":"Rotorway","id":"411"},{"count":"6","name":"Schweizer","id":"425"},{"count":"1","name":"Sikorsky","id":"437"},{"count":"1","name":"Vertical Aviation Technologies","id":"514"},{"count":"1","name":"Wanted All Makes","id":"742"}]
@@ -0,0 +1 @@
1
+ [{"count":"5","model_group":"R22 Series","name":"R22","id":"5153"},{"count":"7","model_group":"R22 Series","name":"R22 Beta","id":"5154"},{"count":"16","model_group":"R22 Series","name":"R22 Beta II","id":"5155"},{"count":"6","model_group":"R44 Series","name":"R44","id":"6175"},{"count":"6","model_group":"R44 Series","name":"R44 Astro","id":"5159"},{"count":"4","model_group":"R44 Series","name":"R44 Clipper","id":"5160"},{"count":"2","model_group":"R44 Series","name":"R44 Clipper II","id":"5161"},{"count":"1","model_group":"R44 Series","name":"R44 Newscopter","id":"5162"},{"count":"9","model_group":"R44 Series","name":"R44 Raven I","id":"5163"},{"count":"23","model_group":"R44 Series","name":"R44 Raven II","id":"5164"}]
@@ -0,0 +1 @@
1
+ [{"count":"4","name":"AMD","id":"37"},{"count":"1","name":"Aero Adventure","id":"6"},{"count":"6","name":"Aero Commander","id":"7"},{"count":"1","name":"Aerocar International","id":"665"},{"count":"1","name":"Aerocomp Inc.","id":"9"},{"count":"13","name":"Aeronca","id":"13"},{"count":"1","name":"Aeroprakt Ltd","id":"14"},{"count":"1","name":"Air Tractor","id":"28"},{"count":"3","name":"Alon Aircoupe","id":"568"},{"count":"1","name":"Alpi Aviation","id":"35"},{"count":"32","name":"American Champion","id":"38"},{"count":"1","name":"American Legend","id":"40"},{"count":"1","name":"Antonov","id":"44"},{"count":"4","name":"Arion Aircraft LLC","id":"673"},{"count":"30","name":"Aviat","id":"55"},{"count":"1","name":"Bakeng Duece","id":"64"},{"count":"1","name":"Barracuda","id":"668"},{"count":"1","name":"Bearhawk","id":"761"},{"count":"3","name":"Bede","id":"68"},{"count":"247","name":"Beechcraft","id":"69"},{"count":"1","name":"Bell","id":"70"},{"count":"29","name":"Bellanca","id":"72"},{"count":"11","name":"Boeing/Stearman","id":"78"},{"count":"1","name":"Bolkow","id":"80"},{"count":"1","name":"Burgess-Wright","id":"712"},{"count":"747","name":"Cessna","id":"114"},{"count":"1","name":"Chance Vought","id":"119"},{"count":"123","name":"Cirrus","id":"125"},{"count":"3","name":"Citabria","id":"625"},{"count":"10","name":"Columbia","id":"128"},{"count":"16","name":"Commander","id":"131"},{"count":"1","name":"Consolidated Vultee","id":"136"},{"count":"9","name":"Cubcrafters","id":"143"},{"count":"1","name":"Culver","id":"145"},{"count":"1","name":"Curtiss","id":"146"},{"count":"1","name":"Curtiss-Wright","id":"147"},{"count":"4","name":"Czech Sport Aircraft","id":"148"},{"count":"1","name":"Dakota Cub Aircraft","id":"780"},{"count":"1","name":"Davis Aircraft Corp","id":"152"},{"count":"24","name":"Dehavilland","id":"153"},{"count":"13","name":"Diamond","id":"155"},{"count":"3","name":"Dova","id":"574"},{"count":"1","name":"Dynaero","id":"161"},{"count":"1","name":"Eaa Biplane","id":"162"},{"count":"2","name":"Ekolot","id":"710"},{"count":"7","name":"Ercoupe","id":"174"},{"count":"1","name":"Ercoupe/Forney","id":"176"},{"count":"6","name":"Evektor","id":"180"},{"count":"2","name":"Extra Flugzeugbau","id":"185"},{"count":"2","name":"Fairchild","id":"187"},{"count":"1","name":"Fairey","id":"190"},{"count":"2","name":"Fantasy Air","id":"193"},{"count":"1","name":"Fisher","id":"197"},{"count":"10","name":"Flight Design","id":"571"},{"count":"3","name":"Focke Wulf","id":"204"},{"count":"1","name":"Fokker","id":"205"},{"count":"6","name":"Found Aircraft Co","id":"209"},{"count":"1","name":"Fuji","id":"214"},{"count":"1","name":"Funk","id":"215"},{"count":"3","name":"Gippsland Aeronautics / GippsAero","id":"221"},{"count":"12","name":"Glasair Aviation","id":"222"},{"count":"1","name":"Globe","id":"223"},{"count":"3","name":"Great Lakes","id":"226"},{"count":"1","name":"Great Plains","id":"227"},{"count":"6","name":"Grumman","id":"231"},{"count":"22","name":"Grumman/American General","id":"232"},{"count":"1","name":"Harmon","id":"237"},{"count":"3","name":"Helio","id":"245"},{"count":"5","name":"Homebuilt","id":"612"},{"count":"1","name":"Iar S.A. Brasov","id":"256"},{"count":"9","name":"Jabiru","id":"266"},{"count":"2","name":"Just Aircraft","id":"732"},{"count":"19","name":"Lake","id":"283"},{"count":"18","name":"Lancair","id":"284"},{"count":"2","name":"Laser","id":"286"},{"count":"1","name":"Liberty Aerospace","id":"293"},{"count":"2","name":"Loehle","id":"295"},{"count":"5","name":"Luscombe","id":"297"},{"count":"43","name":"Maule","id":"302"},{"count":"2","name":"Max Holste","id":"304"},{"count":"1","name":"Micco","id":"313"},{"count":"1","name":"Mitsubishi","id":"323"},{"count":"1","name":"Monocoupe","id":"327"},{"count":"129","name":"Mooney","id":"328"},{"count":"4","name":"Murphy","id":"333"},{"count":"3","name":"Mustang Aeronautics","id":"335"},{"count":"6","name":"Nanchang","id":"337"},{"count":"11","name":"Navion","id":"339"},{"count":"1","name":"Navy","id":"547"},{"count":"24","name":"North American","id":"350"},{"count":"2","name":"PZL","id":"383"},{"count":"1","name":"Papa 51 Ltd. Co.","id":"356"},{"count":"1","name":"Piel","id":"363"},{"count":"5","name":"Pilatus","id":"365"},{"count":"543","name":"Piper","id":"366"},{"count":"12","name":"Pitts","id":"369"},{"count":"3","name":"Porterfield","id":"371"},{"count":"1","name":"Preceptor","id":"373"},{"count":"2","name":"Produits Aviatech Inc","id":"377"},{"count":"1","name":"Quad City","id":"385"},{"count":"1","name":"Questair","id":"388"},{"count":"3","name":"RANS","id":"393"},{"count":"7","name":"REMOS","id":"575"},{"count":"3","name":"Republic","id":"401"},{"count":"1","name":"Rihn","id":"403"},{"count":"3","name":"Rockwell","id":"408"},{"count":"2","name":"Rutan","id":"414"},{"count":"1","name":"Ryan","id":"416"},{"count":"3","name":"Seawind","id":"430"},{"count":"1","name":"Sequoia","id":"432"},{"count":"1","name":"Sherpa","id":"600"},{"count":"3","name":"Siai Marchetti","id":"434"},{"count":"1","name":"Snow","id":"446"},{"count":"14","name":"Socata","id":"447"},{"count":"1","name":"Sonex","id":"450"},{"count":"1","name":"Speed Queen","id":"752"},{"count":"12","name":"Stinson","id":"468"},{"count":"7","name":"Storm Aircraft","id":"770"},{"count":"3","name":"Sukhoi","id":"473"},{"count":"4","name":"Swift","id":"476"},{"count":"2","name":"Symphony Aircraft","id":"477"},{"count":"4","name":"Taylorcraft","id":"480"},{"count":"3","name":"Tecnam","id":"484"},{"count":"1","name":"Thorp","id":"489"},{"count":"2","name":"Thunder Mustang","id":"763"},{"count":"1","name":"Thurston Aircraft Corporation","id":"491"},{"count":"1","name":"Tiger Aircraft","id":"493"},{"count":"2","name":"Travel Air","id":"496"},{"count":"33","name":"Vans","id":"510"},{"count":"1","name":"Varga","id":"511"},{"count":"6","name":"Velocity Aircraft","id":"513"},{"count":"12","name":"Waco","id":"579"},{"count":"2","name":"Wag Aero","id":"525"},{"count":"9","name":"Yakovlev","id":"542"},{"count":"6","name":"Zenair/Zenith","id":"543"},{"count":"1","name":"Zlin","id":"546"}]
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ module Planefinder
4
+ describe AirplaneCategory do
5
+ let(:json_categories) { JSON.parse(file_fixture('airplane_categories.json')) }
6
+
7
+ it "should be initializable with a JSON category description" do
8
+ ac = AirplaneCategory.new(json_categories.first)
9
+ ac.count.should == 2429
10
+ ac.id.should == 1
11
+ ac.name.should == "Single Engine Piston"
12
+ ac.sort_priority.should == 1
13
+ end
14
+
15
+ it "should throw an error with an invalid category" do
16
+ special = json_categories.select { |c| c.has_key?('special_use_counts') }
17
+ expect { AirplaneCategory.new(special) }.to raise_error
18
+ end
19
+
20
+ it "should support ==" do
21
+ a = AirplaneCategory.new(json_categories.first)
22
+ b = AirplaneCategory.new(json_categories.first)
23
+
24
+ a.should == b
25
+ end
26
+
27
+ it "should be able to fetch makes for its category" do
28
+ ac = AirplaneCategory.new(json_categories.first)
29
+ makes = [1, 2, 3]
30
+ Planefinder.should_receive(:get_makes_for_category).with(ac) { makes }
31
+ ac.get_makes.should == makes
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,87 @@
1
+ require "csv"
2
+
3
+ module Geokit
4
+ module Geocoders
5
+ describe AirplaneGeocoder do
6
+ it "should return a LatLng when given a valid city, state" do
7
+ loc = Geokit::Geocoders::AirplaneGeocoder.geocode('Boston, MA')
8
+ loc.class.should == LatLng
9
+ loc.should be_valid
10
+ end
11
+
12
+ it "should return invalid LatLng for invalid city, state combinations" do
13
+ Geokit::Geocoders::AirplaneGeocoder.geocode('New York, MA').should_not be_valid
14
+ Geokit::Geocoders::AirplaneGeocoder.geocode('Boston, CA').should_not be_valid
15
+ Geokit::Geocoders::AirplaneGeocoder.geocode('Total Junk').should_not be_valid
16
+ end
17
+
18
+ it "should return a LatLng when given a valid state" do
19
+ loc = Geokit::Geocoders::AirplaneGeocoder.geocode('MA')
20
+ loc.class.should == LatLng
21
+ loc.should be_valid
22
+ end
23
+
24
+ it "should return a LatLng for each state" do
25
+ CSV.foreach(File.join(File.dirname(__FILE__), "../../db/state_latlon.csv"), :headers => true) do |row|
26
+ state = row[0]
27
+ lat = row[1]
28
+ lng = row[2]
29
+ Geokit::Geocoders::AirplaneGeocoder.geocode(state).should == LatLng.new(lat, lng)
30
+ end
31
+ end
32
+
33
+ it "should return invalid LatLng for invalid state or long state name" do
34
+ Geokit::Geocoders::AirplaneGeocoder.geocode('New York').should_not be_valid
35
+ Geokit::Geocoders::AirplaneGeocoder.geocode('XX').should_not be_valid
36
+ end
37
+
38
+ it "should return a LatLng for a zip code" do
39
+ loc = Geokit::Geocoders::AirplaneGeocoder.geocode("90210")
40
+ loc.class.should == LatLng
41
+ loc.should be_valid
42
+ end
43
+
44
+ it "should return an invalid LatLng for invalid zipcode" do
45
+ Geokit::Geocoders::AirplaneGeocoder.geocode("99999").should_not be_valid
46
+ Geokit::Geocoders::AirplaneGeocoder.geocode("90210-1234").should_not be_valid
47
+ Geokit::Geocoders::AirplaneGeocoder.geocode("9021").should_not be_valid
48
+ end
49
+
50
+ it "should return a LatLng for a phone number" do
51
+ Geokit::Geocoders::AirplaneGeocoder.geocode("9785551212").should be_valid
52
+ end
53
+
54
+ it "should return an invalid LatLng for phone number with bad area code" do
55
+ Geokit::Geocoders::AirplaneGeocoder.geocode("9115551212").should_not be_valid
56
+ end
57
+
58
+ context "sanitize_phone" do
59
+ it "should sanitize phone numbers to be 10 digits" do
60
+ Geokit::Geocoders::AirplaneGeocoder.sanitize_phone('9785551212').should == '9785551212'
61
+ Geokit::Geocoders::AirplaneGeocoder.sanitize_phone('978-555-1212').should == '9785551212'
62
+ Geokit::Geocoders::AirplaneGeocoder.sanitize_phone('978.555.1212').should == '9785551212'
63
+ Geokit::Geocoders::AirplaneGeocoder.sanitize_phone('(978)555-1212').should == '9785551212'
64
+ Geokit::Geocoders::AirplaneGeocoder.sanitize_phone('(978) 555-1212').should == '9785551212'
65
+ Geokit::Geocoders::AirplaneGeocoder.sanitize_phone('+1 978-555-1212').should == '9785551212'
66
+ end
67
+ end
68
+
69
+ context "us_phone_number?" do
70
+ it "should return true if the argument looks like a phone number" do
71
+ Geokit::Geocoders::AirplaneGeocoder.us_phone_number?('9785551212').should be_true
72
+ Geokit::Geocoders::AirplaneGeocoder.us_phone_number?('978-555-1212').should be_true
73
+ Geokit::Geocoders::AirplaneGeocoder.us_phone_number?('978.555.1212').should be_true
74
+ Geokit::Geocoders::AirplaneGeocoder.us_phone_number?('(978)555-1212').should be_true
75
+ Geokit::Geocoders::AirplaneGeocoder.us_phone_number?('(978) 555-1212').should be_true
76
+ Geokit::Geocoders::AirplaneGeocoder.us_phone_number?('+1 978-555-1212').should be_true
77
+
78
+ end
79
+
80
+ it "should return false if the argument does not look like a phone number" do
81
+ Geokit::Geocoders::AirplaneGeocoder.us_phone_number?('978555121').should be_false
82
+ Geokit::Geocoders::AirplaneGeocoder.us_phone_number?('words').should be_false
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end