Class: ActiveScaffold::DataStructures::Column

Inherits:
Object
  • Object
show all
Includes:
Configurable, OrmChecks
Defined in:
lib/active_scaffold/data_structures/column.rb

Constant Summary collapse

NO_PARAMS =
Set.new.freeze
NO_OPTIONS =
{}.freeze
@@associated_limit =
3
@@associated_number =
true
@@show_blank_record =
true
%i[new edit show]
@@association_form_ui =
nil

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from OrmChecks

active_record?, cast, column_type, columns, columns_hash, content_columns, default_value, mongoid?, quoted_table_name, reflect_on_all_associations, table_name, tableless?, type_for_attribute

Methods included from Configurable

#configure, #method_missing, #respond_to_missing?

Constructor Details

#initialize(name, active_record_class, delegated_association = nil) ⇒ Column

instantiation is handled internally through the DataStructures::Columns object



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
# File 'lib/active_scaffold/data_structures/column.rb', line 386

def initialize(name, active_record_class, delegated_association = nil) #:nodoc:
  @name = name.to_sym
  @active_record_class = active_record_class
  @column = _columns_hash[name.to_s]
  if @column.nil? && active_record? && active_record_class._default_attributes.key?(name.to_s)
    @column = active_record_class._default_attributes[name.to_s]
  end
  @db_default_value = ActiveScaffold::OrmChecks.default_value active_record_class, name if @column
  @delegated_association = delegated_association
  @cache_key = [@active_record_class.name, name].compact.map(&:to_s).join('#')
  setup_association_info

  @link = nil
  @autolink = association.present?
  @table = _table_name
  @associated_limit = self.class.associated_limit
  @associated_number = self.class.associated_number
  @show_blank_record = self.class.show_blank_record
  @send_form_on_update_column = self.class.send_form_on_update_column
  @actions_for_association_links = self.class.actions_for_association_links.dup if association
  @select_columns = default_select_columns

  @text = @column.nil? || [:string, :text, :citext, String].include?(column_type)
  @number = false
  setup_defaults_for_column if @column
  @allow_add_existing = true
  @form_ui = self.class.association_form_ui if @association && self.class.association_form_ui

  self.includes = [association.name] if association&.allow_join?
  if delegated_association
    self.includes = includes ? [delegated_association.name => includes] : [delegated_association.name]
  end

  # default all the configurable variables
  self.css_class = ''
  validators_force_require_on = active_record_class.validators_on(name)
                                                   .map { |val| validator_force_required?(val) }
                                                   .select(&:present?)
  self.required = validators_force_require_on.any? { |opt| opt == true } ||
                  validators_force_require_on.reject { |opt| opt == true }.flatten.presence
  self.sort = true
  self.search_sql = true

  @weight = estimate_weight
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class ActiveScaffold::Configurable

Instance Attribute Details

Returns the value of attribute actions_for_association_links.



336
337
338
# File 'lib/active_scaffold/data_structures/column.rb', line 336

def actions_for_association_links
  @actions_for_association_links
end

#active_record_classObject (readonly) Also known as: model

Returns the value of attribute active_record_class.



8
9
10
# File 'lib/active_scaffold/data_structures/column.rb', line 8

def active_record_class
  @active_record_class
end

#allow_add_existingObject

Whether to enable add_existing for this column



29
30
31
# File 'lib/active_scaffold/data_structures/column.rb', line 29

def allow_add_existing
  @allow_add_existing
end

#associated_limitObject

Returns the value of attribute associated_limit.



310
311
312
# File 'lib/active_scaffold/data_structures/column.rb', line 310

def associated_limit
  @associated_limit
end

#associated_number=(value) ⇒ Object (writeonly)

Sets the attribute associated_number

Parameters:

  • value

    the value to set the attribute associated_number to.



315
316
317
# File 'lib/active_scaffold/data_structures/column.rb', line 315

def associated_number=(value)
  @associated_number = value
end

#associationObject (readonly)

the association from the ActiveRecord class



349
350
351
# File 'lib/active_scaffold/data_structures/column.rb', line 349

def association
  @association
end

#association_join_textObject

what string to use to join records from plural associations



321
322
323
# File 'lib/active_scaffold/data_structures/column.rb', line 321

def association_join_text
  @association_join_text
end

#cache_keyObject (readonly)

cache key to cache column info



383
384
385
# File 'lib/active_scaffold/data_structures/column.rb', line 383

def cache_key
  @cache_key
end

#calculateObject

define a calculation for the column. anything that ActiveRecord::Calculations::ClassMethods#calculate accepts will do.



249
250
251
# File 'lib/active_scaffold/data_structures/column.rb', line 249

def calculate
  @calculate
end

#collapsedObject

Whether this column set is collapsed by default in contexts where collapsing is supported



26
27
28
# File 'lib/active_scaffold/data_structures/column.rb', line 26

def collapsed
  @collapsed
end

#columnObject (readonly)

the ConnectionAdapter::*Column object from the ActiveRecord class



346
347
348
# File 'lib/active_scaffold/data_structures/column.rb', line 346

def column
  @column
end

#css_classObject

this will be /joined/ to the :name for the td’s class attribute. useful if you want to style columns on different ActiveScaffolds the same way, but the columns have different names.



90
91
92
# File 'lib/active_scaffold/data_structures/column.rb', line 90

def css_class
  @css_class
end

#delegated_associationObject (readonly)

the singular association which this column belongs to



352
353
354
# File 'lib/active_scaffold/data_structures/column.rb', line 352

def delegated_association
  @delegated_association
end

#description(record = nil, scope = nil) ⇒ Object



73
74
75
76
77
78
79
80
81
# File 'lib/active_scaffold/data_structures/column.rb', line 73

def description(record = nil, scope = nil)
  if @description&.respond_to?(:call)
    @description.call(record, self, scope)
  elsif @description
    @description
  else
    I18n.t name, :scope => [:activerecord, :description, active_record_class.to_s.underscore.to_sym], :default => ''
  end
end

#form_uiObject

supported options:

* for association columns
  * :select - displays a simple <select> or a collection of checkboxes to (dis)associate records


160
161
162
# File 'lib/active_scaffold/data_structures/column.rb', line 160

def form_ui
  @form_ui
end

#form_ui_optionsObject (readonly)

Returns the value of attribute form_ui_options.



162
163
164
# File 'lib/active_scaffold/data_structures/column.rb', line 162

def form_ui_options
  @form_ui_options
end

#hide_form_column_ifObject

add a custom attr_accessor that can contain a Proc (or boolean or symbol) that will be called when the column renders, such that we can dynamically hide or show the column with an element that can be replaced by update_columns, but won’t affect the form submission. The value can be set in the scaffold controller as follows to dynamically hide the column based on a Proc’s output: config.columns.hide_form_column_if = Proc.new { |record, column, scope| record.vehicle_type == ‘tractor’ } OR to always hide the column: config.columns.hide_form_column_if = true OR to call a method on the record to determine whether to hide the column: config.columns.hide_form_column_if = :hide_tractor_fields?



127
128
129
# File 'lib/active_scaffold/data_structures/column.rb', line 127

def hide_form_column_if
  @hide_form_column_if
end

#includesObject

a collection of associations to pre-load when finding the records on a page



257
258
259
# File 'lib/active_scaffold/data_structures/column.rb', line 257

def includes
  @includes
end

#inplace_editObject

Whether to enable inplace editing for this column. Currently works for text columns, in the List.



15
16
17
# File 'lib/active_scaffold/data_structures/column.rb', line 15

def inplace_edit
  @inplace_edit
end

#inplace_edit_updateObject

:table to refresh list true or :row to refresh row



23
24
25
# File 'lib/active_scaffold/data_structures/column.rb', line 23

def inplace_edit_update
  @inplace_edit_update
end

#label(record = nil, scope = nil) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/active_scaffold/data_structures/column.rb', line 56

def label(record = nil, scope = nil)
  if @label.respond_to?(:call)
    if record
      @label.call(record, self, scope)
    else
      # sometimes label is called without a record in context (ie, from table
      # headers).  In this case fall back to the humanized attribute name
      # instead of the Proc
      active_record_class.human_attribute_name(name.to_s)
    end
  else
    as_(@label) || active_record_class.human_attribute_name(name.to_s)
  end
end

#nameObject (readonly)

this is the name of the getter on the ActiveRecord model. it is the only absolutely required attribute … all others will be inferred from this name.



12
13
14
# File 'lib/active_scaffold/data_structures/column.rb', line 12

def name
  @name
end

#number=(value) ⇒ Object (writeonly)

Sets the attribute number

Parameters:

  • value

    the value to set the attribute number to.



359
360
361
# File 'lib/active_scaffold/data_structures/column.rb', line 359

def number=(value)
  @number = value
end

#optionsObject



214
215
216
217
# File 'lib/active_scaffold/data_structures/column.rb', line 214

def options
  return @options || NO_OPTIONS if frozen?
  @options ||= NO_OPTIONS.dup
end

#placeholderObject



85
86
87
# File 'lib/active_scaffold/data_structures/column.rb', line 85

def placeholder
  @placeholder || I18n.t(name, :scope => [:activerecord, :placeholder, active_record_class.to_s.underscore.to_sym], :default => '')
end

#required=(value) ⇒ Object (writeonly)

whether the field is required or not. used on the form for visually indicating the fact to the user. TODO: move into predicate



94
95
96
# File 'lib/active_scaffold/data_structures/column.rb', line 94

def required=(value)
  @required = value
end

#select_associated_columnsObject

a collection of columns to load when eager loading is disabled, if it’s nil all columns will be loaded



280
281
282
# File 'lib/active_scaffold/data_structures/column.rb', line 280

def select_associated_columns
  @select_associated_columns
end

#select_columnsObject

What columns load from main table



32
33
34
# File 'lib/active_scaffold/data_structures/column.rb', line 32

def select_columns
  @select_columns
end

#send_form_on_update_columnObject

Returns the value of attribute send_form_on_update_column.



114
115
116
# File 'lib/active_scaffold/data_structures/column.rb', line 114

def send_form_on_update_column
  @send_form_on_update_column
end

#show_blank_record=(value) ⇒ Object (writeonly)

Sets the attribute show_blank_record

Parameters:

  • value

    the value to set the attribute show_blank_record to.



326
327
328
# File 'lib/active_scaffold/data_structures/column.rb', line 326

def show_blank_record=(value)
  @show_blank_record = value
end

#update_columnsObject

Returns the value of attribute update_columns.



103
104
105
# File 'lib/active_scaffold/data_structures/column.rb', line 103

def update_columns
  @update_columns
end

#weightObject

to modify the default order of columns



305
306
307
# File 'lib/active_scaffold/data_structures/column.rb', line 305

def weight
  @weight
end

Instance Method Details

#<=>(other) ⇒ Object



438
439
440
441
# File 'lib/active_scaffold/data_structures/column.rb', line 438

def <=>(other)
  order_weight = weight <=> other.weight
  order_weight.nonzero? ? order_weight : name.to_s <=> other.name.to_s
end

#==(other) ⇒ Object

this is so that array.delete and array.include?, etc., will work by column name



369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/active_scaffold/data_structures/column.rb', line 369

def ==(other) #:nodoc:
  # another column
  if other.respond_to?(:name) && other.class == self.class
    name == other.name.to_sym
  elsif other.is_a? Symbol
    name == other
  elsif other.is_a? String
    name.to_s == other # avoid creating new symbols
  else # unknown
    eql? other
  end
end

#associated_number?Boolean

Returns:

  • (Boolean)


316
317
318
# File 'lib/active_scaffold/data_structures/column.rb', line 316

def associated_number?
  @associated_number
end

#attributes=(opts) ⇒ Object



504
505
506
507
508
# File 'lib/active_scaffold/data_structures/column.rb', line 504

def attributes=(opts)
  opts.each do |setting, value|
    send "#{setting}=", value
  end
end

#autolink?Boolean

set an action_link to nested list or inline form in this column

Returns:

  • (Boolean)


238
239
240
# File 'lib/active_scaffold/data_structures/column.rb', line 238

def autolink?
  @autolink
end

#calculation?Boolean

get whether to run a calculation on this column

Returns:

  • (Boolean)


252
253
254
# File 'lib/active_scaffold/data_structures/column.rb', line 252

def calculation?
  !(@calculate == false || @calculate.nil?)
end

#cast(value) ⇒ Object



510
511
512
# File 'lib/active_scaffold/data_structures/column.rb', line 510

def cast(value)
  ActiveScaffold::OrmChecks.cast active_record_class, name, value
end

this should not only delete any existing link but also prevent column links from being automatically added by later routines



243
244
245
246
# File 'lib/active_scaffold/data_structures/column.rb', line 243

def clear_link
  @link = nil
  @autolink = false
end

#column_typeObject



500
501
502
# File 'lib/active_scaffold/data_structures/column.rb', line 500

def column_type
  ActiveScaffold::OrmChecks.column_type active_record_class, name
end

#convert_to_native?Boolean

Returns:

  • (Boolean)


443
444
445
# File 'lib/active_scaffold/data_structures/column.rb', line 443

def convert_to_native?
  number? && options[:format] && form_ui != :number
end

#default_for_empty_valueObject



468
469
470
471
472
473
474
475
476
477
# File 'lib/active_scaffold/data_structures/column.rb', line 468

def default_for_empty_value
  return nil unless column
  if column.is_a?(ActiveModel::Attribute)
    column.value
  elsif active_record? && null?
    nil
  else
    @db_default_value
  end
end

#default_valueObject



40
41
42
# File 'lib/active_scaffold/data_structures/column.rb', line 40

def default_value
  @default_value || @db_default_value
end

#default_value=(value) ⇒ Object

Raises:

  • (ArgumentError)


44
45
46
47
# File 'lib/active_scaffold/data_structures/column.rb', line 44

def default_value=(value)
  raise ArgumentError, "Can't set default value for non-DB columns (virtual columns or associations)" unless column
  @default_value = value
end

#default_value?Boolean

Returns:

  • (Boolean)


49
50
51
# File 'lib/active_scaffold/data_structures/column.rb', line 49

def default_value?
  defined? @default_value
end

#fieldObject

the table.field name for this column, if applicable



488
489
490
# File 'lib/active_scaffold/data_structures/column.rb', line 488

def field
  @field ||= quoted_field(field_name)
end

#field_nameObject

just the field (not table.field)



433
434
435
436
# File 'lib/active_scaffold/data_structures/column.rb', line 433

def field_name
  return nil if virtual?
  @field_name ||= column ? quoted_field_name(column.name) : quoted_field_name(association.foreign_key)
end


219
220
221
222
223
# File 'lib/active_scaffold/data_structures/column.rb', line 219

def link
  return @link.call(self) if frozen? && @link.is_a?(Proc)
  @link = @link.call(self) if @link.is_a? Proc
  @link
end

#list_uiObject



176
177
178
# File 'lib/active_scaffold/data_structures/column.rb', line 176

def list_ui
  @list_ui || form_ui
end

#list_ui=(value) ⇒ Object

value must be a Symbol, or an Array of list_ui and options hash which will be used with list_ui only



171
172
173
174
# File 'lib/active_scaffold/data_structures/column.rb', line 171

def list_ui=(value)
  check_valid_action_ui_params(value)
  @list_ui, @list_ui_options = *value
end

#list_ui_optionsObject



180
181
182
# File 'lib/active_scaffold/data_structures/column.rb', line 180

def list_ui_options
  @list_ui ? @list_ui_options : form_ui_options
end

#null?Boolean

Returns:

  • (Boolean)


479
480
481
482
483
484
485
# File 'lib/active_scaffold/data_structures/column.rb', line 479

def null?
  if active_record? && !column.is_a?(ActiveModel::Attribute)
    column&.null
  else
    true
  end
end

#number?Boolean

Returns:

  • (Boolean)


360
361
362
# File 'lib/active_scaffold/data_structures/column.rb', line 360

def number?
  @number
end

#number_to_native(value) ⇒ Object



447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/active_scaffold/data_structures/column.rb', line 447

def number_to_native(value)
  return value if value.blank? || !value.is_a?(String)
  native = '.' # native ruby separator
  format = {:separator => '', :delimiter => ''}.merge! I18n.t('number.format', :default => {})
  specific =
    case options[:format]
    when :currency
      I18n.t('number.currency.format', :default => nil)
    when :size
      I18n.t('number.human.format', :default => nil)
    when :percentage
      I18n.t('number.percentage.format', :default => nil)
    end
  format.merge! specific unless specific.nil?
  if format[:separator].blank? || !value.include?(format[:separator]) && value.include?(native) && (format[:delimiter] != native || value !~ /\.\d{3}$/)
    value
  else
    value.gsub(/[^0-9\-#{format[:separator]}]/, '').gsub(format[:separator], native)
  end
end

#paramsObject

Any extra parameters this particular column uses. This is for create/update purposes.



35
36
37
38
# File 'lib/active_scaffold/data_structures/column.rb', line 35

def params
  return @params || NO_PARAMS if frozen?
  @params ||= NO_PARAMS.dup
end

#quoted_foreign_typeObject



492
493
494
# File 'lib/active_scaffold/data_structures/column.rb', line 492

def quoted_foreign_type
  quoted_field(quoted_field_name(association.foreign_type))
end

#required?(action = nil) ⇒ Boolean

Returns:

  • (Boolean)


95
96
97
98
99
100
101
# File 'lib/active_scaffold/data_structures/column.rb', line 95

def required?(action = nil)
  if action && @required
    @required == true || @required.include?(action)
  else
    @required
  end
end

#search_joinsObject

a collection of associations to do left join when this column is included on search



267
268
269
# File 'lib/active_scaffold/data_structures/column.rb', line 267

def search_joins
  @search_joins || @includes
end

#search_joins=(value) ⇒ Object



271
272
273
274
275
276
277
# File 'lib/active_scaffold/data_structures/column.rb', line 271

def search_joins=(value)
  @search_joins =
    case value
    when Array then value
    else [value] # automatically convert to an array
    end
end

#search_sqlObject



295
296
297
298
# File 'lib/active_scaffold/data_structures/column.rb', line 295

def search_sql
  initialize_search_sql if @search_sql == true
  @search_sql
end

#search_sql=(value) ⇒ Object

describes how to search on a column

search = true           default, uses intelligent search sql
search = "CONCAT(a, b)" define your own sql for searching. this should be the "left-side" of a WHERE condition. the operator and value will be supplied by ActiveScaffold.
search = [:a, :b]       searches in both fields


286
287
288
289
290
291
292
293
# File 'lib/active_scaffold/data_structures/column.rb', line 286

def search_sql=(value)
  @search_sql =
    if value
      value == true || value.is_a?(Proc) ? value : Array(value)
    else
      value
    end
end

#search_uiObject



204
205
206
# File 'lib/active_scaffold/data_structures/column.rb', line 204

def search_ui
  @search_ui || @form_ui || (:select if association && !association.polymorphic?)
end

#search_ui=(value) ⇒ Object

value must be a Symbol, or an Array of search_ui and options hash which will be used with search_ui only



199
200
201
202
# File 'lib/active_scaffold/data_structures/column.rb', line 199

def search_ui=(value)
  check_valid_action_ui_params(value)
  @search_ui, @search_ui_options = *value
end

#search_ui_optionsObject



208
209
210
# File 'lib/active_scaffold/data_structures/column.rb', line 208

def search_ui_options
  @search_ui ? @search_ui_options : form_ui_options
end

#searchable?Boolean

Returns:

  • (Boolean)


300
301
302
# File 'lib/active_scaffold/data_structures/column.rb', line 300

def searchable?
  search_sql.present?
end

associate an action_link with this column



226
227
228
229
230
231
232
233
234
235
# File 'lib/active_scaffold/data_structures/column.rb', line 226

def set_link(action, options = {})
  if action.is_a?(ActiveScaffold::DataStructures::ActionLink) || (action.is_a? Proc)
    @link = action
  else
    options[:label] ||= label
    options[:position] ||= :after unless options.key?(:position)
    options[:type] ||= :member
    @link = ActiveScaffold::DataStructures::ActionLink.new(action, options)
  end
end

#show_blank_record?(associated) ⇒ Boolean

Returns:

  • (Boolean)


327
328
329
330
331
# File 'lib/active_scaffold/data_structures/column.rb', line 327

def show_blank_record?(associated)
  return false unless @show_blank_record
  return false unless association.klass.authorized_for?(:crud_type => :create) && !association.readonly?
  association.collection? || (association.singular? && associated.blank?)
end

#show_uiObject



190
191
192
# File 'lib/active_scaffold/data_structures/column.rb', line 190

def show_ui
  @show_ui || list_ui
end

#show_ui=(value) ⇒ Object

value must be a Symbol, or an Array of show_ui and options hash which will be used with show_ui only



185
186
187
188
# File 'lib/active_scaffold/data_structures/column.rb', line 185

def show_ui=(value)
  check_valid_action_ui_params(value)
  @show_ui, @show_ui_options = *value
end

#show_ui_optionsObject



194
195
196
# File 'lib/active_scaffold/data_structures/column.rb', line 194

def show_ui_options
  @show_ui ? @show_ui_options : list_ui_options
end

#sortObject



143
144
145
146
# File 'lib/active_scaffold/data_structures/column.rb', line 143

def sort
  initialize_sort if @sort == true
  @sort
end

#sort=(value) ⇒ Object

sorting on a column can be configured four ways:

sort = true               default, uses intelligent sorting sql default
sort = false              sometimes sorting doesn't make sense
sort = {:sql => ""}       define your own sql for sorting. this should be result in a sortable value in SQL. ActiveScaffold will handle the ascending/descending.
sort = {:method => ""}    define ruby-side code for sorting. this is SLOW with large recordsets!


134
135
136
137
138
139
140
141
# File 'lib/active_scaffold/data_structures/column.rb', line 134

def sort=(value)
  if value.is_a? Hash
    value.assert_valid_keys(:sql, :method)
    @sort = value
  else
    @sort = value ? true : false # force true or false
  end
end

#sort_by(options) ⇒ Object

a configuration helper for the self.sort property. simply provides a method syntax instead of setter syntax.



153
154
155
# File 'lib/active_scaffold/data_structures/column.rb', line 153

def sort_by(options)
  self.sort = options
end

#sortable?Boolean

Returns:

  • (Boolean)


148
149
150
# File 'lib/active_scaffold/data_structures/column.rb', line 148

def sortable?
  sort != false && !sort.nil?
end

#text?Boolean

Returns:

  • (Boolean)


364
365
366
# File 'lib/active_scaffold/data_structures/column.rb', line 364

def text?
  @text
end

#type_for_attributeObject



496
497
498
# File 'lib/active_scaffold/data_structures/column.rb', line 496

def type_for_attribute
  ActiveScaffold::OrmChecks.type_for_attribute active_record_class, name
end

#virtual?Boolean

an interpreted property. the column is virtual if it isn’t from the active record model or any associated models

Returns:

  • (Boolean)


355
356
357
# File 'lib/active_scaffold/data_structures/column.rb', line 355

def virtual?
  column.nil? && association.nil?
end