Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

CRLF madness, hopefully this will fix the batch of files with wrong l…

…ine endings
  • Loading branch information...
commit 36fae47292b93b22766d46c4778c3ae7d8512052 1 parent b6655b7
@wesen wesen authored
View
96 doc/README-orig
@@ -1,48 +1,48 @@
-BKNR CODENAME: SPUTNIK
-
-Hans Huebner, David Lichteblau, Manuel Odendahl
-
-1. Introduction
-
-BKNR is a software launch platform for LISP satellites. You could
-replace ``launch platform'' with framework and ``satellites'' with
-``applications'', but that would be too many buzzwords.
-
-BKNR is made of facilities that are not very useful on their own, but
-they can be used to quickly build shiny and elegant LISP
-satellites. For example, a very important component of BKNR is its
-datastore, which brings persistence to CLOS in a very simple way. By
-adding a few declarations to your class definitions, you can have
-persistent objects. You can also add XML import/export to your objects
-in a similar way. I think this is the single most attractive feature
-of BKNR: no more mapping from a relational database to LISP objects,
-no more XML parsing and XML generation, you just write plain
-application code.
-
-2. Installation
-
-BKNR has been developed with CMUCL 19a under FreeBSD, and has been
-tested with Allegro Common Lisp 6.2 under Windows and Freebsd. Install
-the BKNR sourcecode and the thirdparty sourcecode.
-
-Then configure the pathnames in bknr/init.lisp, and load
-bknr/init.lisp. Afterwards, you can use ASDF to load the BKNR
-facilities.
-
-To load the BKNR indices facility:
-(asdf:oos 'asdf:load-op :bknr-indices)
-
-To load the BKNR datastore facility:
-(asdf:oos 'asdf:load-op :bknr-indices)
-
-To load the BKNR impex facility:
-(asdf:oos 'asdf:load-op :bknr-indices)
-
-To load the BKNR framework:
-(asdf:oos 'asdf:load-op :bknr)
-
-3. Further documentation
-
-You can read the BKNR manual in bknr/doc/ . You can also browse the
-sourcecode for the tutorials in bknr/src/indices/tutorial.lisp,
-bknr/src/data/tutorial.lisp and bknr/src/xml-impex/tutorial.lisp.
+BKNR CODENAME: SPUTNIK
+
+Hans Huebner, David Lichteblau, Manuel Odendahl
+
+1. Introduction
+
+BKNR is a software launch platform for LISP satellites. You could
+replace ``launch platform'' with framework and ``satellites'' with
+``applications'', but that would be too many buzzwords.
+
+BKNR is made of facilities that are not very useful on their own, but
+they can be used to quickly build shiny and elegant LISP
+satellites. For example, a very important component of BKNR is its
+datastore, which brings persistence to CLOS in a very simple way. By
+adding a few declarations to your class definitions, you can have
+persistent objects. You can also add XML import/export to your objects
+in a similar way. I think this is the single most attractive feature
+of BKNR: no more mapping from a relational database to LISP objects,
+no more XML parsing and XML generation, you just write plain
+application code.
+
+2. Installation
+
+BKNR has been developed with CMUCL 19a under FreeBSD, and has been
+tested with Allegro Common Lisp 6.2 under Windows and Freebsd. Install
+the BKNR sourcecode and the thirdparty sourcecode.
+
+Then configure the pathnames in bknr/init.lisp, and load
+bknr/init.lisp. Afterwards, you can use ASDF to load the BKNR
+facilities.
+
+To load the BKNR indices facility:
+(asdf:oos 'asdf:load-op :bknr-indices)
+
+To load the BKNR datastore facility:
+(asdf:oos 'asdf:load-op :bknr-indices)
+
+To load the BKNR impex facility:
+(asdf:oos 'asdf:load-op :bknr-indices)
+
+To load the BKNR framework:
+(asdf:oos 'asdf:load-op :bknr)
+
+3. Further documentation
+
+You can read the BKNR manual in bknr/doc/ . You can also browse the
+sourcecode for the tutorials in bknr/src/indices/tutorial.lisp,
+bknr/src/data/tutorial.lisp and bknr/src/xml-impex/tutorial.lisp.
View
14 doc/example.tex
@@ -1,7 +1,7 @@
-\chapter{Application Example}
-
-\begin{figure}[htbp]
- \centering
-\includegraphics{applicationicon}
-\end{figure}
-
+\chapter{Application Example}
+
+\begin{figure}[htbp]
+ \centering
+\includegraphics{applicationicon}
+\end{figure}
+
View
426 doc/guidedtour.tex
@@ -1,213 +1,213 @@
-\chapter{Guided Tour}
-
-\vbox{
-\centering
-\includegraphics{guidedtouricon}
-\vspace{1cm}
-}
-
-Let's take a guided tour of the BKNR sourcecode. Most facilities are
-independent of each other, and can be used to build completely
-different types of applications. However, all use the common toolbox
-called `BKNR-UTILS', which contains a lot of small lisp
-functions. These functions are not very important, and we will leave
-them behind to get to the first important facility of the BKNR launch
-platform.
-
-\section{The Datastore}
-
-The datastore is one of the oldest facilities in BKNR, its role is to
-store the application data, so that this data is persistent between
-LISP sessions. Most existing applications use a back-end
-database to store persistent objects, for example a relational
-database or an object-oriented database. Despite the immediate
-advantages of having an existing database (reliability, speed,
-management tools), this approach is quite cumbersome: every
-application data structure has to be mapped onto a data structure in
-the database. This is not trivial, as can be seen by the gazillions
-attempts to write automatic conversion tools. Another fundamental
-problem is the integration of the database into the language itself,
-and into the development workflow. Errors in the database are
-"external" to the LISP process, rollback, error-catching, transactions
-are not as easy to write as they would be by having an "internal"
-database. In the end, we believe that the advantages of having an
-external database are outweighed by the disadvantages.
-
-With the decreasing hardware costs nowadays, it is quite feasible for
-most applications to keep their entire dataset in memory, and indeed
-this is the approach taken by the BKNR datastore. Even with a growing
-data set, memory costs decrease faster than the data grows, so that
-evolution is not a problem. This approach is known as "prevalence",
-and the BKNR datastore was initially based on the prevalence solution
-by Sven Van Caekenberghe (also see
-`http://homepage.mac.com/svc/prevalence/readme.html'). The key points
-of the prevalence model are:
-
-\begin{itemize}
-\item All data is held in RAM
-
-\item Changes to persistent data are written to a transaction log
- file. When loading the datastore, the transaction log is read, and
- all changes are applied to the persistent data. All changes ever
- made to the persistent data are executed in order, and the data is
- recovered.
-
-\item If the data model supports it, the persistent data state can be
- captured and written to a file, which is called a "snapshot
- file". The snapshot file is read when loading the datastore, and
- the snapshotted data is used as a "starting point" for the
- transaction log.
-\end{itemize}
-
-In the datastore, transactions which are logged to the transaction log
-are LISP functions defined using the `DEFTRANSACTION' form. Thus, all
-transactions transforming persistent data are made explicit in the
-source code. This if different from object-oriented databases, where
-the fundamental transactions are object creation, object deletion and
-slot access, which are not special cases in the prevalence model at
-all. The main problem with this approach is that it is possible to
-modify the persistent data in a way that is not logged into the
-transaction log, which leads to lost data after reloading the
-database. However, the BKNR datastore has a lot of development and
-debugging helps, warning the user when he is making dangerous changes
-(or forbidding them alltogether).
-
-The datastore has gone through a few development iterations, which are
-actually quite interesting to explain, as they show which compromises
-have been made, and how the datastore can be tweaked. After having
-used the prevalence solution of Sven van Caekenberghe and deciding
-that it didn't quite fit our needs, we wrote a simple datastore
-closely modelled on Sven's approach. However, the datastore featured
-helpers to create and modify indexed CLOS objects. Also, the
-transaction log consisted of SEXPs, and the loading of the transaction
-log consisted of using the `LOAD' LISP function. The main part of the
-object datastore consisted of a really big `DEFINE-PERSISTENT-CLASS'
-macro, which generated a `DEFCLASS' form, and overrided a lot of
-methods, making up a store generic method protocol. Special slot
-options could be used to tell the store to index objects. For example,
-you could specify that the slot `NAME' of the class `USER' was stored
-in an index called `USER-NAME-INDEX'. The persistent objects could be
-snapshotted and written to a snapshot file, which again was a LISP
-file.
-
-The main problem with this first approach was the lack of real CLOS
-support. For example, you could modify an object using standard CLOS
-methods like `SETF SLOT-VALUE' without ever getting a warning that
-modifying the persistent state outside a transaction is
-dangerous. Moreover, the `DEFINE-PERSISTENT-CLASS' macro was difficult
-to modify, and the slot options for indices could not be modified
-easily. This lead to the development of a separate index layer, a
-separate transaction system, and of an object datastore combining the
-two systems with additional CLOS support built using the Metaobject
-Protocol.
-
-\subsection{The transaction layer}
-
-The transaction layer is now separate from the object oriented
-datastore. It provides the functionality to declare explicit
-transactions (still using the `DEFTRANSACTION' form), and to serialize
-the transactions coming from multiple threads to a transaction log in
-the filesystem. Using the transaction system is done by instantiating
-an instance of the `STORE' class (or `MP-STORE' to serialize
-concurrent transactions) and specifying a directory where the
-transaction logs has to be stored. Only a single transaction system
-can be open in a LISP session. When a transaction is executed, the
-store serializes the transaction call (the transaction name and its
-arguments) to the transaction log. It also stores the timestamp at
-which the transaction was executed. This allows the user to restore
-the transaction system state until a certain time (for debugging his
-application or reverting to an old data state). When a datastore is
-created or restored, the transaction log is read, and all the
-transaction calls are re-executed. After all the transactions have
-been executed, the persistent state is restored.
-
-A transaction store can have multiple subsystems, which control a
-certain subset of the persistent data. For example, the object
-datastore which controls persistent CLOS objects is realized as a
-subsystem of the transaction layer. A subsystem can snapshot the
-current persistent state, and write it to disk. When the persistent
-data is snapshotted, the transaction log can be discarded. Restoring
-this state then consists of loading the snapshot file, and then
-executing the transactions stored in the new transaction log. This
-allows for a much faster restore procedure. The snapshotting procedure
-takes care to backup the old transaction log, so that a restore until
-a certain timestamp is still possible.
-
-\subsection{The object datastore}
-
-The object datastore combines several aspects:
-\begin{itemize}
- \item It is a subsystem of the transaction layer, and can snapshot
-persistent CLOS objects to a snapshot file, saving their slot values.
- \item It provides a CLOS Metaclass for persistent objects. Using
-this metaclass, you can specify additional slot options for persistent
-objects. For example, you can specify that a certain slot is
-transient, which means that it will not be snapshotted and that its
-value can be changed outside of a transaction.
- \item The persistent object metaclass also provides the index
-facility of `BKNR-INDICES', so that persistent objects automatically
-get indexed when restored from the store. The index facility is also
-used by the store itself to keep track of the instantiated objects of
-each class, and to index the objects by their unique ID.
-\end{itemize}
-
-The object datastore also provides a few helpers for developers. For
-example, you cannot change the value of a slot when you are outside of
-a transaction. The store throws an error when such an illegal
-operation is made. The store also warns you when you change a class
-definition. As the class definitions are not stored in the datastore,
-a snapshot is necessary after schema evolution.
-
-\section{The XML Import/Export Facility}
-
-BKNR applications often have to communicate with the external world,
-for example getting data from external sources, or exporting the data
-from the object datastore to external sources. In order to reduce the
-amount of conversion and parsing code that has to be written to
-accomodate these tasks, the BKNR framework provides an automatic
-mapping from CLOS classes to XML data. Using a special Metaclass for
-XML objects, you can map a CLOS class to XML according to a XML
-DTD.
-
-In the following paragraphs, we will use the CLOS class `USER' to show
-how the XML import/export facility works. The class `USER' has a slot
-`PARENT' which points to another `USER', a slot `NAME' which contains
-a string (the name of the user, obviously) and a slot `AGE' which
-contains an integer (the age of the user).
-
-A DTD consists of element declaration and of attribute declarations
-(we will leave entity declarations aside for now). Most of the time,
-the mapping between the XML data and the CLOS representation is quite
-straightforward. An object is represented as an XML element, and its
-slots are represented as either attributes of the element, or child
-elements. All the data in the XML file is encoded as a string, and
-read as such by the CXML parser used by BKNR. You can however specify
-custom parsing and serializing routines for slots.
-
-After the CLOS class has been annotated using specific slot-options,
-objects can be read from an existing XML file (which validates against
-the DTD file), or CLOS objects can be serialized to XML. Furthermore,
-parent-child relations can be directly created by the parsing code, or
-following by the serializing code. XML impex slot options can also be
-combined with BKNR indices, which makes browsing XML data very simple.
-
-Exporting and importing XML data now is a breeze!
-
-\section{The BKNR Web Framework}
-
-BKNR also features a web framework, providing an object oriented web
-handler architecture. Adding handlers for special kinds of data (for
-example RSS feeds for a custom data structure) can easily be achieved
-using multiple inheritance. Furthermore, a template handler is
-available, making it possible to access LISP functions from an XHTML
-file. Sadly, as the web framework has been heavily rewritten in the
-last months, we were not able to document it. However, you can have a
-look at the example sourcecode for different applications on the bknr
-webpage.
-
-
-%\section{The Templater}
-
-
-%\section{eboy.com}
-
+\chapter{Guided Tour}
+
+\vbox{
+\centering
+\includegraphics{guidedtouricon}
+\vspace{1cm}
+}
+
+Let's take a guided tour of the BKNR sourcecode. Most facilities are
+independent of each other, and can be used to build completely
+different types of applications. However, all use the common toolbox
+called `BKNR-UTILS', which contains a lot of small lisp
+functions. These functions are not very important, and we will leave
+them behind to get to the first important facility of the BKNR launch
+platform.
+
+\section{The Datastore}
+
+The datastore is one of the oldest facilities in BKNR, its role is to
+store the application data, so that this data is persistent between
+LISP sessions. Most existing applications use a back-end
+database to store persistent objects, for example a relational
+database or an object-oriented database. Despite the immediate
+advantages of having an existing database (reliability, speed,
+management tools), this approach is quite cumbersome: every
+application data structure has to be mapped onto a data structure in
+the database. This is not trivial, as can be seen by the gazillions
+attempts to write automatic conversion tools. Another fundamental
+problem is the integration of the database into the language itself,
+and into the development workflow. Errors in the database are
+"external" to the LISP process, rollback, error-catching, transactions
+are not as easy to write as they would be by having an "internal"
+database. In the end, we believe that the advantages of having an
+external database are outweighed by the disadvantages.
+
+With the decreasing hardware costs nowadays, it is quite feasible for
+most applications to keep their entire dataset in memory, and indeed
+this is the approach taken by the BKNR datastore. Even with a growing
+data set, memory costs decrease faster than the data grows, so that
+evolution is not a problem. This approach is known as "prevalence",
+and the BKNR datastore was initially based on the prevalence solution
+by Sven Van Caekenberghe (also see
+`http://homepage.mac.com/svc/prevalence/readme.html'). The key points
+of the prevalence model are:
+
+\begin{itemize}
+\item All data is held in RAM
+
+\item Changes to persistent data are written to a transaction log
+ file. When loading the datastore, the transaction log is read, and
+ all changes are applied to the persistent data. All changes ever
+ made to the persistent data are executed in order, and the data is
+ recovered.
+
+\item If the data model supports it, the persistent data state can be
+ captured and written to a file, which is called a "snapshot
+ file". The snapshot file is read when loading the datastore, and
+ the snapshotted data is used as a "starting point" for the
+ transaction log.
+\end{itemize}
+
+In the datastore, transactions which are logged to the transaction log
+are LISP functions defined using the `DEFTRANSACTION' form. Thus, all
+transactions transforming persistent data are made explicit in the
+source code. This if different from object-oriented databases, where
+the fundamental transactions are object creation, object deletion and
+slot access, which are not special cases in the prevalence model at
+all. The main problem with this approach is that it is possible to
+modify the persistent data in a way that is not logged into the
+transaction log, which leads to lost data after reloading the
+database. However, the BKNR datastore has a lot of development and
+debugging helps, warning the user when he is making dangerous changes
+(or forbidding them alltogether).
+
+The datastore has gone through a few development iterations, which are
+actually quite interesting to explain, as they show which compromises
+have been made, and how the datastore can be tweaked. After having
+used the prevalence solution of Sven van Caekenberghe and deciding
+that it didn't quite fit our needs, we wrote a simple datastore
+closely modelled on Sven's approach. However, the datastore featured
+helpers to create and modify indexed CLOS objects. Also, the
+transaction log consisted of SEXPs, and the loading of the transaction
+log consisted of using the `LOAD' LISP function. The main part of the
+object datastore consisted of a really big `DEFINE-PERSISTENT-CLASS'
+macro, which generated a `DEFCLASS' form, and overrided a lot of
+methods, making up a store generic method protocol. Special slot
+options could be used to tell the store to index objects. For example,
+you could specify that the slot `NAME' of the class `USER' was stored
+in an index called `USER-NAME-INDEX'. The persistent objects could be
+snapshotted and written to a snapshot file, which again was a LISP
+file.
+
+The main problem with this first approach was the lack of real CLOS
+support. For example, you could modify an object using standard CLOS
+methods like `SETF SLOT-VALUE' without ever getting a warning that
+modifying the persistent state outside a transaction is
+dangerous. Moreover, the `DEFINE-PERSISTENT-CLASS' macro was difficult
+to modify, and the slot options for indices could not be modified
+easily. This lead to the development of a separate index layer, a
+separate transaction system, and of an object datastore combining the
+two systems with additional CLOS support built using the Metaobject
+Protocol.
+
+\subsection{The transaction layer}
+
+The transaction layer is now separate from the object oriented
+datastore. It provides the functionality to declare explicit
+transactions (still using the `DEFTRANSACTION' form), and to serialize
+the transactions coming from multiple threads to a transaction log in
+the filesystem. Using the transaction system is done by instantiating
+an instance of the `STORE' class (or `MP-STORE' to serialize
+concurrent transactions) and specifying a directory where the
+transaction logs has to be stored. Only a single transaction system
+can be open in a LISP session. When a transaction is executed, the
+store serializes the transaction call (the transaction name and its
+arguments) to the transaction log. It also stores the timestamp at
+which the transaction was executed. This allows the user to restore
+the transaction system state until a certain time (for debugging his
+application or reverting to an old data state). When a datastore is
+created or restored, the transaction log is read, and all the
+transaction calls are re-executed. After all the transactions have
+been executed, the persistent state is restored.
+
+A transaction store can have multiple subsystems, which control a
+certain subset of the persistent data. For example, the object
+datastore which controls persistent CLOS objects is realized as a
+subsystem of the transaction layer. A subsystem can snapshot the
+current persistent state, and write it to disk. When the persistent
+data is snapshotted, the transaction log can be discarded. Restoring
+this state then consists of loading the snapshot file, and then
+executing the transactions stored in the new transaction log. This
+allows for a much faster restore procedure. The snapshotting procedure
+takes care to backup the old transaction log, so that a restore until
+a certain timestamp is still possible.
+
+\subsection{The object datastore}
+
+The object datastore combines several aspects:
+\begin{itemize}
+ \item It is a subsystem of the transaction layer, and can snapshot
+persistent CLOS objects to a snapshot file, saving their slot values.
+ \item It provides a CLOS Metaclass for persistent objects. Using
+this metaclass, you can specify additional slot options for persistent
+objects. For example, you can specify that a certain slot is
+transient, which means that it will not be snapshotted and that its
+value can be changed outside of a transaction.
+ \item The persistent object metaclass also provides the index
+facility of `BKNR-INDICES', so that persistent objects automatically
+get indexed when restored from the store. The index facility is also
+used by the store itself to keep track of the instantiated objects of
+each class, and to index the objects by their unique ID.
+\end{itemize}
+
+The object datastore also provides a few helpers for developers. For
+example, you cannot change the value of a slot when you are outside of
+a transaction. The store throws an error when such an illegal
+operation is made. The store also warns you when you change a class
+definition. As the class definitions are not stored in the datastore,
+a snapshot is necessary after schema evolution.
+
+\section{The XML Import/Export Facility}
+
+BKNR applications often have to communicate with the external world,
+for example getting data from external sources, or exporting the data
+from the object datastore to external sources. In order to reduce the
+amount of conversion and parsing code that has to be written to
+accomodate these tasks, the BKNR framework provides an automatic
+mapping from CLOS classes to XML data. Using a special Metaclass for
+XML objects, you can map a CLOS class to XML according to a XML
+DTD.
+
+In the following paragraphs, we will use the CLOS class `USER' to show
+how the XML import/export facility works. The class `USER' has a slot
+`PARENT' which points to another `USER', a slot `NAME' which contains
+a string (the name of the user, obviously) and a slot `AGE' which
+contains an integer (the age of the user).
+
+A DTD consists of element declaration and of attribute declarations
+(we will leave entity declarations aside for now). Most of the time,
+the mapping between the XML data and the CLOS representation is quite
+straightforward. An object is represented as an XML element, and its
+slots are represented as either attributes of the element, or child
+elements. All the data in the XML file is encoded as a string, and
+read as such by the CXML parser used by BKNR. You can however specify
+custom parsing and serializing routines for slots.
+
+After the CLOS class has been annotated using specific slot-options,
+objects can be read from an existing XML file (which validates against
+the DTD file), or CLOS objects can be serialized to XML. Furthermore,
+parent-child relations can be directly created by the parsing code, or
+following by the serializing code. XML impex slot options can also be
+combined with BKNR indices, which makes browsing XML data very simple.
+
+Exporting and importing XML data now is a breeze!
+
+\section{The BKNR Web Framework}
+
+BKNR also features a web framework, providing an object oriented web
+handler architecture. Adding handlers for special kinds of data (for
+example RSS feeds for a custom data structure) can easily be achieved
+using multiple inheritance. Furthermore, a template handler is
+available, making it possible to access LISP functions from an XHTML
+file. Sadly, as the web framework has been heavily rewritten in the
+last months, we were not able to document it. However, you can have a
+look at the example sourcecode for different applications on the bknr
+webpage.
+
+
+%\section{The Templater}
+
+
+%\section{eboy.com}
+
View
162 doc/introduction.tex
@@ -1,81 +1,81 @@
-\chapter{Introduction}
-\label{sec:introduction}
-
-\vbox{
-\centering
-\includegraphics{satelliteicon}
-\vspace{1cm}
-}
-
-BKNR is a software launch platform for Lisp satellites. You could
-replace ``launch platform'' with framework and ``satellites'' with
-``applications'', but that would be too many buzzwords.
-
-BKNR is made of facilities that are not very useful on their own, but
-they can be used to quickly build shiny and elegant Lisp
-satellites. For example, a very important component of BKNR is its
-datastore, which brings persistence to CLOS in a very simple way. By
-adding a few declarations to your class definitions, you can have
-persistent objects. You can also add XML import/export to your objects
-in a similar way. I think this is the single most attractive feature
-of BKNR: no more mapping from a relational database to Lisp objects,
-no more XML parsing and XML generation, you just write plain
-application code.
-
-
-Another interesting feature of BKNR is its web framework, built on top
-of the Hunchentoot webserver. The web framework has a simple
-object-oriented handler hierarchy, with sessions, authorization and
-all the features you are used to from other frameworks. It also
-gathers usage information, stores it in the datastore, generates
-statistics, maps sessions to persistent users. Furthermore, a very
-useful feature is the HTML templater, which enables you to call Lisp
-code from XML templates. The Lisp template callbacks are simple Lisp
-functions that can work on the XML DOM representation. This eases
-working with web developers, who can still use their standard editors
-to develop the layout of the webpage. Dynamic content is easy to
-integrate.
-
-\begin{figure}[htbp]
- \centering
-\includegraphics[scale=0.4]{eboyshot1}
-\caption{Screenshot of eboy.com}
-\end{figure}
-
-The application which started BKNR was the website for the graphic
-designers eboy. A lot of work went into the manipulation of images and
-the integration of images into the Lisp framework. So another big part
-of the BKNR web framework is the image manipulation code and the image
-layout code, based on the CL-GD library by Edi Weitz.
-
-We have started developing BKNR in March 2004, and it is used in 2 big
-web applications (the Eboy dynasite ``eboy.com'', and the BOS website
-``BOS creates rainforest''), and has been used to implement a few
-personal websites (the website for the hacker gathering GPN in 2004,
-which featured an interactive Lisp music-dj, the temporary conference
-website for the European Lisp Workshop and the BKNR website). The code
-was opensourced right from the start, but we didn't put a lot of
-effort into making it accessible for other developers. This is the
-first try at releasing some kind of public version of the BKNR
-codebase, with (we hope) decent documentation.
-
-If you would like to look at some of the BKNR features in a little
-more detail, take the guided tour in Chapter 2. Chapters 3, 4, 5, 6
-then show how to use the different facilities in short tutorials,
-respectively object indices, the datastore, the web framework and the
-templater. These chapters are slightly modified versions of the
-tutorials that can be found in each of the facilities. Finally,
-Chapter 7 shows how to build a full web photo album application using
-BKNR. Due to time constraints, the last three chapters are not
-available for the release of BKNR Sputnik. We are sorry for the
-inconvenience. The sourcecode is in a working state, but not
-documented and cleaned up yet.
-
-We would like to thank the eboys for their support while developing
-BKNR, their cool graphics and their enthusiasm :) We would also like
-to thank Edi Weitz for his impressive libraries, and his
-support. Also, I would like to thank Steffen Hurrle for his cool
-website designs (he also has a new design for the BKNR website, which
-sadly nobody has the time to update). The BKNR developers are Hans
-Huebner, David Lichteblau and Manuel Odendahl.
-
+\chapter{Introduction}
+\label{sec:introduction}
+
+\vbox{
+\centering
+\includegraphics{satelliteicon}
+\vspace{1cm}
+}
+
+BKNR is a software launch platform for Lisp satellites. You could
+replace ``launch platform'' with framework and ``satellites'' with
+``applications'', but that would be too many buzzwords.
+
+BKNR is made of facilities that are not very useful on their own, but
+they can be used to quickly build shiny and elegant Lisp
+satellites. For example, a very important component of BKNR is its
+datastore, which brings persistence to CLOS in a very simple way. By
+adding a few declarations to your class definitions, you can have
+persistent objects. You can also add XML import/export to your objects
+in a similar way. I think this is the single most attractive feature
+of BKNR: no more mapping from a relational database to Lisp objects,
+no more XML parsing and XML generation, you just write plain
+application code.
+
+
+Another interesting feature of BKNR is its web framework, built on top
+of the Hunchentoot webserver. The web framework has a simple
+object-oriented handler hierarchy, with sessions, authorization and
+all the features you are used to from other frameworks. It also
+gathers usage information, stores it in the datastore, generates
+statistics, maps sessions to persistent users. Furthermore, a very
+useful feature is the HTML templater, which enables you to call Lisp
+code from XML templates. The Lisp template callbacks are simple Lisp
+functions that can work on the XML DOM representation. This eases
+working with web developers, who can still use their standard editors
+to develop the layout of the webpage. Dynamic content is easy to
+integrate.
+
+\begin{figure}[htbp]
+ \centering
+\includegraphics[scale=0.4]{eboyshot1}
+\caption{Screenshot of eboy.com}
+\end{figure}
+
+The application which started BKNR was the website for the graphic
+designers eboy. A lot of work went into the manipulation of images and
+the integration of images into the Lisp framework. So another big part
+of the BKNR web framework is the image manipulation code and the image
+layout code, based on the CL-GD library by Edi Weitz.
+
+We have started developing BKNR in March 2004, and it is used in 2 big
+web applications (the Eboy dynasite ``eboy.com'', and the BOS website
+``BOS creates rainforest''), and has been used to implement a few
+personal websites (the website for the hacker gathering GPN in 2004,
+which featured an interactive Lisp music-dj, the temporary conference
+website for the European Lisp Workshop and the BKNR website). The code
+was opensourced right from the start, but we didn't put a lot of
+effort into making it accessible for other developers. This is the
+first try at releasing some kind of public version of the BKNR
+codebase, with (we hope) decent documentation.
+
+If you would like to look at some of the BKNR features in a little
+more detail, take the guided tour in Chapter 2. Chapters 3, 4, 5, 6
+then show how to use the different facilities in short tutorials,
+respectively object indices, the datastore, the web framework and the
+templater. These chapters are slightly modified versions of the
+tutorials that can be found in each of the facilities. Finally,
+Chapter 7 shows how to build a full web photo album application using
+BKNR. Due to time constraints, the last three chapters are not
+available for the release of BKNR Sputnik. We are sorry for the
+inconvenience. The sourcecode is in a working state, but not
+documented and cleaned up yet.
+
+We would like to thank the eboys for their support while developing
+BKNR, their cool graphics and their enthusiasm :) We would also like
+to thank Edi Weitz for his impressive libraries, and his
+support. Also, I would like to thank Steffen Hurrle for his cool
+website designs (he also has a new design for the BKNR website, which
+sadly nobody has the time to update). The BKNR developers are Hans
+Huebner, David Lichteblau and Manuel Odendahl.
+
View
108 doc/manual.tex
@@ -1,55 +1,55 @@
-\documentclass[a4paper,11pt,titlepage]{book}
-\usepackage{amssymb}
-\usepackage{fancyvrb,color,palatino}
-\definecolor{gray}{gray}{0.6}
-
-
-\newif\ifpdf
-\ifx\pdfoutput\undefined
-\pdffalse % es wird kein PDFLaTeX benutzt
-\else
-\pdfoutput=1 % es wird PDFLaTeX benutzt
-\pdftrue
-\fi
-
-\ifpdf
-\usepackage[pdftex]{graphicx}
-\else
-\usepackage{graphicx}
-\fi
-
-\upshape
-\frenchspacing
-\title{BKNR Manual}
-\author{BKNR}
-
-\begin{document}
-
-\begin{titlepage}
-\begin{center}
-
- \vspace*{6cm}
- \begin{figure}[htbp]
- \centering
- \includegraphics[scale=0.6]{bknrlogo}
- \end{figure}
- \vspace{2cm}
- \Large
- CODENAME: VOSTOK
- (THIS MANUAL IS UNDER EDIT, PLEASE DO NOT PRINT OR DISTRIBUTE)
-
-\end{center}
-\end{titlepage}
-
-\tableofcontents
-
-\include{introduction}
-\include{guidedtour}
-\include{indices}
-\include{datastore}
-\include{impex}
-%\include{web}
-%\include{templates}
-%\include{example}
-
+\documentclass[a4paper,11pt,titlepage]{book}
+\usepackage{amssymb}
+\usepackage{fancyvrb,color,palatino}
+\definecolor{gray}{gray}{0.6}
+
+
+\newif\ifpdf
+\ifx\pdfoutput\undefined
+\pdffalse % es wird kein PDFLaTeX benutzt
+\else
+\pdfoutput=1 % es wird PDFLaTeX benutzt
+\pdftrue
+\fi
+
+\ifpdf
+\usepackage[pdftex]{graphicx}
+\else
+\usepackage{graphicx}
+\fi
+
+\upshape
+\frenchspacing
+\title{BKNR Manual}
+\author{BKNR}
+
+\begin{document}
+
+\begin{titlepage}
+\begin{center}
+
+ \vspace*{6cm}
+ \begin{figure}[htbp]
+ \centering
+ \includegraphics[scale=0.6]{bknrlogo}
+ \end{figure}
+ \vspace{2cm}
+ \Large
+ CODENAME: VOSTOK
+ (THIS MANUAL IS UNDER EDIT, PLEASE DO NOT PRINT OR DISTRIBUTE)
+
+\end{center}
+\end{titlepage}
+
+\tableofcontents
+
+\include{introduction}
+\include{guidedtour}
+\include{indices}
+\include{datastore}
+\include{impex}
+%\include{web}
+%\include{templates}
+%\include{example}
+
\end{document}
View
14 doc/templates.tex
@@ -1,7 +1,7 @@
-\chapter{BKNR Templates}
-
-\begin{figure}[htbp]
- \centering
-\includegraphics{templatesicon}
-\end{figure}
-
+\chapter{BKNR Templates}
+
+\begin{figure}[htbp]
+ \centering
+\includegraphics{templatesicon}
+\end{figure}
+
View
12 doc/web.tex
@@ -1,6 +1,6 @@
-\chapter{BKNR Web Framework}
-
-\vbox{ \centering
-\includegraphics[scale=0.25]{bknrlogo}
-}
-
+\chapter{BKNR Web Framework}
+
+\vbox{ \centering
+\includegraphics[scale=0.25]{bknrlogo}
+}
+
View
404 src/indices/category-index.lisp
@@ -1,202 +1,202 @@
-(in-package :bknr.indices)
-
-;;; category tree structure
-
-(defun make-node (name children)
- (cons name children))
-
-(defun node-name (node)
- (car node))
-
-(defun (setf node-name) (new-value node)
- (setf (car node) new-value))
-
-(defun node-children (node)
- (cdr node))
-
-(defun node-children-empty-p (node)
- (null (node-children node)))
-
-(defun (setf node-children) (new-value node)
- (setf (cdr node) new-value))
-
-(defstruct category-tree
- (test #'eql)
- (root-node (make-node :root nil)))
-
-(defun node-to-categories (node &optional parent-category)
- (let ((category (append parent-category (list (node-name node)))))
- (cons category (mapcan #'(lambda (child) (node-to-categories child category))
- (node-children node)))))
-
-(defun nodes-to-categories (nodes &optional parent-category)
- (mapcan #'(lambda (node) (node-to-categories node parent-category)) nodes))
-
-(defun tree-categories (tree &optional category)
- (nodes-to-categories (node-children (category-tree-root-node tree)) category))
-
-(defun tree-find-node (tree category)
- (unless (listp category)
- (setf category (list category)))
- (do* ((curnode (category-tree-root-node tree)
- (find catname (node-children curnode)
- :key #'node-name
- :test (category-tree-test tree)))
- (curcat category (cdr curcat))
- (catname (car curcat) (car curcat)))
- ((or (null curnode)
- (null curcat))
- curnode)))
-
-(defun category-to-node (category)
- (if (null category)
- nil
- (let ((child (category-to-node (cdr category))))
- (make-node (first category)
- (when child (list child))))))
-
-(defun tree-add-category (tree category)
- (unless (listp category)
- (setf category (list category)))
- (do* ((curnode (category-tree-root-node tree))
- (curcat category (cdr curcat))
- (catname (car curcat) (car curcat)))
- ((or (null curnode)
- (null curcat))
- tree)
- (let ((node (find catname (node-children curnode)
- :key #'node-name
- :test (category-tree-test tree))))
- (if node
- (setf curnode node)
- (progn
- (push (category-to-node curcat)
- (node-children curnode))
- (return-from tree-add-category tree))))))
-
-(defun tree-remove-category (tree category)
- (unless (listp category)
- (setf category (list category)))
- (when category
- (let* ((parent-category (parent-category category))
- (parent-node (tree-find-node tree parent-category)))
- (when parent-node
- (setf (node-children parent-node)
- (remove (category-name category)
- (node-children parent-node)
- :key #'car
- :test (category-tree-test tree)))
- (when (node-children-empty-p parent-node)
- (tree-remove-category tree parent-category)))))
- tree)
-
-(defun parent-categories (category)
- (let (res)
- (dotimes (i (1- (length category)))
- (push (butlast category (1+ i)) res))
- res))
-
-(defun parent-category (category)
- (butlast category 1))
-
-(defun category-name (category)
- (car (last category)))
-
-(defun tree-find-children (tree category)
- (nodes-to-categories (node-children (tree-find-node tree category)) category))
-
-(defun tree-find-siblings (tree category)
- (let ((len (length category)))
- (if (<= len 1)
- tree
- (let ((sib-cat (subseq category 0 (1- (length category)))))
- (nodes-to-categories (tree-find-children tree sib-cat) sib-cat)))))
-
-;;; category index
-
-(defclass category-index (hash-index)
- ((tree :initform (make-category-tree)
- :initarg :tree
- :accessor category-index-tree))
- (:default-initargs :test #'equal))
-
-(defmethod initialize-instance :after ((index category-index) &key (tree-test #'eql))
- (with-slots (tree) index
- (setf tree (make-category-tree :test tree-test))))
-
-(defmethod index-get ((index category-index) category)
- (let* ((tree (category-index-tree index))
- (hash (slot-index-hash-table index))
- (categories (cons category
- (tree-find-children tree category))))
- (mapcan #'(lambda (category)
- (copy-list (gethash category hash))) categories)))
-
-(defmethod index-add ((index category-index) object)
- (unless (slot-boundp object (slot-index-slot-name index))
- (return-from index-add))
- (let ((key (slot-value object (slot-index-slot-name index)))
- (hash-table (slot-index-hash-table index))
- (tree (category-index-tree index)))
- (when (and (not (slot-index-index-nil index))
- (null key))
- (return-from index-add))
- (if (nth-value 1 (gethash key hash-table))
- (push object (gethash key hash-table))
- (progn
- (tree-add-category tree key)
- (setf (gethash key hash-table) (list object))))))
-
-(defmethod index-remove ((index category-index) object)
- (let ((key (slot-value object (slot-index-slot-name index)))
- (hash-table (slot-index-hash-table index))
- (tree (category-index-tree index)))
- (let ((new-value (delete-first object (gethash key hash-table))))
- (if (null new-value)
- (progn
- (tree-remove-category tree key)
- (remhash key hash-table))
- (setf (gethash key hash-table) new-value)))))
-
-(defmethod index-keys ((index category-index))
- (tree-categories (category-index-tree index)))
-
-(defmethod index-reinitialize :around ((new-index category-index)
- (old-index category-index))
- (let* ((new-index (call-next-method))
- (tree (category-index-tree new-index))
- (new-hash (slot-index-hash-table new-index)))
- (loop for key being the hash-key of new-hash
- do (tree-add-category tree key))
- new-index))
-
-#|
-
-(defclass image ()
- ((category :index-type category-index
- :index-reader images-with-category
- :index-keys all-image-categories
- :index-var *image-category-index*
- :initarg :category
- :reader image-category))
- (:metaclass indexed-class))
-
-(make-instance 'image :category '(:photo :stills :nature))
-(make-instance 'image :category '(:photo :naked :woman))
-(make-instance 'image :category '(:painting :abstract :cubist))
-
-(defclass track ()
- ((category :index-type category-index
- :index-initargs (:tree-test #'equal)
- :index-reader tracks-with-category
- :index-keys all-track-categories
- :index-var *track-category-index*
- :initarg :category
- :reader track-category))
- (:metaclass indexed-class))
-
-(make-instance 'track :category '("Rock" "New-Age" "Noise"))
-(make-instance 'track :category '("Rock" "New-Age" "Techno"))
-
-
-|#
+(in-package :bknr.indices)
+
+;;; category tree structure
+
+(defun make-node (name children)
+ (cons name children))
+
+(defun node-name (node)
+ (car node))
+
+(defun (setf node-name) (new-value node)
+ (setf (car node) new-value))
+
+(defun node-children (node)
+ (cdr node))
+
+(defun node-children-empty-p (node)
+ (null (node-children node)))
+
+(defun (setf node-children) (new-value node)
+ (setf (cdr node) new-value))
+
+(defstruct category-tree
+ (test #'eql)
+ (root-node (make-node :root nil)))
+
+(defun node-to-categories (node &optional parent-category)
+ (let ((category (append parent-category (list (node-name node)))))
+ (cons category (mapcan #'(lambda (child) (node-to-categories child category))
+ (node-children node)))))
+
+(defun nodes-to-categories (nodes &optional parent-category)
+ (mapcan #'(lambda (node) (node-to-categories node parent-category)) nodes))
+
+(defun tree-categories (tree &optional category)
+ (nodes-to-categories (node-children (category-tree-root-node tree)) category))
+
+(defun tree-find-node (tree category)
+ (unless (listp category)
+ (setf category (list category)))
+ (do* ((curnode (category-tree-root-node tree)
+ (find catname (node-children curnode)
+ :key #'node-name
+ :test (category-tree-test tree)))
+ (curcat category (cdr curcat))
+ (catname (car curcat) (car curcat)))
+ ((or (null curnode)
+ (null curcat))
+ curnode)))
+
+(defun category-to-node (category)
+ (if (null category)
+ nil
+ (let ((child (category-to-node (cdr category))))
+ (make-node (first category)
+ (when child (list child))))))
+
+(defun tree-add-category (tree category)
+ (unless (listp category)
+ (setf category (list category)))
+ (do* ((curnode (category-tree-root-node tree))
+ (curcat category (cdr curcat))
+ (catname (car curcat) (car curcat)))
+ ((or (null curnode)
+ (null curcat))
+ tree)
+ (let ((node (find catname (node-children curnode)
+ :key #'node-name
+ :test (category-tree-test tree))))
+ (if node
+ (setf curnode node)
+ (progn
+ (push (category-to-node curcat)
+ (node-children curnode))
+ (return-from tree-add-category tree))))))
+
+(defun tree-remove-category (tree category)
+ (unless (listp category)
+ (setf category (list category)))
+ (when category
+ (let* ((parent-category (parent-category category))
+ (parent-node (tree-find-node tree parent-category)))
+ (when parent-node
+ (setf (node-children parent-node)
+ (remove (category-name category)
+ (node-children parent-node)
+ :key #'car
+ :test (category-tree-test tree)))
+ (when (node-children-empty-p parent-node)
+ (tree-remove-category tree parent-category)))))
+ tree)
+
+(defun parent-categories (category)
+ (let (res)
+ (dotimes (i (1- (length category)))
+ (push (butlast category (1+ i)) res))
+ res))
+
+(defun parent-category (category)
+ (butlast category 1))
+
+(defun category-name (category)
+ (car (last category)))
+
+(defun tree-find-children (tree category)
+ (nodes-to-categories (node-children (tree-find-node tree category)) category))
+
+(defun tree-find-siblings (tree category)
+ (let ((len (length category)))
+ (if (<= len 1)
+ tree
+ (let ((sib-cat (subseq category 0 (1- (length category)))))
+ (nodes-to-categories (tree-find-children tree sib-cat) sib-cat)))))
+
+;;; category index
+
+(defclass category-index (hash-index)
+ ((tree :initform (make-category-tree)
+ :initarg :tree
+ :accessor category-index-tree))
+ (:default-initargs :test #'equal))
+
+(defmethod initialize-instance :after ((index category-index) &key (tree-test #'eql))
+ (with-slots (tree) index
+ (setf tree (make-category-tree :test tree-test))))
+
+(defmethod index-get ((index category-index) category)
+ (let* ((tree (category-index-tree index))
+ (hash (slot-index-hash-table index))
+ (categories (cons category
+ (tree-find-children tree category))))
+ (mapcan #'(lambda (category)
+ (copy-list (gethash category hash))) categories)))
+
+(defmethod index-add ((index category-index) object)
+ (unless (slot-boundp object (slot-index-slot-name index))
+ (return-from index-add))
+ (let ((key (slot-value object (slot-index-slot-name index)))
+ (hash-table (slot-index-hash-table index))
+ (tree (category-index-tree index)))
+ (when (and (not (slot-index-index-nil index))
+ (null key))
+ (return-from index-add))
+ (if (nth-value 1 (gethash key hash-table))
+ (push object (gethash key hash-table))
+ (progn
+ (tree-add-category tree key)
+ (setf (gethash key hash-table) (list object))))))
+
+(defmethod index-remove ((index category-index) object)
+ (let ((key (slot-value object (slot-index-slot-name index)))
+ (hash-table (slot-index-hash-table index))
+ (tree (category-index-tree index)))
+ (let ((new-value (delete-first object (gethash key hash-table))))
+ (if (null new-value)
+ (progn
+ (tree-remove-category tree key)
+ (remhash key hash-table))
+ (setf (gethash key hash-table) new-value)))))
+
+(defmethod index-keys ((index category-index))
+ (tree-categories (category-index-tree index)))
+
+(defmethod index-reinitialize :around ((new-index category-index)
+ (old-index category-index))
+ (let* ((new-index (call-next-method))
+ (tree (category-index-tree new-index))
+ (new-hash (slot-index-hash-table new-index)))
+ (loop for key being the hash-key of new-hash
+ do (tree-add-category tree key))
+ new-index))
+
+#|
+
+(defclass image ()
+ ((category :index-type category-index
+ :index-reader images-with-category
+ :index-keys all-image-categories
+ :index-var *image-category-index*
+ :initarg :category
+ :reader image-category))
+ (:metaclass indexed-class))
+
+(make-instance 'image :category '(:photo :stills :nature))
+(make-instance 'image :category '(:photo :naked :woman))
+(make-instance 'image :category '(:painting :abstract :cubist))
+
+(defclass track ()
+ ((category :index-type category-index
+ :index-initargs (:tree-test #'equal)
+ :index-reader tracks-with-category
+ :index-keys all-track-categories
+ :index-var *track-category-index*
+ :initarg :category
+ :reader track-category))
+ (:metaclass indexed-class))
+
+(make-instance 'track :category '("Rock" "New-Age" "Noise"))
+(make-instance 'track :category '("Rock" "New-Age" "Techno"))
+
+
+|#
View
138 src/indices/protocol.lisp
@@ -1,69 +1,69 @@
-(in-package :bknr.indices)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; CLOS Protocol for index objects
-
-;;; A CLOS cursor is a CLOS class that complies to the following
-;;; protocol (note that a cursor doesn't have to support both
-;;; CURSOR-NEXT and CURSOR-PREVIOUS):
-
-(defgeneric cursor-next (cursor &optional eoc)
- (:documentation "Return the current value and advance the
-cursor. Returns EOC if the cursor is at its end."))
-
-(defgeneric cursor-previous (cursor &optional eoc)
- (:documentation "Return the current value and advance the
-cursor. Returns EOC if the cursor is at its end."))
-
-;;; A CLOS index is a CLOS class that complies to the following
-;;; protocol (note that an index doesn't have to support all the
-;;; protocol methods):
-
-(defgeneric index-add (index object)
- (:documentation "Add OBJECT to the INDEX. Throws an ERROR if a
-problem happened while inserting OBJECT."))
-
-(defgeneric index-get (index key)
- (:documentation "Get the object (or the objects) stored under the index-key KEY."))
-
-(defgeneric index-remove (index object)
- (:documentation "Remove OBJECT from the INDEX."))
-
-(defgeneric index-keys (index)
- (:documentation "Returns all the KEYs of the INDEX."))
-
-(defgeneric index-values (index)
- (:documentation "Returns all the objects (or object lists) stored in
-INDEX."))
-
-(defgeneric index-values-cursor (index)
- (:documentation "Returns a cursor that walks all the objects stored in INDEX."))
-
-(defgeneric index-keys-cursor (index)
- (:documentation "Returns a cursor that walks all the keys stored in INDEX."))
-
-(defgeneric index-mapvalues (index fun)
- (:documentation "Apply FUN to every object in the INDEX."))
-
-(defgeneric index-reinitialize (new-index old-index)
- (:documentation "Called when the definition of an index is changed."))
-
-(defgeneric index-clear (index)
- (:documentation "Remove all indexed objects from the index."))
-
-(defun index-create (class-name &rest initargs)
- "Instantiate the index object with class CLASS-NAME with INITARGS."
- (apply #'make-instance class-name initargs))
-
-;;; Adding to an index can throw the following errors:
-
-(define-condition index-existing-error (error)
- ((index :initarg :index :reader condition-index)
- (key :initarg :key :reader condition-key)
- (value :initarg :value :reader condition-value)))
-
-(defmethod print-object ((obj index-existing-error) stream)
- (print-unreadable-object (obj stream :type t)
- (format stream "INDEX: ~A KEY: ~S VALUE: ~S"
- (condition-index obj) (condition-key obj) (condition-value obj))))
-
+(in-package :bknr.indices)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; CLOS Protocol for index objects
+
+;;; A CLOS cursor is a CLOS class that complies to the following
+;;; protocol (note that a cursor doesn't have to support both
+;;; CURSOR-NEXT and CURSOR-PREVIOUS):
+
+(defgeneric cursor-next (cursor &optional eoc)
+ (:documentation "Return the current value and advance the
+cursor. Returns EOC if the cursor is at its end."))
+
+(defgeneric cursor-previous (cursor &optional eoc)
+ (:documentation "Return the current value and advance the
+cursor. Returns EOC if the cursor is at its end."))
+
+;;; A CLOS index is a CLOS class that complies to the following
+;;; protocol (note that an index doesn't have to support all the
+;;; protocol methods):
+
+(defgeneric index-add (index object)
+ (:documentation "Add OBJECT to the INDEX. Throws an ERROR if a
+problem happened while inserting OBJECT."))
+
+(defgeneric index-get (index key)
+ (:documentation "Get the object (or the objects) stored under the index-key KEY."))
+
+(defgeneric index-remove (index object)
+ (:documentation "Remove OBJECT from the INDEX."))
+
+(defgeneric index-keys (index)
+ (:documentation "Returns all the KEYs of the INDEX."))
+
+(defgeneric index-values (index)
+ (:documentation "Returns all the objects (or object lists) stored in
+INDEX."))
+
+(defgeneric index-values-cursor (index)
+ (:documentation "Returns a cursor that walks all the objects stored in INDEX."))
+
+(defgeneric index-keys-cursor (index)
+ (:documentation "Returns a cursor that walks all the keys stored in INDEX."))
+
+(defgeneric index-mapvalues (index fun)
+ (:documentation "Apply FUN to every object in the INDEX."))
+
+(defgeneric index-reinitialize (new-index old-index)
+ (:documentation "Called when the definition of an index is changed."))
+
+(defgeneric index-clear (index)
+ (:documentation "Remove all indexed objects from the index."))
+
+(defun index-create (class-name &rest initargs)
+ "Instantiate the index object with class CLASS-NAME with INITARGS."
+ (apply #'make-instance class-name initargs))
+
+;;; Adding to an index can throw the following errors:
+
+(define-condition index-existing-error (error)
+ ((index :initarg :index :reader condition-index)
+ (key :initarg :key :reader condition-key)
+ (value :initarg :value :reader condition-value)))
+
+(defmethod print-object ((obj index-existing-error) stream)
+ (print-unreadable-object (obj stream :type t)
+ (format stream "INDEX: ~A KEY: ~S VALUE: ~S"
+ (condition-index obj) (condition-key obj) (condition-value obj))))
+
View
78 src/xml-impex/xml-update.lisp
@@ -1,39 +1,39 @@
-(in-package :bknr.impex)
-
-;;; sax parser for xml impex updater, reads updates to objects from an xml file
-
-(defclass xml-class-updater (xml-class-importer)
- ())
-
-(defun class-find-slot (class slot-name)
- (find-if #'(lambda (slot)
- (equal (slot-definition-name slot) slot-name))
- (mop:class-slots class)))
-
-(defmethod importer-finalize ((handler xml-class-updater)
- (instance xml-class-instance))
- (with-slots (class slots) instance
- (if (and (xml-class-unique-id-slot class)
- (xml-class-unique-id-reader class))
- (let* ((id-slot (class-find-slot class (xml-class-unique-id-slot class)))
- (id-value (gethash id-slot slots))
- (obj (when id-value (funcall (xml-class-unique-id-reader class) id-value))))
- (if (and obj id-value)
- (progn
- (loop for slot being the hash-keys of slots using (hash-value value)
- when (not (equal (slot-definition-name slot) (xml-class-unique-id-slot class)))
- do
- (format t "updating slot ~A with ~S~%" (slot-definition-name slot)
- value)
- (setf (slot-value obj (slot-definition-name slot))
- value))
- obj)
- (progn
- (warn "no id-value or object found, creating new~%")
- (call-next-method))))
-
- (call-next-method))))
-
-(defun parse-xml-update-file (xml-file classes &key (recoder #'cxml::rod-string)
- (importer-class 'xml-class-updater))
- (parse-xml-file xml-file classes :recoder recoder :importer-class importer-class))
+(in-package :bknr.impex)
+
+;;; sax parser for xml impex updater, reads updates to objects from an xml file
+
+(defclass xml-class-updater (xml-class-importer)
+ ())
+
+(defun class-find-slot (class slot-name)
+ (find-if #'(lambda (slot)
+ (equal (slot-definition-name slot) slot-name))
+ (mop:class-slots class)))
+
+(defmethod importer-finalize ((handler xml-class-updater)
+ (instance xml-class-instance))
+ (with-slots (class slots) instance
+ (if (and (xml-class-unique-id-slot class)
+ (xml-class-unique-id-reader class))
+ (let* ((id-slot (class-find-slot class (xml-class-unique-id-slot class)))
+ (id-value (gethash id-slot slots))
+ (obj (when id-value (funcall (xml-class-unique-id-reader class) id-value))))
+ (if (and obj id-value)
+ (progn
+ (loop for slot being the hash-keys of slots using (hash-value value)
+ when (not (equal (slot-definition-name slot) (xml-class-unique-id-slot class)))
+ do
+ (format t "updating slot ~A with ~S~%" (slot-definition-name slot)
+ value)
+ (setf (slot-value obj (slot-definition-name slot))
+ value))
+ obj)
+ (progn
+ (warn "no id-value or object found, creating new~%")
+ (call-next-method))))
+
+ (call-next-method))))
+
+(defun parse-xml-update-file (xml-file classes &key (recoder #'cxml::rod-string)
+ (importer-class 'xml-class-updater))
+ (parse-xml-file xml-file classes :recoder recoder :importer-class importer-class))
Please sign in to comment.
Something went wrong with that request. Please try again.