gtfs 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +24 -0
- data/Rakefile +7 -0
- data/lib/gtfs/agency.rb +15 -0
- data/lib/gtfs/calendar.rb +12 -0
- data/lib/gtfs/calendar_date.rb +12 -0
- data/lib/gtfs/custom_exceptions.rb +4 -0
- data/lib/gtfs/local_source.rb +10 -0
- data/lib/gtfs/model.rb +77 -0
- data/lib/gtfs/route.rb +15 -0
- data/lib/gtfs/shape.rb +15 -0
- data/lib/gtfs/source.rb +114 -0
- data/lib/gtfs/stop.rb +18 -0
- data/lib/gtfs/stop_time.rb +13 -0
- data/lib/gtfs/trip.rb +15 -0
- data/lib/gtfs/url_source.rb +22 -0
- data/lib/gtfs/version.rb +4 -0
- data/lib/gtfs.rb +14 -0
- data/spec/fixtures/cassettes/invalid_gtfs_uri.yml +70 -0
- data/spec/fixtures/cassettes/valid_gtfs_uri.yml +127 -0
- data/spec/fixtures/missing_files.zip +0 -0
- data/spec/fixtures/valid_gtfs.zip +0 -0
- data/spec/gtfs/agency_spec.rb +14 -0
- data/spec/gtfs/calendar_date_spec.rb +14 -0
- data/spec/gtfs/calendar_spec.rb +14 -0
- data/spec/gtfs/local_source_spec.rb +25 -0
- data/spec/gtfs/route_spec.rb +14 -0
- data/spec/gtfs/shape_spec.rb +14 -0
- data/spec/gtfs/source_spec.rb +65 -0
- data/spec/gtfs/stop_spec.rb +15 -0
- data/spec/gtfs/stop_time_spec.rb +15 -0
- data/spec/gtfs/trip_spec.rb +16 -0
- data/spec/gtfs/url_source_spec.rb +21 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/model_shared_examples.rb +31 -0
- metadata +208 -0
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
data/lib/gtfs/agency.rb
ADDED
@@ -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
|
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
|
data/lib/gtfs/source.rb
ADDED
@@ -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
|
data/lib/gtfs/version.rb
ADDED
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 » 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;\"> </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> • \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
|
+
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
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -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: []
|