Implementing read access and lazy loading

In order to implement lazy loading, we can extend the getproperty function. During the call, we can check whether the file content has been loaded yet. If not, we just load the file content right before returning the data back to the caller.

Extending the getproperty function is as easy as simply defining it with the FileContent type and a symbol as the arguments of the function. The following code shows this:

function Base.getproperty(fc::FileContent, s::Symbol)
direct_passthrough_fields = (:path, )
if s in direct_passthrough_fields
return getfield(fc, s)
end
if s === :contents
!getfield(fc, :loaded) && load_contents!(fc)
return getfield(fc, :contents)
end
error("Unsupported property: $s")
end

It is important that we define the function for Base.getproperty rather than just getproperty. That is because the compiler will translate the dot notation to Base.getproperty rather than the getproperty function in your own module. If this is unclear, you are encouraged to revisit the namespace concept from the Understanding namespaces, modules, and packages section in Chapter 2, Modules, Packages, and Data Type Concepts.

We have chosen to put Base as a prefix to the function name in the definition. This style of coding is preferred because it is clear from the function definition that we are extending the getproperty function from the Base package.

Another way to extend functions from another package is to first import the third-party package. For the preceding example, we could have written it as follows. This coding style is not recommended because it is less obvious that the getproperty function being defined is an extension of the function from Base:

import Base: getproperty

function getproperty(fc::FileContent, s::Symbol)
 ....
end

By contrast, the getproperty function must handle all possible property names. Let's first consider the following section of code:

In this case, we must support :path and :contents. If the s symbol is one of those fields that we want to pass through directly, then we just forward the call to the getfield function.

Now, let's consider the next section of code: 

If the symbol is :contents, then we check the value of the loaded field. If the loaded field contains false, then we call the load_contents! function to load the file content into memory.

Note that we have used getfield all over the place in this function. If we had written the code using the normal dot syntax, for example, fc.loaded, then it would start calling the getproperty function again and we could end up with infinite recursion. 

If the field name is not one of the supported ones, then we just raise an exception, as follows:

One interesting observation is that we have decided to support two property names only – path and contents – and we have dropped the support for the loaded property. The reason for this is that the loaded field is really used as an internal state of the object. There is no reason to expose it as part of the public programming interface. As we talk about software robustness in this chapter, we can also appreciate developing code that only exposes necessary information. 

An analogy is that data is always classified but can be released only on a need-to-know basis, which is how government officials usually like to describe highly sensitive data.

We are almost done. The only remaining piece of work is to refactor the load_content! function to use getfield and setfield! instead of the dot notation:

# lazy load
function load_contents!(fc::FileContent)
open(getfield(fc, :path)) do io
readbytes!(io, getfield(fc, :contents))
setfield!(fc, :loaded, true)
end
nothing
end

We can now test the lazy loading functionality:

Both references to the path and contents fields are working properly. In particular, a reference to fc.contents triggered the file load and then returned the proper content. So, what happened to the loaded field? Let's try it:

Voila! We have successfully prevented the loaded field from being accessed directly. 

The property interface has enabled us to manage read access and implement the lazy loading feature. Next, we will look at how to manage write access as well.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.143.4