Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Reflection

Blueprints may be reflected on to inspect their views, fields, and options. This is useful for building extensions, and possibly even for some applications.

We will use the following blueprint in the examples below:

class WidgetBlueprint < ApplicationBlueprint
  field :name
  field :description, exclude_if_empty: true
  object :category, CategoryBlueprint
  collection :parts, PartBlueprint

  view :extended do
    object :manufacturer, CompanyBlueprint[:full]

    view :with_price do
      field :price
    end
  end
end

Blueprint & view names

WidgetBlueprint.blueprint_name
=> "WidgetBlueprint"

WidgetBlueprint.view_name
=> :default

WidgetBlueprint[:extended].blueprint_name
=> "WidgetBlueprint.extended"

WidgetBlueprint[:extended].view_name
=> :extended

WidgetBlueprint["extended.with_price"].blueprint_name
=> "WidgetBlueprint.extended.with_price"

WidgetBlueprint["extended.with_price"].view_name
=> :"extended.with_price"

Blueprint & view options

WidgetBlueprint.options
=> {exclude_if_nil: true}

WidgetBlueprint[:extended].options
=> {exclude_if_nil: true, exclude_if_empty: true}

Views

Here, :default refers to the top level of the blueprint.

WidgetBlueprint.reflections.keys
=> [:default, :extended, :"extended.with_price"]

You can also reflect directly on a view.

WidgetBlueprint[:extended].reflections.keys
=> [:default, :with_price]

Notice that the names are relative: :default now refers to the :extended view, since we called .reflections on :extended. The prefix is also gone from the nested :with_price view.

Fields

view = WidgetBlueprint.reflections[:default]

# Regular fields
view.fields.keys
=> [:name, :description]

# Object fields
view.objects.keys
=> [:category]

# Collection fields
view.collections.keys
=> [:parts]

# All fields in the order they were defined
view.ordered
# returns an array of field objects

Field metadata

view = WidgetBlueprint.reflections[:default]
field = view.fields[:description]

field.name
=> :description

field.from
=> :description # the :from option in the DSL

field.value_proc
=> nil # the block you passed to the field, if any

field.options # all other options passed to the field
=> { exclude_if_empty: true }

Object and collection fields have the same metadata as regular fields, plus a blueprint attribute:

view = WidgetBlueprint.reflections[:default]
field = view.collections[:parts]

# it returns the Blueprint class, so you can continue reflecting
field.blueprint
=> PartBlueprint

field.blueprint.reflections[:default].fields
=> # array of fields on the default view of PartBlueprint

If you used a view in an object or collection field, you can reflect on that view just like a blueprint:

view = WidgetBlueprint.reflections[:extended]
field = view.objects[:manufacturer]

field.blueprint.to_s
=> "CompanyBlueprint.full"

# Remember, we're reflecting ON the :full view, so the name is relative!
field.blueprint.reflections[:default].fields
=> # array of fields on the :full view of CompanyBlueprint