Как создать класс, подкласс и свойства в Lua?

Нет никаких реальных "Направляющих пути" к администраторским интерфейсам, на самом деле - можно найти каждое возможное решение во многих приложениях. 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, поскольку Вы просматриваете свой участок разработки.

7
задан Peter Mortensen 12 September 2012 в 18:42
поделиться

6 ответов

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.

8
ответ дан 6 December 2019 в 07:07
поделиться

Мне нравилось это делать, реализуя функцию 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
}

Этот метод ДЕЙСТВИТЕЛЬНО простой, возможно, слишком простой, но он работал хорошо для моего проекта.

3
ответ дан 6 December 2019 в 07:07
поделиться

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.

1
ответ дан 6 December 2019 в 07:07
поделиться

В 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 ).

1
ответ дан 6 December 2019 в 07:07
поделиться

Есть несколько способов сделать это, но я так делаю (обновлено с выстрелом по наследованию):

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();

Я не реализую частные, защищенные и т. Д. , хотя возможно .

7
ответ дан 6 December 2019 в 07:07
поделиться

Если вы не хотите изобретать велосипед, есть хорошая библиотека Lua, реализующая несколько объектных моделей. Он называется LOOP .

4
ответ дан 6 December 2019 в 07:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: