Optional/required

Unlike with querying values you can, but don't need to specify whether elements queried from the document are optional or required. As you will most often use the elements' values themselves, the necessity for the element hierarchy in between the document and that value is automatically inferred by whether the value itself is optional or required:

document.section('content').section('translated').field('revision').optional_string_value

The line above can be run for an entirely empty document and still return nil, while by modifying the query of the value itself to be required would make the code raise an error for the first element missing in that hierarchy instead (again assuming an empty document):

document.section('content').section('translated').field('revision').required_string_value

This would produce an error:

The section 'content' is missing - in case it has been specified look for typos and also check for correct capitalization.

   Line | Content
 *    1 | 

With that said, there will of course also be cases where you want to be explicit about whether an element is optional or required, for this you can use the explicit optional_* or required_* accessor variant, which exists for all element types, for instance:

document.required_field('author')
document.optional_section('notes')

As with value accessors, the optional_* variants return nil if the element does not exist (be aware that you should not chain further queries after such a call because you'd potentially be calling a function on nil), while the required_* variants immediately raise an error when the element is missing (here you can safely chain further queries afterwards).

Below is a pattern you might find useful, it's basically saying "There always has to be a field with the key 'author', but it can be empty". It returns nil or the value, and only raises an error when the field itself is missing.

document.required_field('author').optional_string_value

The pattern below is sort of the reverse, it's "If there is a field with the key 'author', it also requires a value", and guarantees there is a value if there is also a field, otherwise an error is raised.

author = document.optional_field('author')

if author
  value = author.required_string_value
  # ...
end

Last but not least a pattern that does not work:

author = document.field('author')

if author
  # Will always enter here, author is never nil
end

As shown earlier you can safely and deeply query a non-existing document hierarchy. This is possible because the standard query methods return proxy objects (e.g. MissingSection) instead of nil when an element in the chain is missing, and with that it's clear why the previous example does not work, instead you have to use the explicit optional_field accessor to ensure you are being returned nil if there is no element.

author = document.optional_field('author')

if author
  # This works, author can be nil now
end

Next page: Dynamic layouts