todo_lint 0.3.1 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9468fdec36cf8fd3dd9af14c1969c31181fed8ff
4
- data.tar.gz: 665b4a107a8dc3202edfe9125ca7f7787446da10
3
+ metadata.gz: 8dd171321dda2f34a965d06f4db8a014fbd6972c
4
+ data.tar.gz: 2790af0c8f0d386ede48b57d7122bbefd141224f
5
5
  SHA512:
6
- metadata.gz: 62036c3c37d81615f93f88699de46fbf31bad91f831ff7d961f8527c3fea201fef25a45f21c5a39bf52aadd17e9bf7eb8691bb5faeda3ae00908506f676ceb86
7
- data.tar.gz: e5683ed19b617972f455b36562feec1297eb9c5f7250f66b1951bf8c6cbaa5ffd9b48ce0162880ac134aea2203b08596ac97e796ddb6531ca99915be13fa3898
6
+ metadata.gz: 3ecf76a06b3f3da22f3d39b9152e4d99aa6fbbe95e7fb377c2211846fba7a55b35f7c8e015daf21cd60cbfd30429fb614b83168cc81c233a0ef3b2752eaffc87
7
+ data.tar.gz: f59058158b7f2e2bd76be1253106b779987034c947e58fb02ddb09ed3763c0e23d985e0c7d675e06f6faf280e74cf20c6f5ccba38cba1a1b62260a1e82730ca6
@@ -18,3 +18,7 @@ Metrics/MethodLength:
18
18
  Exclude:
19
19
  - lib/todo_lint/cli.rb
20
20
  - lib/todo_lint/options.rb
21
+
22
+ Metrics/ModuleLength:
23
+ Exclude:
24
+ - spec/**/*.rb
@@ -64,7 +64,7 @@ module TodoLint
64
64
  files = load_files(finder)
65
65
  files_count = files.count
66
66
  reports = files.map do |file|
67
- Todo.within(File.open(file)).map do |todo|
67
+ Todo.within(File.open(file), :config => @options).map do |todo|
68
68
  reporter = Reporter.new(todo,
69
69
  :judge => Judge.new(todo))
70
70
  reporter.report.tap do |report|
@@ -96,11 +96,12 @@ module TodoLint
96
96
  finder = FileFinder.new(path, options)
97
97
  files = load_files(finder)
98
98
  files.each do |file|
99
- todos += Todo.within(File.open(file))
99
+ todos += Todo.within(File.open(file), :config => @options)
100
100
  end
101
101
  todos.sort.each.with_index do |todo, num|
102
102
  due_date = if todo.due_date
103
- Rainbow(" (due #{todo.due_date.to_date})")
103
+ tag_context = " via #{todo.tag}" if todo.tag?
104
+ Rainbow(" (due #{todo.due_date.to_date}#{tag_context})")
104
105
  .public_send(todo.due_date.overdue? ? :red : :blue)
105
106
  else
106
107
  Rainbow(" (no due date)").red
@@ -5,12 +5,13 @@ module TodoLint
5
5
  class ConfigFile
6
6
  # Parses the config file and loads the options
7
7
  # @api public
8
- # @example ConfigFile.new.parse('.todo-lint.yml')
8
+ # @example ConfigFile.new.read_config_file('.todo-lint.yml')
9
9
  # @return [Hash] parsed file-options
10
10
  def read_config_file(file)
11
11
  @config_hash = YAML.load_file(file)
12
12
  @starting_path = File.expand_path(File.split(file).first)
13
13
  @config_options = {}
14
+ load_tags
14
15
  load_file_exclusions
15
16
  load_extension_inclusions
16
17
  config_options
@@ -51,5 +52,21 @@ module TodoLint
51
52
  return unless config_hash["Extensions"]
52
53
  config_options[:extensions] = config_hash["Extensions"]
53
54
  end
55
+
56
+ # Load the tags from the configuration file as DueDates
57
+ #
58
+ # @return is irrelevant
59
+ # @api private
60
+ def load_tags
61
+ config_options[:tags] = {}
62
+ return unless config_hash["Tags"]
63
+ config_hash["Tags"].each do |tag, due_date|
64
+ unless due_date.is_a? Date
65
+ raise ArgumentError, "#{due_date} is not a date"
66
+ end
67
+
68
+ config_options[:tags]["##{tag}"] = DueDate.new(due_date)
69
+ end
70
+ end
54
71
  end
55
72
  end
@@ -3,7 +3,18 @@ require "date"
3
3
  module TodoLint
4
4
  # When is this todo actually due? When ought we be reminded of this one?
5
5
  class DueDate
6
- ANNOTATION_PATTERN = /\((\d{4})-(\d{2})-(\d{2})/
6
+ DATE_PATTERN = /(\d{4})-(\d{2})-(\d{2})/
7
+ ANNOTATION_PATTERN = /\(#{DATE_PATTERN}\)/
8
+
9
+ # Parse the date from the todo_lint configuration file
10
+ # @example
11
+ # DueDate.from_config_file("2015-04-14")
12
+ # @return [DueDate] if the annotation is formatted properly
13
+ # @raise [ArgumentError] if the annotation is not formatted properly
14
+ # @api public
15
+ def self.from_config_file(date)
16
+ from_pattern(date, DATE_PATTERN)
17
+ end
7
18
 
8
19
  # Parse the date from the todo comment's due date annotation
9
20
  # @example
@@ -11,14 +22,23 @@ module TodoLint
11
22
  # @return [DueDate] if the annotation is formatted properly
12
23
  # @raise [ArgumentError] if the annotation is not formatted properly
13
24
  # @api public
14
- def self.from_annotation(annotation)
15
- if (match = ANNOTATION_PATTERN.match(annotation))
25
+ def self.from_annotation(date)
26
+ from_pattern(date, ANNOTATION_PATTERN)
27
+ end
28
+
29
+ # Helper method for extracting dates from patterns
30
+ # @return [DueDate] if pattern matches
31
+ # @raise [ArgumentError] if pattern does not match
32
+ # @api private
33
+ def self.from_pattern(date, pattern)
34
+ if (match = pattern.match(date))
16
35
  DueDate.new(Date.new(match[1].to_i, match[2].to_i, match[3].to_i))
17
36
  else
18
- msg = "not a properly formatted annotation: #{annotation.inspect}"
37
+ msg = "not a properly formatted date: #{date.inspect}"
19
38
  raise ArgumentError, msg
20
39
  end
21
40
  end
41
+ private_class_method :from_pattern
22
42
 
23
43
  # The actual date object when something is due
24
44
  # @example
@@ -15,10 +15,30 @@ module TodoLint
15
15
  # Judge.new(todo)
16
16
  # @api public
17
17
  def initialize(todo)
18
- if todo.annotated?
19
- @charge = "Overdue due date" if todo.due_date.overdue?
20
- else
21
- @charge = "Missing due date annotation"
18
+ @todo = todo
19
+ @charge = make_charge
20
+ end
21
+
22
+ private
23
+
24
+ # Which todo is being judged?
25
+ #
26
+ # @return [Todo]
27
+ # @api private
28
+ attr_reader :todo
29
+
30
+ # What is the problem with this todo?
31
+ #
32
+ # @return [String] if there's a problem
33
+ # @return [NilClass] if no charge needed
34
+ # @api private
35
+ def make_charge
36
+ if !todo.annotated?
37
+ "Missing due date annotation"
38
+ elsif todo.due_date.overdue? && todo.tag?
39
+ "Overdue due date #{todo.due_date.to_date} via tag"
40
+ elsif todo.due_date.overdue?
41
+ "Overdue due date"
22
42
  end
23
43
  end
24
44
  end
@@ -5,8 +5,9 @@ module TodoLint
5
5
  PATTERN = /
6
6
  (?<flag> TODO ){0}
7
7
  (?<due_date> \(\d{4}-\d{2}-\d{2}\)){0}
8
+ (?<tag>\#\w+){0}
8
9
  (?<task>.+){0}
9
- \g<flag>\g<due_date>?: \g<task>
10
+ \g<flag>(?:\g<due_date>|(?:\(\g<tag>\)))?: \g<task>
10
11
  /x
11
12
 
12
13
  # Search a file for all of the todo/fixme/etc comments within it
@@ -14,11 +15,15 @@ module TodoLint
14
15
  # Todo.within(File.open("app.rb"))
15
16
  # @api public
16
17
  # @return [Array<Todo>]
17
- def self.within(file)
18
+ def self.within(file, config: RequiredArg.new(:config))
18
19
  file.each_line.with_index.map do |line, line_number|
19
- if present_in?(line)
20
- new(line, :line_number => line_number + 1, :path => file.path)
21
- end
20
+ next unless present_in?(line)
21
+ new(
22
+ line,
23
+ :line_number => line_number + 1,
24
+ :path => file.path,
25
+ :config => config
26
+ )
22
27
  end.compact
23
28
  end
24
29
 
@@ -60,11 +65,13 @@ module TodoLint
60
65
  # @api public
61
66
  def initialize(line,
62
67
  line_number: RequiredArg.new(:line_number),
63
- path: RequiredArg.new(:path))
68
+ path: RequiredArg.new(:path),
69
+ config: RequiredArg.new(:config))
64
70
  absent_todo!(line) unless self.class.present_in?(line)
65
71
  @line = line
66
72
  @line_number = line_number
67
73
  @path = path
74
+ @config = config
68
75
  end
69
76
 
70
77
  # Was this todo annotated with a due date?
@@ -77,7 +84,7 @@ module TodoLint
77
84
  # @return [Boolean]
78
85
  # @api public
79
86
  def annotated?
80
- !match[:due_date].nil?
87
+ !match[:due_date].nil? || !match[:tag].nil?
81
88
  end
82
89
 
83
90
  # What is the actual task associated with this todo?
@@ -101,7 +108,13 @@ module TodoLint
101
108
  # @return [NilClass] if there is no due date
102
109
  # @api public
103
110
  def due_date
104
- DueDate.from_annotation(match[:due_date]) if annotated?
111
+ return unless annotated?
112
+ return @due_date if defined?(@due_date)
113
+ @due_date = if match[:due_date]
114
+ DueDate.from_annotation(match[:due_date])
115
+ elsif match[:tag]
116
+ lookup_tag_due_date
117
+ end
105
118
  end
106
119
 
107
120
  # What did the developer write to get our attention?
@@ -122,6 +135,29 @@ module TodoLint
122
135
  (line =~ PATTERN) + 1
123
136
  end
124
137
 
138
+ # Was this todo using a tag (as opposed to a direct due date)?
139
+ #
140
+ # @example
141
+ # todo.tag? #=> true
142
+ # @return [Boolean]
143
+ # @api public
144
+ def tag?
145
+ !match[:tag].nil?
146
+ end
147
+
148
+ # What tag does this todo use?
149
+ #
150
+ # @example
151
+ # todo.tag #=> "#shipit"
152
+ # todo.tag #=> nil
153
+ #
154
+ # @return [String] if the Todo has a tag
155
+ # @return [NilClass] if the Todo has no tag
156
+ # @api public
157
+ def tag
158
+ match[:tag]
159
+ end
160
+
125
161
  # Which todo is due sooner?
126
162
  #
127
163
  # @example
@@ -161,6 +197,12 @@ module TodoLint
161
197
 
162
198
  private
163
199
 
200
+ # What is the configuration for this code base's todo_lint setup?
201
+ #
202
+ # @return [Hash]
203
+ # @api private
204
+ attr_reader :config
205
+
164
206
  # Analyze the line to help identify when the todo is due
165
207
  # @return [MatchData]
166
208
  # @api private
@@ -174,5 +216,16 @@ module TodoLint
174
216
  def absent_todo!(line)
175
217
  raise ArgumentError, "Not even a todo: #{line.inspect}"
176
218
  end
219
+
220
+ # A tag was referenced, so let's see when that's due
221
+ # @return [DueDate]
222
+ # @raise [KeyError] if the tag does not reference a due date in the config
223
+ # @api private
224
+ def lookup_tag_due_date
225
+ config.fetch(:tags).fetch(match[:tag])
226
+ rescue KeyError
227
+ msg = "#{match[:tag]} tag not defined in config file"
228
+ raise KeyError, msg
229
+ end
177
230
  end
178
231
  end
@@ -2,5 +2,5 @@
2
2
  # it's in its own file so it can be required in the gemspec without requiring
3
3
  # everything else as well
4
4
  module TodoLint
5
- VERSION = "0.3.1".freeze
5
+ VERSION = "0.4.0".freeze
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: todo_lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max Jacobson