Posts Tagged ‘openacs’

Automated Testing Rules

Tuesday, May 16th, 2006

Automated testing is a good thing. For my running log application, I have a proc that creates a new log for a user, called rl::runner::new. If you happen to call it for a user who already has a log, it returns the already created log. When I created the proc way back when, I also created an automated test to make sure that creation worked and that redundant calls returned the orignal log.

Over time, I added some code to make sure that users are given proper permissions on their log. Anytime you make a change, you should run your tests, but of course, I didn’t :-) Well, today I did and my test now failed.

Here is the relevant (edited) code:

 set runner_id [db_nextval acs_object_id_seq] set ret_val [db_exec_plsql new_runner {}]  permission::grant -party_id $user_id -object_id $runner_id -privilege admin permission::grant -party_id $user_id -object_id $runner_id -privilege write 

The error was happening in permission::grant. It was complaining that $runner_id wasn’t a valid acs_object. What’s going on? It looks like it should work. The problem is that I should be granting the permission on ret_val, not on runner_id. (Probably a bad choice of variable names). The first time a log is created, db_exec_plsql returns a value which happens to be the same as runner_id. The second time it’s called, it returns a value which is different and permission::grant fails.

I would never have caught this error until I tried to call rl::runner::new the second time on the same user. Which would make it one of those very difficult to track bugs. Automated testing saved me a lot of annoying debugging.

That said, I wish OpenACS’s testing facilities were better. I worked on a couple other bugs today and I’m trying really hard to write tests to expose bugs before fixing them, but it’s really a strain. Especially when you need to do a combination of black box and white box testing. I’d like to make testing easier in OpenACS, but I’m not sure if I have the brainpower :-)

OpenACS in Linux Journal

Wednesday, April 26th, 2006

Linux Journal featured an article about a web application written in OpenACS. It’s called Fungoes and it tabulates and formats baseball statistics. If I had access to this app when I was a kid, I would’ve been in heaven. I remember spending hours and hours memorizing stats from the back of baseball cards. I’ve forgotten most of what I memorized, but I can still rattle off HR stats for most of the 1982 Brewers. This site really takes me back.

Fungoes is still in it’s early stages, but Mat apparently has big plans for it. It should be fun to watch.

Hit The Wall (a running log)

Friday, April 21st, 2006

I built a web application to keep track of my running a couple years ago. I really began using it in earnest during my training for the New York Marathon last year. I’ve been meaning to make it available for others to use for a long time, but it always seemed like there was a little more work I needed to do on it. While working on it the other day, I noticed the original timestamp on one of the source files: September 21, 2003 Yikes! That’s 2 and a half years ago. Enough is enough — it’s time to let this baby free…

Hit The Wall is officially version 0.2 and available for free use by anyone who wants to keep track of their running. Feel free to look through my real log (read-only) or demorunner’s fake log (read-write) to try it out.

I’ve created a separate blog and a forum where I’ll post updates and respond to questions or comments.

Prettier permalinks

Tuesday, March 28th, 2006

Permalink stands for ‘permanent link’. When you create a blog post, it gets added to the top of your blog, pushing older entries down. Eventually, posts get pushed off the front page, so it’s important that you have a way to refer to old blog posts. A permalink is the link that will always point to a specific post.

OpenACS (lars-blogger) permalinks look like this:

http://kurup.org/blog/one-entry?entry_id=58950

This works and it’s really easy to code, but it doesn’t give much information about the content or date of the post.

A lot of other blog packages use permalinks which look like this:

http://kurup.org/blog/2006/03/01/tag-cloud

It’s easy to tell how old the post is, and what it might be about. More importantly, it seems that search engines give URLs like this more weight. So, I’ve gone ahead and made the changes on my blog. Enjoy!

For the OpenACS geeks, here’s the changes that I made. It won’t necessarily apply cleanly against OpenACS, but it will give you an idea of what to change. It includes an automated test to assure you that both the old and new permalinks work.

Tcl, a short introduction

Thursday, March 9th, 2006

The scripting language used in most of OpenACS is Tcl. It doesn’t receive a lot of press. When people talk about scripting languages, they mean Perl or Python (and now Ruby). Tcl is not considered in the same category, but it should be. I’m not smart enough to explain why, but Salvatore Sanfilippo is and has written a gentle introduction to both the basic and powerful features of Tcl.

My blog is tagged

Monday, February 27th, 2006

I’m finally getting around to moving my blog into the Web 1.5 world. (2.0 minus javascript). I scrapped the categories system and created a super-simple tags package. So, now I can tag posts and technorati can find them. OpenACS barely lights up on technorati. Compare RubyOnRails (20 mentions per day) with OpenACS (1 mention in the past month!). I know Rails is popular, but is OpenACS completely dead? I don’t think so.

Creating a Blog Application using the OpenACS Content Repository

Thursday, February 16th, 2006

I created a very simple blog application using the OpenACS Content Repository (CR). When I say simple, I mean simple, probably to the point of being useless. I just wanted to try writing something as quickly as possible using the CR. Here’s a quick run-through of the code, in case it might help someone else understand how to use the CR. I’m not including much commentary; email me (or comment) if you’d like me to expound more.

blog.info

 <?xml version="1.0"?> <!-- Generated by the OpenACS Package Manager -->  <package key="blog" url="http://openacs.org/repository/apm/packages/blog" type="apm_application">     <package-name>Blog</package-name>     <pretty-plural>Blogs</pretty-plural>     <initial-install-p>f</initial-install-p>     <singleton-p>f</singleton-p>          <version name="0.1d" url="http://openacs.org/repository/download/apm/blog-0.1d.apm">         <owner url="mailto:admin@localhost">Admin User</owner>         <summary>A blog application.</summary>         <description format="text/html">A simple Blog application using the Content Repository.</description>         <maturity>0</maturity>          <provides url="blog" version="0.1d"/>         <requires url="acs-content-repository" version="5.2.2"/>          <callbacks>             <callback type="after-install"  proc="blog::apm::after_install"/>             <callback type="after-instantiate"  proc="blog::apm::after_instantiate"/>             <callback type="before-uninstantiate"  proc="blog::apm::before_uninstantiate"/>             <callback type="before-uninstall"  proc="blog::apm::before_uninstall"/>         </callbacks>         <parameters>         <!-- No version parameters -->         </parameters>      </version> </package>  

tcl/blog-apm-procs.tcl

 namespace eval blog::apm {}  ad_proc -private blog::apm::after_install {} {      Install the blog datamodel. } {     content::type::new -content_type blog_post \         -pretty_name "Blog Post" \         -pretty_plural "Blog Posts" \         -table_name "cr_blog_posts" \         -id_column "post_id" }  ad_proc -private blog::apm::after_instantiate {     -package_id } {     Setup one instance of a blog. Creates a content folder and associate     blog_posts with it. } {     set folder_id [content::folder::new \                        -name $package_id \                        -label "Blog Folder $package_id" \                        -package_id $package_id \                        -context_id $package_id]      content::folder::register_content_type \         -folder_id $folder_id \         -content_type "blog_post" \         -include_subtypes "t" }  ad_proc -private blog::apm::before_uninstantiate {     -package_id } {     Remove 1 instance of blog application. } {     set folder_id [blog::folder_id $package_id]     content::folder::delete -folder_id $folder_id -cascade_p t     content::type::delete -content_type blog_post \         -drop_children_p t \         -drop_table_p t }  ad_proc -private blog::apm::before_uninstall {} {     Drop the application. } {     content::type::delete -content_type blog_post \         -drop_children_p t \         -drop_table_p t } 

tcl/blog-procs.tcl

 namespace eval blog {}  ad_proc -public blog::folder_id {package_id} {     Return the CR folder_id associated with this package_id      @param package_id Current package_id } {     return [db_string get_folder_id "select folder_id from cr_folders where package_id=:package_id"] } 

www/index.tcl

 ad_page_contract {      List posts      @author Vinod Kurup [vinod@kurup.com]      @creation-date Tue Feb 14 20:56:24 2006      @cvs-id $Id:$ } { }  set package_id [ad_conn package_id]  db_multirow posts posts "   select i.item_id,           r.title,          r.content,          to_char(o.creation_date,'YYYY-MM-DD HH24:MI:SS') as creation_date,          o.creation_user     from cr_blog_posts p, cr_items i, cr_revisions r, acs_objects o    where i.item_id=o.object_id      and p.post_id=r.revision_id      and o.package_id=:package_id       and i.live_revision=p.post_id    order by creation_date desc" 

www/index.adp

 <master>  <p><a href="post/ae">Add Post</a></p>  <multiple name="posts">   <include src="../lib/one-post"             item_id="@posts.item_id@"             title="@posts.title@"             content="@posts.content@"             creation_user="@posts.creation_user@"             creation_date="@posts.creation_date@" /> </multiple> 

www/post/ae.tcl

 ad_page_contract {      Add or edit a post      @author Vinod Kurup [vinod@kurup.com]      @creation-date Tue Feb 14 20:39:39 2006      @cvs-id $Id:$ } {     item_id:integer,optional }  set package_id [ad_conn package_id] set user_id [ad_conn user_id] set ip_addr [ad_conn peeraddr]  permission::require_permission -object_id $package_id -privilege write  set context [list "Add/Edit Post"]  ad_form -name add_post -form {     item_id:key(t_acs_object_id_seq)     title:text(text)     content:text(textarea) } -select_query {     select r.title, r.content        from cr_items i, cr_revisions r       where i.item_id=:item_id         and i.live_revision=r.revision_id } -edit_data {     content::revision::new -item_id $item_id \         -title $title \         -content $content \         -package_id $package_id \         -is_live t      ns_returnredirect .. } -new_data {     content::item::new -name "Post $item_id" \         -item_id $item_id \         -creation_user $user_id \         -text $content \         -package_id $package_id \         -parent_id [blog::folder_id $package_id] \         -creation_ip $ip_addr \         -content_type "blog_post" \         -title $title      ns_returnredirect .. } 

www/post/ae.adp

 <master> <property name="title">Add/Edit Post</property> <property name="context">@context;noquote@</property>  <formtemplate id="add_post"></formtemplate> 

www/post/del.tcl

 ad_page_contract {      Delete a post      @author Vinod Kurup [vinod@kurup.com]      @creation-date Tue Feb 14 22:49:11 2006      @cvs-id $Id:$ } {     item_id:integer }  permission::require_permission -object_id $item_id -privilege write  content::item::delete -item_id $item_id  ns_returnredirect .. 

www/post/view.tcl

 ad_page_contract {      View one post      @author Vinod Kurup [vinod@kurup.com]      @creation-date Wed Feb 15 20:24:32 2006      @cvs-id $Id:$ } {     item_id:integer }  set package_id [ad_conn package_id] set package_url [ad_conn package_url]  db_0or1row post " select i.item_id,         r.title,        r.content,        to_char(o.creation_date,'YYYY-MM-DD HH24:MI:SS') as creation_date,        o.creation_user   from cr_blog_posts p, cr_items i, cr_revisions r, acs_objects o  where i.item_id=o.object_id    and p.post_id=r.revision_id    and o.package_id=:package_id     and i.live_revision=p.post_id    and i.item_id=:item_id"   set context [list $title] 

www/post/view.adp

 <master> <property name="context">@context;noquote@</property> <property name="title">@title@</property>  <include src="../../lib/one-post"           item_id="@item_id@"           title="@title@"           content="@content@"           creation_user="@creation_user@"           creation_date="@creation_date@" /> 

lib/one-post.tcl

 set package_url [ad_conn package_url]  set now [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S"]  set creation_date_pretty [util::age_pretty \                               -timestamp_ansi $creation_date \                               -sysdate_ansi $now]  set author [acs_community_member_link -user_id $creation_user] set write_p [permission::permission_p -object_id $item_id -privilege write] set perm_url [export_vars -base "${package_url}post/view" {item_id}] 

lib/one-post.adp

 <div class="post">   <h2><a href="@perm_url@">@title@</a></h2>   <p class="byline">     Posted by @author;noquote@     <span class="date">@creation_date_pretty@</span>   </p>   <div class="content">     @content;noquote@   </div>   <p class="actions">     <if @write_p@>      <a href="@package_url@post/ae?item_id=@item_id@">Edit</a> |     <a href="@package_url@post/del?item_id=@item_id@">Delete</a>     </if>   </p> </div> 

Notes

You’ll notice that there are no SQL datamodel files. Since I’m using the CR, all of the datamodel is created by using the CR TCL API (specifically content::type::new). This package is subsite aware meaning you can install apps on multiple subsites and the data in each subsite will remain isolated from other subsites. It should uninstall itself cleanly since I’ve written callbacks for before-uninstantiate and before-uninstall.

Like I said, this is very simplistic. You can create, read, update and delete posts, but that’s about it. No commenting. No tags or categories. No RSS feeds. No search. But, since we’ve used the CR as our base, I think it would be very easy to extend the app to do all those things. I’m going to work on that, just for fun, of course. Thanks to Dave Bauer for writing the TCL interface to the CR and for writing the wiki package which made me see the light about the benefits of using the CR.

OpenACS via Darwinports

Wednesday, August 3rd, 2005

I’ve mentioned Darwinports before. I’ve finally gotten around to (minimally) documenting my Darwinports OpenACS installation. It’s not quite 1-click installation, but it’s getting closer.

Compiling OpenFTS on Mac OS X

Thursday, January 15th, 2004

It wasn’t easy, but I finally got OpenFTS-0.3.2-tcl to compile on Mac OS X (10.2.8). I started by reading the excellent Mac OS X porting guides from Fink and Apple. Unfortunately, the OpenFTS Makefile and configure scripts aren’t standard, so I had to muck around with things alot. Here’s the diff:

 diff -U 2 -rbB Search-OpenFTS-tcl-0.3.2/aolserver/Makefile  Search-OpenFTS-tcl-0.3.2-vk/aolserver/Makefile --- Search-OpenFTS-tcl-0.3.2/aolserver/Makefile	Tue Nov 19 14:24:44 2002 +++ Search-OpenFTS-tcl-0.3.2-vk/aolserver/Makefile	Wed Jan 14 23:23:13 2004 @@ -13,6 +13,6 @@  OBJ       = $(SOBJ) $(POBJ) $(OOBJ) nsfts.o  NSFTSLIB  = nsfts.so -LDSO      = gcc -shared  INC       = -I../include -I$(NS_HOME)/include -I$(NS_HOME)/nsd +MODLIBS += -L/sw/lib -L/usr/local/aolserver/lib -ltcl8.4 -lnsd    .SUFFIXES: .c .h .so .l @@ -26,5 +26,5 @@    $(NSFTSLIB): $(OBJ) -	$(LDSO) $(OBJ) $(LIBS) -o $(NSFTSLIB) +	$(LDSO) $(OBJ) $(LIBS) -o $(NSFTSLIB) $(MODLIBS)    clean: diff -U 2 -rbB Search-OpenFTS-tcl-0.3.2/configure Search-OpenFTS-tcl-0.3.2-vk/configure --- Search-OpenFTS-tcl-0.3.2/configure	Tue Nov 19 14:24:44 2002 +++ Search-OpenFTS-tcl-0.3.2-vk/configure	Wed Jan 14 22:36:44 2004 @@ -2771,4 +2771,16 @@  fi   +# +# Mac OS X 10.2 +# +# vinodk: not sure if all of this is needed/accurate +if test `uname` = "Darwin"; then +    PLATFORM="osx" +    CC="cc" +    LD="cc" +    CFLAGS="$CFLAGS -no-cpp-precomp" +    LDSO="$LD -flat_namespace -bundle" +fi +  if test $PLATFORM = "unknown"; then          { { echo "$as_me:2774: error: unknown platform" >&5 

Then, follow the instructions in AOLSERVER.INSTALL. I’m not too handy with this Makefile/configure stuff, so I’d appreciate any guidance on how to do this properly :-).

Now running OpenACS 5

Friday, January 9th, 2004

I upgraded to OpenACS 5 thanks to instructions from DaveB, et. al. I had a few hitches with that process, but the system seems to be working OK and is certainly zippier. I haven’t tested everything out yet, so please holler if anything breaks.