gtfs-reader 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,66 @@
1
+ require 'log4r'
2
+ require 'colorize'
3
+
4
+ module GtfsReader
5
+ module Log
6
+ class << self
7
+ def debug(*args, &block); log :debug, *args, &block end
8
+ def info(*args, &block); log :info, *args, &block end
9
+ def warn(*args, &block); log :warn, *args, &block end
10
+ def error(*args, &block); log :error, *args, &block end
11
+ def fatal(*args, &block); log :fatal, *args, &block end
12
+
13
+ def log(level, *args, &block)
14
+ logger.send level, *args, &block
15
+ nil
16
+ end
17
+
18
+ def logger
19
+ @logger = yield if block_given?
20
+ @logger ||= create_logger
21
+ end
22
+
23
+ def level=(lev)
24
+ logger.level =
25
+ case lev
26
+ when :debug then logger.levels.index 'DEBUG'
27
+ when :info then logger.levels.index 'INFO'
28
+ when :warn then logger.levels.index 'WARN'
29
+ when :error then logger.levels.index 'ERROR'
30
+ when :fatal then logger.levels.index 'FATAL'
31
+ else raise "unknown log level '#{lev}'"
32
+ end
33
+ end
34
+
35
+ def level
36
+ logger.level
37
+ end
38
+
39
+ def quiet
40
+ old_logger = @logger
41
+ begin
42
+ @logger = NoOp.new
43
+ yield
44
+ ensure
45
+ @logger = old_logger
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def create_logger
52
+ Log4r::Logger.new('GtfsReader').tap do |log|
53
+ log.outputters << Log4r::StdoutOutputter.new('log_stdout')
54
+ log.level = Log4r::INFO
55
+ log.debug { 'Starting GtfsReader...'.underline.colorize :yellow }
56
+ end
57
+ end
58
+ end
59
+
60
+ class NoOp
61
+ def method_missing(*args, &block)
62
+ nil
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,130 @@
1
+ require 'net/http'
2
+ require 'open-uri'
3
+ require 'zip/filesystem'
4
+ require 'csv'
5
+ require 'uri'
6
+
7
+ require_relative 'file_reader'
8
+
9
+ module GtfsReader
10
+ class SourceUpdater
11
+ #@param name [String] an arbitrary string describing this source
12
+ #@param source [Source]
13
+ def initialize(name, source)
14
+ @name, @source = name, source
15
+ end
16
+
17
+ def before_callbacks
18
+ @source.before.call fetch_remote_etag if @source.before
19
+ end
20
+
21
+ # Download the data from the remote server
22
+ def read
23
+ Log.debug { " Reading #{@source.url.green}" }
24
+ @file = Tempfile.new 'gtfs'
25
+ @file.binmode
26
+ @file << open(@source.url).read
27
+ @file.rewind
28
+ #binding.pry
29
+ @zip = Zip::File.open @file
30
+ Log.debug { "Finished reading #{@source.url.green}" }
31
+ end
32
+
33
+ # Close any streams still open
34
+ def finish
35
+ @zip.close if @zip
36
+ @file.delete if @file
37
+ end
38
+
39
+ def check_files
40
+ @found_files = []
41
+ check_required_files
42
+ check_optional_files
43
+ end
44
+
45
+ def check_required_files
46
+ Log.info { 'required files'.magenta }
47
+ files = @source.feed_definition.required_files
48
+ missing = check_missing_files files, :green, :red
49
+ raise RequiredFilenamesMissing, missing unless missing.empty?
50
+ end
51
+
52
+ def check_optional_files
53
+ Log.info { 'optional files'.cyan }
54
+ files = @source.feed_definition.optional_files
55
+ check_missing_files files, :cyan, :light_yellow
56
+ end
57
+
58
+ # Check that every file has its required columns
59
+ def check_columns
60
+ @found_files.each do |file|
61
+ @zip.file.open(file.filename) do |data|
62
+ FileReader.new data, file, validate: true
63
+ end
64
+ end
65
+ end
66
+
67
+ def process_files
68
+ @found_files.each do |file|
69
+ if @source.handlers.handler? file.name
70
+ process_from_temp_file file
71
+ else
72
+ Log.warn { "Skipping #{file.filename.yellow} (no handler)" }
73
+ end
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def check_missing_files(expected, found_color, missing_color)
80
+ check = '✔'.colorize found_color
81
+ cross = '✘'.colorize missing_color
82
+
83
+ expected.collect do |req|
84
+ filename = req.filename
85
+ if filenames.include? filename
86
+ Log.info { "#{filename.rjust filename_width} [#{check}]" }
87
+ @found_files << req
88
+ nil
89
+ else
90
+ Log.info { "#{filename.rjust filename_width} [#{cross}]" }
91
+ filename
92
+ end
93
+ end.compact
94
+ end
95
+
96
+ def filename_width
97
+ @filename_width ||= @source.feed_definition.files.max do |a, b|
98
+ a.filename.length <=> b.filename.length
99
+ end.filename.length
100
+ end
101
+
102
+ def filenames
103
+ @filenames ||= @zip.entries.collect &:name
104
+ end
105
+
106
+ def fetch_remote_etag
107
+ url = URI @source.url
108
+ Net::HTTP.start(url.host) do |http|
109
+ /[^"]+/ === http.request_head(url.path)['etag'] and $&
110
+ end
111
+ end
112
+
113
+ def process_from_temp_file(file)
114
+ do_parse = !GtfsReader.config.skip_parsing
115
+ hash = !!GtfsReader.config.return_hashes
116
+
117
+ Log.info "Reading file #{file.filename.cyan}..."
118
+
119
+ temp = Tempfile.new 'gtfs_file'
120
+ begin
121
+ @zip.file.open(file.filename) { |z| temp.write z.read }
122
+ temp.rewind
123
+ reader = FileReader.new temp, file, parse: do_parse, hash: hash
124
+ @source.handlers.handle_file file.name, reader
125
+ ensure
126
+ temp.close and temp.unlink
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,71 @@
1
+ module GtfsReader
2
+ module Version
3
+ # The following four lines are generated, so don't mess with them.
4
+ MAJOR = 0
5
+ MINOR = 2
6
+ PATCH = 2
7
+ BUILD = nil
8
+
9
+ def self.to_s
10
+ [MAJOR, MINOR, PATCH, BUILD].compact.join '.'
11
+ end
12
+
13
+ # A helper class which bumps the version number stored in this file
14
+ class Bumper
15
+ PARTS = %i[major minor patch]
16
+ PATTERN = %r[(\s+)MAJOR = \d+\s+MINOR = \d+\s+PATCH = \d+\s+BUILD = .+]
17
+
18
+ def initialize(filename=__FILE__, part)
19
+ raise "#{part} not one of #{PARTS}" unless PARTS.include? part
20
+ @filename, @part = filename, part
21
+ end
22
+
23
+ # Increase the version number and write it to this file
24
+ def bump
25
+ parts = new_version
26
+ # \1 holds a newline and the indentation from the source
27
+ text = '\1' + ["MAJOR = #{parts[:major]}",
28
+ "MINOR = #{parts[:minor]}",
29
+ "PATCH = #{parts[:patch]}",
30
+ "BUILD = #{parts[:build] || 'nil'}"].join( '\1' )
31
+
32
+ out_data = File.read( @filename ).gsub PATTERN, text
33
+ #puts out_data
34
+ File.open( @filename, 'w' ) { |out| out << out_data }
35
+ puts "Bumped version to #{to_s}"
36
+ end
37
+
38
+ #@return [String] What the new version string will be.
39
+ def to_s
40
+ p = new_version
41
+ [p[:major], p[:minor], p[:patch], p[:build]].compact.join ?.
42
+ end
43
+
44
+ private
45
+
46
+ def new_version
47
+ @vers ||= { major: MAJOR,
48
+ minor: MINOR,
49
+ patch: PATCH,
50
+ build: BUILD }.merge new_parts
51
+ end
52
+
53
+ def new_parts
54
+ case @part
55
+ when :major then {
56
+ major: MAJOR + 1,
57
+ minor: 0,
58
+ patch: 0
59
+ }
60
+ when :minor then {
61
+ minor: MINOR + 1,
62
+ patch: 0
63
+ }
64
+ else {
65
+ patch: PATCH + 1
66
+ }
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gtfs-reader
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Jon Sangster
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: log4r
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubyzip
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colorize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '0.9'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '0.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '0.8'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '0.8'
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: jeweler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '2.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: '2.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ~>
130
+ - !ruby/object:Gem::Version
131
+ version: '4.2'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: '4.2'
139
+ description: 'Reads and parses zip files conforming to Google''s GTFS spec. Such files
140
+ can take up quite a bit of memory when inflated, so this gem prefers to read them
141
+ as a stream of rows. GTFS Spec: https://developers.google.com/transit/gtfs'
142
+ email: jon@ertt.ca
143
+ executables: []
144
+ extensions: []
145
+ extra_rdoc_files:
146
+ - LICENSE
147
+ - README.md
148
+ files:
149
+ - LICENSE
150
+ - README.md
151
+ - Rakefile
152
+ - lib/gtfs_reader.rb
153
+ - lib/gtfs_reader/bulk_feed_handler.rb
154
+ - lib/gtfs_reader/config/column.rb
155
+ - lib/gtfs_reader/config/defaults/gtfs_feed_definition.rb
156
+ - lib/gtfs_reader/config/feed_definition.rb
157
+ - lib/gtfs_reader/config/file_definition.rb
158
+ - lib/gtfs_reader/config/prefixed_column_setter.rb
159
+ - lib/gtfs_reader/config/source.rb
160
+ - lib/gtfs_reader/config/sources.rb
161
+ - lib/gtfs_reader/configuration.rb
162
+ - lib/gtfs_reader/core.rb
163
+ - lib/gtfs_reader/exceptions.rb
164
+ - lib/gtfs_reader/feed_handler.rb
165
+ - lib/gtfs_reader/file_reader.rb
166
+ - lib/gtfs_reader/file_row.rb
167
+ - lib/gtfs_reader/log.rb
168
+ - lib/gtfs_reader/source_updater.rb
169
+ - lib/gtfs_reader/version.rb
170
+ homepage: http://github.com/sangster/gtfs-reader
171
+ licenses:
172
+ - GPL 3
173
+ metadata: {}
174
+ post_install_message:
175
+ rdoc_options: []
176
+ require_paths:
177
+ - lib
178
+ required_ruby_version: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - '>='
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ required_rubygems_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ requirements: []
189
+ rubyforge_project:
190
+ rubygems_version: 2.0.14
191
+ signing_key:
192
+ specification_version: 4
193
+ summary: Read General Transit Feed Specification zip files
194
+ test_files: []
195
+ has_rdoc: