> On May 30, 2020, at 6:21 PM, Sam Ruby <ru...@intertwingly.net> wrote: > > On Sat, May 30, 2020 at 3:08 PM Craig Russell <apache....@gmail.com > <mailto:apache....@gmail.com>> wrote: >> >>> On May 29, 2020, at 3:17 AM, sebb <seb...@gmail.com> wrote: >>> >>> On Fri, 29 May 2020 at 00:49, Craig Russell <apache....@gmail.com> wrote: >>>> >>>> It looks like the best approach is to do the svn operations in two steps: >>>> >>>> 1. Update the svn foundation/members.txt based on the request (active, >>>> emeritus, deceased) which is already in place. >>>> 2. Perform the moves in svn documents >>>> (emeritus-request-received>emeritus-request-rescinded, >>>> emeritus-request-received->emeritus) which is new code to be written. >>>> >>>> These are two different svn repositories so they need to be in two svn >>>> commits anyway. >>> >>> Not AFAIK. All the ASF documents are in the same SVN repo, which is rooted >>> at: >>> >>> https://svn.apache.org/repos/private >> >> Thanks for that. I have each repository checked out and didn't think to look >> to see if they shared a root. >>> >>>> And if the operations are retried on failure there is nothing to be done >>>> to coordinate the two operations. Meanwhile, I am sure that I do not know >>>> how to make the roster ruby/svn code retriable. >>> >>> A problem arises if the second request fails even after retry. >>> There will be an outstanding change which has to be performed at some >>> later stage. >> >> So if I understand you, we can do both updates with the same svn commit, and >> should do so to avoid a partial commit that then needs to be corrected by >> hand. > > Correct. In fact, if the commit is ONLY intended to have the move, it > can be done via a single svn move command where both the source and > destination are specified as URLs.
In case of deceased, there is only the update of members.txt. Current code is just fine. In case of emeritus, there are the update of members.txt and move of the request from emeritus-requests-received to emeritus. This is the case where I'd like to insert an svn command into the anonymous update function. In case of rescind, there is only the move of the request from emeritus-requests-received to emeritus-requests-rescinded. In this case, there is no need for the update function, just an svn command. > > http://svnbook.red-bean.com/en/1.7/svn.ref.svn.c.move.html > <http://svnbook.red-bean.com/en/1.7/svn.ref.svn.c.move.html> I know svn and know how to use the mv command. What I don't know is how to issue an svn command from within the anonymous function that is processing the members.txt file. > >>>> I plan to start on figuring out how to do svn move from emeritus to >>>> emeritus-request-rescinded. >>> >>> Do you mean emeritus-request => emeritus-request-rescinded ? >> >> Yes. So I'm thinking that we should adapt the memstat.json.rb code to accept >> another url parameter with the file name that we already know, and send that >> file name to the _svn.update method. >> >> As long as we're looking at this method, isn't it a bit badly named? Its >> purpose is specifically to modify the members.txt file to move an entry from >> one place to another, so shouldn't it have a more specific name, like >> update_members_info? >> >> Anyway, we now need to pass the emeritus_file_name from memstat.json.rb to >> the update method. And then have the update method use that file name to >> move the file from one emeritus directory to another, depending on the >> action: >> emeritus: move the file from emeritus-requests-received to emeritus >> rescind: move the file from emeritus-requests-received to >> emeritus-requests-rescinded >> active: move the file from emeritus to emeritus-rescinded >> >> I'd like to understand how this method call works: >> _svn.update members_txt, message: message do |dir, text| >> >> Specifically, where did dir and text come from and why are they enclosed in >> |...|? > > the ```do |args|.... end``` effectively defines an anonymous function. > The parameters to that function are enclosed in vertical bars. Ruby > calls this anonymous function a block. > > There are two syntaxes for calling blocks. In this case, the block is > called via a yield statement. A more complete description can be > found here: > > https://mixandgo.com/learn/mastering-ruby-blocks-in-less-than-5-minutes > <https://mixandgo.com/learn/mastering-ruby-blocks-in-less-than-5-minutes> Very helpful. Thanks. > > Taking a step back, what is happening here is that the update method > does a checkout into a temporary directory, reads the current contents > of the file in question and passes that directory and contents to the > anonymous function which returns new contents. A commit is then > attempted. > > Making the code that converts old contents into new contants a block > means that the overall process (starting with the creating of a > temporary directory to commit potentially common code that is > independent of the update operation being attempted. So now I just need an example of svn code executed with no update block and some code executed inside the update block. Thanks, Craig > >> It looks like the code in memstat.json.rb is a co-routine with the code in >> lib...svn.rb, where the svn.rb code opens the members_txt file, passes >> control to the code in memstat.json.rb to update the members.txt data and >> when that's done, svn.rb takes over and completes the commit operation. >> >> So now we are in the _svn.update do block and need to execute an svn mv >> command. >> >> # determine where to put the entry >> if @action == 'emeritus' >> index = text.index(/^\s\*\)\s/, text.index(/^Emeritus/)) >> entry.sub! %r{\s*/\* deceased, .+?\*/},'' # drop the deceased comment if >> necessary >> >> dir = ASF:SVN:find!('documents') # this is the root directory >> from = 'emeritus-requests-received' >> to = 'emeritus' >> # execute the code to move the file from one directory to another >> svn.move(dir, from, to, @emeritus-file-name) >> >> Over to you... >> >> Craig >>> >>>> Thanks, >>>> Craig >>>> >>>>> On May 26, 2020, at 10:37 PM, Sam Ruby <ru...@intertwingly.net> wrote: >>>>> >>>>> On Wed, May 27, 2020 at 12:38 AM Craig Russell <apache....@gmail.com> >>>>> wrote: >>>>>> >>>>>> I'm not having any success understanding the memstat.json.rb code: >>>>>> # update members.txt >>>>>> _svn.update members_txt, message: message do |dir, text| >>>>>> >>>>>> Does this call the code in svn.rb? >>>>>> def self.update(path, msg, env, _, options={}) >>>>>> >>>>>> I guess I'm not so good at reading the ruby... >>>>> >>>>> The definition of _svn is here: >>>>> >>>>> https://github.com/apache/whimsy/blob/4e89c0e2a1069fbe7b6f4472722836c12a846e6c/www/roster/models/svn.rb#L29 >>>>> >>>>> It does indeed call the update method in lib/whimsy/asf/svn.rb. >>>>> >>>>> As an aside, the JavaScript module system, like Python, requires each >>>>> module to explicitly import what it needs, this makes it easier to >>>>> identify where the definition of a given method is. Enough so that >>>>> IDEs like VSCode allow you to control-click (or command-click on >>>>> MacOS) on a function call and it will take you to its definition. >>>>> >>>>>> I'm more used to the read/update/write/repeat/commit style of database >>>>>> access, where the memstat.json.rb would >>>>>> - read the members.txt >>>>>> - using the action (emeritus, active, deceased, request_emeritus, >>>>>> rescind_emeritus) edit the members.txt >>>>>> - move the file from emeritus-requests-received to emeritus (emeritus) >>>>>> - remove the file from emeritus-requests-received (rescind) >>>>>> - svn commit the changes >>>>> >>>>> That's indeed how it was done on the original whimsy_vm (effectively >>>>> whimsy_vm1). It routinely would get 'wedged' (that's the term you >>>>> used at the time) and I would have to ssh into that machine, run a >>>>> combination of svn reverts and svn cleanups until the directory was >>>>> clean again, then the operation could be attempted again. Perhaps you >>>>> might remember this happening. >>>>> >>>>> The second iteration creates a fresh checkout of the directory in >>>>> question, runs your block against that directory and commits the >>>>> results. Should the directory get 'wedged', the operation will fail, >>>>> but no matter what happens that temporary directory will be thrown >>>>> away, so the operation can be attempted again. This is the code you >>>>> are now looking at. >>>>> >>>>> The third iteration (present only in the Ruby board agenda tool at the >>>>> moment) takes this a step further and retries operations. Unlike the >>>>> secretary workbench and even to some extent the roster tool, the board >>>>> agenda tool is often used by multiple people concurrently, often with >>>>> changes to the same lines (most notably, the approval lines) on the >>>>> day before the board meeting. If a commit fails, a sleep for a random >>>>> time occurs and the operation is attempted again with a fresh checkout >>>>> and re-invoking the same block. I even have a test tool that >>>>> simulates 9 directors committing at once: >>>>> >>>>> https://github.com/apache/whimsy/blob/master/www/board/agenda/test/stresstest.rb >>>>> >>>>> The fourth iteration (currently being prototyped in >>>>> JavaScript/Node.js) is intended to incorporate the above retry logic >>>>> in production, but will also employ different strategies in >>>>> development and test. >>>>> >>>>> In the fullness of time, all of this should hopefully converge. >>>>> Meanwhile, build on the strategy that you feel most comfortable with, >>>>> and over time it will likely be rewritten as new strategies emerge. >>>>> >>>>>> Craig >>>>> >>>>> - Sam Ruby >>>>> >>>>>>> On May 26, 2020, at 6:09 PM, Sam Ruby <ru...@intertwingly.net> wrote: >>>>>>> >>>>>>> On Tue, May 26, 2020 at 8:44 PM Craig Russell <apache....@gmail.com> >>>>>>> wrote: >>>>>>>> >>>>>>>> Sorry for not being clear. In the whimsy/lib there is a helper file >>>>>>>> svn.rb that has a number of functions including update(path, msg, env, >>>>>>>> _, options={}) that is used by roster memstat.json.rb and it contains >>>>>>>> the commit in the function. >>>>>>>> >>>>>>>> I did not see a move or remove function in svn.rb. Should I be looking >>>>>>>> somewhere else for examples of svn commands? Should I look at adding >>>>>>>> move and remove functions to svn.rb? >>>>>>> >>>>>>> Not currently. >>>>>>> >>>>>>> I'm convinced that the right way forward is for all svn access to be >>>>>>> done via what you are calling helpers. This not only makes the code >>>>>>> cleaner >>>>>>> >>>>>>> Ruby: File.read(File.join(ASF::SVN['foundation_board'], >>>>>>> 'board_agenda_06_17.txt') >>>>>>> JS: Board.read('board_agenda_06_17.txt') >>>>>>> >>>>>>> ... it also enables redirecting all commits to a local repository in >>>>>>> development, and mocking the repository in test. >>>>>>> >>>>>>> In any case, all of the svn operations should be updated to make use >>>>>>> of --password-from-stdin where available, which unfortunately won't be >>>>>>> until whimsy-vm is upgraded to Ubuntu 20.04. >>>>>>> >>>>>>>> Thanks, >>>>>>>> Craig >>>>>>> >>>>>>> - Sam Ruby >>>>>>> >>>>>>>>> On May 26, 2020, at 5:23 PM, Sam Ruby <ru...@intertwingly.net> wrote: >>>>>>>>> >>>>>>>>> On Tue, May 26, 2020 at 8:15 PM Craig Russell <apache....@gmail.com> >>>>>>>>> wrote: >>>>>>>>>> >>>>>>>>>> The cancel button now works (thanks, Sam). On to the server side. >>>>>>>>>> >>>>>>>>>> To implement active->emeritus we need to update members.txt and move >>>>>>>>>> the request file from emeritus-requests-received to emeritus. >>>>>>>>>> >>>>>>>>>> - Is there an svn function to move a file from one directory to >>>>>>>>>> another? >>>>>>>>> >>>>>>>>> http://svnbook.red-bean.com/en/1.6/svn.ref.svn.c.move.html >>>>>>>>> >>>>>>>>>> - Is there a way to have both things done in the same svn commit? >>>>>>>>> >>>>>>>>> If both the source and target have a common parent, do the svn commit >>>>>>>>> from that parent. >>>>>>>>> >>>>>>>>> When the secretary files board minutes, >>>>>>>>> 1) the minutes are moved to the website. Those source and target >>>>>>>>> don't share a common parent, so two commits are required. >>>>>>>>> 2) the agenda is moved to a subdirectory. The source and target share >>>>>>>>> a common parent, so one commit suffices. >>>>>>>>> >>>>>>>>>> To implement request_emeritus_status we need to create an email. >>>>>>>>>> Easy.. >>>>>>>>>> >>>>>>>>>> To implement rescind_emeritus_request we need to svn rm the file >>>>>>>>>> from emeritus-requests-received. >>>>>>>>>> >>>>>>>>>> - Is there an svn function to delete a file? >>>>>>>>> >>>>>>>>> http://svnbook.red-bean.com/en/1.6/svn.ref.svn.c.delete.html >>>>>>>>> >>>>>>>>>> Thanks, >>>>>>>>>> Craig >>>>>>>>> >>>>>>>>> - Sam Ruby >>>>>>>>> >>>>>>>>>>> On May 26, 2020, at 3:20 PM, Craig Russell <apache....@gmail.com> >>>>>>>>>>> wrote: >>>>>>>>>>> >>>>>>>>>>> Here are the files. >>>>>>>>>>> <main.js.rb><memstat.js.rb> >>>>>>>>>>> And the console logs: >>>>>>>>>>> [Log] dblclick event.currentTarget: [object HTMLDivElement] >>>>>>>>>>> (app.js, line 3268) >>>>>>>>>>> [Log] dblclick event.currentTarget.dataset: [object DOMStringMap] >>>>>>>>>>> (app.js, line 3269) >>>>>>>>>>> [Log] dblclick event.currentTarget.dataset.edit: memstat (app.js, >>>>>>>>>>> line 3270) >>>>>>>>>>> [Log] canx called from Cancel button (app.js, line 4021) >>>>>>>>>>> [Log] event: [object MouseEvent] parent: [object HTMLButtonElement] >>>>>>>>>>> (app.js, line 4024) >>>>>>>>>>> [Log] parent: [object HTMLButtonElement] memstatElement: [object >>>>>>>>>>> HTMLDivElement] (app.js, line 4025) >>>>>>>>>>> [Log] preventing default (app.js, line 4026) >>>>>>>>>>> [Log] memstatElement.dataset: [object DOMStringMap] (app.js, line >>>>>>>>>>> 4027) >>>>>>>>>>> [Log] memstatElement.dataset.edit: memstat (app.js, line 4028) >>>>>>>>>>> [Log] submit event: [object Object] (app.js, line 3291) >>>>>>>>>>> [Log] submit target: [object HTMLButtonElement] (app.js, line 3292) >>>>>>>>>>> [Log] submit parent: [object HTMLButtonElement] (app.js, line 3293) >>>>>>>>>>> [Log] submit parent.getAttribute(data_cancel): null (app.js, line >>>>>>>>>>> 3294) >>>>>>>>>>> [Log] submit target.dataset_edit: undefined (app.js, line 3295) >>>>>>>>>>> [Log] submit target.dataset: [object DOMStringMap] (app.js, line >>>>>>>>>>> 3296) >>>>>>>>>>> [Log] submit target.dataset.edit: undefined (app.js, line 3297) >>>>>>>>>>> [Log] submit memstatElement.dataset.edit: memstat (app.js, line >>>>>>>>>>> 3298) >>>>>>>>>>> [Log] submit cancel_submit: null (app.js, line 3300) >>>>>>>>>>> >>>>>>>>>>>> On May 26, 2020, at 2:19 PM, Sam Ruby <ru...@intertwingly.net> >>>>>>>>>>>> wrote: >>>>>>>>>>>> >>>>>>>>>>>> On Tue, May 26, 2020 at 1:32 AM Craig Russell >>>>>>>>>>>> <apache....@gmail.com> wrote: >>>>>>>>>>>>> >>>>>>>>>>>>> But calling preventDefault on the event doesn't appear to do >>>>>>>>>>>>> anything. It still calls the POST behavior, and does not cause >>>>>>>>>>>>> the inline edit menu to disappear. And setting the edit function >>>>>>>>>>>>> to nil doesn't do anything either. >>>>>>>>>>>>> >>>>>>>>>>>>> Maybe there is something else that I need to do? >>>>>>>>>>>> >>>>>>>>>>>> I'll admit that I'm not clear on what you are trying to accomplish, >>>>>>>>>>>> but apparently the problem here is that there is another piece of >>>>>>>>>>>> code >>>>>>>>>>>> that attaches an event handler (and calls preventDefault): >>>>>>>>>>>> >>>>>>>>>>>> https://github.com/apache/whimsy/blob/d3246f107a35f4d989350f4c1ca64366c98ef423/www/roster/views/person/main.js.rb#L341 >>>>>>>>>>>> >>>>>>>>>>>> Perhaps it would be best to add an attribute to the button, and >>>>>>>>>>>> have >>>>>>>>>>>> the submit method remove the buttons and exit early: >>>>>>>>>>>> >>>>>>>>>>>> diff --git a/www/roster/views/person/main.js.rb >>>>>>>>>>>> b/www/roster/views/person/main.js.rb >>>>>>>>>>>> index 1b5ffdb8..f498489f 100644 >>>>>>>>>>>> --- a/www/roster/views/person/main.js.rb >>>>>>>>>>>> +++ b/www/roster/views/person/main.js.rb >>>>>>>>>>>> @@ -355,6 +355,12 @@ class Person < Vue >>>>>>>>>>>> form = jQuery(event.currentTarget).closest('form') >>>>>>>>>>>> target = event.target >>>>>>>>>>>> >>>>>>>>>>>> + # if button is a cancel button, don't submit and remove >>>>>>>>>>>> buttons >>>>>>>>>>>> + if target.getAttribute('data-cancel') >>>>>>>>>>>> + @edit = null >>>>>>>>>>>> + return >>>>>>>>>>>> + end >>>>>>>>>>>> + >>>>>>>>>>>> # serialize form >>>>>>>>>>>> formData = form.serializeArray(); >>>>>>>>>>>> >>>>>>>>>>>> diff --git a/www/roster/views/person/memstat.js.rb >>>>>>>>>>>> b/www/roster/views/person/memstat.js.rb >>>>>>>>>>>> index 39367c40..e8d3d318 100644 >>>>>>>>>>>> --- a/www/roster/views/person/memstat.js.rb >>>>>>>>>>>> +++ b/www/roster/views/person/memstat.js.rb >>>>>>>>>>>> @@ -35,6 +35,8 @@ class PersonMemberStatus < Vue >>>>>>>>>>>> _button.btn.btn_primary 'move to emeritus', >>>>>>>>>>>> name: 'action', value: 'emeritus' >>>>>>>>>>>> end >>>>>>>>>>>> + >>>>>>>>>>>> + _button.btn.btn_secondary 'cancel', data_cancel: >>>>>>>>>>>> true >>>>>>>>>>>> end >>>>>>>>>>>> end >>>>>>>>>>>> end >>>>>>>>>>>> >>>>>>>>>>>> - Sam Ruby >>>>>>>>>>> >>>>>>>>>>> Craig L Russell >>>>>>>>>>> c...@apache.org >>>>>>>>>>> >>>>>>>>>> >>>>>>>>>> Craig L Russell >>>>>>>>>> c...@apache.org >>>>>>>>>> >>>>>>>> >>>>>>>> Craig L Russell >>>>>>>> c...@apache.org >>>>>>>> >>>>>> >>>>>> Craig L Russell >>>>>> c...@apache.org >>>>>> >>>> >>>> Craig L Russell >>>> c...@apache.org >>>> >> >> Craig L Russell >> c...@apache.org Craig L Russell c...@apache.org