gtfs 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +13 -2
  3. data/Gemfile +11 -8
  4. data/Gemfile.lock +41 -29
  5. data/LICENSE +21 -0
  6. data/README.md +15 -17
  7. data/Rakefile +2 -0
  8. data/gtfs.gemspec +5 -2
  9. data/lib/gtfs.rb +1 -0
  10. data/lib/gtfs/agency.rb +3 -3
  11. data/lib/gtfs/calendar.rb +2 -2
  12. data/lib/gtfs/calendar_date.rb +2 -2
  13. data/lib/gtfs/fare_attribute.rb +3 -3
  14. data/lib/gtfs/fare_rule.rb +2 -2
  15. data/lib/gtfs/feed_info.rb +19 -0
  16. data/lib/gtfs/frequency.rb +2 -2
  17. data/lib/gtfs/model.rb +28 -9
  18. data/lib/gtfs/route.rb +2 -2
  19. data/lib/gtfs/shape.rb +2 -2
  20. data/lib/gtfs/source.rb +19 -11
  21. data/lib/gtfs/stop.rb +3 -3
  22. data/lib/gtfs/stop_time.rb +3 -3
  23. data/lib/gtfs/transfer.rb +2 -2
  24. data/lib/gtfs/trip.rb +3 -3
  25. data/lib/gtfs/url_source.rb +1 -1
  26. data/lib/gtfs/version.rb +1 -2
  27. data/spec/fixtures/cassettes/invalid_gtfs_uri.yml +58 -65
  28. data/spec/fixtures/cassettes/valid_gtfs_uri.yml +55 -122
  29. data/spec/gtfs/agency_spec.rb +2 -2
  30. data/spec/gtfs/calendar_date_spec.rb +1 -1
  31. data/spec/gtfs/calendar_spec.rb +1 -1
  32. data/spec/gtfs/fare_attribute_spec.rb +14 -0
  33. data/spec/gtfs/fare_rule_spec.rb +13 -0
  34. data/spec/gtfs/feed_info_spec.rb +14 -0
  35. data/spec/gtfs/frequency_spec.rb +14 -0
  36. data/spec/gtfs/route_spec.rb +1 -1
  37. data/spec/gtfs/shape_spec.rb +1 -1
  38. data/spec/gtfs/source_spec.rb +23 -11
  39. data/spec/gtfs/stop_spec.rb +1 -1
  40. data/spec/gtfs/stop_time_spec.rb +2 -2
  41. data/spec/gtfs/transfer_spec.rb +14 -0
  42. data/spec/gtfs/trip_spec.rb +2 -2
  43. data/spec/gtfs/url_source_spec.rb +4 -4
  44. data/spec/spec_helper.rb +7 -3
  45. data/spec/support/model_shared_examples.rb +46 -22
  46. metadata +46 -52
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8df8ca58b1e0d2b4855443d0d7825c59f7eabfaf8be6c89fff0da03cd69ce6b0
4
+ data.tar.gz: e38f08d1959b969400f80353d26ad1ee12965df92fc4c0ebe405ee39d81555ea
5
+ SHA512:
6
+ metadata.gz: 689c542dead3e918da4d02dd6170ae31a134eeea4dfff6ba7bf1591fd71d297bcbd616b935790823a4247d8421aeb7fc9be9f8618ebd67f6a50388df57c27193
7
+ data.tar.gz: 4b09664e10ccd3c84dbdccbde25b66536395769dc3e789ce371c8f41ad6b77f68a2dbf70466ef88f508e717081eb7bfbf5be197d53a3fb80c6423d70d6495812
data/.travis.yml CHANGED
@@ -1,4 +1,15 @@
1
+ os: linux
2
+ dist: bionic
1
3
  language: ruby
2
4
  rvm:
3
- - 1.9.2
4
- - 1.9.3
5
+ - 2.5.7
6
+ - 2.6.5
7
+ - 2.7.0
8
+
9
+ before_install:
10
+ - gem install bundler
11
+
12
+ install: bundle install --retry=3
13
+
14
+ notifications:
15
+ email: false
data/Gemfile CHANGED
@@ -1,10 +1,13 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
2
 
3
- gem 'rake'
4
- gem 'rspec'
5
3
  gem 'multi_json', '1.0.4'
6
- gem 'simplecov', :require => false
7
- gem 'rubyzip'
8
- gem 'vcr'
9
- gem 'fakeweb'
10
- gem 'debugger'
4
+ gem 'rubyzip', '~> 1.1'
5
+
6
+ group :test, :development do
7
+ gem 'simplecov', :require => false
8
+ gem 'rake'
9
+ gem 'rspec-its'
10
+ gem 'rspec'
11
+ gem 'vcr'
12
+ gem 'webmock'
13
+ end
data/Gemfile.lock CHANGED
@@ -1,42 +1,54 @@
1
1
  GEM
2
- remote: http://rubygems.org/
2
+ remote: https://rubygems.org/
3
3
  specs:
4
- columnize (0.3.6)
5
- debugger (1.2.2)
6
- columnize (>= 0.3.1)
7
- debugger-linecache (~> 1.1.1)
8
- debugger-ruby_core_source (~> 1.1.5)
9
- debugger-linecache (1.1.2)
10
- debugger-ruby_core_source (>= 1.1.1)
11
- debugger-ruby_core_source (1.1.5)
12
- diff-lcs (1.1.3)
13
- fakeweb (1.3.0)
4
+ addressable (2.7.0)
5
+ public_suffix (>= 2.0.2, < 5.0)
6
+ crack (0.4.4)
7
+ diff-lcs (1.4.4)
8
+ docile (1.3.2)
9
+ hashdiff (1.0.1)
14
10
  multi_json (1.0.4)
15
- rake (0.9.2.2)
16
- rspec (2.8.0)
17
- rspec-core (~> 2.8.0)
18
- rspec-expectations (~> 2.8.0)
19
- rspec-mocks (~> 2.8.0)
20
- rspec-core (2.8.0)
21
- rspec-expectations (2.8.0)
22
- diff-lcs (~> 1.1.2)
23
- rspec-mocks (2.8.0)
24
- rubyzip (0.9.5)
25
- simplecov (0.6.1)
26
- multi_json (~> 1.0)
27
- simplecov-html (~> 0.5.3)
28
- simplecov-html (0.5.3)
29
- vcr (1.11.3)
11
+ public_suffix (4.0.6)
12
+ rake (13.0.1)
13
+ rspec (3.9.0)
14
+ rspec-core (~> 3.9.0)
15
+ rspec-expectations (~> 3.9.0)
16
+ rspec-mocks (~> 3.9.0)
17
+ rspec-core (3.9.3)
18
+ rspec-support (~> 3.9.3)
19
+ rspec-expectations (3.9.3)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.9.0)
22
+ rspec-its (1.3.0)
23
+ rspec-core (>= 3.0.0)
24
+ rspec-expectations (>= 3.0.0)
25
+ rspec-mocks (3.9.1)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.9.0)
28
+ rspec-support (3.9.4)
29
+ rubyzip (1.3.0)
30
+ simplecov (0.19.0)
31
+ docile (~> 1.1)
32
+ simplecov-html (~> 0.11)
33
+ simplecov-html (0.12.3)
34
+ vcr (6.0.0)
35
+ webmock (3.9.3)
36
+ addressable (>= 2.3.6)
37
+ crack (>= 0.3.2)
38
+ hashdiff (>= 0.4.0, < 2.0.0)
30
39
 
31
40
  PLATFORMS
32
41
  ruby
33
42
 
34
43
  DEPENDENCIES
35
- debugger
36
- fakeweb
37
44
  multi_json (= 1.0.4)
38
45
  rake
39
46
  rspec
40
- rubyzip
47
+ rspec-its
48
+ rubyzip (~> 1.1)
41
49
  simplecov
42
50
  vcr
51
+ webmock
52
+
53
+ BUNDLED WITH
54
+ 2.1.4
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2012 Ed Schmalzle
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.org/nerdEd/gtfs.svg?branch=master)](https://travis-ci.org/nerdEd/gtfs)
2
+
1
3
  ### GTFS Ruby
2
4
 
3
5
  A Ruby wrapper for the [General Transit Feed Specification](https://developers.google.com/transit/gtfs/)
@@ -6,8 +8,12 @@ A Ruby wrapper for the [General Transit Feed Specification](https://developers.g
6
8
 
7
9
  Initialize a new GTFS source:
8
10
 
11
+ # Defaults to strict checking of required columns
9
12
  source = GTFS::Source.build(<URI or Path to GTFS zip file>)
10
-
13
+
14
+ # Relax the column checks, useful for sources that don't conform to standard
15
+ source = GTFS::Source.build(<URI or Path to GTFS zip file>, {strict: false})
16
+
11
17
  Accessing GTFS data from the source:
12
18
 
13
19
  source.agencies
@@ -22,24 +28,16 @@ Accessing GTFS data from the source:
22
28
  source.shapes
23
29
  source.frequencies
24
30
  source.transfers
31
+
32
+ Alternatively:
25
33
 
26
- ### License
34
+ source.each_agency {|agency| puts agency}
35
+ ...
36
+ source.each_transfer {|transfer| puts transfer}
27
37
 
28
- Copyright (C) 2012 Ed Schmalzle
29
38
 
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:
39
+ ### License
36
40
 
37
- The above copyright notice and this permission notice shall be included in
38
- all copies or substantial portions of the Software.
41
+ This project is licensed under the terms of the MIT license.
39
42
 
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.
43
+ See this license at [`LICENSE`](LICENSE).
data/Rakefile CHANGED
@@ -4,3 +4,5 @@ require 'rspec/core/rake_task'
4
4
  RSpec::Core::RakeTask.new do |t|
5
5
  t.pattern = './spec/**/*_spec.rb'
6
6
  end
7
+
8
+ task :default => [:spec]
data/gtfs.gemspec CHANGED
@@ -5,7 +5,6 @@ require './lib/gtfs/version'
5
5
  Gem::Specification.new do |gem|
6
6
  gem.name = 'gtfs'
7
7
  gem.version = GTFS::VERSION
8
- gem.date = Date.today.to_s
9
8
 
10
9
  gem.summary = 'Load and read GTFS data from zip bundles'
11
10
  gem.description = 'gtfs reads GTFS data from a google-compliant Zip bundle
@@ -15,9 +14,13 @@ Gem::Specification.new do |gem|
15
14
  gem.email = ['ed@nerded.net']
16
15
  gem.homepage = 'https://github.com/nerdEd/gtfs'
17
16
 
17
+ gem.license = 'MIT'
18
+
19
+ gem.required_ruby_version = '>= 1.9.2'
20
+
18
21
  gem.add_dependency 'rake'
19
22
  gem.add_dependency 'multi_json'
20
- gem.add_dependency 'rubyzip'
23
+ gem.add_dependency 'rubyzip', ['~> 1.1']
21
24
 
22
25
  gem.add_development_dependency 'rspec', ['>= 2.0.0']
23
26
  gem.add_development_dependency 'simplecov'
data/lib/gtfs.rb CHANGED
@@ -3,6 +3,7 @@ require 'gtfs/model'
3
3
  require 'gtfs/agency'
4
4
  require 'gtfs/calendar'
5
5
  require 'gtfs/calendar_date'
6
+ require 'gtfs/feed_info'
6
7
  require 'gtfs/route'
7
8
  require 'gtfs/shape'
8
9
  require 'gtfs/stop'
data/lib/gtfs/agency.rb CHANGED
@@ -3,7 +3,7 @@ module GTFS
3
3
  include GTFS::Model
4
4
 
5
5
  has_required_attrs :name, :url, :timezone
6
- has_optional_attrs :id, :lang, :phone, :fare_url
6
+ has_optional_attrs :id, :lang, :phone, :fare_url, :email
7
7
  attr_accessor *attrs
8
8
 
9
9
  column_prefix :agency_
@@ -12,8 +12,8 @@ module GTFS
12
12
  required_file true
13
13
  uses_filename 'agency.txt'
14
14
 
15
- def self.parse_agencies(data)
16
- return parse_models(data)
15
+ def self.parse_agencies(data, options={})
16
+ return parse_models(data, options)
17
17
  end
18
18
  end
19
19
  end
data/lib/gtfs/calendar.rb CHANGED
@@ -9,8 +9,8 @@ module GTFS
9
9
  required_file true
10
10
  uses_filename 'calendar.txt'
11
11
 
12
- def self.parse_calendars(data)
13
- return parse_models(data)
12
+ def self.parse_calendars(data, options={})
13
+ return parse_models(data, options)
14
14
  end
15
15
  end
16
16
  end
@@ -9,8 +9,8 @@ module GTFS
9
9
  required_file true
10
10
  uses_filename 'calendar_dates.txt'
11
11
 
12
- def self.parse_calendar_dates(data)
13
- return parse_models(data)
12
+ def self.parse_calendar_dates(data, options={})
13
+ return parse_models(data, options)
14
14
  end
15
15
  end
16
16
  end
@@ -3,15 +3,15 @@ module GTFS
3
3
  include GTFS::Model
4
4
 
5
5
  has_required_attrs :fare_id, :price, :currency_type, :payment_method, :transfers
6
- has_optional_attrs :transfer_duration
6
+ has_optional_attrs :transfer_duration
7
7
  attr_accessor *attrs
8
8
 
9
9
  collection_name :fare_attributes
10
10
  required_file false
11
11
  uses_filename 'fare_attributes.txt'
12
12
 
13
- def self.parse_fare_attributes(data)
14
- return parse_models(data)
13
+ def self.parse_fare_attributes(data, options={})
14
+ return parse_models(data, options)
15
15
  end
16
16
  end
17
17
  end
@@ -10,8 +10,8 @@ module GTFS
10
10
  required_file false
11
11
  uses_filename 'fare_rules.txt'
12
12
 
13
- def self.parse_fare_rules(data)
14
- return parse_models(data)
13
+ def self.parse_fare_rules(data, options={})
14
+ return parse_models(data, options)
15
15
  end
16
16
  end
17
17
  end
@@ -0,0 +1,19 @@
1
+ module GTFS
2
+ class FeedInfo
3
+ include GTFS::Model
4
+
5
+ has_required_attrs :publisher_name, :publisher_url, :lang
6
+ has_optional_attrs :start_date, :end_date, :version
7
+ attr_accessor *attrs
8
+
9
+ column_prefix :feed_
10
+
11
+ collection_name :feed_infos
12
+ required_file false
13
+ uses_filename 'feed_info.txt'
14
+
15
+ def self.parse_feed_infos(data, options={})
16
+ return parse_models(data, options)
17
+ end
18
+ end
19
+ end
@@ -10,8 +10,8 @@ module GTFS
10
10
  required_file false
11
11
  uses_filename 'frequencies.txt'
12
12
 
13
- def self.parse_frequencies(data)
14
- return parse_models(data)
13
+ def self.parse_frequencies(data, options={})
14
+ return parse_models(data, options)
15
15
  end
16
16
  end
17
17
  end
data/lib/gtfs/model.rb CHANGED
@@ -64,23 +64,42 @@ module GTFS
64
64
 
65
65
  def collection_name(collection_name)
66
66
  self.define_singleton_method(:name) {collection_name}
67
+
68
+ self.define_singleton_method(:singular_name) {
69
+ self.to_s.split('::').last.
70
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
71
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
72
+ tr("-", "_").downcase
73
+ }
67
74
  end
68
-
69
- def uses_filename(filename)
75
+
76
+ def uses_filename(filename)
70
77
  self.define_singleton_method(:filename) {filename}
71
78
  end
72
79
 
73
- def parse_models(data)
80
+ def each(filename)
81
+ CSV.foreach(filename, :headers => true) do |row|
82
+ yield parse_model(row.to_hash)
83
+ end
84
+ end
85
+
86
+ def parse_model(attr_hash, options={})
87
+ unprefixed_attr_hash = {}
88
+
89
+ attr_hash.each do |key, val|
90
+ unprefixed_attr_hash[key.gsub(/^#{prefix}/, '')] = val
91
+ end
92
+
93
+ model = self.new(unprefixed_attr_hash)
94
+ end
95
+
96
+ def parse_models(data, options={})
74
97
  return [] if data.nil? || data.empty?
75
98
 
76
99
  models = []
77
100
  CSV.parse(data, :headers => true) do |row|
78
- attr_hash = {}
79
- row.to_hash.each do |key, val|
80
- attr_hash[key.gsub(/^#{prefix}/, '')] = val
81
- end
82
- model = self.new(attr_hash)
83
- models << model if model.valid?
101
+ model = parse_model(row.to_hash, options)
102
+ models << model if options[:strict] == false || model.valid?
84
103
  end
85
104
  models
86
105
  end
data/lib/gtfs/route.rb CHANGED
@@ -12,8 +12,8 @@ module GTFS
12
12
  required_file true
13
13
  uses_filename 'routes.txt'
14
14
 
15
- def self.parse_routes(data)
16
- return parse_models(data)
15
+ def self.parse_routes(data, options={})
16
+ return parse_models(data, options)
17
17
  end
18
18
  end
19
19
  end
data/lib/gtfs/shape.rb CHANGED
@@ -12,8 +12,8 @@ module GTFS
12
12
 
13
13
  column_prefix :shape_
14
14
 
15
- def self.parse_shapes(data)
16
- return parse_models(data)
15
+ def self.parse_shapes(data, options={})
16
+ return parse_models(data, options)
17
17
  end
18
18
  end
19
19
  end
data/lib/gtfs/source.rb CHANGED
@@ -1,21 +1,23 @@
1
1
  require 'tmpdir'
2
2
  require 'fileutils'
3
- require 'zip/zip'
3
+ require 'zip'
4
4
 
5
5
  module GTFS
6
6
  class Source
7
7
 
8
- ENTITIES = [GTFS::Agency, GTFS::Stop, GTFS::Route, GTFS::Trip, GTFS::StopTime,
8
+ ENTITIES = [GTFS::Agency, GTFS::Stop, GTFS::Route, GTFS::Trip, GTFS::StopTime,
9
9
  GTFS::Calendar, GTFS::CalendarDate, GTFS::Shape, GTFS::FareAttribute,
10
- GTFS::FareRule, GTFS::Frequency, GTFS::Transfer]
10
+ GTFS::FareRule, GTFS::Frequency, GTFS::Transfer, GTFS::FeedInfo]
11
11
 
12
12
  REQUIRED_SOURCE_FILES = ENTITIES.select(&:required_file?).map(&:filename)
13
13
  OPTIONAL_SOURCE_FILES = ENTITIES.reject(&:required_file?).map(&:filename)
14
14
  SOURCE_FILES = ENTITIES.map(&:filename)
15
15
 
16
- attr_accessor :source, :archive
16
+ DEFAULT_OPTIONS = {strict: true}
17
17
 
18
- def initialize(source)
18
+ attr_accessor :source, :archive, :options
19
+
20
+ def initialize(source, opts={})
19
21
  raise 'Source cannot be nil' if source.nil?
20
22
 
21
23
  @tmp_dir = Dir.mktmpdir
@@ -23,6 +25,8 @@ module GTFS
23
25
 
24
26
  @source = source
25
27
  load_archive(@source)
28
+
29
+ @options = DEFAULT_OPTIONS.merge(opts)
26
30
  end
27
31
 
28
32
  def self.finalize(directory)
@@ -30,7 +34,7 @@ module GTFS
30
34
  end
31
35
 
32
36
  def extract_to_cache(source_path)
33
- Zip::ZipFile.open(source_path) do |zip|
37
+ Zip::File.open(source_path) do |zip|
34
38
  zip.entries.each do |entry|
35
39
  zip.extract(entry.name, File.join(@tmp_dir, '/', entry.name))
36
40
  end
@@ -41,11 +45,11 @@ module GTFS
41
45
  raise 'Cannot directly instantiate base GTFS::Source'
42
46
  end
43
47
 
44
- def self.build(data_root)
48
+ def self.build(data_root, opts={})
45
49
  if File.exists?(data_root)
46
- LocalSource.new(data_root)
50
+ src = LocalSource.new(data_root, opts)
47
51
  else
48
- URLSource.new(data_root)
52
+ src = URLSource.new(data_root, opts)
49
53
  end
50
54
  end
51
55
 
@@ -61,9 +65,13 @@ module GTFS
61
65
  ENTITIES.each do |entity|
62
66
  define_method entity.name.to_sym do
63
67
  parse_file entity.filename do |f|
64
- entity.send("parse_#{entity.name}".to_sym, f.read)
68
+ entity.send("parse_#{entity.name}".to_sym, f.read, options)
65
69
  end
66
70
  end
71
+
72
+ define_method "each_#{entity.singular_name}".to_sym do |&block|
73
+ entity.each(File.join(@tmp_dir, entity.filename)) { |model| block.call model }
74
+ end
67
75
  end
68
76
 
69
77
  def files
@@ -72,7 +80,7 @@ module GTFS
72
80
 
73
81
  def parse_file(filename)
74
82
  raise_if_missing_source filename
75
- open File.join(@tmp_dir, '/', filename) do |f|
83
+ open File.join(@tmp_dir, '/', filename), 'r:bom|utf-8' do |f|
76
84
  files[filename] ||= yield f
77
85
  end
78
86
  end