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.



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

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

#admin_declObject (readonly)

Returns the value of attribute admin_decl.



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

def admin_decl
  @admin_decl
end

#app_path_declObject (readonly)

Returns the value of attribute app_path_decl.



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

def app_path_decl
  @app_path_decl
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:



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

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

#admin(enabled = true, **options) ⇒ void

This method returns an undefined value.

How this model appears in the mounted admin UI (inert without it).

Parameters:

  • enabled (Boolean) (defaults to: true)

    false keeps the model out of the admin entirely.

  • options (Hash)

    actions: (a subset of %i[index show new create edit update destroy]), group:, label:, fieldset:, scope: (a callable narrowing the base relation).

Raises:



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/crud_components/builder.rb', line 174

def admin(enabled = true, **options)
  raise DefinitionError, "#{model}: admin declared twice" if defined?(@admin_decl) && !@admin_decl.nil?

  unless [true, false].include?(enabled)
    raise DefinitionError, "#{model}: admin takes true or false, got #{enabled.inspect}"
  end

  if enabled == false && options.any?
    raise DefinitionError, "#{model}: `admin false` takes no options — #{options.keys.map(&:inspect).join(', ')} " \
                           'would never apply'
  end

  validate_admin_options!(options)
  @admin_decl = enabled ? options : false
end

#app_path {|record| ... } ⇒ void

This method returns an undefined value.

The host application's own page for a record, when it is not the conventional route (main_app.book_path(book)). Runs in the view context; return nil to suppress the link for a record. app_path { |book| main_app.publisher_book_path(book.publisher, book) }

Yields:

  • (record)

    the record to link to.

Raises:



161
162
163
164
165
166
# File 'lib/crud_components/builder.rb', line 161

def app_path(&block)
  raise DefinitionError, "#{model}: app_path requires a block" unless block
  raise DefinitionError, "#{model}: app_path declared twice" if defined?(@app_path_decl) && @app_path_decl

  @app_path_decl = 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.



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

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:



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

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:



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

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:



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

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:



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

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 :display_title, preload: %i[publisher authors]). 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:



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

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 :publisher, :authors.

Parameters:

  • names (Array<Symbol>)

    association names (nested hashes allowed).



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

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:



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

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