aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ClientHamr/GuiLua/skang.lua
diff options
context:
space:
mode:
authorDavid Walter Seikel2014-04-01 21:54:07 +1000
committerDavid Walter Seikel2014-04-01 21:54:07 +1000
commitda619eb9131bb137a375cfcc65df9412eec33d80 (patch)
tree578813776cc9f5c308d446fcfb9fcfad46afc36b /ClientHamr/GuiLua/skang.lua
parentFix up setting Stuff, mostly. (diff)
downloadSledjHamr-da619eb9131bb137a375cfcc65df9412eec33d80.zip
SledjHamr-da619eb9131bb137a375cfcc65df9412eec33d80.tar.gz
SledjHamr-da619eb9131bb137a375cfcc65df9412eec33d80.tar.bz2
SledjHamr-da619eb9131bb137a375cfcc65df9412eec33d80.tar.xz
Massage Thing design notes into docs, and clean up some Stuff as a result.
Diffstat (limited to 'ClientHamr/GuiLua/skang.lua')
-rw-r--r--ClientHamr/GuiLua/skang.lua176
1 files changed, 96 insertions, 80 deletions
diff --git a/ClientHamr/GuiLua/skang.lua b/ClientHamr/GuiLua/skang.lua
index 0a9752b..8d15dfe 100644
--- a/ClientHamr/GuiLua/skang.lua
+++ b/ClientHamr/GuiLua/skang.lua
@@ -343,39 +343,12 @@ Other Thing things are -
343 quitter:action('quit') 343 quitter:action('quit')
344 344
345 345
346 Things as a metatable field - 346 Squeal -
347 In this plan test.__something is short for metatable(test).__something.
348
349 C can set metatables to non table values. NOTE - values, not variables, so likely useless.
350
351 __index is used to look up things that don't exist in a table.
352 If table isn't actually a table, then use __index as well.
353 Actually, in that case, the environment is the "table".
354 Either way, if we get to __index, and it's a function, call the function.
355 If __index is not a function, then use __index as a table to start the lookup all over again.
356
357 __newindex is similar, it's used if table.key is nil, AND __newindex exists.
358 Otherwise, it just sets table.key.
359 Again, if __newindow is a table, then start the lookup all over again with that.
360
361 metatable(module).__stuff
362 metatable(module).__values
363
364 The Thing is stuffed in a metatable. It can be just a reference to an existing Thing elsewhere.
365 So module is free to be full of random variables that are anything.
366 If module.foo is a table, it can have it's own metatable(foo).
367 Such a table will use Thing as a proxy table via __index and __newindex, as it does now.
368
369 test.foo -> test.__index(test, 'foo') -> test.__values[foo]; if that's nil, and test.__stuff[foo], then return an empty table instead?
370
371 stuff.s = {a='foo'} -> changes a, deletes everything else, or should.
372
373 What we really want is -
374 squeal.database('db', 'host', 'someDb', 'user', 'password') -> Should return a module. 347 squeal.database('db', 'host', 'someDb', 'user', 'password') -> Should return a module.
375 local db = require 'someDbThing' -> Same as above, only the database details are encodode in the someDbThing source, OR come from someDbThing.properties. 348 local db = require 'someDbThing' -> Same as above, only the database details are encodode in the someDbThing source, OR come from someDbThing.properties.
376 db:getTable('stuff', 'someTable') -> Grabs the metadata, but not the rows. 349 db:getTable('stuff', 'someTable') -> Grabs the metadata, but not the rows.
377 db:read('stuff', 'select * from someTable'} -> Fills stuff up with several rows, including setting up the metadata for the columns. 350 db:read('stuff', 'select * from someTable'} -> Fills stuff up with several rows, including setting up the metadata for the columns.
378 stuff[1].field1 -> Is a Thing, with a __stuff in the stuff metatable, that was created automatically from the database meta data. 351 stuff[1].field1 -> Is a Thing, with a stuff in the stuff metatable, that was created automatically from the database meta data.
379 stuff:read('someIndex') -> Grabs a single row that has the key 'someIndex', or perhaps multiple rows since this might have SQL under it. 352 stuff:read('someIndex') -> Grabs a single row that has the key 'someIndex', or perhaps multiple rows since this might have SQL under it.
380 stuff = db:read('stuff', 'select * from someTable where key='someIndex') 353 stuff = db:read('stuff', 'select * from someTable where key='someIndex')
381 354
@@ -389,7 +362,7 @@ Other Thing things are -
389 widget.stuff = stuff:stuff('field1') -> This widget gets a particular stufflet. 362 widget.stuff = stuff:stuff('field1') -> This widget gets a particular stufflet.
390 widget would have to look up getmetatable(window.stuff).parent. Or maybe this should work some other way? 363 widget would have to look up getmetatable(window.stuff).parent. Or maybe this should work some other way?
391 364
392 In all these cases above, stuff is a table that has a Thing metatable, so it has __stuff. 365 In all these cases above, stuff is a table that has a Thing metatable, so it has stuff.
393 It is also a Stuff. 366 It is also a Stuff.
394 Should include some way of specifyings details like key name, where string, etc. 367 Should include some way of specifyings details like key name, where string, etc.
395 getmetatable(stuff).__keyName 368 getmetatable(stuff).__keyName
@@ -430,6 +403,61 @@ public class PersonStuff extends SquealStuff
430 403
431]] 404]]
432 405
406
407--[[ Thing structure -
408
409In the following, meta(table) is short for getmetatable(table).
410
411In Lua everything is supposed to be a first class value, and variables are just places to store values.
412All variables are in fact stored in tables, even if it's just the local environment table.
413Any variable that has a value of nil, doesn't actually exist. That's the definition.
414While non table things can have metatables, Lua can only set metatables on tables, C has no such restriction.
415meta(table).__index and __newindex only work on table entries that don't exist.
416 __index(table, key) is called if table.key is nil.
417 Though if __index is a table, then try __index[key].
418 __newindex(table, key, value) is called if table.key is nil.
419 Though if __newindex is a table, then try __newindex[key] = value.
420Using both __index and __newindex, and keeping the actual values elsewhere, is called a proxy table.
421meta(table).__call(table, ...) is called when trying to access table as a function - table(...).
422
423
424A Thing is a managed variable stored in a parent proxy table, which is usually empty.
425So the values stored in this Thing are actually stored in meta(parent)__values[thing].
426 parent[thing] -> __index (parent, thing) -> meta(parent).__values[thing]
427 parent[thing] = value -> __newindex(parent, thing, value) -> meta(parent).__values[thing] = value
428
429
430A Stuff is a description table of a Thing that includes -
431 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.
433 help - A descriptive text for humans to read.
434 default - The default value.
435 widget - A default widget definition.
436 required - If the Thing is required.
437 isValid - A function that tests if the Thing is valid.
438 errors - Any errors related to the Thing.
439 isKeyed - Is this thing itself a parent for Stuff that is 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.
441 and other things that aren't actually used yet.
442All things that a Stuff doesn't have should be inherited from the Thing table.
443Stuff's should be able to be easily shared by various Things.
444
445
446A parent's metatable has __self, which is it's own Stuff.
447A parent is free to use it's own name space for anything it wants.
448Only the variables it specifies as managed Things are dealt with here.
449
450
451TODO -
452 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.
454 Got rid of Stuff.parent, but do we still need a parent pointer?
455 Should be in __values I guess.
456 __values[key].value
457 __values[key].parent
458]]
459
460
433-- Default things values. 461-- Default things values.
434-- help - help text describing this Thing. 462-- help - help text describing this Thing.
435-- 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.
@@ -450,7 +478,7 @@ Thing.action = 'nada' -- An optional action to perform.
450Thing.tell = '' -- The skang command that created this Thing. 478Thing.tell = '' -- The skang command that created this Thing.
451Thing.pattern = '.*' -- A pattern to restrict values. 479Thing.pattern = '.*' -- A pattern to restrict values.
452 480
453Thing.isStuff = false -- Is this thing a Stuff? 481Thing.isKeyed = false -- Is this thing an arbitrarily Keyed table?
454Thing.isReadOnly = false -- Is this Thing read only? 482Thing.isReadOnly = false -- Is this Thing read only?
455Thing.isServer = false -- Is this Thing server side? 483Thing.isServer = false -- Is this Thing server side?
456Thing.isStub = false -- Is this Thing a stub? 484Thing.isStub = false -- Is this Thing a stub?
@@ -461,12 +489,9 @@ Thing.hasCrashed = 0 -- How many times this Thing has crashed.
461Thing.append = function (self,data) -- Append to the value of this Thing. 489Thing.append = function (self,data) -- Append to the value of this Thing.
462end 490end
463 491
464Thing.things = {} -- The sub things this Thing has, for modules and Stuff. 492Thing.stuff = {} -- The sub things this Thing has, for modules, tables, and Keyed tables.
465Thing.errors = {} -- A list of errors returned by isValid(). 493Thing.errors = {} -- A list of errors returned by isValid().
466 494
467Thing.__stuff = {}
468Thing.__values = {}
469
470Thing.isValid = function (self, parent) -- Check if this Thing is valid, return resulting error messages in errors. 495Thing.isValid = function (self, parent) -- Check if this Thing is valid, return resulting error messages in errors.
471 -- Anything that overrides this method, should call this super method first. 496 -- Anything that overrides this method, should call this super method first.
472 local name = self.names[1] 497 local name = self.names[1]
@@ -477,13 +502,13 @@ Thing.isValid = function (self, parent) -- Check if this Thing is valid, return
477 self.errors = {} 502 self.errors = {}
478 -- TODO - Naturally there should be formatting functions for stuffing Thing stuff into strings, and overridable output functions. 503 -- TODO - Naturally there should be formatting functions for stuffing Thing stuff into strings, and overridable output functions.
479 if 'nil' == t then 504 if 'nil' == t then
480 if self.required then table.insert(self.errors, mumThing.names[1] .. '.' .. name .. ' is required!') end 505 if self.required then table.insert(self.errors, mumThing.__self.names[1] .. '.' .. name .. ' is required!') end
481 else 506 else
482 if self.types[1] ~= t then table.insert(self.errors, mumThing.names[1] .. '.' .. name .. ' should be a ' .. self.types[1] .. ', but it is a ' .. t .. '!') 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 .. '!')
483 else 508 else
484 if 'number' == t then value = '' .. value end 509 if 'number' == t then value = '' .. value end
485 if ('number' == t) or ('string' == t) then 510 if ('number' == t) or ('string' == t) then
486 if 1 ~= string.find(value, '^' .. self.pattern .. '$') then table.insert(self.errors, mumThing.names[1] .. '.' .. name .. ' does not match pattern "' .. self.pattern .. '"!') end 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
487 end 512 end
488 end 513 end
489 end 514 end
@@ -507,14 +532,13 @@ Thing.__index = function (parent, key)
507 -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. 532 -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist.
508 -- TODO - Java skang called isValid() on get(). On the other hand, doesn't seem to call it on set(), but calls it on append(). 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().
509 -- Ah, it was doing isValid() on setStufflet(). 534 -- Ah, it was doing isValid() on setStufflet().
510 -- TODO - Call thingy.func() if it exists.
511 535
512 -- First see if this is a Thing. 536 -- First see if this is a Thing.
513 local mumThing = getmetatable(parent) 537 local mumThing = getmetatable(parent)
514 local thingy 538 local thingy
515 539
516 if mumThing and mumThing.self then 540 if mumThing and mumThing.__self then
517 thingy = mumThing.self.stuff[key] 541 thingy = mumThing.__self.stuff[key]
518 if thingy then 542 if thingy then
519 local name = thingy.names[1]; 543 local name = thingy.names[1];
520 return mumThing.__values[name] or thingy.default 544 return mumThing.__values[name] or thingy.default
@@ -529,20 +553,20 @@ Thing.__newindex = function (parent, key, value)
529 -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. 553 -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist.
530 local mumThing = getmetatable(parent) 554 local mumThing = getmetatable(parent)
531 555
532 if mumThing and mumThing.self then 556 if mumThing and mumThing.__self then
533 -- This is a proxy table, the values never exist in the real table. In theory. 557 -- This is a proxy table, the values never exist in the real table. In theory.
534 558
535 -- Find the Thing and get it done. 559 -- Find the Thing and get it done.
536 local thingy = mumThing.self.stuff[key] 560 local thingy = mumThing.__self.stuff[key]
537 561
538 if not thingy then 562 if not thingy then
539 -- Deal with setting a new Stuff[key]. 563 -- Deal with setting a new Stuff[key].
540 if mumThing.self.isStuff and (nil == mumThing.__values[key]) then 564 if mumThing.__self.isKeyed and (nil == mumThing.__values[key]) then
541 local newThing = copy(parent, key) 565 local newThing = copy(parent, key)
542 rawset(mumThing.__values, key, newThing) 566 rawset(mumThing.__values, key, newThing)
543 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, }
544 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.
545 mumThing.self.stuff[key] = thingy 569 mumThing.__self.stuff[key] = thingy
546 end 570 end
547 end 571 end
548 572
@@ -558,7 +582,7 @@ Thing.__newindex = function (parent, key, value)
558 if valueMeta then 582 if valueMeta then
559 -- TODO - This wont clear out any values in the old table that are not in the new table. Should it? 583 -- TODO - This wont clear out any values in the old table that are not in the new table. Should it?
560 for k, v in pairs(value) do 584 for k, v in pairs(value) do
561 local newK = valueMeta.self.stuff[k] 585 local newK = valueMeta.__self.stuff[k]
562 if newK then newK = newK.names[1] else newK = k end 586 if newK then newK = newK.names[1] else newK = k end
563 valueMeta.__values[newK] = v 587 valueMeta.__values[newK] = v
564 end 588 end
@@ -566,17 +590,13 @@ Thing.__newindex = function (parent, key, value)
566 end 590 end
567 end 591 end
568 if nil == valueMeta then mumThing.__values[name] = value end 592 if nil == valueMeta then mumThing.__values[name] = value end
569 if 'function' == type(value) then
570 thingy.func = value
571 else
572 -- NOTE - invalid values are still stored, this is by design. 593 -- NOTE - invalid values are still stored, this is by design.
573 if not thingy:isValid(parent) then 594 if not thingy:isValid(parent) then
574 for i, v in ipairs(thingy.errors) do 595 for i, v in ipairs(thingy.errors) do
575 print('ERROR - ' .. v) 596 print('ERROR - ' .. v)
576 end 597 end
577 end
578 -- TODO - Go through it's linked things and set them to.
579 end 598 end
599 -- TODO - Go through it's linked things and set them to.
580 -- Done, don't fall through to the rawset() 600 -- Done, don't fall through to the rawset()
581 return 601 return
582 end 602 end
@@ -652,26 +672,25 @@ thingasm = function (names, ...)
652 local mumThing = getmetatable(parent) 672 local mumThing = getmetatable(parent)
653 if nil == mumThing then 673 if nil == mumThing then
654 mumThing = {} 674 mumThing = {}
655 mumThing.self = {stuff={}}
656 setmetatable(parent, mumThing) 675 setmetatable(parent, mumThing)
657 end 676 end
658 -- Coz at module creation time, Thing is an empty table, and setmetatable(mumThing, {__index = Thing}) doesn't do the right thing. 677 -- Coz at module creation time, Thing is an empty table, and setmetatable(mumThing, {__index = Thing}) doesn't do the right thing.
659 if nil == mumThing.__values then 678 if nil == mumThing.__self then
660 -- Seems this does not deal with __index and __newindex, and also screws up __stuff somehow. 679 mumThing.__self = {stuff={}}
680 -- Seems this does not deal with __index and __newindex, and also screws up stuff somehow.
661-- setmetatable(mumThing, {__index = Thing}) 681-- setmetatable(mumThing, {__index = Thing})
662 mumThing.names = {parent._NAME or 'NoName'} 682 mumThing.__self.names = {parent._NAME or 'NoName'}
663 if parent._NAME then mumThing.types = {'Module'} end 683 if parent._NAME then mumThing.__self.types = {'Module'} end
664 mumThing.__values = {} 684 mumThing.__values = {}
665 mumThing.__index = Thing.__index 685 mumThing.__index = Thing.__index
666 mumThing.__newindex = Thing.__newindex 686 mumThing.__newindex = Thing.__newindex
667 end 687 end
668 688
669 local thingy = mumThing.self.stuff[name] 689 local thingy = mumThing.__self.stuff[name]
670 if not thingy then -- This is a new Thing. 690 if not thingy then -- This is a new Thing.
671 new = true 691 new = true
672 thingy = {} 692 thingy = {}
673 thingy.names = names 693 thingy.names = names
674 thingy.parent = parent
675 thingy.stuff = {} 694 thingy.stuff = {}
676 setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default. 695 setmetatable(thingy, {__index = Thing}) -- To pick up isValid, pattern, and the other stuff by default.
677 end 696 end
@@ -702,17 +721,15 @@ thingasm = function (names, ...)
702 if '' == types then types = typ end 721 if '' == types then types = typ end
703 thingy.types = csv2table(types) 722 thingy.types = csv2table(types)
704 723
705 if 'Stuff' == thingy.types[1] then 724 if 'Keyed' == thingy.types[1] then
706 thingy.types[1] = 'table' 725 thingy.types[1] = 'table'
707 thingy.isStuff = true 726 thingy.isKeyed = true
708 end 727 end
709 if 'table' == thingy.types[1] then 728 if 'table' == thingy.types[1] then
710 if '' == thingy.default then thingy.default = {} end 729 if '' == thingy.default then thingy.default = {} end
711 setmetatable(thingy.default, 730 setmetatable(thingy.default,
712 { 731 {
713 self = thingy, 732 __self = thingy,
714 parent = parent,
715 names = names,
716 __values = {}, 733 __values = {},
717 __index = Thing.__index, 734 __index = Thing.__index,
718 __newindex = Thing.__newindex, 735 __newindex = Thing.__newindex,
@@ -722,22 +739,22 @@ thingasm = function (names, ...)
722 739
723 -- 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.
724 for i, v in ipairs(oldNames) do 741 for i, v in ipairs(oldNames) do
725 mumThing.self.stuff[v] = nil 742 mumThing.__self.stuff[v] = nil
726 end 743 end
727 for i, v in ipairs(thingy.names) do 744 for i, v in ipairs(thingy.names) do
728 mumThing.self.stuff[v] = thingy 745 mumThing.__self.stuff[v] = thingy
729 end 746 end
730 747
731 -- This triggers the Thing.__newindex metamethod above. If nothing else, it triggers thingy.isValid() 748 -- This triggers the Thing.__newindex metamethod above. If nothing else, it triggers thingy.isValid()
732 if new and not mumThing.self.isStuff then parent[name] = thingy.default end 749 if new and not mumThing.__self.isKeyed then parent[name] = thingy.default end
733end 750end
734 751
735 752
736fixNames = function (module, name) 753fixNames = function (module, name)
737 local stuff = getmetatable(module) 754 local stuff = getmetatable(module)
738 if stuff then 755 if stuff then
739 stuff.names[1] = name 756 stuff.__self.names[1] = name
740 for k, v in pairs(stuff.self.stuff) do 757 for k, v in pairs(stuff.__self.stuff) do
741 if 'table' == v.types[1] then 758 if 'table' == v.types[1] then
742 local name = v.names[1] 759 local name = v.names[1]
743 print(name .. ' -> ' .. name) 760 print(name .. ' -> ' .. name)
@@ -750,7 +767,7 @@ end
750 767
751copy = function (parent, name) 768copy = function (parent, name)
752 local result = {} 769 local result = {}
753 local stuff = getmetatable(parent).self.stuff 770 local stuff = getmetatable(parent).__self.stuff
754 771
755 for k, v in pairs(stuff) do 772 for k, v in pairs(stuff) do
756 local temp = {} 773 local temp = {}
@@ -761,10 +778,9 @@ copy = function (parent, name)
761 temp.names = nil 778 temp.names = nil
762 temp.types = table.concat(temp.types, ',') 779 temp.types = table.concat(temp.types, ',')
763 temp.errors = nil 780 temp.errors = nil
764 temp.parent = nil
765 thingasm(result, temp) 781 thingasm(result, temp)
766 end 782 end
767 getmetatable(result).names = {name} 783 getmetatable(result).__self.names = {name}
768 784
769-- TODO - Should we copy values to? 785-- TODO - Should we copy values to?
770 786
@@ -773,7 +789,7 @@ end
773 789
774 790
775stuff = function (aThingy, aStuff) 791stuff = function (aThingy, aStuff)
776 return getmetatable(aThingy).self.stuff[aStuff] 792 return getmetatable(aThingy).__self.stuff[aStuff]
777end 793end
778 794
779 795
@@ -782,7 +798,7 @@ get = function (stuff, key, name)
782 if name then 798 if name then
783 local thingy = getmetatable(stuff) 799 local thingy = getmetatable(stuff)
784 if thingy then 800 if thingy then
785 local this = thingy.self.stuff[key] 801 local this = thingy.__self.stuff[key]
786 if this then result = this[name] end 802 if this then result = this[name] end
787 end 803 end
788 else 804 else
@@ -796,7 +812,7 @@ reset = function (stuff, key, name)
796 if name then 812 if name then
797 local thingy = getmetatable(stuff) 813 local thingy = getmetatable(stuff)
798 if thingy then 814 if thingy then
799 local this = thingy.self.stuff[key] 815 local this = thingy.__self.stuff[key]
800 if this then this[name] = nil end 816 if this then this[name] = nil end
801 end 817 end
802 else 818 else
@@ -809,7 +825,7 @@ set = function (stuff, key, name, value)
809 if 'nil' ~= type(value) then 825 if 'nil' ~= type(value) then
810 local thingy = getmetatable(stuff) 826 local thingy = getmetatable(stuff)
811 if thingy then 827 if thingy then
812 local this = thingy.self.stuff[key] 828 local this = thingy.__self.stuff[key]
813 if this then this[name] = value end 829 if this then this[name] = value end
814 end 830 end
815 else 831 else