Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

add an alternate implementation for LuaSQLite3 (lsqlite3)

  • Loading branch information...
commit 3fdf3b19fe4177dedbdde003681b4903d1ca25a5 1 parent 6e1712e
@fperrad authored
View
1  CHANGES
@@ -2,6 +2,7 @@ Revision history for lua-CoatPersistent
0.1.3
+ work with LuaSQL 2.3.0
+ + add an alternate implementation for LuaSQLite3 (lsqlite3)
0.1.2 Sat Mar 3 22:30:00 2012
+ work with Lua 5.2.0
View
1  CMakeLists.txt
@@ -5,6 +5,7 @@ cmake_minimum_required ( VERSION 2.6 )
include ( dist.cmake )
install_lua_module ( Coat.Persistent src/Coat/Persistent.lua )
+install_lua_module ( Coat.Persistent.lsqlite3 src/Coat/Persistent/lsqlite3.lua )
install_data ( CHANGES COPYRIGHT README.md )
install_test ( test/ )
View
6 Makefile
@@ -15,10 +15,13 @@ all: dist.cmake
@echo "Nothing to build here, you can just make install"
install:
- cp src/Coat/Persistent.lua $(LIBDIR)/Coat
+ mkdir -p $(LIBDIR)/Coat/Persistent
+ cp src/Coat/Persistent.lua $(LIBDIR)/Coat
+ cp src/Coat/Persistent/lsqlite3.lua $(LIBDIR)/Coat/Persistent
uninstall:
rm -f $(LIBDIR)/Coat/Persistent.lua
+ rm -f $(LIBDIR)/Coat/Persistent/lsqlite3.lua
manifest_pl := \
use strict; \
@@ -102,6 +105,7 @@ check: test
test:
cd src && prove --exec=$(LUA) ../test/*.t
+ cd src && prove --exec="$(LUA) -l Coat.Persistent.lsqlite3" ../test/*.t
coverage:
rm -f src/luacov.stats.out src/luacov.report.out
View
4 rockspec.in
@@ -19,6 +19,7 @@ description = {
dependencies = {
'lua >= 5.1',
'luasql-sqlite3 >= 2.2.0',
+-- 'lsqlite3 >= 0.7',
'dado >= 1.2.0',
'lua-coat >= 0.8.6',
'lua-testmore >= 0.2.3',
@@ -26,7 +27,8 @@ dependencies = {
build = {
type = 'builtin',
modules = {
- ['Coat.Persistent'] = 'src/Coat/Persistent.lua',
+ ['Coat.Persistent'] = 'src/Coat/Persistent.lua',
+ ['Coat.Persistent.lsqlite3'] = 'src/Coat/Persistent/lsqlite3.lua',
},
copy_directories = { 'doc', 'test' },
}
View
329 src/Coat/Persistent/lsqlite3.lua
@@ -0,0 +1,329 @@
+
+--
+-- lua-CoatPersistent : <http://fperrad.github.com/lua-CoatPersistent/>
+--
+
+local ipairs = ipairs
+local pairs = pairs
+local rawget = rawget
+local rawset = rawset
+local setmetatable = setmetatable
+local tonumber = tonumber
+local tostring = tostring
+local type = type
+local _G = _G
+local Coat = require 'Coat'
+local Meta = require 'Coat.Meta.Class'
+local dado = require 'dado.sql'
+local sqlite3 = require 'lsqlite3'
+
+local error = Coat.error
+local argerror = Coat.argerror
+local checktype = Coat.checktype
+local _class = Coat._class
+local has = Coat.has
+local type = Coat.type
+
+_ENV = nil
+local _M = {}
+
+local drv = {}
+local cnx = {}
+
+local function establish_connection (class, driver, fname)
+ driver = driver or 'sqlite3'
+ drv[class] = driver
+ local conn = cnx[driver]
+ if not conn then
+ local err, msg
+ if fname then
+ conn, err, msg = sqlite3.open(fname)
+ else
+ conn, err, msg = sqlite3.open_memory()
+ end
+ if not conn then
+ error(msg)
+ end
+ cnx[driver] = conn
+ end
+ return conn
+end
+_M.establish_connection = establish_connection
+
+local function connection (class)
+ return cnx[drv[class]]
+end
+_M.connection = connection
+
+local function execute (class, sql)
+ local trace = _M.trace
+ if trace then
+ trace('#', sql)
+ end
+ local conn = cnx[drv[class]]
+ if not conn then
+ error("No connection for class " .. class._NAME)
+ end
+ local r = conn:exec(sql)
+ if r ~= sqlite3.OK then
+ error(r)
+ end
+ return r
+end
+
+local function next_id (class)
+ local conn = cnx[drv[class]]
+ if not conn then
+ error("No connection for class " .. class._NAME)
+ end
+ return 1 + conn:last_insert_rowid()
+end
+
+local function attributes (class)
+ local t = {}
+ for _, v in ipairs(class._ATTR_P) do
+ t[#t+1] = v
+ end
+ for _, cl in ipairs(class._PARENT) do
+ for _, v in ipairs(cl._ATTR_P) do
+ t[#t+1] = v
+ end
+ end
+ return t
+end
+
+local function save (class, obj)
+ local primary_key = class._PRIMARY_KEY
+
+ local values = {}
+ for _, field in ipairs(attributes(class)) do
+ local val = obj[field]
+ if val ~= nil then
+ values[field] = tostring(val)
+ end
+ end
+
+ if rawget(obj, '_db_exist') then
+ local cond = dado.AND { [primary_key] = obj[primary_key] }
+ execute(class, dado.update(class._TABLE_NAME, values, cond))
+ else
+ obj[primary_key] = next_id(class)
+ values[primary_key] = obj[primary_key]
+ execute(class, dado.insert(class._TABLE_NAME, values))
+ rawset(obj, '_db_exist', true)
+ end
+
+ local t = rawget(obj, '_subobjects')
+ if t then
+ for i = 1, #t do
+ t[i]:save()
+ end
+ rawset(obj, '_subobjects', nil)
+ end
+
+ return obj[primary_key]
+end
+_M.save = save
+
+local function delete (class, obj)
+ local primary_key = class._PRIMARY_KEY
+ local cond = dado.AND { [primary_key] = obj[primary_key] }
+ return execute(class, dado.delete(class._TABLE_NAME, cond))
+end
+_M.delete = delete
+
+local function create (class, val)
+ if type(val) == 'table' and #val > 0 then
+ local t = {}
+ for i = 1, #val do
+ t[#t+1] = create(class, val[i])
+ end
+ return t
+ else
+ local obj = class.new(val)
+ obj:save()
+ return obj
+ end
+end
+_M.create = create
+
+local function find_by_sql (class, sql)
+ local trace = _M.trace
+ if trace then
+ trace('#', sql)
+ end
+ local conn = cnx[drv[class]]
+ if not conn then
+ error("No connection for class " .. class._NAME)
+ end
+ local f, s = conn:nrows(sql)
+ return function (_, var)
+ local row = f(s, var)
+ if row then
+ for k, v in pairs(row) do
+ row[k] = tonumber(v) or v
+ end
+ local obj = class.new(row)
+ rawset(obj, '_db_exist', true)
+ return obj
+ else
+ return nil
+ end
+ end
+end
+_M.find_by_sql = find_by_sql
+
+local function find (class, val)
+ if val == nil then
+ return find_by_sql(class, dado.select('*', class._TABLE_NAME))
+ elseif type(val) == 'number' then
+ local cond = dado.AND { [class._PRIMARY_KEY] = val }
+ return find_by_sql(class, dado.select('*', class._TABLE_NAME, cond))
+ elseif type(val) == 'string' then
+ return find_by_sql(class, dado.select('*', class._TABLE_NAME, val))
+ else
+ argerror('find', 2, "number or string expected")
+ end
+end
+_M.find = find
+
+local function has_p (class, name, options)
+ checktype('has_p', 1, name, 'string')
+ checktype('has_p', 2, options or {}, 'table')
+
+ class['find_by_' .. name] = function (val)
+ if val == nil then
+ error "Cannot find without a value"
+ end
+ local cond = dado.AND { [name] = val }
+ return find_by_sql(class, dado.select('*', class._TABLE_NAME, cond))
+ end
+
+ local t = class._ATTR_P; t[#t+1] = name
+ has(class, name, options)
+end
+_M.has_p = has_p
+
+local function has_one (class, name, options)
+ checktype('has_one', 1, name, 'string')
+ options = options or {}
+ checktype('has_one', 2, options, 'table')
+ local owned_class_name = options.class_name or name
+ local owned_class = Meta.class(owned_class_name)
+ if not owned_class then
+ error("Unknown class " .. owned_class_name)
+ end
+ local owned_table_name = owned_class._TABLE_NAME
+ local owned_primary_key = owned_class._PRIMARY_KEY
+ if not owned_primary_key then
+ error("The class " .. owned_class_name .. " has not a primary key.")
+ end
+ local attr_name = owned_table_name
+ if options.class_name then
+ attr_name = name
+ end
+ local foreign_key = options.foreign_key or owned_table_name .. '_' .. owned_primary_key
+
+ has_p(class, foreign_key, { is = 'rw', isa = 'number' })
+
+ class['_set_' .. attr_name] = function (obj, val)
+ obj[foreign_key] = val[owned_primary_key]
+ return val
+ end
+
+ class['_get_' .. attr_name] = function (obj)
+ local id = obj[foreign_key]
+ if id then
+ return find(owned_class, id)()
+ end
+ end
+
+ class._ACCESSOR = attr_name
+end
+_M.has_one = has_one
+
+local function has_many (class, name, options)
+ checktype('has_one', 1, name, 'string')
+ options = options or {}
+ checktype('has_one', 2, options, 'table')
+ local owned_class_name = options.class_name or name
+ local owned_class = Meta.class(owned_class_name)
+ if not owned_class then
+ error("Unknown class " .. owned_class_name)
+ end
+ local table_name = class._TABLE_NAME
+ local primary_key = class._PRIMARY_KEY
+ local owned_table_name = owned_class._TABLE_NAME
+ local owned_primary_key = owned_class._PRIMARY_KEY
+ if not owned_primary_key then
+ error("The class " .. owned_class_name .. " has not a primary key.")
+ end
+ local attr_name = owned_table_name .. 's'
+ if options.class_name then
+ attr_name = name
+ end
+
+ class['_set_' .. attr_name] = function (obj, list)
+ if type(list) ~= 'table' or list._CLASS then
+ error("Not an array of object")
+ end
+ local accessor = owned_class._ACCESSOR or table_name
+ local t = rawget(obj, '_subobjects')
+ if not t then
+ t = {}
+ rawset(obj, '_subobjects', t)
+ end
+ for i = 1, #list do
+ local val = list[i]
+ if not val:isa(owned_class) then
+ error("Not an object of class " .. owned_class._NAME .. " (got " .. type(val) .. ")")
+ end
+ val[accessor] = obj
+ t[#t+1] = val
+ end
+ end
+
+ class['_get_' .. attr_name] = function (obj)
+ local t = {}
+ local iter = owned_class['find_by_' .. table_name .. '_' .. primary_key](obj[primary_key])
+ for v in iter do
+ t[#t+1] = v
+ end
+ return t
+ end
+end
+_M.has_many = has_many
+
+function _G.persistent (modname, options)
+ checktype('persistent', 1, modname, 'string')
+ options = options or {}
+ checktype('persistent', 2, options, 'table')
+ local primary_key = options.primary_key or 'id'
+ local table_name = options.table_name or modname:gsub('%.', '_')
+ local M = _class(modname)
+ M._PRIMARY_KEY = primary_key
+ M._TABLE_NAME = table_name:lower()
+ M._ATTR_P = { primary_key }
+ M.establish_connection = function (...) return establish_connection(M, ...) end
+ M.connection = function () return connection(M) end
+ M.save = function (...) return save(M, ...) end
+ M.delete = function (...) return delete(M, ...) end
+ M.create = function (...) return create(M, ...) end
+ M.find = function (...) return find(M, ...) end
+ M.find_by_sql = function (...) return find_by_sql(M, ...) end
+ M.has_p = setmetatable({}, { __newindex = function (t, k, v) has_p(M, k, v) end })
+ M.has_one = setmetatable({}, { __newindex = function (t, k, v) has_one(M, k, v) end })
+ M.has_many = setmetatable({}, { __newindex = function (t, k, v) has_many(M, k, v) end })
+ has(M, primary_key, { is = 'rw', isa = 'number' })
+end
+
+_G.package.loaded['Coat.Persistent'] = _M
+
+_M._VERSION = "0.1.2"
+_M._DESCRIPTION = "lua-CoatPersistent : an ORM for lua-Coat"
+_M._COPYRIGHT = "Copyright (c) 2010-2012 Francois Perrad"
+return _M
+--
+-- This library is licensed under the terms of the MIT/X11 license,
+-- like Lua itself.
+--
View
2  test/006-find_by_sql.t
@@ -58,5 +58,5 @@ error_like( [[Person.find(true)]],
"bad argument #2 to find %(number or string expected%)" )
error_like( [[Person.find_by_sql "syntax error"]],
- 'LuaSQL: near "syntax": syntax error' )
+ 'near "syntax": syntax error' )
Please sign in to comment.
Something went wrong with that request. Please try again.