Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #15 from sigurdmeldgaard/Sqlite

Sqlite
  • Loading branch information...
commit 6a2c1a8bc25552ac84a0ea49c1bcf838799d51dc 2 parents c0306e3 + c875993
@jgm authored
View
36 README.markdown
@@ -2,7 +2,8 @@
======================================================
`yst` is a tool for generating a static website by filling [string
-template][]s with data taken from [YAML][] or [CSV][] text files. This approach
+template][]s with data taken from [YAML][] or [CSV][] text files or
+[SQLite3][] file based databases. This approach
combines the speed, security, and ease of deployment of a static
website with the flexibility and maintainability of a dynamic site that
separates presentation and data.
@@ -221,20 +222,26 @@ Subtrees can contain other subtrees. Just be consistent about indentation.
### The `data` field
-The `data` field in `index.yaml` can populate any number of stringtemplate
-attributes with data from YAML or CSV files. The syntax is easiest to
-explain by example (note that the keywords do not have to be in ALL CAPS,
-although they may, and the query doesn't have to end with a semicolon,
-though it may):
+The `data` field in `index.yaml` can populate any number of
+stringtemplate attributes with data from YAML or CSV files or SQLite3
+databases. The syntax is easiest to explain by example (note that the
+keywords do not have to be in ALL CAPS, although they may, and the
+query doesn't have to end with a semicolon, though it may):
data:
events: from events.yaml order by date desc group by title then location
people: from people.csv order by birthday then lastname where
birthstate = 'CA' limit 5
+ beststudents: from students.sqlite
+ query "select * from students where grade > 5"
+ order by name
First we have the name of the stringtemplate attribute to be populated
(say, `events`). Then, after the colon, we have the data source
-(`events.yaml`) followed by one or more *transformations*, which will
+(`events.yaml`). If the data source is an SQLite3 database, it should be
+followed by a query that is a quoted string.
+
+The data source is followed by one or more *transformations*, which will
be applied in order. Here are the possible transformations. In
what follows, brackets denote an optional component, `|` denotes
alternatives, and `*` indicates that the component may be repeated
@@ -438,6 +445,20 @@ database, you can use a query like this to get the CSV:
(Thanks to
<http://www.terminally-incoherent.com/blog/2006/07/20/dump-mysql-table-into-csv-file/>.)
+### Using a SQLite database as data source
+
+You can also get the data directly from a database. Just give the file
+name from the database followed by a query quoted in "'s
+
+In this way you can do joins and other advanced operations on your
+data before handing them over to yst:
+
+ data:
+ meetings : FROM data.sqlite
+ QUERY "select * from meetings
+ left outer join persons
+ on meetings.speaker = persons.name"
+
### Using HTML in the templates
Markdown allows raw HTML to be used, so you can embed HTML in templates.
@@ -528,6 +549,7 @@ If you find a bug, please report it using
[string template]: http://www.stringtemplate.org/
[YAML]: http://www.yaml.org/
[CSV]: http://en.wikipedia.org/wiki/Comma-separated_values
+[SQLite3]: http://www.sqlite.org/
[cabal install]: http://hackage.haskell.org/trac/hackage/wiki/CabalInstall
[Haskell platform]: http://hackage.haskell.org/platform/
[markdown]: http://daringfireball.com/markdown
View
1  Yst/Build.hs
@@ -51,6 +51,7 @@ dependencies site url =
TemplateFile f -> stripStExt f <.> "st"
SourceFile f -> f
fileFromSpec (DataFromFile f _) = Just f
+ fileFromSpec (DataFromSqlite3 f _ _) = Just f
fileFromSpec _ = Nothing
dataFiles = map (dataDir site </>) $ mapMaybe (\(_,s) -> fileFromSpec s) $ pageData page
in indexFile site : layout : srcdir : (requires ++ dataFiles)
View
24 Yst/Data.hs
@@ -23,6 +23,7 @@ import Yst.Types
import Yst.Util
import Yst.Yaml
import Yst.CSV
+import Yst.Sqlite3 (readSqlite3)
import Control.Monad
import Data.Char
import Data.Maybe (fromMaybe)
@@ -35,6 +36,10 @@ getData site (DataFromFile file opts) = do
raw <- catch (readDataFile $ dataDir site </> file)
(\e -> errorExit 15 ("Error reading data from " ++ file ++ ": " ++ show e) >> return undefined)
return $ foldl applyDataOption raw opts
+getData site (DataFromSqlite3 database query opts) = do
+ raw <- catch (readSqlite3 (dataDir site </> database) query)
+ (\e -> errorExit 15 ("Error reading Sqlite3 database from " ++ database ++ ": " ++ show e) >> return undefined)
+ return $ foldl applyDataOption raw opts
getData _ (DataConstant n) = return n
readDataFile :: FilePath -> IO Node
@@ -103,18 +108,20 @@ reverseIfDescending Descending GT = LT
parseDataField :: Node -> DataSpec
parseDataField n@(NString s) = case parse pDataField s s of
- Right (f,opts) -> DataFromFile f opts
+ Right (f, Nothing, opts) -> DataFromFile f opts
+ Right (f, Just query, opts) -> DataFromSqlite3 f query opts
Left err -> if "from" `isPrefixOf` (dropWhile isSpace $ map toLower s)
then error $ "Error parsing data field: " ++ show err
else DataConstant n
parseDataField n = DataConstant n
-pDataField :: GenParser Char st ([Char], [DataOption])
+pDataField :: GenParser Char st (String, Maybe String,[DataOption])
pDataField = do
spaces
pString "from"
pSpace
- fname <- pIdentifier <?> "name of YAML or CSV file"
+ fname <- pIdentifier <?> "name of YAML, CSV or SQLite3 file"
+ query <- (optionMaybe $ pQuery) <?> "a SQL query"
opts <- many $ (pOptWhere <?> "where [CONDITION]")
<|> (pOptLimit <?> "limit [NUMBER]")
<|> (pOptOrderBy <?> "order by [CONDITION]")
@@ -123,7 +130,7 @@ pDataField = do
optional $ char ';'
spaces
eof
- return (fname, opts)
+ return (fname, query, opts)
pIdentifier :: GenParser Char st [Char]
pIdentifier = spaces >> (pQuoted '\'' <|> pQuoted '"' <|> many (noneOf " \t\n<>=;,'\""))
@@ -143,6 +150,15 @@ pQuoted delim = try $ do
char delim
return res
+pQuery :: GenParser Char st String
+pQuery = try $ do
+ optional $ oneOf ",;"
+ spaces
+ pString "query"
+ pSpace
+ res <- pQuoted '"'
+ return res
+
pOptLimit :: GenParser Char st DataOption
pOptLimit = try $ do
optional $ oneOf ",;"
View
27 Yst/Sqlite3.hs
@@ -0,0 +1,27 @@
+module Yst.Sqlite3 (readSqlite3)
+where
+
+import Database.HDBC
+import Database.HDBC.Sqlite3
+import Data.Maybe
+import Yst.Types
+import Yst.Util (parseAsDate)
+
+readSqlite3 :: FilePath -> String -> IO Node
+readSqlite3 filename query = do
+ conn <- connectSqlite3 filename
+ stmt <- prepare conn query
+ execute stmt []
+ records <- sFetchAllRows' stmt
+ fieldNames <- getColumnNames stmt
+ disconnect conn
+
+ return $ NList $ map (NMap .
+ (zip fieldNames) .
+ (map (fieldToNode . fromMaybe ""))) records
+
+fieldToNode :: String -> Node
+fieldToNode s =
+ case parseAsDate s of
+ Nothing -> NString s
+ Just d -> NDate d
View
1  Yst/Types.hs
@@ -75,6 +75,7 @@ instance Ord Node where
data DataSpec = DataConstant Node
| DataFromFile FilePath [DataOption]
+ | DataFromSqlite3 FilePath String [DataOption]
deriving (Show, Read, Eq)
data DataOption = OrderBy [(String, SortDirection)]
View
7 yst.cabal
@@ -7,7 +7,8 @@ synopsis: Builds a static website from templates and data in YAML or
CSV files.
description: `yst` is a tool for generating a static website
by filling string templates with data taken from
- YAML text files (or, alternatively, CSV files).
+ YAML text files (or, alternatively, CSV files or
+ from a Sqlite3 database).
This approach combines the speed, security, and
ease of deployment of a static website with the
flexibility and maintainability of a dynamic site
@@ -46,11 +47,11 @@ Executable yst
hs-source-dirs: .
main-is: yst.hs
other-modules: Yst.Types, Yst.Yaml, Yst.Util, Yst.Data, Yst.Config,
- Yst.Render, Yst.Build, Yst.CSV
+ Yst.Render, Yst.Build, Yst.CSV, Yst.Sqlite3
build-depends: base >=3 && < 5, HStringTemplate >= 0.6.1, HsSyck, csv,
filepath, containers, directory, utf8-string, time,
old-locale, old-time, parsec, xhtml, pandoc, bytestring,
- split
+ split, HDBC, HDBC-sqlite3
extensions: CPP
if impl(ghc >= 6.12)
ghc-options: -Wall -threaded -fno-warn-orphans -fno-warn-unused-do-bind

0 comments on commit 6a2c1a8

Please sign in to comment.
Something went wrong with that request. Please try again.