REBOL layout: How to create layout words automatically - word has no context? - rebol

Using the REBOL/View 2.7.8 Core, I would like to prepare a view layout beforehand by automatically assigning words to various layout items, as in the following example.
Instead of
prepared-view: [across
cb1: check
label "Checkbox 1"
cb2: check
label "Checkbox 2"
cb3: check
label "Checkbox 3"
cb4: check
label "Checkbox 4"
]
view layout prepared-view
I would thus like the words cb1 thru cb5 to be created automatically, e.g.:
prepared-view2: [ across ]
for i 1 4 1 [
cbi: join "cb" i
cbi: join cbi ":"
cbi: join cbi " check"
append prepared-view2 to-block cbi
append prepared-view2 [
label ]
append prepared-view2 to-string join "Checkbox " i
]
view layout prepared-view2
However, while difference prepared-view prepared-view2 shows no differences in the block being parsed (== []), the second script leads to an error:
** Script Error: cb1 word has no context
** Where: forever
** Near: new/var: bind to-word :var :var
I've spent hours trying to understand why, and I think somehow the new words need to be bound to the specific context, but I have not yet found any solution to the problem.
What do I need to do?

bind prepared-view2 'view
view layout prepared-view2
creates the correct bindings.
And here's another way to dynamically create layouts
>> l: [ across ]
== [across]
>> append l to-set-word 'check
== [across check:]
>> append l 'check
== [across check: check]
>> append l "test"
== [across check: check "test"]
>> view layout l
And then you can use loops to create different variables to add to your layout.

When you use TO-BLOCK to convert a string to a block, that's a low-level operation that doesn't go through the "ordinary" binding to "default" contexts. All words will be unbound:
>> x: 10
== 10
>> code: to-block "print [x]"
== [print [x]]
>> do code
** Script Error: print word has no context
** Where: halt-view
** Near: print [x]
So when you want to build code from raw strings at runtime whose lookups will work, one option is to use LOAD and it will do something default-ish, and that might work for some code (the loader is how the bindings were made for the code you're running that came from source):
>> x: 10
== 10
>> code: load "print [x]"
== [print [x]]
>> do code
10
Or you can name the contexts/objects explicitly (or by way of an exemplar word bound into that context) and use BIND.

Related

channel checks as empty even if it has content

I am trying to have a process that is launched only if a combination of conditions is met, but when checking if a channel has a path to a file, it always returns it as empty. Probably I am doing something wrong, in that case please correct my code. I tried to follow some of the suggestions in this issue but no success.
Consider the following minimal example:
process one {
output:
file("test.txt") into _chProcessTwo
script:
"""
echo "Hello world" > "test.txt"
"""
}
// making a copy so I check first if something in the channel or not
// avoids raising exception of MultipleInputChannel
_chProcessTwo.into{
_chProcessTwoView;
_chProcessTwoCheck;
_chProcessTwoUse
}
//print contents of channel
println "Channel contents: " + _chProcessTwoView.toList().view()
process two {
input:
file(myInput) from _chProcessTwoUse
when:
(!_chProcessTwoCheck.toList().isEmpty())
script:
def test = _chProcessTwoUse.toList().isEmpty() ? "I'm empty" : "I'm NOT empty"
println "The outcome is: " + test
}
I want to have process two run if and only if there is a file in the _chProcessTwo channel.
If I run the above code I obtain:
marius#dev:~/pipeline$ ./bin/nextflow run test.nf
N E X T F L O W ~ version 19.09.0-edge
Launching `test.nf` [infallible_gutenberg] - revision: 9f57464dc1
[c8/bf38f5] process > one [100%] 1 of 1 ✔
[- ] process > two -
[/home/marius/pipeline/work/c8/bf38f595d759686a497bb4a49e9778/test.txt]
where the last line are actually the contents of _chProcessTwoView
If I remove the when directive from the second process I get:
marius#mg-dev:~/pipeline$ ./bin/nextflow run test.nf
N E X T F L O W ~ version 19.09.0-edge
Launching `test.nf` [modest_descartes] - revision: 5b2bbfea6a
[57/1b7b97] process > one [100%] 1 of 1 ✔
[a9/e4b82d] process > two [100%] 1 of 1 ✔
[/home/marius/pipeline/work/57/1b7b979933ca9e936a3c0bb640c37e/test.txt]
with the contents of the second worker .command.log file being: The outcome is: I'm empty
I tried also without toList()
What am I doing wrong? Thank you in advance
Update: a workaround would be to check _chProcessTwoUse.view() != "" but that is pretty dirty
Update 2 as required by #Steve, I've updated the code to reflect a bit more the actual conditions i have in my own pipeline:
def runProcessOne = true
process one {
when:
runProcessOne
output:
file("inputProcessTwo.txt") into _chProcessTwo optional true
file("inputProcessThree.txt") into _chProcessThree optional true
script:
// this would replace the probability that output is not created
def outputSomething = false
"""
if ${outputSomething}; then
echo "Hello world" > "inputProcessTwo.txt"
echo "Goodbye world" > "inputProcessThree.txt"
else
echo "Sorry. Process one did not write to file."
fi
"""
}
// making a copy so I check first if something in the channel or not
// avoids raising exception of MultipleInputChannel
_chProcessTwo.into{
_chProcessTwoView;
_chProcessTwoCheck;
_chProcessTwoUse
}
//print contents of channel
println "Channel contents: " + _chProcessTwoView.view()
println _chProcessTwoView.view() ? "Me empty" : "NOT empty"
process two {
input:
file(myInput) from _chProcessTwoUse
when:
(runProcessOne)
script:
"""
echo "The outcome is: ${myInput}"
"""
}
process three {
input:
file(defaultInput) from _chUpstreamProcesses
file(inputFromProcessTwo) from _chProcessThree
script:
def extra_parameters = _chProcessThree.isEmpty() ? "" : "--extra-input " + inputFromProcessTwo
"""
echo "Hooray! We got: ${extra_parameters}"
"""
}
As #Steve mentioned, I should not even check if a channel is empty, NextFlow should know better to not initiate the process. But I think in this construct I will have to.
Marius
I think part of the problem here is that process 'one' creates only optional outputs. This makes dealing with the optional inputs in process 'three' a bit tricky. I would try to reconcile this if possible. If this can't be reconciled, then you'll need to deal with the optional inputs in process 'three'. To do this, you'll basically need to create a dummy file, pass it into the channel using the ifEmpty operator, then use the name of the dummy file to check whether or not to prepend the argument's prefix. It's a bit of a hack, but it works pretty well.
The first step is to actually create the dummy file. I like shareable pipelines, so I would just create this in your baseDir, perhaps under a folder called 'assets':
mkdir assets
touch assets/NO_FILE
Then pass in your dummy file if your '_chProcessThree' channel is empty:
params.dummy_file = "${baseDir}/assets/NO_FILE"
dummy_file = file(params.dummy_file)
process three {
input:
file(defaultInput) from _chUpstreamProcesses
file(optfile) from _chProcessThree.ifEmpty(dummy_file)
script:
def extra_parameters = optfile.name != 'NO_FILE' ? "--extra-input ${optfile}" : ''
"""
echo "Hooray! We got: ${extra_parameters}"
"""
}
Also, these lines are problematic:
//print contents of channel
println "Channel contents: " + _chProcessTwoView.view()
println _chProcessTwoView.view() ? "Me empty" : "NOT empty"
Calling view() will emit all values from the channel to stdout. You can ignore whatever value it returns. Unless you enable DSL2, the channel will then be empty. I think what you're looking for here is a closure:
_chProcessTwoView.view { "Found: $it" }
Be sure to append -ansi-log false to your nextflow run command so the output doesn't get clobbered. HTH.

Lingo Code "[cc]" is coming up as a fault

The game is a word search game in an advanced lingo book and the lingo code is using [cc] which is coming up as a code fault. What is wrong or is this use of [cc] obsolete? And if so, how can it be corrected?
on getPropertyDescriptionList me
list = [:]
-- the text member with the words in it
addProp list, #pWordSource,[cc]
[#comment: "Word Source",[cc]
#format: #text,[cc]
#default: VOID]
addProp list, #pEndGameFrame,[cc]
[#comment: "End Game Frame",[cc]
#format: #marker,[cc]
#default: #next]
return list
end
I guess this is code from here, right?
That seems like an older version of Lingo syntax. [cc], apparently, stands for "continuation character". It basically makes the compiler ignore the linebreak right after it, so that it sees everything from [#comment: to #default: VOID] as one long line, which is the syntactically correct way to write it.
If I remember correctly, once upon a time, the guys who made Lingo made one more crazy decision and made the continuation character look like this: ¬ Of course, this didn't print in lots of places, so some texts like your book used things like [cc] in its place.
In modern versions of Lingo, the continuation character is \, just like in C.
I programmed in early director but have gone on to other languages in the many years since. I understand this code. The function attempts to generate a dictionary of dictionaries. in quasi-JSON:
{
'pWordSource': { ... } ,
'pEndGameFrame': { ... }
}
It is creating a string hash, then storing a "pWordSource" as a new key pointing to a 3 item hash of it's own. The system then repeats the process with a new key "pEndGameFrame", providing yet another 3 item hash. So just to expand the ellipses ... from the above code example:
{
'pWordSource': { 'comment': 'Word Source', 'format': 'text', 'default': null } ,
'pEndGameFrame': { 'End Game Frame': 'Word Source', 'format': 'marker', 'default': 'next' }
}
So I hope that explains the hash characters. It's lingo's way of saying "this is not just a string, it's a special director-specific system we're calling a symbol. It can be described in more conventional programming terms as a constant. The lingo compiler will replace your #string1 with an integer, and it's always going to be the same integer associated with #string1. Because the hash keys are actually integers rather than strings, we can change the json model to look something more like this:
{
0: { 2: 'Word Source', 3: 'text', 4: null } ,
1: { 2:'End Game Frame', 3: 'marker', 4: 'next' }
}
where:
0 -> pWordSource
1 -> pEndGameFrame
2 -> comment
3 -> format
4 -> default
So to mimic the same construction behavior in 2016 lingo, we use the newer object oriented dot syntax for calling addProp on property lists.
on getPropertyDescriptionList me
list = [:]
-- the text member with the words in it
list.addProp(#pWordSource,[ \
#comment: "Word Source", \
#format: #text, \
#default: void \
])
list.addProp(#pEndGameFrame,[ \
#comment: "End Game Frame", \
#format: #marker, \
#default: #next \
])
return list
end
Likewise, the same reference shows examples of how to use square brackets to "access" properties, then initialize them by setting their first value.
on getPropertyDescriptionList me
list = [:]
-- the text member with the words in it
list[#pWordSource] = [ \
#comment: "Word Source", \
#format: #text, \
#default: void \
]
list[#pEndGameFrame] = [ \
#comment: "End Game Frame", \
#format: #marker, \
#default: #next \
]
return list
end
And if you are still confused about what the backslashes are doing, there are other ways to make the code more vertical.
on getPropertyDescriptionList me
list = [:]
-- the text member with the words in it
p = [:]
p[#comment] = "Word Source"
p[#format] = #text
p[#default] = void
list[#pWordSource] = p
p = [:] -- allocate new dict to avoid pointer bug
p[#comment] = "End Game Frame"
p[#format] = #marker
p[#default] = #next
list[#pEndGameFrame] = p
return list
end
The above screenshot shows it working in Director 12.0 on OS X Yosemite.

How do I pick elements from a block using a string in Rebol?

Given this block
fs: [
usr [
local [
bin []
]
share []
]
bin []
]
I could retrieve an item using a path notation like so:
fs/usr/local
How do I do the same when the path is a string?
path: "/usr/local"
find fs path ;does not work!
find fs to-path path ;does not work!
You need to complete the input string path with the right root, then LOAD it and evaluate it.
>> path: "/usr/local"
>> insert path "fs"
>> do load path
== [
bin []
]
Did you know Rebol has a native path type?
although this doesn't exactly answer your question, I tought I'd add a reference on how to use paths directly in Rebol. Rebol has a lot of datatypes, and when you can, you should leverage that rich language feature. Especially when you start to use and build dialects, knowing what types exist and how to use them becomes even more potent.
Here is an example on how you can build and run a path directly, without using strings. in order to represent a path within source code, you use the lit-path! datatype.
example:
>> p: 'fs/usr/local
== fs/usr/local
>> do p
== [
bin []
]
you can even append to a path to manipulate it:
>> append p 'bin
== fs/usr/local/bin
>> do p
== []
if it where stored within a block, you use a path! type directly (not a lit-path!):
>> p: [fs/usr/local/bin]
== [fs/usr/local]
>> do first p
== [
bin []
]
also note that using paths directly has advantages over using strings because the path is composed of a series of words, which you can do some manipulation more easily than with strings example:
>> head change next p 'bin
== fs/bin/local
>> p: 'fs/path/issue/is
== fs/path/issue/is
>> head replace p 'is 'was
== fs/path/issue/w
as opposed to using a string:
>> p: "fs/path/issue/is"
== "fs/path/issue/is"
>> head replace p "is" "was"
== "fs/path/wassue/is"
If you want to browse the disk, instead of Rebol datasets, you must simply give 'FS a value of a file! and the rest of the path with browse from there (this is how paths work on file! types):
fs: %/c/
read dirize fs/windows

How to make a window return value when closed in Tcl/Tk

I have a proc that creates a new window, asking the user to give a database name.. And I want the function, after it's closed to return a value.
How do I make a window to return a value to it's calling proc? I tried calling it using:
puts "dbug:: [set top [new_db_window]]"
The puts is to see the result. It doesn't work. Prints an empty sting ("dbug::") as the window is created and an error "can't read "::new_db_window": no such variable" when I hit the 'ok' button.
The code for the proc is:
proc new_db_window {} {
toplevel .new_db_menu
wm title .new_db_menu "New Data Base"
# Main Frame
frame .new_db_menu.frm -relief "groove" -bd 2
grid .new_db_menu.frm
if {[info exists db_name]} {
unset db_name
}
set ::new_db_window:db_name "Data_Base"
# The Name Entry
set frm_top [frame .new_db_menu.frm.top]
set lbl [label .new_db_menu.frm.top.label -text "Database Name:" -width 15]
set entr [entry .new_db_menu.frm.top.entry -textvariable ::new_db_window:db_name -width 15]
# The buttons
set b_ok [button .new_db_menu.frm.ok -image icon_v -command {return [new_db_ok_button]}]
set b_no [button .new_db_menu.frm.cancel -image icon_x -command {new_db_cancel_button}]
set sep_w [label .new_db_menu.frm.sep_w -text "" -width 1]
set sep_e [label .new_db_menu.frm.sep_e -text "" -width 1]
grid $lbl -row 1 -column 1 -sticky w
grid $entr -row 1 -column 2 -sticky w
grid $frm_top -row 1 -column 1 -columnspan 4
grid $sep_w -row 2 -column 1 -sticky w
grid $b_ok -row 2 -column 2 -sticky w
grid $b_no -row 2 -column 3 -sticky e
grid $sep_e -row 2 -column 4 -sticky e
bind .new_db_menu <Key-KP_Enter> {return [new_db_ok_button]}
bind .new_db_menu <Return> {return [new_db_ok_button]}
bind .new_db_menu <Escape> {new_db_cancel_button}
# catch presses on the window's `x` button
wm protocol .new_db_menu WM_DELETE_WINDOW {
new_db_cancel_button
}
# make the top window unusable
focus $entr
grab release .
grab set .new_db_menu
}
proc new_db_ok_button {} {
new_db_cancel_button
return "$::new_db_window:db_name"
}
proc new_db_cancel_button {} {
grab set .
destroy .new_db_menu
}
One way would be to just use tkwait window $yourwindow to wait until the user closes the window. The window itself should probably use some variable passed to it by the client code to manage user input. For instance, if you need the user to input a database name, use the entry widget and bind it to a variable using its -textvariable option. After the window is closed, and tkwait in the client code returns, read the value of that variable.
Another approach is to not use modal windows and turn into event-driven control flow. That is, make your inquiry window to receive the name of a procedure which should be called when the user accepts its input (and that input is validated) and do any further processing there instead of posting a window and waiting until the user deals with it.
The relevant manual pages are: tkwait and options (for -textvariable).

Rebol Request-Download doesn't support Big File: how to correct?

download-dir: request-dir
Print ["downloading " "VStudio2008Express.iso" "..." ]
url: http://go.microsoft.com/fwlink/?LinkId=104679
file-save: to-rebol-file rejoin [download-dir "VStudio2008Express.iso"]
request-download/to url file-save
In the end whereas the progress bar has shown the download has finished:
** Script Error: Not enough memory
** Where: append
** Near: insert tail series :value
>>
So how to correct request-download as it is a mezzanine function:
func [
{Request a file download from the net. Show progress. Return none on error.}
url [url!]
/to "Specify local file target." local-file [file! none!]
/local prog lo stop data stat event-port event
][
view/new center-face lo: layout [
backeffect [gradient 1x1 water gray]
space 10x8
vh2 300 gold "Downloading File:"
vtext bold center 300 to-string url
prog: progress 300
across
btn 90 "Cancel" [stop: true]
stat: text 160x24 middle
]
stop: false
data: read-thru/to/progress/update url local-file func [total bytes] [
prog/data: bytes / (max 1 total)
stat/text: reform [bytes "bytes"]
show [prog stat]
not stop
]
unview/only lo
if not stop [data]
]
Rebol's read functions read all the input data into memory at once and cannot be used on large datasets. You have to open a port and copy the data from it in chunks to handle large datasets.
I would think that the request-download function could be modified to use ports for both the input and output data. This thread from the Rebol Mailing List may help you:
http://www.rebol.org/ml-display-thread.r?m=rmlFQXC
You can find a fuller example on Carl's blog at http://www.rebol.com/cgi-bin/blog.r?view=0281#comments
Even using this technique there is a limit of approximately 2Gb to the size of files that can be handled in Rebol 2.
Try this
http://anton.wildit.net.au/rebol/util/batch-download.r