class ASF::ICLA
Provide access to the contents of iclas.txt.
N.B. only id and name should be considered public form and claRef may contain details of the legal name beyond that in the public name
Constants
- OFFICERS
location of a working copy of the officers directory in
SVN
- SOURCE
location of the iclas.txt file; may be
nil
if not found.- SOURCE_URL
Attributes
cla name or SVN
revision info; extracted from the form
email address from the ICLA
(may include multiple values, separated by space or comma)
lists the name of the form on file; includes claRef information
availid of the ICLA
, or notinavail
if no id has been issued
legal name for the individual; should not be shared
public name for the individual; should match LDAP
Public Class Methods
find number of matches in target array
# File lib/whimsy/asf/icla.rb, line 131 def self.array_count_match(source, target) count = 0 source.each {|src| count += 1 if target.include? src} count end
is the id available? See also ASF::Mail.taken?
# File lib/whimsy/asf/icla.rb, line 293 def self.available?(id) return !self.taken?(id) end
list of all ids
# File lib/whimsy/asf/icla.rb, line 161 def self.availids return [] unless SOURCE refresh return @@availids if @@availids availids = [] each {|icla| availids << icla.id unless icla.id == 'notinavail'} @@availids = availids end
list of reserved availids
# File lib/whimsy/asf/icla.rb, line 270 def self.availids_reserved return @@availids_reserved if @@availids_reserved reserved = File.read(File.join(ASF::SVN['officers'], 'reserved-ids.yml')).scan(/^- (\S+)/).flatten.uniq # Add in badrcptto reserved += self.badmails @@availids_reserved = reserved.uniq end
list of all availids that are are taken or reserved See also ASF::Mail.taken?
# File lib/whimsy/asf/icla.rb, line 280 def self.availids_taken self.availids_reserved + self.availids end
list of mails rejected by badrcptto and badrcptto_patterns Not intended for external use
# File lib/whimsy/asf/icla.rb, line 241 def self.badmails qmc = ASF::SVN['qmail_control'] # non-patterns brt = File.join(qmc, 'badrcptto') badmails = File.read(brt).scan(/^(\w.+)@apache\.org\s*$/).flatten # now parse patterns brtpat = File.join(qmc, 'badrcptto_patterns') File.read(brtpat).each_line do |line| m = line.match(/^\^(\w.+)\\@/) if m badmails << m[1] next end # ^(abc|def|ghi)(jkl|mno|pqr)\@ m = line.match(/^\^\(([|\w]+)\)\(([|\w]+)\)\\@/) if m m[1].split('|').each do |one| m[2].split('|').each do |two| badmails << "#{one}#{two}" end end else Wunderbar.warn "Error parsing #{brtpat} : could not match #{line}" end end badmails.uniq end
iterate over all of the ICLAs
# File lib/whimsy/asf/icla.rb, line 171 def self.each(&block) refresh if @@icla_index and not @@icla_index.empty? @@icla_index.each(&block) elsif SOURCE and File.exist?(SOURCE) @@icla_index = [] File.read(SOURCE).scan(/^([-\w]+):(.*?):(.*?):(.*?):(.*)/).each do |list| icla = ICLA.new() icla.id = list[0] icla.legal_name = list[1] icla.name = list[2] icla.email = list[3] icla.form = list[4] match = icla.form.match(/^Signed CLA(?:;(\S+)| \((\+=.+)\))/) if match # match either the cla name or the SVN ref (+=...) icla.claRef = match[1] || match[2] end block.call(icla) @@icla_index << icla end end end
find ICLA
by email
# File lib/whimsy/asf/icla.rb, line 93 def self.find_by_email(value) return unless SOURCE refresh unless @@email_index @@email_index = {} # Allow for multiple emails separated by comma or space each {|icla| icla.emails.each {|m| @@email_index[m.downcase] = icla}} end @@email_index[value.downcase] end
find ICLA
by ID
# File lib/whimsy/asf/icla.rb, line 80 def self.find_by_id(value) return if value == 'notinavail' or not SOURCE refresh unless @@id_index @@id_index = {} each {|icla| @@id_index[icla.id] = icla} end @@id_index[value] end
find ICLA
by (public) name There are multiple entries with the same name. So if there are multiple matches, it does not make sense to return a single entry. By default, only return an entry if there is a single match. (else nil) If the multiple param is true, return an array of all matching entries. The array may be empty; does not return nil.
N.B. matching by name is inherently inaccurate due to misspellings and duplicates. There are likely to be both false positives and false negatives. Use with caution!
# File lib/whimsy/asf/icla.rb, line 115 def self.find_by_name(value, multiple=false) return unless SOURCE refresh unless @@name_index # Collect all the entries for each matching name @@name_index = Hash.new {|h, k| h[k] = Array.new} each {|icla| @@name_index[icla.name] << icla } end entries = @@name_index[value] return entries if multiple # no filtering needed return entries.first if entries&.size == 1 return nil end
find close matches
# File lib/whimsy/asf/icla.rb, line 138 def self.find_matches(value) matches = [] source = value.strip.downcase.split(' ') self.each do |icla| target = icla.legal_name.strip.downcase.split(' ') if target.sort == source.sort # order- and case-independent match matches << icla else cnt = self.array_count_match(source, target) if cnt >= 2 matches << icla else cnt = self.array_count_match(target, source) if cnt >= 2 matches << icla end end end end matches end
rearrange line in an order suitable for sorting
# File lib/whimsy/asf/icla.rb, line 215 def self.lname(line) return '' if line.start_with? '#' _, name, rest = line.split(':', 3) return '' unless name # Drop trailing (comment string) or /* comment */ name.sub!(/\(.+\)$/, '') name.sub!(/\/\*.+\*\/$/, '') return '' if name.strip.empty? name = ASF::Person.sortable_name(name) "#{name}:#{rest}" end
load ICLA
information for every committer
# File lib/whimsy/asf/icla.rb, line 67 def self.preload people = [] each do |icla| unless icla.id == 'notinavail' person = ASF::Person.find(icla.id) people << person person.icla = icla end end people end
flush caches if source file changed
# File lib/whimsy/asf/icla.rb, line 45 def self.refresh if not SOURCE or File.mtime(SOURCE) != @@mtime @@mtime = SOURCE ? File.mtime(SOURCE) : Time.now @@id_index = nil @@email_index = nil @@name_index = nil @@icla_index = nil # cache of all iclas as an array @@svn_change = nil @@availids = nil end end
sort an entire iclas.txt
file
# File lib/whimsy/asf/icla.rb, line 231 def self.sort(source) headers = source.scan(/^#.*/) lines = source.scan(/^\w.*/) headers.join("\n") + "\n" + lines.sort_by {|line| lname(line + "\n")}.join("\n") + "\n" end
Date and time of the last change in iclas.txt
in the working copy
# File lib/whimsy/asf/icla.rb, line 59 def self.svn_change self.refresh if SOURCE @@svn_change ||= Time.parse(ASF::SVN.getInfoItem(SOURCE, 'last-changed-date')).gmtime end end
is the availid taken (in use or reserved)? See also ASF::Mail.taken?
# File lib/whimsy/asf/icla.rb, line 286 def self.taken?(id) return self.availids_reserved.include?(id) || self.availids.include?(id) end
return a hash of unlisted ICLA
names, keyed by email if age > 0, return the entries added in the last n days
# File lib/whimsy/asf/icla.rb, line 197 def self.unlisted_name_by_email(age=0, env=nil) if age > 0 rev = "{%s}:HEAD" % (Date.today - age) diff, _err = ASF::SVN.svn('diff', SOURCE_URL, {revision: rev, env: env}) raise _err unless diff return Hash[*diff.scan(/^[+]notinavail:.*?:(.*?):(.*?):Signed CLA/).flatten.reverse] end hash = {} self.each { |icla| hash[icla.email] = icla.name if icla.noId? } hash end
Public Instance Methods
show the original entry (reconstructed for now)
# File lib/whimsy/asf/icla.rb, line 210 def as_line [id, legal_name, name, email, form].join(':') end
return emails split by comma or space
# File lib/whimsy/asf/icla.rb, line 303 def emails email.split(/[, ]/) end
does the entry not have an id?
# File lib/whimsy/asf/icla.rb, line 298 def noId? self.id == 'notinavail' end