class PDD::Source
Public Class Methods
new(file, path)
click to toggle source
Ctor.
file
-
Absolute file name with source code
path
-
Path to show (without full file name)
# File lib/pdd/source.rb, line 35 def initialize(file, path) @file = file @path = path end
Public Instance Methods
match_markers(line)
click to toggle source
# File lib/pdd/source.rb, line 40 def match_markers(line) MARKERS.map do |mkr| %r{(.*(?:^|\s))#{mkr}\s+#([\w\-\.:/]+)\s+(.+)}.match(line) end.compact end
puzzles()
click to toggle source
Fetch all puzzles.
# File lib/pdd/source.rb, line 47 def puzzles PDD.log.info "Reading #{@path} ..." puzzles = [] lines = File.readlines(@file, encoding: 'UTF-8') lines.each_with_index do |line, idx| begin check_rules(line) match_markers(line).each do |m| puzzles << puzzle(lines.drop(idx + 1), m, idx) end rescue Error, ArgumentError => ex message = "#{@path}:#{idx + 1} #{ex.message}" raise Error, message unless PDD.opts && PDD.opts['skip-errors'] PDD.log.warn message end end puzzles end
Private Instance Methods
add_github_login(info)
click to toggle source
# File lib/pdd/source.rb, line 190 def add_github_login(info) login = find_github_login(info) info[:author] = "@#{login}" unless login.empty? info end
check_rules(line)
click to toggle source
# File lib/pdd/source.rb, line 85 def check_rules(line) /[^\s]\x40todo/.match(line) do |_| raise Error, get_no_leading_space_error("\x40todo") end /\x40todo(?!\s+#)/.match(line) do |_| raise Error, get_no_puzzle_marker_error("\x40todo") end /\x40todo\s+#\s/.match(line) do |_| raise Error, get_space_after_hash_error("\x40todo") end /[^\s]TODO:?/.match(line) do |_| raise Error, get_no_leading_space_error('TODO') end /TODO(?!:?\s+#)/.match(line) do |_| raise Error, get_no_puzzle_marker_error('TODO') end /TODO:?\s+#\s/.match(line) do |_| raise Error, get_space_after_hash_error('TODO') end end
find_github_login(info)
click to toggle source
# File lib/pdd/source.rb, line 222 def find_github_login(info) user = find_github_user info user['login'] rescue StandardError '' end
find_github_user(info)
click to toggle source
# File lib/pdd/source.rb, line 206 def find_github_user(info) email, author = info.values_at(:email, :author) # if email is not defined, changes have not been committed return if email.nil? base_uri = 'https://api.github.com/search/users?per_page=1' query = base_uri + "&q=#{email}+in:email" json = get_json query # find user by name instead since users can make github email private unless json['total_count'].positive? return if author.nil? query = base_uri + "&q=#{author}+in:fullname" json = get_json query end json['items'].first end
get_json(query)
click to toggle source
# File lib/pdd/source.rb, line 196 def get_json(query) uri = URI.parse(query) http = Net::HTTP.new(uri.hostname, uri.port) http.use_ssl = uri.scheme == 'https' req = Net::HTTP::Get.new(uri.request_uri) req.set_content_type('application/json') res = http.request(req) JSON.parse res.body end
get_no_leading_space_error(todo)
click to toggle source
# File lib/pdd/source.rb, line 68 def get_no_leading_space_error(todo) "#{todo} must have a leading space to become \ a puzzle, as this page explains: https://github.com/cqfn/pdd#how-to-format" end
get_no_puzzle_marker_error(todo)
click to toggle source
# File lib/pdd/source.rb, line 73 def get_no_puzzle_marker_error(todo) "#{todo} found, but puzzle can't be parsed, \ most probably because #{todo} is not followed by a puzzle marker, \ as this page explains: https://github.com/cqfn/pdd#how-to-format" end
get_space_after_hash_error(todo)
click to toggle source
# File lib/pdd/source.rb, line 79 def get_space_after_hash_error(todo) "#{todo} found, but there is an unexpected space \ after the hash sign, it should not be there, \ see https://github.com/cqfn/pdd#how-to-format" end
git(pos)
click to toggle source
Git information at the line
# File lib/pdd/source.rb, line 163 def git(pos) dir = Shellwords.escape(File.dirname(@file)) name = Shellwords.escape(File.basename(@file)) git = "cd #{dir} && git" if `#{git} rev-parse --is-inside-work-tree 2>/dev/null`.strip == 'true' cmd = "#{git} blame -L #{pos},#{pos} --porcelain #{name}" add_github_login(Hash[ `#{cmd}`.split("\n").map do |line| if line =~ /^author / [:author, line.sub(/^author /, '')] elsif line =~ /^author-mail [^@]+@[^\.]+\..+/ [:email, line.sub(/^author-mail <(.+)>$/, '\1')] elsif line =~ /^author-time / [ :time, Time.at( line.sub(/^author-time ([0-9]+)$/, '\1').to_i ).utc.iso8601 ] end end.compact ]) else {} end end
marker(text)
click to toggle source
Parse a marker.
# File lib/pdd/source.rb, line 123 def marker(text) re = %r{([\w\-\.]+)(?::(\d+)(?:(m|h)[a-z]*)?)?(?:/([A-Z]+))?} match = re.match(text) if match.nil? raise "Invalid puzzle marker \"#{text}\", most probably formatted \ against the rules explained here: https://github.com/cqfn/pdd#how-to-format" end { ticket: match[1], estimate: minutes(match[2], match[3]), role: match[4].nil? ? 'DEV' : match[4] } end
minutes(num, units)
click to toggle source
Parse minutes.
# File lib/pdd/source.rb, line 138 def minutes(num, units) min = num.nil? ? 0 : Integer(num) min *= 60 if !units.nil? && units.start_with?('h') min end
puzzle(lines, match, idx)
click to toggle source
Fetch puzzle
# File lib/pdd/source.rb, line 107 def puzzle(lines, match, idx) tail = tail(lines, match[1], idx) body = (match[3] + ' ' + tail.join(' ')).gsub(/\s+/, ' ').strip body = body.chomp('*/-->').strip marker = marker(match[2]) Puzzle.new( marker.merge( id: "#{marker[:ticket]}-#{Digest::MD5.hexdigest(body)[0..7]}", lines: "#{idx + 1}-#{idx + tail.size + 1}", body: body, file: @path ).merge(git(idx + 1)) ) end
tail(lines, prefix, start)
click to toggle source
Fetch puzzle tail (all lines after the first one)
# File lib/pdd/source.rb, line 145 def tail(lines, prefix, start) lines .take_while { |t| match_markers(t).none? && t.start_with?(prefix) } .map { |t| t[prefix.length, t.length] } .take_while { |t| t =~ /^[ a-zA-Z0-9]/ } .each_with_index do |t, i| next if t.start_with?(' ') raise Error, "Space expected at #{start + i + 2}:#{prefix.length}; \ make sure all lines in the puzzle body have a single leading space." end .map { |t| t[1, t.length] } end