In order to manage write access to the fields of an object, we can extend the setproperty! function, in a similar way to how we did for read access.
Let's recall how the FileContent data type was designed:
mutable struct FileContent
path
loaded
contents
end
Suppose that we want to allow the user to switch to a different file by mutating the path field with a new file location. In addition to this, we want to prevent the loaded and contents fields from being changed directly using dot notation. To achieve that, we can extend the setproperty! function as follows:
function Base.setproperty!(fc::FileContent, s::Symbol, value)
if s === :path
ss = lstat(value)
setfield!(fc, :path, value)
setfield!(fc, :loaded, false)
setfield!(fc, :contents, zeros(UInt8, ss.size))
println("Object re-initialized for $value (size $(ss.size))")
return nothing
end
error("Property $s cannot be changed.")
end
To extend the setproperty! function, we must use setfield! in the function definition whenever we need to change any field in the object.
In this case, when the user tries to assign a value to the path field, we can just reinitialize the object like how we did in the constructor function. This involves setting the values of the path and loaded fields, as well as pre-allocating memory space for the file content. Let's go ahead and test it now:
If the user tries to assign a value to any other field, an error is thrown:
By extending the setproperty! function, we have successfully controlled write access to any field for any object.
By now, we know how to implement the getproperty and setproperty! functions so that we can control access to the individual fields of an object. Next, we will look at how to document what properties are available.