Class: CrudComponents::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/crud_components/builder.rb

Overview

The DSL evaluated inside ‘crud_structure do … end`. Its instance methods —#attribute, #attributes, #action, #fieldset, #label, #identify_by, #search_in — are the public declaration API; the block runs against a Builder instance. It only collects declarations; Structure resolves and validates them against what Rails already knows.

Examples:

class Book < ApplicationRecord
  include CrudComponents::Model
  crud_structure do
    label :title
    identify_by :slug
    attribute :title  { filter :title; sort :title }
    fieldset :index, %i[title author], actions: %i[edit destroy]
  end
end

Defined Under Namespace

Classes: FacetCollector

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model) { ... } ⇒ Builder

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Builder.

Parameters:

  • model (Class)

    the ActiveRecord model being described.

Yields:

  • the ‘crud_structure` block, evaluated against this Builder.



26
27
28
29
30
31
32
# File 'lib/crud_components/builder.rb', line 26

def initialize(model, &block)
  @model = model
  @declarations = {}
  @actions = {}
  @fieldsets = {}
  instance_exec(&block)
end

Instance Attribute Details

#actionsObject (readonly)

Returns the value of attribute actions.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def actions
  @actions
end

#declarationsObject (readonly)

Returns the value of attribute declarations.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def declarations
  @declarations
end

#fieldsetsObject (readonly)

Returns the value of attribute fieldsets.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def fieldsets
  @fieldsets
end

#icon_declObject (readonly)

Returns the value of attribute icon_decl.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def icon_decl
  @icon_decl
end

#identify_by_declObject (readonly)

Returns the value of attribute identify_by_decl.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def identify_by_decl
  @identify_by_decl
end

#label_declObject (readonly)

Returns the value of attribute label_decl.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def label_decl
  @label_decl
end

#label_preload_declObject (readonly)

Returns the value of attribute label_preload_decl.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def label_preload_decl
  @label_preload_decl
end

#modelObject (readonly)

Returns the value of attribute model.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def model
  @model
end

#preload_declObject (readonly)

Returns the value of attribute preload_decl.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def preload_decl
  @preload_decl
end

#search_declObject (readonly)

Returns the value of attribute search_decl.



19
20
21
# File 'lib/crud_components/builder.rb', line 19

def search_decl
  @search_decl
end

Instance Method Details

#action(name, **options) { ... } ⇒ void

This method returns an undefined value.

Declare a custom action button. The block returns its path, evaluated in the view context (and given the record for a row action).

Parameters:

  • name (Symbol)

    the action name (also the i18n/route key).

  • options (Hash)

    ‘on:` (`:row`/`:collection`/`:selection`), `icon:`, `title:`, `class:`, `confirm:`, `method:`, `if:`.

Yields:

  • the path block.

Raises:



132
133
134
135
136
137
# File 'lib/crud_components/builder.rb', line 132

def action(name, **options, &path_block)
  name = name.to_sym
  raise DefinitionError, "#{model}: action :#{name} declared twice" if @actions.key?(name)

  @actions[name] = Action.new(name, **options, &path_block)
end

#attribute(name, **options) { ... } ⇒ void

This method returns an undefined value.

Declare (or refine) one attribute. The optional block sets facets: a one-arity block is the render facet; a zero-arity block declares ‘filter`/`sort`/`render` (see FacetCollector).

Parameters:

  • name (Symbol)

    the attribute/column/association name.

  • options (Hash)

    e.g. ‘as:` (renderer), `form_as:`, `if:`, `editable:`, `label:`, `null:`.

Yields:

  • optional facet block.



102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/crud_components/builder.rb', line 102

def attribute(name, **options, &block)
  name = name.to_sym
  if @declarations.key?(name)
    raise DefinitionError, "#{model}: attribute :#{name} declared twice — merge the declarations"
  end
  if RESERVED_PARAMS.include?(name.to_s)
    raise DefinitionError, "#{model}: :#{name} is a reserved param name " \
                           "(#{RESERVED_PARAMS.join(', ')}) and cannot be a field"
  end

  @declarations[name] = { options: options, facets: parse_facets(name, block) }
end

#attributes(*names, **options) ⇒ void

This method returns an undefined value.

Declare several attributes that share the same options.

Parameters:

  • names (Array<Symbol>)

    one or more attribute names.

  • options (Hash)

    applied to each (see #attribute).

Raises:



119
120
121
122
123
# File 'lib/crud_components/builder.rb', line 119

def attributes(*names, **options)
  raise DefinitionError, "#{model}: attributes needs at least one name" if names.empty?

  names.each { |name| attribute(name, **options) }
end

#fieldset(name, fields = :all, actions: nil, filters: nil) ⇒ void

This method returns an undefined value.

A named selection of fields + actions for a surface (index/show/form/…).

Parameters:

  • name (Symbol)

    the fieldset name.

  • fields (Array<Symbol>, :all) (defaults to: :all)

    which fields, in order (‘:all` = every declared/derived field).

  • actions (Array<Symbol>, nil) (defaults to: nil)

    curate the actions (per kind); nil keeps the derived defaults.

  • filters (Array<Symbol>, nil) (defaults to: nil)

    filterable fields beyond the visible ones.

Raises:



147
148
149
150
151
152
# File 'lib/crud_components/builder.rb', line 147

def fieldset(name, fields = :all, actions: nil, filters: nil)
  name = name.to_sym
  raise DefinitionError, "#{model}: fieldset :#{name} declared twice" if @fieldsets.key?(name)

  @fieldsets[name] = Fieldset.new(name, fields, actions: actions, filters: filters)
end

#icon(name) ⇒ void

This method returns an undefined value.

The icon (no library prefix — paired with ‘css.icon_prefix`) that badges this model wherever it appears: column-picker groups, association links, path-column cells. Overrides the name-based guess in `config.model_icons`.

Parameters:

  • name (String, Symbol)

    a Bootstrap-icon name, e.g. ‘’building’‘.

Raises:



75
76
77
78
79
# File 'lib/crud_components/builder.rb', line 75

def icon(name)
  raise DefinitionError, "#{model}: icon declared twice" if @icon_decl

  @icon_decl = name.to_s
end

#identify_by(column) ⇒ void

This method returns an undefined value.

The column used in URLs (‘to_param`) and to resolve a bulk selection.

Parameters:

  • column (Symbol)

    e.g. ‘:slug`. Defaults to `:id` when undeclared.

Raises:



64
65
66
67
68
# File 'lib/crud_components/builder.rb', line 64

def identify_by(column)
  raise DefinitionError, "#{model}: identify_by declared twice" if @identify_by_decl

  @identify_by_decl = column.to_sym
end

#label(method = nil, preload: nil) {|record| ... } ⇒ void

This method returns an undefined value.

How a record is titled (links, headings). Give a method name or a block.

Parameters:

  • method (Symbol, nil) (defaults to: nil)

    a method on the record returning its label.

  • preload (Array<Symbol>, Symbol, nil) (defaults to: nil)

    associations the label reaches into (‘label :full_title, preload: %i[customer training]`). They’re eager-loaded automatically whenever this model is shown as another model’s association column — declare once, no N+1 anywhere.

Yields:

  • (record)

    computes the label; receives the record.

Raises:



42
43
44
45
46
47
48
49
# File 'lib/crud_components/builder.rb', line 42

def label(method = nil, preload: nil, &block)
  raise DefinitionError, "#{model}: label declared twice" if defined?(@label_decl) && @label_decl
  raise DefinitionError, "#{model}: label takes a method name or a block, not both" if method && block
  raise DefinitionError, "#{model}: label needs a method name or a block" unless method || block

  @label_decl = block || method.to_sym
  @label_preload_decl = preload_list(preload)
end

#preload(*names) ⇒ void

This method returns an undefined value.

Associations to eager-load whenever this model is rendered (as a row or as another model’s association cell) — for label/render dependencies the gem can’t infer. Additive with ‘label …, preload:`; declare more than once to accumulate. e.g. `preload :customer, :training`.

Parameters:

  • names (Array<Symbol>)

    association names (nested hashes allowed).



57
58
59
# File 'lib/crud_components/builder.rb', line 57

def preload(*names)
  @preload_decl = (@preload_decl || []) + names
end

#search_in(*spec) {|scope, term| ... } ⇒ void

This method returns an undefined value.

The columns/associations full-text search (‘?q=`) spans, in the same mini-language as the positional `filter` spec.

Parameters:

  • spec (Array<Symbol, Hash>)

    e.g. ‘:title, authors: %i[name email]`.

Yields:

  • (scope, term)

    a custom search; receives the scope and the term.

Raises:



86
87
88
89
90
91
92
# File 'lib/crud_components/builder.rb', line 86

def search_in(*spec, &block)
  raise DefinitionError, "#{model}: search_in declared twice" if @search_decl
  raise DefinitionError, "#{model}: search_in takes a spec or a block, not both" if spec.any? && block
  raise DefinitionError, "#{model}: search_in needs a spec or a block" if spec.empty? && !block

  @search_decl = block || spec
end