Module: Structured
- Defined in:
- lib/structured.rb
Overview
Sets up a class to receive an initializing hash and to populate information about the class from that hash. The expected hash elements are self-documenting and type-checking to facilitate future generation of hash elements.
The basic usage is to include the Structured
module in a class, which gives the class a method ClassMethods#element, used to declare elements expected in the initializing hash. Once an element is declared, a few things happen:
-
The element is looked for upon initialization
-
If found, the element’s value is type-checked and possibly converted to a new object. In particular:
-
If the expected type is a Structured object, then the value is expected to be a hash, which is used as input to construct the expected Structured object. This subsidiary Structured object has its @parent instance variable set so that a complete two-way tree of objects is maintained.
-
If the expected type is an Array of Structured objects, then the value is expected to be an array of hashes, each of which is converted to the expected Structured object. The @parent variable is also set.
-
If the expected type is a Hash including Structured object, then the value is similarly converted to a hash of Structured objects. As an added benefit, besides @parent being set, hash values have the @key instance variable set, so that the values are aware of the hash key with which they are associated.
-
-
An instance variable @[element] is set to the given value.
As a result, at the end of the initialization of a Structured object, it will have instance variables set corresponding to all the defined elements.
Customization of Structured Classes
The above explanation is default behavior, and several customizations are available.
-
Methods
receive_[element]
can be defined, taking a single parameter. By default, the method sets an instance variable @[name] with the parameter value. Classes may override this method to provide different initialization actions. (Alternately, classes can accept the default initialization methods and override #initialize for further processing.) -
Methods receive_parent and receive_key can be similarly redefined to change the processing of parent Structured objects and hash keys, respectively.
-
To process unknown elements, call ClassMethods#default_element to specify their expected type. (It should typically be just a class name, as that method’s documentation explains.) Then define
receive_any
to handle undefined elements, for example by placing them in a hash. For these elements, the @key instance variable is also set for them if the expected type is a Structured class.
Please read the documentation for Structured::ClassMethods for more on defining expected elements, type checking, and so on.
Subfiles as Input
The Structured class provides automatic support for separating inputs into YAML subfiles. This is useful for including complex objects in a file. Two types of subfile inputs are supported: those for object hashes, and those for arrays.
To include a subfile as part of an object hash, include the key ‘read_file` in the hash, with the value being the file to be read. (Other keys besides `read_file` may be included.) The subfile should itself contain YAML for a hash with further keys for the object.
To include multiple subfiles in an array, set the first element of the array to the string ‘read_file`, and then the other elements of the array should be filenames. These subfiles should contain YAML for arrays.
Consider a Structured object for a book, containing elements for the title, subtitle, and an array of authors. The input file could look like this:
---
title: A Book
subtitle: Containing Many Pages
author:
- John Q. Public
- Jane Doe
Using the subfile feature, the input file could instead look like:
---
title: A Book
read_file: subtitle_file.yaml
author:
- read_file
- author_file.yaml
This would instruct Structured to read hash keys out of ‘subtitle_file.yaml`, and to read array elements out of `author_file.yaml`. These two files, in turn, should look like:
# subtitle_file.yaml
---
subtitle: Containing Many Pages
# author_file.yaml
---
- John Q. Public
- Jane Doe
When incorporated, Structured will combine these subfiles as if they were a single object specification.
Defined Under Namespace
Modules: ClassMethods Classes: InputError
Instance Attribute Summary collapse
-
#key ⇒ Object
readonly
Returns the value of attribute key.
-
#parent ⇒ Object
readonly
Returns the value of attribute parent.
Class Method Summary collapse
-
.included(base) ⇒ Object
Includes ClassMethods.
-
.trace(note) ⇒ Object
Enable tracing of object creation.
Instance Method Summary collapse
-
#initialize(hash, parent = nil) ⇒ Object
Initializes the object based on an initialization hash.
-
#input_err(text) ⇒ Object
Raises an InputError.
-
#post_initialize ⇒ Object
Subclasses may override this method to provide post-initialization routines, run after the initializing hash is processed.
-
#pre_initialize ⇒ Object
Subclasses may override this method to provide pre-initialization routines, run before the initializing hash is processed.
-
#receive_any(element, val) ⇒ Object
Processes an undefined element in the initializing hash.
-
#receive_key(key) ⇒ Object
Processes the key object for this Structured class.
-
#receive_parent(parent) ⇒ Object
Processes the parent object for this Structured class.
Instance Attribute Details
#key ⇒ Object (readonly)
Returns the value of attribute key.
221 222 223 |
# File 'lib/structured.rb', line 221 def key @key end |
#parent ⇒ Object (readonly)
Returns the value of attribute parent.
206 207 208 |
# File 'lib/structured.rb', line 206 def parent @parent end |
Class Method Details
.included(base) ⇒ Object
Includes ClassMethods.
815 816 817 818 819 820 |
# File 'lib/structured.rb', line 815 def self.included(base) if base.is_a?(Class) base.extend(ClassMethods) base.reset_elements end end |
.trace(note) ⇒ Object
Enable tracing of object creation.
825 826 827 828 829 830 831 832 833 834 835 |
# File 'lib/structured.rb', line 825 def self.trace(note) begin @trace_stack.push(note) return yield rescue InputError => e e.structured_stack ||= @trace_stack.dup raise e ensure @trace_stack.pop end end |
Instance Method Details
#initialize(hash, parent = nil) ⇒ Object
Initializes the object based on an initialization hash. All methods that include Structured should retain this initialization signature to the extent possible, because downstream Structured objects expect to be initialized this way.
169 170 171 172 173 174 175 176 |
# File 'lib/structured.rb', line 169 def initialize(hash, parent = nil) Structured.trace(self.class) do pre_initialize receive_parent(parent) if parent self.class.build_from_hash(self, hash) post_initialize end end |
#input_err(text) ⇒ Object
Raises an InputError.
240 241 242 |
# File 'lib/structured.rb', line 240 def input_err(text) raise InputError, text end |
#post_initialize ⇒ Object
Subclasses may override this method to provide post-initialization routines, run after the initializing hash is processed. This may be useful for global data checks (that depend on several values).
190 191 |
# File 'lib/structured.rb', line 190 def post_initialize end |
#pre_initialize ⇒ Object
Subclasses may override this method to provide pre-initialization routines, run before the initializing hash is processed.
182 183 |
# File 'lib/structured.rb', line 182 def pre_initialize end |
#receive_any(element, val) ⇒ Object
Processes an undefined element in the initializing hash. By default, this raises an error, but classes may override this method to use the undefined elements.
a string.
233 234 235 |
# File 'lib/structured.rb', line 233 def receive_any(element, val) raise NameError, "Unexpected element for #{self.class}: #{element}" end |
#receive_key(key) ⇒ Object
Processes the key object for this Structured class. The key is automatically given when this Structured object is a subsidiary of another, within a key-value hash. It is also automatically given when this Structured object is created while processing a default element.
By default, this method sets @key to the given object. Classes may override this method to do other things with the key object.
217 218 219 |
# File 'lib/structured.rb', line 217 def receive_key(key) @key = key end |
#receive_parent(parent) ⇒ Object
Processes the parent object for this Structured class. The parent is automatically given for subsidiary Structured objects, triggering a call to this method.
By default, @parent is set to the given object. Classes may override this method to do other things with the parent object (for example, test the parent object type).
202 203 204 |
# File 'lib/structured.rb', line 202 def receive_parent(parent) @parent = parent end |