gtfs 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ Session.vim
2
+ coverage
3
+ .rvmrc
4
+ *.gem
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rake'
4
+ gem 'rspec'
5
+ gem 'multi_json', '1.0.4'
6
+ gem 'simplecov', :require => false
7
+ gem 'rubyzip'
8
+ gem 'vcr'
9
+ gem 'fakeweb'
@@ -0,0 +1,33 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ fakeweb (1.3.0)
6
+ multi_json (1.0.4)
7
+ rake (0.9.2.2)
8
+ rspec (2.8.0)
9
+ rspec-core (~> 2.8.0)
10
+ rspec-expectations (~> 2.8.0)
11
+ rspec-mocks (~> 2.8.0)
12
+ rspec-core (2.8.0)
13
+ rspec-expectations (2.8.0)
14
+ diff-lcs (~> 1.1.2)
15
+ rspec-mocks (2.8.0)
16
+ rubyzip (0.9.5)
17
+ simplecov (0.6.1)
18
+ multi_json (~> 1.0)
19
+ simplecov-html (~> 0.5.3)
20
+ simplecov-html (0.5.3)
21
+ vcr (1.11.3)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ fakeweb
28
+ multi_json (= 1.0.4)
29
+ rake
30
+ rspec
31
+ rubyzip
32
+ simplecov
33
+ vcr
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ### GTFS Ruby
2
2
 
3
- A Ruby wrapper for the General Transit Feed Specification
3
+ A Ruby wrapper for the [General Transit Feed Specification](https://developers.google.com/transit/gtfs/)
4
4
 
5
5
  ### Getting started
6
6
 
@@ -15,10 +15,31 @@ Accessing GTFS data from the source:
15
15
  source.routes
16
16
  source.trips
17
17
  source.stop_times
18
- source.calendar
19
- source.calendar_dates
20
- source.fare_attributes
21
- source.fare_rules
18
+ source.calendars
19
+ source.calendar_dates
20
+ source.fare_attributes
21
+ source.fare_rules
22
22
  source.shapes
23
- source.frequencies
24
- source.transfers
23
+ source.frequencies
24
+ source.transfers
25
+
26
+ ### License
27
+
28
+ Copyright (C) 2012 Ed Schmalzle
29
+
30
+ Permission is hereby granted, free of charge, to any person obtaining a
31
+ copy of this software and associated documentation files (the "Software"), to
32
+ deal in the Software without restriction, including without limitation the
33
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34
+ copies of the Software, and to permit persons to whom the Software is furnished
35
+ to do so, subject to the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be included in
38
+ all copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
42
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
43
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
44
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
45
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,7 +1,6 @@
1
- #!/usr/bin/env rake
2
1
  require 'rubygems'
3
2
  require 'rspec/core/rake_task'
4
3
 
5
4
  RSpec::Core::RakeTask.new do |t|
6
- t.pattern = "./spec/**/*_spec.rb"
5
+ t.pattern = './spec/**/*_spec.rb'
7
6
  end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require './lib/gtfs/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'gtfs'
7
+ gem.version = GTFS::VERSION
8
+ gem.date = Date.today.to_s
9
+
10
+ gem.summary = 'Load and read GTFS data from zip bundles'
11
+ gem.description = 'gtfs reads GTFS data from a google-compliant Zip bundle
12
+ and returns an object representing the CSV data inside'
13
+
14
+ gem.authors = ['nerdEd']
15
+ gem.email = ['ed@nerded.net']
16
+ gem.homepage = 'https://github.com/nerdEd/gtfs'
17
+
18
+ gem.add_dependency 'rake'
19
+ gem.add_dependency 'multi_json'
20
+ gem.add_dependency 'rubyzip'
21
+
22
+ gem.add_development_dependency 'rspec', ['>= 2.0.0']
23
+ gem.add_development_dependency 'simplecov'
24
+ gem.add_development_dependency 'ruby-debug19'
25
+ gem.add_development_dependency 'vcr'
26
+ gem.add_development_dependency 'fakeweb'
27
+
28
+ gem.files = `git ls-files`.split("\n")
29
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
30
+
31
+ gem.require_paths = ['lib']
32
+ end
@@ -3,12 +3,17 @@ require 'gtfs/model'
3
3
  require 'gtfs/agency'
4
4
  require 'gtfs/calendar'
5
5
  require 'gtfs/calendar_date'
6
- require 'gtfs/source'
7
- require 'gtfs/url_source'
8
- require 'gtfs/local_source'
9
6
  require 'gtfs/route'
10
7
  require 'gtfs/shape'
11
8
  require 'gtfs/stop'
12
9
  require 'gtfs/stop_time'
13
10
  require 'gtfs/trip'
14
11
  require 'gtfs/custom_exceptions'
12
+ require 'gtfs/fare_attribute'
13
+ require 'gtfs/fare_rule'
14
+ require 'gtfs/frequency'
15
+ require 'gtfs/transfer'
16
+
17
+ require 'gtfs/source'
18
+ require 'gtfs/url_source'
19
+ require 'gtfs/local_source'
@@ -8,6 +8,10 @@ module GTFS
8
8
 
9
9
  column_prefix :agency_
10
10
 
11
+ collection_name :agencies
12
+ required_file true
13
+ uses_filename 'agency.txt'
14
+
11
15
  def self.parse_agencies(data)
12
16
  return parse_models(data)
13
17
  end
@@ -5,6 +5,10 @@ module GTFS
5
5
  has_required_attrs :service_id, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday, :start_date, :end_date
6
6
  attr_accessor *attrs
7
7
 
8
+ collection_name :calendars
9
+ required_file true
10
+ uses_filename 'calendars.txt'
11
+
8
12
  def self.parse_calendars(data)
9
13
  return parse_models(data)
10
14
  end
@@ -5,6 +5,10 @@ module GTFS
5
5
  has_required_attrs :service_id, :date, :exception_type
6
6
  attr_accessor *attrs
7
7
 
8
+ collection_name :calendar_dates
9
+ required_file true
10
+ uses_filename 'calendar_dates.txt'
11
+
8
12
  def self.parse_calendar_dates(data)
9
13
  return parse_models(data)
10
14
  end
@@ -0,0 +1,17 @@
1
+ module GTFS
2
+ class FareAttribute
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :fare_id, :price, :currency_type, :payment_method, :transfers
6
+ has_optional_attrs :transfer_duration
7
+ attr_accessor *attrs
8
+
9
+ collection_name :fare_attributes
10
+ required_file false
11
+ uses_filename 'fare_attributes.txt'
12
+
13
+ def self.parse_fare_attributes(data)
14
+ return parse_models(data)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module GTFS
2
+ class FareRule
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :fare_id
6
+ has_optional_attrs :route_id, :origin_id, :destination_id, :contains_id
7
+ attr_accessor *attrs
8
+
9
+ collection_name :fare_rules
10
+ required_file false
11
+ uses_filename 'fare_rules.txt'
12
+
13
+ def self.parse_fare_rules(data)
14
+ return parse_models(data)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module GTFS
2
+ class Frequency
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :trip_id, :start_time, :end_time, :headway_secs
6
+ has_optional_attrs :exact_times
7
+ attr_accessor *attrs
8
+
9
+ collection_name :frequencies
10
+ required_file false
11
+ uses_filename 'frequencies.txt'
12
+
13
+ def self.parse_frequencies(data)
14
+ return parse_models(data)
15
+ end
16
+ end
17
+ end
@@ -1,4 +1,4 @@
1
- require 'CSV'
1
+ require 'csv'
2
2
 
3
3
  module GTFS
4
4
  module Model
@@ -58,6 +58,18 @@ module GTFS
58
58
  self.class_variable_set('@@prefix', prefix)
59
59
  end
60
60
 
61
+ def required_file(required)
62
+ self.define_singleton_method(:required_file?) {required}
63
+ end
64
+
65
+ def collection_name(collection_name)
66
+ self.define_singleton_method(:name) {collection_name}
67
+ end
68
+
69
+ def uses_filename(filename)
70
+ self.define_singleton_method(:filename) {filename}
71
+ end
72
+
61
73
  def parse_models(data)
62
74
  return [] if data.nil? || data.empty?
63
75
 
@@ -8,6 +8,10 @@ module GTFS
8
8
 
9
9
  column_prefix :route_
10
10
 
11
+ collection_name :routes
12
+ required_file true
13
+ uses_filename 'routes.txt'
14
+
11
15
  def self.parse_routes(data)
12
16
  return parse_models(data)
13
17
  end
@@ -6,6 +6,10 @@ module GTFS
6
6
  has_optional_attrs :dist_traveled
7
7
  attr_accessor *attrs
8
8
 
9
+ collection_name :shapes
10
+ required_file false
11
+ uses_filename 'shapes.txt'
12
+
9
13
  column_prefix :shape_
10
14
 
11
15
  def self.parse_shapes(data)
@@ -5,9 +5,13 @@ require 'zip/zip'
5
5
  module GTFS
6
6
  class Source
7
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
8
+ ENTITIES = [GTFS::Agency, GTFS::Stop, GTFS::Route, GTFS::Trip, GTFS::StopTime,
9
+ GTFS::Calendar, GTFS::CalendarDate, GTFS::Shape, GTFS::FareAttribute,
10
+ GTFS::FareRule, GTFS::Frequency, GTFS::Transfer]
11
+
12
+ REQUIRED_SOURCE_FILES = ENTITIES.select(&:required_file?).map(&:filename)
13
+ OPTIONAL_SOURCE_FILES = ENTITIES.reject(&:required_file?).map(&:filename)
14
+ SOURCE_FILES = ENTITIES.map(&:filename)
11
15
 
12
16
  attr_accessor :source, :archive
13
17
 
@@ -19,8 +23,6 @@ module GTFS
19
23
 
20
24
  @source = source
21
25
  load_archive(@source)
22
-
23
- raise InvalidSourceException.new('Missing required source files') if missing_sources?
24
26
  end
25
27
 
26
28
  def self.finalize(directory)
@@ -40,10 +42,10 @@ module GTFS
40
42
  end
41
43
 
42
44
  def self.build(data_root)
43
- if data_root.match(/http|www/)
44
- URLSource.new(data_root)
45
- else
45
+ if File.exists?(data_root)
46
46
  LocalSource.new(data_root)
47
+ else
48
+ URLSource.new(data_root)
47
49
  end
48
50
  end
49
51
 
@@ -51,64 +53,28 @@ module GTFS
51
53
  Dir.entries(@tmp_dir)
52
54
  end
53
55
 
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
56
+ def raise_if_missing_source(filename)
57
+ file_missing = !entries.include?(filename)
58
+ raise InvalidSourceException.new("Missing required source file: #{filename}") if file_missing
82
59
  end
83
60
 
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)
61
+ ENTITIES.each do |entity|
62
+ define_method entity.name.to_sym do
63
+ parse_file entity.filename do |f|
64
+ entity.send("parse_#{entity.name}".to_sym, f.read)
65
+ end
95
66
  end
96
- @shapes
97
67
  end
98
68
 
99
- def trips
100
- open(File.join(@tmp_dir, '/', 'trips.txt')) do |f|
101
- @trips ||= Trip.parse_trips(f.read)
102
- end
103
- @trips
69
+ def files
70
+ @files ||= {}
104
71
  end
105
72
 
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)
73
+ def parse_file(filename)
74
+ raise_if_missing_source filename
75
+ open File.join(@tmp_dir, '/', filename) do |f|
76
+ files[filename] ||= yield f
110
77
  end
111
- @stop_times
112
78
  end
113
79
  end
114
80
  end
@@ -7,6 +7,10 @@ module GTFS
7
7
  column_prefix :stop_
8
8
  attr_accessor *attrs
9
9
 
10
+ collection_name :stops
11
+ required_file true
12
+ uses_filename 'stops.txt'
13
+
10
14
  LOCATION_TYPE_STOP = 0
11
15
  LOCATION_TYPE_STATION = 1
12
16
 
@@ -6,6 +6,10 @@ module GTFS
6
6
  has_optional_attrs :stop_headsign, :pickup_type, :drop_off_type, :shape_dist_traveled
7
7
  attr_accessor *attrs
8
8
 
9
+ collection_name :stop_times
10
+ required_file true
11
+ uses_filename 'stop_times.txt'
12
+
9
13
  def self.parse_stop_times(data)
10
14
  return parse_models(data)
11
15
  end
@@ -0,0 +1,19 @@
1
+ module GTFS
2
+ class Transfer
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :from_stop_id, :to_stop_id, :type
6
+ has_optional_attrs :min_transfer_time
7
+ attr_accessor *attrs
8
+
9
+ column_prefix :transfer_
10
+
11
+ collection_name :transfers
12
+ required_file false
13
+ uses_filename 'transfers.txt'
14
+
15
+ def self.parse_transfers(data)
16
+ return parse_models(data)
17
+ end
18
+ end
19
+ end
@@ -8,6 +8,10 @@ module GTFS
8
8
 
9
9
  column_prefix :trip_
10
10
 
11
+ collection_name :trips
12
+ required_file true
13
+ uses_filename 'trips.txt'
14
+
11
15
  def self.parse_trips(data)
12
16
  return parse_models(data)
13
17
  end
@@ -1,5 +1,5 @@
1
1
  require 'net/http'
2
- require 'URI'
2
+ require 'uri'
3
3
 
4
4
  module GTFS
5
5
  class URLSource < Source
@@ -1,4 +1,4 @@
1
1
  module GTFS
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
4
4
 
@@ -23,13 +23,11 @@ describe GTFS::Source do
23
23
 
24
24
  it {should == 'LocalSource'}
25
25
  end
26
- end
27
26
 
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
27
+ context 'with a file object as a data root' do
28
+ let(:data_source) {File.open(valid_local_source)}
29
+
30
+ it {should == 'LocalSource'}
33
31
  end
34
32
  end
35
33
 
@@ -55,6 +53,13 @@ describe GTFS::Source do
55
53
  end
56
54
 
57
55
  describe '#routes' do
56
+ context 'when the source is missing routes' do
57
+ let(:source) { GTFS::Source.build source_missing_required_files }
58
+
59
+ it do
60
+ expect { source.routes }.to raise_exception GTFS::InvalidSourceException
61
+ end
62
+ end
58
63
  end
59
64
 
60
65
  describe '#trips' do
@@ -7,7 +7,6 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
7
  require 'bundler/setup'
8
8
  require 'rspec'
9
9
  require 'vcr'
10
- require 'ruby-debug'
11
10
  require 'gtfs'
12
11
 
13
12
  require File.expand_path(File.dirname(__FILE__) + '/support/model_shared_examples')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gtfs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-03 00:00:00.000000000 Z
12
+ date: 2012-12-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -139,19 +139,29 @@ dependencies:
139
139
  - - ! '>='
140
140
  - !ruby/object:Gem::Version
141
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
142
+ description: ! "gtfs reads GTFS data from a google-compliant Zip bundle \n and
143
+ returns an object representing the CSV data inside"
144
144
  email:
145
- - nerdEd
145
+ - ed@nerded.net
146
146
  executables: []
147
147
  extensions: []
148
148
  extra_rdoc_files: []
149
149
  files:
150
+ - .gitignore
151
+ - .travis.yml
152
+ - Gemfile
153
+ - Gemfile.lock
154
+ - README.md
150
155
  - Rakefile
156
+ - gtfs.gemspec
157
+ - lib/gtfs.rb
151
158
  - lib/gtfs/agency.rb
152
159
  - lib/gtfs/calendar.rb
153
160
  - lib/gtfs/calendar_date.rb
154
161
  - lib/gtfs/custom_exceptions.rb
162
+ - lib/gtfs/fare_attribute.rb
163
+ - lib/gtfs/fare_rule.rb
164
+ - lib/gtfs/frequency.rb
155
165
  - lib/gtfs/local_source.rb
156
166
  - lib/gtfs/model.rb
157
167
  - lib/gtfs/route.rb
@@ -159,10 +169,10 @@ files:
159
169
  - lib/gtfs/source.rb
160
170
  - lib/gtfs/stop.rb
161
171
  - lib/gtfs/stop_time.rb
172
+ - lib/gtfs/transfer.rb
162
173
  - lib/gtfs/trip.rb
163
174
  - lib/gtfs/url_source.rb
164
175
  - lib/gtfs/version.rb
165
- - lib/gtfs.rb
166
176
  - spec/fixtures/cassettes/invalid_gtfs_uri.yml
167
177
  - spec/fixtures/cassettes/valid_gtfs_uri.yml
168
178
  - spec/fixtures/missing_files.zip
@@ -180,7 +190,6 @@ files:
180
190
  - spec/gtfs/url_source_spec.rb
181
191
  - spec/spec_helper.rb
182
192
  - spec/support/model_shared_examples.rb
183
- - README.md
184
193
  homepage: https://github.com/nerdEd/gtfs
185
194
  licenses: []
186
195
  post_install_message:
@@ -205,4 +214,21 @@ rubygems_version: 1.8.24
205
214
  signing_key:
206
215
  specification_version: 3
207
216
  summary: Load and read GTFS data from zip bundles
208
- test_files: []
217
+ test_files:
218
+ - spec/fixtures/cassettes/invalid_gtfs_uri.yml
219
+ - spec/fixtures/cassettes/valid_gtfs_uri.yml
220
+ - spec/fixtures/missing_files.zip
221
+ - spec/fixtures/valid_gtfs.zip
222
+ - spec/gtfs/agency_spec.rb
223
+ - spec/gtfs/calendar_date_spec.rb
224
+ - spec/gtfs/calendar_spec.rb
225
+ - spec/gtfs/local_source_spec.rb
226
+ - spec/gtfs/route_spec.rb
227
+ - spec/gtfs/shape_spec.rb
228
+ - spec/gtfs/source_spec.rb
229
+ - spec/gtfs/stop_spec.rb
230
+ - spec/gtfs/stop_time_spec.rb
231
+ - spec/gtfs/trip_spec.rb
232
+ - spec/gtfs/url_source_spec.rb
233
+ - spec/spec_helper.rb
234
+ - spec/support/model_shared_examples.rb