diff --git a/CHANGELOG.md b/CHANGELOG.md index b0b5739..e6f2925 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,8 +21,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Fixed -- Fixed formatted values in tables not being separated by newlines +- Fixed formatted values in tables not being separated by newlines. - Fixed panicking (crashing) when using `process.spawn` with a program that does not exist. +- Fixed `instance:SetAttribute("name", nil)` throwing an error and not removing the attribute. ## `0.8.4` - May 12th, 2024 diff --git a/crates/lune-roblox/src/instance/base.rs b/crates/lune-roblox/src/instance/base.rs index cc35373..58a2ae7 100644 --- a/crates/lune-roblox/src/instance/base.rs +++ b/crates/lune-roblox/src/instance/base.rs @@ -155,13 +155,18 @@ pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(m: &mut M) { |lua, this, (attribute_name, lua_value): (String, LuaValue)| { ensure_not_destroyed(this)?; ensure_valid_attribute_name(&attribute_name)?; - match lua_value.lua_to_dom_value(lua, None) { - Ok(dom_value) => { - ensure_valid_attribute_value(&dom_value)?; - this.set_attribute(attribute_name, dom_value); - Ok(()) + if lua_value.is_nil() || lua_value.is_null() { + this.remove_attribute(attribute_name); + Ok(()) + } else { + match lua_value.lua_to_dom_value(lua, None) { + Ok(dom_value) => { + ensure_valid_attribute_value(&dom_value)?; + this.set_attribute(attribute_name, dom_value); + Ok(()) + } + Err(e) => Err(e.into()), } - Err(e) => Err(e.into()), } }, ); diff --git a/crates/lune-roblox/src/instance/mod.rs b/crates/lune-roblox/src/instance/mod.rs index bc75f2f..da3ccb4 100644 --- a/crates/lune-roblox/src/instance/mod.rs +++ b/crates/lune-roblox/src/instance/mod.rs @@ -442,6 +442,29 @@ impl Instance { } } + /** + Removes an attribute from the instance. + + Note that this does not have an equivalent in the Roblox engine API, + but separating this from `set_attribute` lets `set_attribute` be more + ergonomic and not require an `Option` for the value argument. + The equivalent in the Roblox engine API would be `instance:SetAttribute(name, nil)`. + */ + pub fn remove_attribute(&self, name: impl AsRef) { + let mut dom = INTERNAL_DOM.lock().expect("Failed to lock document"); + let inst = dom + .get_by_ref_mut(self.dom_ref) + .expect("Failed to find instance in document"); + if let Some(DomValue::Attributes(attributes)) = + inst.properties.get_mut(PROPERTY_NAME_ATTRIBUTES) + { + attributes.remove(name.as_ref()); + if attributes.is_empty() { + inst.properties.remove(PROPERTY_NAME_ATTRIBUTES); + } + } + } + /** Adds a tag to the instance. diff --git a/tests/roblox/instance/attributes.luau b/tests/roblox/instance/attributes.luau index a75ac87..4c0a62f 100644 --- a/tests/roblox/instance/attributes.luau +++ b/tests/roblox/instance/attributes.luau @@ -101,6 +101,11 @@ local folder = Instance.new("Folder") folder:SetAttribute("Foo", "Bar") assert(folder:GetAttribute("Foo") == "Bar") +-- Setting attributes to nil should work + +folder:SetAttribute("Foo", nil) +assert(folder:GetAttribute("Foo") == nil) + -- Writing files with modified attributes should work local game = Instance.new("DataModel")