Module: CrudComponents

Defined in:
lib/crud_components.rb,
lib/crud_components.rb,
lib/crud_components/model.rb,
lib/crud_components/query.rb,
lib/crud_components/action.rb,
lib/crud_components/config.rb,
lib/crud_components/engine.rb,
lib/crud_components/errors.rb,
lib/crud_components/markup.rb,
lib/crud_components/builder.rb,
lib/crud_components/helpers.rb,
lib/crud_components/version.rb,
lib/crud_components/fieldset.rb,
lib/crud_components/like_spec.rb,
lib/crud_components/structure.rb,
lib/crud_components/where_like.rb,
lib/crud_components/fields/base.rb,
lib/crud_components/dynamic_column.rb,
lib/crud_components/route_resolver.rb,
lib/crud_components/presenters/base.rb,
lib/crud_components/presenters/form.rb,
lib/crud_components/presenters/cells.rb,
lib/crud_components/fields/date_field.rb,
lib/crud_components/fields/enum_field.rb,
lib/crud_components/fields/json_field.rb,
lib/crud_components/fields/path_field.rb,
lib/crud_components/fields/text_field.rb,
lib/crud_components/presenters/filter.rb,
lib/crud_components/presenters/record.rb,
lib/crud_components/permission_context.rb,
lib/crud_components/presenters/actions.rb,
lib/crud_components/fields/string_field.rb,
lib/crud_components/fields/boolean_field.rb,
lib/crud_components/fields/dynamic_field.rb,
lib/crud_components/fields/numeric_field.rb,
lib/crud_components/fields/computed_field.rb,
lib/crud_components/fields/has_many_field.rb,
lib/crud_components/presenters/collection.rb,
lib/crud_components/fields/attachment_field.rb,
lib/crud_components/fields/belongs_to_field.rb,
lib/crud_components/presenters/cell_context.rb,
lib/crud_components/presenters/column_selection.rb,
lib/generators/crud_components/views/views_generator.rb,
lib/generators/crud_components/install/install_generator.rb

Defined Under Namespace

Modules: Fields, Generators, Helpers, LikeSpec, Markup, Model, Permission, Presenters, RouteResolver, WhereLike Classes: Action, Builder, Config, DefinitionError, DynamicColumn, Engine, Error, Fieldset, PermissionContext, Query, Structure, UnknownFieldsetError

Constant Summary collapse

RESERVED_PARAMS =

The query params the gem owns (filters are top-level params named after the field, so a field can’t share these names). Declaring such an attribute raises in the Builder rather than silently colliding with sort/pagination.

%w[q sort dir page per cols].freeze
NULL_FILTER_VALUE =

Sentinel filter value meaning “the column is NULL” (boolean/enum filters on nullable columns offer it as a “not set” choice). Improbable as a real value, so it never collides with a genuine enum key or boolean string.

'__null__'.freeze
VERSION =
'0.1.0'.freeze

Class Method Summary collapse

Class Method Details

.bundled_cssObject

The gem’s stylesheet (the column-picker float styles), read once from the packaged file. Backs the ‘crud_components_styles` helper, which inlines it; the same file is also linkable via `stylesheet_link_tag “crud_components”` on hosts whose asset pipeline serves engine assets.



154
155
156
# File 'lib/crud_components.rb', line 154

def bundled_css
  @bundled_css ||= File.read(File.expand_path('../app/assets/stylesheets/crud_components.css', __dir__))
end

.configObject



52
53
54
# File 'lib/crud_components.rb', line 52

def config
  @config ||= Config.new
end

.configure {|config| ... } ⇒ Object

Yields:



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

def configure
  yield config
end

.permitted_attributes(model, action: :update, ability: nil) ⇒ Object

The strong-params permit list for a model’s derived form — the same field metadata the form renders from, so the two can’t drift. Use in a controller:

params.require(:book)
      .permit(*CrudComponents.permitted_attributes(Book, action: :update,
                                                    ability: current_ability))


131
132
133
# File 'lib/crud_components.rb', line 131

def permitted_attributes(model, action: :update, ability: nil)
  Structure.for(model).permitted_params(action, PermissionContext.new(ability))
end

.previews_available?Boolean

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.

Whether non-image attachment previews (e.g. a PDF’s first page) can actually be generated here. Beyond a previewer binary (poppler/ffmpeg, which ‘previewable?` already checks), processing needs `image_processing` plus the configured variant backend’s gem (ruby-vips or mini_magick). When any is missing, the renderer shows an icon + filename rather than a preview that would 500 at processing time.

Returns:

  • (Boolean)


112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/crud_components.rb', line 112

def previews_available?
  return @previews_available if defined?(@previews_available)

  @previews_available = begin
    require 'image_processing'
    processor = defined?(ActiveStorage) ? ActiveStorage.variant_processor : :vips
    require(processor.to_s == 'mini_magick' ? 'mini_magick' : 'vips')
    true
  rescue LoadError
    false
  end
end

.selected(scope, params, param: :selected) ⇒ Object

Resolve a bulk-action selection from request params into a relation. The row checkboxes submit ‘selected[]=<identify_by>` (a slug array; a comma string is also accepted).

Pass the same authorized scope you’d render — selection narrows within it, so a tampered slug can never reach a row outside it:

CrudComponents.selected(@books, params).destroy_all   # @books already scoped

A model class also works when you don’t scope (acts on the whole table):

CrudComponents.selected(Book, params)


144
145
146
147
148
# File 'lib/crud_components.rb', line 144

def selected(scope, params, param: :selected)
  model = scope.respond_to?(:klass) ? scope.klass : scope
  values = Array(params[param]).flat_map { |v| v.to_s.split(',') }.map(&:strip).reject(&:blank?)
  scope.where(Structure.for(model).identify_by => values)
end

.selected_columns(params, param_prefix: nil) {|names| ... } ⇒ Object

The column-picker selection from a request’s params: the ordered list of column names the user ticked, or nil when the picker wasn’t submitted. Honors ‘param_prefix:` (match it to the picker’s). Persist it however you like, then feed it back via ‘picked_columns:`.

cols = CrudComponents.selected_columns(params)
current_user.update!(book_columns: cols) if cols

A block runs only when a selection was submitted, and receives the list:

CrudComponents.selected_columns(params) { |cols| current_user.update!(book_columns: cols) }

Accepts both the no-JS ‘cols[]=a&cols=b` array and the comma-joined `cols=a,b` the crud-columns controller submits.

Yields:

  • (names)


95
96
97
98
99
100
101
102
103
# File 'lib/crud_components.rb', line 95

def selected_columns(params, param_prefix: nil)
  key = param_prefix ? "#{param_prefix}_cols" : 'cols'
  raw = params[key] || params[key.to_sym]
  list = raw.is_a?(Array) ? raw : raw.is_a?(String) ? raw.split(',') : nil
  names = list&.map { |n| n.to_s.strip }&.reject(&:blank?)
  names = nil if names.nil? || names.empty?
  yield names if block_given? && names
  names
end

.structure_for(model) ⇒ Object



60
61
62
# File 'lib/crud_components.rb', line 60

def structure_for(model)
  Structure.for(model)
end

.where_like(relation, spec, value) ⇒ Object

Safe case-insensitive contains-match on any relation, using the same escaped-ILIKE machinery as ‘filter like:` / `search_in` — so you never hand-write `where(“col LIKE ?”, “%#value%”)` (which forgets to escape the user’s ‘%`/`_`). The scope handed to a filter/search block already carries `#where_like`; this module function is for the relations you build yourself, e.g. a subquery on another model:

filter: ->(scope, value) {
  ids = CrudComponents.where_like(PropertyValue.where(definition: prop), :value, value)
  scope.where(id: ids.select(:subject_id))
}

‘spec` is a LikeSpec spec (`:value`, `%i[a b]`, `{ assoc: :col }`).



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

def where_like(relation, spec, value)
  LikeSpec.apply(relation, spec, value)
end