active_scaffold 3.6.0.pre → 3.6.0.rc1
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} +39 -0
- data/app/assets/javascripts/active_scaffold.js.erb +0 -1
- data/app/assets/javascripts/jquery/active_scaffold.js +35 -4
- 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/_list_header.html.erb +5 -7
- 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/_refresh_list.js.erb +4 -0
- 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 +8 -3
- data/lib/active_scaffold/actions/common_search.rb +11 -8
- data/lib/active_scaffold/actions/core.rb +79 -51
- data/lib/active_scaffold/actions/create.rb +27 -27
- data/lib/active_scaffold/actions/delete.rb +1 -1
- data/lib/active_scaffold/actions/field_search.rb +52 -42
- data/lib/active_scaffold/actions/list.rb +106 -23
- data/lib/active_scaffold/actions/nested.rb +59 -42
- data/lib/active_scaffold/actions/show.rb +3 -3
- data/lib/active_scaffold/actions/subform.rb +9 -16
- data/lib/active_scaffold/actions/update.rb +95 -77
- data/lib/active_scaffold/attribute_params.rb +93 -68
- data/lib/active_scaffold/bridges/active_storage.rb +6 -0
- data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +33 -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 +1 -0
- data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
- data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +6 -0
- data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
- data/lib/active_scaffold/bridges/date_picker/helper.rb +46 -41
- data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
- data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +2 -2
- 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 +3 -1
- data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
- data/lib/active_scaffold/bridges/record_select/helpers.rb +3 -7
- data/lib/active_scaffold/bridges/shared/date_bridge.rb +19 -18
- data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
- data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +20 -3
- data/lib/active_scaffold/config/base.rb +58 -34
- data/lib/active_scaffold/config/core.rb +31 -12
- data/lib/active_scaffold/config/delete.rb +12 -1
- data/lib/active_scaffold/config/list.rb +17 -7
- data/lib/active_scaffold/config/mark.rb +1 -1
- data/lib/active_scaffold/configurable.rb +5 -3
- data/lib/active_scaffold/constraints.rb +21 -19
- data/lib/active_scaffold/core.rb +35 -26
- data/lib/active_scaffold/data_structures/action_columns.rb +1 -1
- data/lib/active_scaffold/data_structures/action_link.rb +34 -16
- data/lib/active_scaffold/data_structures/action_links.rb +9 -11
- data/lib/active_scaffold/data_structures/association/abstract.rb +35 -13
- data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
- data/lib/active_scaffold/data_structures/association/active_record.rb +5 -1
- data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
- data/lib/active_scaffold/data_structures/column.rb +49 -58
- data/lib/active_scaffold/data_structures/columns.rb +3 -2
- data/lib/active_scaffold/data_structures/nested_info.rb +20 -18
- data/lib/active_scaffold/data_structures/sorting.rb +5 -0
- data/lib/active_scaffold/delayed_setup.rb +16 -6
- data/lib/active_scaffold/extensions/action_controller_rendering.rb +1 -1
- data/lib/active_scaffold/extensions/action_view_rendering.rb +34 -14
- data/lib/active_scaffold/extensions/cow_proxy.rb +50 -2
- data/lib/active_scaffold/extensions/localize.rb +3 -1
- data/lib/active_scaffold/extensions/routing_mapper.rb +2 -2
- data/lib/active_scaffold/extensions/to_label.rb +3 -2
- data/lib/active_scaffold/finder.rb +81 -46
- data/lib/active_scaffold/helpers/action_link_helpers.rb +47 -21
- data/lib/active_scaffold/helpers/association_helpers.rb +13 -11
- data/lib/active_scaffold/helpers/controller_helpers.rb +14 -11
- data/lib/active_scaffold/helpers/form_column_helpers.rb +133 -99
- data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
- data/lib/active_scaffold/helpers/id_helpers.rb +4 -0
- data/lib/active_scaffold/helpers/list_column_helpers.rb +76 -49
- data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
- data/lib/active_scaffold/helpers/search_column_helpers.rb +25 -30
- data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
- data/lib/active_scaffold/helpers/view_helpers.rb +31 -22
- data/lib/active_scaffold/orm_checks.rb +2 -2
- data/lib/active_scaffold/paginator.rb +1 -3
- data/lib/active_scaffold/registry.rb +11 -0
- data/lib/active_scaffold/responds_to_parent.rb +6 -5
- data/lib/active_scaffold/tableless.rb +6 -8
- data/lib/active_scaffold/version.rb +1 -1
- data/shoulda_macros/macros.rb +3 -1
- data/test/bridges/paperclip_test.rb +1 -1
- data/test/company.rb +2 -2
- data/test/data_structures/action_columns_test.rb +2 -2
- data/test/data_structures/column_test.rb +3 -6
- data/test/data_structures/columns_test.rb +2 -2
- 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 +2 -2
- data/test/misc/attribute_params_test.rb +4 -0
- data/test/misc/configurable_test.rb +10 -10
- data/test/misc/convert_numbers_format_test.rb +4 -0
- 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 +3 -1
- data/test/mock_app/config/application.rb +1 -0
- 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 +2 -1
- metadata +22 -12
- data/app/assets/javascripts/prototype/rico_corner.js +0 -370
- data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -5
@@ -19,11 +19,11 @@ module ActiveScaffold::Actions
|
|
19
19
|
protected
|
20
20
|
|
21
21
|
def show_respond_to_json
|
22
|
-
|
22
|
+
response_to_api(:json, show_columns_names)
|
23
23
|
end
|
24
24
|
|
25
25
|
def show_respond_to_xml
|
26
|
-
|
26
|
+
response_to_api(:xml, show_columns_names)
|
27
27
|
end
|
28
28
|
|
29
29
|
def show_respond_to_js
|
@@ -58,7 +58,7 @@ module ActiveScaffold::Actions
|
|
58
58
|
private
|
59
59
|
|
60
60
|
def show_authorized_filter
|
61
|
-
link = active_scaffold_config.show.link || active_scaffold_config.show.class.link
|
61
|
+
link = active_scaffold_config.show.link || self.class.active_scaffold_config.show.class.link
|
62
62
|
raise ActiveScaffold::ActionNotAllowed unless Array(send(link.security_method))[0]
|
63
63
|
end
|
64
64
|
|
@@ -16,25 +16,18 @@ module ActiveScaffold::Actions
|
|
16
16
|
create_association_with_parent @parent_record if nested?
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
cache_generated_id(@parent_record, params[:generated_id]) if @parent_record.new_record?
|
20
20
|
@column = active_scaffold_config.columns[params[:child_association]]
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
@record = @column.association.klass.find(params[:associated_id])
|
25
|
-
if (reverse = @column.association.reverse_association)
|
26
|
-
if reverse.collection?
|
27
|
-
@record.send(reverse.name) << @parent_record
|
28
|
-
elsif @column.association.belongs_to?
|
29
|
-
@parent_record.send("#{@column.name}=", @record)
|
30
|
-
else
|
31
|
-
@record.send("#{reverse.name}=", @parent_record)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
else
|
35
|
-
@record = build_associated(@column.association, @parent_record)
|
36
|
-
end
|
22
|
+
@record = find_associated_record if params[:associated_id]
|
23
|
+
@record ||= build_associated(@column.association, @parent_record)
|
37
24
|
@scope = params[:scope]
|
38
25
|
end
|
26
|
+
|
27
|
+
def find_associated_record
|
28
|
+
@column.association.klass.find(params[:associated_id]).tap do |record|
|
29
|
+
save_record_to_association(record, @column.association.reverse_association, @parent_record, @column.association)
|
30
|
+
end
|
31
|
+
end
|
39
32
|
end
|
40
33
|
end
|
@@ -35,51 +35,59 @@ module ActiveScaffold::Actions
|
|
35
35
|
render(:partial => 'update_form')
|
36
36
|
end
|
37
37
|
|
38
|
+
def update_respond_on_iframe
|
39
|
+
do_refresh_list if successful? && active_scaffold_config.update.refresh_list && !render_parent?
|
40
|
+
responds_to_parent do
|
41
|
+
render :action => 'on_update', :formats => [:js], :layout => false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
38
45
|
def update_respond_to_html
|
39
|
-
if
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
else # just a regular post
|
45
|
-
if successful?
|
46
|
-
message = as_(:updated_model, :model => ERB::Util.h(@record.to_label))
|
47
|
-
if params[:dont_close]
|
48
|
-
flash.now[:info] = message
|
49
|
-
render(:action => 'update')
|
50
|
-
else
|
51
|
-
flash[:info] = message
|
52
|
-
return_to_main
|
53
|
-
end
|
54
|
-
else
|
46
|
+
if successful? # just a regular post
|
47
|
+
message = as_(:updated_model, :model => ERB::Util.h(@record.to_label))
|
48
|
+
if params[:dont_close]
|
49
|
+
flash.now[:info] = message
|
55
50
|
render(:action => 'update')
|
51
|
+
else
|
52
|
+
flash[:info] = message
|
53
|
+
return_to_main
|
56
54
|
end
|
55
|
+
else
|
56
|
+
render(:action => 'update')
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
+
def record_to_refresh_on_update
|
61
|
+
if update_refresh_list?
|
62
|
+
do_refresh_list
|
63
|
+
else
|
64
|
+
reload_record_on_update
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def reload_record_on_update
|
69
|
+
@updated_record = @record
|
70
|
+
# get_row so associations are cached like in list action
|
71
|
+
# if record doesn't fullfil current conditions remove it from list
|
72
|
+
@record = get_row
|
73
|
+
rescue ActiveRecord::RecordNotFound
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
60
77
|
def update_respond_to_js
|
61
78
|
if successful?
|
62
|
-
if !render_parent? && active_scaffold_config.actions.include?(:list)
|
63
|
-
if update_refresh_list?
|
64
|
-
do_refresh_list
|
65
|
-
else
|
66
|
-
@updated_record = @record
|
67
|
-
# get_row so associations are cached like in list action
|
68
|
-
# if record doesn't fullfil current conditions remove it from list
|
69
|
-
@record = get_row rescue nil # rubocop:disable Style/RescueModifier
|
70
|
-
end
|
71
|
-
end
|
79
|
+
record_to_refresh_on_update if !render_parent? && active_scaffold_config.actions.include?(:list)
|
72
80
|
flash.now[:info] = as_(:updated_model, :model => ERB::Util.h((@updated_record || @record).to_label)) if active_scaffold_config.update.persistent
|
73
81
|
end
|
74
82
|
render :action => 'on_update'
|
75
83
|
end
|
76
84
|
|
77
85
|
def update_respond_to_xml
|
78
|
-
|
86
|
+
response_to_api(:xml, update_columns_names)
|
79
87
|
end
|
80
88
|
|
81
89
|
def update_respond_to_json
|
82
|
-
|
90
|
+
response_to_api(:json, update_columns_names)
|
83
91
|
end
|
84
92
|
|
85
93
|
def update_columns_names
|
@@ -92,45 +100,47 @@ module ActiveScaffold::Actions
|
|
92
100
|
@record = find_if_allowed(params[:id], :update)
|
93
101
|
end
|
94
102
|
|
95
|
-
# A complex method to update a record. The complexity comes from the support for subforms,
|
103
|
+
# A complex method to update a record. The complexity comes from the support for subforms,
|
104
|
+
# and saving associated records.
|
96
105
|
# If you want to customize this algorithm, consider using the +before_update_save+ callback
|
97
106
|
def do_update
|
98
107
|
do_edit
|
99
108
|
update_save
|
100
109
|
end
|
101
110
|
|
102
|
-
def update_save(
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@record = update_record_from_params(@record, active_scaffold_config.update.columns, attributes) unless options[:no_record_param_update]
|
107
|
-
before_update_save(@record)
|
108
|
-
# errors to @record can be added by update_record_from_params when association fails to set and ActiveRecord::RecordNotSaved is raised
|
109
|
-
self.successful = [@record.keeping_errors { @record.valid? }, @record.associated_valid?].all? # this syntax avoids a short-circuit
|
110
|
-
if successful?
|
111
|
-
@record.save! && @record.save_associated!
|
112
|
-
after_update_save(@record)
|
113
|
-
else
|
114
|
-
# some associations such as habtm are saved before saved is called on parent object
|
115
|
-
# we have to revert these changes if validation fails
|
116
|
-
raise ActiveRecord::Rollback, "don't save habtm associations unless record is valid"
|
117
|
-
end
|
111
|
+
def update_save(attributes: params[:record], no_record_param_update: false)
|
112
|
+
active_scaffold_config.model.transaction do
|
113
|
+
unless no_record_param_update
|
114
|
+
@record = update_record_from_params(@record, active_scaffold_config.update.columns, attributes)
|
118
115
|
end
|
119
|
-
|
120
|
-
@record
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
116
|
+
before_update_save(@record)
|
117
|
+
# errors to @record can be added by update_record_from_params when association fails
|
118
|
+
# to set and ActiveRecord::RecordNotSaved is raised
|
119
|
+
# this syntax avoids a short-circuit, so we run validations on record and associations
|
120
|
+
self.successful = [@record.keeping_errors { @record.valid? }, @record.associated_valid?].all?
|
121
|
+
|
122
|
+
unless successful?
|
123
|
+
# some associations such as habtm are saved before saved is called on parent object
|
124
|
+
# we have to revert these changes if validation fails
|
125
|
+
raise ActiveRecord::Rollback, "don't save habtm associations unless record is valid"
|
127
126
|
end
|
128
|
-
@record.
|
129
|
-
|
130
|
-
rescue ActiveRecord::ActiveRecordError => ex
|
131
|
-
flash[:error] = ex.message
|
132
|
-
self.successful = false
|
127
|
+
@record.save! && @record.save_associated!
|
128
|
+
after_update_save(@record)
|
133
129
|
end
|
130
|
+
rescue ActiveRecord::StaleObjectError
|
131
|
+
@record.errors.add(:base, as_(:version_inconsistency))
|
132
|
+
self.successful = false
|
133
|
+
rescue ActiveRecord::RecordNotSaved => exception
|
134
|
+
logger.warn do
|
135
|
+
"\n\n#{exception.class} (#{exception.message}):\n " +
|
136
|
+
Rails.backtrace_cleaner.clean(exception.backtrace).join("\n ") +
|
137
|
+
"\n\n"
|
138
|
+
end
|
139
|
+
@record.errors.add(:base, as_(:record_not_saved)) if @record.errors.empty?
|
140
|
+
self.successful = false
|
141
|
+
rescue ActiveRecord::ActiveRecordError => ex
|
142
|
+
flash[:error] = ex.message
|
143
|
+
self.successful = false
|
134
144
|
end
|
135
145
|
|
136
146
|
def do_update_column
|
@@ -140,25 +150,10 @@ module ActiveScaffold::Actions
|
|
140
150
|
params.delete(:original_html)
|
141
151
|
params.delete(:original_value)
|
142
152
|
@column = active_scaffold_config.columns[column]
|
143
|
-
|
144
|
-
return unless
|
145
|
-
if @column.delegated_association
|
146
|
-
value_record = @record.send(@column.delegated_association.name)
|
147
|
-
value_record ||= @record.association(@column.delegated_association.name).build
|
148
|
-
return unless value_record.authorized_for?(:crud_type => :update, :column => column)
|
149
|
-
end
|
150
|
-
|
151
|
-
value ||=
|
152
|
-
unless @column.column.nil? || @column.column.null
|
153
|
-
default_val = @column.column.default
|
154
|
-
default_val = ActiveScaffold::Core.column_type_cast default_val, @column.column
|
155
|
-
default_val == true ? false : default_val
|
156
|
-
end
|
157
|
-
unless @column.nil?
|
158
|
-
value = column_value_from_param_value(value_record, @column, value)
|
159
|
-
value = [] if value.nil? && @column.form_ui && @column.association&.collection?
|
160
|
-
end
|
153
|
+
value_record = record_for_update_column
|
154
|
+
return unless value_record
|
161
155
|
|
156
|
+
value = value_for_update_column(value, @column, value_record)
|
162
157
|
value_record.send("#{@column.name}=", value)
|
163
158
|
before_update_save(@record)
|
164
159
|
self.successful = value_record.save
|
@@ -175,6 +170,29 @@ module ActiveScaffold::Actions
|
|
175
170
|
after_update_save(@record)
|
176
171
|
end
|
177
172
|
|
173
|
+
def record_for_update_column
|
174
|
+
@record = find_if_allowed(params[:id], :read)
|
175
|
+
return unless @record.authorized_for?(:crud_type => :update, :column => @column.name)
|
176
|
+
|
177
|
+
if @column.delegated_association
|
178
|
+
value_record = @record.send(@column.delegated_association.name)
|
179
|
+
value_record ||= @record.association(@column.delegated_association.name).build
|
180
|
+
value_record if value_record.authorized_for?(:crud_type => :update, :column => @column.name)
|
181
|
+
else
|
182
|
+
@record
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def value_for_update_column(param_value, column, record)
|
187
|
+
unless param_value
|
188
|
+
param_value = ActiveScaffold::Core.column_type_cast @column.default_for_empty_value, @column.column
|
189
|
+
param_value = false if param_value == true
|
190
|
+
end
|
191
|
+
value = column_value_from_param_value(record, @column, param_value)
|
192
|
+
value = [] if value.nil? && @column.form_ui && @column.association&.collection?
|
193
|
+
value
|
194
|
+
end
|
195
|
+
|
178
196
|
# override this method if you want to inject data in the record (or its associated objects) before the save
|
179
197
|
def before_update_save(record); end
|
180
198
|
|
@@ -199,7 +217,7 @@ module ActiveScaffold::Actions
|
|
199
217
|
private
|
200
218
|
|
201
219
|
def update_authorized_filter
|
202
|
-
link = active_scaffold_config.update.link || active_scaffold_config.update.class.link
|
220
|
+
link = active_scaffold_config.update.link || self.class.active_scaffold_config.update.class.link
|
203
221
|
raise ActiveScaffold::ActionNotAllowed unless Array(send(link.security_method))[0]
|
204
222
|
end
|
205
223
|
|
@@ -71,6 +71,15 @@ module ActiveScaffold
|
|
71
71
|
!params_hash?(value) && association.belongs_to? && association.counter_cache_hack?
|
72
72
|
end
|
73
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
|
+
|
74
83
|
# Takes attributes (as from params[:record]) and applies them to the parent_record. Also looks for
|
75
84
|
# association attributes and attempts to instantiate them as associated objects.
|
76
85
|
#
|
@@ -80,28 +89,22 @@ module ActiveScaffold
|
|
80
89
|
crud_type = parent_record.new_record? ? :create : :update
|
81
90
|
return parent_record unless parent_record.authorized_for?(:crud_type => crud_type)
|
82
91
|
|
83
|
-
|
84
|
-
attributes.each do |k, v|
|
85
|
-
next unless k.include? '('
|
86
|
-
column_name = k.split('(').first
|
87
|
-
multi_parameter_attributes[column_name] ||= []
|
88
|
-
multi_parameter_attributes[column_name] << [k, v]
|
89
|
-
end
|
92
|
+
multi_parameter_attrs = multi_parameter_attributes(attributes)
|
90
93
|
|
91
94
|
columns.each_column(for: parent_record, crud_type: crud_type, flatten: true) do |column|
|
92
95
|
begin
|
93
96
|
# Set any passthrough parameters that may be associated with this column (ie, file column "keep" and "temp" attributes)
|
94
|
-
|
95
|
-
column.params.each { |p| parent_record.send("#{p}=", attributes[p]) if attributes.key? p }
|
96
|
-
end
|
97
|
+
column.params.select { |p| attributes.key? p }.each { |p| parent_record.send("#{p}=", attributes[p]) }
|
97
98
|
|
98
|
-
if
|
99
|
-
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])
|
100
101
|
elsif attributes.key? column.name
|
101
|
-
|
102
|
+
update_column_from_params(parent_record, column, attributes[column.name], avoid_changes)
|
102
103
|
end
|
103
104
|
rescue StandardError => e
|
104
|
-
|
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}"
|
105
108
|
raise
|
106
109
|
end
|
107
110
|
end
|
@@ -111,33 +114,40 @@ module ActiveScaffold
|
|
111
114
|
|
112
115
|
def update_column_from_params(parent_record, column, attribute, avoid_changes = false)
|
113
116
|
value = column_value_from_param_value(parent_record, column, attribute, avoid_changes)
|
114
|
-
if
|
115
|
-
|
116
|
-
|
117
|
-
|
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)
|
118
136
|
parent_record.send "#{column.association.foreign_key}=", value&.id
|
119
137
|
parent_record.association(column.name).target = value
|
120
|
-
elsif column.association
|
138
|
+
elsif column.association.collection? && column.association.through_singular?
|
121
139
|
through = column.association.through_reflection.name
|
122
140
|
through_record = parent_record.send(through)
|
123
141
|
through_record ||= parent_record.send "build_#{through}"
|
124
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)
|
125
145
|
else
|
126
|
-
|
127
|
-
if column.association && hack_for_has_many_counter_cache?(parent_record, column)
|
128
|
-
hack_for_has_many_counter_cache(parent_record, column, value)
|
129
|
-
else
|
130
|
-
parent_record.send "#{column.name}=", value
|
131
|
-
end
|
132
|
-
rescue ActiveRecord::RecordNotSaved
|
133
|
-
parent_record.errors.add column.name, :invalid
|
134
|
-
parent_record.association(column.name).target = value if column.association
|
135
|
-
end
|
136
|
-
end
|
137
|
-
if column.association&.reverse_association&.belongs_to?
|
138
|
-
Array(value).each { |v| v.send("#{column.association.reverse}=", parent_record) if v.new_record? }
|
146
|
+
parent_record.send "#{column.name}=", value
|
139
147
|
end
|
140
|
-
|
148
|
+
rescue ActiveRecord::RecordNotSaved
|
149
|
+
parent_record.errors.add column.name, :invalid
|
150
|
+
parent_record.association(column.name).target = value
|
141
151
|
end
|
142
152
|
|
143
153
|
def column_value_from_param_value(parent_record, column, value, avoid_changes = false)
|
@@ -172,27 +182,24 @@ module ActiveScaffold
|
|
172
182
|
Date.parse("#{value}-01")
|
173
183
|
end
|
174
184
|
|
175
|
-
def
|
176
|
-
if column.association
|
177
|
-
if value.present?
|
178
|
-
|
179
|
-
class_name = parent_record.send(column.association.foreign_type)
|
180
|
-
class_name.constantize.find(value) if class_name.present?
|
181
|
-
else
|
182
|
-
# it's a single id
|
183
|
-
column.association.klass.find(value)
|
184
|
-
end
|
185
|
-
end
|
186
|
-
elsif column.association&.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?
|
187
189
|
column_plural_assocation_value_from_value(column, Array(value))
|
188
|
-
|
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?
|
189
197
|
column.number_to_native(value)
|
190
|
-
|
198
|
+
elsif value.is_a?(String) && value.empty? && !column.virtual?
|
191
199
|
# convert empty strings into nil. this works better with 'null => true' columns (and validations),
|
192
200
|
# for 'null => false' columns is just converted to default value from column
|
193
|
-
|
194
|
-
|
195
|
-
end
|
201
|
+
column.default_for_empty_value
|
202
|
+
else
|
196
203
|
value
|
197
204
|
end
|
198
205
|
end
|
@@ -248,17 +255,22 @@ module ActiveScaffold
|
|
248
255
|
if params.key? klass.primary_key
|
249
256
|
record_from_current_or_find(klass, params[klass.primary_key], current)
|
250
257
|
elsif klass.authorized_for?(:crud_type => :create)
|
251
|
-
parent_column.association
|
258
|
+
association = parent_column.association
|
259
|
+
record = association.klass.new
|
260
|
+
if association.reverse_association&.belongs_to? && (association.collection? || current.nil?)
|
261
|
+
record.send("#{parent_column.association.reverse}=", parent_record)
|
262
|
+
end
|
263
|
+
record
|
252
264
|
end
|
253
265
|
end
|
254
266
|
|
255
267
|
# Attempts to find an instance of klass (which must be an ActiveRecord object) with id primary key
|
256
268
|
# Returns record from current if it's included or find from DB
|
257
269
|
def record_from_current_or_find(klass, id, current)
|
258
|
-
if current
|
270
|
+
if current.is_a?(ActiveRecord::Base) && current.id.to_s == id
|
259
271
|
# modifying the current object of a singular association
|
260
272
|
current
|
261
|
-
elsif current
|
273
|
+
elsif current.respond_to?(:any?) && current.any? { |o| o.id.to_s == id }
|
262
274
|
# modifying one of the current objects in a plural association
|
263
275
|
current.detect { |o| o.id.to_s == id }
|
264
276
|
else # attaching an existing but not-current object
|
@@ -266,10 +278,13 @@ module ActiveScaffold
|
|
266
278
|
end
|
267
279
|
end
|
268
280
|
|
269
|
-
def save_record_to_association(record, association, value)
|
270
|
-
|
281
|
+
def save_record_to_association(record, association, value, reverse = nil)
|
282
|
+
return unless association
|
283
|
+
if association.collection?
|
271
284
|
record.send(association.name) << value
|
272
|
-
elsif
|
285
|
+
elsif reverse&.belongs_to?
|
286
|
+
value.send("#{reverse.name}=", record)
|
287
|
+
else
|
273
288
|
record.send("#{association.name}=", value)
|
274
289
|
end
|
275
290
|
end
|
@@ -277,25 +292,17 @@ module ActiveScaffold
|
|
277
292
|
# Determines whether the given attributes hash is "empty".
|
278
293
|
# This isn't a literal emptiness - it's an attempt to discern whether the user intended it to be empty or not.
|
279
294
|
def attributes_hash_is_empty?(hash, klass)
|
280
|
-
# old style date form management... ignore them too
|
281
|
-
part_ignore_column_types = [:datetime, :date, :time, Time, Date]
|
282
|
-
|
283
295
|
hash.all? do |key, value|
|
284
296
|
# convert any possible multi-parameter attributes like 'created_at(5i)' to simply 'created_at'
|
285
|
-
|
286
|
-
column_name = parts.first
|
287
|
-
column = ActiveScaffold::OrmChecks.columns_hash(klass)[column_name]
|
288
|
-
column_type = ActiveScaffold::OrmChecks.column_type(klass, column_name) if column
|
297
|
+
column_name = key.to_s.split('(', 2)[0]
|
289
298
|
|
290
299
|
# datetimes will always have a value. so we ignore them when checking whether the hash is empty.
|
291
300
|
# this could be a bad idea. but the current situation (excess record entry) seems worse.
|
292
|
-
next true if
|
301
|
+
next true if mulitpart_ignored?(key, klass)
|
293
302
|
|
294
303
|
# defaults are pre-filled on the form. we can't use them to determine if the user intends a new row.
|
295
304
|
# booleans always have value, so they are ignored if not changed from default
|
296
|
-
|
297
|
-
casted_value = column ? ActiveScaffold::Core.column_type_cast(value, column) : value
|
298
|
-
next true if casted_value == default_value
|
305
|
+
next true if default_value?(column_name, klass, value)
|
299
306
|
|
300
307
|
if params_hash? value
|
301
308
|
attributes_hash_is_empty?(value, klass)
|
@@ -307,7 +314,25 @@ module ActiveScaffold
|
|
307
314
|
end
|
308
315
|
end
|
309
316
|
|
310
|
-
|
317
|
+
# old style date form management... ignore them
|
318
|
+
MULTIPART_IGNORE_TYPES = [:datetime, :date, :time, Time, Date].freeze
|
319
|
+
|
320
|
+
def mulitpart_ignored?(param_name, klass)
|
321
|
+
column_name, multipart = param_name.to_s.split('(', 2)
|
322
|
+
return false unless multipart
|
323
|
+
column_type = ActiveScaffold::OrmChecks.column_type(klass, column_name)
|
324
|
+
MULTIPART_IGNORE_TYPES.include?(column_type) if column_type
|
325
|
+
end
|
326
|
+
|
327
|
+
def default_value?(column_name, klass, value)
|
328
|
+
column = ActiveScaffold::OrmChecks.columns_hash(klass)[column_name]
|
329
|
+
default_value = column_default_value(column_name, klass)
|
330
|
+
casted_value = ActiveScaffold::Core.column_type_cast(value, column)
|
331
|
+
casted_value == default_value
|
332
|
+
end
|
333
|
+
|
334
|
+
def column_default_value(column_name, klass)
|
335
|
+
column = ActiveScaffold::OrmChecks.columns_hash(klass)[column_name]
|
311
336
|
return unless column
|
312
337
|
if ActiveScaffold::OrmChecks.mongoid? klass
|
313
338
|
column.default_val
|