Нет никаких реальных "Направляющих пути" к администраторским интерфейсам, на самом деле - можно найти каждое возможное решение во многих приложениях. DHH подразумевал, что он предпочитает пространства имен (с Базовой аутентификацией HTTP), но это осталось простой импликацией и не одним из официальных Мнений о направляющих.
Однако я нашел хороший успех с тем подходом в последнее время (пространство имен + HTTP Основной). Это похоже на это:
routes.rb:
map.namespace :admin do |admin|
admin.resources :users
admin.resources :posts
end
admin/users_controller.rb:
class Admin::UsersController < ApplicationController
before_filter :admin_required
# ...
end
application.rb
class ApplicationController < ActionController::Base
# ...
protected
def admin_required
authenticate_or_request_with_http_basic do |user_name, password|
user_name == 'admin' && password == 's3cr3t'
end if RAILS_ENV == 'production' || params[:admin_http]
end
end
условное выражение на authenticate_or_request_with_http_basic
триггеры HTTP Основной автор в производственном режиме или когда Вы добавляете ?admin_http=true
к любому URL, таким образом, можно протестировать его на функциональных испытаниях и путем ручного обновления URL, поскольку Вы просматриваете свой участок разработки.
Here's an example literal transcription of your code, with a helpful Class
library that could be moved to another file.
This is by no means a canonical implementation of Class
; feel free to define your object model however you like.
Class = {}
function Class:new(super)
local class, metatable, properties = {}, {}, {}
class.metatable = metatable
class.properties = properties
function metatable:__index(key)
local prop = properties[key]
if prop then
return prop.get(self)
elseif class[key] ~= nil then
return class[key]
elseif super then
return super.metatable.__index(self, key)
else
return nil
end
end
function metatable:__newindex(key, value)
local prop = properties[key]
if prop then
return prop.set(self, value)
elseif super then
return super.metatable.__newindex(self, key, value)
else
rawset(self, key, value)
end
end
function class:new(...)
local obj = setmetatable({}, self.metatable)
if obj.__new then
obj:__new(...)
end
return obj
end
return class
end
ElectronicDevice = Class:new()
function ElectronicDevice:__new()
self.isOn = false
end
ElectronicDevice.properties.isOn = {}
function ElectronicDevice.properties.isOn:get()
return self._isOn
end
function ElectronicDevice.properties.isOn:set(value)
self._isOn = value
end
function ElectronicDevice:Reboot()
self._isOn = false
self:ResetHardware()
self._isOn = true
end
Router = Class:new(ElectronicDevice)
Modem = Class:new(ElectronicDevice)
function Modem:WarDialNeighborhood(areaCode)
local cisco = Router:new()
cisco:Reboot()
self:Reboot()
if self._isOn then
self:StartDialing(areaCode)
end
end
If you were to stick to get/set methods for properties, you wouldn't need __index
and __newindex
functions, and could just have an __index
table. In that case, the easiest way to simulate inheritance is something like this:
BaseClass = {}
BaseClass.index = {}
BaseClass.metatable = {__index = BaseClass.index}
DerivedClass = {}
DerivedClass.index = setmetatable({}, {__index = BaseClass.index})
DerivedClass.metatable = {__index = DerivedClass.index}
In other words, the derived class's __index
table "inherits" the base class's __index
table. This works because Lua, when delegating to an __index
table, effectively repeats the lookup on it, so the __index
table's metamethods are invoked.
Also, be wary about calling obj.Method(...)
vs obj:Method(...)
. obj:Method(...)
is syntactic sugar for obj.Method(obj, ...)
, and mixing up the two calls can produce unusual errors.
Мне нравилось это делать, реализуя функцию clone ().
Обратите внимание, что это для Lua 5.0. Я думаю, что в 5.1 больше встроенных объектно-ориентированных конструкций.
clone = function(object, ...)
local ret = {}
-- clone base class
if type(object)=="table" then
for k,v in pairs(object) do
if type(v) == "table" then
v = clone(v)
end
-- don't clone functions, just inherit them
if type(v) ~= "function" then
-- mix in other objects.
ret[k] = v
end
end
end
-- set metatable to object
setmetatable(ret, { __index = object })
-- mix in tables
for _,class in ipairs(arg) do
for k,v in pairs(class) do
if type(v) == "table" then
v = clone(v)
end
-- mix in v.
ret[k] = v
end
end
return ret
end
Затем вы определяете класс как таблицу:
Thing = {
a = 1,
b = 2,
foo = function(self, x)
print("total = ", self.a + self.b + x)
end
}
Чтобы создать его экземпляр или наследовать от него, вы используете clone (), и вы можете переопределить вещи, передав их в другую таблицу (или таблицы) в качестве подстановок
myThing = clone(Thing, { a = 5, b = 10 })
Для вызова вы используете синтаксис:
myThing:foo(100);
Это напечатает:
total = 115
Чтобы получить подкласс, вы в основном определяете другой объект-прототип:
BigThing = clone(Thing, {
-- and override stuff.
foo = function(self, x)
print("hello");
end
}
Этот метод ДЕЙСТВИТЕЛЬНО простой, возможно, слишком простой, но он работал хорошо для моего проекта.
Your updated code is wordy, but should work. Except, you have a typo that is breaking one of the metatables:
--Modem inherits from ElectronicDevice Modem = setmetatable({},{__index,ElectronicDevice});
should read
--Modem inherits from ElectronicDevice Modem = setmetatable({},{__index=ElectronicDevice});
The existing fragment made the Modem
metatable be an array where the first element was almost certainly nil (the usual value of _G.__index
unless you are using strict.lua
or something similar) and the second element is ElectronicDevice
.
The Lua Wiki description will make sense after you've grokked metatables a bit more. One thing that helps is to build a little infrastructure to make the usual patterns easier to get right.
I'd also recommend reading the chapter on OOP in PiL. You will want to re-read the chapters on tables and metatables too. Also, I've linked to the online copy of the 1st edition, but owning a copy of the 2nd is highly recommended. There is also a couple of articles in the Lua Gems book that relate. It, too, is recommended.
В Lua действительно легко выполнять ООП, подобное классу; просто поместите все «методы» в поле __ index
метатаблицы:
local myClassMethods = {}
local my_mt = {__index=myClassMethods}
function myClassMethods:func1 (x, y)
-- Do anything
self.x = x + y
self.y = y - x
end
............
function myClass ()
return setmetatable ({x=0,y=0}, my_mt)
Лично мне никогда не требовалось наследование, так что вышеизложенного мне достаточно. Если этого недостаточно, вы можете установить метатаблицу для таблицы методов:
local mySubClassMethods = setmetatable ({}, {__index=myClassMethods})
local my_mt = {__index=mySubClassMethods}
function mySubClassMethods:func2 (....)
-- Whatever
end
function mySubClass ()
return setmetatable ({....}, my_mt)
update: В вашем обновленном коде есть ошибка:
Router = {};
mt_for_router = {__index=Router}
--Router inherits from ElectronicDevice
Router = setmetatable({},{__index=ElectronicDevice});
Обратите внимание, что вы инициализируете Router
и строите mt_for_router
из этого; но затем вы переназначаете Маршрутизатор
новой таблице, а mt_for_router
по-прежнему указывает на исходный Маршрутизатор
.
Замените Маршрутизатор = {}
с Router = setmetatable ({}, {__ index = ElectronicDevice})
(до инициализации mt_for_router
).
Есть несколько способов сделать это, но я так делаю (обновлено с выстрелом по наследованию):
function newRGB(r, g, b)
local rgb={
red = r;
green = g;
blue = b;
setRed = function(self, r)
self.red = r;
end;
setGreen = function(self, g)
self.green= g;
end;
setBlue = function(self, b)
self.blue= b;
end;
show = function(self)
print("red=",self.red," blue=",self.blue," green=",self.green);
end;
}
return rgb;
end
purple = newRGB(128, 0, 128);
purple:show();
purple:setRed(180);
purple:show();
---// Does this count as inheritance?
function newNamedRGB(name, r, g, b)
local nrgb = newRGB(r, g, b);
nrgb.__index = nrgb; ---// who is self?
nrgb.setName = function(self, n)
self.name = n;
end;
nrgb.show = function(self)
print(name,": red=",self.red," blue=",self.blue," green=",self.green);
end;
return nrgb;
end
orange = newNamedRGB("orange", 180, 180, 0);
orange:show();
orange:setGreen(128);
orange:show();
Я не реализую частные, защищенные и т. Д. , хотя возможно .
Если вы не хотите изобретать велосипед, есть хорошая библиотека Lua, реализующая несколько объектных моделей. Он называется LOOP .