jsmith's Journal http://use.perl.org/~jsmith/journal/ jsmith's use Perl Journal en-us use Perl; is Copyright 1998-2006, Chris Nandor. Stories, comments, journals, and other submissions posted on use Perl; are Copyright their respective owners. 2012-01-25T02:25:19+00:00 pudge pudge@perl.org Technology hourly 1 1970-01-01T00:00+00:00 jsmith's Journal http://use.perl.org/images/topics/useperl.gif http://use.perl.org/~jsmith/journal/ RDF::Server 0.02 released http://use.perl.org/~jsmith/journal/35875?from=rss <p>Version 0.02 has been uploaded. It fixes a few things, such as a bug that worked on older versions of Moose. Apparently, a new Moose was released between the time I started development and when I released 0.01. Hopefully I've not had that happen again.</p><p>I've added beginning support for FastCGI. It passes tests using lighttpd as the translator from HTTP to FastCGI. I don't have process monitoring though, so I don't recommend it for production environments. For that matter, I wouldn't recommend any of RDF::Server for production yet.</p><p>I've added an RDF semantic. The semantic manages the configuration of how URIs are handled once the interface role has taken its stab at translating the HTTP request into something the semantic backend can work with. With the RDF semantic, the whole RDF model is a document. The semantic doesn't try to impose any subdivision on the model like the Atom semantic does.</p><p>Feed support is in development. Feeds shouldn't work yet. I'm probably going to bring SPARQL/RDQL into play along with feeds.</p><p>I added an rdf-server script that will take a configuration file and run the resulting service. No need to create Perl modules that bring together the appropriate roles. Some example configuration scripts are included in the distribution.</p><p>Enough code is there now that I can start working on the layers above/around it to do what I want to do, so I'll probably start focusing more on documentation. A lot of the detail isn't there that needs to be for someone coming to the distribution fresh.</p> jsmith 2008-03-10T22:12:42+00:00 journal RDF::Server 0.01 on CPAN Now http://use.perl.org/~jsmith/journal/35805?from=rss <p>I exceeded 90% overall test coverage, so 0.01 is out on CPAN. I still have a lot of documentation to write.</p><p> &nbsp; Protocols: Embedded (basic Perl API - implies the REST interface), HTTP (POE HTTP server)<br> &nbsp; Interface: REST<br> &nbsp; Semantic: Atom<br> &nbsp; Formatters: Atom, RDF, JSON</p><p>For version 0.02, I'm aiming for overall coverage of 95%. I'm adding an RDF semantic that works with the model as a complete document instead of the Atom semantic of working with parts of the model as documents. More bug fixes. Better documentation.</p><p>Atom support is a bit sketchy at the moment. Resources can be managed, but categories, collections, workspaces, and services are still a work in progress.</p><p>JSON support is read-only for now. I hope to allow resources to be created/modified using JSON in 0.03. I'll also try to add YAML support in 0.03.</p> jsmith 2008-03-01T03:39:01+00:00 journal RDF::Server 0.01 any day now http://use.perl.org/~jsmith/journal/35796?from=rss <p> I'm getting closer. I'm working on small details now. </p><blockquote><div><p> <code> All tests successful, 2 tests skipped (pod tests).<br> Files=17, Tests=219, 114 wallclock secs (81.32 cusr + 18.51 csys = 99.83 CPU)<br> <br> File stmt bran cond sub pod time total<br><nobr> <wbr></nobr>...<br> Total 88.2 70.2 55.9 89.1 35.8 100.0 82.1<br> </code></p></div> </blockquote><p> My goal is to get the Total:total coverage to at least 90% before doing an initial release. That will be additional tests, but might also include additional documentation. I hope I can get that as early as tomorrow (Friday). I'm at the point now where code is being driven completely by tests. </p><p> The first release won't be as strict as it should be on the Atom spec. It will, however, require that the<nobr> <wbr></nobr>/atom:entry/atom:content/@type of resources be 'application/rdf+xml' for now. </p><p> It will ship with read/write support for Atom and RDF as well as read support for JSON. I'll get write support for JSON in a near future release. I'll probably want it for some projects anyway. </p><p> It automagically manages an atom:updated and dc:created triple for each resource. The creator and other similar attributes will have to be supplied by an outside application for now. </p><p> RDF::Server doesn't understand authentication or authorization yet -- and I've not even thought a lot about that except that I might want to limit which predicates certain people can modify or read. I don't actually have a use case for that yet, so I've not tackled it. </p><p> The initial release will only support RDF::Core, but I want to extend support to RDF::Redland and probably RDF::Helper. Support for a triple store consists of two modules, one for the model and one for the resource. Everything else in the framework is (or should be) independent of the store. </p><p> Future directions: SPRQL, RDQL, and inference engines as feed producers -- because I want to do magic with the resulting feeds. </p> jsmith 2008-02-29T05:50:17+00:00 journal RDF::Server almost done http://use.perl.org/~jsmith/journal/35698?from=rss <p>By the power invested in me by Moose, I can do:</p><p> &nbsp; package My::Server;</p><p> &nbsp; use RDF::Server;<br> &nbsp; protocol 'HTTP';<br> &nbsp; interface 'REST';<br> &nbsp; semantic 'Atom';</p><p> &nbsp; render xml =&gt; 'Atom';<br> &nbsp; render rdf =&gt; 'RDF';</p><p>Then, to instantiate it, I use the following configuration (as an example):</p><p> &nbsp; my $server = My::Server -&gt; new(<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; default_renderer =&gt; 'Atom',<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; handler =&gt; [ service =&gt; (<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; path_prefix =&gt; '/',<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; workspaces =&gt; [<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title =&gt; 'Workspace',<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; collections =&gt; [<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title =&gt; 'All of Foo',<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; path_prefix =&gt; 'foo/',<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; model =&gt; {<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; namespace =&gt; 'http://www.example.com/foo/',<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; class =&gt; 'RDFCore'<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ]<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ]<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } ]<br> &nbsp; );</p><p>This results in the following urls:</p><p>- / - an app:service document<br>-<nobr> <wbr></nobr>/foo/ - an app:collection document (because there's no path component configured for the app:workspace)<br>-<nobr> <wbr></nobr>/foo/$id - an atom:entry document for the RDF resources centered around http://www.example.com/foo/$id.</p><p>The handler attribute is supposed to be a code ref that returns the information (so it can be dynamically built with each request if you really want it to be), but the module that defines the Atom semantics overloads the handler attribute's Moose type and allows coercion. This should allow configuration to be from a config file if the right Moose role is included in the server class.</p><p>I might make the rendering management configurable instead of part of the module definition. Unlike the protocol, interface, and semantic modules, the renderers don't include code, attributes, or expectations in the server class.</p><p>So far, I have passing tests for fetching app:service and app:collection documents and creating, fetching, and adding triples to RDF resources.</p><p>One thing I'm doing that might not be quite 'usual' is that I'm treating an RDF model as a collection of RDF resource documents. RDF resource documents are a collection of RDF triples centered around a particular RDF subject. I'm not treating the entire body of knowledge in the model as a single document. That's part of what's in the Atom semantic I'm working with.</p><p>Hopefully more next week, including something on CPAN with a lot more documentation than what I have now.</p> jsmith 2008-02-20T04:37:19+00:00 journal RDF/Atom/REST Server http://use.perl.org/~jsmith/journal/35618?from=rss <p>It's been a few years since I last posted here. I've gone on to a different job and jumped back into Perl after an extended time away. Stuff has happened in that time.</p><p>Like Moose.</p><p>The use of roles is nice. I can declare an expected interface and easily test that an object or class implements it. I can use that information to know what kind of capabilities I can expose to the world.</p><p>I'm working on a framework for building RDF servers. I need such a beast to support some research projects at the university, but decided that I should produce something that might be useful to someone else. Of course, that means it has to be able to do things that I don't need to do.</p><p>But I don't want to have to program every conceivable behavior.</p><p>Instead, I'm building a framework that lets me get done what I need while staying flexible enough that someone else can replace bits of it to get done what they need.</p><p>So far, I have the following as a way to build a server (subject to change, of course):</p><p> &nbsp; &nbsp; package My::Server;</p><p> &nbsp; &nbsp; use RDF::Server;</p><p> &nbsp; &nbsp; with 'MooseX::Daemonize';<br> &nbsp; &nbsp; with 'MooseX::SimpleConfig';<br> &nbsp; &nbsp; with 'MooseX::Getopt';</p><p> &nbsp; &nbsp; interface 'REST';<br> &nbsp; &nbsp; protocol 'HTTP';<br> &nbsp; &nbsp; style 'Atom';</p><p> &nbsp; &nbsp; #----------</p><p> &nbsp; &nbsp; my $server = My::Server -&gt; new(<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; handler =&gt; [ workspace =&gt; {<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; collections =&gt; [<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { # defines an app:collection<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; path_prefix =&gt; 'gallery',<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; entry_handler =&gt; [ RDFCore =&gt; { } ],<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; categories =&gt; [ ]<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ]<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }]<br> &nbsp; &nbsp; );</p><p> &nbsp; &nbsp; $server -&gt; start; # would run on port 8080 by default</p><p>That will bring together everything needed to run a standalone server exposing a REST interface over HTTP using POE. Other protocols could be Apache2 or FastCGI (or even plain CGI if we really wanted to). I'm not sure what other interfaces should be. If I can figure out how, I'll make the triad HTTP/REST/Atom the default.</p><p>My::Server-&gt;handler is an object that will handle the actual request. The HTTP and REST classes just handle the communication with the world and how the request gets handed off to the handler object. This lets the same data backend work with different protocols and interfaces.</p><p>The handlers determine if we're speaking RSS or Atom and how the path (as supplied by the interface class) maps to data. The interface class also determines what action is requested.</p><p>The 'style' declaration is optional and mainly determines how the configuration is handled. The Atom style expects a hierarchy based on RFC 5023, though the top-level document type can be any of the ones defined in the app: namespace.</p><p>Hopefully, if I can get everything together and tested and working, I'll get something out on CPAN in the next week or two.</p> jsmith 2008-02-08T23:49:30+00:00 journal Perl Syntax Mangling and XML Compilers http://use.perl.org/~jsmith/journal/23591?from=rss <p> My mind has been trying to wrap itself around the idea of an XML Compiler Compiler. That would be something that takes a description of an XML language (such as a RelaxNG description with some additional bits to explain how to actually do the compile) and writes a SAX handler that will do the compile. Of course, we need to allow one XML language to embed or be embedded in another XML language. (A lot of this comes from the work on the Gestinanna project that resulted in a compiler for statemachines and another for workflows that shared a lot of common code.) </p><p> With that in mind, I started fresh work on the compiler code without trying to hack the existing Gestinanna modules. I also changed where I put the commas and semi-colons. </p><p> Now, I have code like the following:</p><blockquote><div><p> <tt>package XML::Compiler::SAXHandler<br> <br>; sub new_handler {<br>&nbsp; &nbsp; my($type, %params) = @_<br> <br>&nbsp; ; return bless { %params<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;, Context =&gt; [ ]<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;, Current_NS =&gt; { }<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} =&gt; $type<br>}<br> <br>; sub start_document {<br>&nbsp; &nbsp; my $e = shift<br> <br>&nbsp; ; my $sub<br>&nbsp; ; foreach my $ns ($e -&gt; handled_namespaces) {<br>&nbsp; &nbsp; &nbsp; &nbsp; foreach my $h ($e -&gt; ns_handlers($ns)) {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if( ($sub = $h -&gt; can('start_document'))<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &amp;&amp; ($sub != \&amp;start_document) ) {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $sub-&gt;($h, $e) &amp;&amp; last<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; &nbsp; }<br>}<br> <br>; sub start_element {<br>&nbsp; &nbsp; my($e, $el) = @_<br>&nbsp; ; $el -&gt; {Parent} ||= $e -&gt; {Current_Element}<br>&nbsp; ; $el -&gt; {Namespaces} = { %{$el -&gt; {Parent} -&gt; {Namespaces} || {}}<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , %{$el -&gt; {Namespaces} || {}}<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br>&nbsp; ; $e -&gt; {Current_Element} = $el<br> <br>&nbsp; ; my $ns = $el -&gt; {NamespaceURI}<br> <br>&nbsp; ; my @attribs<br>&nbsp; ; my @defined_ns<br> <br>&nbsp; ; foreach my $attr (@{$el -&gt; {Attributes}}) {<br>&nbsp; &nbsp; }<br> <br>&nbsp; ; $el -&gt; {_Defined_NS} = \@defined_ns<br>&nbsp; ; $el -&gt; {Attributes} = \@attribs<br> <br>&nbsp; # need to handle block v. expression context setting<br>&nbsp; ; push @{$e -&gt; {Context}}, 0<br>}</tt></p></div> </blockquote><p> After you uncross your eyes, you might wonder why I put the semis and the commas at the beginning of the line (and instead of commas and semis being optional at the end of a block, they are now optional at the beginning of a block, with semis optional after a block as well). The key to this is the last comment in the code example. Semicolons delimit series of statements while commas delimit series of expressions. The traditional approach of putting these at the end of their respective part means we need to know the type of code we just emitted. By putting them at the beginning, we only need to know what kind of code we are expecting. </p><p> By managing what we expect when we see a start element, we can hopefully simplify some of the code that otherwise would need to handle the selection of the statement or expression terminator in the end element. </p> jsmith 2005-03-10T20:54:08+00:00 journal Gestinanna 0.02 http://use.perl.org/~jsmith/journal/20071?from=rss <p>The right way . . . is to separate the meaning of a program from the implementation details.</p><p>Saying less about implementation should also make programs more flexible. Specifications change while a program is being written, and this is not only inevitable, but desirable.</p><p>---Paul Graham, ``The Hundred-Year Language''</p><p>Gestinanna uses XML vocabularies to describe workflows and controllers as state machines. Using taglibs, it provides various extensions for accessing the Gestinanna::POF packages, workflows, scripting, authorization and authentication, and various misc. odds and ends.</p><p>I finally got around to getting the code released. Twice as much code as the previous release<nobr> <wbr></nobr>:) Work got a bit funner for a few weeks.</p><p>Change log:</p><p> &nbsp; o Basic workflow support. See the listserv package for an example.</p><p> &nbsp; o Added a package system. Shell support is not complete, but sufficient for installing a package.</p><p> &nbsp; o Moved sample pages into packages installable via the package system.</p><p> &nbsp; o Added site configuration single inheritance.</p><p> &nbsp; o Extensive unit tests in the low-level modules. Not yet complete.</p><p> &nbsp; o Split state machine XML schema into two namespaces: state machine and scripting.</p><p> &nbsp; o shell tool may be configured not to use a pager.</p><p> &nbsp; o Added `site clone' command to the shell. Both cloning and creating a site will now create a site configuration as well.</p><p> &nbsp; o Added `site config' command to the shell to edit an existing site's configuration. The resulting configuration must be parsable by XML::LibXML.</p><p> &nbsp; o The `site uri add' command now takes an argument to indicate the object type the URI refers to.</p><p> &nbsp; o Gestinanna::Request::get_url added to resolve an object to a url (for use by Apache::AxKit::Provider::Gestinanna).</p><p> &nbsp; o Alzabo naming routine updated for Alzabo 0.82.</p><p> &nbsp; o Namespace handlers for XSM scripts are now configurable on a per-site basis.</p><p> &nbsp; o Namespaces can be specified for namespace handlers, overriding the default. This is useful for using different handlers that offer the same interface but work with different backends.</p><p> &nbsp; o Configuration information specific to a content handler type is now enclosed in a tag.</p> jsmith 2004-07-26T19:44:24+00:00 journal Gestinanna release by end of week http://use.perl.org/~jsmith/journal/19415?from=rss <p>I should have a new release of Gestinanna by the end of the week. I have re-written quite a bit and modified even more<nobr> <wbr></nobr>:/ But we now have a package system and inheritable site configurations as well as inheritable RDBMS schema definitions. We have a *lot* of unit tests and some refactoring. We also have a new testing framework that lets me embed tests in the code (via pod) and track test results on a per-method basis. I also can make a nice dependency graph in SVG showing the test results.</p><p>I will get the Apache side of it working, make sure stuff installs and runs, and then ship a release. Workflows won't work yet, though some code is there. Security isn't tied in everywhere it needs to be. Several things still remain before it can be considered beta, but we're getting there.</p><p>Unfortunately, I might have to pause development for a while. It seems management prefers rapid development (with lots of security holes) over secure development (even though we handle SSNs and student grades). I'll know by the end of the week what will happen.</p> jsmith 2004-06-22T02:02:41+00:00 journal Update on Gestinanna http://use.perl.org/~jsmith/journal/19048?from=rss <p>I have a new supervisor. He started at the beginning of May, was here for two weeks, and is now finishing up a three-week vacation. It wasn't us<nobr> <wbr></nobr>:) He had already made plans for the vacation (and conference, actually), so they let him keep it. He was moving within the University, so is wasn't like he was changing employers.</p><p>He's had some good effects on the Gestinanna project. He's forced me to get organized such that we could bring someone else into the project if we needed to.</p><p>As a result, I have code that can sketch out the dependencies between the various modules. I can automatically build tests from tests embedded in pod (which I am now writing furiosly -- unit tests are good, but take a while to write). I can run tests in the order required by module and method dependencies. I can generate reports showing how many tests pass/fail for each method. I just need to make all this a subclass of Module::Build and release it. The SVG dependency graph is my personal favorite.</p><p>I've added initial support for workflows based on the Workflow module released on CPAN a short while ago. I had been wondering for some time who I was going to manage coordination between applications to make things happen in particular ways (approval of changes to allow them to go production, for example). Workflow.pm fell into my lap<nobr> <wbr></nobr>:)</p><p>I'm wrestling with continuations. I have rudementary ones in a sense (at least in the sense of a paper I saw the other day), but I can't do the following yet:</p><p> &nbsp; &lt;value name="/email"&gt;<br> &nbsp; &nbsp; &nbsp; &lt;call-out state-machine="/std/email/composer"/&gt;<br> &nbsp; &lt;/value&gt;</p><p>and have<nobr> <wbr></nobr>/email//* be all the information needed to send the e-mail. This is a simple and contrived exmaple in some ways, but the more interesting example is when one program dynamically determines that it must call another to get needed information (such as a site configuration manager calling out to the machine for a particular content provider type - allowing dynamic addition of new content provider types without requiring changes to the overall config manager application).</p><p>Part of the problem is that we are using a SAX-based XML-&gt;Perl compiler. By time we know we need to manage a continuation, we might be embedded in a loop, if-then-else (choose/when/otherwise) sequence, or other difficult context. If we assume we will have a continuation, then loops will probably be slow and clunky for all cases. I'll have to think about this some more, but it doesn't look impossible -- just a little difficult at the moment.</p><p>Continuations in the above sense won't be on CPAN by OSCon, but I hope the other stuff is. That's my goal anyway at this point.</p> jsmith 2004-06-02T14:05:05+00:00 journal Accidental Inheritable Site Configurations http://use.perl.org/~jsmith/journal/18361?from=rss <p>Another version of the Gestinanna framework is on its way -- still working out some ripples. Exciting though.</p><p>I went to a newly installed test system to install the framework and found several assumptions that caused it to fail. It had worked just fine on my development system because it and that system had grown up together. Anyone else will see errors and missing parts if they try to install it. Oh, well. It's only the initial release of a fairly large, complex system. Not a good first impression, but hopefully it can be fixed with a second release.</p><p>Well, what started as an effort to just fix those problems and get another release out has snowballed into a series of refactorings that are making things a lot better.</p><p>There's now a packaging system. The shell tool can create and open packages (just compressed tarballs), list the contents, edit the contents of a file, manage the configuration file, manifest, and readme, and write them to disk. The package manager can find them. They can almost be installed.</p><p>The installation of a package led me to wonder where the information was that told me where to install them to. I wasn't trying to force installation on a per-site basis, especially when all the sites in a schema share the tables in a schema, which is where the files are installed to from the package. The repository for views, controllers, xslt, etc., is in the RDBMS schema and not necessarily tied to a particular site. Yet all the configuration information that tied a data type (view, xsm, xslt, etc.) to a table (View, XSM, XSLT, etc. and their associated auxiliary tables) was tied to a site.</p><p>So I saw the need for a global configuration. But if I try to create a site 0 using Alzabo, it ignores the primary key (site = 0) and uses the next sequence (since it is auto-incrementing the primary key). What to do....</p><p>Now, I have a site configuration package, Gestinanna::SiteConfiguration, whose constructor can take a parent site as an argument. This parent site doesn't have to be a global configuration. It just supplies information that can be overridden or augmented by the child site. The key is that the parent site is just a Gestinanna::SiteConfiguration object.</p><p>This simple design decision led to me being able to make arbitrarily long chains of single-inheriting site configurations<nobr> <wbr></nobr>:) Something I had been wanting to do for a long time, but hadn't quite figured out. It wasn't something that was important to me since we didn't really need it that much, but I knew someone else might want it. Now we have it, and it may make life a *lot* easier for anyone using this framework to manage multiply-branded applications.</p><p>Now, all the tag paths, data providers, content providers, etc., can be configured in one top-level configuration for a site that is never exposed. All the exposed sites can then inherit from it and just change the theme - resulting in the ability to manage one site, but have it show up on multiple URLs with different brandings.</p><p>The ripples? Well, the site configuration is now managed by a class instead of being a hash that the rest of the system could freely inspect. Data providers (for example, Gestinanna::POF::*) now need to be able to get info from the DOM during configuration (done during server startup). Content providers (Gestinanna::ContentProvider::*) likewise. URL mappings will need to be inheritable instead of looking at current site and site 0 (the marker for a global mapping). We now have a site path to search in addition to the tag path. The factory is created and managed by the site configuration. Etc., etc.,<nobr> <wbr></nobr>....</p><p>I was hoping to have a new set of packages out this week, but it looks like I'll need to hold off until next week. All because I wanted to fix some errors and get away from telling people to put a file in a particular location after installation<nobr> <wbr></nobr>:/</p> jsmith 2004-04-16T14:17:27+00:00 journal First release of Gestinanna framework http://use.perl.org/~jsmith/journal/17611?from=rss <p>I finally got enough code together to put something out on CPAN. It's not complete, but enough is there to get it running.</p><p>The framework maps urls to content. Four content providers are in the base distribution: documents (unparsed), views (parsed by Template Toolkit), portals (documents that can embed other content), and xsm (eXtensible State Machines). The last one, xsm, acts as the controller in an application. Each state can map to a view which has access to the state information.</p><p>Sessions are handled automatically. If cookies are not available, then the URL is mangled, though some support work remains for this -- external links don't redirect correctly yet to lose the session id information. Each state machine has its own independent context. If I have time, I want to develop an application that allows help desk people to trace the path a customer took through an application so they can see what the customer might have done wrong or what the customer needs to do next.</p><p>Forms automatically fill themselves out based on the context information. The TT2 view doesn't have to track default values. Using the XPath-like expression syntax in XSM makes data management in complex forms simple -- take a look at the wizard creation wizard for an example: we have a wizard that can contain multiple steps, each of which can require multiple pieces of data.</p><p>The XML schema for form markup makes embedding one form in another simple. It also means the form elements should be coded based on their logical relationship to one another, not based on how the page should be laid out.</p><p>Views are inheritable, for the most part (this may still require a bit of work). I can subclass an existing application and only change those views that require changing. Only change those state transitions that require changing.</p><p>I think it's neat. There's too much there to try and cram it all into one post. If you have time and want to just play around with some new code, download the packages and give it a spin. I'd love to hear what you think.</p> jsmith 2004-02-25T06:14:38+00:00 journal Transforming programs and XSM http://use.perl.org/~jsmith/journal/15743?from=rss <p> I've been working some more on my eXtensible State Machines --- web applications written using an XML language. Some interesting things are coming out of this. (And I hope to have something released around the end of the month if I can get certain testing done.) </p><p> First, there are continuations. I don't have those implemented yet, but I have about half-continuations done. That is, you can suspend the current program and run another, sending views to the client browser and processing the input. Then, when that program is finished, you can drop back to the prior program in the state you left it. You don't yet get dropped back into the code where you called out to the other program and you can't pass data back to the calling program. That would be full continuations. </p><p> Second, I borrowed an idea from AxKit/XSP (<a href="http://www.perl.com/lpt/a/2002/07/02/axkit.html">see ``Taglib TMTOWTDI''</a>) and now support applying XSLTs to the programs written in XML. Makes sense. XSLT is designed to transform XML documents. Now, instead of running program P, I can run a transform of it: f(P), f: XML -&gt; XML. I like the mathematical notions. </p><p> This is useful because some (many?) of the little applications we are needing to write consist of a series of pages collecting information and then a final step doing something with that information. If we wanted to support `Prev', `Next', etc., functionality to make a wizard-like flow, we had to code transitions for each one in each state. Now, I can write something like: </p><blockquote><div><p> <tt> &lt;statemachine&gt;<br>&nbsp; &nbsp;&lt;wiz:steps&gt;<br>&nbsp; &nbsp; &nbsp;&lt;wiz:step&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp;&lt;variable id="filename"/&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp;&lt;variable id="title"/&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp;&lt;variable id="description"/&gt;<br>&nbsp; &nbsp; &nbsp;&lt;/wiz:step&gt;<br>&nbsp; &nbsp; &nbsp;&lt;wiz:step&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp;&lt;variable id="states"/&gt;<br>&nbsp; &nbsp; &nbsp;&lt;/wiz:step&gt;<br>&nbsp; &nbsp; &nbsp;&lt;wiz:step&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp;&lt;variable id="state.initial"/&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp;&lt;variable id="state.transitions"/&gt;<br>&nbsp; &nbsp; &nbsp;&lt;/wiz:step&gt;<br>&nbsp; &nbsp;&lt;/wiz:steps&gt;<br> &lt;/statemachine&gt;</tt></p></div> </blockquote><p> You can get the idea. Applying an appropriate XSLT ensures that I can go back and forth through the pages with proper validation and storage of the data without having to duplicate a lot of code by hand. So now, I have XML -&gt; f(XML) -&gt; Perl(f(XML)) -&gt; byte-code(Perl(f(XML))) -&gt;<nobr> <wbr></nobr>.... </p><p> Third thing (second that I did today) is making a form element. </p><blockquote><div><p> <tt> &lt;grid id="things" count="multiple"&gt;<br>&nbsp; &nbsp;&lt;column id="foo"/&gt;<br>&nbsp; &nbsp;&lt;column id="bar"/&gt;<br>&nbsp; &nbsp;&lt;row id="red"/&gt;<br>&nbsp; &nbsp;&lt;row id="black"/&gt;<br> &lt;/grid&gt;</tt></p></div> </blockquote><p> This produces a 2x2 grid of checkboxes allowing the selection of the values red.foo, red.bar, black.foo, and black.bar. Different values of @count affect whether its a checkbox or radio button, and whether it is constrained by row, by column, or not at all. Now I just need to get the XSM content provider to be able to manage the default values automatically. </p> jsmith 2003-11-13T06:03:30+00:00 journal First Post and eXtensible State Machines http://use.perl.org/~jsmith/journal/14891?from=rss <p> I'm on <a href="http://luggage.livejournal.com/">LiveJournal</a>, but most of the people I know there aren't into Perl. This semester, I've been busy digging into a project that I've been working on for a year or so now. It's finally time to get it done. So, because I'm enthusiastic about it and spending long hours each day on it, I'll let some of that enthusiasm bubble over here. </p><p> I've been working on a XPath-like expression language for Perl data structures using Barrie Slaymaker's EventPath grammer which was based on James Clark's XPath grammer. I can now say<nobr> <wbr></nobr><code>/this/method::foo(/that)[@can="dance"]</code> and get back any objects returned from the <code>foo</code> method of<nobr> <wbr></nobr><code>/this</code> called with<nobr> <wbr></nobr><code>/that</code>, and select those returned objects that <code>can('dance')</code>. Eventually I'll factor most of the code out and put it on CPAN as Data::DPath. </p><p> The Data::DPath stuff is the expression component of what I am calling eXtensible State Machines. These are like eXtensible Server Pages except they have no Perl (they are pure XML) and define the controller in an MVC application. The View is going to be provided by Template Toolkit (but that's configurable). I haven't decided what XSM will be called when I factor it out for CPAN. </p><p> The state machine compiler is borrowed from / based on the XSP compiler in AxKit. At this point, I'm more interested in it working correctly than efficiently. The data in the %EDGES hash is used to create Data::FormValidator objects to help decide which state to transition to. Of course, details are subject to change while in development. </p><blockquote><div><p> <tt>&lt;statemachine xmlns="http://some/url"&gt;<br>&nbsp; &lt;alias id="_begin" state="start"/&gt;<br>&nbsp; &lt;state id="start"&gt;<br>&nbsp; &nbsp; &lt;transition state="edit"&gt;<br>&nbsp; &nbsp; &nbsp; &lt;variable id="uin"/&gt;<br>&nbsp; &nbsp; &nbsp; &lt;group id="birth"&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;variable id="day"/&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;variable id="month"/&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;variable id="year"/&gt;<br>&nbsp; &nbsp; &nbsp; &lt;/group&gt;<br>&nbsp; &nbsp; &lt;/transition&gt;<br>&nbsp; &lt;/state&gt;<br>&nbsp; &lt;state id="edit"&gt;<br>&nbsp; &nbsp; &lt;transition state="confirm"&gt;<br>&nbsp; &nbsp; &nbsp; &lt;variable id="netid"/&gt;<br>&nbsp; &nbsp; &nbsp; &lt;variable id="password1"/&gt;<br>&nbsp; &nbsp; &nbsp; &lt;variable id="password2"/&gt;<br>&nbsp; &nbsp; &nbsp; &lt;script&gt; &lt;!-- defines the edit_to_confirm subroutine --&gt;<br>&nbsp; &nbsp; &nbsp; &nbsp; &lt;assert test="not(string-cmp(/password1,/password2))" state="edit"/&gt;<br>&nbsp; &nbsp; &nbsp; &lt;/script&gt;<br>&nbsp; &nbsp; &lt;/transition&gt;<br>&nbsp; &lt;/state&gt;<br>&nbsp; &lt;state id="confirm"&gt;<br>&nbsp; &nbsp; &lt;transition state="done"&gt;<br>&nbsp; &nbsp; &nbsp; &lt;variable id="confirm"/&gt;<br>&nbsp; &nbsp; &lt;/transition&gt;<br>&nbsp; &nbsp; &lt;transition state="edit"&gt;<br>&nbsp; &nbsp; &nbsp; &lt;variable id="edit"/&gt;<br>&nbsp; &nbsp; &lt;/transition&gt;<br>&nbsp; &lt;/state&gt;<br>&lt;/statemachine&gt;</tt></p></div> </blockquote><p>is compiled to the following Perl code:</p><blockquote><div><p> <tt>package Gestinanna::Sites::Hinoto::XSM::activate::V1_5;<br># line 555 "Perl generated by<nobr> <wbr></nobr>/usr/local/lib/perl5/site_perl/5.8.0/Gestinanna/XSM.pm"<br>#initialize sm namespace<br>sub edit_to_confirm {<br>&nbsp; &nbsp; my ($sm) = shift;<br>&nbsp; &nbsp; my %data = (<br>&nbsp; &nbsp; &nbsp; &nbsp; local =&gt; $sm-&gt;data('out'),<br> <br>&nbsp; &nbsp; &nbsp; &nbsp; #session =&gt; $sm -&gt; session,<br>&nbsp; &nbsp; &nbsp; &nbsp; context =&gt; $sm-&gt;data, solar =&gt; {}, global =&gt; {},<br>&nbsp; &nbsp; );<br> <br>&nbsp; &nbsp; return "edit" # &lt;assert test="..." state="..."/&gt;<br>&nbsp; &nbsp; &nbsp; unless (<br>&nbsp; &nbsp; &nbsp; &nbsp; (<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Gestinanna::XSM::Core::xsm_not(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Gestinanna::XSM::Core::xsm_string_cmp(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;grep { defined } map {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Gestinanna::XSM::Expression::axis_child($_,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "password1" )<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} $data{"local"}<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;)[0],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; grep { defined } map {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Gestinanna::XSM::Expression::axis_child($_,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "password2" )<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } $data{"local"}<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;)[0],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;)[0],<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;)<br>&nbsp; &nbsp; &nbsp; &nbsp; )<br>&nbsp; &nbsp; &nbsp; );<br> <br>&nbsp; &nbsp; return;<br>} ## end sub edit_to_confirm<br>use vars qw(@ISA %HASA %VIEWS %ALIASES %EDGES);<br> <br>@ISA&nbsp; &nbsp; &nbsp;= ('Gestinanna::XSM::Base');<br>%HASA&nbsp; &nbsp; = ();<br>%VIEWS&nbsp; &nbsp;= ();<br>%ALIASES = ('_begin' =&gt; 'start');<br>%EDGES = (<br>&nbsp; &nbsp; &nbsp; &nbsp;'confirm' =&gt; { 'done' =&gt; { 'required' =&gt; ['confirm'] },<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'edit' =&gt; { 'required' =&gt; ['edit'] }<br>&nbsp; &nbsp; &nbsp; &nbsp;},<br>&nbsp; &nbsp; &nbsp; &nbsp;'edit' =&gt; # required to get from edit to confirm<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{ 'confirm' =&gt; { 'required' =&gt; ['netid', 'password1', 'password2'] } },<br>&nbsp; &nbsp; &nbsp; &nbsp;'start' =&gt; {<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'edit' =&gt; # required to get from start to edit<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{ 'required' =&gt; ['uin', 'birth.day', 'birth.month', 'birth.year'] }<br>&nbsp; &nbsp; &nbsp; &nbsp;}<br>);</tt></p></div> </blockquote> jsmith 2003-09-25T03:55:11+00:00 journal