gtfs_reader 1.2.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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