pdd 0.20.4 → 0.20.8

Sign up to get free protection for your applications and to get access to all the features.
data/bin/pdd CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- # Copyright (c) 2014-2018 Yegor Bugayenko
2
+ # Copyright (c) 2014-2021 Yegor Bugayenko
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  # of this software and associated documentation files (the 'Software'), to deal
@@ -19,8 +19,11 @@
19
19
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
20
  # SOFTWARE.
21
21
 
22
- STDOUT.sync = true
22
+ $stdout.sync = true
23
23
 
24
+ require 'shellwords'
25
+ require 'English'
26
+ require 'find'
24
27
  require 'slop'
25
28
  require 'nokogiri'
26
29
  require 'rainbow'
@@ -46,6 +49,8 @@ begin
46
49
  o.bool '-v', '--verbose', 'Enable verbose mode (a lot of logging)'
47
50
  o.bool '-q', '--quiet', 'Enable quiet mode (almost no logging)'
48
51
  o.bool '--skip-gitignore', 'Don\'t look into .gitignore for excludes'
52
+ o.bool '--skip-errors', 'Suppress error as warning and skip badly
53
+ formatted puzzles'
49
54
  o.bool '-i', '--version', 'Show current version' do
50
55
  puts PDD::VERSION
51
56
  exit
@@ -53,21 +58,23 @@ begin
53
58
  o.string '-s', '--source', 'Source directory to parse ("." by default)'
54
59
  o.string '-f', '--file', 'File to save XML into'
55
60
  o.array '-e', '--exclude', 'Glob pattern to exclude, e.g. "**/*.jpg"',
56
- default: []
61
+ default: []
62
+ o.array '-n', '--include', 'Glob pattern to include, e.g. "**/*.jpg"',
63
+ default: []
57
64
  o.string '-t', '--format', 'Format of the report (xml|html)'
58
65
  o.array(
59
66
  '-r', '--rule', 'Rule to apply (can be used many times)',
60
67
  delimiter: ';'
61
68
  )
62
69
  end
63
- rescue Slop::Error => ex
64
- raise StandardError, "#{ex.message}, try --help"
70
+ rescue Slop::Error => e
71
+ raise StandardError, "#{e.message}, try --help"
65
72
  end
66
73
 
67
74
  if opts.help?
68
75
  puts opts
69
76
  puts "This is our README to learn more: \
70
- https://github.com/yegor256/pdd/blob/master/README.md"
77
+ https://github.com/cqfn/pdd/blob/master/README.md"
71
78
  exit
72
79
  end
73
80
 
@@ -75,13 +82,17 @@ https://github.com/yegor256/pdd/blob/master/README.md"
75
82
  raise '-f is mandatory when using -v, try --help for more information'
76
83
  end
77
84
 
78
- if opts['skip-gitignore']
79
- raise 'For --skip-gitignore see https://github.com/yegor256/pdd/issues/80'
85
+ if opts['skip-gitignore'] && File.exist?('.gitignore')
86
+ cfg = File.new('.gitignore')
87
+ body = File.read(cfg)
88
+ extra = body.split(/\s+/).map(&:strip)
89
+ opts['skip-gitignore'] = extra
90
+ puts "Found #{body.split(/\n/).length} lines in #{File.absolute_path(cfg)}"
80
91
  end
81
92
 
82
93
  Encoding.default_external = Encoding::UTF_8
83
94
  Encoding.default_internal = Encoding::UTF_8
84
- file = opts.file? ? File.new(opts[:file], 'w') : STDOUT
95
+ file = opts.file? ? File.new(opts[:file], 'w') : $stdout
85
96
  output = PDD::Base.new(opts).xml
86
97
  if opts[:format]
87
98
  if opts[:format] == 'html'
@@ -95,21 +106,21 @@ https://github.com/yegor256/pdd/blob/master/README.md"
95
106
  end
96
107
  end
97
108
  file << output
98
- rescue SystemExit => ex
99
- puts ex.message unless ex.success?
100
- PDD.log.info "Exit code is #{ex.status}"
101
- exit(ex.status)
102
- rescue PDD::Error => ex
103
- puts "#{Rainbow('ERROR').red}: #{ex.message}
109
+ rescue SystemExit => e
110
+ puts e.message unless e.success?
111
+ PDD.log.info "Exit code is #{e.status}"
112
+ exit(e.status)
113
+ rescue PDD::Error => e
114
+ puts "#{Rainbow('ERROR').red}: #{e.message}
104
115
  If you can't understand the cause of this issue or you don't know \
105
116
  how to fix it, please submit a GitHub issue, we will try to help you: \
106
- https://github.com/yegor256/pdd/issues. This tool is still in its beta \
117
+ https://github.com/cqfn/pdd/issues. This tool is still in its beta \
107
118
  version and we will appreciate your feedback. Here is where you can find \
108
- more documentation: https://github.com/yegor256/pdd/blob/master/README.md."
119
+ more documentation: https://github.com/cqfn/pdd/blob/master/README.md."
109
120
  PDD.log.info 'Exit code is 1'
110
121
  exit(1)
111
- rescue StandardError => ex
112
- puts "#{Rainbow('ERROR').red} (#{ex.class.name}): #{ex.message}"
122
+ rescue StandardError => e
123
+ puts "#{Rainbow('ERROR').red} (#{e.class.name}): #{e.message}"
113
124
  PDD.log.info 'Exit code is 255'
114
125
  exit(255)
115
126
  end
@@ -21,22 +21,7 @@ Feature: Catches Broken Puzzles
21
21
  }
22
22
  """
23
23
  When I run pdd it fails with "Space expected"
24
- When I run pdd it fails with "puzzle at line #6"
25
-
26
- Scenario: Throwing exception on another broken puzzle
27
- Given I have a "Sample.java" file with content:
28
- """
29
- public class Main {
30
- /**
31
- * @todo #13 This puzzle has an incorrect format
32
- * because its second line starts with too many spaces
33
- */
34
- public void main(String[] args) {
35
- // later
36
- }
37
- }
38
- """
39
- When I run pdd it fails with "Too many leading spaces"
24
+ When I run pdd it fails with "Sample.java:6"
40
25
 
41
26
  Scenario: Throwing exception on yet another broken puzzle
42
27
  Given I have a "Sample.java" file with content:
data/features/cli.feature CHANGED
@@ -26,7 +26,7 @@ Feature: Command Line Processing
26
26
  """
27
27
  When I run bin/pdd with "-v -s . -f out.xml"
28
28
  Then Exit code is zero
29
- And Stdout contains "Reading ."
29
+ And Stdout contains "Reading from root dir ."
30
30
  And XML file "out.xml" matches "/puzzles[count(puzzle)=1]"
31
31
  And XML file "out.xml" matches "//puzzle[starts-with(body,'Привет, Let')]"
32
32
 
@@ -18,5 +18,5 @@ Feature: HTML output
18
18
  """
19
19
  When I run bin/pdd with "-v -s . -f out.html --format=html"
20
20
  Then Exit code is zero
21
- And Stdout contains "Reading ."
21
+ And Stdout contains "Reading from root dir ."
22
22
  And XML file "out.html" matches "/html/body"
@@ -0,0 +1,21 @@
1
+ Feature: Rake Task
2
+ As a source code writer I want to be able to
3
+ run PDD from Rakefile
4
+ Scenario: PDD can be used in Rakefile
5
+ Given It is Unix
6
+ And I have a "Rakefile" file with content:
7
+ """
8
+ require 'pdd/rake_task'
9
+ PDD::RakeTask.new(:pdd) do |task|
10
+ task.includes = ['a.txt']
11
+ end
12
+ """
13
+ And I have a "a.txt" file with content:
14
+ """
15
+ \x40todo #55 hello!
16
+ """
17
+
18
+ When I run bash with "rake pdd"
19
+ Then Exit code is zero
20
+
21
+
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -64,9 +64,9 @@ When(/^I run pdd it fails with "([^"]*)"$/) do |txt|
64
64
  begin
65
65
  PDD::Base.new(@opts).xml
66
66
  passed = true
67
- rescue PDD::Error => ex
68
- unless ex.message.include?(txt)
69
- raise "PDD failed but exception doesn't contain \"#{txt}\": #{ex.message}"
67
+ rescue PDD::Error => e
68
+ unless e.message.include?(txt)
69
+ raise "PDD failed but exception doesn't contain \"#{txt}\": #{e.message}"
70
70
  end
71
71
  end
72
72
  raise "PDD didn't fail" if passed
@@ -90,6 +90,7 @@ end
90
90
 
91
91
  Then(/^XML file "([^"]+)" matches "([^"]+)"$/) do |file, xpath|
92
92
  raise "File #{file} doesn't exit" unless File.exist?(file)
93
+
93
94
  xml = Nokogiri::XML.parse(File.read(file))
94
95
  xml.remove_namespaces!
95
96
  if xml.xpath(xpath).empty?
@@ -111,6 +112,12 @@ When(/^I run bash with$/) do |text|
111
112
  @exitstatus = $CHILD_STATUS.exitstatus
112
113
  end
113
114
 
115
+ When(/^I run bash with "([^"]*)"$/) do |text|
116
+ FileUtils.copy_entry(@cwd, File.join(@dir, 'pdd'))
117
+ @stdout = `#{text}`
118
+ @exitstatus = $CHILD_STATUS.exitstatus
119
+ end
120
+
114
121
  Given(/^It is Unix$/) do
115
122
  pending if Gem.win_platform?
116
123
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -20,3 +20,4 @@
20
20
 
21
21
  require 'simplecov'
22
22
  require_relative '../../lib/pdd'
23
+ require 'aruba/cucumber'
@@ -16,6 +16,6 @@ Feature: Using .pdd config file
16
16
  """
17
17
  When I run bin/pdd with ""
18
18
  Then Exit code is zero
19
- And Stdout contains "Reading ."
19
+ And Stdout contains "Reading from root dir ."
20
20
  And XML file "out.xml" matches "/puzzles[count(puzzle)=1]"
21
21
 
data/lib/pdd/puzzle.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -0,0 +1,39 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'minitest/autorun'
4
+ require 'nokogiri'
5
+ require 'tmpdir'
6
+ require 'slop'
7
+ require 'pdd'
8
+
9
+ # PDD Rake task
10
+ module PDD
11
+ # Rake task
12
+ class RakeTask < Rake::TaskLib
13
+ attr_accessor :name, :fail_on_error, :includes, :license, :quiet
14
+
15
+ def initialize(*args, &task_block)
16
+ @name = args.shift || :pdd
17
+ @includes = []
18
+ @excludes = []
19
+ @license = nil
20
+ @quiet = false
21
+ desc 'Run PDD' unless ::Rake.application.last_description
22
+ task(name, *args) do |_, task_args|
23
+ RakeFileUtils.send(:verbose, true) do
24
+ yield(*[self, task_args].slice(0, task_block.arity)) if block_given?
25
+ run
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def run
33
+ # @todo #125:30m need to implement this method.
34
+ # For now, it's just a task,
35
+ # that prints a simple Running pdd... message to user
36
+ puts 'Running pdd...' unless @quiet
37
+ end
38
+ end
39
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -35,6 +35,7 @@ module PDD
35
35
  .group_by { |p| p.xpath('body/text()').to_s }
36
36
  .map do |_, puzzles|
37
37
  next nil if puzzles.count <= @max
38
+
38
39
  "there are #{puzzles.count} duplicate(s) of the same puzzle: " +
39
40
  puzzles.map do |p|
40
41
  "#{p.xpath('file/text()')}:#{p.xpath('lines/text()')}"
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -34,6 +34,7 @@ module PDD
34
34
  @xml.xpath('//puzzle').map do |p|
35
35
  role = p.xpath('role/text()').to_s
36
36
  next nil if @roles.include?(role)
37
+
37
38
  "puzzle #{p.xpath('file/text()')}:#{p.xpath('lines/text()')}" +
38
39
  if role.empty?
39
40
  " doesn't define any role"\
data/lib/pdd/rule/text.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -34,6 +34,7 @@ module PDD
34
34
  @xml.xpath('//puzzle').map do |p|
35
35
  words = p.xpath('body/text()').to_s.split.size
36
36
  next nil if words >= @min
37
+
37
38
  "Puzzle #{p.xpath('file/text()')}:#{p.xpath('lines/text()')}"\
38
39
  " has a very short description of just #{words} words while"\
39
40
  " a minimum of #{@min} is required"
data/lib/pdd/source.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -26,6 +26,7 @@ require_relative '../pdd'
26
26
  require_relative '../pdd/puzzle'
27
27
 
28
28
  module PDD
29
+ MARKERS = ["\x40todo", 'TODO:?'].freeze
29
30
  # Source.
30
31
  class Source
31
32
  # Ctor.
@@ -36,22 +37,27 @@ module PDD
36
37
  @path = path
37
38
  end
38
39
 
40
+ def match_markers(line)
41
+ MARKERS.map do |mkr|
42
+ %r{(.*(?:^|\s))#{mkr}\s+#([\w\-.:/]+)\s+(.+)}.match(line)
43
+ end.compact
44
+ end
45
+
39
46
  # Fetch all puzzles.
40
47
  def puzzles
41
- PDD.log.info "Reading #{@path}..."
48
+ PDD.log.info "Reading #{@path} ..."
42
49
  puzzles = []
43
50
  lines = File.readlines(@file, encoding: 'UTF-8')
44
51
  lines.each_with_index do |line, idx|
45
52
  begin
46
53
  check_rules(line)
47
- %r{(.*(?:^|\s))\x40todo\s+#([\w\-\.:/]+)\s+(.+)}.match(line) do |m|
48
- puzzles << puzzle(lines.drop(idx + 1), m, idx)
49
- end
50
- %r{(.*(?:^|\s))TODO:?\s+#([\w\-\.:/]+)\s+(.+)}.match(line) do |m|
54
+ match_markers(line).each do |m|
51
55
  puzzles << puzzle(lines.drop(idx + 1), m, idx)
52
56
  end
53
57
  rescue Error, ArgumentError => ex
54
- raise Error, "puzzle at line ##{idx + 1}; #{ex.message}"
58
+ message = "#{@path}:#{idx + 1} #{ex.message}"
59
+ raise Error, message unless PDD.opts && PDD.opts['skip-errors']
60
+ PDD.log.warn message
55
61
  end
56
62
  end
57
63
  puzzles
@@ -61,19 +67,19 @@ module PDD
61
67
 
62
68
  def get_no_leading_space_error(todo)
63
69
  "#{todo} must have a leading space to become \
64
- a puzzle, as this page explains: https://github.com/yegor256/pdd#how-to-format"
70
+ a puzzle, as this page explains: https://github.com/cqfn/pdd#how-to-format"
65
71
  end
66
72
 
67
73
  def get_no_puzzle_marker_error(todo)
68
74
  "#{todo} found, but puzzle can't be parsed, \
69
75
  most probably because #{todo} is not followed by a puzzle marker, \
70
- as this page explains: https://github.com/yegor256/pdd#how-to-format"
76
+ as this page explains: https://github.com/cqfn/pdd#how-to-format"
71
77
  end
72
78
 
73
79
  def get_space_after_hash_error(todo)
74
80
  "#{todo} found, but there is an unexpected space \
75
81
  after the hash sign, it should not be there, \
76
- see https://github.com/yegor256/pdd#how-to-format"
82
+ see https://github.com/cqfn/pdd#how-to-format"
77
83
  end
78
84
 
79
85
  def check_rules(line)
@@ -100,7 +106,7 @@ see https://github.com/yegor256/pdd#how-to-format"
100
106
  # Fetch puzzle
101
107
  def puzzle(lines, match, idx)
102
108
  tail = tail(lines, match[1], idx)
103
- body = (match[3] + ' ' + tail.join(' ')).gsub(/\s+/, ' ').strip
109
+ body = "#{match[3]} #{tail.join(' ')}".gsub(/\s+/, ' ').strip
104
110
  body = body.chomp('*/-->').strip
105
111
  marker = marker(match[2])
106
112
  Puzzle.new(
@@ -115,11 +121,11 @@ see https://github.com/yegor256/pdd#how-to-format"
115
121
 
116
122
  # Parse a marker.
117
123
  def marker(text)
118
- re = %r{([\w\-\.]+)(?::(\d+)(?:(m|h)[a-z]*)?)?(?:/([A-Z]+))?}
124
+ re = %r{([\w\-.]+)(?::(\d+)(?:(m|h)[a-z]*)?)?(?:/([A-Z]+))?}
119
125
  match = re.match(text)
120
126
  if match.nil?
121
127
  raise "Invalid puzzle marker \"#{text}\", most probably formatted \
122
- against the rules explained here: https://github.com/yegor256/pdd#how-to-format"
128
+ against the rules explained here: https://github.com/cqfn/pdd#how-to-format"
123
129
  end
124
130
  {
125
131
  ticket: match[1],
@@ -138,21 +144,15 @@ against the rules explained here: https://github.com/yegor256/pdd#how-to-format"
138
144
  # Fetch puzzle tail (all lines after the first one)
139
145
  def tail(lines, prefix, start)
140
146
  lines
141
- .take_while { |t| t.start_with?(prefix) }
147
+ .take_while { |t| match_markers(t).none? && t.start_with?(prefix) }
142
148
  .map { |t| t[prefix.length, t.length] }
143
149
  .take_while { |t| t =~ /^[ a-zA-Z0-9]/ }
144
150
  .each_with_index do |t, i|
145
- next if t.start_with?(' ')
146
- raise Error, "Space expected at #{start + i + 2}:#{prefix.length}; \
151
+ next if t.start_with?(' ')
152
+
153
+ raise Error, "Space expected at #{start + i + 2}:#{prefix.length}; \
147
154
  make sure all lines in the puzzle body have a single leading space."
148
- end
149
- .each_with_index do |t, i|
150
- next if t !~ /^\s{2,}/
151
- raise Error, "Too many leading spaces \
152
- at #{start + i + 2}:#{prefix.length}; \
153
- make sure all lines that include the puzzle body start \
154
- at position ##{prefix.length + 1}."
155
- end
155
+ end
156
156
  .map { |t| t[1, t.length] }
157
157
  end
158
158
 
@@ -168,28 +168,29 @@ at position ##{prefix.length + 1}."
168
168
  if `#{git} rev-parse --is-inside-work-tree 2>/dev/null`.strip == 'true'
169
169
  cmd = "#{git} blame -L #{pos},#{pos} --porcelain #{name}"
170
170
  add_github_login(Hash[
171
- `#{cmd}`.split("\n").map do |line|
172
- if line =~ /^author /
173
- [:author, line.sub(/^author /, '')]
174
- elsif line =~ /^author-mail [^@]+@[^\.]+\..+/
175
- [:email, line.sub(/^author-mail <(.+)>$/, '\1')]
176
- elsif line =~ /^author-time /
177
- [
178
- :time,
179
- Time.at(
180
- line.sub(/^author-time ([0-9]+)$/, '\1').to_i
181
- ).utc.iso8601
182
- ]
183
- end
184
- end.compact
185
- ])
171
+ `#{cmd}`.split("\n").map do |line|
172
+ case line
173
+ when /^author /
174
+ [:author, line.sub(/^author /, '')]
175
+ when /^author-mail [^@]+@[^.]+\..+/
176
+ [:email, line.sub(/^author-mail <(.+)>$/, '\1')]
177
+ when /^author-time /
178
+ [
179
+ :time,
180
+ Time.at(
181
+ line.sub(/^author-time ([0-9]+)$/, '\1').to_i
182
+ ).utc.iso8601
183
+ ]
184
+ end
185
+ end.compact
186
+ ])
186
187
  else
187
188
  {}
188
189
  end
189
190
  end
190
191
 
191
192
  def add_github_login(info)
192
- login = find_github_login(info[:email])
193
+ login = find_github_login(info)
193
194
  info[:author] = "@#{login}" unless login.empty?
194
195
  info
195
196
  end
@@ -204,15 +205,26 @@ at position ##{prefix.length + 1}."
204
205
  JSON.parse res.body
205
206
  end
206
207
 
207
- def find_github_user(email)
208
- base_uri = 'https://api.github.com/search/users'
209
- query = base_uri + "?q=#{email}+in:email&perpage=1"
208
+ def find_github_user(info)
209
+ email, author = info.values_at(:email, :author)
210
+ # if email is not defined, changes have not been committed
211
+ return if email.nil?
212
+
213
+ base_uri = 'https://api.github.com/search/users?per_page=1'
214
+ query = base_uri + "&q=#{email}+in:email"
210
215
  json = get_json query
216
+ # find user by name instead since users can make github email private
217
+ unless json['total_count'].positive?
218
+ return if author.nil?
219
+
220
+ query = base_uri + "&q=#{author}+in:fullname"
221
+ json = get_json query
222
+ end
211
223
  json['items'].first
212
224
  end
213
225
 
214
- def find_github_login(email)
215
- user = find_github_user email
226
+ def find_github_login(info)
227
+ user = find_github_user info
216
228
  user['login']
217
229
  rescue StandardError
218
230
  ''
@@ -232,8 +244,8 @@ at position ##{prefix.length + 1}."
232
244
  # Fetch all puzzles.
233
245
  def puzzles
234
246
  @source.puzzles
235
- rescue Error => ex
236
- raise Error, "#{@file}; #{ex.message}"
247
+ rescue Error => e
248
+ raise Error, "#{@file}; #{e.message}"
237
249
  end
238
250
  end
239
251
  end
data/lib/pdd/sources.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -18,41 +18,61 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
19
  # SOFTWARE.
20
20
 
21
+ require 'rainbow'
21
22
  require 'shellwords'
22
23
  require 'English'
23
24
  require_relative 'source'
25
+ require_relative '../../utils/glob'
24
26
 
25
27
  module PDD
26
28
  # Code base abstraction
27
29
  class Sources
28
30
  # Ctor.
29
31
  # +dir+:: Directory with source code files
30
- def initialize(dir, ptns = [])
32
+ def initialize(dir)
31
33
  @dir = File.absolute_path(dir)
32
- @exclude = ptns + ['.git/**/*']
34
+ @exclude = ['.git/**/*']
35
+ @include = []
33
36
  end
34
37
 
35
38
  # Fetch all sources.
36
39
  def fetch
40
+ exclude_paths = @exclude.map do |ptn|
41
+ Glob.new(File.join(@dir, ptn)).to_regexp
42
+ end
37
43
  files = Dir.glob(
38
44
  File.join(@dir, '**/*'), File::FNM_DOTMATCH
39
- ).reject { |f| File.directory?(f) }
40
- excluded = 0
41
- @exclude.each do |ptn|
42
- Dir.glob(File.join(@dir, ptn), File::FNM_DOTMATCH) do |f|
43
- files.delete_if { |i| i == f }
44
- excluded += 1
45
- end
45
+ ).reject do |f|
46
+ File.directory?(f) || exclude_paths.any? { |ptn| f.match(ptn) }
46
47
  end
47
- PDD.log.info "#{files.size} file(s) found, #{excluded} excluded"
48
+ files += Dir.glob(
49
+ @include.map { |ptn| File.join(@dir, ptn) }
50
+ ).reject { |f| File.directory?(f) }
51
+ files = files.uniq # remove duplicates
48
52
  files.reject { |f| binary?(f) }.map do |file|
49
53
  path = file[@dir.length + 1, file.length]
50
54
  VerboseSource.new(path, Source.new(file, path))
51
55
  end
52
56
  end
53
57
 
54
- def exclude(ptn)
55
- Sources.new(@dir, @exclude.push(ptn))
58
+ def exclude(paths)
59
+ paths = paths.nil? ? [] : paths
60
+ paths = paths.is_a?(Array) ? paths : [paths]
61
+ @exclude.push(*paths)
62
+ paths&.each do |path|
63
+ PDD.log.info "#{Rainbow('Excluding').orange} #{path}"
64
+ end
65
+ self
66
+ end
67
+
68
+ def include(paths)
69
+ paths = paths.nil? ? [] : paths
70
+ paths = paths.is_a?(Array) ? paths : [paths]
71
+ @include.push(*paths)
72
+ paths&.each do |path|
73
+ PDD.log.info "#{Rainbow('Including').blue} #{path}"
74
+ end
75
+ self
56
76
  end
57
77
 
58
78
  private
@@ -63,6 +83,7 @@ module PDD
63
83
  # `test_ignores_binary_files` in `test_sources.rb`.
64
84
  def binary?(file)
65
85
  return false if Gem.win_platform?
86
+
66
87
  `grep -qI '.' #{Shellwords.escape(file)}`
67
88
  if $CHILD_STATUS.success?
68
89
  false
data/lib/pdd/version.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2014-2018 Yegor Bugayenko
1
+ # Copyright (c) 2014-2021 Yegor Bugayenko
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the 'Software'), to deal
@@ -20,8 +20,8 @@
20
20
 
21
21
  # PDD main module.
22
22
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
23
- # Copyright:: Copyright (c) 2014-2018 Yegor Bugayenko
23
+ # Copyright:: Copyright (c) 2014-2021 Yegor Bugayenko
24
24
  # License:: MIT
25
25
  module PDD
26
- VERSION = '0.20.4'.freeze
26
+ VERSION = '0.20.8'.freeze
27
27
  end