todo_lint 0.3.1 → 0.4.0

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