active_scaffold 3.6.20 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.rdoc +27 -0
- data/README.md +27 -16
- data/app/assets/javascripts/jquery/active_scaffold.js +38 -6
- data/app/assets/javascripts/jquery/active_scaffold_chosen.js +6 -5
- data/app/assets/javascripts/jquery/tiny_mce_bridge.js +18 -4
- data/app/assets/stylesheets/active_scaffold_layout.css +12 -1
- data/app/views/active_scaffold_overrides/_base_form.html.erb +5 -1
- data/app/views/active_scaffold_overrides/_field_search.html.erb +1 -0
- data/app/views/active_scaffold_overrides/_render_field.js.erb +19 -11
- data/config/locales/ja.yml +59 -59
- data/lib/active_scaffold/actions/common_search.rb +2 -2
- data/lib/active_scaffold/actions/core.rb +30 -10
- data/lib/active_scaffold/actions/field_search.rb +9 -6
- data/lib/active_scaffold/actions/nested.rb +7 -7
- data/lib/active_scaffold/attribute_params.rb +19 -57
- data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +0 -3
- data/lib/active_scaffold/bridges/active_storage/list_ui.rb +1 -1
- data/lib/active_scaffold/bridges/active_storage.rb +3 -0
- data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +2 -2
- data/lib/active_scaffold/bridges/date_picker/helper.rb +4 -4
- data/lib/active_scaffold/bridges/paper_trail/actions.rb +4 -1
- data/lib/active_scaffold/bridges/record_select/helpers.rb +2 -2
- data/lib/active_scaffold/bridges/tiny_mce.rb +1 -1
- data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +1 -6
- data/lib/active_scaffold/config/core.rb +1 -1
- data/lib/active_scaffold/config/field_search.rb +9 -1
- data/lib/active_scaffold/config/form.rb +9 -1
- data/lib/active_scaffold/core.rb +2 -8
- data/lib/active_scaffold/data_structures/action_columns.rb +0 -25
- data/lib/active_scaffold/data_structures/action_links.rb +1 -1
- data/lib/active_scaffold/data_structures/association/abstract.rb +8 -0
- data/lib/active_scaffold/data_structures/association/active_mongoid.rb +8 -0
- data/lib/active_scaffold/data_structures/association/active_record.rb +1 -13
- data/lib/active_scaffold/data_structures/association/mongoid.rb +21 -8
- data/lib/active_scaffold/data_structures/column.rb +31 -5
- data/lib/active_scaffold/data_structures/columns.rb +12 -12
- data/lib/active_scaffold/data_structures/nested_info.rb +12 -0
- data/lib/active_scaffold/data_structures/sorting.rb +1 -1
- data/lib/active_scaffold/engine.rb +0 -1
- data/lib/active_scaffold/extensions/action_view_rendering.rb +13 -5
- data/lib/active_scaffold/extensions/cow_proxy.rb +1 -1
- data/lib/active_scaffold/extensions/unsaved_record.rb +9 -3
- data/lib/active_scaffold/finder.rb +5 -1
- data/lib/active_scaffold/helpers/action_link_helpers.rb +1 -1
- data/lib/active_scaffold/helpers/form_column_helpers.rb +48 -22
- data/lib/active_scaffold/helpers/list_column_helpers.rb +3 -2
- data/lib/active_scaffold/helpers/search_column_helpers.rb +8 -2
- data/lib/active_scaffold/helpers/view_helpers.rb +1 -1
- data/lib/active_scaffold/registry.rb +10 -15
- data/lib/active_scaffold/tableless.rb +10 -79
- data/lib/active_scaffold/version.rb +2 -2
- data/lib/active_scaffold.rb +0 -7
- data/lib/generators/active_scaffold/install_generator.rb +2 -2
- data/test/bridges/bridge_test.rb +1 -1
- data/test/bridges/paperclip_test.rb +16 -13
- data/test/bridges/tiny_mce_test.rb +1 -1
- data/test/config/base_test.rb +1 -1
- data/test/config/core_test.rb +1 -1
- data/test/config/create_test.rb +1 -1
- data/test/config/delete_test.rb +1 -1
- data/test/config/field_search_test.rb +1 -1
- data/test/config/list_test.rb +1 -1
- data/test/config/nested_test.rb +1 -1
- data/test/config/search_test.rb +1 -1
- data/test/config/show_test.rb +1 -1
- data/test/config/subform_test.rb +1 -1
- data/test/config/update_test.rb +1 -1
- data/test/data_structures/action_columns_test.rb +1 -1
- data/test/data_structures/action_link_test.rb +1 -1
- data/test/data_structures/action_links_test.rb +1 -1
- data/test/data_structures/actions_test.rb +1 -1
- data/test/data_structures/association_column_test.rb +1 -1
- data/test/data_structures/column_test.rb +1 -1
- data/test/data_structures/columns_test.rb +1 -1
- data/test/data_structures/set_test.rb +1 -1
- data/test/data_structures/sorting_test.rb +1 -1
- data/test/data_structures/standard_column_test.rb +1 -1
- data/test/data_structures/validation_reflection_test.rb +1 -1
- data/test/data_structures/virtual_column_test.rb +1 -1
- data/test/extensions/active_record_test.rb +1 -1
- data/test/helpers/pagination_helpers_test.rb +1 -1
- data/test/misc/active_record_permissions_test.rb +1 -1
- data/test/misc/attribute_params_test.rb +1 -1
- data/test/misc/calculation_test.rb +1 -1
- data/test/misc/configurable_test.rb +1 -1
- data/test/misc/constraints_test.rb +1 -1
- data/test/misc/convert_numbers_format_test.rb +1 -1
- data/test/misc/finder_test.rb +1 -1
- data/test/misc/lang_test.rb +1 -1
- data/test/misc/parse_datetime_test.rb +1 -1
- data/test/misc/tableless_test.rb +1 -1
- data/test/test_helper.rb +4 -4
- metadata +5 -13
- data/lib/active_scaffold/delayed_setup.rb +0 -41
- data/lib/active_scaffold/extensions/left_outer_joins.rb +0 -43
|
@@ -2,15 +2,28 @@ module ActiveScaffold::DataStructures::Association
|
|
|
2
2
|
class Mongoid < Abstract
|
|
3
3
|
delegate :inverse_klass, :as, :dependent, :inverse, to: :@association
|
|
4
4
|
|
|
5
|
+
def belongs_to?
|
|
6
|
+
# once Ruby 2.6 support is dropped, use macro_mapping? always
|
|
7
|
+
defined?(::Mongoid::Association) ? macro_mapping?(:belongs_to) : super
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def has_one? # rubocop:disable Naming/PredicateName
|
|
11
|
+
defined?(::Mongoid::Association) ? macro_mapping?(:has_one) : super
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def has_many? # rubocop:disable Naming/PredicateName
|
|
15
|
+
defined?(::Mongoid::Association) ? macro_mapping?(:has_many) : super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def habtm?
|
|
19
|
+
defined?(::Mongoid::Association) ? macro_mapping?(:has_and_belongs_to_many) : super
|
|
20
|
+
end
|
|
21
|
+
|
|
5
22
|
# polymorphic belongs_to
|
|
6
23
|
def polymorphic?
|
|
7
24
|
belongs_to? && @association.polymorphic?
|
|
8
25
|
end
|
|
9
26
|
|
|
10
|
-
def primary_key
|
|
11
|
-
@association[:primary_key]
|
|
12
|
-
end
|
|
13
|
-
|
|
14
27
|
def association_primary_key
|
|
15
28
|
@association.primary_key
|
|
16
29
|
end
|
|
@@ -19,10 +32,6 @@ module ActiveScaffold::DataStructures::Association
|
|
|
19
32
|
@association.type
|
|
20
33
|
end
|
|
21
34
|
|
|
22
|
-
def counter_cache
|
|
23
|
-
@association[:counter_cache]
|
|
24
|
-
end
|
|
25
|
-
|
|
26
35
|
def table_name
|
|
27
36
|
@association.klass.collection.name
|
|
28
37
|
end
|
|
@@ -38,5 +47,9 @@ module ActiveScaffold::DataStructures::Association
|
|
|
38
47
|
def self.reflect_on_all_associations(klass)
|
|
39
48
|
klass.relations.values
|
|
40
49
|
end
|
|
50
|
+
|
|
51
|
+
def macro_mapping?(macro)
|
|
52
|
+
@association.is_a? ::Mongoid::Association::MACRO_MAPPING[macro]
|
|
53
|
+
end
|
|
41
54
|
end
|
|
42
55
|
end
|
|
@@ -40,14 +40,27 @@ module ActiveScaffold::DataStructures
|
|
|
40
40
|
# the display-name of the column. this will be used, for instance, as the column title in the table and as the field name in the form.
|
|
41
41
|
# if left alone it will utilize human_attribute_name which includes localization
|
|
42
42
|
attr_writer :label
|
|
43
|
-
def label
|
|
44
|
-
|
|
43
|
+
def label(record = nil, scope = nil)
|
|
44
|
+
if @label.respond_to?(:call)
|
|
45
|
+
if record
|
|
46
|
+
@label.call(record, self, scope)
|
|
47
|
+
else
|
|
48
|
+
# sometimes label is called without a record in context (ie, from table
|
|
49
|
+
# headers). In this case fall back to the humanized attribute name
|
|
50
|
+
# instead of the Proc
|
|
51
|
+
active_record_class.human_attribute_name(name.to_s)
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
as_(@label) || active_record_class.human_attribute_name(name.to_s)
|
|
55
|
+
end
|
|
45
56
|
end
|
|
46
57
|
|
|
47
58
|
# a textual description of the column and its contents. this will be displayed with any associated form input widget, so you may want to consider adding a content example.
|
|
48
59
|
attr_writer :description
|
|
49
|
-
def description
|
|
50
|
-
if @description
|
|
60
|
+
def description(record = nil, scope = nil)
|
|
61
|
+
if @description&.respond_to?(:call)
|
|
62
|
+
@description.call(record, self, scope)
|
|
63
|
+
elsif @description
|
|
51
64
|
@description
|
|
52
65
|
else
|
|
53
66
|
I18n.t name, :scope => [:activerecord, :description, active_record_class.to_s.underscore.to_sym], :default => ''
|
|
@@ -87,6 +100,19 @@ module ActiveScaffold::DataStructures
|
|
|
87
100
|
cattr_accessor :send_form_on_update_column, instance_accessor: false
|
|
88
101
|
attr_accessor :send_form_on_update_column
|
|
89
102
|
|
|
103
|
+
# add a custom attr_accessor that can contain a Proc (or boolean or symbol)
|
|
104
|
+
# that will be called when the column renders, such that we can dynamically
|
|
105
|
+
# hide or show the column with an element that can be replaced by
|
|
106
|
+
# update_columns, but won't affect the form submission.
|
|
107
|
+
# The value can be set in the scaffold controller as follows to dynamically
|
|
108
|
+
# hide the column based on a Proc's output:
|
|
109
|
+
# config.columns[:my_column].hide_form_column_if = Proc.new { |record, column, scope| record.vehicle_type == 'tractor' }
|
|
110
|
+
# OR to always hide the column:
|
|
111
|
+
# config.columns[:my_column].hide_form_column_if = true
|
|
112
|
+
# OR to call a method on the record to determine whether to hide the column:
|
|
113
|
+
# config.columns[:my_column].hide_form_column_if = :hide_tractor_fields?
|
|
114
|
+
attr_accessor :hide_form_column_if
|
|
115
|
+
|
|
90
116
|
# sorting on a column can be configured four ways:
|
|
91
117
|
# sort = true default, uses intelligent sorting sql default
|
|
92
118
|
# sort = false sometimes sorting doesn't make sense
|
|
@@ -328,7 +354,7 @@ module ActiveScaffold::DataStructures
|
|
|
328
354
|
@actions_for_association_links = self.class.actions_for_association_links.dup if association
|
|
329
355
|
@select_columns = default_select_columns
|
|
330
356
|
|
|
331
|
-
@text = @column.nil? || [:string, :text, String].include?(column_type)
|
|
357
|
+
@text = @column.nil? || [:string, :text, :citext, String].include?(column_type)
|
|
332
358
|
@number = false
|
|
333
359
|
if @column
|
|
334
360
|
if active_record_class.respond_to?(:defined_enums) && active_record_class.defined_enums[name.to_s]
|
|
@@ -13,7 +13,7 @@ module ActiveScaffold::DataStructures
|
|
|
13
13
|
# IT IS NOT MEANT FOR PUBLIC USE (but if you know what you're doing, go ahead)
|
|
14
14
|
def _inheritable=(value)
|
|
15
15
|
@sorted = true
|
|
16
|
-
@_inheritable = value
|
|
16
|
+
@_inheritable = ::Set.new(value)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# This accessor is used by ActionColumns to create new Column objects without adding them to this set
|
|
@@ -21,8 +21,8 @@ module ActiveScaffold::DataStructures
|
|
|
21
21
|
|
|
22
22
|
def initialize(active_record_class, *args)
|
|
23
23
|
@active_record_class = active_record_class
|
|
24
|
-
@_inheritable =
|
|
25
|
-
@set =
|
|
24
|
+
@_inheritable = ::Set.new
|
|
25
|
+
@set = {}
|
|
26
26
|
@sorted = nil
|
|
27
27
|
|
|
28
28
|
add(*args)
|
|
@@ -35,9 +35,11 @@ module ActiveScaffold::DataStructures
|
|
|
35
35
|
args = args.collect(&:to_sym)
|
|
36
36
|
|
|
37
37
|
# make the columns inheritable
|
|
38
|
-
@_inheritable.
|
|
38
|
+
@_inheritable.merge(args)
|
|
39
39
|
# then add columns to @set (unless they already exist)
|
|
40
|
-
args.each
|
|
40
|
+
args.each do |a|
|
|
41
|
+
@set[a.to_sym] = ActiveScaffold::DataStructures::Column.new(a, @active_record_class) unless find_by_name(a)
|
|
42
|
+
end
|
|
41
43
|
end
|
|
42
44
|
alias << add
|
|
43
45
|
|
|
@@ -55,7 +57,7 @@ module ActiveScaffold::DataStructures
|
|
|
55
57
|
klass = column.association.klass
|
|
56
58
|
columns.each do |col|
|
|
57
59
|
next if find_by_name col
|
|
58
|
-
@set
|
|
60
|
+
@set[col.to_sym] = ActiveScaffold::DataStructures::Column.new(col, klass, column.association)
|
|
59
61
|
end
|
|
60
62
|
end
|
|
61
63
|
|
|
@@ -66,24 +68,22 @@ module ActiveScaffold::DataStructures
|
|
|
66
68
|
|
|
67
69
|
# returns an array of columns with the provided names
|
|
68
70
|
def find_by_names(*names)
|
|
69
|
-
|
|
71
|
+
names.map { |name| find_by_name name }
|
|
70
72
|
end
|
|
71
73
|
|
|
72
74
|
# returns the column of the given name.
|
|
73
75
|
def find_by_name(name)
|
|
74
|
-
|
|
75
|
-
column = @set.find { |c| c == name }
|
|
76
|
-
column
|
|
76
|
+
@set[name.to_sym]
|
|
77
77
|
end
|
|
78
78
|
alias [] find_by_name
|
|
79
79
|
|
|
80
80
|
def each
|
|
81
|
-
@set.
|
|
81
|
+
@set.each_value { |name| yield name }
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
def _inheritable
|
|
85
85
|
if @sorted
|
|
86
|
-
@_inheritable
|
|
86
|
+
@_inheritable.to_a
|
|
87
87
|
else
|
|
88
88
|
@_inheritable.sort do |a, b|
|
|
89
89
|
self[a] <=> self[b]
|
|
@@ -70,6 +70,10 @@ module ActiveScaffold::DataStructures
|
|
|
70
70
|
def match_model?(model)
|
|
71
71
|
false
|
|
72
72
|
end
|
|
73
|
+
|
|
74
|
+
def create_with_parent?
|
|
75
|
+
false
|
|
76
|
+
end
|
|
73
77
|
end
|
|
74
78
|
|
|
75
79
|
class NestedInfoAssociation < NestedInfo
|
|
@@ -104,6 +108,14 @@ module ActiveScaffold::DataStructures
|
|
|
104
108
|
association.through_singular? && source_reflection.reverse
|
|
105
109
|
end
|
|
106
110
|
|
|
111
|
+
def create_with_parent?
|
|
112
|
+
if has_many? && !association.through?
|
|
113
|
+
false
|
|
114
|
+
elsif child_association || create_through_singular?
|
|
115
|
+
true
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
107
119
|
def source_reflection
|
|
108
120
|
@source_reflection ||= ActiveScaffold::DataStructures::Association::ActiveRecord.new(association.source_reflection)
|
|
109
121
|
end
|
|
@@ -41,7 +41,7 @@ module ActiveScaffold::DataStructures
|
|
|
41
41
|
column = get_column(column_name)
|
|
42
42
|
raise ArgumentError, "Could not find column #{column_name}" if column.nil?
|
|
43
43
|
raise ArgumentError, 'Sorting direction unknown' unless %i[ASC DESC].include? direction.to_sym
|
|
44
|
-
@clauses << [column, direction
|
|
44
|
+
@clauses << [column, direction] if column.sortable?
|
|
45
45
|
raise ArgumentError, "Can't mix :method- and :sql-based sorting" if mixed_sorting?
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -3,7 +3,6 @@ module ActiveScaffold
|
|
|
3
3
|
initializer 'active_scaffold.action_controller' do
|
|
4
4
|
ActiveSupport.on_load :action_controller do
|
|
5
5
|
include ActiveScaffold::Core
|
|
6
|
-
include ActiveScaffold::DelayedSetup if ActiveScaffold.delayed_setup
|
|
7
6
|
include ActiveScaffold::RespondsToParent
|
|
8
7
|
include ActiveScaffold::Helpers::ControllerHelpers
|
|
9
8
|
include ActiveScaffold::ActiveRecordPermissions::ModelUserAccess::Controller
|
|
@@ -119,6 +119,17 @@ module ActiveScaffold #:nodoc:
|
|
|
119
119
|
end
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
+
def remote_controller_config(controller_path)
|
|
123
|
+
# attempt to retrieve the active_scaffold_config by constantizing the controller path
|
|
124
|
+
"#{controller_path}_controller".camelize.constantize.active_scaffold_config
|
|
125
|
+
rescue NameError
|
|
126
|
+
# if we couldn't determine the controller config by instantiating the
|
|
127
|
+
# controller class, parse the ActiveRecord model name from the
|
|
128
|
+
# controller path, which might be a namespaced controller (e.g., 'admin/admins')
|
|
129
|
+
model = controller_path.to_s.sub(%r{.*/}, '').singularize
|
|
130
|
+
active_scaffold_config_for(model)
|
|
131
|
+
end
|
|
132
|
+
|
|
122
133
|
def render_embedded(options)
|
|
123
134
|
require 'digest/md5'
|
|
124
135
|
|
|
@@ -144,13 +155,10 @@ module ActiveScaffold #:nodoc:
|
|
|
144
155
|
else
|
|
145
156
|
url = url_for(url_options)
|
|
146
157
|
content_tag(:div, :id => id, :class => 'active-scaffold-component', :data => {:refresh => url}) do
|
|
147
|
-
# parse the ActiveRecord model name from the controller path, which
|
|
148
|
-
# might be a namespaced controller (e.g., 'admin/admins')
|
|
149
|
-
model = remote_controller.to_s.sub(%r{.*/}, '').singularize
|
|
150
158
|
content_tag(:div, :class => 'active-scaffold-header') do
|
|
151
159
|
content_tag(:h2) do
|
|
152
|
-
|
|
153
|
-
link_to(
|
|
160
|
+
label = options[:label] || remote_controller_config(remote_controller).list.label
|
|
161
|
+
link_to(label, url, remote: true, class: 'load-embedded', data: {error_msg: as_(:error_500)}) <<
|
|
154
162
|
loading_indicator_tag(url_options)
|
|
155
163
|
end
|
|
156
164
|
end
|
|
@@ -49,7 +49,7 @@ module CowProxy
|
|
|
49
49
|
class ActionLinks < ::CowProxy::WrapClass(::ActiveScaffold::DataStructures::ActionLinks)
|
|
50
50
|
def method_missing(name, *args, &block)
|
|
51
51
|
CowProxy.debug { "method missing #{name} in #{__getobj__.name}" }
|
|
52
|
-
return super if name
|
|
52
|
+
return super if name.match?(/[!?]$/)
|
|
53
53
|
subgroup =
|
|
54
54
|
if _instance_variable_defined?("@#{name}")
|
|
55
55
|
_instance_variable_get("@#{name}")
|
|
@@ -11,15 +11,21 @@ module ActiveScaffold::UnsavedRecord
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# automatically unsets the unsaved flag
|
|
14
|
-
def save(
|
|
14
|
+
def save(**)
|
|
15
15
|
super.tap { self.unsaved = false }
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def keeping_errors
|
|
19
19
|
old_errors = errors.dup if errors.present?
|
|
20
20
|
result = yield
|
|
21
|
-
old_errors&.each do |
|
|
22
|
-
|
|
21
|
+
old_errors&.each do |e|
|
|
22
|
+
if e.is_a?(String) || e.is_a?(Symbol)
|
|
23
|
+
# Rails <6.1 errors API.
|
|
24
|
+
old_errors[e].each { |msg| errors.add(e, msg) unless errors.added?(e, msg) }
|
|
25
|
+
else
|
|
26
|
+
# Rails >=6.1 errors API (https://code.lulalala.com/2020/0531-1013.html).
|
|
27
|
+
errors.add(e.attribute, e.message) unless errors.added?(e.attribute, e.message)
|
|
28
|
+
end
|
|
23
29
|
end
|
|
24
30
|
result && old_errors.blank?
|
|
25
31
|
end
|
|
@@ -122,7 +122,11 @@ module ActiveScaffold
|
|
|
122
122
|
def condition_for_search_ui(column, value, like_pattern, search_ui)
|
|
123
123
|
case search_ui
|
|
124
124
|
when :boolean, :checkbox
|
|
125
|
-
|
|
125
|
+
if value == 'null'
|
|
126
|
+
condition_for_null_type(column, value)
|
|
127
|
+
else
|
|
128
|
+
['%<search_sql>s = ?', column.column ? ActiveScaffold::Core.column_type_cast(value, column.column) : value]
|
|
129
|
+
end
|
|
126
130
|
when :integer, :decimal, :float
|
|
127
131
|
condition_for_numeric(column, value)
|
|
128
132
|
when :string, :range
|
|
@@ -212,7 +212,7 @@ module ActiveScaffold
|
|
|
212
212
|
end
|
|
213
213
|
|
|
214
214
|
def column_in_params_conditions?(key)
|
|
215
|
-
if key
|
|
215
|
+
if key.match?(/!$/)
|
|
216
216
|
conditions_from_params[1..-1].any? { |node| node.left.name.to_s == key[0..-2] }
|
|
217
217
|
else
|
|
218
218
|
conditions_from_params[0].include?(key)
|
|
@@ -38,8 +38,7 @@ module ActiveScaffold
|
|
|
38
38
|
|
|
39
39
|
else # final ultimate fallback: use rails' generic input method
|
|
40
40
|
# for textual fields we pass different options
|
|
41
|
-
|
|
42
|
-
options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
|
|
41
|
+
options = active_scaffold_input_text_options(options) if column.text? || column.number?
|
|
43
42
|
if column.column.type == :string && options[:maxlength].blank?
|
|
44
43
|
options[:maxlength] = column.column.limit
|
|
45
44
|
options[:size] ||= options[:maxlength].to_i > 30 ? 30 : options[:maxlength]
|
|
@@ -107,7 +106,11 @@ module ActiveScaffold
|
|
|
107
106
|
classes = "#{column.name}-input"
|
|
108
107
|
classes += ' numeric-input' if column.number?
|
|
109
108
|
|
|
110
|
-
|
|
109
|
+
if column.options[:collapsible]
|
|
110
|
+
collapsible_id = "container_#{id_control}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
{name: name, class: classes, id: id_control, collapsible_id: collapsible_id}.merge(options)
|
|
111
114
|
end
|
|
112
115
|
|
|
113
116
|
def current_form_columns(record, scope, subform_controller = nil)
|
|
@@ -120,15 +123,15 @@ module ActiveScaffold
|
|
|
120
123
|
end
|
|
121
124
|
end
|
|
122
125
|
|
|
123
|
-
def update_columns_options(column, scope, options, force = false)
|
|
126
|
+
def update_columns_options(column, scope, options, force = false, form_columns: nil, url_params: {})
|
|
124
127
|
record = options[:object]
|
|
125
128
|
subform_controller = controller.class.active_scaffold_controller_for(record.class) if scope
|
|
126
129
|
if @main_columns && (scope.nil? || subform_controller == controller.class)
|
|
127
|
-
form_columns
|
|
130
|
+
form_columns ||= @main_columns.visible_columns_names
|
|
128
131
|
end
|
|
129
|
-
form_columns ||= current_form_columns(record, scope, subform_controller)
|
|
132
|
+
form_columns ||= options[:form_columns] || current_form_columns(record, scope, subform_controller)
|
|
130
133
|
if force || (form_columns && column.update_columns && (column.update_columns & form_columns).present?)
|
|
131
|
-
url_params
|
|
134
|
+
url_params.reverse_merge! params_for(action: 'render_field', column: column.name, id: record.to_param)
|
|
132
135
|
if nested? && scope
|
|
133
136
|
url_params[:nested] = url_params.slice(:parent_scaffold, :association, nested.param_name)
|
|
134
137
|
url_params = url_params.except(:parent_scaffold, :association, nested.param_name)
|
|
@@ -153,7 +156,14 @@ module ActiveScaffold
|
|
|
153
156
|
end
|
|
154
157
|
|
|
155
158
|
def render_column(column, record, renders_as, scope = nil, only_value = false, col_class = nil) # rubocop:disable Metrics/ParameterLists
|
|
156
|
-
if (
|
|
159
|
+
if form_column_is_hidden?(column, record, scope)
|
|
160
|
+
# creates an element that can be replaced by the update_columns routine,
|
|
161
|
+
# but will not affect the value of the submitted form in this state:
|
|
162
|
+
# <dl><input type="hidden" class="<%= column.name %>-input"></dl>
|
|
163
|
+
content_tag :dl, style: 'display: none' do
|
|
164
|
+
hidden_field_tag(nil, nil, :class => "#{column.name}-input")
|
|
165
|
+
end
|
|
166
|
+
elsif (partial = override_form_field_partial(column))
|
|
157
167
|
render :partial => partial, :locals => {:column => column, :only_value => only_value, :scope => scope, :col_class => col_class, :record => record}
|
|
158
168
|
elsif renders_as == :field || override_form_field?(column)
|
|
159
169
|
form_attribute(column, record, scope, only_value, col_class)
|
|
@@ -164,6 +174,16 @@ module ActiveScaffold
|
|
|
164
174
|
end
|
|
165
175
|
end
|
|
166
176
|
|
|
177
|
+
def form_column_is_hidden?(column, record, scope = nil)
|
|
178
|
+
if column.hide_form_column_if&.respond_to?(:call)
|
|
179
|
+
column.hide_form_column_if.call(record, column, scope)
|
|
180
|
+
elsif column.hide_form_column_if&.is_a?(Symbol)
|
|
181
|
+
record.send(column.hide_form_column_if)
|
|
182
|
+
else
|
|
183
|
+
column.hide_form_column_if
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
167
187
|
def form_attribute(column, record, scope = nil, only_value = false, col_class = nil)
|
|
168
188
|
column_options = active_scaffold_input_options(column, scope, :object => record)
|
|
169
189
|
attributes = field_attributes(column, record)
|
|
@@ -181,12 +201,15 @@ module ActiveScaffold
|
|
|
181
201
|
end
|
|
182
202
|
if field
|
|
183
203
|
field << loading_indicator_tag(:action => :render_field, :id => params[:id]) if column.update_columns
|
|
184
|
-
|
|
204
|
+
desc = column.description(record, scope)
|
|
205
|
+
field << content_tag(:span, desc, :class => 'description') if desc.present?
|
|
185
206
|
end
|
|
186
207
|
|
|
208
|
+
label = label_tag(label_for(column, column_options), form_column_label(column, record, scope))
|
|
209
|
+
collapsible_id = column_options.delete :collapsible_id
|
|
210
|
+
label << h(' ') << link_to_visibility_toggle(collapsible_id) if collapsible_id
|
|
187
211
|
content_tag :dl, attributes do
|
|
188
|
-
content_tag(:dt,
|
|
189
|
-
content_tag(:dd, field)
|
|
212
|
+
content_tag(:dt, label) << content_tag(:dd, field, id: collapsible_id)
|
|
190
213
|
end
|
|
191
214
|
end
|
|
192
215
|
|
|
@@ -194,8 +217,8 @@ module ActiveScaffold
|
|
|
194
217
|
options[:id] unless column.form_ui == :select && column.association&.collection?
|
|
195
218
|
end
|
|
196
219
|
|
|
197
|
-
def form_column_label(column)
|
|
198
|
-
column.label
|
|
220
|
+
def form_column_label(column, record = nil, scope = nil)
|
|
221
|
+
column.label(record, scope)
|
|
199
222
|
end
|
|
200
223
|
|
|
201
224
|
def subform_label(column, hidden)
|
|
@@ -298,7 +321,7 @@ module ActiveScaffold
|
|
|
298
321
|
|
|
299
322
|
html_options.merge!(column.options[:html_options] || {})
|
|
300
323
|
options.merge!(column.options)
|
|
301
|
-
|
|
324
|
+
html_options.delete(:multiple) # no point using multiple in a form for singular assoc, but may be set for field search
|
|
302
325
|
active_scaffold_translate_select_options(options)
|
|
303
326
|
|
|
304
327
|
html =
|
|
@@ -312,7 +335,7 @@ module ActiveScaffold
|
|
|
312
335
|
html
|
|
313
336
|
end
|
|
314
337
|
|
|
315
|
-
def active_scaffold_new_record_subform(column, record, html_options, new_record_attributes: nil, locals: {}, skip_link: false)
|
|
338
|
+
def active_scaffold_new_record_subform(column, record, html_options, new_record_attributes: nil, locals: {}, skip_link: false)
|
|
316
339
|
klass =
|
|
317
340
|
if column.association.polymorphic? && column.association.belongs_to?
|
|
318
341
|
type = record.send(column.association.foreign_type)
|
|
@@ -603,14 +626,17 @@ module ActiveScaffold
|
|
|
603
626
|
# Column.type-based inputs
|
|
604
627
|
#
|
|
605
628
|
|
|
606
|
-
def active_scaffold_input_boolean(column,
|
|
607
|
-
record =
|
|
608
|
-
|
|
609
|
-
select_options << [as_(:_select_), nil] if !column.virtual? && column.column.null
|
|
610
|
-
select_options << [as_(:true), true] # rubocop:disable Lint/BooleanSymbol
|
|
611
|
-
select_options << [as_(:false), false] # rubocop:disable Lint/BooleanSymbol
|
|
629
|
+
def active_scaffold_input_boolean(column, html_options)
|
|
630
|
+
record = html_options.delete(:object)
|
|
631
|
+
html_options.merge!(column.options[:html_options] || {})
|
|
612
632
|
|
|
613
|
-
|
|
633
|
+
options = {selected: record.send(column.name), object: record}
|
|
634
|
+
options[:include_blank] = :_select_ if column.column&.null
|
|
635
|
+
options.merge!(column.options)
|
|
636
|
+
active_scaffold_translate_select_options(options)
|
|
637
|
+
|
|
638
|
+
options_for_select = [[as_(:true), true], [as_(:false), false]] # rubocop:disable Lint/BooleanSymbol
|
|
639
|
+
select(:record, column.name, options_for_select, options, html_options)
|
|
614
640
|
end
|
|
615
641
|
|
|
616
642
|
def active_scaffold_input_date(column, options)
|
|
@@ -113,8 +113,9 @@ module ActiveScaffold
|
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
def tel_to(text)
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
text = text.to_s
|
|
117
|
+
groups = text.scan(/(?:^\+)?\d+/)
|
|
118
|
+
extension = groups.pop if text.match?(/\s*[^\d\s]+\s*\d+$/)
|
|
118
119
|
link_to text, "tel:#{[groups.join('-'), extension].compact.join(',')}"
|
|
119
120
|
end
|
|
120
121
|
|
|
@@ -6,6 +6,8 @@ module ActiveScaffold
|
|
|
6
6
|
# It does not do any rendering. It only decides which method is responsible for rendering.
|
|
7
7
|
def active_scaffold_search_for(column, options = nil)
|
|
8
8
|
options ||= active_scaffold_search_options(column)
|
|
9
|
+
search_columns = active_scaffold_config.field_search.columns.visible_columns_names
|
|
10
|
+
options = update_columns_options(column, nil, options, form_columns: search_columns, url_params: {form_action: :field_search})
|
|
9
11
|
record = options[:object]
|
|
10
12
|
if column.delegated_association
|
|
11
13
|
record = record.send(column.delegated_association.name) || column.active_record_class.new
|
|
@@ -42,8 +44,7 @@ module ActiveScaffold
|
|
|
42
44
|
|
|
43
45
|
else # final ultimate fallback: use rails' generic input method
|
|
44
46
|
# for textual fields we pass different options
|
|
45
|
-
|
|
46
|
-
options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
|
|
47
|
+
options = active_scaffold_input_text_options(options) if column.text? || column.number?
|
|
47
48
|
text_field(:record, column.name, options.merge(column.options))
|
|
48
49
|
end
|
|
49
50
|
rescue StandardError => e
|
|
@@ -134,6 +135,11 @@ module ActiveScaffold
|
|
|
134
135
|
def active_scaffold_search_boolean(column, options)
|
|
135
136
|
select_options = []
|
|
136
137
|
select_options << [as_(:_select_), nil]
|
|
138
|
+
if column.column&.null
|
|
139
|
+
null_label = column.options[:include_blank] || :null
|
|
140
|
+
null_label = as_(null_label) if null_label.is_a?(Symbol)
|
|
141
|
+
select_options << [null_label, 'null']
|
|
142
|
+
end
|
|
137
143
|
select_options << [as_(:true), true] # rubocop:disable Lint/BooleanSymbol
|
|
138
144
|
select_options << [as_(:false), false] # rubocop:disable Lint/BooleanSymbol
|
|
139
145
|
|
|
@@ -247,7 +247,7 @@ module ActiveScaffold
|
|
|
247
247
|
|
|
248
248
|
message = options.include?(:message) ? options[:message] : as_('errors.template.body')
|
|
249
249
|
|
|
250
|
-
error_messages = objects.sum do |object|
|
|
250
|
+
error_messages = objects.sum([]) do |object|
|
|
251
251
|
object.errors.full_messages.map do |msg|
|
|
252
252
|
options[:list_type] != :br ? content_tag(:li, msg) : msg
|
|
253
253
|
end
|
|
@@ -1,33 +1,28 @@
|
|
|
1
1
|
module ActiveScaffold
|
|
2
2
|
class Registry
|
|
3
|
-
|
|
4
|
-
attr_accessor :current_user_proc, :current_ability_proc, :marked_records
|
|
3
|
+
thread_mattr_accessor :current_user_proc, :current_ability_proc, :marked_records
|
|
5
4
|
|
|
6
|
-
def user_settings
|
|
7
|
-
|
|
5
|
+
def self.user_settings
|
|
6
|
+
RequestStore.store[:attr_Registry_user_settings] ||= {}
|
|
8
7
|
end
|
|
9
8
|
|
|
10
|
-
def constraint_columns
|
|
11
|
-
|
|
9
|
+
def self.constraint_columns
|
|
10
|
+
RequestStore.store[:attr_Registry_constraint_columns] ||= Hash.new { |h, k| h[k] = [] }
|
|
12
11
|
end
|
|
13
12
|
|
|
14
|
-
def unauthorized_columns
|
|
15
|
-
|
|
13
|
+
def self.unauthorized_columns
|
|
14
|
+
RequestStore.store[:attr_Registry_unauthorized_columns] ||= Hash.new { |h, k| h[k] = [] }
|
|
16
15
|
end
|
|
17
16
|
|
|
18
|
-
def cache(kind, key = nil, &block)
|
|
17
|
+
def self.cache(kind, key = nil, &block)
|
|
19
18
|
unless key
|
|
20
19
|
key = kind
|
|
21
20
|
kind = :cache
|
|
22
21
|
end
|
|
23
|
-
|
|
24
|
-
cache =
|
|
22
|
+
RequestStore.store[:attr_Registry_cache] ||= {}
|
|
23
|
+
cache = RequestStore.store[:attr_Registry_cache][kind] ||= {}
|
|
25
24
|
return cache[key] if cache.include? key
|
|
26
25
|
cache[key] ||= yield
|
|
27
26
|
end
|
|
28
|
-
|
|
29
|
-
def self.instance
|
|
30
|
-
RequestStore.store[@per_thread_registry_key] ||= new
|
|
31
|
-
end
|
|
32
27
|
end
|
|
33
28
|
end
|