fork download
  1. --[[
  2. lua readonly module
  3. 2013 (c) nyaocat
  4. this program is licensed under NYSL( http://w...content-available-to-author-only...s.net/nysl/ )
  5.  
  6. lua5.1 and lua5.2
  7. ]]
  8.  
  9. local newproxy = newproxy or require("newproxy") -- for Lua5.2
  10. local type, getmetatable, pairs, assert, error = type, getmetatable, pairs, assert, error
  11. local next, ipairs, pairs = next, ipairs, pairs
  12.  
  13. local modname = ...
  14.  
  15. local readonly_table_tag = newproxy() -- unique tag
  16. local readonly = {}
  17.  
  18. function readonly.next(t, index )
  19. local mt = getmetatable(t)
  20. if mt and mt.__type == readonly_table_tag then
  21. return getmetatable(t).__next(t, index )
  22. else
  23. return next(t, index)
  24. end
  25. end
  26.  
  27. function readonly.pairs(t)
  28. local mt = getmetatable(t)
  29. if mt and mt.__type == readonly_table_tag then
  30. return getmetatable(t).__next, t, nil
  31. else
  32. return pairs(t)
  33. end
  34. end
  35.  
  36. local function iter(tbl, i)
  37. i = i + 1
  38. local var = tbl[i]
  39. if var then
  40. return i, var
  41. end
  42. end
  43.  
  44. function readonly.ipairs(t)
  45. local mt = getmetatable(t)
  46. if mt and mt.__type == readonly_table_tag then
  47. return iter, t, 0
  48. else
  49. return ipairs(t)
  50. end
  51. end
  52.  
  53. function readonly.readonly(tbl)
  54. assert(type(tbl) == "table" or type(tbl) == "userdata",
  55. "Only user data or table is possible to read-only.")
  56. local ret = newproxy(true) -- ※
  57. local mt = getmetatable(ret)
  58. local tbl_mt = getmetatable(tbl)
  59. if tbl_mt then
  60. for k, v in pairs(tbl_mt) do
  61. if k:match("^__") then
  62. mt[k] = function(...) return v(...) end
  63. end
  64. end
  65. end
  66. mt.__len = function() return #tbl end
  67. mt.__index = function(ud, key) return tbl[key] end
  68. mt.__newindex = function()
  69. error("value of a read-only table can not be updated.")
  70. end
  71. mt.__type = readonly_table_tag
  72. mt.__next = function(t, index ) return next(tbl, index) end
  73. return ret
  74. end
  75.  
  76. function readonly.readonly2(tbl) -- member function can be updated.
  77. assert(type(tbl) == "table" or type(tbl) == "userdata",
  78. "Only user data or table is possible to read-only.")
  79. local ret = newproxy(true)
  80. local mt = getmetatable(ret)
  81. local tbl_mt = getmetatable(tbl)
  82. if tbl_mt then
  83. for k, v in pairs(tbl_mt) do
  84. if k:match("^__") then
  85. mt[k] = function(...) return v(...) end
  86. end
  87. end
  88. end
  89. mt.__len = function() return #tbl end
  90. mt.__index = function(ud, key)
  91. if (type(tbl[key]) == "function") then
  92. return function(self, ...) return tbl[key](tbl, ... ) end
  93. else
  94. return tbl[key]
  95. end
  96. end
  97. mt.__newindex = function()
  98. error("value of a read-only table can not be updated.")
  99. end
  100. mt.__type = readonly_table_tag
  101. mt.__next = function(t, index ) return next(tbl, index) end
  102. return ret
  103. end
  104.  
  105. -- tests
  106.  
  107. if not (modname) then
  108. local print, pcall = print, pcall
  109. local ret, err = pcall(function()
  110. arr = readonly.readonly {3, 9, 7, 4}
  111. assert(pcall(function() local val = arr[2] end) == true )
  112. assert(pcall(function() arr[1] = 98 end) == false)
  113.  
  114. t1 = readonly.readonly {
  115. val = 10,
  116. getVal = function(self) return self.val end,
  117. setVal = function(self, newval) self.val = newval end
  118. }
  119. assert(pcall(function() t1:setVal(20) end) == false )
  120. assert(pcall(function() t1:getVal() end) == true )
  121. assert(pcall(function() t1.val = 30 end) == false )
  122.  
  123. t2 = readonly.readonly2 {
  124. val = 10,
  125. getVal = function(self) return self.val end,
  126. setVal = function(self, newval) self.val = newval end
  127. }
  128. assert(pcall(function() t2:setVal(20) end) == true )
  129. assert(pcall(function() t2:getVal() end) == true )
  130. assert(pcall(function() t2.val = 30 end) == false )
  131. end)
  132. if ret then
  133. print("tests all ok")
  134. else
  135. print("test failed ...", err)
  136. end
  137. end
  138.  
  139. return readonly
Success #stdin #stdout 0.02s 2496KB
stdin
Standard input is empty
stdout
tests all ok