active_scaffold 3.5.4 → 3.6.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 → CHANGELOG.rdoc} +72 -0
- data/README.md +20 -8
- data/app/assets/javascripts/active_scaffold.js.erb +0 -1
- data/app/assets/javascripts/jquery/active_scaffold.js +98 -7
- data/app/assets/stylesheets/active_scaffold_colors.scss +1 -1
- data/app/assets/stylesheets/active_scaffold_layout.css +52 -29
- data/app/views/active_scaffold_overrides/_base_form.html.erb +2 -2
- data/app/views/active_scaffold_overrides/_form.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_form_association.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +3 -2
- data/app/views/active_scaffold_overrides/_form_association_record.html.erb +9 -7
- data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +4 -4
- data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_list.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_list_header.html.erb +5 -7
- data/app/views/active_scaffold_overrides/_list_messages.html.erb +1 -0
- data/app/views/active_scaffold_overrides/_list_record.html.erb +4 -5
- data/app/views/active_scaffold_overrides/_list_with_header.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_messages.html.erb +1 -0
- data/app/views/active_scaffold_overrides/_refresh_list.js.erb +4 -0
- data/app/views/active_scaffold_overrides/_render_field.js.erb +2 -1
- data/app/views/active_scaffold_overrides/_show_association_horizontal.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_show_columns.html.erb +2 -2
- data/app/views/active_scaffold_overrides/_show_horizontal_record.html.erb +4 -4
- data/app/views/active_scaffold_overrides/_update_calculations.js.erb +1 -1
- data/app/views/active_scaffold_overrides/_update_column.js.erb +2 -2
- data/app/views/active_scaffold_overrides/_vertical_subform.html.erb +2 -2
- data/app/views/active_scaffold_overrides/action_confirmation.html.erb +2 -2
- data/app/views/active_scaffold_overrides/delete.html.erb +2 -2
- data/app/views/active_scaffold_overrides/on_action_update.js.erb +16 -6
- data/app/views/active_scaffold_overrides/on_update.js.erb +1 -1
- data/app/views/active_scaffold_overrides/row.js.erb +1 -1
- data/app/views/active_scaffold_overrides/update_column.js.erb +2 -2
- data/config/locales/de.yml +2 -1
- data/config/locales/en.yml +1 -0
- data/config/locales/es.yml +1 -0
- data/config/locales/fr.yml +2 -1
- data/config/locales/hu.yml +1 -0
- data/config/locales/ja.yml +1 -0
- data/config/locales/ru.yml +1 -0
- data/lib/active_scaffold.rb +19 -16
- data/lib/active_scaffold/actions/common_search.rb +11 -8
- data/lib/active_scaffold/actions/core.rb +91 -70
- data/lib/active_scaffold/actions/create.rb +28 -28
- data/lib/active_scaffold/actions/delete.rb +3 -3
- data/lib/active_scaffold/actions/field_search.rb +53 -43
- data/lib/active_scaffold/actions/list.rb +111 -27
- data/lib/active_scaffold/actions/nested.rb +65 -48
- data/lib/active_scaffold/actions/search.rb +1 -1
- data/lib/active_scaffold/actions/show.rb +4 -4
- data/lib/active_scaffold/actions/subform.rb +23 -22
- data/lib/active_scaffold/actions/update.rb +96 -77
- data/lib/active_scaffold/active_record_permissions.rb +2 -11
- data/lib/active_scaffold/attribute_params.rb +102 -94
- data/lib/active_scaffold/bridges.rb +8 -8
- data/lib/active_scaffold/bridges/active_storage.rb +6 -0
- data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +34 -0
- data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +54 -0
- data/lib/active_scaffold/bridges/active_storage/form_ui.rb +22 -0
- data/lib/active_scaffold/bridges/active_storage/list_ui.rb +36 -0
- data/lib/active_scaffold/bridges/bitfields.rb +2 -1
- data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
- data/lib/active_scaffold/bridges/bitfields/list_ui.rb +19 -0
- data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +9 -12
- data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
- data/lib/active_scaffold/bridges/chosen/helpers.rb +7 -6
- data/lib/active_scaffold/bridges/date_picker/ext.rb +0 -13
- data/lib/active_scaffold/bridges/date_picker/helper.rb +49 -44
- data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
- data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +3 -3
- data/lib/active_scaffold/bridges/file_column/form_ui.rb +3 -3
- data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +10 -7
- data/lib/active_scaffold/bridges/paper_trail.rb +1 -1
- data/lib/active_scaffold/bridges/paper_trail/actions.rb +3 -1
- data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
- data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +1 -1
- data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
- data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -17
- data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -19
- data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
- data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +21 -4
- data/lib/active_scaffold/config/base.rb +133 -41
- data/lib/active_scaffold/config/core.rb +146 -18
- data/lib/active_scaffold/config/delete.rb +14 -1
- data/lib/active_scaffold/config/field_search.rb +7 -1
- data/lib/active_scaffold/config/form.rb +10 -1
- data/lib/active_scaffold/config/list.rb +39 -13
- data/lib/active_scaffold/config/mark.rb +4 -2
- data/lib/active_scaffold/config/nested.rb +16 -17
- data/lib/active_scaffold/config/search.rb +9 -0
- data/lib/active_scaffold/config/show.rb +4 -0
- data/lib/active_scaffold/config/update.rb +4 -0
- data/lib/active_scaffold/configurable.rb +14 -7
- data/lib/active_scaffold/constraints.rb +22 -20
- data/lib/active_scaffold/core.rb +67 -28
- data/lib/active_scaffold/data_structures/action_columns.rb +50 -59
- data/lib/active_scaffold/data_structures/action_link.rb +50 -20
- data/lib/active_scaffold/data_structures/action_links.rb +15 -13
- data/lib/active_scaffold/data_structures/association/abstract.rb +38 -15
- data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
- data/lib/active_scaffold/data_structures/association/active_record.rb +6 -2
- data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
- data/lib/active_scaffold/data_structures/column.rb +75 -66
- data/lib/active_scaffold/data_structures/columns.rb +3 -2
- data/lib/active_scaffold/data_structures/nested_info.rb +33 -19
- data/lib/active_scaffold/data_structures/set.rb +8 -0
- data/lib/active_scaffold/data_structures/sorting.rb +10 -2
- data/lib/active_scaffold/delayed_setup.rb +16 -5
- data/lib/active_scaffold/extensions/action_controller_rendering.rb +3 -2
- data/lib/active_scaffold/extensions/action_view_rendering.rb +93 -32
- data/lib/active_scaffold/extensions/cow_proxy.rb +95 -0
- data/lib/active_scaffold/extensions/ice_nine.rb +36 -0
- data/lib/active_scaffold/extensions/left_outer_joins.rb +8 -33
- data/lib/active_scaffold/extensions/localize.rb +3 -1
- data/lib/active_scaffold/extensions/routing_mapper.rb +6 -45
- data/lib/active_scaffold/extensions/to_label.rb +3 -2
- data/lib/active_scaffold/extensions/unsaved_record.rb +2 -4
- data/lib/active_scaffold/finder.rb +110 -77
- data/lib/active_scaffold/helpers/action_link_helpers.rb +62 -36
- data/lib/active_scaffold/helpers/association_helpers.rb +18 -16
- data/lib/active_scaffold/helpers/controller_helpers.rb +34 -10
- data/lib/active_scaffold/helpers/form_column_helpers.rb +196 -124
- data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
- data/lib/active_scaffold/helpers/id_helpers.rb +6 -2
- data/lib/active_scaffold/helpers/list_column_helpers.rb +90 -57
- data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
- data/lib/active_scaffold/helpers/search_column_helpers.rb +29 -34
- data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
- data/lib/active_scaffold/helpers/view_helpers.rb +39 -36
- data/lib/active_scaffold/marked_model.rb +2 -2
- data/lib/active_scaffold/orm_checks.rb +3 -7
- data/lib/active_scaffold/paginator.rb +7 -7
- data/lib/active_scaffold/registry.rb +33 -0
- data/lib/active_scaffold/responds_to_parent.rb +8 -11
- data/lib/active_scaffold/tableless.rb +82 -66
- data/lib/active_scaffold/version.rb +2 -2
- data/lib/generators/active_scaffold/controller_generator.rb +2 -2
- data/lib/generators/active_scaffold/install_generator.rb +52 -4
- data/lib/generators/active_scaffold/resource_generator.rb +2 -2
- data/shoulda_macros/macros.rb +3 -1
- data/test/bridges/date_picker_test.rb +1 -2
- data/test/bridges/paperclip_test.rb +6 -6
- data/test/class_with_finder.rb +2 -2
- data/test/company.rb +4 -4
- data/test/config/create_test.rb +4 -2
- data/test/config/nested_test.rb +1 -1
- data/test/config/show_test.rb +1 -1
- data/test/config/update_test.rb +7 -6
- data/test/data_structures/action_columns_test.rb +2 -2
- data/test/data_structures/action_links_test.rb +1 -1
- data/test/data_structures/column_test.rb +3 -6
- data/test/data_structures/columns_test.rb +2 -2
- data/test/data_structures/sorting_test.rb +7 -0
- data/test/extensions/action_view_rendering_test.rb +20 -0
- data/test/extensions/active_record_test.rb +4 -4
- data/test/extensions/routing_mapper_test.rb +2 -2
- data/test/helpers/list_column_helpers_test.rb +3 -1
- data/test/misc/active_record_permissions_test.rb +3 -11
- data/test/misc/attribute_params_test.rb +12 -8
- data/test/misc/calculation_test.rb +1 -1
- data/test/misc/configurable_test.rb +10 -10
- data/test/misc/constraints_test.rb +2 -2
- data/test/misc/convert_numbers_format_test.rb +7 -3
- data/test/misc/lang_test.rb +1 -1
- data/test/misc/parse_datetime_test.rb +3 -4
- data/test/misc/tableless_test.rb +14 -0
- data/test/mock_app/Rakefile +1 -1
- data/test/mock_app/app/assets/config/manifest.js +0 -0
- data/test/mock_app/app/controllers/cars_controller.rb +1 -0
- data/test/mock_app/app/controllers/people_controller.rb +5 -1
- data/test/mock_app/app/controllers/roles_controller.rb +4 -0
- data/test/mock_app/app/views/active_scaffold_overrides/_form.html.erb +2 -0
- data/test/mock_app/app/views/active_scaffold_overrides/list.html.erb +2 -0
- data/test/mock_app/app/views/people/_first_name_form_column.html.erb +2 -0
- data/test/mock_app/app/views/people/_form.html.erb +2 -0
- data/test/mock_app/app/views/people/list.html.erb +2 -0
- data/test/mock_app/config/application.rb +2 -1
- data/test/mock_app/config/boot.rb +1 -1
- data/test/mock_app/config/environment.rb +2 -2
- data/test/mock_app/config/routes.rb +4 -1
- data/test/mock_app/db/schema.rb +2 -0
- data/test/performance/list_cars_performance_test.rb +34 -0
- data/test/performance/list_people_performance_test.rb +31 -0
- data/test/performance_test_help.rb +3 -0
- data/test/test_helper.rb +12 -4
- metadata +69 -18
- data/app/assets/javascripts/prototype/rico_corner.js +0 -370
- data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -7
@@ -37,7 +37,7 @@ module ActiveScaffold
|
|
37
37
|
# because the object may change (someone may log in or out). So we give ActiveRecord a proc that ties to the
|
38
38
|
# current_user_method on this ApplicationController.
|
39
39
|
def assign_current_user_to_models
|
40
|
-
|
40
|
+
ActiveScaffold::Registry.current_user_proc = proc { send(ActiveRecordPermissions.current_user_method) }
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -48,18 +48,9 @@ module ActiveScaffold
|
|
48
48
|
end
|
49
49
|
|
50
50
|
module ClassMethods
|
51
|
-
# The proc to call that retrieves the current_user from the ApplicationController.
|
52
|
-
def current_user_proc
|
53
|
-
Thread.current[:current_user_proc]
|
54
|
-
end
|
55
|
-
|
56
|
-
def current_user_proc=(proc_value)
|
57
|
-
Thread.current[:current_user_proc] = proc_value
|
58
|
-
end
|
59
|
-
|
60
51
|
# Class-level access to the current user
|
61
52
|
def current_user
|
62
|
-
|
53
|
+
ActiveScaffold::Registry.current_user_proc&.call
|
63
54
|
end
|
64
55
|
end
|
65
56
|
|
@@ -41,13 +41,8 @@ module ActiveScaffold
|
|
41
41
|
difference = value.select(&:persisted?).size - parent_record.send(counter_attr)
|
42
42
|
|
43
43
|
if parent_record.new_record?
|
44
|
-
|
45
|
-
|
46
|
-
parent_record.send "#{counter_attr}_will_change!"
|
47
|
-
else # < 4.2
|
48
|
-
parent_record.send "#{counter_attr}=", difference
|
49
|
-
parent_record.send "#{column.name}=", value
|
50
|
-
end
|
44
|
+
parent_record.send "#{column.name}=", value
|
45
|
+
parent_record.send "#{counter_attr}_will_change!"
|
51
46
|
else
|
52
47
|
# don't decrement counter for deleted records, on destroy they will update counter
|
53
48
|
difference += (parent_record.send(column.name) - value).size
|
@@ -57,7 +52,7 @@ module ActiveScaffold
|
|
57
52
|
# update counters on old parents if belongs_to is changed
|
58
53
|
value.select(&:persisted?).each do |record|
|
59
54
|
key = record.send(column.association.foreign_key)
|
60
|
-
parent_record.class.decrement_counter counter_attr, key if key && key != parent_record.id
|
55
|
+
parent_record.class.decrement_counter counter_attr, key if key && key != parent_record.id # rubocop:disable Rails/SkipsModelValidations
|
61
56
|
end
|
62
57
|
parent_record.send "#{column.name}=", value if parent_record.persisted?
|
63
58
|
end
|
@@ -76,6 +71,15 @@ module ActiveScaffold
|
|
76
71
|
!params_hash?(value) && association.belongs_to? && association.counter_cache_hack?
|
77
72
|
end
|
78
73
|
|
74
|
+
def multi_parameter_attributes(attributes)
|
75
|
+
params_hash(attributes).each_with_object({}) do |(k, v), result|
|
76
|
+
next unless k.include? '('
|
77
|
+
column_name = k.split('(').first
|
78
|
+
result[column_name] ||= []
|
79
|
+
result[column_name] << [k, v]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
79
83
|
# Takes attributes (as from params[:record]) and applies them to the parent_record. Also looks for
|
80
84
|
# association attributes and attempts to instantiate them as associated objects.
|
81
85
|
#
|
@@ -85,28 +89,22 @@ module ActiveScaffold
|
|
85
89
|
crud_type = parent_record.new_record? ? :create : :update
|
86
90
|
return parent_record unless parent_record.authorized_for?(:crud_type => crud_type)
|
87
91
|
|
88
|
-
|
89
|
-
attributes.each do |k, v|
|
90
|
-
next unless k.include? '('
|
91
|
-
column_name = k.split('(').first
|
92
|
-
multi_parameter_attributes[column_name] ||= []
|
93
|
-
multi_parameter_attributes[column_name] << [k, v]
|
94
|
-
end
|
92
|
+
multi_parameter_attrs = multi_parameter_attributes(attributes)
|
95
93
|
|
96
|
-
columns.
|
94
|
+
columns.each_column(for: parent_record, crud_type: crud_type, flatten: true) do |column|
|
97
95
|
begin
|
98
96
|
# Set any passthrough parameters that may be associated with this column (ie, file column "keep" and "temp" attributes)
|
99
|
-
|
100
|
-
column.params.each { |p| parent_record.send("#{p}=", attributes[p]) if attributes.key? p }
|
101
|
-
end
|
97
|
+
column.params.select { |p| attributes.key? p }.each { |p| parent_record.send("#{p}=", attributes[p]) }
|
102
98
|
|
103
|
-
if
|
104
|
-
parent_record.send(:assign_multiparameter_attributes,
|
99
|
+
if multi_parameter_attrs.key? column.name.to_s
|
100
|
+
parent_record.send(:assign_multiparameter_attributes, multi_parameter_attrs[column.name.to_s])
|
105
101
|
elsif attributes.key? column.name
|
106
|
-
|
102
|
+
update_column_from_params(parent_record, column, attributes[column.name], avoid_changes)
|
107
103
|
end
|
108
|
-
rescue => e
|
109
|
-
|
104
|
+
rescue StandardError => e
|
105
|
+
message = "on the ActiveScaffold column = :#{column.name} for #{parent_record.inspect} "\
|
106
|
+
"(value from params #{attributes[column.name].inspect})"
|
107
|
+
Rails.logger.error "#{e.class.name}: #{e.message} -- #{message}"
|
110
108
|
raise
|
111
109
|
end
|
112
110
|
end
|
@@ -116,38 +114,45 @@ module ActiveScaffold
|
|
116
114
|
|
117
115
|
def update_column_from_params(parent_record, column, attribute, avoid_changes = false)
|
118
116
|
value = column_value_from_param_value(parent_record, column, attribute, avoid_changes)
|
119
|
-
if
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
117
|
+
if column.association
|
118
|
+
if avoid_changes
|
119
|
+
parent_record.association(column.name).target = value
|
120
|
+
parent_record.send("#{column.association.foreign_key}=", value&.id) if column.association.belongs_to?
|
121
|
+
else
|
122
|
+
update_column_association(parent_record, column, attribute, value)
|
123
|
+
end
|
124
|
+
else
|
125
|
+
parent_record.send "#{column.name}=", value
|
126
|
+
end
|
127
|
+
# needed? probably done on find_or_create_for_params, need more testing
|
128
|
+
if column.association&.reverse_association&.belongs_to?
|
129
|
+
Array(value).each { |v| v.send("#{column.association.reverse}=", parent_record) if v.new_record? }
|
130
|
+
end
|
131
|
+
value
|
132
|
+
end
|
133
|
+
|
134
|
+
def update_column_association(parent_record, column, attribute, value)
|
135
|
+
if counter_cache_hack?(column.association, attribute)
|
136
|
+
parent_record.send "#{column.association.foreign_key}=", value&.id
|
124
137
|
parent_record.association(column.name).target = value
|
125
|
-
elsif column.association.
|
138
|
+
elsif column.association.collection? && column.association.through_singular?
|
126
139
|
through = column.association.through_reflection.name
|
127
140
|
through_record = parent_record.send(through)
|
128
141
|
through_record ||= parent_record.send "build_#{through}"
|
129
142
|
through_record.send "#{column.association.source_reflection.name}=", value
|
143
|
+
elsif hack_for_has_many_counter_cache?(parent_record, column)
|
144
|
+
hack_for_has_many_counter_cache(parent_record, column, value)
|
130
145
|
else
|
131
|
-
|
132
|
-
if column.association && hack_for_has_many_counter_cache?(parent_record, column)
|
133
|
-
hack_for_has_many_counter_cache(parent_record, column, value)
|
134
|
-
else
|
135
|
-
parent_record.send "#{column.name}=", value
|
136
|
-
end
|
137
|
-
rescue ActiveRecord::RecordNotSaved
|
138
|
-
parent_record.errors.add column.name, :invalid
|
139
|
-
parent_record.association(column.name).target = value if column.association
|
140
|
-
end
|
146
|
+
parent_record.send "#{column.name}=", value
|
141
147
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
value
|
148
|
+
rescue ActiveRecord::RecordNotSaved
|
149
|
+
parent_record.errors.add column.name, :invalid
|
150
|
+
parent_record.association(column.name).target = value
|
146
151
|
end
|
147
152
|
|
148
153
|
def column_value_from_param_value(parent_record, column, value, avoid_changes = false)
|
149
154
|
# convert the value, possibly by instantiating associated objects
|
150
|
-
form_ui = column.form_ui || column.column
|
155
|
+
form_ui = column.form_ui || column.column&.type
|
151
156
|
if form_ui && respond_to?("column_value_for_#{form_ui}_type", true)
|
152
157
|
send("column_value_for_#{form_ui}_type", parent_record, column, value)
|
153
158
|
elsif params_hash? value
|
@@ -177,27 +182,24 @@ module ActiveScaffold
|
|
177
182
|
Date.parse("#{value}-01")
|
178
183
|
end
|
179
184
|
|
180
|
-
def
|
181
|
-
if column.association.
|
182
|
-
if value.present?
|
183
|
-
|
184
|
-
class_name = parent_record.send(column.association.foreign_type)
|
185
|
-
class_name.constantize.find(value) if class_name.present?
|
186
|
-
else
|
187
|
-
# it's a single id
|
188
|
-
column.association.klass.find(value)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
elsif column.association.try :collection?
|
185
|
+
def association_value_from_param_simple_value(parent_record, column, value)
|
186
|
+
if column.association.singular?
|
187
|
+
column.association.klass(parent_record)&.find(value) if value.present?
|
188
|
+
else # column.association.collection?
|
192
189
|
column_plural_assocation_value_from_value(column, Array(value))
|
193
|
-
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def column_value_from_param_simple_value(parent_record, column, value)
|
194
|
+
if column.association
|
195
|
+
association_value_from_param_simple_value(parent_record, column, value)
|
196
|
+
elsif column.convert_to_native?
|
194
197
|
column.number_to_native(value)
|
195
|
-
|
198
|
+
elsif value.is_a?(String) && value.empty? && !column.virtual?
|
196
199
|
# convert empty strings into nil. this works better with 'null => true' columns (and validations),
|
197
200
|
# for 'null => false' columns is just converted to default value from column
|
198
|
-
|
199
|
-
|
200
|
-
end
|
201
|
+
column.default_for_empty_value
|
202
|
+
else
|
201
203
|
value
|
202
204
|
end
|
203
205
|
end
|
@@ -213,9 +215,9 @@ module ActiveScaffold
|
|
213
215
|
end
|
214
216
|
|
215
217
|
def column_value_from_param_hash_value(parent_record, column, value, avoid_changes = false)
|
216
|
-
if column.association
|
218
|
+
if column.association&.singular?
|
217
219
|
manage_nested_record_from_params(parent_record, column, value, avoid_changes)
|
218
|
-
elsif column.association
|
220
|
+
elsif column.association&.collection?
|
219
221
|
# HACK: to be able to delete all associated records, hash will include "0" => ""
|
220
222
|
values = value.values.reject(&:blank?)
|
221
223
|
values.collect { |val| manage_nested_record_from_params(parent_record, column, val, avoid_changes) }.compact
|
@@ -228,7 +230,7 @@ module ActiveScaffold
|
|
228
230
|
return nil unless build_record_from_params?(attributes, column, parent_record)
|
229
231
|
record = find_or_create_for_params(attributes, column, parent_record)
|
230
232
|
if record
|
231
|
-
record_columns = active_scaffold_config_for(
|
233
|
+
record_columns = active_scaffold_config_for(record.class).subform.columns
|
232
234
|
prev_constraints = record_columns.constraint_columns
|
233
235
|
record_columns.constraint_columns = [column.association.reverse].compact
|
234
236
|
update_record_from_params(record, record_columns, attributes, avoid_changes)
|
@@ -240,8 +242,9 @@ module ActiveScaffold
|
|
240
242
|
|
241
243
|
def build_record_from_params?(params, column, record)
|
242
244
|
current = record.send(column.name)
|
243
|
-
|
244
|
-
|
245
|
+
return true if column.association.collection? && !column.show_blank_record?(current)
|
246
|
+
klass = column.association.klass(record)
|
247
|
+
klass && !attributes_hash_is_empty?(params, klass)
|
245
248
|
end
|
246
249
|
|
247
250
|
# Attempts to create or find an instance of the klass of the association in parent_column from the
|
@@ -249,21 +252,26 @@ module ActiveScaffold
|
|
249
252
|
# otherwise it will build a new one.
|
250
253
|
def find_or_create_for_params(params, parent_column, parent_record)
|
251
254
|
current = parent_record.send(parent_column.name)
|
252
|
-
klass = parent_column.association.klass
|
255
|
+
klass = parent_column.association.klass(parent_record)
|
253
256
|
if params.key? klass.primary_key
|
254
257
|
record_from_current_or_find(klass, params[klass.primary_key], current)
|
255
258
|
elsif klass.authorized_for?(:crud_type => :create)
|
256
|
-
parent_column.association
|
259
|
+
association = parent_column.association
|
260
|
+
record = klass.new
|
261
|
+
if association.reverse_association&.belongs_to? && (association.collection? || current.nil?)
|
262
|
+
record.send("#{parent_column.association.reverse}=", parent_record)
|
263
|
+
end
|
264
|
+
record
|
257
265
|
end
|
258
266
|
end
|
259
267
|
|
260
268
|
# Attempts to find an instance of klass (which must be an ActiveRecord object) with id primary key
|
261
269
|
# Returns record from current if it's included or find from DB
|
262
270
|
def record_from_current_or_find(klass, id, current)
|
263
|
-
if current
|
271
|
+
if current.is_a?(ActiveRecord::Base) && current.id.to_s == id
|
264
272
|
# modifying the current object of a singular association
|
265
273
|
current
|
266
|
-
elsif current
|
274
|
+
elsif current.respond_to?(:any?) && current.any? { |o| o.id.to_s == id }
|
267
275
|
# modifying one of the current objects in a plural association
|
268
276
|
current.detect { |o| o.id.to_s == id }
|
269
277
|
else # attaching an existing but not-current object
|
@@ -271,36 +279,20 @@ module ActiveScaffold
|
|
271
279
|
end
|
272
280
|
end
|
273
281
|
|
274
|
-
def save_record_to_association(record, association, value)
|
275
|
-
if association.try(:collection?)
|
276
|
-
record.send(association.name) << value
|
277
|
-
elsif association
|
278
|
-
record.send("#{association.name}=", value)
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
282
|
# Determines whether the given attributes hash is "empty".
|
283
283
|
# This isn't a literal emptiness - it's an attempt to discern whether the user intended it to be empty or not.
|
284
284
|
def attributes_hash_is_empty?(hash, klass)
|
285
|
-
# old style date form management... ignore them too
|
286
|
-
part_ignore_column_types = [:datetime, :date, :time, Time, Date]
|
287
|
-
|
288
285
|
hash.all? do |key, value|
|
289
286
|
# convert any possible multi-parameter attributes like 'created_at(5i)' to simply 'created_at'
|
290
|
-
|
291
|
-
column_name = parts.first
|
292
|
-
column = ActiveScaffold::OrmChecks.columns_hash(klass)[column_name]
|
293
|
-
column_type = ActiveScaffold::OrmChecks.column_type(klass, column_name) if column
|
287
|
+
column_name = key.to_s.split('(', 2)[0]
|
294
288
|
|
295
289
|
# datetimes will always have a value. so we ignore them when checking whether the hash is empty.
|
296
290
|
# this could be a bad idea. but the current situation (excess record entry) seems worse.
|
297
|
-
next true if
|
291
|
+
next true if mulitpart_ignored?(key, klass)
|
298
292
|
|
299
293
|
# defaults are pre-filled on the form. we can't use them to determine if the user intends a new row.
|
300
294
|
# booleans always have value, so they are ignored if not changed from default
|
301
|
-
|
302
|
-
casted_value = column ? ActiveScaffold::Core.column_type_cast(value, column) : value
|
303
|
-
next true if casted_value == default_value
|
295
|
+
next true if default_value?(column_name, klass, value)
|
304
296
|
|
305
297
|
if params_hash? value
|
306
298
|
attributes_hash_is_empty?(value, klass)
|
@@ -312,14 +304,30 @@ module ActiveScaffold
|
|
312
304
|
end
|
313
305
|
end
|
314
306
|
|
315
|
-
|
307
|
+
# old style date form management... ignore them
|
308
|
+
MULTIPART_IGNORE_TYPES = [:datetime, :date, :time, Time, Date].freeze
|
309
|
+
|
310
|
+
def mulitpart_ignored?(param_name, klass)
|
311
|
+
column_name, multipart = param_name.to_s.split('(', 2)
|
312
|
+
return false unless multipart
|
313
|
+
column_type = ActiveScaffold::OrmChecks.column_type(klass, column_name)
|
314
|
+
MULTIPART_IGNORE_TYPES.include?(column_type) if column_type
|
315
|
+
end
|
316
|
+
|
317
|
+
def default_value?(column_name, klass, value)
|
318
|
+
column = ActiveScaffold::OrmChecks.columns_hash(klass)[column_name]
|
319
|
+
default_value = column_default_value(column_name, klass)
|
320
|
+
casted_value = ActiveScaffold::Core.column_type_cast(value, column)
|
321
|
+
casted_value == default_value
|
322
|
+
end
|
323
|
+
|
324
|
+
def column_default_value(column_name, klass)
|
325
|
+
column = ActiveScaffold::OrmChecks.columns_hash(klass)[column_name]
|
316
326
|
return unless column
|
317
327
|
if ActiveScaffold::OrmChecks.mongoid? klass
|
318
328
|
column.default_val
|
319
329
|
elsif ActiveScaffold::OrmChecks.active_record? klass
|
320
|
-
if Rails.version < '
|
321
|
-
column.default
|
322
|
-
elsif Rails.version < '5.0'
|
330
|
+
if Rails.version < '5.0'
|
323
331
|
column.type_cast_from_database(column.default)
|
324
332
|
else
|
325
333
|
column_type = ActiveScaffold::OrmChecks.column_type(klass, column_name)
|
@@ -11,7 +11,7 @@ module ActiveScaffold
|
|
11
11
|
self.bridges = {}
|
12
12
|
|
13
13
|
def self.register(file)
|
14
|
-
match = file.match(
|
14
|
+
match = file.match(%r{(active_scaffold/bridges/(.*))\.rb\Z})
|
15
15
|
bridges[match[2].to_sym] = match[1] if match
|
16
16
|
end
|
17
17
|
|
@@ -34,18 +34,18 @@ module ActiveScaffold
|
|
34
34
|
|
35
35
|
def self.run_all
|
36
36
|
return false if bridges_run
|
37
|
-
bridges.
|
38
|
-
|
39
|
-
bridge.run if bridge
|
37
|
+
bridges.each_key do |bridge_name|
|
38
|
+
self[bridge_name]&.run
|
40
39
|
end
|
40
|
+
ActiveScaffold::Config::Core.freeze if ActiveScaffold.threadsafe
|
41
41
|
self.bridges_run = true
|
42
42
|
end
|
43
43
|
|
44
44
|
def self.prepare_all
|
45
45
|
return false if bridges_prepared
|
46
|
-
bridges.
|
46
|
+
bridges.each_key do |bridge_name|
|
47
47
|
bridge = self[bridge_name]
|
48
|
-
bridge.prepare if bridge
|
48
|
+
bridge.prepare if bridge&.install?
|
49
49
|
end
|
50
50
|
self.bridges_prepared = true
|
51
51
|
end
|
@@ -53,14 +53,14 @@ module ActiveScaffold
|
|
53
53
|
def self.all_stylesheets
|
54
54
|
bridges.keys.collect do |bridge_name|
|
55
55
|
bridge = self[bridge_name]
|
56
|
-
bridge.stylesheets if bridge
|
56
|
+
bridge.stylesheets if bridge&.install?
|
57
57
|
end.compact.flatten
|
58
58
|
end
|
59
59
|
|
60
60
|
def self.all_javascripts
|
61
61
|
bridges.keys.collect do |bridge_name|
|
62
62
|
bridge = self[bridge_name]
|
63
|
-
bridge.javascripts if bridge
|
63
|
+
bridge.javascripts if bridge&.install?
|
64
64
|
end.compact.flatten
|
65
65
|
end
|
66
66
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
class ActiveScaffold::Bridges::ActiveStorage < ActiveScaffold::DataStructures::Bridge
|
2
|
+
def self.install
|
3
|
+
Dir[File.join(__dir__, 'active_storage', '*.rb')].each { |file| require file }
|
4
|
+
ActiveScaffold::Config::Core.send :prepend, ActiveScaffold::Bridges::ActiveStorage::ActiveStorageBridge
|
5
|
+
end
|
6
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ActiveScaffold
|
2
|
+
module Bridges
|
3
|
+
class ActiveStorage
|
4
|
+
module ActiveStorageBridge
|
5
|
+
def initialize(model_id)
|
6
|
+
super
|
7
|
+
return unless ActiveScaffold::Bridges::ActiveStorage::ActiveStorageBridgeHelpers.klass_has_active_storage_fields?(model)
|
8
|
+
|
9
|
+
model.send :extend, ActiveScaffold::Bridges::ActiveStorage::ActiveStorageBridgeHelpers
|
10
|
+
|
11
|
+
# include the "delete" helpers for use with active scaffold, unless they are already included
|
12
|
+
model.generate_delete_helpers
|
13
|
+
|
14
|
+
update.multipart = true
|
15
|
+
create.multipart = true
|
16
|
+
|
17
|
+
model.active_storage_has_one_fields.each { |field| configure_active_storage_field(field.to_sym, :has_one) }
|
18
|
+
model.active_storage_has_many_fields.each { |field| configure_active_storage_field(field.to_sym, :has_many) }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def configure_active_storage_field(field, field_type)
|
24
|
+
columns << field
|
25
|
+
columns.exclude "#{field}_attachment#{'s' if field_type == :has_many}".to_sym
|
26
|
+
columns.exclude "#{field}_blob#{'s' if field_type == :has_many}".to_sym
|
27
|
+
columns[field].includes ||= ["#{field}_attachment#{'s' if field_type == :has_many}".to_sym, "#{field}_blob#{'s' if field_type == :has_many}".to_sym]
|
28
|
+
columns[field].form_ui ||= "active_storage_#{field_type}".to_sym
|
29
|
+
columns[field].params.add "delete_#{field}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ActiveScaffold
|
2
|
+
module Bridges
|
3
|
+
class ActiveStorage
|
4
|
+
module ActiveStorageBridgeHelpers
|
5
|
+
mattr_accessor :thumbnail_variant
|
6
|
+
self.thumbnail_variant = {resize_to_limit: [nil, 30]}
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# has_one :"#{name}_attachment", -> { where(name: name) }, class_name: "ActiveStorage::Attachment", as: :record, inverse_of: :record, dependent: false
|
10
|
+
def active_storage_has_one_fields(klass)
|
11
|
+
klass.reflect_on_all_associations(:has_one)&.select { |reflection| reflection.class_name == 'ActiveStorage::Attachment' } &.collect { |association| association.name[0..-12] } || []
|
12
|
+
end
|
13
|
+
|
14
|
+
# has_many :"#{name}_attachments", -> { where(name: name) }, as: :record, class_name: "ActiveStorage::Attachment", inverse_of: :record, dependent: false do
|
15
|
+
def active_storage_has_many_fields(klass)
|
16
|
+
klass.reflect_on_all_associations(:has_many)&.select { |reflection| reflection.class_name == 'ActiveStorage::Attachment' } &.collect { |association| association.name[0..-13] } || []
|
17
|
+
end
|
18
|
+
|
19
|
+
def klass_has_active_storage_fields?(klass)
|
20
|
+
active_storage_has_one_fields(klass).present? || active_storage_has_many_fields(klass).present?
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_delete_helpers(klass)
|
24
|
+
(active_storage_has_one_fields(klass) | active_storage_has_many_fields(klass)).each do |field|
|
25
|
+
klass.send :class_eval, <<-CODE, __FILE__, __LINE__ + 1 unless klass.method_defined?(:"#{field}_with_delete=")
|
26
|
+
attr_reader :delete_#{field}
|
27
|
+
|
28
|
+
def delete_#{field}=(value)
|
29
|
+
value = (value=="true") if String===value
|
30
|
+
return unless value
|
31
|
+
|
32
|
+
# passing nil to the file column causes the file to be deleted.
|
33
|
+
self.#{field}.purge
|
34
|
+
end
|
35
|
+
CODE
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def active_storage_has_one_fields
|
41
|
+
@active_storage_has_one_fields ||= ActiveStorageBridgeHelpers.active_storage_has_one_fields(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
def active_storage_has_many_fields
|
45
|
+
@active_storage_has_many_fields ||= ActiveStorageBridgeHelpers.active_storage_has_many_fields(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
def generate_delete_helpers
|
49
|
+
ActiveStorageBridgeHelpers.generate_delete_helpers(self)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|