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/typed_filter.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, TypedFilter, 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
PRESENT_FILTER_VALUE =

The two non-blank values of an attachment presence filter — its 3-state control (any / present / absent) submits these, and the query turns them into an EXISTS / NOT EXISTS (where.associated / where.missing) over the backing attachment association rather than a value match. See Fields::AttachmentField.

'present'.freeze
ABSENT_FILTER_VALUE =
'absent'.freeze
VERSION =
'0.2.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.



162
163
164
# File 'lib/crud_components.rb', line 162

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

.configObject



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

def config
  @config ||= Config.new
end

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

Yields:



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

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))


139
140
141
# File 'lib/crud_components.rb', line 139

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)


120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/crud_components.rb', line 120

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)


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

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)


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

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



68
69
70
# File 'lib/crud_components.rb', line 68

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 }).



85
86
87
# File 'lib/crud_components.rb', line 85

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