pdd 0.20.4 → 0.20.8

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.
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