This repository has been archived by the owner on Dec 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replaces `DEBUG_ENABLE` with `setGlobalConfig`, and `getGlobalConfigValue`
- Loading branch information
Showing
9 changed files
with
255 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
--[[ | ||
Exposes an interface to set global configuration values for Roact. | ||
Configuration can only occur once, and should only be done by an application | ||
using Roact, not a library. | ||
Any keys that aren't recognized will cause errors. Configuration is only | ||
intended for configuring Roact itself, not extensions or libraries. | ||
Configuration is expected to be set immediately after loading Roact. Setting | ||
configuration values after an application starts may produce unpredictable | ||
behavior. | ||
]] | ||
|
||
-- Every valid configuration value should be non-nil in this table. | ||
local defaultConfig = { | ||
-- Enables storage of `debug.traceback()` values on elements for debugging. | ||
["elementTracing"] = false, | ||
} | ||
|
||
-- Build a list of valid configuration values up for debug messages. | ||
local defaultConfigKeys = {} | ||
for key in pairs(defaultConfig) do | ||
table.insert(defaultConfigKeys, key) | ||
end | ||
|
||
--[[ | ||
Merges two tables together into a new table. | ||
]] | ||
local function join(a, b) | ||
local new = {} | ||
|
||
for key, value in pairs(a) do | ||
new[key] = value | ||
end | ||
|
||
for key, value in pairs(b) do | ||
new[key] = value | ||
end | ||
|
||
return new | ||
end | ||
|
||
local Config = {} | ||
|
||
function Config.new() | ||
local self = {} | ||
|
||
-- Once configuration has been set, we record a traceback. | ||
-- That way, if the user mistakenly calls `set` twice, we can point to the | ||
-- first place it was called. | ||
self._lastConfigTraceback = nil | ||
|
||
self._currentConfig = defaultConfig | ||
|
||
-- We manually bind these methods here so that the Config's methods can be | ||
-- used without passing in self, since they eventually get exposed on the | ||
-- root Roact object. | ||
self.set = function(...) | ||
return Config.set(self, ...) | ||
end | ||
|
||
self.getValue = function(...) | ||
return Config.getValue(self, ...) | ||
end | ||
|
||
self.reset = function(...) | ||
return Config.reset(self, ...) | ||
end | ||
|
||
return self | ||
end | ||
|
||
function Config.set(self, configValues) | ||
if self._lastConfigTraceback then | ||
local message = ( | ||
"Global configuration can only be set once. Configuration was already set at:%s" | ||
):format( | ||
self._lastConfigTraceback | ||
) | ||
|
||
error(message, 3) | ||
end | ||
|
||
-- We use 3 as our traceback and error level because all of the methods are | ||
-- manually bound to 'self', which creates an additional stack frame we want | ||
-- to skip through. | ||
self._lastConfigTraceback = debug.traceback("", 3) | ||
|
||
-- Validate values without changing any configuration. | ||
-- We only want to apply this configuration if it's valid! | ||
for key, value in pairs(configValues) do | ||
if defaultConfig[key] == nil then | ||
local message = ( | ||
"Invalid global configuration key %q (type %s). Valid configuration keys are: %s" | ||
):format( | ||
tostring(key), | ||
typeof(key), | ||
table.concat(defaultConfigKeys, ", ") | ||
) | ||
|
||
error(message, 3) | ||
end | ||
|
||
-- Right now, all configuration values must be boolean. | ||
if typeof(value) ~= "boolean" then | ||
local message = ( | ||
"Invalid value %q (type %s) for global configuration key %q. Valid values are: true, false" | ||
):format( | ||
tostring(value), | ||
typeof(value), | ||
tostring(key) | ||
) | ||
|
||
error(message, 3) | ||
end | ||
end | ||
|
||
-- Assign all of the (validated) configuration values in one go. | ||
self._currentConfig = join(self._currentConfig, configValues) | ||
end | ||
|
||
function Config.getValue(self, key) | ||
if defaultConfig[key] == nil then | ||
local message = ( | ||
"Invalid global configuration key %q (type %s). Valid configuration keys are: %s" | ||
):format( | ||
tostring(key), | ||
typeof(key), | ||
table.concat(defaultConfigKeys, ", ") | ||
) | ||
|
||
error(message, 3) | ||
end | ||
|
||
return self._currentConfig[key] | ||
end | ||
|
||
function Config.reset(self) | ||
self._lastConfigTraceback = nil | ||
self._currentConfig = defaultConfig | ||
end | ||
|
||
return Config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
return function() | ||
local Config = require(script.Parent.Config) | ||
|
||
it("should accept valid configuration", function() | ||
local config = Config.new() | ||
|
||
expect(config.getValue("elementTracing")).to.equal(false) | ||
|
||
config.set({ | ||
elementTracing = true, | ||
}) | ||
|
||
expect(config.getValue("elementTracing")).to.equal(true) | ||
end) | ||
|
||
it("should reject invalid configuration keys", function() | ||
local config = Config.new() | ||
|
||
local badKey = "garblegoop" | ||
|
||
local ok, err = pcall(function() | ||
config.set({ | ||
[badKey] = true, | ||
}) | ||
end) | ||
|
||
expect(ok).to.equal(false) | ||
|
||
-- The error should mention our bad key somewhere. | ||
expect(err:find(badKey)).to.be.ok() | ||
end) | ||
|
||
it("should reject invalid configuration values", function() | ||
local config = Config.new() | ||
|
||
local goodKey = "elementTracing" | ||
local badValue = "Hello there!" | ||
|
||
local ok, err = pcall(function() | ||
config.set({ | ||
[goodKey] = badValue, | ||
}) | ||
end) | ||
|
||
expect(ok).to.equal(false) | ||
|
||
-- The error should mention both our key and value | ||
expect(err:find(goodKey)).to.be.ok() | ||
expect(err:find(badValue)).to.be.ok() | ||
end) | ||
|
||
it("should prevent setting configuration more than once", function() | ||
local config = Config.new() | ||
|
||
-- We're going to use the name of this function to see if the traceback | ||
-- was correct. | ||
local function setEmptyConfig() | ||
config.set({}) | ||
end | ||
|
||
setEmptyConfig() | ||
|
||
local ok, err = pcall(setEmptyConfig) | ||
|
||
expect(ok).to.equal(false) | ||
|
||
-- The error should mention the stack trace with the original set call. | ||
expect(err:find("setEmptyConfig")).to.be.ok() | ||
end) | ||
|
||
it("should reset to default values after invoking reset()", function() | ||
local config = Config.new() | ||
|
||
expect(config.getValue("elementTracing")).to.equal(false) | ||
|
||
config.set({ | ||
elementTracing = true, | ||
}) | ||
|
||
expect(config.getValue("elementTracing")).to.equal(true) | ||
|
||
config.reset() | ||
|
||
expect(config.getValue("elementTracing")).to.equal(false) | ||
end) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--[[ | ||
Exposes a single instance of a configuration as Roact's GlobalConfig. | ||
]] | ||
|
||
local Config = require(script.Parent.Config) | ||
|
||
return Config.new() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
return function() | ||
local GlobalConfig = require(script.Parent.GlobalConfig) | ||
|
||
it("should have the correct methods", function() | ||
expect(GlobalConfig).to.be.ok() | ||
expect(GlobalConfig.set).to.be.ok() | ||
expect(GlobalConfig.getValue).to.be.ok() | ||
expect(GlobalConfig.reset).to.be.ok() | ||
end) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters