diff options
author | Araq <rumpf_a@web.de> | 2012-11-01 15:36:08 -0700 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2012-11-01 15:36:08 -0700 |
commit | 12f7a0b0cab8ed4a30c6914804068780f34af848 (patch) | |
tree | 09cd4f2ffa02c1b6fd199c0e7d89a6d987a72939 /examples | |
parent | 9ef0c5359704b133f461bebd347c1403be834260 (diff) | |
parent | a8df766fd9bbdfcf7c7549bdc53a6e43710d3d9e (diff) | |
download | Nim-12f7a0b0cab8ed4a30c6914804068780f34af848.tar.gz |
Merge pull request #243 from gradha/pr_database_example
Pr database example
Diffstat (limited to 'examples')
-rw-r--r-- | examples/cross_calculator/readme.txt | 2 | ||||
-rw-r--r-- | examples/cross_todo/nimrod_backend/backend.nim | 222 | ||||
-rw-r--r-- | examples/cross_todo/nimrod_backend/readme.txt | 14 | ||||
-rw-r--r-- | examples/cross_todo/nimrod_backend/testbackend.nim | 86 | ||||
-rw-r--r-- | examples/cross_todo/nimrod_commandline/nimtodo.nim | 347 | ||||
-rw-r--r-- | examples/cross_todo/nimrod_commandline/readme.txt | 18 | ||||
-rw-r--r-- | examples/cross_todo/readme.txt | 5 |
7 files changed, 694 insertions, 0 deletions
diff --git a/examples/cross_calculator/readme.txt b/examples/cross_calculator/readme.txt index e59cb7e54..55c403d0e 100644 --- a/examples/cross_calculator/readme.txt +++ b/examples/cross_calculator/readme.txt @@ -9,3 +9,5 @@ platforms. To avoid duplication of code, the backend code lies in a separate directory and each platform compiles it with a different custom build process, usually generating C code in a temporary build directory. + +For a more ellaborate and useful example see the cross_todo example. diff --git a/examples/cross_todo/nimrod_backend/backend.nim b/examples/cross_todo/nimrod_backend/backend.nim new file mode 100644 index 000000000..190ecf535 --- /dev/null +++ b/examples/cross_todo/nimrod_backend/backend.nim @@ -0,0 +1,222 @@ +# Backend for a simple todo program with sqlite persistence. +# +# Most procs dealing with a TDbConn object may raise an EDb exception. + +import db_sqlite +import parseutils +import strutils +import times + + +type + TTodo* = object of TObject + ## A todo object holding the information serialized to the database. + id: int64 ## Unique identifier of the object in the + ## database, use the getId() accessor to read it. + text*: string ## Description of the task to do. + priority*: int ## The priority can be any user defined integer. + isDone*: bool ## Done todos are still kept marked. + modificationDate: TTime ## The modification time can't be modified from + ## outside of this module, use the + ## getModificationDate accessor. + + TPagedParams* = object of TObject + ## Contains parameters for a query, initialize default values with + ## initDefaults(). + pageSize*: int64 ## Lines per returned query page, -1 for + ## unlimited. + priorityAscending*: bool ## Sort results by ascending priority. + dateAscending*: bool ## Sort results by ascending modification date. + showUnchecked*: bool ## Get unchecked objects. + showChecked*: bool ## Get checked objects. + + +# - General procs +# +proc initDefaults*(params: var TPagedParams) = + ## Sets sane defaults for a TPagedParams object. + ## + ## Note that you should always provide a non zero pageSize, either a specific + ## positive value or negative for unbounded query results. + params.pageSize = high(int64) + params.priorityAscending = false + params.dateAscending = false + params.showUnchecked = true + params.showChecked = false + + +proc openDatabase*(path: string): TDbConn = + ## Creates or opens the sqlite3 database. + ## + ## Pass the path to the sqlite database, if the database doesn't exist it + ## will be created. The proc may raise a EDB exception + let + conn = db_sqlite.open(path, "user", "pass", "db") + query = sql"""CREATE TABLE IF NOT EXISTS Todos ( + id INTEGER PRIMARY KEY, + priority INTEGER NOT NULL, + is_done BOOLEAN NOT NULL, + desc TEXT NOT NULL, + modification_date INTEGER NOT NULL, + CONSTRAINT Todos UNIQUE (id))""" + + db_sqlite.exec(conn, query) + result = conn + + +# - Procs related to TTodo objects +# +proc initFromDB(id: int64; text: string; priority: int, isDone: bool; + modificationDate: TTime): TTodo = + ## Returns an initialized TTodo object created from database parameters. + ## + ## The proc assumes all values are right. Note this proc is NOT exported. + assert(id >= 0, "Identity identifiers should not be negative") + result.id = id + result.text = text + result.priority = priority + result.isDone = isDone + result.modificationDate = modificationDate + + +proc getId*(todo: TTodo): int64 = + ## Accessor returning the value of the private id property. + return todo.id + + +proc getModificationDate*(todo: TTodo): TTime = + ## Returns the last modification date of a TTodo entry. + return todo.modificationDate + + +proc update*(todo: var TTodo; conn: TDbConn): bool = + ## Checks the database for the object and refreshes its variables. + ## + ## Use this method if you (or another entity) have modified the database and + ## want to update the object you have with whatever the database has stored. + ## Returns true if the update suceeded, or false if the object was not found + ## in the database any more, in which case you should probably get rid of the + ## TTodo object. + assert(todo.id >= 0, "The identifier of the todo entry can't be negative") + let query = sql"""SELECT desc, priority, is_done, modification_date + FROM Todos WHERE id = ?""" + + try: + let rows = conn.GetAllRows(query, $todo.id) + if len(rows) < 1: + return + assert(1 == len(rows), "Woah, didn't expect so many rows") + todo.text = rows[0][0] + todo.priority = rows[0][1].parseInt + todo.isDone = rows[0][2].parseBool + todo.modificationDate = TTime(rows[0][3].parseInt) + result = true + except: + echo("Something went wrong selecting for id " & $todo.id) + + +proc save*(todo: var TTodo; conn: TDbConn): bool = + ## Saves the current state of text, priority and isDone to the database. + ## + ## Returns true if the database object was updated (in which case the + ## modification date will have changed). The proc can return false if the + ## object wasn't found, for instance, in which case you should drop that + ## object anyway and create a new one with addTodo(). Also EDb can be raised. + assert(todo.id >= 0, "The identifier of the todo entry can't be negative") + let + currentDate = getTime() + query = sql"""UPDATE Todos + SET desc = ?, priority = ?, is_done = ?, modification_date = ? + WHERE id = ?""" + rowsUpdated = conn.execAffectedRows(query, $todo.text, + $todo.priority, $todo.isDone, $int(currentDate), $todo.id) + + if 1 == rowsUpdated: + todo.modificationDate = currentDate + result = true + + +# - Procs dealing directly with the database +# +proc addTodo*(conn: TDbConn; priority: int; text: string): TTodo = + ## Inserts a new todo into the database. + ## + ## Returns the generated todo object. If there is an error EDb will be raised. + let + currentDate = getTime() + query = sql"""INSERT INTO Todos + (priority, is_done, desc, modification_date) + VALUES (?, 'false', ?, ?)""" + todoId = conn.insertId(query, priority, text, $int(currentDate)) + + result = initFromDB(todoId, text, priority, false, currentDate) + + +proc deleteTodo*(conn: TDbConn; todoId: int64): int64 {.discardable.} = + ## Deletes the specified todo identifier. + ## + ## Returns the number of rows which were affected (1 or 0) + let query = sql"""DELETE FROM Todos WHERE id = ?""" + result = conn.execAffectedRows(query, $todoId) + + +proc getNumEntries*(conn: TDbConn): int = + ## Returns the number of entries in the Todos table. + ## + ## If the function succeeds, returns the zero or positive value, if something + ## goes wrong a negative value is returned. + let query = sql"""SELECT COUNT(id) FROM Todos""" + try: + let row = conn.getRow(query) + result = row[0].parseInt + except: + echo("Something went wrong retrieving number of Todos entries") + result = -1 + + +proc getPagedTodos*(conn: TDbConn; params: TPagedParams; + page = 0'i64): seq[TTodo] = + ## Returns the todo entries for a specific page. + ## + ## Pages are calculated based on the params.pageSize parameter, which can be + ## set to a negative value to specify no limit at all. The query will be + ## affected by the TPagedParams, which should have sane values (call + ## initDefaults). + assert(page >= 0, "You should request a page zero or bigger than zero") + result = @[] + + # Well, if you don't want to see anything, there's no point in asking the db. + if not params.showUnchecked and not params.showChecked: return + + let + order_by = [ + if params.priorityAscending: "ASC" else: "DESC", + if params.dateAscending: "ASC" else: "DESC"] + + query = sql("""SELECT id, desc, priority, is_done, modification_date + FROM Todos + WHERE is_done = ? OR is_done = ? + ORDER BY priority $1, modification_date $2, id DESC + LIMIT ? * ?,?""" % order_by) + + args = @[$params.showChecked, $(not params.showUnchecked), + $params.pageSize, $page, $params.pageSize] + + #echo("Query " & string(query)) + #echo("args: " & args.join(", ")) + + var newId: biggestInt + for row in conn.fastRows(query, args): + let numChars = row[0].parseBiggestInt(newId) + assert(numChars > 0, "Huh, couldn't parse identifier from database?") + result.add(initFromDB(int64(newId), row[1], row[2].parseInt, + row[3].parseBool, TTime(row[4].parseInt))) + + +proc getTodo*(conn: TDbConn; todoId: int64): ref TTodo = + ## Returns a reference to a TTodo or nil if the todo could not be found. + var tempTodo: TTodo + tempTodo.id = todoId + if tempTodo.update(conn): + new(result) + result[] = tempTodo diff --git a/examples/cross_todo/nimrod_backend/readme.txt b/examples/cross_todo/nimrod_backend/readme.txt new file mode 100644 index 000000000..6529f2e67 --- /dev/null +++ b/examples/cross_todo/nimrod_backend/readme.txt @@ -0,0 +1,14 @@ +This directory contains the nimrod backend code for the todo cross platform +example. + +Unlike the cross platform calculator example, this backend features more code, +using an sqlite database for storage. Also a basic test module is provided, not +to be included with the final program but to test the exported functionality. +The test is not embedded directly in the backend.nim file to avoid being able +to access internal data types and procs not exported and replicate the +environment of client code. + +In a bigger project with several people you could run `nimrod doc backend.nim` +(or use the doc2 command for a whole project) and provide the generated html +documentation to another programer for her to implement an interface without +having to look at the source code. diff --git a/examples/cross_todo/nimrod_backend/testbackend.nim b/examples/cross_todo/nimrod_backend/testbackend.nim new file mode 100644 index 000000000..d46b34380 --- /dev/null +++ b/examples/cross_todo/nimrod_backend/testbackend.nim @@ -0,0 +1,86 @@ +# Tests the backend code. + +import backend +import db_sqlite +import strutils +import times + + +proc showPagedResults(conn: TDbConn; params: TPagedParams) = + ## Shows the contents of the database in pages of specified size. + ## + ## Hmm... I guess this is more of a debug proc which should be moved outside, + ## or to a commandline interface (hint). + var + page = 0'i64 + rows = conn.getPagedTodos(params) + + while rows.len > 0: + echo("page " & $page) + for row in rows: + echo("row id:$1, text:$2, priority:$3, done:$4, date:$5" % [$row.getId, + $row.text, $row.priority, $row.isDone, + $row.getModificationDate]) + # Query the database for the next page or quit. + if params.pageSize > 0: + page = page + 1 + rows = conn.getPagedTodos(params, page) + else: + break + + +proc dumTest() = + let conn = openDatabase("todo.sqlite3") + try: + let numTodos = conn.getNumEntries + echo("Current database contains " & $numTodos & " todo items.") + if numTodos < 10: + # Fill some dummy rows if there are not many entries yet. + discard conn.addTodo(3, "Filler1") + discard conn.addTodo(4, "Filler2") + + var todo = conn.addTodo(2, "Testing") + echo("New todo added with id " & $todo.getId) + + # Try changing it and updating the database. + var clonedTodo = conn.getTodo(todo.getId)[] + assert(clonedTodo.text == todo.text, "Should be equal") + todo.text = "Updated!" + todo.priority = 7 + todo.isDone = true + if todo.save(conn): + echo("Updated priority $1, done $2" % [$todo.priority, $todo.isDone]) + else: + assert(false, "Uh oh, I wasn't expecting that!") + + # Verify our cloned copy is different but can be updated. + assert(clonedTodo.text != todo.text, "Should be different") + discard clonedTodo.update(conn) + assert(clonedTodo.text == todo.text, "Should be equal") + + var params : TPagedParams + params.initDefaults + conn.showPagedResults(params) + conn.deleteTodo(todo.getId) + echo("Deleted rows for id 3? ") + let res = conn.deleteTodo(todo.getId) + echo("Deleted rows for id 3? " & $res) + if todo.update(conn): + echo("Later priority $1, done $2" % [$todo.priority, $todo.isDone]) + else: + echo("Can't update object $1 from db!" % $todo.getId) + + # Try to list content in a different way. + params.pageSize = 5 + params.priorityAscending = true + params.dateAscending = true + params.showChecked = true + conn.showPagedResults(params) + finally: + conn.close + echo("Database closed") + + +# Code that will be run only on the commandline. +when isMainModule: + dumTest() diff --git a/examples/cross_todo/nimrod_commandline/nimtodo.nim b/examples/cross_todo/nimrod_commandline/nimtodo.nim new file mode 100644 index 000000000..83405a4a0 --- /dev/null +++ b/examples/cross_todo/nimrod_commandline/nimtodo.nim @@ -0,0 +1,347 @@ +# Implements a command line interface against the backend. + +import backend +import db_sqlite +import os +import parseopt +import parseutils +import strutils +import times + +const + USAGE = """nimtodo - Nimrod cross platform todo manager + +Usage: + nimtodo [command] [list options] + +Commands: + -a=int text Adds a todo entry with the specified priority and text. + -c=int Marks the specified todo entry as done. + -u=int Marks the specified todo entry as not done. + -d=int|all Deletes a single entry from the database, or all entries. + -g Generates some rows with values for testing. + -l Lists the contents of the database. + -h, --help shows this help + +List options (optional): + -p=+|- Sorts list by ascending|desdencing priority. Default:desdencing. + -m=+|- Sorts list by ascending|desdencing date. Default:desdencing. + -t Show checked entries. By default they are not shown. + -z Hide unchecked entries. By default they are shown. + +Examples: + nimtodo -a=4 Water the plants + nimtodo -c:87 + nimtodo -d:2 + nimtodo -d:all + nimtodo -l -p=+ -m=- -t + +""" + +type + TCommand = enum # The possible types of commands + commandAdd # The user wants to add a new todo entry. + commandCheck # User wants to check a todo entry. + commandUncheck # User wants to uncheck a todo entry. + commandDelete # User wants to delete a single todo entry. + commandNuke # User wants to purge all database entries. + commandGenerate # Add random rows to the database, for testing. + commandList # User wants to list contents. + + TParamConfig = object of TObject + # Structure containing the parsed options from the commandline. + command: TCommand # Store the type of operation + addPriority: int # Only valid with commandAdd, stores priority. + addText: seq[string] # Only valid with commandAdd, stores todo text. + todoId: int64 # The todo id for operations like check or delete. + listParams: TPagedParams # Uses the backend structure directly for params. + + +proc initDefaults(params: var TParamConfig) = + ## Initialises defaults value in the structure. + ## + ## Most importantly we want to have an empty list for addText. + params.listParams.initDefaults + params.addText = @[] + + +proc parseCmdLine(): TParamConfig = + ## Parses the commandline. + ## + ## Returns a TParamConfig structure filled with the proper values or directly + ## calls quit() with the appropriate error message. + var + specifiedCommand = false + usesListParams = false + p = initOptParser() + key, val: TaintedString + newId: biggestInt + + result.initDefaults + + try: + while true: + next(p) + key = p.key + val = p.val + + case p.kind + of cmdArgument: + if specifiedCommand and commandAdd == result.command: + result.addText.add(key) + else: + stdout.write(USAGE) + quit("Argument ($1) detected without add command." % [key], 1) + of cmdLongOption, cmdShortOption: + case normalize(key) + of "help", "h": + stdout.write(USAGE) + quit(0) + of "a": + if specifiedCommand: + stdout.write(USAGE) + quit("Only one command can be specified at a time! ($1)" % [val], 2) + else: + result.command = commandAdd + result.addPriority = val.parseInt + specifiedCommand = true + of "c": + if specifiedCommand: + stdout.write(USAGE) + quit("Only one command can be specified at a time! ($1)" % [val], 2) + else: + result.command = commandCheck + let numChars = string(val).parseBiggestInt(newId) + if numChars < 1: raise newException(EInvalidValue, "Empty string?") + result.todoId = newId + specifiedCommand = true + of "u": + if specifiedCommand: + stdout.write(USAGE) + quit("Only one command can be specified at a time! ($1)" % [val], 2) + else: + result.command = commandUncheck + let numChars = val.parseBiggestInt(newId) + if numChars < 1: raise newException(EInvalidValue, "Empty string?") + result.todoId = newId + specifiedCommand = true + of "d": + if specifiedCommand: + stdout.write(USAGE) + quit("Only one command can be specified at a time! ($1)" % [val], 2) + else: + if "all" == val: + result.command = commandNuke + else: + result.command = commandDelete + let numChars = val.parseBiggestInt(newId) + if numChars < 1: + raise newException(EInvalidValue, "Empty string?") + result.todoId = newId + specifiedCommand = true + of "g": + if specifiedCommand: + stdout.write(USAGE) + quit("Only one command can be specified at a time! ($1)" % [val], 2) + else: + if val.len > 0: + stdout.write(USAGE) + quit("Unexpected value '$1' for switch l." % [val], 3) + result.command = commandGenerate + specifiedCommand = true + of "l": + if specifiedCommand: + stdout.write(USAGE) + quit("Only one command can be specified at a time! ($1)" % [val], 2) + else: + if val.len > 0: + stdout.write(USAGE) + quit("Unexpected value '$1' for switch l." % [val], 3) + result.command = commandList + specifiedCommand = true + of "p": + usesListParams = true + if "+" == val: + result.listParams.priorityAscending = true + elif "-" == val: + result.listParams.priorityAscending = false + else: + stdout.write(USAGE) + quit("Priority parameter ($1) should be + or |." % [val], 4) + of "m": + usesListParams = true + if "+" == val: + result.listParams.dateAscending = true + elif "-" == val: + result.listParams.dateAscending = false + else: + stdout.write(USAGE) + quit("Date parameter ($1) should be + or |." % [val], 4) + of "t": + usesListParams = true + if val.len > 0: + stdout.write(USAGE) + quit("Unexpected value '$1' for switch t." % [val], 5) + result.listParams.showChecked = true + of "z": + usesListParams = true + if val.len > 0: + stdout.write(USAGE) + quit("Unexpected value '$1' for switch z." % [val], 5) + result.listParams.showUnchecked = false + else: + stdout.write(USAGE) + quit("Unexpected option '$1'." % [key], 6) + of cmdEnd: + break + except EInvalidValue: + stdout.write(USAGE) + quit("Invalid int value '$1' for parameter '$2'." % [val, key], 7) + + if not specifiedCommand: + stdout.write(USAGE) + quit("Didn't specify any command.", 8) + + if commandAdd == result.command and result.addText.len < 1: + stdout.write(USAGE) + quit("Used the add command, but provided no text/description.", 9) + + if usesListParams and commandList != result.command: + stdout.write(USAGE) + quit("Used list options, but didn't specify the list command.", 10) + + +proc generateDatabaseRows(conn: TDbConn) = + ## Adds some rows to the database ignoring errors. + discard conn.addTodo(1, "Watch another random youtube video") + discard conn.addTodo(2, "Train some starcraft moves for the league") + discard conn.addTodo(3, "Spread the word about Nimrod") + discard conn.addTodo(4, "Give fruit superavit to neighbours") + var todo = conn.addTodo(4, "Send tax form through snail mail") + todo.isDone = true + discard todo.save(conn) + discard conn.addTodo(1, "Download new anime to watch") + todo = conn.addTodo(2, "Build train model from scraps") + todo.isDone = true + discard todo.save(conn) + discard conn.addTodo(5, "Buy latest Britney Spears album") + discard conn.addTodo(6, "Learn a functional programming language") + echo("Generated some entries, they were added to your database.") + + +proc listDatabaseContents(conn: TDbConn; listParams: TPagedParams) = + ## Dumps the database contents formatted to the standard output. + ## + ## Pass the list/filter parameters parsed from the commandline. + var params = listParams + params.pageSize = -1 + + let todos = conn.getPagedTodos(params) + if todos.len < 1: + echo("Database empty") + return + + echo("Todo id, is done, priority, last modification date, text:") + # First detect how long should be our columns for formatting. + var cols: array[0..2, int] + for todo in todos: + cols[0] = max(cols[0], ($todo.getId).len) + cols[1] = max(cols[1], ($todo.priority).len) + cols[2] = max(cols[2], ($todo.getModificationDate).len) + + # Now dump all the rows using the calculated alignment sizes. + for todo in todos: + echo("$1 $2 $3, $4, $5" % [ + ($todo.getId).align(cols[0]), + if todo.isDone: "[X]" else: "[-]", + ($todo.priority).align(cols[1]), + ($todo.getModificationDate).align(cols[2]), + todo.text]) + + +proc deleteOneTodo(conn: TDbConn; todoId: int64) = + ## Deletes a single todo entry from the database. + let numDeleted = conn.deleteTodo(todoId) + if numDeleted > 0: + echo("Deleted todo id " & $todoId) + else: + quit("Couldn't delete todo id " & $todoId, 11) + + +proc deleteAllTodos(conn: TDbConn) = + ## Deletes all the contents from the database. + ## + ## Note that it would be more optimal to issue a direct DELETE sql statement + ## on the database, but for the sake of the example we will restrict + ## ourselfves to the API exported by backend. + var + counter: int64 + params: TPagedParams + + params.initDefaults + params.pageSize = -1 + params.showUnchecked = true + params.showChecked = true + + let todos = conn.getPagedTodos(params) + for todo in todos: + if conn.deleteTodo(todo.getId) > 0: + counter += 1 + else: + quit("Couldn't delete todo id " & $todo.getId, 12) + + echo("Deleted $1 todo entries from database." % $counter) + + +proc setTodoCheck(conn: TDbConn; todoId: int64; value: bool) = + ## Changes the check state of a todo entry to the specified value. + let + newState = if value: "checked" else: "unchecked" + todo = conn.getTodo(todoId) + + if todo == nil: + quit("Can't modify todo id $1, its not in the database." % $todoId, 13) + + if todo[].isDone == value: + echo("Todo id $1 was already set to $2." % [$todoId, newState]) + return + + todo[].isDone = value + if todo[].save(conn): + echo("Todo id $1 set to $2." % [$todoId, newState]) + else: + quit("Error updating todo id $1 to $2." % [$todoId, newState]) + + +proc addTodo(conn: TDbConn; priority: int; tokens: seq[string]) = + ## Adds to the database a todo with the specified priority. + ## + ## The tokens are joined as a single string using the space character. The + ## created id will be displayed to the user. + let todo = conn.addTodo(priority, tokens.join(" ")) + echo("Created todo entry with id:$1 for priority $2 and text '$3'." % [ + $todo.getId, $todo.priority, todo.text]) + + +when isMainModule: + ## Main entry point. + let + opt = parseCmdLine() + dbPath = getConfigDir() / "nimtodo.sqlite3" + + if not dbPath.existsFile: + createDir(getConfigDir()) + echo("No database found at $1, it will be created for you." % dbPath) + + let conn = openDatabase(dbPath) + try: + case opt.command + of commandAdd: addTodo(conn, opt.addPriority, opt.addText) + of commandCheck: setTodoCheck(conn, opt.todoId, true) + of commandUncheck: setTodoCheck(conn, opt.todoId, false) + of commandDelete: deleteOneTodo(conn, opt.todoId) + of commandNuke: deleteAllTodos(conn) + of commandGenerate: generateDatabaseRows(conn) + of commandList: listDatabaseContents(conn, opt.listParams) + finally: + conn.close diff --git a/examples/cross_todo/nimrod_commandline/readme.txt b/examples/cross_todo/nimrod_commandline/readme.txt new file mode 100644 index 000000000..17820a7d3 --- /dev/null +++ b/examples/cross_todo/nimrod_commandline/readme.txt @@ -0,0 +1,18 @@ +This directory contains the nimrod commandline version of the todo cross +platform example. + +The commandline interface can be used only through switches, running the binary +once will spit out the basic help. The commands you can use are the typical on +such an application: add, check/uncheck and delete (further could be added, +like modification at expense of parsing/option complexity). The list command is +the only one which dumps the contents of the database. The output can be +filtered and sorted through additional parameters. + +When you run the program for the first time the todo database will be generated +in your user's data directory. To cope with an empty database, a special +generation switch can be used to fill the database with some basic todo entries +you can play with. + +Compilation of the interface is fairly easy, just include the path to the +backend in your compilation command. A basic build.sh is provided for unix like +platforms with the correct parameters. diff --git a/examples/cross_todo/readme.txt b/examples/cross_todo/readme.txt new file mode 100644 index 000000000..acea8973a --- /dev/null +++ b/examples/cross_todo/readme.txt @@ -0,0 +1,5 @@ +The cross platform todo illustrates how to use Nimrod to create a backend +called by different native user interfaces. + +This example builds on the knowledge learned from the cross_calculator example. +Check it out first to learn how to set up nimrod on different platforms. |