Skip to content

Commit

Permalink
Add in impl for easily mocking roblox classes in lune
Browse files Browse the repository at this point in the history
  • Loading branch information
gaymeowing committed Aug 20, 2024
1 parent 5d70ace commit c687d77
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 38 deletions.
22 changes: 12 additions & 10 deletions libs/pagesutil/init.luau
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
--!optimize 2

-- pagesutil
-- utility for dealing with roblox page instancs easily
Expand All @@ -9,9 +8,15 @@ export type PageInfo = {
page: number,
}

local advance_to_next_page_err_format = "[PAGES] an error occurred while trying to advance to the next page\n\tadvance-next-page-err: %s"
local ADVANCE_ERR_FORMAT = "[PAGES] an error occurred while trying to advance to the next page\
\tadvance-next-page-err: %s"

local function iter(pages: Pages, f: (info: PageInfo, item: { [any]: any }) -> ("continue" | "break")?)
local pagesutil = {}

function pagesutil.iter(
pages: Pages,
f: (info: PageInfo, item: { [any]: any }) -> ("continue" | "break")?
)
local advance_to_next_page = pages.AdvanceToNextPageAsync
local page = pages:GetCurrentPage()
local page_info = {
Expand Down Expand Up @@ -51,14 +56,14 @@ local function iter(pages: Pages, f: (info: PageInfo, item: { [any]: any }) -> (
end
end
else
warn(string.format(advance_to_next_page_err_format, err))
warn(string.format(ADVANCE_ERR_FORMAT, err))
return
end
until pages.IsFinished
end
end

local function toarray(pages: Pages): { { [any]: any } }
function pagesutil.toarray(pages: Pages): { { [any]: any } }
local advance_to_next_page = pages.AdvanceToNextPageAsync
local contents = pages:GetCurrentPage()

Expand All @@ -70,15 +75,12 @@ local function toarray(pages: Pages): { { [any]: any } }
local current_page = pages:GetCurrentPage()
table.move(current_page, 1, #current_page, #contents + 1, contents)
else
warn(string.format(advance_to_next_page_err_format, err))
warn(string.format(ADVANCE_ERR_FORMAT, err))
break
end
until pages.IsFinished
end
return contents
end

return table.freeze {
toarray = toarray,
iter = iter,
}
return table.freeze(pagesutil)
150 changes: 150 additions & 0 deletions scripts/mock/roblox/impl.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@

-- impl
-- a wrapper for easily implementing properties and methods
-- in lune to mock roblox stuffs

local roblox = require("@lune/roblox")

type GenericInstance<I> = roblox.Instance & I

type PropertySetter<I, P> = (instance: GenericInstance<I>, new_value: unknown) -> P

type ImplPrototype<I> = {
rwprop: (<P>(self: Impl<I>, name: string, setter: PropertySetter<I, P>, default: P) -> Impl<I>) &
(<P>(self: Impl<I>, name: string, default: P) -> Impl<I>),
rprop: <P>(self: Impl<I>, name: string, default: P) -> Impl<I>,
method: <A..., R...>(self: Impl<I>, name: string, f: (instance: GenericInstance<I>, A...) -> R...) -> Impl<I>,
__index: ImplPrototype<I>,
}

export type Impl<I> = typeof(setmetatable({} :: {
propstores: { [string]: { [GenericInstance<I>]: any } },
name: string,
}, {} :: ImplPrototype<I>))

local CANNOT_SET_ERR_FORMAT = "[IMPL] cannot set property \"%s\" on class \"%s\" to a value that isn't of type \"%s?\""
local CLASSES_IMPLEMENTED = {} :: { [string]: boolean }

local function rwprop<I, P, D>(
impl: Impl<I>,
propname: string,
default_or_setter: P | PropertySetter<I, P>,
default: P
): Impl<I>
local propstores = impl.propstores
local classname = impl.name
local propstore = {}

propstores[propname] = propstore

if type(default_or_setter) == "function" and default then
roblox.implementProperty(
classname,
propname,
function(instance: any)
local value = propstore[instance]

if value then
return value
else
propstore[instance] = default
return default
end
end,
function(instance: any, new_value)
local new_value = default_or_setter(instance, new_value)
propstore[instance] = new_value
end
)
else
local type = typeof(default_or_setter)
local cannot_set_err = string.format(
CANNOT_SET_ERR_FORMAT, propname,
classname, type
)

roblox.implementProperty(
classname,
propname,
function(instance: any)
local value = propstore[instance]

if value then
return value
else
propstore[instance] = default_or_setter
return default_or_setter
end
end,
function(instance: any, new_value)
if typeof(new_value) == type then
propstore[instance] = new_value
elseif new_value == nil then
propstore[instance] = nil
else
error(cannot_set_err)
end
end
)
end
return impl
end

local function rprop<I, P, D>(
impl: Impl<I>,
propname: string,
default: P
): Impl<I>
local propstores = impl.propstores
local classname = impl.name
local propstore = {}

propstores[propname] = propstore

roblox.implementProperty(
classname,
propname,
function(instance: any)
local value = propstore[instance]

if value then
return value
else
propstore[instance] = default
return default
end
end
)
return impl
end

local function method<I, P, A..., R...>(
impl: Impl<I>,
method_name: string,
f: (instance: GenericInstance<I>, A...) -> R...
): Impl<I>
roblox.implementMethod(impl.name, method_name, f :: any)
return impl
end

local impl = {
method = method,
rwprop = rwprop,
rprop = rprop,
}
impl.__index = impl
table.freeze(impl)

local function create<I>(name: string): Impl<I>
if CLASSES_IMPLEMENTED[name] then
error(`[IMPL] class "{name}" has already been implemented`)
end
CLASSES_IMPLEMENTED[name] = true

return table.freeze(setmetatable({
propstores = {},
name = name,
}, impl :: any))
end

return create
22 changes: 0 additions & 22 deletions scripts/mock/roblox/implementer.luau

This file was deleted.

7 changes: 7 additions & 0 deletions scripts/mock/roblox/services/analytics.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

-- analytics
-- implements a mock analytics service for lune

local impl = require("../impl")

return impl("AnalyticsService")
7 changes: 7 additions & 0 deletions scripts/mock/roblox/services/collection.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

-- collection
-- implements a mock collection service for lune

local impl = require("../impl")

return impl("CollectionService")
7 changes: 7 additions & 0 deletions scripts/mock/roblox/services/datastore.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

-- datastore
-- implements a mock datastore service for lune

local impl = require("../impl")

return impl("DatastoreService")
9 changes: 3 additions & 6 deletions scripts/mock/roblox/services/http.luau
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
-- httpservice
-- implements a mock http service for lune

local roblox = require("@lune/roblox")
local net = require("@lune/net")
local impl = require("../impl")



local function impl_mock_http()

end
return impl("HttpService")
:rwprop("HttpEnabled", false)
10 changes: 10 additions & 0 deletions scripts/mock/roblox/services/players.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

-- players
-- implements a mock players service for lune

local impl = require("../impl")

return impl("Players")
:method("GetPlayers", function(players)
return players:GetChildren()
end)
Empty file.

0 comments on commit c687d77

Please sign in to comment.