From 6d3a72ee1e838046d9177d589cc29f1e7cab7ebd Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Mon, 10 Mar 2014 12:30:23 -0400 Subject: [PATCH 01/13] README: fix typos Signed-off-by: Emilio G. Cota --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2d126c3..4baa1d5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Examples of use `inspect` has the following declaration: `str = inspect(value, )`. `value` can be any Lua value. `inspect` transforms simple types (like strings or numbers) into strings. Tables, on the other -hand, are rendered in a way a human can undersand. +hand, are rendered in a way a human can understand. "Array-like" tables are rendered horizontally: @@ -116,14 +116,14 @@ Sometimes it might be convenient to "filter out" some parts of the output. The ` Gotchas / Warnings ================== -This method is *not* appropiate for saving/restoring tables. It is ment to be used by the programmer mainly while debugging a program. +This method is *not* appropriate for saving/restoring tables. It is meant to be used by the programmer mainly while debugging a program. Installation ============ Just copy the inspect.lua file somewhere in your projects (maybe inside a /lib/ folder) and require it accordingly. -Remember to store the value returned by require somewhere! (I suggest a local variable named inspect, altough others might like table.inspect) +Remember to store the value returned by require somewhere! (I suggest a local variable named inspect, although others might like table.inspect) local inspect = require 'inspect' -- or -- From ddf5d00e1c025b21bc948c80c9654725082d5863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20Burgue=C3=B1o?= Date: Sat, 15 Mar 2014 17:49:59 -0700 Subject: [PATCH 02/13] Still mentions telescope --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4baa1d5..c0795a3 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ Also, make sure to read the license file; the text of that license file must app Specs ===== -This project uses [busted](http://olivinelabs.com/busted/) for its specs. If you want to run the specs, you will have to install telescope first. Then just execute the following from the root inspect folder: +This project uses [busted](http://olivinelabs.com/busted/) for its specs. If you want to run the specs, you will have to install busted first. Then just execute the following from the root inspect folder: busted From 7195bfa42339afd0a50e8549c477f69d754f27b2 Mon Sep 17 00:00:00 2001 From: kikito Date: Sun, 6 Jul 2014 17:24:37 +0200 Subject: [PATCH 03/13] remove filter option --- inspect.lua | 32 ++++++------------- spec/inspect_spec.lua | 73 ------------------------------------------- 2 files changed, 9 insertions(+), 96 deletions(-) diff --git a/inspect.lua b/inspect.lua index 638a673..fdaf012 100644 --- a/inspect.lua +++ b/inspect.lua @@ -136,15 +136,6 @@ local function countTableAppearances(t, tableAppearances) return tableAppearances end -local function parse_filter(filter) - if type(filter) == 'function' then return filter end - -- not a function, so it must be a table or table-like - filter = type(filter) == 'table' and filter or {filter} - local dictionary = {} - for _,v in pairs(filter) do dictionary[v] = true end - return function(x) return dictionary[x] end -end - local function makePath(path, key) local newPath, len = {}, #path for i=1, len do newPath[i] = path[i] end @@ -156,7 +147,6 @@ end function inspect.inspect(rootObject, options) options = options or {} local depth = options.depth or math.huge - local filter = parse_filter(options.filter or {}) local tableAppearances = countTableAppearances(rootObject) @@ -269,20 +259,16 @@ function inspect.inspect(rootObject, options) -- putvalue is forward-declared before putTable & putKey putValue = function(v, path) - if filter(v, path) then - puts('') + local tv = type(v) + + if tv == 'string' then + puts(smartQuote(escape(v))) + elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then + puts(tostring(v)) + elseif tv == 'table' then + putTable(v, path) else - local tv = type(v) - - if tv == 'string' then - puts(smartQuote(escape(v))) - elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then - puts(tostring(v)) - elseif tv == 'table' then - putTable(v, path) - else - puts('<',tv,' ',getId(v),'>') - end + puts('<',tv,' ',getId(v),'>') end end diff --git a/spec/inspect_spec.lua b/spec/inspect_spec.lua index 36daac8..229e4c7 100644 --- a/spec/inspect_spec.lua +++ b/spec/inspect_spec.lua @@ -180,79 +180,6 @@ describe( 'inspect', function() end) end) - describe('The filter option', function() - - it('filters hash values', function() - local a = {'this is a'} - local b = {x = 1, a = a} - - assert.equals(inspect(b, {filter = {a}}), [[{ - a = , - x = 1 -}]]) - end) - - it('filtereds hash keys', function() - local a = {'this is a'} - local b = {x = 1, [a] = 'a is used as a key here'} - - assert.equals(inspect(b, {filter = {a}}), [[{ - x = 1, - [] = "a is used as a key here" -}]]) - end) - - it('filtereds array values', function() - assert.equals(inspect({10,20,30}, {filter = {20}}), "{ 10, , 30 }") - end) - - it('filtereds metatables', function() - local a = {'this is a'} - local b = setmetatable({x = 1}, a) - assert.equals(inspect(b, {filter = {a}}), [[{ - x = 1, - = -}]]) - - end) - - it('filters by path', function() - local people = { tony = { age = 21 }, martha = { age = 34} } - local hideMarthaAge = function(_,path) - return table.concat(path, '.') == 'martha.age' - end - - assert.equals(inspect(people, {filter = hideMarthaAge}), [[{ - martha = { - age = - }, - tony = { - age = 21 - } -}]]) - end) - - it('does not increase the table ids', function() - local a = {'this is a'} - local b = {} - local c = {a, b, b} - assert.equals(inspect(c, {filter = {a}}), "{ , <1>{}, }") - end) - - it('can be a non table (gets interpreted as a table with one element)', function() - assert.equals(inspect({'foo', 'bar', 'baz'}, {filter = "bar"}), '{ "foo", , "baz" }') - end) - - it('can be a function which returns true for the elements that needs to be filtered', function() - local msg = inspect({1,2,3,4,5}, { filter = function(x) - return type(x) == 'number' and x % 2 == 0 - end }) - - assert.equals(msg, '{ 1, , 3, , 5 }') - end) - - end) - describe('metatables', function() it('includes the metatable as an extra hash attribute', function() From a1b30ad8a3ab363211b10d6561e1d9f208edbc42 Mon Sep 17 00:00:00 2001 From: kikito Date: Sun, 6 Jul 2014 18:44:31 +0200 Subject: [PATCH 04/13] process now handles most values. But what to do with keys? --- inspect.lua | 38 +++++++++++++++++++++++++++++--------- spec/inspect_spec.lua | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/inspect.lua b/inspect.lua index fdaf012..1476b19 100644 --- a/inspect.lua +++ b/inspect.lua @@ -143,12 +143,32 @@ local function makePath(path, key) return newPath end +local processRecursive = function(object, path, process) + local processed = process(object, path) + if type(processed) == 'table' then + local processed2 = {} + + for k,v in pairs(processed) do + processed2[k] = process(v, makePath(path, k), process) + end + + local mt = process(getmetatable(processed), makePath(path, '')) + setmetatable(processed2, mt) + processed = processed2 + end + return processed +end + ------------------------------------------------------------------- -function inspect.inspect(rootObject, options) +function inspect.inspect(root, options) options = options or {} local depth = options.depth or math.huge + local process = options.process + if process then + root = processRecursive(root, {}, process) + end - local tableAppearances = countTableAppearances(rootObject) + local tableAppearances = countTableAppearances(root) local buffer = {} local maxIds = setmetatable({}, maxIdsMetaTable) @@ -203,7 +223,7 @@ function inspect.inspect(rootObject, options) puts("]") end - local function putTable(t, path) + local function putTable(t) if alreadyVisited(t) then puts('
') elseif level >= depth then @@ -227,7 +247,7 @@ function inspect.inspect(rootObject, options) for i=1, length do needsComma = commaControl(needsComma) puts(' ') - putValue(t[i], makePath(path, i)) + putValue(t[i]) end for _,k in ipairs(dictKeys) do @@ -235,14 +255,14 @@ function inspect.inspect(rootObject, options) tabify() putKey(k) puts(' = ') - putValue(t[k], makePath(path, k)) + putValue(t[k]) end if mt then needsComma = commaControl(needsComma) tabify() puts(' = ') - putValue(mt, makePath(path, '')) + putValue(mt) end end) @@ -258,7 +278,7 @@ function inspect.inspect(rootObject, options) end -- putvalue is forward-declared before putTable & putKey - putValue = function(v, path) + putValue = function(v) local tv = type(v) if tv == 'string' then @@ -266,13 +286,13 @@ function inspect.inspect(rootObject, options) elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then puts(tostring(v)) elseif tv == 'table' then - putTable(v, path) + putTable(v) else puts('<',tv,' ',getId(v),'>') end end - putValue(rootObject, {}) + putValue(root, {}) return table.concat(buffer) end diff --git a/spec/inspect_spec.lua b/spec/inspect_spec.lua index 229e4c7..74ed8d4 100644 --- a/spec/inspect_spec.lua +++ b/spec/inspect_spec.lua @@ -180,6 +180,48 @@ describe( 'inspect', function() end) end) + describe('the process option', function() + + it('can be used to remove a particular element easily', function() + local names = {'Andrew', 'Peter', 'Ann' } + local removeAnn = function(item) if item ~= 'Ann' then return item end end + assert.equals(inspect(names, {process = removeAnn}), '{ "Andrew", "Peter" }') + end) + + it('can use the path', function() + local names = {'Andrew', 'Peter', 'Ann' } + local removeThird = function(item, path) if path[1] ~= 3 then return item end end + assert.equals(inspect(names, {process = removeThird}), '{ "Andrew", "Peter" }') + end) + + it('can replace values', function() + local names = {'Andrew', 'Peter', 'Ann' } + local filterAnn = function(item) return item == 'Ann' and '' or item end + assert.equals(inspect(names, {process = filterAnn}), '{ "Andrew", "Peter", "" }') + end) + + it('can nullify metatables', function() + local mt = {'world'} + local t = setmetatable({'hello'}, mt) + local removeMt = function(item) if item ~= mt then return item end end + assert.equals(inspect(t, {process = removeMt}), '{ "hello" }') + end) + + it('can nullify metatables via their paths', function() + local mt = {'world'} + local t = setmetatable({'hello'}, mt) + local removeMt = function(item, path) if path[#path] ~= '' then return item end end + assert.equals(inspect(t, {process = removeMt}), '{ "hello" }') + end) + + it('can nullify the root object', function() + local names = {'Andrew', 'Peter', 'Ann' } + local removeNames = function(item) if item ~= names then return item end end + assert.equals(inspect(names, {process = removeNames}), 'nil') + end) + + end) + describe('metatables', function() it('includes the metatable as an extra hash attribute', function() From e74c899b7db6dc14dca31643e0808be0d122d343 Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 16 Aug 2014 16:35:24 +0200 Subject: [PATCH 05/13] refactor processRecursive --- inspect.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/inspect.lua b/inspect.lua index 1476b19..ea874b2 100644 --- a/inspect.lua +++ b/inspect.lua @@ -143,18 +143,18 @@ local function makePath(path, key) return newPath end -local processRecursive = function(object, path, process) +local function processRecursive(object, path, process) local processed = process(object, path) if type(processed) == 'table' then - local processed2 = {} + local processedCopy = {} for k,v in pairs(processed) do - processed2[k] = process(v, makePath(path, k), process) + processedCopy[k] = processRecursive(v, makePath(path, k), process) end - local mt = process(getmetatable(processed), makePath(path, '')) - setmetatable(processed2, mt) - processed = processed2 + local mt = processRecursive(getmetatable(processed), makePath(path, ''), process) + setmetatable(processedCopy, mt) + processed = processedCopy end return processed end From 5ecaca920579f7ec5b62832d62d3fede8d83d449 Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 16 Aug 2014 16:50:29 +0200 Subject: [PATCH 06/13] better test names --- spec/inspect_spec.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/inspect_spec.lua b/spec/inspect_spec.lua index 74ed8d4..c16871c 100644 --- a/spec/inspect_spec.lua +++ b/spec/inspect_spec.lua @@ -182,39 +182,39 @@ describe( 'inspect', function() describe('the process option', function() - it('can be used to remove a particular element easily', function() + it('removes one element', function() local names = {'Andrew', 'Peter', 'Ann' } local removeAnn = function(item) if item ~= 'Ann' then return item end end assert.equals(inspect(names, {process = removeAnn}), '{ "Andrew", "Peter" }') end) - it('can use the path', function() + it('uses the path', function() local names = {'Andrew', 'Peter', 'Ann' } local removeThird = function(item, path) if path[1] ~= 3 then return item end end assert.equals(inspect(names, {process = removeThird}), '{ "Andrew", "Peter" }') end) - it('can replace values', function() + it('replaces items', function() local names = {'Andrew', 'Peter', 'Ann' } local filterAnn = function(item) return item == 'Ann' and '' or item end assert.equals(inspect(names, {process = filterAnn}), '{ "Andrew", "Peter", "" }') end) - it('can nullify metatables', function() + it('nullifies metatables', function() local mt = {'world'} local t = setmetatable({'hello'}, mt) local removeMt = function(item) if item ~= mt then return item end end assert.equals(inspect(t, {process = removeMt}), '{ "hello" }') end) - it('can nullify metatables via their paths', function() + it('nullifies metatables using their paths', function() local mt = {'world'} local t = setmetatable({'hello'}, mt) local removeMt = function(item, path) if path[#path] ~= '' then return item end end assert.equals(inspect(t, {process = removeMt}), '{ "hello" }') end) - it('can nullify the root object', function() + it('nullifies the root object', function() local names = {'Andrew', 'Peter', 'Ann' } local removeNames = function(item) if item ~= names then return item end end assert.equals(inspect(names, {process = removeNames}), 'nil') From 2961caa14aded6358ae96f95646d60fbd229ad1b Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 16 Aug 2014 19:44:35 +0200 Subject: [PATCH 07/13] process option handles keys as well as items --- inspect.lua | 27 +++++++++++++++++++-------- spec/inspect_spec.lua | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/inspect.lua b/inspect.lua index ea874b2..ea2df3d 100644 --- a/inspect.lua +++ b/inspect.lua @@ -136,23 +136,34 @@ local function countTableAppearances(t, tableAppearances) return tableAppearances end +local copySequence = function(s) + local copy, len = {}, #s + for i=1, len do copy[i] = copy[i] end + return copy, len +end + local function makePath(path, key) - local newPath, len = {}, #path - for i=1, len do newPath[i] = path[i] end - newPath[len+1] = key + local newPath, len = copySequence(path) + newPath[len + 1] = key return newPath end -local function processRecursive(object, path, process) - local processed = process(object, path) +local function processRecursive(process, item, path) + if item == nil then return nil end + + local processed = process(item, path) if type(processed) == 'table' then local processedCopy = {} + local processedKey for k,v in pairs(processed) do - processedCopy[k] = processRecursive(v, makePath(path, k), process) + processedKey = processRecursive(process, k, makePath(path, '')) + if processedKey ~= nil then + processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey)) + end end - local mt = processRecursive(getmetatable(processed), makePath(path, ''), process) + local mt = processRecursive(process, getmetatable(processed), makePath(path, '')) setmetatable(processedCopy, mt) processed = processedCopy end @@ -165,7 +176,7 @@ function inspect.inspect(root, options) local depth = options.depth or math.huge local process = options.process if process then - root = processRecursive(root, {}, process) + root = processRecursive(process, root, {}) end local tableAppearances = countTableAppearances(root) diff --git a/spec/inspect_spec.lua b/spec/inspect_spec.lua index c16871c..f387592 100644 --- a/spec/inspect_spec.lua +++ b/spec/inspect_spec.lua @@ -220,6 +220,27 @@ describe( 'inspect', function() assert.equals(inspect(names, {process = removeNames}), 'nil') end) + it('changes keys', function() + local dict = {a = 1} + local changeKey = function(item, path) return item == 'a' and 'x' or item end + assert.equals(inspect(dict, {process = changeKey}), '{\n x = 1\n}') + end) + + it('nullifies keys', function() + local dict = {a = 1, b = 2} + local removeA = function(item, path) return item ~= 'a' and item or nil end + assert.equals(inspect(dict, {process = removeA}), '{\n b = 2\n}') + end) + + it('marks key paths with ', function() + local names = {a = 1} + local paths = {} + local addPath = function(item, path) paths[#paths + 1] = path; return item end + + inspect(names, {process = addPath}) + + assert.same(paths, { {}, {''}, {'a'} }) + end) end) describe('metatables', function() From d07147ed547cfd3cf07576306f732c94381c469f Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 21 Aug 2014 20:15:25 +0200 Subject: [PATCH 08/13] transform back into metatable-based object-orientation --- inspect.lua | 232 +++++++++++++++++++++++++++------------------------- 1 file changed, 119 insertions(+), 113 deletions(-) diff --git a/inspect.lua b/inspect.lua index ea2df3d..4fee019 100644 --- a/inspect.lua +++ b/inspect.lua @@ -170,142 +170,148 @@ local function processRecursive(process, item, path) return processed end -------------------------------------------------------------------- -function inspect.inspect(root, options) - options = options or {} - local depth = options.depth or math.huge - local process = options.process - if process then - root = processRecursive(process, root, {}) - end - local tableAppearances = countTableAppearances(root) +------------------------------------------------------------------- - local buffer = {} - local maxIds = setmetatable({}, maxIdsMetaTable) - local ids = setmetatable({}, idsMetaTable) - local level = 0 - local blen = 0 -- buffer length +local Inspector = {} +local Inspector_mt = {__index = Inspector} - local function puts(...) - local args = {...} - for i=1, #args do - blen = blen + 1 - buffer[blen] = tostring(args[i]) - end +function Inspector:puts(...) + local args = {...} + local buffer = self.buffer + local len = #buffer + for i=1, #args do + len = len + 1 + buffer[len] = tostring(args[i]) end +end - local function down(f) - level = level + 1 - f() - level = level - 1 - end +function Inspector:down(f) + self.level = self.level + 1 + f() + self.level = self.level - 1 +end - local function tabify() - puts("\n", string.rep(" ", level)) - end +function Inspector:tabify() + self:puts("\n", string.rep(" ", self.level)) +end - local function commaControl(needsComma) - if needsComma then puts(',') end - return true - end +function Inspector:commaControl(needsComma) + if needsComma then self:puts(',') end + return true +end - local function alreadyVisited(v) - return ids[type(v)][v] ~= nil - end +function Inspector:alreadyVisited(v) + return self.ids[type(v)][v] ~= nil +end - local function getId(v) - local tv = type(v) - local id = ids[tv][v] - if not id then - id = maxIds[tv] + 1 - maxIds[tv] = id - ids[tv][v] = id - end - return id +function Inspector:getId(v) + local tv = type(v) + local id = self.ids[tv][v] + if not id then + id = self.maxIds[tv] + 1 + self.maxIds[tv] = id + self.ids[tv][v] = id end + return id +end - local putValue -- forward declaration that needs to go before putTable & putKey +function Inspector:putKey(k) + if isIdentifier(k) then return self:puts(k) end + self:puts("[") + self:putValue(k) + self:puts("]") +end - local function putKey(k) - if isIdentifier(k) then return puts(k) end - puts( "[" ) - putValue(k, {}) - puts("]") - end +function Inspector:putTable(t) + if self:alreadyVisited(t) then + self:puts('
') + elseif self.level >= self.depth then + self:puts('{...}') + else + if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end + + local dictKeys = getDictionaryKeys(t) + local length = #t + local mt = getmetatable(t) + local to_string_result = getToStringResultSafely(t, mt) + + self:puts('{') + self:down(function() + if to_string_result then + self:puts(' -- ', escape(to_string_result)) + if length >= 1 then self:tabify() end + end - local function putTable(t) - if alreadyVisited(t) then - puts('
') - elseif level >= depth then - puts('{...}') - else - if tableAppearances[t] > 1 then puts('<', getId(t), '>') end - - local dictKeys = getDictionaryKeys(t) - local length = #t - local mt = getmetatable(t) - local to_string_result = getToStringResultSafely(t, mt) - - puts('{') - down(function() - if to_string_result then - puts(' -- ', escape(to_string_result)) - if length >= 1 then tabify() end -- tabify the array values - end - - local needsComma = false - for i=1, length do - needsComma = commaControl(needsComma) - puts(' ') - putValue(t[i]) - end - - for _,k in ipairs(dictKeys) do - needsComma = commaControl(needsComma) - tabify() - putKey(k) - puts(' = ') - putValue(t[k]) - end - - if mt then - needsComma = commaControl(needsComma) - tabify() - puts(' = ') - putValue(mt) - end - end) - - if #dictKeys > 0 or mt then -- dictionary table. Justify closing } - tabify() - elseif length > 0 then -- array tables have one extra space before closing } - puts(' ') + local needsComma = false + for i=1, length do + needsComma = self:commaControl(needsComma) + self:puts(' ') + self:putValue(t[i]) end - puts('}') + for _,k in ipairs(dictKeys) do + needsComma = self:commaControl(needsComma) + self:tabify() + self:putKey(k) + self:puts(' = ') + self:putValue(t[k]) + end + + if mt then + needsComma = self:commaControl(needsComma) + self:tabify() + self:puts(' = ') + self:putValue(mt) + end + end) + + if #dictKeys > 0 or mt then -- dictionary table. Justify closing } + self:tabify() + elseif length > 0 then -- array tables have one extra space before closing } + self:puts(' ') end + self:puts('}') end +end - -- putvalue is forward-declared before putTable & putKey - putValue = function(v) - local tv = type(v) +function Inspector:putValue(v) + local tv = type(v) + + if tv == 'string' then + self:puts(smartQuote(escape(v))) + elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then + self:puts(tostring(v)) + elseif tv == 'table' then + self:putTable(v) + else + self:puts('<',tv,' ',self:getId(v),'>') + end +end - if tv == 'string' then - puts(smartQuote(escape(v))) - elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then - puts(tostring(v)) - elseif tv == 'table' then - putTable(v) - else - puts('<',tv,' ',getId(v),'>') - end +------------------------------------------------------------------- + +function inspect.inspect(root, options) + options = options or {} + local depth = options.depth or math.huge + local process = options.process + if process then + root = processRecursive(process, root, {}) end - putValue(root, {}) + local inspector = setmetatable({ + depth = depth, + buffer = {}, + level = 0, + ids = setmetatable({}, idsMetaTable), + maxIds = setmetatable({}, maxIdsMetaTable), + tableAppearances = countTableAppearances(root) + }, Inspector_mt) + + inspector:putValue(root) - return table.concat(buffer) + return table.concat(inspector.buffer) end setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) From 9ce951f6afb27761ecc1769e6f6dca78f2a9c68c Mon Sep 17 00:00:00 2001 From: kikito Date: Sun, 7 Sep 2014 17:54:37 +0200 Subject: [PATCH 09/13] updated readme with wishes --- README.md | 216 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 165 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index c0795a3..e7e933f 100644 --- a/README.md +++ b/README.md @@ -10,90 +10,204 @@ The objective here is human understanding (i.e. for debugging), not serializatio Examples of use =============== -`inspect` has the following declaration: `str = inspect(value, )`. +`inspect` has the following declaration: `local str = inspect(value, )`. -`value` can be any Lua value. `inspect` transforms simple types (like strings or numbers) into strings. Tables, on the other -hand, are rendered in a way a human can understand. +`value` can be any Lua value. + +`inspect` transforms simple types (like strings or numbers) into strings. + +```lua +assert(inspect(1) == "1") +assert(inspect("Hello") == '"Hello"') +``` + +Tables, on the other hand, are rendered in a way a human can read easily. "Array-like" tables are rendered horizontally: - inspect({1,2,3,4}) == "{ 1, 2, 3, 4 }" +```lua +assert(inspect({1,2,3,4}) == "{ 1, 2, 3, 4 }") +``` -"dictionary-like" tables are rendered with one element per line: +"Dictionary-like" tables are rendered with one element per line: - inspect({a=1,b=2}) == [[{ - a = 1, - b = 2 - }]] +```lua +assert(inspect({a=1,b=2}) == [[{ + a = 1, + b = 2 +}]]) +``` The keys will be sorted alphanumerically when possible. "Hybrid" tables will have the array part on the first line, and the dictionary part just below them: - inspect({1,2,3,b=2,a=1}) == [[{ 1, 2, 3, - a = 1, - b = 2 - }]] +```lua +assert(inspect({1,2,3,b=2,a=1}) == [[{ 1, 2, 3, + a = 1, + b = 2 +}]]) +``` -Tables can be nested, and will be indented with two spaces per level. +Subtables are indented with two spaces per level. - inspect({a={b=2}}) == [[{ - a = { - b = 2 - } - }]] +```lua +assert(inspect({a={b=2}}) == [[{ + a = { + b = 2 + } +}]]) +``` Functions, userdata and any other custom types from Luajit are simply as ``, ``, etc.: - inspect({ f = print, ud = some_user_data, thread = a_thread} ) == [[{ - f = , - u = , - thread = - }]]) +```lua +assert(inspect({ f = print, ud = some_user_data, thread = a_thread} ) == [[{ + f = , + u = , + thread = +}]]) +``` If the table has a metatable, inspect will include it at the end, in a special field called ``: - inspect(setmetatable({a=1}, {b=2}) == [[{ - a = 1 - = { - b = 2 - } - }]]) +```lua +assert(inspect(setmetatable({a=1}, {b=2}) == [[{ + a = 1 + = { + b = 2 + } +}]])) +``` `inspect` can handle tables with loops inside them. It will print `` right before the table is printed out the first time, and replace the whole table with `
` from then on, preventing infinite loops. - a = {1, 2} - b = {3, 4, a} - a[3] = b -- a references b, and b references a - inspect(a) = "<1>{ 1, 2, { 3, 4,
} }" +```lua +local a = {1, 2} +local b = {3, 4, a} +a[3] = b -- a references b, and b references a +assert(inspect(a) == "<1>{ 1, 2, { 3, 4,
} }") +``` Notice that since both `a` appears more than once in the expression, it is prefixed by `<1>` and replaced by `
` every time it appears later on. -### options.depth +### options -`inspect`'s second parameter allows controlling the maximum depth that will be printed out. When the max depth is reached, it'll just return `{...}`: +`inspect` has a second parameter, called `options`. It is not mandatory, but when it is provided, it must be a table. - local t5 = {a = {b = {c = {d = {e = 5}}}}} +#### options.depth - inspect(t5, {depth = 4}) == [[{ - a = { - b = { - c = { - d = {...} - } - } - } - }]] +`options.depth` sets the maximum depth that will be printed out. +When the max depth is reached, `inspect` will stop parsing tables and just return `{...}`: + +```lua + +local t5 = {a = {b = {c = {d = {e = 5}}}}} - inspect(t5, {depth = 2}) == [[{ - a = { - b = {...} +assert(inspect(t5, {depth = 4}) == [[{ + a = { + b = { + c = { + d = {...} } - }]]) + } + } +}]]) + +assert(inspect(t5, {depth = 2}) == [[{ + a = { + b = {...} + } +}]]) + +``` `options.depth` defaults to infinite (`math.huge`). -### options.filter +### options.process + +`options.process` is a function which allow altering the passed object before transforming it into a string. +A typical way to use it would be to remove certain values so that they don't appear at all. + +`options.process` has the following signature: + +``` lua +local processed_item = function(item, path) +``` + +* `item` is either a key or a value on the table, or any of its subtables +* `path` is an array-like table built with all the keys that have been used to reach `item`, from the root. + * For values, it is just a regular list of keys. For example, to reach the 1 in `{a = {b = 1}}`, the `path` + will be `{'a', 'b'}` + * For keys, a special value called `` is inserted. For example, to reach the `c` in `{a = {b = {c = 1}}}`, + the path will be `{'a', 'b', 'c', '' }` + * For metatables, a special value called `` is inserted. For `{a = {b = 1}}}`, the path + `{'a', 'b', ''}` means "the metatable of the table `{b = 1}`". +* `processed_item` is the value returned by `options.process`. If it is equal to `item`, then the inspected + table will look unchanged. If it is different, then the table will look different; most notably, if it's `nil`, + the item will dissapear on the inspected table. + +#### Examples + +Remove a particular metatable from the result: + +``` lua +local t = {1,2,3} +local mt = {b = 2} +setmetatable(t, mt) + +local remove_mt = function(item) + if item ~= mt then return item end +end + +-- mt does not appear +assert(inspect(t, {process = remove_mt}) == "{ 1, 2, 3 }") +``` + +The previous exaple only works for a particular metatable. If you want to make *all* metatables, you can use `path`: + +``` lua +local t, mt = ... -- (defined as before) + +local remove_all_metatables = function(item, path) + if path[#path] ~= '' then return item end +end + +-- Removes all metatables +assert(inspect(t, {process = remove_mt}) == "{ 1, 2, 3 }") +``` + +Filter a value: + +```lua +local anonymize_password = function(item, path) + if path[#path] == 'password' then return "XXXX" end + return item +end + +local info = {user = 'peter', password = 'secret'} + +assert(inspect(info, {process = anonymize_password}) == [[{ + password = "XXXX", + user = "peter" +}]]) +``` + + + + + + + + + + + + + + + + Sometimes it might be convenient to "filter out" some parts of the output. The `options.filter` option can do that. From b9b780caad33ec642b713c4facdeccd11a33bc8a Mon Sep 17 00:00:00 2001 From: kikito Date: Sun, 7 Sep 2014 22:32:31 +0200 Subject: [PATCH 10/13] unindent in specs --- spec/inspect_spec.lua | 218 +++++++++++++++++++++++------------------- spec/unindent.lua | 39 ++++++++ 2 files changed, 161 insertions(+), 96 deletions(-) create mode 100644 spec/unindent.lua diff --git a/spec/inspect_spec.lua b/spec/inspect_spec.lua index f387592..dc57cc3 100644 --- a/spec/inspect_spec.lua +++ b/spec/inspect_spec.lua @@ -1,5 +1,6 @@ -local inspect = require 'inspect' -local is_luajit, ffi = pcall(require, 'ffi') +local inspect = require 'inspect' +local unindent = require 'spec.unindent' +local is_luajit, ffi = pcall(require, 'ffi') describe( 'inspect', function() @@ -82,41 +83,45 @@ describe( 'inspect', function() it('sorts keys in dictionary tables', function() local t = { 1,2,3, [print] = 1, ["buy more"] = 1, a = 1, + [coroutine.create(function() end)] = 1, [14] = 1, [{c=2}] = 1, [true]= 1 } - local s = [[{ 1, 2, 3, - [14] = 1, - [true] = 1, - a = 1, - ["buy more"] = 1, - [{ - c = 2 - }] = 1, - [] = 1]] - if is_luajit then - t[ffi.new("int", 1)] = 1 - s = s .. ",\n [] = 1" - end - assert.equals(inspect(t), s .. "\n}") + assert.equals(inspect(t), unindent([[ + { 1, 2, 3, + [14] = 1, + [true] = 1, + a = 1, + ["buy more"] = 1, + [{ + c = 2 + }] = 1, + [] = 1, + [] = 1 + } + ]])) end) it('works with nested dictionary tables', function() - assert.equals(inspect( {d=3, b={c=2}, a=1} ), [[{ - a = 1, - b = { - c = 2 - }, - d = 3 -}]]) + assert.equals(inspect( {d=3, b={c=2}, a=1} ), unindent([[{ + a = 1, + b = { + c = 2 + }, + d = 3 + }]])) end) it('works with hybrid tables', function() - assert.equals(inspect({ 'a', {b = 1}, 2, c = 3, ['ahoy you'] = 4 }), [[{ "a", { - b = 1 - }, 2, - ["ahoy you"] = 4, - c = 3 -}]]) + assert.equals( + inspect({ 'a', {b = 1}, 2, c = 3, ['ahoy you'] = 4 }), + unindent([[ + { "a", { + b = 1 + }, 2, + ["ahoy you"] = 4, + c = 3 + } + ]])) end) it('displays
instead of repeating an already existing table', function() @@ -133,50 +138,62 @@ describe( 'inspect', function() local keys = { [level5] = true } it('has infinite depth by default', function() - assert.equals(inspect(level5), [[{ 1, 2, 3, - a = { - b = { - c = { - d = { - e = 5 - } - } - } - } -}]]) + assert.equals(inspect(level5), unindent([[ + { 1, 2, 3, + a = { + b = { + c = { + d = { + e = 5 + } + } + } + } + } + ]])) end) it('is modifiable by the user', function() - assert.equals(inspect(level5, {depth = 2}), [[{ 1, 2, 3, - a = { - b = {...} - } -}]]) - assert.equals(inspect(level5, {depth = 1}), [[{ 1, 2, 3, - a = {...} -}]]) - assert.equals(inspect(level5, {depth = 0}), "{...}") - assert.equals(inspect(level5, {depth = 4}), [[{ 1, 2, 3, - a = { - b = { - c = { - d = {...} - } - } - } -}]]) + assert.equals(inspect(level5, {depth = 2}), unindent([[ + { 1, 2, 3, + a = { + b = {...} + } + } + ]])) + + assert.equals(inspect(level5, {depth = 1}), unindent([[ + { 1, 2, 3, + a = {...} + } + ]])) + + assert.equals(inspect(level5, {depth = 4}), unindent([[ + { 1, 2, 3, + a = { + b = { + c = { + d = {...} + } + } + } + } + ]])) + assert.equals(inspect(level5, {depth = 0}), "{...}") end) it('respects depth on keys', function() - assert.equals(inspect(keys, {depth = 4}), [[{ - [{ 1, 2, 3, - a = { - b = { - c = {...} - } - } - }] = true -}]]) + assert.equals(inspect(keys, {depth = 4}), unindent([[ + { + [{ 1, 2, 3, + a = { + b = { + c = {...} + } + } + }] = true + } + ]])) end) end) @@ -248,58 +265,67 @@ describe( 'inspect', function() it('includes the metatable as an extra hash attribute', function() local foo = { foo = 1, __mode = 'v' } local bar = setmetatable({a = 1}, foo) - assert.equals(inspect(bar), [[{ - a = 1, - = { - __mode = "v", - foo = 1 - } -}]]) + assert.equals(inspect(bar), unindent([[ + { + a = 1, + = { + __mode = "v", + foo = 1 + } + } + ]])) end) it('includes the __tostring metamethod if it exists', function() local foo = { foo = 1, __tostring = function() return 'hello\nworld' end } local bar = setmetatable({a = 1}, foo) - assert.equals(inspect(bar), [[{ -- hello\nworld - a = 1, - = { - __tostring = , - foo = 1 - } -}]]) + assert.equals(inspect(bar), unindent([[ + { -- hello\nworld + a = 1, + = { + __tostring = , + foo = 1 + } + } + ]])) end) it('includes an error string if __tostring metamethod throws an error', function() local foo = { foo = 1, __tostring = function() error('hello', 0) end } local bar = setmetatable({a = 1}, foo) - assert.equals(inspect(bar), [[{ -- error: hello - a = 1, - = { - __tostring = , - foo = 1 - } -}]]) + assert.equals(inspect(bar), unindent([[ + { -- error: hello + a = 1, + = { + __tostring = , + foo = 1 + } + } + ]])) end) describe('When a table is its own metatable', function() it('accepts a table that is its own metatable without stack overflowing', function() local x = {} setmetatable(x,x) - assert.equals(inspect(x), [[<1>{ - =
-}]]) + assert.equals(inspect(x), unindent([[ + <1>{ + =
+ } + ]])) end) it('can invoke the __tostring method without stack overflowing', function() local t = {} t.__index = t setmetatable(t,t) - assert.equals(inspect(t), [[<1>{ - __index =
, - =
-}]]) + assert.equals(inspect(t), unindent([[ + <1>{ + __index =
, + =
+ } + ]])) end) - end) end) end) diff --git a/spec/unindent.lua b/spec/unindent.lua new file mode 100644 index 0000000..02324a1 --- /dev/null +++ b/spec/unindent.lua @@ -0,0 +1,39 @@ +-- Unindenting transforms a string like this: +-- [[ +-- { +-- foo = 1, +-- bar = 2 +-- } +-- ]] +-- +-- Into the same one without indentation, nor start/end newlines +-- +-- [[{ +-- foo = 1, +-- bar = 2 +-- }]] +-- +-- This makes the strings look and read better in the tests +-- + +local getIndentPreffix = function(str) + local level = math.huge + local minPreffix = "" + local len + for preffix in str:gmatch("\n( +)") do + len = #preffix + if len < level then + level = len + minPreffix = preffix + end + end + return minPreffix +end + +local unindent = function(str) + str = str:gsub(" +$", ""):gsub("^ +", "") -- remove spaces at start and end + local preffix = getIndentPreffix(str) + return (str:gsub("\n" .. preffix, "\n"):gsub("\n$", "")) +end + +return unindent From 1fb5373a45eb870005bb431a3aa7ebb1043035aa Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 12 Sep 2014 15:36:38 +0200 Subject: [PATCH 11/13] Use '' and ' on keys correctly --- inspect.lua | 11 +++++++---- spec/inspect_spec.lua | 30 +++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/inspect.lua b/inspect.lua index 4fee019..9bdaf99 100644 --- a/inspect.lua +++ b/inspect.lua @@ -138,13 +138,16 @@ end local copySequence = function(s) local copy, len = {}, #s - for i=1, len do copy[i] = copy[i] end + for i=1, len do copy[i] = s[i] end return copy, len end -local function makePath(path, key) +local function makePath(path, ...) + local keys = {...} local newPath, len = copySequence(path) - newPath[len + 1] = key + for i=1, #keys do + newPath[len + i] = keys[i] + end return newPath end @@ -157,7 +160,7 @@ local function processRecursive(process, item, path) local processedKey for k,v in pairs(processed) do - processedKey = processRecursive(process, k, makePath(path, '')) + processedKey = processRecursive(process, k, makePath(path, k, '')) if processedKey ~= nil then processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey)) end diff --git a/spec/inspect_spec.lua b/spec/inspect_spec.lua index dc57cc3..2e3511b 100644 --- a/spec/inspect_spec.lua +++ b/spec/inspect_spec.lua @@ -249,14 +249,30 @@ describe( 'inspect', function() assert.equals(inspect(dict, {process = removeA}), '{\n b = 2\n}') end) - it('marks key paths with ', function() - local names = {a = 1} - local paths = {} - local addPath = function(item, path) paths[#paths + 1] = path; return item end + it('marks key paths with and metatables with ', function() + local t = { [{a=1}] = setmetatable({b=2}, {c=3}) } + + local items = {} + local addItem = function(item, path) + items[#items + 1] = {item = item, path = path} + return item + end + + inspect(t, {process = addItem}) + + assert.same(items, { + {item = t, path = {}}, + {item = {a=1}, path = {{a=1}, ''}}, + {item = 'a', path = {{a=1}, '', 'a', ''}}, + {item = 1, path = {{a=1}, '', 'a'}}, + {item = setmetatable({b=2}, {c=3}), path = {{a=1}}}, + {item = 'b', path = {{a=1}, 'b', ''}}, + {item = 2, path = {{a=1}, 'b'}}, + {item = {c=3}, path = {{a=1}, ''}}, + {item = 'c', path = {{a=1}, '', 'c', ''}}, + {item = 3, path = {{a=1}, '', 'c'}} + }) - inspect(names, {process = addPath}) - - assert.same(paths, { {}, {''}, {'a'} }) end) end) From 9054ee1051f99b52b31a184848992fd368bde887 Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 13 Sep 2014 12:12:41 +0200 Subject: [PATCH 12/13] , -> inspect.KEY, inspect.METATABLE --- README.md | 8 ++++---- inspect.lua | 11 ++++++++--- spec/inspect_spec.lua | 23 ++++++++++++++--------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e7e933f..d79101d 100644 --- a/README.md +++ b/README.md @@ -139,10 +139,10 @@ local processed_item = function(item, path) * `path` is an array-like table built with all the keys that have been used to reach `item`, from the root. * For values, it is just a regular list of keys. For example, to reach the 1 in `{a = {b = 1}}`, the `path` will be `{'a', 'b'}` - * For keys, a special value called `` is inserted. For example, to reach the `c` in `{a = {b = {c = 1}}}`, - the path will be `{'a', 'b', 'c', '' }` - * For metatables, a special value called `` is inserted. For `{a = {b = 1}}}`, the path - `{'a', 'b', ''}` means "the metatable of the table `{b = 1}`". + * For keys, the special value `inspect.KEY` is inserted. For example, to reach the `c` in `{a = {b = {c = 1}}}`, + the path will be `{'a', 'b', 'c', inspect.KEY }` + * For metatables, the special value `inspect.METATABLE` is inserted. For `{a = {b = 1}}}`, the path + `{'a', {b = 1}, inspect.METATABLE}` means "the metatable of the table `{b = 1}`". * `processed_item` is the value returned by `options.process`. If it is equal to `item`, then the inspected table will look unchanged. If it is different, then the table will look different; most notably, if it's `nil`, the item will dissapear on the inspected table. diff --git a/inspect.lua b/inspect.lua index 9bdaf99..d548be1 100644 --- a/inspect.lua +++ b/inspect.lua @@ -28,6 +28,9 @@ local inspect ={ ]] } +inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) +inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) + -- Apostrophizes the string if it has quotes, but not aphostrophes -- Otherwise, it returns a regular quoted string local function smartQuote(str) @@ -160,13 +163,13 @@ local function processRecursive(process, item, path) local processedKey for k,v in pairs(processed) do - processedKey = processRecursive(process, k, makePath(path, k, '')) + processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY)) if processedKey ~= nil then processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey)) end end - local mt = processRecursive(process, getmetatable(processed), makePath(path, '')) + local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE)) setmetatable(processedCopy, mt) processed = processedCopy end @@ -227,7 +230,9 @@ function Inspector:putKey(k) end function Inspector:putTable(t) - if self:alreadyVisited(t) then + if t == inspect.KEY or t == inspect.METATABLE then + self:puts(tostring(t)) + elseif self:alreadyVisited(t) then self:puts('
') elseif self.level >= self.depth then self:puts('{...}') diff --git a/spec/inspect_spec.lua b/spec/inspect_spec.lua index 2e3511b..958aa81 100644 --- a/spec/inspect_spec.lua +++ b/spec/inspect_spec.lua @@ -227,7 +227,7 @@ describe( 'inspect', function() it('nullifies metatables using their paths', function() local mt = {'world'} local t = setmetatable({'hello'}, mt) - local removeMt = function(item, path) if path[#path] ~= '' then return item end end + local removeMt = function(item, path) if path[#path] ~= inspect.METATABLE then return item end end assert.equals(inspect(t, {process = removeMt}), '{ "hello" }') end) @@ -249,7 +249,12 @@ describe( 'inspect', function() assert.equals(inspect(dict, {process = removeA}), '{\n b = 2\n}') end) - it('marks key paths with and metatables with ', function() + it('prints inspect.KEY & inspect.METATABLE', function() + local t = {inspect.KEY, inspect.METATABLE} + assert.equals(inspect(t), "{ inspect.KEY, inspect.METATABLE }") + end) + + it('marks key paths with inspect.KEY and metatables with inspect.METATABLE', function() local t = { [{a=1}] = setmetatable({b=2}, {c=3}) } local items = {} @@ -262,15 +267,15 @@ describe( 'inspect', function() assert.same(items, { {item = t, path = {}}, - {item = {a=1}, path = {{a=1}, ''}}, - {item = 'a', path = {{a=1}, '', 'a', ''}}, - {item = 1, path = {{a=1}, '', 'a'}}, + {item = {a=1}, path = {{a=1}, inspect.KEY}}, + {item = 'a', path = {{a=1}, inspect.KEY, 'a', inspect.KEY}}, + {item = 1, path = {{a=1}, inspect.KEY, 'a'}}, {item = setmetatable({b=2}, {c=3}), path = {{a=1}}}, - {item = 'b', path = {{a=1}, 'b', ''}}, + {item = 'b', path = {{a=1}, 'b', inspect.KEY}}, {item = 2, path = {{a=1}, 'b'}}, - {item = {c=3}, path = {{a=1}, ''}}, - {item = 'c', path = {{a=1}, '', 'c', ''}}, - {item = 3, path = {{a=1}, '', 'c'}} + {item = {c=3}, path = {{a=1}, inspect.METATABLE}}, + {item = 'c', path = {{a=1}, inspect.METATABLE, 'c', inspect.KEY}}, + {item = 3, path = {{a=1}, inspect.METATABLE, 'c'}} }) end) From 99e8c039596ea878ff7ee50078ed092b45db9232 Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 13 Sep 2014 12:33:20 +0200 Subject: [PATCH 13/13] newline & indent options --- README.md | 18 ++++++++++++++++++ inspect.lua | 6 +++++- spec/inspect_spec.lua | 16 ++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d79101d..cd1ec88 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,24 @@ assert(inspect(t5, {depth = 2}) == [[{ `options.depth` defaults to infinite (`math.huge`). +### options.newline & options.indent + +These are the strings used by `inspect` to respectively add a newline and indent each level of a table. + +By default, `options.newline` is `"\n"` and `options.indent` is `" "` (two spaces). + +``` lua +local t = {a={b=1}} + +assert(inspect(t) == [[{ + a = { + b = 1 + } +}]]) + +assert(inspect(t, {newline='@', indent="++"}), "{@++a = {@++++b = 1@++}@}" +``` + ### options.process `options.process` is a function which allow altering the passed object before transforming it into a string. diff --git a/inspect.lua b/inspect.lua index d548be1..8d2aa9f 100644 --- a/inspect.lua +++ b/inspect.lua @@ -199,7 +199,7 @@ function Inspector:down(f) end function Inspector:tabify() - self:puts("\n", string.rep(" ", self.level)) + self:puts(self.newline, string.rep(self.indent, self.level)) end function Inspector:commaControl(needsComma) @@ -304,6 +304,8 @@ function inspect.inspect(root, options) options = options or {} local depth = options.depth or math.huge local process = options.process + local newline = options.newline or '\n' + local indent = options.indent or ' ' if process then root = processRecursive(process, root, {}) end @@ -314,6 +316,8 @@ function inspect.inspect(root, options) level = 0, ids = setmetatable({}, idsMetaTable), maxIds = setmetatable({}, maxIdsMetaTable), + newline = newline, + indent = indent, tableAppearances = countTableAppearances(root) }, Inspector_mt) diff --git a/spec/inspect_spec.lua b/spec/inspect_spec.lua index 958aa81..f5ee147 100644 --- a/spec/inspect_spec.lua +++ b/spec/inspect_spec.lua @@ -197,6 +197,22 @@ describe( 'inspect', function() end) end) + describe('the newline option', function() + it('changes the substring used for newlines', function() + local t = {a={b=1}} + + assert.equal(inspect(t, {newline='@'}), "{@ a = {@ b = 1@ }@}") + end) + end) + + describe('the indent option', function() + it('changes the substring used for indenting', function() + local t = {a={b=1}} + + assert.equal(inspect(t, {indent='>>>'}), "{\n>>>a = {\n>>>>>>b = 1\n>>>}\n}") + end) + end) + describe('the process option', function() it('removes one element', function()