Forum OpenACS Q&A: Problems defining procs

Collapse
Posted by ultra newb on
I have a need to define a proc like this:

my_proc {} "my code goes here"

or this:

my_proc {} [list my code goes here]

In other words, I need to dynamically define this proc. But this doesn't work. It works just fine if I open a tcl interpreter and type the code in there. But it doesn't work in OpenACS for some reason.

If it makes any difference, I am trying to define this proc in a "blah-procs" file to get automatically sourced when the system starts.

Any ideas? Thanks.

Collapse
Posted by ultra newb on
Whoops - change the above to:

proc my_proc {} "my code goes here"

or:

proc my_proc {} [list my code goes here]

Collapse
Posted by ultra newb on
If this wasn't apparent, I will try to clarify. The proc gets sourced just fine if I define it the "normal" way using {} instead of "" or [].
Collapse
Posted by Gustaf Neumann on
The normal way to define procs is via

proc myproc {} {
  # my code comes here
}
or
ad_proc myproc {} {
  Some documentation
} {
  # my code comes here
}

If you define the body of the proc with double-quotes or [list], the body is evaluated at definition time (at the time the file is sourced). Therefore, if you have e.g. set x $a in the body, $a will be evaluated at definition time and will throw an error. Check you error.log file.

Collapse
Posted by ultra newb on
Yes, I know the "normal" way. I don't want the normal way in this instance because I need the definition of the proc to change based on certain circumstances.

What I need is a proc that returns a list of values. The list of values changes day to day. I want the list of values to come from a backend database. I want the proc to be defined dynamically at startup to return that list of values. I don't want to have to connect to the backend database each time I need the list of values - I just want to connect one time at system startup, get the list of values, and have that list of values "hard-coded" in the proc.

This is easy to do. In fact, I've tested it outside of OpenACS and it works just fine (TCL is made to do stuff like this). It just doesn't work in OpenACS for some reason.

Is there some reason why this sort of definition is not possible in OpenACS or AOLServer?

Collapse
Posted by Dave Bauer on
We'll need to see the actual code to help.

Please review the error logs during startup. Grep or search for the filename that contains the procedure definition and see if there is an error while loading that file.

When you say "doesn't work" is the procedure not defined, or is it returning an error or wrong information when you try to call it?

Collapse
Posted by Claudio Pasolini on
Here is a small example of how to dynamically define a proc:

# get/set the values
set day_values "aaa bbb ccc"
set myproc "
proc myproc {} {
puts \"The day's values are: $day_values\"
}
"
eval $myproc

The values get hardcoded into the proc.

Collapse
Posted by ultra newb on
Nothing in the error logs except the same error I get when I try to access the page that tests the proc - "name of proc" isn't a procedure. Seems as if the proc is not being defined. If I define it the "normal" way (where I can't hard code info in the proc) it gets defined. If I do it the other way, it apparently doesn't.

If you want to see the actual code, at this point I've degenerated it to a simple "do nothing" proc, which still doesn't work. Here is one that doesn't work:

proc mkt {} [list return hi]

Here is another:

proc mkt {} "return hi"

I will try it the way the other poster recommends, but if that works my question would be why that works, but these other ways don't.

Collapse
Posted by ultra newb on
Claudio Pasolini's method doesn't work either. Can anyone else get this to work? Again this is with a "load_once-procs" file in a tcl directory, haven't tried in a "normal" way yet.
Collapse
Posted by Jeff Rogers on
The util_memoize procs are for exactly this purpose - run something once and return it quickly after that.

https://openacs.org/api-doc/procs-file-view?version_id=2967965&path=packages/acs-tcl/tcl/memoize-procs.tcl

You can wrap it in a proc very easily too, for example:

proc get_values {} {
return [util_memoize get_values_internal 86400]
}

proc get_values_internal {} {
# do slow/heavy database access
return $values
}

Collapse
Posted by ultra newb on
Okay, more intel. This "dynamic proc definition" works if I don't define it in a "blah-procs" file in a tcl directory.

If you guys say it works for you, I'll suspect something with my installation.

Collapse
Posted by Andrew Piskorski on
In other words, I need to dynamically define this proc. But this doesn't work.

You left out perhaps the single most important piece of information: Where are you trying to do this proc definition?

Each thread in Tcl - and in AOLserver - gets its own Tcl interpreter. If you define your proc in a *.tcl page, it will of course get (re)created every single time that page runs, and will always be available while that page is running. Try that and see that it works.

Next, to reduce the innefficiency of recreating the proc on every page load, you should instead try to define your proc in a library at AOLserver startup time, which automatically makes it available to all threads. Whether that is feasible for you depends on why you're trying to dynamically define your proc in the first place. You have a full Tcl interpretor available at AOLserver startup time and can do arbitrary programming, but naturally many of APIs you would use in a connection thread don't make sense to use during server startup and will fail if you call them then.

Collapse
Posted by ultra newb on
I stated several times that I put this proc definition in a "blah-procs" file in a tcl folder. In other words, I put it in a file that is supposed to "auto-run" when the system starts.
Collapse
Posted by Brian Fenton on
Break it down into manageable chunks. Can you get a basic proc working that just writes to the error log (using ns_log)? I presume you are bouncing AOLserver or watching the library file that contains your proc. Are you developing in an existing OpenACS package or did you create your own? If you still can't get it working, I would take an existing working OpenACS library file, add your proc, watch the file and see what happens. How are you calling the proc? Also check in the api-doc - your proc should appear there if you defined it with ad_proc.

Hope this gives you some ideas,
Brian

Collapse
Posted by ultra newb on
The util_memoize method both works and doesn't work. It works depending on the position of my proc within the file. If it is at the end, it doesn't work. If I move it up in the file (above some other memoized procs), it works.
Collapse
Posted by Gustaf Neumann on
Can you specify what you mean precisely by "not working"?

i have just added proc mkt {} [list return hi] into a library file (in my case packages/xowiki/tcl/xowiki-procs.tcl) reloaded the file via acs-admin/apm and called the proc mkt via ds/shell, and it returns the expected result (i.e. "hi"). i see nothing wrong.

Collapse
Posted by ultra newb on
From above (I guess it got lost in the wall of text):

Nothing in the error logs except the same error I get when I try to access the page that tests the proc - "name of proc" isn't a procedure. Seems as if the proc is not being defined. If I define it the "normal" way (where I can't hardcode info in the proc) it gets defined. If I do it the other way, it apparently doesn't.

To repeat, when I say it is "not working," when I call the web page which uses the proc, I get a "'name of proc' isn't a procedure" error (it doesn't literally say 'name of proc', it uses the actual name of the proc I am trying to define).

Also, to repeat, this is not with a package I am defining, nor an existing package. I am simply using the www directory for my web pages, and I am putting my custom procs in a "blah-procs" file in the tcl folder.

Repeating again, I got the util_memoize to work, but only if I put the proc in a certain location in the "blah-procs" file. If I put it in a different location, I get the same error.

If you guys don't experience any of these problems, and don't believe a bug exists, I'll chalk it up to the installation.

Thanks.

Collapse
Posted by ultra newb on
Thanks for testing, Gustaf.
Collapse
Posted by Ryan Gallimore on
You need to put your blah-procs file inside a package. Copy it to /packages/some-package/tcl/ and visit acs-admin/apm and reload that package.

See the documentation on Packages for more information.

Collapse
Posted by Jeff Rogers on
Unless you're redefining commands for proc creation, it shouldn't matter which is defined first. Putting it in a different location in the same file makes it sound like something is erroring out in the middle of that file and preventing the rest of it from running. If you're not seeing any error messages in your log from startup, try adding a log message after each proc is defined in your source file, as well as at the beginning and end. If you only get some of the log messages then you can pinpoint where it stopped running (and hopefully why).

If you get all the log messages but still see only some of the procs defined, then you've got everyone baffled. If you could share your entire source file, maybe we could reproduce the issue.

Collapse
Posted by ultra newb on
Okay, I am indeed getting errors in the log. Here is the relevant portion of the log:

[11/Jun/2011:16:47:14][1032.1992][-thread1992-] Error: tcl: source c:/aolserver/servers/openacs/tcl/exchange-procs.tcl failed: no connection
NONE
no connection
while executing
"ns_conn headers"
(procedure "ad_host" line 2)
invoked from within
"ad_host"
(procedure "exch" line 2)
invoked from within
"exch MKT"
(procedure "mkt" line 1)
invoked from within
"mkt"
("eval" body line 1)
invoked from within
"eval $script"
invoked from within
"ns_cache eval util_memoize $script {
list $current_time [eval $script]
}"
(procedure "util_memoize" line 20)
invoked from within
"util_memoize mkt"
(file "c:/aolserver/servers/openacs/tcl/exchange-procs.tcl" line 36)
invoked from within
"source $file"

Don't know what "no connection means." If the other poster above is correct, I should put my code in a package... correct? I didn't want to have to create one, but I will try and report back.

Thanks to all for continuing to help - this is a great community.

Collapse
Posted by ultra newb on
Update: Created a package for everything. I still have the same problems with the package.

Here is the exchange-procs.tcl file:

proc ret x {return $x}

proc exch cmd {
set EXCH_HOST [ad_host]
set EXCH_PORT 9999

set socket [socket $EXCH_HOST $EXCH_PORT]
puts -nonewline $socket $cmd
flush $socket

set reply {}
while {[set line [string trim [gets $socket]]] != {}} {
lappend reply $line
}
close $socket
return $reply
}

proc libdir {} {return [get_server_root]/packages/acs-subsite/lib}
util_memoize libdir

proc mkt {} {return [lindex [list [lrange [lindex [exch MKT] 0] 1 end]] 0]}
util_memoize mkt

proc mktmin {} {
return [lindex [exch MKMIN,MKMAX] 0]
}

As stated previously, it depends on the postition of various procs as to whether this works or not. I had rearranged things and had everything working up until I added the last proc (mktmin), which caused it to blow up again.

Collapse
Posted by Jeff Rogers on
The "no connection" error is telling you that you are trying to perform an operation (in this case, getting the "host" header from the current request) in a context where there is no current request - at startup.

Once an error is raised in a tcl file, nothing further in that file will get executed, including commands to define procs. That would explain why the proc is available if you define it early in the file but not at the end.

To get the name of the server you are on when there is no request, use [ns_info hostname], or could you just use "localhost"?

Collapse
Posted by ultra newb on
This was indeed the problem! All problems have now been solved, including dynamically-defining a proc.

Also, apparently don't have to define a package.

Thanks to everyone, this was a big help.

Collapse
Posted by Andrew Piskorski on
I stated several times that I put this proc definition in a "blah-procs" file in a tcl folder.

Which is not a way to "dynamically define this proc".

Collapse
Posted by ultra newb on
What's the correct way to do it? (current way seems to be working now)
Collapse
Posted by Jim Lynch on
so the proc has to return a list... that's not a problem, a list is a string. I'm really, really skeptical when you say the proc has to be dynamic; there's a database you can use, are the values you need to return in the database?
Collapse
Posted by ultra newb on
No, the values are in a backend system I wrote in another programming language (a server). These values change from time to time. I'd like to avoid having to change values in the openacs system (front end) when the values on the backend change. I thought the best and cleanest approach was to make a dynamic proc (the memoized approach one person recommended works also).

At any rate, everything is working fine now. The problem (as someone pointed out) was accessing a system proc when there wasn't a connection.