2016-02-27 2 views
1

Я пытаюсь реализовать базу данных в виде таблицы Lua. Используя metatables, эта таблица будет пустой, и когда элемент будет запрошен или изменен в таблице, он вернет или изменит элемент в базе данных. Сама база данных никогда не будет загружена в память, за исключением запрашиваемых частей. Он должен взаимодействовать с программой как с таблицей (как с таблицей). Таблица, так как это только «фронт», сохранит измененные данные в базе данных (а не определяет этот элемент в таблице).Lua table как интерфейс к базе данных

В таблице без таблиц внутри нее это легко реализовать. Я пытаюсь заставить его работать с многоуровневой таблицей неопределенной глубины. (За исключением: База данных Я рассматриваю это Redis В идеале это может быть реализовано для любой базы данных или базы данных, как сервер с помощью только изменения основного рабочего синтаксиса.).

Из-за поведения метатаблицы Lua, метод __newindex используется только тогда, когда что-то изменяется на верхнем уровне (или создается, если вы используете прокси). Метод __index вызывается, когда что-то читается, даже если вызов состоит в том, чтобы что-то изменить в подкатегории. Из-за этого я пытаюсь написать метод __index, который, когда запрашивается таблица, возвращает еще один псевдопрокси (таблица проксирования базы данных, а не другие таблицы) с тем же поведением, за исключением прокси для таблицы/array/list на верхнем уровне и т. д. до неопределенной глубины. Я борюсь.

Мои вопросы:

  • ли это было реализовано раньше?
  • Должен ли я сосредоточиться на «правильной» системе классов, а не на том, что я сейчас делаю?

ответ

1

При создании таблицы, просто добавьте пустую таблицу в подделке и установить его метатаблица:

local fake = {} 
do 
    local lookup = {} --Will be using this to avoid using lots of metatables 

    local real = {} 

    local meta 
    meta = { 
     __index = function(self,i) 
     return rawget(lookup[self], i) 
     end, 
     __newindex = function(self,i,v) 
     rawset(lookup[self], i, v) 
     if type(v) == "table" then 
      rawset(self, i, setmetatable({},meta)) 
      lookup[self[i]] = v 
     end 
     end 
    } 

    setmetatable(fake, meta) 
    lookup[fake] = real 
end 

fake[1] = "hello" 
print(fake[1]) 
print(rawget(fake, 1)) 
fake.x = {"hi"} 
print(fake.x) 
print(rawget(fake, 'x')) --This still prints a table because there actually is one, but in reality it's abiding by our rules 
print(fake.x[1]) 
print(rawget(fake.x, 1)) 
fake.x.y = "aha" 
print(fake.x.y) 
print(rawget(fake.x, 'y')) 

Единственное ограничение этого метода является то они могут напрямую изменять базу данных следующим образом:

fake.myvalue = {} 
fake.myvalue = 5 

Другой метод может быть, чтобы обернуть как вы идете:

local fake = {} 
do 
    local lookup = {} --Will be using this to avoid using lots of metatables 
    local cache = {} --will be using to avoid usings tons of new objects 

    local real = {} 

    local meta 
    meta = { 
     __index = function(self,i) 
     local val = rawget(lookup[self], i) 
     if type(val) == "table" then 
      if cache[val] then 
       return cache[val] 
      else 
       local faker = {} 
       lookup[faker] = val 
       cache[val] = faker 
       return setmetatable(faker, meta) 
      end 
     else 
      return val 
     end 
     end, 
     __newindex = function(self,i,v) 
     rawset(lookup[self], i, v) 
     end 
    } 

    setmetatable(fake, meta) 
    lookup[fake] = real 
end 

fake[1] = "hello" 
print(fake[1]) 
print(rawget(fake, 1)) 

fake.x = {"hi"} 
print(fake.x) 
print(rawget(fake, 'x')) --This still prints a table because there actually is one, but in reality it's abiding by our rules 
print(fake.x[1]) 
print(rawget(fake.x, 1)) 
fake.x.y = "aha" 
print(fake.x.y) 
print(rawget(fake.x, 'y')) 

Что полностью устраняет проблему прямого изменения

Смежные вопросы