class ASF::MemberFiles
Constants
- BOARD_REGEX
-
Same as
MEMBER_REGEX
, but no <uid> and no <email> - MEMBER_REGEX
-
This Regex is very similar to the one in the script used to create ballots: svn.apache.org/repos/private/foundation/Meetings/steve-tools/seed-issues.py
- NAME2OUTPUTKEY
- NOMINATED_BOARD
- NOMINATED_MEMBERS
Public Class Methods
Source
# File lib/whimsy/asf/member-files.rb, line 244 def self.board_nominees nominees = {} ASF::MemberFiles.parse_file(NOMINATED_BOARD) do |hdr, nominee| # for board, the header currently looks like this: # <PUBLIC NAME> id = ASF::Person.find_by_name!(hdr) || hdr # default to full name nominee['Public Name'] = hdr # the board file does not have ids nominees[id] = nominee end nominees end
Return hash of board nominees
Source
# File lib/whimsy/asf/member-files.rb, line 41 def self.latest_meeting(name=nil) if name.nil? # we want the parent directory name = NOMINATED_MEMBERS # ensure the target directory has been set up File.dirname(Dir[File.join(ASF::SVN['Meetings'], '[2-9][0-9]*', name)].max) else Dir[File.join(ASF::SVN['Meetings'], '[2-9][0-9]*', name)].max end end
get the latest meeting directory or nomination file
Source
# File lib/whimsy/asf/member-files.rb, line 139 def self.make_board_nomination(fields = {}) availid = fields[:availid] or raise ArgumentError.new(':availid is required') publicname = ASF::Person[availid]&.cn or raise ArgumentError.new(":availid #{availid} is invalid") nomby = fields[:nomby] or raise ArgumentError.new(':nomby is required') ASF::Person[nomby]&.dn or raise ArgumentError.new(':nomby is invalid') secby = fields[:secby] || '' statement = fields[:statement] or raise ArgumentError.new(':statement is required') [ '', " #{publicname}", " Nominated by: #{nomby}@apache.org", " Seconded by: #{secby}", '', ' Nomination Statement:', ASFString.reflow(statement, 4, 80), '' ].compact.join("\n") + "\n" end
create a board nomination entry in the standard format
Source
# File lib/whimsy/asf/member-files.rb, line 116 def self.make_member_nomination(fields = {}) availid = fields[:availid] or raise ArgumentError.new(':availid is required') publicname = ASF::Person[availid]&.cn or raise ArgumentError.new(":availid #{availid} is invalid") nomby = fields[:nomby] or raise ArgumentError.new(':nomby is required') ASF::Person[nomby]&.dn or raise ArgumentError.new(':nomby is invalid') secby = fields[:secby] || '' statement = fields[:statement] or raise ArgumentError.new(':statement is required') [ '', " #{availid} <#{publicname}>", '', " Nominee email: #{availid}@apache.org", " Nominated by: #{nomby}@apache.org", " Seconded by: #{secby}", '', ' Nomination Statement:', ASFString.reflow(statement, 4, 80), '' ].compact.join("\n") + "\n" end
create a member nomination entry in the standard format
Source
# File lib/whimsy/asf/member-files.rb, line 226 def self.member_nominees nominees = {} ASF::MemberFiles.parse_file(NOMINATED_MEMBERS) do |hdr, nominee| # for members, the header currently looks like this: # availid <PUBLIC NAME> # In the past, it has had other layouts, for example: # availid PUBLIC NAME # PUBLIC NAME <email address>: id, name = hdr.split(' ', 2) # remove the spurious <> wrapper nominee['Public Name'] = name.sub(%r{^<}, '').chomp('>') # TODO: handle missing availid better nominees[id] = nominee end nominees end
Return hash of member nominees
Source
# File lib/whimsy/asf/member-files.rb, line 59 def self.parse_file(name) case name when NOMINATED_BOARD regex = BOARD_REGEX when NOMINATED_MEMBERS regex = MEMBER_REGEX else raise ArgumentError.new "Unexpected name: #{name}" end # N.B. The format has changed over the years. This is the syntax as of 2021. # ----------------------------------------- # <empty line> # header line # Nominee email: (not present in board file) # Nominated by: # Seconded by: # Nomination Statement: # Find most recent file: nomfile = latest_meeting(name) lastheader = nil # what was the last valid header # It does not appear to be possible to have file open or read # automatically transcode strings, so we do it here. # This is necessary to avoid issues with matching Regexes. File.open(nomfile, mode: 'rb:UTF-8') .map(&:scrub) .slice_before(/^\s*-{35,60}\s*/) .drop(2) # instructions and sample block .each do |block| block.shift(1) # divider nominee = {} header = nil data = block.join.strip next if data == '' md = regex.match(data) raise ArgumentError.new "Cannot parse #{data}" unless md md.named_captures.each do |k, v| case k when 'header' header = v.strip when 'uid', 'name' # not currently used else outkey = NAME2OUTPUTKEY[k] raise ArgumentError.new "Unexpected regex capture name: #{k}" if outkey.nil? v = v.split("\n") if k == 'statement' or k == 'seconds' nominee[outkey] = v end end yield header, nominee end end
Return a hash of nominees. key: availid (name for board nominees) value: hash of entries: keys: Public
Name Nominee email Nominated by Seconded by => array of seconders Nomination Statement => array of text lines
Source
# File lib/whimsy/asf/member-files.rb, line 181 def self.sort_board_nominees(contents, entries=nil) sections = contents.split(%r{^-{10,}\n}) header = sections.shift(2) sections.pop if sections.last.strip == '' sections.append(*entries) if entries # add new entries if any names = {} # replace 'each' by 'sort_by!' to sort by last name sections.each do |s| # sort by last name; check for duplicates m = s.match %r{\s+(.+)} if m name = m[1] raise ArgumentError.new("Duplicate id: #{name}") if names.include? name names[name] = 1 name.split.last else 'ZZ' end end # reconstitute the file [header, sections, ''].join("---------------------------------------\n") end
Sort the board_nominees
, optionally adding new entries
Source
# File lib/whimsy/asf/member-files.rb, line 159 def self.sort_member_nominees(contents, entries=nil) sections = contents.split(%r{^-{10,}\n}) header = sections.shift(2) sections.append(*entries) if entries # add new entries if any ids = {} sections.sort_by! do |s| # sort by last name; check for duplicates m = s.match %r{(\S+) +<([^>]+)>} if m id = m[1] raise ArgumentError.new("Duplicate id: #{id}") if ids.include? id ids[id] = 1 m[2].split.last else 'ZZ' end end # reconstitute the file [header, sections, ''].join("-----------------------------------------\n") end
Sort the member_nominees
, optionally adding new entries
Source
# File lib/whimsy/asf/member-files.rb, line 214 def self.update_board_nominees(env, wunderbar, entries=nil, msg=nil, opt={}) nomfile = latest_meeting(NOMINATED_BOARD) opt[:diff] = true unless opt.include? :diff # default to true ASF::SVN.update(nomfile, msg || 'Updating board nominations', env, wunderbar, opt) do |_tmpdir, contents| sort_board_nominees(contents, entries) end end
update the board nominees
Source
# File lib/whimsy/asf/member-files.rb, line 205 def self.update_member_nominees(env, wunderbar, entries=nil, msg=nil, opt={}) nomfile = latest_meeting(NOMINATED_MEMBERS) opt[:diff] = true unless opt.include? :diff # default to true ASF::SVN.update(nomfile, msg || 'Updating nominated members', env, wunderbar, opt) do |_tmpdir, contents| sort_member_nominees(contents, entries) end end
update the member nominees