diff options
author | David Walter Seikel | 2014-04-01 22:52:55 +1000 |
---|---|---|
committer | David Walter Seikel | 2014-04-01 22:52:55 +1000 |
commit | 61f2d31ba18f0cc516893ce3aabc24008e900e29 (patch) | |
tree | ea23e6ecb00ae09ce6896425a25eced585e16eba | |
parent | Massage Thing design notes into docs, and clean up some Stuff as a result. (diff) | |
download | SledjHamr-61f2d31ba18f0cc516893ce3aabc24008e900e29.zip SledjHamr-61f2d31ba18f0cc516893ce3aabc24008e900e29.tar.gz SledjHamr-61f2d31ba18f0cc516893ce3aabc24008e900e29.tar.bz2 SledjHamr-61f2d31ba18f0cc516893ce3aabc24008e900e29.tar.xz |
Separate out the metatable __ stuff from Thing, put it into Mum.
-rw-r--r-- | ClientHamr/GuiLua/skang.lua | 140 |
1 files changed, 70 insertions, 70 deletions
diff --git a/ClientHamr/GuiLua/skang.lua b/ClientHamr/GuiLua/skang.lua index 8d15dfe..de0a0af 100644 --- a/ClientHamr/GuiLua/skang.lua +++ b/ClientHamr/GuiLua/skang.lua | |||
@@ -57,10 +57,6 @@ The old skang argument types are - | |||
57 | do -- Only I'm not gonna indent this. | 57 | do -- Only I'm not gonna indent this. |
58 | 58 | ||
59 | 59 | ||
60 | -- There is no ThingSpace, or Stuff, now it's all just in this meta table. Predefined here coz moduleBegin references Thing. | ||
61 | local Thing = {} | ||
62 | |||
63 | |||
64 | -- TODO - This needs to be expanded a bit to cover things like 1.42 | 60 | -- TODO - This needs to be expanded a bit to cover things like 1.42 |
65 | local versions = { | 61 | local versions = { |
66 | '0%.0', 'unwritten', 'Just a stub, no code at all, or completely non-existant.', | 62 | '0%.0', 'unwritten', 'Just a stub, no code at all, or completely non-existant.', |
@@ -427,7 +423,7 @@ So the values stored in this Thing are actually stored in meta(parent)__values[t | |||
427 | parent[thing] = value -> __newindex(parent, thing, value) -> meta(parent).__values[thing] = value | 423 | parent[thing] = value -> __newindex(parent, thing, value) -> meta(parent).__values[thing] = value |
428 | 424 | ||
429 | 425 | ||
430 | A Stuff is a description table of a Thing that includes - | 426 | Each Thing has a description table that includes - |
431 | names - An array of names, the first one is the "official" name. | 427 | names - An array of names, the first one is the "official" name. |
432 | types - An array of types, the first one is the "official" type. | 428 | types - An array of types, the first one is the "official" type. |
433 | help - A descriptive text for humans to read. | 429 | help - A descriptive text for humans to read. |
@@ -436,14 +432,15 @@ A Stuff is a description table of a Thing that includes - | |||
436 | required - If the Thing is required. | 432 | required - If the Thing is required. |
437 | isValid - A function that tests if the Thing is valid. | 433 | isValid - A function that tests if the Thing is valid. |
438 | errors - Any errors related to the Thing. | 434 | errors - Any errors related to the Thing. |
439 | isKeyed - Is this thing itself a parent for Stuff that is stored under an arbitrary key. | 435 | isKeyed - Is this a parent for Things that are stored under an arbitrary key. |
440 | stuff - An array of Stuff's for sub Things, so Things that are tables can have their own Things. | 436 | stuff - An array of descriptions for sub Things, so Things that are tables can have their own Things. |
441 | and other things that aren't actually used yet. | 437 | and other things that aren't actually used yet. |
442 | All things that a Stuff doesn't have should be inherited from the Thing table. | 438 | All things that a description doesn't have should be inherited from the Thing table. |
443 | Stuff's should be able to be easily shared by various Things. | 439 | setmetatable(aStuff, {__index = Thing}) |
440 | Descriptions should be able to be easily shared by various Things. | ||
444 | 441 | ||
445 | 442 | ||
446 | A parent's metatable has __self, which is it's own Stuff. | 443 | A parent's metatable has __self, which is it's own description. |
447 | A parent is free to use it's own name space for anything it wants. | 444 | A parent is free to use it's own name space for anything it wants. |
448 | Only the variables it specifies as managed Things are dealt with here. | 445 | Only the variables it specifies as managed Things are dealt with here. |
449 | 446 | ||
@@ -451,13 +448,16 @@ Only the variables it specifies as managed Things are dealt with here. | |||
451 | TODO - | 448 | TODO - |
452 | test.foo -> test.__index(test, 'foo') -> test.__values[foo]; if that's nil, and test.stuff[foo], then return an empty table instead? | 449 | test.foo -> test.__index(test, 'foo') -> test.__values[foo]; if that's nil, and test.stuff[foo], then return an empty table instead? |
453 | stuff.s = {a='foo'} -> changes a, deletes everything else, or should. | 450 | stuff.s = {a='foo'} -> changes a, deletes everything else, or should. |
454 | Got rid of Stuff.parent, but do we still need a parent pointer? | 451 | Do we still need a parent pointer? |
455 | Should be in __values I guess. | 452 | Should be in __values I guess. |
456 | __values[key].value | 453 | __values[key].value |
457 | __values[key].parent | 454 | __values[key].parent |
458 | ]] | 455 | ]] |
459 | 456 | ||
460 | 457 | ||
458 | -- There is no ThingSpace, or Stuff, now it's all just in this meta table. | ||
459 | local Thing = {} | ||
460 | |||
461 | -- Default things values. | 461 | -- Default things values. |
462 | -- help - help text describing this Thing. | 462 | -- help - help text describing this Thing. |
463 | -- default - the default value. This could be a funcion, making this a command. | 463 | -- default - the default value. This could be a funcion, making this a command. |
@@ -495,20 +495,21 @@ Thing.errors = {} -- A list of errors returned by isValid(). | |||
495 | Thing.isValid = function (self, parent) -- Check if this Thing is valid, return resulting error messages in errors. | 495 | Thing.isValid = function (self, parent) -- Check if this Thing is valid, return resulting error messages in errors. |
496 | -- Anything that overrides this method, should call this super method first. | 496 | -- Anything that overrides this method, should call this super method first. |
497 | local name = self.names[1] | 497 | local name = self.names[1] |
498 | local mumThing = getmetatable(parent) | 498 | local metaMum = getmetatable(parent) |
499 | local value = mumThing.__values[name] | 499 | local value = metaMum.__values[name] |
500 | local mum = metaMum.__self.names[1] | ||
500 | 501 | ||
501 | local t = type(value) or 'nil' | 502 | local t = type(value) or 'nil' |
502 | self.errors = {} | 503 | self.errors = {} |
503 | -- TODO - Naturally there should be formatting functions for stuffing Thing stuff into strings, and overridable output functions. | 504 | -- TODO - Naturally there should be formatting functions for stuffing Thing stuff into strings, and overridable output functions. |
504 | if 'nil' == t then | 505 | if 'nil' == t then |
505 | if self.required then table.insert(self.errors, mumThing.__self.names[1] .. '.' .. name .. ' is required!') end | 506 | if self.required then table.insert(self.errors, mum .. '.' .. name .. ' is required!') end |
506 | else | 507 | else |
507 | if self.types[1] ~= t then table.insert(self.errors, mumThing.__self.names[1] .. '.' .. name .. ' should be a ' .. self.types[1] .. ', but it is a ' .. t .. '!') | 508 | if self.types[1] ~= t then table.insert(self.errors, mum .. '.' .. name .. ' should be a ' .. self.types[1] .. ', but it is a ' .. t .. '!') |
508 | else | 509 | else |
509 | if 'number' == t then value = '' .. value end | 510 | if 'number' == t then value = '' .. value end |
510 | if ('number' == t) or ('string' == t) then | 511 | if ('number' == t) or ('string' == t) then |
511 | if 1 ~= string.find(value, '^' .. self.pattern .. '$') then table.insert(self.errors, mumThing.__self.names[1] .. '.' .. name .. ' does not match pattern "' .. self.pattern .. '"!') end | 512 | if 1 ~= string.find(value, '^' .. self.pattern .. '$') then table.insert(self.errors, mum .. '.' .. name .. ' does not match pattern "' .. self.pattern .. '"!') end |
512 | end | 513 | end |
513 | end | 514 | end |
514 | end | 515 | end |
@@ -528,45 +529,44 @@ Thing.remove = function (self) -- Delete this Thing. | |||
528 | end | 529 | end |
529 | 530 | ||
530 | 531 | ||
531 | Thing.__index = function (parent, key) | 532 | local Mum = |
533 | { | ||
534 | __index = function (parent, key) | ||
532 | -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. | 535 | -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. |
533 | -- TODO - Java skang called isValid() on get(). On the other hand, doesn't seem to call it on set(), but calls it on append(). | 536 | -- TODO - Java skang called isValid() on get(). On the other hand, doesn't seem to call it on set(), but calls it on append(). |
534 | -- Ah, it was doing isValid() on setStufflet(). | 537 | -- Ah, it was doing isValid() on setStufflet(). |
535 | 538 | ||
536 | -- First see if this is a Thing. | 539 | -- First see if this is a Thing. |
537 | local mumThing = getmetatable(parent) | 540 | local metaMum = getmetatable(parent) |
538 | local thingy | ||
539 | 541 | ||
540 | if mumThing and mumThing.__self then | 542 | if metaMum and metaMum.__self then |
541 | thingy = mumThing.__self.stuff[key] | 543 | local thingy = metaMum.__self.stuff[key] |
542 | if thingy then | 544 | if thingy then |
543 | local name = thingy.names[1]; | 545 | return metaMum.__values[thingy.names[1] ] or thingy.default |
544 | return mumThing.__values[name] or thingy.default | ||
545 | end | 546 | end |
546 | end | 547 | end |
547 | 548 | ||
548 | -- Then see if we can inherit it from Thing. | 549 | -- Then see if we can inherit it from Thing. |
549 | return Thing[key] | 550 | return Thing[key] |
550 | end | 551 | end, |
551 | 552 | ||
552 | Thing.__newindex = function (parent, key, value) | 553 | __newindex = function (parent, key, value) |
553 | -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. | 554 | -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. |
554 | local mumThing = getmetatable(parent) | ||
555 | 555 | ||
556 | if mumThing and mumThing.__self then | 556 | -- First see if this is a Thing. |
557 | -- This is a proxy table, the values never exist in the real table. In theory. | 557 | local metaMum = getmetatable(parent) |
558 | 558 | ||
559 | -- Find the Thing and get it done. | 559 | if metaMum and metaMum.__self then |
560 | local thingy = mumThing.__self.stuff[key] | 560 | local thingy = metaMum.__self.stuff[key] |
561 | 561 | ||
562 | if not thingy then | 562 | if not thingy then |
563 | -- Deal with setting a new Stuff[key]. | 563 | -- Deal with setting a new Keyed table entry. |
564 | if mumThing.__self.isKeyed and (nil == mumThing.__values[key]) then | 564 | if metaMum.__self.isKeyed and (nil == metaMum.__values[key]) then |
565 | local newThing = copy(parent, key) | 565 | local newThing = copy(parent, key) |
566 | rawset(mumThing.__values, key, newThing) | 566 | rawset(metaMum.__values, key, newThing) |
567 | thingy = {names={key}, types={'table'}, parent=newThing, stuff=getmetatable(newThing).__self.stuff, } | 567 | thingy = {names={key}, types={'table'}, parent=newThing, stuff=getmetatable(newThing).__self.stuff, } |
568 | setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default. | 568 | setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default. |
569 | mumThing.__self.stuff[key] = thingy | 569 | metaMum.__self.stuff[key] = thingy |
570 | end | 570 | end |
571 | end | 571 | end |
572 | 572 | ||
@@ -575,8 +575,8 @@ Thing.__newindex = function (parent, key, value) | |||
575 | local valueMeta | 575 | local valueMeta |
576 | 576 | ||
577 | if 'table' == type(value) then | 577 | if 'table' == type(value) then |
578 | -- Coz setting it via mumThing screws with the __index stuff somehow. | 578 | -- Coz setting it via metaMum screws with the __index stuff somehow. |
579 | local oldValue = mumThing.__values[name] | 579 | local oldValue = metaMum.__values[name] |
580 | if 'table' == type(oldValue) then | 580 | if 'table' == type(oldValue) then |
581 | valueMeta = getmetatable(oldValue) | 581 | valueMeta = getmetatable(oldValue) |
582 | if valueMeta then | 582 | if valueMeta then |
@@ -589,7 +589,7 @@ Thing.__newindex = function (parent, key, value) | |||
589 | end | 589 | end |
590 | end | 590 | end |
591 | end | 591 | end |
592 | if nil == valueMeta then mumThing.__values[name] = value end | 592 | if nil == valueMeta then metaMum.__values[name] = value end |
593 | -- NOTE - invalid values are still stored, this is by design. | 593 | -- NOTE - invalid values are still stored, this is by design. |
594 | if not thingy:isValid(parent) then | 594 | if not thingy:isValid(parent) then |
595 | for i, v in ipairs(thingy.errors) do | 595 | for i, v in ipairs(thingy.errors) do |
@@ -603,10 +603,22 @@ Thing.__newindex = function (parent, key, value) | |||
603 | end | 603 | end |
604 | 604 | ||
605 | rawset(parent, key, value) -- Stuff it normally. | 605 | rawset(parent, key, value) -- Stuff it normally. |
606 | end | 606 | end, |
607 | 607 | ||
608 | Thing.__call = function (func, ...) | 608 | __call = function (func, ...) |
609 | return thingasm(func, ...) -- (func, {...}) | 609 | return thingasm(func, ...) -- (func, {...}) |
610 | end, | ||
611 | |||
612 | } | ||
613 | |||
614 | newMum = function () | ||
615 | local result = {} | ||
616 | for k, v in pairs(Mum) do | ||
617 | result[k] = v | ||
618 | end | ||
619 | result.__self = {stuff={}} | ||
620 | result.__values = {} | ||
621 | return result | ||
610 | end | 622 | end |
611 | 623 | ||
612 | 624 | ||
@@ -614,20 +626,19 @@ end | |||
614 | --[[ It can be called in many different ways - | 626 | --[[ It can be called in many different ways - |
615 | 627 | ||
616 | It can be called with positional arguments - (names, help, default, types, widget, required, acl, boss) | 628 | It can be called with positional arguments - (names, help, default, types, widget, required, acl, boss) |
617 | Or it can be called with a table - {names, help, pattern='...', acl='rwx'} | 629 | Or it can be called with a table - {names, help, pattern='.*', acl='rwx'} |
618 | 630 | ||
619 | The first argument can be another Thing (the parent), or a string list of names (see below). | 631 | The first argument can be another Thing (the parent), or a string list of names (see below). |
620 | 632 | ||
621 | It can be called by itself, with no parent specified - | 633 | It can be called by itself, with no parent specified - |
622 | thingasm('foo', 'help text) | 634 | thingasm('foo', 'help text) |
623 | |||
624 | In this case the surrounding Lua environment becomes the parent of foo. | 635 | In this case the surrounding Lua environment becomes the parent of foo. |
625 | If the first argument (or first in the table) is a string, then it's this form. | 636 | If the first argument (or first in the table) is a string, then it's this form. |
626 | All others include the parent as the first argument, which would be a table. | 637 | All others include the parent as the first argument, which would be a table. |
627 | 638 | ||
628 | It can be called by calling the parent as a function - | 639 | It can be called by calling the parent as a function - |
629 | foo('bar', 'some help', types='table') -- ___call(foo, 'bar', ...) And now foo is the parent. | 640 | foo('bar', 'some help', types='table') -- ___call(foo, 'bar', ...) And now foo is the parent. |
630 | foo.bar{'baz', types='Stuff'} -- thingasm({foo.bar, 'baz', ...}) | 641 | foo.bar{'baz', types='Keyed'} -- thingasm({foo.bar, 'baz', ...}) |
631 | foo.bar.baz{'field0'} -- thingasm({foo.bar.baz, 'field0'}) | 642 | foo.bar.baz{'field0'} -- thingasm({foo.bar.baz, 'field0'}) |
632 | foo.bar.baz{'field1'} | 643 | foo.bar.baz{'field1'} |
633 | ]] | 644 | ]] |
@@ -669,24 +680,16 @@ thingasm = function (names, ...) | |||
669 | 680 | ||
670 | -- Grab the environment of the calling function if no parent was passed in. | 681 | -- Grab the environment of the calling function if no parent was passed in. |
671 | parent = parent or getfenv(2) | 682 | parent = parent or getfenv(2) |
672 | local mumThing = getmetatable(parent) | 683 | local metaMum = getmetatable(parent) |
673 | if nil == mumThing then | 684 | -- Coz at module creation time, Thing is an empty table, or in case this is for a new parent. |
674 | mumThing = {} | 685 | if nil == metaMum then |
675 | setmetatable(parent, mumThing) | 686 | metaMum = newMum() |
676 | end | 687 | metaMum.__self.names = {parent._NAME or 'NoName'} |
677 | -- Coz at module creation time, Thing is an empty table, and setmetatable(mumThing, {__index = Thing}) doesn't do the right thing. | 688 | if parent._NAME then metaMum.__self.types = {'Module'} end |
678 | if nil == mumThing.__self then | 689 | setmetatable(parent, metaMum) |
679 | mumThing.__self = {stuff={}} | ||
680 | -- Seems this does not deal with __index and __newindex, and also screws up stuff somehow. | ||
681 | -- setmetatable(mumThing, {__index = Thing}) | ||
682 | mumThing.__self.names = {parent._NAME or 'NoName'} | ||
683 | if parent._NAME then mumThing.__self.types = {'Module'} end | ||
684 | mumThing.__values = {} | ||
685 | mumThing.__index = Thing.__index | ||
686 | mumThing.__newindex = Thing.__newindex | ||
687 | end | 690 | end |
688 | 691 | ||
689 | local thingy = mumThing.__self.stuff[name] | 692 | local thingy = metaMum.__self.stuff[name] |
690 | if not thingy then -- This is a new Thing. | 693 | if not thingy then -- This is a new Thing. |
691 | new = true | 694 | new = true |
692 | thingy = {} | 695 | thingy = {} |
@@ -721,32 +724,29 @@ thingasm = function (names, ...) | |||
721 | if '' == types then types = typ end | 724 | if '' == types then types = typ end |
722 | thingy.types = csv2table(types) | 725 | thingy.types = csv2table(types) |
723 | 726 | ||
727 | -- Deal with Keyed and tables. | ||
724 | if 'Keyed' == thingy.types[1] then | 728 | if 'Keyed' == thingy.types[1] then |
725 | thingy.types[1] = 'table' | 729 | thingy.types[1] = 'table' |
726 | thingy.isKeyed = true | 730 | thingy.isKeyed = true |
727 | end | 731 | end |
728 | if 'table' == thingy.types[1] then | 732 | if 'table' == thingy.types[1] then |
733 | -- Default in this case becomes a parent. | ||
729 | if '' == thingy.default then thingy.default = {} end | 734 | if '' == thingy.default then thingy.default = {} end |
730 | setmetatable(thingy.default, | 735 | local thisMum = newMum() |
731 | { | 736 | thisMum.__self = thingy |
732 | __self = thingy, | 737 | setmetatable(thingy.default, thisMum) |
733 | __values = {}, | ||
734 | __index = Thing.__index, | ||
735 | __newindex = Thing.__newindex, | ||
736 | __call = Thing.__call, | ||
737 | }) | ||
738 | end | 738 | end |
739 | 739 | ||
740 | -- Remove old names, then stash the Thing under all of it's new names. | 740 | -- Remove old names, then stash the Thing under all of it's new names. |
741 | for i, v in ipairs(oldNames) do | 741 | for i, v in ipairs(oldNames) do |
742 | mumThing.__self.stuff[v] = nil | 742 | metaMum.__self.stuff[v] = nil |
743 | end | 743 | end |
744 | for i, v in ipairs(thingy.names) do | 744 | for i, v in ipairs(thingy.names) do |
745 | mumThing.__self.stuff[v] = thingy | 745 | metaMum.__self.stuff[v] = thingy |
746 | end | 746 | end |
747 | 747 | ||
748 | -- This triggers the Thing.__newindex metamethod above. If nothing else, it triggers thingy.isValid() | 748 | -- This triggers the Mum.__newindex metamethod above. If nothing else, it triggers thingy.isValid() |
749 | if new and not mumThing.__self.isKeyed then parent[name] = thingy.default end | 749 | if new and not metaMum.__self.isKeyed then parent[name] = thingy.default end |
750 | end | 750 | end |
751 | 751 | ||
752 | 752 | ||