active_scaffold 3.6.0.pre → 3.6.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|