gtfs 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.
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ ### GTFS Ruby
2
+
3
+ A Ruby wrapper for the General Transit Feed Specification
4
+
5
+ ### Getting started
6
+
7
+ Initialize a new GTFS source:
8
+
9
+ source = GTFS::Source.build(<URI or Path to GTFS zip file>)
10
+
11
+ Accessing GTFS data from the source:
12
+
13
+ source.agencies
14
+ source.stops
15
+ source.routes
16
+ source.trips
17
+ source.stop_times
18
+ source.calendar
19
+ source.calendar_dates
20
+ source.fare_attributes
21
+ source.fare_rules
22
+ source.shapes
23
+ source.frequencies
24
+ source.transfers
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require 'rubygems'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.pattern = "./spec/**/*_spec.rb"
7
+ end
@@ -0,0 +1,15 @@
1
+ module GTFS
2
+ class Agency
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :name, :url, :timezone
6
+ has_optional_attrs :id, :lang, :phone, :fare_url
7
+ attr_accessor *attrs
8
+
9
+ column_prefix :agency_
10
+
11
+ def self.parse_agencies(data)
12
+ return parse_models(data)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module GTFS
2
+ class Calendar
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :service_id, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday, :start_date, :end_date
6
+ attr_accessor *attrs
7
+
8
+ def self.parse_calendars(data)
9
+ return parse_models(data)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module GTFS
2
+ class CalendarDate
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :service_id, :date, :exception_type
6
+ attr_accessor *attrs
7
+
8
+ def self.parse_calendar_dates(data)
9
+ return parse_models(data)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ module GTFS
2
+ class InvalidSourceException < Exception
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ module GTFS
2
+ class LocalSource < Source
3
+
4
+ def load_archive(source_path)
5
+ extract_to_cache(source_path)
6
+ rescue Exception => e
7
+ raise InvalidSourceException.new(e.message)
8
+ end
9
+ end
10
+ end
data/lib/gtfs/model.rb ADDED
@@ -0,0 +1,77 @@
1
+ require 'CSV'
2
+
3
+ module GTFS
4
+ module Model
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+
8
+ base.class_variable_set('@@prefix', '')
9
+ base.class_variable_set('@@optional_attrs', [])
10
+ base.class_variable_set('@@required_attrs', [])
11
+
12
+ def valid?
13
+ !self.class.required_attrs.any?{|f| self.send(f.to_sym).nil?}
14
+ end
15
+
16
+ def initialize(attrs)
17
+ attrs.each do |key, val|
18
+ instance_variable_set("@#{key}", val)
19
+ end
20
+ end
21
+ end
22
+
23
+ module ClassMethods
24
+
25
+ #####################################
26
+ # Getters for class variables
27
+ #####################################
28
+
29
+ def prefix
30
+ self.class_variable_get('@@prefix')
31
+ end
32
+
33
+ def optional_attrs
34
+ self.class_variable_get('@@optional_attrs')
35
+ end
36
+
37
+ def required_attrs
38
+ self.class_variable_get('@@required_attrs')
39
+ end
40
+
41
+ def attrs
42
+ required_attrs + optional_attrs
43
+ end
44
+
45
+ #####################################
46
+ # Helper methods for setting up class variables
47
+ #####################################
48
+
49
+ def has_required_attrs(*attrs)
50
+ self.class_variable_set('@@required_attrs', attrs)
51
+ end
52
+
53
+ def has_optional_attrs(*attrs)
54
+ self.class_variable_set('@@optional_attrs', attrs)
55
+ end
56
+
57
+ def column_prefix(prefix)
58
+ self.class_variable_set('@@prefix', prefix)
59
+ end
60
+
61
+ def parse_models(data)
62
+ return [] if data.nil? || data.empty?
63
+
64
+ models = []
65
+ CSV.parse(data, :headers => true) do |row|
66
+ attr_hash = {}
67
+ row.to_hash.each do |key, val|
68
+ attr_hash[key.gsub(/^#{prefix}/, '')] = val
69
+ end
70
+ model = self.new(attr_hash)
71
+ models << model if model.valid?
72
+ end
73
+ models
74
+ end
75
+ end
76
+ end
77
+ end
data/lib/gtfs/route.rb ADDED
@@ -0,0 +1,15 @@
1
+ module GTFS
2
+ class Route
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :id, :short_name, :long_name, :type
6
+ has_optional_attrs :agency_id, :desc, :url, :color, :text_color
7
+ attr_accessor *attrs
8
+
9
+ column_prefix :route_
10
+
11
+ def self.parse_routes(data)
12
+ return parse_models(data)
13
+ end
14
+ end
15
+ end
data/lib/gtfs/shape.rb ADDED
@@ -0,0 +1,15 @@
1
+ module GTFS
2
+ class Shape
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :id, :pt_lat, :pt_lon, :pt_sequence
6
+ has_optional_attrs :dist_traveled
7
+ attr_accessor *attrs
8
+
9
+ column_prefix :shape_
10
+
11
+ def self.parse_shapes(data)
12
+ return parse_models(data)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,114 @@
1
+ require 'tmpdir'
2
+ require 'fileutils'
3
+ require 'zip/zip'
4
+
5
+ module GTFS
6
+ class Source
7
+
8
+ REQUIRED_SOURCE_FILES = %w{agency.txt stops.txt routes.txt trips.txt stop_times.txt calendar.txt}
9
+ OPTIONAL_SOURCE_FILES = %w{calendar_dates.txt fare_attributes.txt fare_rules.txt shapes.txt frequencies.txt transfers.txt}
10
+ SOURCE_FILES = REQUIRED_SOURCE_FILES + OPTIONAL_SOURCE_FILES
11
+
12
+ attr_accessor :source, :archive
13
+
14
+ def initialize(source)
15
+ raise 'Source cannot be nil' if source.nil?
16
+
17
+ @tmp_dir = Dir.mktmpdir
18
+ ObjectSpace.define_finalizer(self, self.class.finalize(@tmp_dir))
19
+
20
+ @source = source
21
+ load_archive(@source)
22
+
23
+ raise InvalidSourceException.new('Missing required source files') if missing_sources?
24
+ end
25
+
26
+ def self.finalize(directory)
27
+ proc {FileUtils.rm_rf(directory)}
28
+ end
29
+
30
+ def extract_to_cache(source_path)
31
+ Zip::ZipFile.open(source_path) do |zip|
32
+ zip.entries.each do |entry|
33
+ zip.extract(entry.name, File.join(@tmp_dir, '/', entry.name))
34
+ end
35
+ end
36
+ end
37
+
38
+ def load_archive(source)
39
+ raise 'Cannot directly instantiate base GTFS::Source'
40
+ end
41
+
42
+ def self.build(data_root)
43
+ if data_root.match(/http|www/)
44
+ URLSource.new(data_root)
45
+ else
46
+ LocalSource.new(data_root)
47
+ end
48
+ end
49
+
50
+ def entries
51
+ Dir.entries(@tmp_dir)
52
+ end
53
+
54
+ def missing_sources?
55
+ return true if @source.nil?
56
+
57
+ REQUIRED_SOURCE_FILES.each do |rf|
58
+ return true if !entries.include?(rf)
59
+ end
60
+ false
61
+ end
62
+
63
+ def agencies
64
+ open(File.join(@tmp_dir, '/', 'agency.txt')) do |f|
65
+ @agencies ||= Agency.parse_agencies(f.read)
66
+ end
67
+ @agencies
68
+ end
69
+
70
+ def stops
71
+ open(File.join(@tmp_dir, '/', 'stops.txt')) do |f|
72
+ @stops ||= Stop.parse_stops(f.read)
73
+ end
74
+ @stops
75
+ end
76
+
77
+ def calendars
78
+ open(File.join(@tmp_dir, '/', 'calendar.txt')) do |f|
79
+ @calendars ||= Calendar.parse_calendars(f.read)
80
+ end
81
+ @calendars
82
+ end
83
+
84
+ def routes
85
+ open(File.join(@tmp_dir, '/', 'routes.txt')) do |f|
86
+ @routes ||= Route.parse_routes(f.read)
87
+ end
88
+ @routes
89
+ end
90
+
91
+ # TODO: huge, isn't practical to parse all at once
92
+ def shapes
93
+ open(File.join(@tmp_dir, '/', 'shapes.txt')) do |f|
94
+ @shapes ||= Shape.parse_shapes(f.read)
95
+ end
96
+ @shapes
97
+ end
98
+
99
+ def trips
100
+ open(File.join(@tmp_dir, '/', 'trips.txt')) do |f|
101
+ @trips ||= Trip.parse_trips(f.read)
102
+ end
103
+ @trips
104
+ end
105
+
106
+ # TODO: huge, isn't practical to parse all at once
107
+ def stop_times
108
+ open(File.join(@tmp_dir, '/', 'stop_times.txt')) do |f|
109
+ @stop_times ||= StopTime.parse_stop_times(f.read)
110
+ end
111
+ @stop_times
112
+ end
113
+ end
114
+ end
data/lib/gtfs/stop.rb ADDED
@@ -0,0 +1,18 @@
1
+ module GTFS
2
+ class Stop
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :id, :name, :lat, :lon
6
+ has_optional_attrs :code, :desc, :zone_id, :url, :location_type, :parent_station, :timezone
7
+ column_prefix :stop_
8
+ attr_accessor *attrs
9
+
10
+ LOCATION_TYPE_STOP = 0
11
+ LOCATION_TYPE_STATION = 1
12
+
13
+ def self.parse_stops(data)
14
+ return parse_models(data)
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,13 @@
1
+ module GTFS
2
+ class StopTime
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :trip_id, :arrival_time, :departure_time, :stop_id, :stop_sequence
6
+ has_optional_attrs :stop_headsign, :pickup_type, :drop_off_type, :shape_dist_traveled
7
+ attr_accessor *attrs
8
+
9
+ def self.parse_stop_times(data)
10
+ return parse_models(data)
11
+ end
12
+ end
13
+ end
data/lib/gtfs/trip.rb ADDED
@@ -0,0 +1,15 @@
1
+ module GTFS
2
+ class Trip
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :route_id, :service_id, :id
6
+ has_optional_attrs :headsign, :short_name, :direction_id, :block_id, :shape_id
7
+ attr_accessor *attrs
8
+
9
+ column_prefix :trip_
10
+
11
+ def self.parse_trips(data)
12
+ return parse_models(data)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ require 'net/http'
2
+ require 'URI'
3
+
4
+ module GTFS
5
+ class URLSource < Source
6
+
7
+ def load_archive(source_url)
8
+ Dir.mktmpdir do |tmp|
9
+ file_name = File.join(tmp, "/gtfs_temp_#{Time.now}.zip")
10
+ uri = URI.parse(source_url)
11
+ response = Net::HTTP.get_response(uri)
12
+ open(file_name, 'wb') do |file|
13
+ file.write response.body
14
+ end
15
+ extract_to_cache(file_name)
16
+ end
17
+ rescue Exception => e
18
+ raise InvalidSourceException.new(e.message)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ module GTFS
2
+ VERSION = '0.1.0'
3
+ end
4
+
data/lib/gtfs.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'gtfs/version'
2
+ require 'gtfs/model'
3
+ require 'gtfs/agency'
4
+ require 'gtfs/calendar'
5
+ require 'gtfs/calendar_date'
6
+ require 'gtfs/source'
7
+ require 'gtfs/url_source'
8
+ require 'gtfs/local_source'
9
+ require 'gtfs/route'
10
+ require 'gtfs/shape'
11
+ require 'gtfs/stop'
12
+ require 'gtfs/stop_time'
13
+ require 'gtfs/trip'
14
+ require 'gtfs/custom_exceptions'
@@ -0,0 +1,70 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: http://www.edschmalzle.com:80/gtfs.zip
6
+ body: !!null
7
+ headers: !!null
8
+ response: !ruby/struct:VCR::Response
9
+ status: !ruby/struct:VCR::ResponseStatus
10
+ code: 404
11
+ message: Not Found
12
+ headers:
13
+ date:
14
+ - Wed, 18 Jan 2012 05:04:53 GMT
15
+ server:
16
+ - Apache
17
+ vary:
18
+ - Cookie,Accept-Encoding
19
+ x-pingback:
20
+ - http://www.edschmalzle.com/xmlrpc.php
21
+ expires:
22
+ - Wed, 11 Jan 1984 05:00:00 GMT
23
+ cache-control:
24
+ - no-cache, must-revalidate, max-age=0
25
+ pragma:
26
+ - no-cache
27
+ last-modified:
28
+ - Wed, 18 Jan 2012 05:04:54 GMT
29
+ transfer-encoding:
30
+ - chunked
31
+ content-type:
32
+ - text/html; charset=UTF-8
33
+ body: ! "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html
34
+ xmlns=\"http://www.w3.org/1999/xhtml\" dir=\"ltr\" lang=\"en-US\">\r\n<head
35
+ profile=\"http://gmpg.org/xfn/11\">\r\n<meta http-equiv=\"Content-Type\" content=\"text/html;
36
+ charset=UTF-8\" />\r\n<title>Ed Schmalzle &raquo; Page not found</title>\r\n<link
37
+ rel=\"stylesheet\" href=\"http://www.edschmalzle.com/wp-content/themes/shallowgrunge/shallowgrunge/style.css\"
38
+ type=\"text/css\" media=\"screen\" />\r\n<link rel=\"alternate\" type=\"application/rss+xml\"
39
+ title=\"Ed Schmalzle RSS Feed\" href=\"http://www.edschmalzle.com/feed/\" />\r\n<link
40
+ rel=\"pingback\" href=\"http://www.edschmalzle.com/xmlrpc.php\" />\r\n<script
41
+ type='text/javascript' src='http://www.edschmalzle.com/wp-includes/js/jquery/jquery.js?ver=1.7.1'></script>\n<script
42
+ type='text/javascript' src='http://www.edschmalzle.com/wp-content/plugins/google-analyticator/external-tracking.min.js?ver=6.2'></script>\n<link
43
+ rel=\"EditURI\" type=\"application/rsd+xml\" title=\"RSD\" href=\"http://www.edschmalzle.com/xmlrpc.php?rsd\"
44
+ />\n<link rel=\"wlwmanifest\" type=\"application/wlwmanifest+xml\" href=\"http://www.edschmalzle.com/wp-includes/wlwmanifest.xml\"
45
+ /> \n<meta name=\"generator\" content=\"WordPress 3.3.1\" />\n<!-- Google Analytics
46
+ Tracking by Google Analyticator 6.2: http://ronaldheft.com/code/analyticator/
47
+ -->\n<script type=\"text/javascript\">\n\tvar analyticsFileTypes = [''];\n\tvar
48
+ analyticsEventTracking = 'enabled';\n</script>\n<script type=\"text/javascript\">\n\tvar
49
+ _gaq = _gaq || [];\n\t_gaq.push(['_setAccount', 'UA-378745-4']);\n\t_gaq.push(['_trackPageview']);\n\t_gaq.push(['_trackPageLoadTime']);\n\n\t(function()
50
+ {\n\t\tvar ga = document.createElement('script'); ga.type = 'text/javascript';
51
+ ga.async = true;\n\t\tga.src = ('https:' == document.location.protocol ? 'https://ssl'
52
+ : 'http://www') + '.google-analytics.com/ga.js';\n\t\tvar s = document.getElementsByTagName('script')[0];
53
+ s.parentNode.insertBefore(ga, s);\n\t})();\n</script>\n</head>\r\n<body>\r\n<div
54
+ id=\"wrapper\">\r\n<div id=\"wrapper2\">\r\n\r\n<div id=\"header\">\r\n\t<div
55
+ id=\"logo\">\r\n\t\t<h1><a href=\"http://www.edschmalzle.com/\">Ed Schmalzle</a></h1>\r\n\t\t<p></p>\r\n\t</div>\r\n</div>\r\n<!--
56
+ end #header -->\r\n<div id=\"menu\">\r\n\t<ul>\r\n\t\t<li class=\"page_item\"><a
57
+ href=\"http://www.edschmalzle.com/\">Blog</a></li>\r\n\t\t<li class=\"page_item
58
+ page-item-60\"><a href=\"http://www.edschmalzle.com/projects/\">Projects</a></li>\n<li
59
+ class=\"page_item page-item-5\"><a href=\"http://www.edschmalzle.com/resume/\">Resume</a></li>\n<li
60
+ class=\"page_item page-item-6\"><a href=\"http://www.edschmalzle.com/contact/\">Contact</a></li>\n<li
61
+ class=\"page_item page-item-75\"><a href=\"http://www.edschmalzle.com/archives/\">Archives</a></li>\n\t\t<li
62
+ class=\"page_item\"><a href=\"http://www.edschmalzle.com/feed/\">RSS</a></li>\r\n\t</ul>\r\n</div>\r\n<!--
63
+ end #menu -->\r\n</div>\r\n<hr />\r\n\r\n<div id=\"page\">\r\n\t<div id=\"content\"
64
+ class=\"narrowcolumn\">\r\n\r\n\t\t<h2 class=\"center\">Error 404 - Not Found</h2>\r\n\r\n\t</div>\r\n\r\n<div
65
+ id=\"sidebar\">\r\n\t<ul>\r\n\t</ul>\r\n</div>\r\n<!-- end #sidebar -->\r\n\r\n<div
66
+ style=\"clear: both;\">&nbsp;</div>\r\n</div>\r\n<!-- end #page -->\r\n<hr />\r\n</div>\r\n</div>\r\n<!--
67
+ end #footer -->\r\n<div id=\"footer\">\r\n\t<div>\r\n\t\t<p>Copyright (c) 2008\r\n\t\t\tEd
68
+ Schmalzle\t\t\tAll rights reserved. Design by <a href=\"http://www.freewpthemes.net/\">Free
69
+ WordPress Themes</a> &nbsp;&bull;&nbsp;\r\n\t\t\tPowered by <a href=\"http://wordpress.org/\">WordPress</a></p>\r\n\t</div>\r\n</div>\r\n</body></html>"
70
+ http_version: '1.1'
@@ -0,0 +1,127 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: http://dl.dropbox.com:80/u/416235/work/valid_gtfs.zip
6
+ body: !!null
7
+ headers: !!null
8
+ response: !ruby/struct:VCR::Response
9
+ status: !ruby/struct:VCR::ResponseStatus
10
+ code: 200
11
+ message: OK
12
+ headers:
13
+ server:
14
+ - nginx/1.0.14
15
+ date:
16
+ - Fri, 13 Apr 2012 21:52:30 GMT
17
+ content-type:
18
+ - application/zip
19
+ connection:
20
+ - keep-alive
21
+ content-length:
22
+ - '4147'
23
+ x-robots-tag:
24
+ - noindex,nofollow
25
+ accept-ranges:
26
+ - bytes
27
+ etag:
28
+ - 1380046134n
29
+ pragma:
30
+ - public
31
+ cache-control:
32
+ - max-age=0
33
+ body: !binary |-
34
+ UEsDBBQAAAAIALxiOECgcHeOgAAAAKoAAAAKABwAYWdlbmN5LnR4dFVUCQAD
35
+ tOgeT5idiE91eAsAAQT1AQAABBQAAAA9ykEOgjAQQNE9CXfgAIWWKAvdcQBc
36
+ uXFFJm0DE5kpKWMaPL0kWncv+R8mz3Yf0Sn4ioF89isumYLk34H/aQGestf5
37
+ CGXRqgHifgRX3SPwhlL1jpBxkwiCgdUssl61Tik1JEC/u7GBVE8+ogV982l8
38
+ hPhUntW5NXV3utSdMaYsPlBLAwQUAAAACADuYjhAhaz4EGQAAADCAAAADAAc
39
+ AGNhbGVuZGFyLnR4dFVUCQADEOkeT5idiE91eAsAAQT1AQAABBQAAABti0EK
40
+ gDAMBO8Ff9KDVSj4GikmYg9WSFPF32vSgx7KBnZgJxnpjAvOEex+JAi35YJZ
41
+ +kJIlXgrpLBSlMqBCykU/cgciGcIjBYTKHTG2S/9m6F3clMF73xnBh1qXFsZ
42
+ f4pIDeUBUEsDBBQAAAAIAGNtOEDmjKJpNQAAADwAAAASABwAY2FsZW5kYXJf
43
+ ZGF0ZXMudHh0VVQJAAO6+x5PmJ2IT3V4CwABBPUBAAAEFAAAACtOLSrLTE6N
44
+ z0zRSUksSdVJrUhOLSjJzM+LL6ksSOXlMtYxMjA0MjA1stAx5OUyRPCMeLkA
45
+ UEsDBBQAAAAIAG12JkCHn2pHkQYAAJATAAAKABwAcm91dGVzLnR4dFVUCQAD
46
+ vlAHT5idiE91eAsAAQT1AQAABBQAAACNmFtv4zgShd8XmP/Ap36iY1EXXx5p
47
+ iba4oUUNKfmyL4NBTzCzQG9nkekBdv79nqJlR07MxGkYcBgdkaz66rDYL89/
48
+ /Xj65d+/8V9/f/r+9W/69hKG/vzj+eXHL99//c/TMPDt+fvv499/e/rz6/D1
49
+ x9//PY/+9fJt+Pb1+dvzy/mBp//9OA389I9CLBacJ4ngXjdSswlbW9ex7dda
50
+ Ne7IeUZ/TJL1mq/DT1AsaTDjvlZtK13FWie7blo6u90rYyaV3TcdPrfVy4QG
51
+ c971rlHOM9/JTtsGM5f4t8JHea8OEbGgwYJvbVPJ/VYHmapso2VEkNLg/Fog
52
+ sbzY6sLggpu+q5XbaWMUFP22KyPP5zS4HD8/0U2nXBN2JQ0rtSuNiqgLDIqE
53
+ u+6B5cnUWWN0s8GEq96YRpWPzFUPEemMpIIj0N6GcKvL1hhS4ZHPTUQ6J2nK
54
+ fYdsSbaVTntoGyS+ZnIXWytxIjJ+mmK6htaz1mKz0O6lWTlrHyNSAkbknMI4
55
+ bWUnW19aqGTTyNYa7W/K0oRIEQX3quyd7o5YqTGQ2Z1yRslpq5w7shqDET3B
56
+ ImZXuQ+rNMeG1XarYvMSM2LOLysdEJ0A9se9PII4ynBETINiwTdGhfmk9OwL
57
+ 66TZ9pUu5WmpC6gWyVhFGIklp5nU8H6oN7YvgdW0lKiV29WYJsRQmrxGyf/c
58
+ S6dOwcI7WNVj++Z2atKEMEoFv07nJWIREQGUpnwljzut9ncIiJ00A+d5gmi8
59
+ cr62B+Z0tbnNXJoQOGnO97X2yDZp9tZWPszo5C5S9akgclJk0Mh/yZHDtORt
60
+ pd3hRTEHSAVRky45BZ7wQklf3uDrvusixZwKwiZLRlFRFcUF5cnIGGRsl4IG
61
+ M0THbq4N8eM9EjJZQbHpFGrY16z94oJZrUo2ZSuEuGL43DaQVBA32YyHsodf
62
+ sfZxf0RyjtY94i1O78jP1D6iJm6yxUnNVqqpJqU1FfNtyNIXtnGyqS76d8AL
63
+ Qig78POhgcIkDDCxbnAsoKrdyjqmDm3Qr9e0grGeiMqvqH/Apre6quDYYfEP
64
+ g5K0vMyTRTYPSmIqzxFtr1AXikKEvWPmq3dto7aSElz5jA/nDrmCrLRX7Ode
65
+ l4+r3t+eNyWu8jk/W+VksLHPZERVvhh8HqvsEFivgXHnIgpadAEOlZEaIcTa
66
+ PkM3JZYK8YbAyVsDjIgJpCLlW23QO1TIAdLpqsmr7ZI8oiWMiowTOqXtqfNQ
67
+ nbMjbfg9IiaGCmRSksEZT4fg5Rz7wlrVxFZM8BQFv7gPhTXEN/I8IVPM4OnH
68
+ pgpJsHtQ7sNW/QeoZIRKMefvyJp6L6nILvGOFGlG0BQL7tqRLYSKZ6HkIypi
69
+ pljyd6ukKduPs5kFeg5nusnF+k7uEU8Pzju7kVSUDj3a7cLMCKVZcr3iLfqb
70
+ vfT1dOOUak4eEZmeYJoJflacnJo6GziCUR49w9BvRvQE1Cznl2aGpOAKHQ58
71
+ GQ6h4C6bIwo+cpxmxMZ8PiIS26CjY1QLESVRsjw30rX1rcaxD+mH/XCaEyPL
72
+ cYd6XaqLa+PMiQj0YrXctpX67GECAd3X62bQkew1gy/QURhZEAYFcjpUnji9
73
+ 9yrJeUGP5Dfc+5+2bjw23z7qJkIIrBPiAz93Ac7KahSlj/GCgXIBBx7VwKn9
74
+ 6Ldomqi36DsDRiJqJFfAGnFU4ZSEd4+Y7h4imiVpDvy12ww0lNRXgObu4/Vi
75
+ Mi5mydsOZhruOJNzoAItkRcg/mJ5OHeGnXsIbT71g9OhPcRRv4uflHBmTq20
76
+ 0Zu6Y05qeFW4gtED4+cyek7wjdTUD/hV7zYDLjicGE6REy9yVsp1ErBJkiDM
77
+ SZi+FZ7ukUpWKiIDQxnWtXJ94/c4AsM14lzxeCXt5Q138GBoBCdrv+fxOT2e
78
+ 4sqyrdRdAvCRiVc+QgNvOo1GLLYL0JGBRyN7p8znzwMFntMMde/K+nS//Ewi
79
+ SCJ4jRhZINjZ99i8F2Ewx7pqucNdoFLUj5VUoOPSfK9CKmf54co90Utp5SR6
80
+ PFnWH8M+Q3qWSM8QjPY6pawqI7POSUb3UWYU7syuulO3IB1upHQ02A7lMVTn
81
+ PdolafNL/0p7u0M2T0hWvJlyip6usm599YbICwS9YI4QMVxOwslyz7QpqXAn
82
+ kUajrqjrvEeFbC7R5o9ovkdFSUTX+Uh+qj2Z6ZvKuamiHNKtCXM1989FGYRF
83
+ DnPdKaLU4ZytJTVPQ9PnLAzk1FhE2F5Q6paIhsEt3p//Z+fjff0fUEsDBBQA
84
+ AAAIALmMjUD/uKgtsQAAAJcBAAAKABwAc2hhcGVzLnR4dFVUCQADrZyIT5id
85
+ iE91eAsAAQT1AQAABBQAAABdz1lqw0AMAND/QG6iGC0jyTqNMfVADCFNa7fn
86
+ r6d4knj0JYmHluU6PvIwT7D8J491uI3rW/F5fxVL/vrJ94+8d6Z5WYf1e/zN
87
+ tzydTyaaGCQ6UfRguLh1ZkjOQIAdbtGg9ESKCFwQ99YgiorcHKQgJT8ic9wR
88
+ RQpIGyKVaCZJncRIPWhBJtQg9opSENiGGFODeraKjAm8IIrmu+DnOneBviCx
89
+ 4+GEqhWVw6MgdT6f/gBQSwMEFAAAAAgAwoyNQPKLtVLGAAAA0gEAAA4AHABz
90
+ dG9wX3RpbWVzLnR4dFVUCQADvJyIT5idiE91eAsAAQT1AQAABBQAAABtztmK
91
+ wyAUgOH7wrzJobgdNXkZkXo6lW5WTaFvP8GkwoTgxY/6udQck4sBfM7x7W+u
92
+ xjtBoORznTIt01KfzbQWek30OK2rF/KhxN8HpHi6TsnVT5qP53nneT4vs3Lx
93
+ iVyIpbqa/ZtuFH4OnBlEZkGNUo2M9QpjgAOweWyQ0L1WWg5iVyH2oga5Y3AU
94
+ qhcNqB2j1y+1ogVcDDsiN/qfNKtsxQH0V1rNxUZy1asZmJ137XpbqxZgF8OP
95
+ UiuzkWroFVJIGL73/QFQSwMEFAAAAAgAyYyNQO5l2ficAQAAmAMAAAkAHABz
96
+ dG9wcy50eHRVVAkAA8mciE+YnYhPdXgLAAEE9QEAAAQUAAAAfZJLT4QwFIX3
97
+ Jv6HrlxVpbSUsuwMdSQiTDrjY1aTeWBiMgIRjNFfb0svjKiRDZee9ju959K0
98
+ Vb1+3uPGvnfVvnBVuXmBal80O1cdNi0UVYk/q7IYzr29HvCh2m3a56pctx91
99
+ gevNa1G266bt1k5PCPZDz8PTVTq50xk6QzOtVLaY6ySboafLBr1vB1Heq58b
100
+ Op1GFzTwhI/PQ37BuUeEwOY5PaF46kUUpzJLlDZHF0kmE3SdL+bJUqb9usOO
101
+ NVQClhAWAJcSQh2XWS47cicqvVex0qpjZZeTMXokO6wvSAhYxqPIYQWOBRV4
102
+ pdI0f8jz2By9SuUjWiqtUVXXqNmC2KkO/muH5bOQ+HDtQBAOcUSYcurhpbyV
103
+ Jj2LP1rZqE3Pg6aP8mD21PSxsEAIBnxmDByfeKYBNjKAOc2kjlXWJTN2GOs9
104
+ XdDh9iwUEDohNvUQf5+/iyCXN6la9Vc0EQDF/AYOElFOAOJbiPgDYrs0U4sB
105
+ 8c+Go09nw2lEeh8mAvAJTNY+GWHOUHw3z7Pl33jQ7EdPZj7j/RAFgQ6+AFBL
106
+ AwQUAAAACADNjI1AkY7neagAAADfAQAACQAcAHRyaXBzLnR4dFVUCQAD0pyI
107
+ T5idiE91eAsAAQT1AQAABBQAAACVz8ELgjAUx/F70P+wP+Ad9pybduxg1CGD
108
+ kKRT1Bw5CpVp/f3pqCB5CJ32YJ/v4efqR2dOtoDWuKfV/uycbb5vac5Fa68V
109
+ FNYZ3dm6Gr4u91rffFaemyGazyTGMSAgj6Tk/cVWGdvqdZLuj8Ah4IhCgBIy
110
+ VCO7IG3orfi1yEkrSYukVaQNSBuRVkxsC0Y2/GOb7O0bssNmydIkz3Z5+oli
111
+ H0WjSE2PfAFQSwECHgMUAAAACAC8YjhAoHB3joAAAACqAAAACgAYAAAAAAAB
112
+ AAAApIEAAAAAYWdlbmN5LnR4dFVUBQADtOgeT3V4CwABBPUBAAAEFAAAAFBL
113
+ AQIeAxQAAAAIAO5iOECFrPgQZAAAAMIAAAAMABgAAAAAAAEAAACkgcQAAABj
114
+ YWxlbmRhci50eHRVVAUAAxDpHk91eAsAAQT1AQAABBQAAABQSwECHgMUAAAA
115
+ CABjbThA5oyiaTUAAAA8AAAAEgAYAAAAAAABAAAApIFuAQAAY2FsZW5kYXJf
116
+ ZGF0ZXMudHh0VVQFAAO6+x5PdXgLAAEE9QEAAAQUAAAAUEsBAh4DFAAAAAgA
117
+ bXYmQIefakeRBgAAkBMAAAoAGAAAAAAAAQAAAKSB7wEAAHJvdXRlcy50eHRV
118
+ VAUAA75QB091eAsAAQT1AQAABBQAAABQSwECHgMUAAAACAC5jI1A/7ioLbEA
119
+ AACXAQAACgAYAAAAAAABAAAApIHECAAAc2hhcGVzLnR4dFVUBQADrZyIT3V4
120
+ CwABBPUBAAAEFAAAAFBLAQIeAxQAAAAIAMKMjUDyi7VSxgAAANIBAAAOABgA
121
+ AAAAAAEAAACkgbkJAABzdG9wX3RpbWVzLnR4dFVUBQADvJyIT3V4CwABBPUB
122
+ AAAEFAAAAFBLAQIeAxQAAAAIAMmMjUDuZdn4nAEAAJgDAAAJABgAAAAAAAEA
123
+ AACkgccKAABzdG9wcy50eHRVVAUAA8mciE91eAsAAQT1AQAABBQAAABQSwEC
124
+ HgMUAAAACADNjI1AkY7neagAAADfAQAACQAYAAAAAAABAAAApIGmDAAAdHJp
125
+ cHMudHh0VVQFAAPSnIhPdXgLAAEE9QEAAAQUAAAAUEsFBgAAAAAIAAgAjAIA
126
+ AJENAAAAAA==
127
+ http_version: '1.1'
Binary file
Binary file
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::Agency do
4
+ describe 'Agency.parse_agencies' do
5
+ let(:header_line) {"agency_id,agency_name,agency_url,agency_timezone,agency_lang,agency_phone\n"}
6
+ let(:invalid_header_line) {"agency_id,agency_url,agency_timezone,agency_lang,agency_phone\n"}
7
+ let(:valid_line) {"1,Maryland Transit Administration,http://www.mta.maryland.gov,America/New_York,en,410-539-5000\n"}
8
+ let(:invalid_line) {"1,,http://www.mta.maryland.gov,America/New_York,en,410-539-5000\n"}
9
+
10
+ subject {GTFS::Agency.parse_agencies(source_text)}
11
+
12
+ include_examples 'models'
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::CalendarDate do
4
+ describe 'CalendarDate.parse_calendar_dates' do
5
+ let(:header_line) {"service_id,date,exception_type\n"}
6
+ let(:invalid_header_line) {",date,\n"}
7
+ let(:valid_line) {"3,20110905,1\n"}
8
+ let(:invalid_line) {"3,,1\n"}
9
+
10
+ subject {GTFS::CalendarDate.parse_calendar_dates(source_text)}
11
+
12
+ include_examples 'models'
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::Calendar do
4
+ describe 'Calendar.parse_calendars' do
5
+ let(:header_line) {"service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date\n"}
6
+ let(:invalid_header_line) {"service_id,,tuesday,,thursday,friday,,sunday,start_date,end_date\n"}
7
+ let(:valid_line) {"1,1,1,1,1,1,0,0,20110828,20120204\n"}
8
+ let(:invalid_line) {"1,,1,,1,,0,,,20120204\n"}
9
+
10
+ subject {GTFS::Calendar.parse_calendars(source_text)}
11
+
12
+ include_examples 'models'
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::LocalSource do
4
+ describe '#new' do
5
+ subject {lambda{GTFS::LocalSource.new(source_path)}}
6
+
7
+ context 'with a local source path that is invalid' do
8
+ let(:source_path) {File.expand_path(File.dirname(__FILE__) + '/../fixtures/not_here.zip')}
9
+
10
+ it {should raise_error(GTFS::InvalidSourceException)}
11
+ end
12
+
13
+ context 'with a local source path to a zip w/o required files' do
14
+ let(:source_path) {File.expand_path(File.dirname(__FILE__) + '/../fixtures/missing_files_gtfs.zip')}
15
+
16
+ it {should raise_error(GTFS::InvalidSourceException)}
17
+ end
18
+
19
+ context 'with a local source path to a valid source zip' do
20
+ let(:source_path) {File.expand_path(File.dirname(__FILE__) + '/../fixtures/valid_gtfs.zip')}
21
+
22
+ it {should_not raise_error(GTFS::InvalidSourceException)}
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::Route do
4
+ describe 'Route.parse_routes' do
5
+ let(:header_line) {"route_id,agency_id,route_short_name,route_long_name,route_desc,route_type,route_url,route_color,route_text_color\n"}
6
+ let(:invalid_header_line) {",agency_id,,route_long_name,,route_type,,route_color,\n"}
7
+ let(:valid_line) {"4679,1,001,SINAI - FORT McHENRY,,3,,0000FF,FFFFFF\n"}
8
+ let(:invalid_line) {",1,,,,3,,,FFFFFF\n"}
9
+
10
+ subject {GTFS::Route.parse_routes(source_text)}
11
+
12
+ include_examples 'models'
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::Shape do
4
+ describe 'Shape.parse_shapes' do
5
+ let(:header_line) {"shape_id,shape_pt_lat,shape_pt_lon,shape_pt_sequence,shape_dist_traveled\n"}
6
+ let(:invalid_header_line) {",,,shape_pt_sequence,shape_dist_traveled\n"}
7
+ let(:valid_line) {"59135,39.354286,-76.662453,19,0.8136\n"}
8
+ let(:invalid_line) {",39.354286,,,0.8136\n"}
9
+
10
+ subject {GTFS::Shape.parse_shapes(source_text)}
11
+
12
+ include_examples 'models'
13
+ end
14
+ end
@@ -0,0 +1,65 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::Source do
4
+ let(:valid_local_source) {File.expand_path(File.dirname(__FILE__) + '/../fixtures/valid_gtfs.zip')}
5
+ let(:source_missing_required_files) {File.expand_path(File.dirname(__FILE__) + '/../fixtures/missing_files.zip')}
6
+
7
+ describe '#build' do
8
+ subject {GTFS::Source.build(data_source)}
9
+
10
+ before do
11
+ GTFS::URLSource.stub(:new).and_return('URLSource')
12
+ GTFS::LocalSource.stub(:new).and_return('LocalSource')
13
+ end
14
+
15
+ context 'with a url as a data root' do
16
+ let(:data_source) {'http://www.edschmalzle.com/gtfs.zip'}
17
+
18
+ it {should == 'URLSource'}
19
+ end
20
+
21
+ context 'with a file path as a data root' do
22
+ let(:data_source) {valid_local_source}
23
+
24
+ it {should == 'LocalSource'}
25
+ end
26
+ end
27
+
28
+ describe '#build' do
29
+ context 'when the source lacks a required file' do
30
+ it 'should raise an exception if the source is missing a file' do
31
+ lambda {GTFS::Source.build(source_missing_required_fields)}.should raise_exception
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#new(source)' do
37
+ it 'should not allow a base GTFS::Source to be initialized' do
38
+ lambda {GTFS::Source.new(valid_local_source)}.should raise_exception
39
+ end
40
+ end
41
+
42
+ describe '#agencies' do
43
+ subject {source.agencies}
44
+
45
+ context 'when the source has agencies' do
46
+ let(:source) {GTFS::Source.build(valid_local_source)}
47
+
48
+ it {should_not be_empty}
49
+ its(:first) {should be_an_instance_of(GTFS::Agency)}
50
+ end
51
+
52
+ end
53
+
54
+ describe '#stops' do
55
+ end
56
+
57
+ describe '#routes' do
58
+ end
59
+
60
+ describe '#trips' do
61
+ end
62
+
63
+ describe '#stop_times' do
64
+ end
65
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::Stop do
4
+ describe 'Trip.parse_stops' do
5
+ let(:header_line) {"stop_id,stop_code,stop_name,stop_lat,stop_lon,zone_id,stop_url,location_type,parent_station\n"}
6
+ let(:invalid_header_line) {"stop_lon, zone_id, stop_url, location_type\n"}
7
+ let(:valid_line) {"3,C093,LANIER & SINAI HOSPITAL,39.351145,-76.663113,,,,\n"}
8
+ let(:invalid_line) {"3,,,,-76.663113,,,,\n"}
9
+
10
+ subject {GTFS::Stop.parse_stops(source_text)}
11
+
12
+ include_examples 'models'
13
+ end
14
+ end
15
+
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::StopTime do
4
+ describe 'StopTime.parse_stop_times' do
5
+ let(:header_line) {"trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type,shape_dist_traveled\n"}
6
+ let(:invalid_header_line) {",arrival_time,,stop_id,,stop_headsign,,drop_off_type,\n"}
7
+ let(:valid_line) {"982385,4:34:00,4:34:00,277,1,,0,0,\n"}
8
+ let(:invalid_line) {",,4:34:00,,1,,,0,\n"}
9
+
10
+ subject {GTFS::StopTime.parse_stop_times(source_text)}
11
+
12
+ include_examples 'models'
13
+ end
14
+ end
15
+
@@ -0,0 +1,16 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::Trip do
4
+ describe 'Trip.parse_trips' do
5
+ let(:header_line) {"route_id,service_id,trip_id,trip_headsign,direction_id,block_id,shape_id\n"}
6
+ let(:invalid_header_line) {",,,,direction_id,block_id,shape_id\n"}
7
+ let(:valid_line) {"4679,1,982394,1 FT McHENRY,0,189021,59135\n"}
8
+ let(:invalid_line) {",1,,1 FT McHENRY,,189021,\n"}
9
+
10
+ subject {GTFS::Trip.parse_trips(source_text)}
11
+
12
+ include_examples 'models'
13
+ end
14
+ end
15
+
16
+
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe GTFS::URLSource do
4
+ context 'with a URI to a valid source zip' do
5
+ let(:source_path) {'http://dl.dropbox.com/u/416235/work/valid_gtfs.zip'}
6
+ it 'should create a new source successfully' do
7
+ VCR.use_cassette('valid_gtfs_uri') do
8
+ lambda {GTFS::URLSource.new(source_path)}.should_not raise_error(GTFS::InvalidSourceException)
9
+ end
10
+ end
11
+ end
12
+
13
+ context 'with a non-existent URI' do
14
+ let(:source_path) {'http://www.edschmalzle.com/gtfs.zip'}
15
+ it 'should raise an exception' do
16
+ VCR.use_cassette('invalid_gtfs_uri') do
17
+ lambda {GTFS::URLSource.new(source_path)}.should raise_error(GTFS::InvalidSourceException)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+
7
+ require 'bundler/setup'
8
+ require 'rspec'
9
+ require 'vcr'
10
+ require 'ruby-debug'
11
+ require 'gtfs'
12
+
13
+ require File.expand_path(File.dirname(__FILE__) + '/support/model_shared_examples')
14
+
15
+ RSpec.configure do |config|
16
+ # Configure you some RSpec
17
+ end
18
+
19
+ VCR.config do |c|
20
+ c.cassette_library_dir = File.join(File.dirname(__FILE__), '/fixtures/cassettes')
21
+ c.stub_with :fakeweb
22
+ end
@@ -0,0 +1,31 @@
1
+ shared_examples 'models' do |collection_class|
2
+ context 'with a nil source' do
3
+ let(:source_text) {nil}
4
+ it {should == []}
5
+ end
6
+
7
+ context 'with an empty source' do
8
+ let(:source_text) {''}
9
+ it {should == []}
10
+ end
11
+
12
+ context 'with a source w/ only headers' do
13
+ let(:source_text) {header_line}
14
+ it {should == []}
15
+ end
16
+
17
+ context 'with a valid row of data' do
18
+ let(:source_text) {header_line + valid_line}
19
+ its(:size) {should == 1}
20
+ end
21
+
22
+ context 'with multiple valid rows of data' do
23
+ let(:source_text) {header_line + valid_line + valid_line}
24
+ its(:size) {should == 2}
25
+ end
26
+
27
+ context 'with 1 invalid and 1 valid row of data' do
28
+ let(:source_text) {header_line + valid_line + invalid_line}
29
+ its(:size) {should == 1}
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,208 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gtfs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - nerdEd
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: multi_json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rubyzip
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 2.0.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 2.0.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: simplecov
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: ruby-debug19
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: vcr
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: fakeweb
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ description: gtfs reads GTFS data from a google-compliant Zip bundle and returns an
143
+ object representing the CSV data inside
144
+ email:
145
+ - nerdEd
146
+ executables: []
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - Rakefile
151
+ - lib/gtfs/agency.rb
152
+ - lib/gtfs/calendar.rb
153
+ - lib/gtfs/calendar_date.rb
154
+ - lib/gtfs/custom_exceptions.rb
155
+ - lib/gtfs/local_source.rb
156
+ - lib/gtfs/model.rb
157
+ - lib/gtfs/route.rb
158
+ - lib/gtfs/shape.rb
159
+ - lib/gtfs/source.rb
160
+ - lib/gtfs/stop.rb
161
+ - lib/gtfs/stop_time.rb
162
+ - lib/gtfs/trip.rb
163
+ - lib/gtfs/url_source.rb
164
+ - lib/gtfs/version.rb
165
+ - lib/gtfs.rb
166
+ - spec/fixtures/cassettes/invalid_gtfs_uri.yml
167
+ - spec/fixtures/cassettes/valid_gtfs_uri.yml
168
+ - spec/fixtures/missing_files.zip
169
+ - spec/fixtures/valid_gtfs.zip
170
+ - spec/gtfs/agency_spec.rb
171
+ - spec/gtfs/calendar_date_spec.rb
172
+ - spec/gtfs/calendar_spec.rb
173
+ - spec/gtfs/local_source_spec.rb
174
+ - spec/gtfs/route_spec.rb
175
+ - spec/gtfs/shape_spec.rb
176
+ - spec/gtfs/source_spec.rb
177
+ - spec/gtfs/stop_spec.rb
178
+ - spec/gtfs/stop_time_spec.rb
179
+ - spec/gtfs/trip_spec.rb
180
+ - spec/gtfs/url_source_spec.rb
181
+ - spec/spec_helper.rb
182
+ - spec/support/model_shared_examples.rb
183
+ - README.md
184
+ homepage: https://github.com/nerdEd/gtfs
185
+ licenses: []
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ none: false
192
+ requirements:
193
+ - - ! '>='
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
196
+ required_rubygems_version: !ruby/object:Gem::Requirement
197
+ none: false
198
+ requirements:
199
+ - - ! '>='
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ requirements: []
203
+ rubyforge_project:
204
+ rubygems_version: 1.8.24
205
+ signing_key:
206
+ specification_version: 3
207
+ summary: Load and read GTFS data from zip bundles
208
+ test_files: []