Now building: Lesson 10 - Authorization

🌡
I've got a fever, and the only cure is
more Reflex!

An assorted collection of patterns, directly extracted from the course.

Filterable

More

Decouple your filtering and faceted search using concerns

How

  • Use a Filterable concern to mediate between your controllers/concerns and the filtering logic
  • In separate Filter classes, define how and which rules apply, and how to update filter params

Caveats

The Book and Restaurant classes in this example mimick an ActiveRecord model. The array refinements simply serve as stand-ins for model scopes or AR queries.

NestedFormReflex

More

A reflex to construct a form that wraps a has_many relationship with nested attributes on the fly.

How?

  • New children are instantiated by calling .build on the has_many association
  • fields_for expands to all children if a child_attributes= setter is present (which is the case if accepts_nested_attributes_for is set) - see API docs

Caveat

Clean up your session (or other persistent store) after form submission.

TemplateReflex

More

Compose a UI using page morphs

How?

  • UI components (“templates”) are inserted/removed using two reflex actions, and are identified by uuids
  • The session is used to persist/manage them

Caveat

Note that in a real-world app, you’d probably want to use model partials and empty model instances to construct your UI (the Template class acts as a stand-in for both model and partial)

Variations

Use kredis as ephemeral persistence store

WizardReflex

More

Write powerful wizards using page morphs

How?

  • An empty state model is memoized in the controller (@book ||= Book.new(title: ...))
  • A general purpose WizardReflex is used to step through the wizard and perist the model’s state in the session
  • A @current_step variable is in/decreased to display the current wizard pane.
  • Validations are performed contextually, i.e. on: :step_1, etc.
  • An allowlist approach is used to centrally sanitize resource classes and strong params in that reflex.

Caveat

In these examples, the amount of steps per wizard are hardcoded.

Variations

Enrich individual WizardReflexes with custom input processing logic:

class WizardReflex < ApplicationReflex
  def refresh
    additional_attributes, processed_resource_params = yield(resource_params) if block_given?

    session[:"new_#{resource_name.underscore}"] = resource_class.new(processed_resource_params || resource_params)
    session[:"new_#{resource_name.underscore}"].assign_attributes(**additional_attributes || {})

    # ...
  end
  
  # ...
end

class BookReflex < WizardReflex
  def refresh
    super do |params|
      [{isbn: '1234'}, params.except(...)]
    end
  end
end