gtfs_reader 1.2.0 → 3.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.
@@ -9,46 +9,46 @@ module GtfsReader
9
9
  @file_definition = {}
10
10
  end
11
11
 
12
- #@return [Array<FileDefinition>] All of the defined files.
12
+ # @return [Array<FileDefinition>] All of the defined files.
13
13
  def files
14
14
  @file_definition.values
15
15
  end
16
16
 
17
17
  def required_files
18
- files.select &:required?
18
+ files.select(&:required?)
19
19
  end
20
20
 
21
21
  def optional_files
22
- files.reject &:required?
22
+ files.reject(&:required?)
23
23
  end
24
24
 
25
- #@overload file(name, *args, &block)
26
- # Defines a new file in the feed.
25
+ # @overload file(name, *args, &block)
26
+ # Defines a new file in the feed.
27
27
  #
28
- # @param name [String] the name of this file within the feed. This name
29
- # should not include a file extension (like +.txt+)
30
- # @param args [Array] the first argument is used as a +Hash+ of options
31
- # to create the new file definition
32
- # @param block [Proc] this block is +instance_eval+ed on the new
33
- # {FileDefinition file}
34
- # @return [FileDefinition] the newly created file
28
+ # @param name [String] the name of this file within the feed. This name
29
+ # should not include a file extension (like +.txt+)
30
+ # @param args [Array] the first argument is used as a +Hash+ of options
31
+ # to create the new file definition
32
+ # @param block [Proc] this block is +instance_eval+ed on the new
33
+ # {FileDefinition file}
34
+ # @return [FileDefinition] the newly created file
35
35
  #
36
- #@overload file(name)
37
- # @param name [String] the name of the file to return
38
- # @return [FileDefinition] the previously created file with the given name
39
- #@see FileDefinition
36
+ # @overload file(name)
37
+ # @param name [String] the name of the file to return
38
+ # @return [FileDefinition] the previously created file with +name+
39
+ # @see FileDefinition
40
40
  def file(name, *args, &block)
41
41
  return @file_definition[name] unless block_given?
42
42
 
43
- definition_for!( name, args.first ).tap do |d|
44
- d.instance_exec &block if block
43
+ definition_for!(name, args.first).tap do |definition|
44
+ definition.instance_exec(&block) if block
45
45
  end
46
46
  end
47
47
 
48
48
  private
49
49
 
50
50
  def definition_for!(name, opts)
51
- @file_definition[name] ||= FileDefinition.new( name, opts )
51
+ @file_definition[name] ||= FileDefinition.new(name, opts)
52
52
  end
53
53
  end
54
54
  end
@@ -6,25 +6,26 @@ module GtfsReader
6
6
  class FileDefinition
7
7
  attr_reader :name
8
8
 
9
- #@param name [String] The name of the file within the feed.
10
- #@option opts [Boolean] :required (false)
11
- # If this file is required to be in the feed.
12
- def initialize(name, opts={})
13
- @name, @columns = name, {}
14
- @opts = { required: false }.merge (opts || {})
9
+ # @param name [String] The name of the file within the feed.
10
+ # @option opts [Boolean] :required (false)
11
+ # If this file is required to be in the feed.
12
+ def initialize(name, opts = {})
13
+ @name = name
14
+ @columns = {}
15
+ @opts = { required: false }.merge(opts || {})
15
16
  end
16
17
 
17
- #@return [Boolean] If this file is required to be in the feed.
18
+ # @return [Boolean] If this file is required to be in the feed.
18
19
  def required?
19
20
  @opts[:required]
20
21
  end
21
22
 
22
- #@return [String] The filename of this file within the GTFS feed.
23
+ # @return [String] The filename of this file within the GTFS feed.
23
24
  def filename
24
25
  "#{name}.txt"
25
26
  end
26
27
 
27
- #@return [Column] The column with the given name
28
+ # @return [Column] The column with the given name
28
29
  def [](name)
29
30
  @columns[name]
30
31
  end
@@ -33,39 +34,39 @@ module GtfsReader
33
34
  @columns.values
34
35
  end
35
36
 
36
- #@return [Array<Column>] The columns required to appear in this file.
37
+ # @return [Array<Column>] The columns required to appear in this file.
37
38
  def required_columns
38
- columns.select &:required?
39
+ columns.select(&:required?)
39
40
  end
40
41
 
41
- #@return [Array<Column>] The columns not required to appear in this file.
42
+ # @return [Array<Column>] The columns not required to appear in this file.
42
43
  def optional_columns
43
- columns.reject &:required?
44
+ columns.reject(&:required?)
44
45
  end
45
46
 
46
- #@return [Array<Column>] The columns which cannot have two rows with the
47
- # same value.
47
+ # @return [Array<Column>] The columns which cannot have two rows with the
48
+ # same value.
48
49
  def unique_columns
49
- columns.select &:unique?
50
+ columns.select(&:unique?)
50
51
  end
51
52
 
52
53
  # Creates a column with the given name.
53
54
  #
54
- #@param name [String] The name of the column to define.
55
- #@param args [Array] The first element of this args list is used as a
56
- # +Hash+ of options to create the new column with.
57
- #@param block [Proc] An optional block used to parse the values of this
58
- # column on each row.
59
- #@yieldparam input [String] The value of this column for a particular row.
60
- #@yieldreturn Any kind of object.
61
- #@return [Column] The newly created column.
55
+ # @param name [String] The name of the column to define.
56
+ # @param args [Array] The first element of this args list is used as a
57
+ # +Hash+ of options to create the new column with.
58
+ # @param block [Proc] An optional block used to parse the values of this
59
+ # column on each row.
60
+ # @yieldparam input [String] The value of this column for a particular row.
61
+ # @yieldreturn Any kind of object.
62
+ # @return [Column] The newly created column.
62
63
  def col(name, *args, &block)
63
64
  if @columns.key? name
64
- @columns[name].parser &block if block_given?
65
+ @columns[name].parser(&block) if block_given?
65
66
  return @columns[name]
66
67
  end
67
68
 
68
- @columns[name] = Column.new name, args.first, &block
69
+ @columns[name] = Column.new(name, args.first, &block)
69
70
  end
70
71
 
71
72
  # Creates an input-output proc to convert column values from one form to
@@ -75,19 +76,19 @@ module GtfsReader
75
76
  # known values. This helper creates such a proc from a given hash and
76
77
  # optional default.
77
78
  #
78
- #@param default [] The value to return if there is no mapping for a given
79
- # input.
80
- #@param reverse_map [Hash] A map of returns values to their input values.
79
+ # @param reverse_map [Hash] A map of returns values to their input values.
81
80
  # This is in reverse because it looks better, like a list of labels:
82
81
  # +{bus: 3, ferry: 4}+
83
- def output_map(default=nil, reverse_map)
82
+ # @param default [] The value to return if there is no mapping for a given
83
+ # input.
84
+ def output_map(reverse_map, default = nil)
84
85
  if reverse_map.values.uniq.length != reverse_map.values.length
85
86
  raise FileDefinitionError, "Duplicate values given: #{reverse_map}"
86
87
  end
87
88
 
88
- map = default.nil? ? {} : Hash.new( default )
89
- reverse_map.each { |k,v| map[v] = k }
90
- map.method( :[] ).to_proc
89
+ map = default.nil? ? {} : Hash.new(default)
90
+ reverse_map.each { |k, v| map[v] = k }
91
+ map.method(:[]).to_proc
91
92
  end
92
93
  end
93
94
  end
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/hash/reverse_merge'
2
+ require 'active_support/core_ext/object/try'
2
3
 
3
4
  require_relative 'feed_definition'
4
5
  require_relative 'defaults/gtfs_feed_definition'
@@ -15,35 +16,35 @@ module GtfsReader
15
16
  @name = name
16
17
  @feed_definition = Config::Defaults::FEED_DEFINITION
17
18
  @feed_handler = FeedHandler.new {}
19
+ @url = nil
20
+ @before = nil
18
21
  end
19
22
 
20
- #@param t [String] if given, will be used as the title of this source
21
- #@return [String] the title of this source
22
- def title(t=nil)
23
- @title = t if t.present?
23
+ # @param title [String] if given, will be used as the title of this source
24
+ # @return [String] the title of this source
25
+ def title(title = nil)
26
+ @title = title if title.present?
24
27
  @title
25
28
  end
26
29
 
27
- #@param u [String] if given, will be used as the URL for this source
28
- #@return [String] the URL this source's ZIP file
29
- def url(u=nil)
30
- @url = u if u.present?
30
+ # @param url [String] if given, will be used as the URL for this source
31
+ # @return [String] the URL this source's ZIP file
32
+ def url(url = nil)
33
+ @url = url if url.present?
31
34
  @url
32
35
  end
33
36
 
34
37
  # Define a block to call before the source is read. If this block
35
38
  # returns +false+, cancel processing the source
36
39
  def before(&block)
37
- if block_given?
38
- @before = block
39
- end
40
+ @before = block if block_given?
40
41
  @before
41
42
  end
42
43
 
43
44
  def feed_definition(&block)
44
45
  if block_given?
45
46
  @feed_definition = FeedDefinition.new.tap do |feed|
46
- feed.instance_exec feed, &block
47
+ feed.instance_exec(feed, &block)
47
48
  end
48
49
  end
49
50
 
@@ -52,15 +53,16 @@ module GtfsReader
52
53
 
53
54
  def handlers(*args, &block)
54
55
  if block_given?
55
- opts = Hash === args.last ? args.pop : {}
56
+ opts = args.last.try(:is_a?, Hash) ? args.pop : {}
56
57
  opts = opts.reverse_merge bulk: nil
57
58
  @feed_handler =
58
59
  if opts[:bulk]
59
- BulkFeedHandler.new opts[:bulk], args, &block
60
+ BulkFeedHandler.new(opts[:bulk], args, &block)
60
61
  else
61
- FeedHandler.new args, &block
62
+ FeedHandler.new(args, &block)
62
63
  end
63
64
  end
65
+
64
66
  @feed_handler
65
67
  end
66
68
  end
@@ -8,18 +8,22 @@ module GtfsReader
8
8
  end
9
9
 
10
10
  def each(&block)
11
- @sources.each &block
11
+ @sources.each(&block)
12
12
  end
13
13
 
14
14
  def [](key)
15
15
  @sources[key]
16
16
  end
17
17
 
18
- def method_missing(name, *args, &block)
18
+ def method_missing(name, *_args, &block)
19
19
  (@sources[name] ||= Source.new name).tap do |src|
20
- src.instance_exec src, &block if ::Kernel.block_given?
20
+ src.instance_exec(src, &block) if ::Kernel.block_given?
21
21
  end
22
22
  end
23
+
24
+ def respond_to_missing?(_name, _include_private = false)
25
+ true
26
+ end
23
27
  end
24
28
  end
25
29
  end
@@ -1,15 +1,14 @@
1
1
  module GtfsReader
2
2
  class Configuration
3
-
4
3
  # Creates simple configuration parameters which may be set by the user
5
- #@param names [Array<Symbol>] the names of the parameters to create
4
+ # @param names [Array<Symbol>] the names of the parameters to create
6
5
  def parameter(*names)
7
6
  names.each do |name|
8
- define_singleton_method name do |*values|
9
- if value = values.first
10
- instance_variable_set "@#{name}", value
7
+ define_singleton_method(name) do |*values|
8
+ if (value = values.first)
9
+ instance_variable_set("@#{name}", value)
11
10
  else
12
- instance_variable_get "@#{name}"
11
+ instance_variable_get("@#{name}")
13
12
  end
14
13
  end
15
14
  end
@@ -17,9 +16,9 @@ module GtfsReader
17
16
 
18
17
  def block_parameter(name, obj_class, *init_args)
19
18
  obj = nil
20
- define_singleton_method name do |*args, &block|
21
- obj ||= obj_class.new *init_args
22
- obj.instance_exec( obj, *args, &block ) if block
19
+ define_singleton_method(name) do |*args, &block|
20
+ obj ||= obj_class.new(*init_args)
21
+ obj.instance_exec(obj, *args, &block) if block
23
22
  obj
24
23
  end
25
24
  end
@@ -4,21 +4,22 @@ require_relative 'config/sources'
4
4
  require_relative 'source_updater'
5
5
 
6
6
  module GtfsReader
7
- extend self
7
+ module_function
8
8
 
9
- #@override config(*args, &blk)
9
+ # @override config(*args, &blk)
10
10
  # @param args [Array] an array or arguments to pass to the given block
11
11
  # @param blk [Proc] a block to to call in the context of the configuration
12
12
  # object. Subsequent calls will use the same configuration for additional
13
13
  # modification.
14
14
  # @return [Configuration] the configuration object
15
15
  #
16
- #@override config
16
+ # @override config
17
17
  # @return [Configuration] the configuration object
18
18
  def config(*args, &blk)
19
19
  @cfg ||= create_config
20
+
20
21
  if block_given?
21
- @cfg.instance_exec *args.unshift(@cfg), &blk
22
+ @cfg.instance_exec(*args.unshift(@cfg), &blk)
22
23
  elsif args.any?
23
24
  raise ArgumentError, 'arguments given without a block'
24
25
  end
@@ -26,24 +27,22 @@ module GtfsReader
26
27
  end
27
28
 
28
29
  def update_all!
29
- config.sources.each {|name, _| update name }
30
+ config.sources.each { |name, _| update(name) }
30
31
  end
31
32
 
32
33
  def update(name)
33
34
  if config.verbose
34
- update_verbosely name
35
+ update_verbosely(name)
35
36
  else
36
- Log.quiet { update_verbosely name }
37
+ Log.quiet { update_verbosely(name) }
37
38
  end
38
39
  end
39
40
 
40
- private
41
-
42
41
  def update_verbosely(name)
43
42
  source = config.sources[name]
44
43
  raise UnknownSourceError, "No source named '#{name}'" if source.nil?
45
44
 
46
- updater = SourceUpdater.new name, source
45
+ updater = SourceUpdater.new(name, source)
47
46
  begin
48
47
  updater.instance_exec do
49
48
  Log.info { "Updating #{name.to_s.green}".underline }
@@ -67,11 +66,11 @@ module GtfsReader
67
66
  def create_config
68
67
  Configuration.new.tap do |cfg|
69
68
  cfg.instance_exec do
70
- parameter :verbose
71
- parameter :skip_parsing
72
- parameter :return_hashes
73
- block_parameter :sources, Config::Sources
74
- block_parameter :feed_definition, Config::FeedDefinition
69
+ parameter(:verbose)
70
+ parameter(:skip_parsing)
71
+ parameter(:return_hashes)
72
+ block_parameter(:sources, Config::Sources)
73
+ block_parameter(:feed_definition, Config::FeedDefinition)
75
74
  end
76
75
  end
77
76
  end
@@ -1,5 +1,4 @@
1
1
  module GtfsReader
2
-
3
2
  class FileReaderError < StandardError; end
4
3
 
5
4
  class RequiredColumnsMissing < FileReaderError
@@ -1,22 +1,22 @@
1
1
  module GtfsReader
2
2
  # This handler returns each row individually as it is read in from the source.
3
3
  class FeedHandler
4
- def initialize(args=[], &block)
4
+ def initialize(args = [], &block)
5
5
  @read_callbacks = {}
6
- FeedHandlerDsl.new(self).instance_exec *args, &block
6
+ FeedHandlerDsl.new(self).instance_exec(*args, &block)
7
7
  end
8
8
 
9
- #@param filename [String] the name of the file to handle
10
- #@return [Boolean] if this handler can handle the given filename
9
+ # @param filename [String] the name of the file to handle
10
+ # @return [Boolean] if this handler can handle the given filename
11
11
  def handler?(filename)
12
- @read_callbacks.key? filename
12
+ @read_callbacks.key?(filename)
13
13
  end
14
14
 
15
15
  def handle_file(filename, enumerator)
16
- enumerator.each &@read_callbacks[filename]
16
+ enumerator.each(&@read_callbacks[filename])
17
17
  end
18
18
 
19
- def create_read_handler(filename, *args, &block)
19
+ def create_read_handler(filename, *_args, &block)
20
20
  @read_callbacks[filename] = block
21
21
  end
22
22
  end
@@ -27,7 +27,11 @@ module GtfsReader
27
27
  end
28
28
 
29
29
  def method_missing(filename, *args, &block)
30
- @feed_handler.create_read_handler filename, *args, &block
30
+ @feed_handler.create_read_handler(filename, *args, &block)
31
+ end
32
+
33
+ def respond_to_missing?(_name, _include_private = false)
34
+ true
31
35
  end
32
36
  end
33
37
  end
@@ -5,59 +5,59 @@ require_relative 'file_row'
5
5
  module GtfsReader
6
6
  CSV_OPTIONS = { headers: :first_row,
7
7
  return_headers: true,
8
- header_converters: :symbol }
8
+ header_converters: :symbol }.freeze
9
9
 
10
10
  # Iterates over the rows in a single file using a provided definition.
11
- #@see #each
11
+ # @see #each
12
12
  class FileReader
13
13
  include Enumerable
14
14
 
15
15
  attr_reader :definition, :columns, :col_names
16
16
 
17
- #@param data [IO,String] CSV data
18
- #@param definition [FileDefinition] describes the expected columns in this
19
- # file
20
- def initialize(data, definition, opts={})
21
- opts = { parse: true, validate: false, hash: true }.merge opts
17
+ # @param data [IO,String] CSV data
18
+ # @param definition [FileDefinition] describes the expected columns in this
19
+ # file
20
+ def initialize(data, definition, opts = {})
21
+ opts = { parse: true, validate: false, hash: true }.merge(opts)
22
22
 
23
- @csv = CSV.new data, CSV_OPTIONS
24
- @definition, @do_parse, @return_hash =
25
- definition, opts[:parse], opts[:hash]
23
+ @csv = CSV.new(data, CSV_OPTIONS)
24
+ @definition = definition
25
+ @do_parse = opts[:parse]
26
+ @return_hash = opts[:hash]
26
27
  @index = 0
27
28
  @csv_headers = @csv.shift.headers
28
- @columns = find_columns opts[:validate]
29
+ @columns = find_columns(opts[:validate])
29
30
  end
30
31
 
31
32
  def filename
32
33
  @definition.filename
33
34
  end
34
35
 
35
- #@overload each(&blk)
36
- # @yieldparam hash [Hash] a hash of columns to their values in this row
37
- #@overload each
38
- # @return [Enumerator] an {::Enumerator} that iterates of the rows in the
39
- # file
40
- #@see FileRow#to_hash
36
+ # @overload each(&blk)
37
+ # @yieldparam hash [Hash] a hash of columns to their values in this row
38
+ # @overload each
39
+ # @return [Enumerator] an {::Enumerator} that iterates of the rows in the
40
+ # file
41
+ # @see FileRow#to_hash
41
42
  def each
42
- return to_enum :each unless block_given?
43
+ return to_enum(:each) unless block_given?
43
44
 
44
- while row = shift
45
+ while (row = shift)
45
46
  yield(@return_hash ? row.to_hash : row.to_a)
46
47
  end
47
48
  end
48
49
 
49
- #@return [FileRow,nil] the next row from the file, or +nil+ if the end of
50
+ # @return [FileRow,nil] the next row from the file, or +nil+ if the end of
50
51
  # the file has been reached.
51
52
  def shift
52
- if row = @csv.shift
53
- file_row(row).tap { @index += 1 }
54
- end
53
+ row = @csv.shift
54
+ file_row(row).tap { @index += 1 } if row
55
55
  end
56
56
 
57
57
  private
58
58
 
59
59
  def file_row(row)
60
- FileRow.new @index, @col_names, row, @definition, @do_parse
60
+ FileRow.new(@index, @col_names, row, @definition, @do_parse)
61
61
  end
62
62
 
63
63
  # Check the list of headers in the file against the expected columns in
@@ -70,31 +70,31 @@ module GtfsReader
70
70
  unless required.empty?
71
71
  Log.info { "#{prefix} #{'required columns'.magenta}" } if validate
72
72
 
73
- missing = check_columns validate, prefix, required, :green, :red
73
+ missing = check_columns(validate, prefix, required, :green, :red)
74
74
  raise RequiredColumnsMissing, missing if validate && missing.present?
75
75
  end
76
76
 
77
77
  optional = @definition.optional_columns
78
78
  unless optional.empty?
79
79
  Log.info { "#{prefix} #{'optional columns'.cyan}" } if validate
80
- check_columns validate, prefix, optional, :cyan, :light_yellow
80
+ check_columns(validate, prefix, optional, :cyan, :light_yellow)
81
81
  end
82
82
 
83
- cols = @definition.columns.collect( &:name )
84
- headers = @csv_headers.select {|h| cols.include? h }
83
+ cols = @definition.columns.collect(&:name)
84
+ headers = @csv_headers.select { |h| cols.include?(h) }
85
85
 
86
- @col_names ||= @found_columns.map &:name
87
- ::Hash[ *headers.inject([]) {|list,c| list << c << @definition[c] } ]
86
+ @col_names ||= @found_columns.map(&:name)
87
+ ::Hash[*headers.inject([]) { |list, c| list << c << @definition[c] }]
88
88
  end
89
89
 
90
90
  def check_columns(validate, prefix, expected, found_color, missing_color)
91
- check = '✔'.colorize found_color
92
- cross = '✘'.colorize missing_color
91
+ check = '✔'.colorize(found_color)
92
+ cross = '✘'.colorize(missing_color)
93
93
 
94
94
  expected.map do |col|
95
95
  name = col.name
96
96
  missing =
97
- if @csv_headers.include? name
97
+ if @csv_headers.include?(name)
98
98
  @found_columns << col
99
99
  nil
100
100
  else
@@ -112,9 +112,7 @@ module GtfsReader
112
112
  end
113
113
 
114
114
  def column_width
115
- @column_width ||= @definition.columns.collect( &:name ).max do |a, b|
116
- a.length <=> b.length
117
- end.length
115
+ @column_width ||= @definition.columns.collect(&:name).max_by(&:length).length
118
116
  end
119
117
  end
120
118
  end