active_scaffold 3.5.2 → 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} +66 -0
- data/README.md +17 -7
- data/app/assets/javascripts/active_scaffold.js.erb +0 -1
- data/app/assets/javascripts/jquery/active_scaffold.js +63 -6
- 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_footer.html.erb +3 -2
- data/app/views/active_scaffold_overrides/_form_association_record.html.erb +6 -6
- data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +1 -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/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 +1 -1
- 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 +89 -71
- 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 +12 -17
- 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 +104 -86
- 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 +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/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 +11 -9
- 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/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 +12 -16
- 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 +68 -29
- 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 +41 -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 +21 -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 +34 -14
- data/lib/active_scaffold/extensions/cow_proxy.rb +91 -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 +104 -73
- data/lib/active_scaffold/helpers/action_link_helpers.rb +62 -36
- data/lib/active_scaffold/helpers/association_helpers.rb +21 -19
- data/lib/active_scaffold/helpers/controller_helpers.rb +23 -10
- data/lib/active_scaffold/helpers/form_column_helpers.rb +157 -121
- 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 +82 -53
- 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 +38 -35
- 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 +67 -65
- 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 +1 -1
- 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/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 +6 -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 +3 -1
- 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 +10 -2
- metadata +56 -15
- data/app/assets/javascripts/prototype/rico_corner.js +0 -370
- data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -7
@@ -19,7 +19,7 @@ module ActiveScaffold::Actions
|
|
19
19
|
def do_search
|
20
20
|
if search_params.is_a?(String) && search_params.present?
|
21
21
|
query = search_params.to_s.strip
|
22
|
-
columns = active_scaffold_config.search.columns
|
22
|
+
columns = active_scaffold_config.search.columns.visible_columns
|
23
23
|
text_search = active_scaffold_config.search.text_search
|
24
24
|
query = query.split(active_scaffold_config.search.split_terms) if active_scaffold_config.search.split_terms
|
25
25
|
search_conditions = self.class.conditions_for_columns(query, columns, text_search)
|
@@ -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
|
@@ -35,7 +35,7 @@ module ActiveScaffold::Actions
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def show_columns_names
|
38
|
-
active_scaffold_config.show.columns.
|
38
|
+
active_scaffold_config.show.columns.visible_columns_names
|
39
39
|
end
|
40
40
|
|
41
41
|
# A simple method to retrieve and prepare a record for showing.
|
@@ -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
|
|
@@ -2,7 +2,9 @@ module ActiveScaffold::Actions
|
|
2
2
|
module Subform
|
3
3
|
def edit_associated
|
4
4
|
do_edit_associated
|
5
|
-
|
5
|
+
respond_to do |format|
|
6
|
+
format.js { render :action => 'edit_associated', :formats => [:js], :readonly => @column.association.readonly? }
|
7
|
+
end
|
6
8
|
end
|
7
9
|
|
8
10
|
protected
|
@@ -14,25 +16,18 @@ module ActiveScaffold::Actions
|
|
14
16
|
create_association_with_parent @parent_record if nested?
|
15
17
|
end
|
16
18
|
|
17
|
-
|
19
|
+
cache_generated_id(@parent_record, params[:generated_id]) if @parent_record.new_record?
|
18
20
|
@column = active_scaffold_config.columns[params[:child_association]]
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
@record = @column.association.klass.find(params[:associated_id])
|
23
|
-
if (reverse = @column.association.reverse_association)
|
24
|
-
if reverse.collection?
|
25
|
-
@record.send(reverse.name) << @parent_record
|
26
|
-
elsif @column.association.belongs_to?
|
27
|
-
@parent_record.send("#{@column.name}=", @record)
|
28
|
-
else
|
29
|
-
@record.send("#{reverse.name}=", @parent_record)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
else
|
33
|
-
@record = build_associated(@column.association, @parent_record)
|
34
|
-
end
|
22
|
+
@record = find_associated_record if params[:associated_id]
|
23
|
+
@record ||= build_associated(@column.association, @parent_record)
|
35
24
|
@scope = params[:scope]
|
36
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
|
37
32
|
end
|
38
33
|
end
|
@@ -35,54 +35,63 @@ 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
|
-
@record = get_row rescue nil # if record doesn't fullfil current conditions remove it from list
|
69
|
-
end
|
70
|
-
end
|
79
|
+
record_to_refresh_on_update if !render_parent? && active_scaffold_config.actions.include?(:list)
|
71
80
|
flash.now[:info] = as_(:updated_model, :model => ERB::Util.h((@updated_record || @record).to_label)) if active_scaffold_config.update.persistent
|
72
81
|
end
|
73
82
|
render :action => 'on_update'
|
74
83
|
end
|
75
84
|
|
76
85
|
def update_respond_to_xml
|
77
|
-
|
86
|
+
response_to_api(:xml, update_columns_names)
|
78
87
|
end
|
79
88
|
|
80
89
|
def update_respond_to_json
|
81
|
-
|
90
|
+
response_to_api(:json, update_columns_names)
|
82
91
|
end
|
83
92
|
|
84
93
|
def update_columns_names
|
85
|
-
active_scaffold_config.update.columns.
|
94
|
+
active_scaffold_config.update.columns.visible_columns_names
|
86
95
|
end
|
87
96
|
|
88
97
|
# A simple method to find and prepare a record for editing
|
@@ -91,45 +100,47 @@ module ActiveScaffold::Actions
|
|
91
100
|
@record = find_if_allowed(params[:id], :update)
|
92
101
|
end
|
93
102
|
|
94
|
-
# 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.
|
95
105
|
# If you want to customize this algorithm, consider using the +before_update_save+ callback
|
96
106
|
def do_update
|
97
107
|
do_edit
|
98
108
|
update_save
|
99
109
|
end
|
100
110
|
|
101
|
-
def update_save(
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
@record = update_record_from_params(@record, active_scaffold_config.update.columns, attributes) unless options[:no_record_param_update]
|
106
|
-
before_update_save(@record)
|
107
|
-
# errors to @record can be added by update_record_from_params when association fails to set and ActiveRecord::RecordNotSaved is raised
|
108
|
-
self.successful = [@record.keeping_errors { @record.valid? }, @record.associated_valid?].all? # this syntax avoids a short-circuit
|
109
|
-
if successful?
|
110
|
-
@record.save! && @record.save_associated!
|
111
|
-
after_update_save(@record)
|
112
|
-
else
|
113
|
-
# some associations such as habtm are saved before saved is called on parent object
|
114
|
-
# we have to revert these changes if validation fails
|
115
|
-
raise ActiveRecord::Rollback, "don't save habtm associations unless record is valid"
|
116
|
-
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)
|
117
115
|
end
|
118
|
-
|
119
|
-
@record
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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"
|
126
126
|
end
|
127
|
-
@record.
|
128
|
-
|
129
|
-
rescue ActiveRecord::ActiveRecordError => ex
|
130
|
-
flash[:error] = ex.message
|
131
|
-
self.successful = false
|
127
|
+
@record.save! && @record.save_associated!
|
128
|
+
after_update_save(@record)
|
132
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
|
133
144
|
end
|
134
145
|
|
135
146
|
def do_update_column
|
@@ -139,25 +150,10 @@ module ActiveScaffold::Actions
|
|
139
150
|
params.delete(:original_html)
|
140
151
|
params.delete(:original_value)
|
141
152
|
@column = active_scaffold_config.columns[column]
|
142
|
-
|
143
|
-
return unless
|
144
|
-
if @column.delegated_association
|
145
|
-
value_record = @record.send(@column.delegated_association.name)
|
146
|
-
value_record ||= @record.association(@column.delegated_association.name).build
|
147
|
-
return unless value_record.authorized_for?(:crud_type => :update, :column => column)
|
148
|
-
end
|
149
|
-
|
150
|
-
value ||=
|
151
|
-
unless @column.column.nil? || @column.column.null
|
152
|
-
default_val = @column.column.default
|
153
|
-
default_val = ActiveScaffold::Core.column_type_cast default_val, @column.column if Rails.version >= '4.2.0'
|
154
|
-
default_val == true ? false : default_val
|
155
|
-
end
|
156
|
-
unless @column.nil?
|
157
|
-
value = column_value_from_param_value(value_record, @column, value)
|
158
|
-
value = [] if value.nil? && @column.form_ui && @column.association.try(:collection?)
|
159
|
-
end
|
153
|
+
value_record = record_for_update_column
|
154
|
+
return unless value_record
|
160
155
|
|
156
|
+
value = value_for_update_column(value, @column, value_record)
|
161
157
|
value_record.send("#{@column.name}=", value)
|
162
158
|
before_update_save(@record)
|
163
159
|
self.successful = value_record.save
|
@@ -174,6 +170,29 @@ module ActiveScaffold::Actions
|
|
174
170
|
after_update_save(@record)
|
175
171
|
end
|
176
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
|
+
|
177
196
|
# override this method if you want to inject data in the record (or its associated objects) before the save
|
178
197
|
def before_update_save(record); end
|
179
198
|
|
@@ -198,7 +217,7 @@ module ActiveScaffold::Actions
|
|
198
217
|
private
|
199
218
|
|
200
219
|
def update_authorized_filter
|
201
|
-
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
|
202
221
|
raise ActiveScaffold::ActionNotAllowed unless Array(send(link.security_method))[0]
|
203
222
|
end
|
204
223
|
|
@@ -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
|
141
|
-
end
|
142
|
-
if column.association.try(:reverse_association).try(:belongs_to?)
|
143
|
-
Array(value).each { |v| v.send("#{column.association.reverse}=", parent_record) if v.new_record? }
|
146
|
+
parent_record.send "#{column.name}=", value
|
144
147
|
end
|
145
|
-
|
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
|
@@ -253,17 +255,22 @@ module ActiveScaffold
|
|
253
255
|
if params.key? klass.primary_key
|
254
256
|
record_from_current_or_find(klass, params[klass.primary_key], current)
|
255
257
|
elsif klass.authorized_for?(:crud_type => :create)
|
256
|
-
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
|
257
264
|
end
|
258
265
|
end
|
259
266
|
|
260
267
|
# Attempts to find an instance of klass (which must be an ActiveRecord object) with id primary key
|
261
268
|
# Returns record from current if it's included or find from DB
|
262
269
|
def record_from_current_or_find(klass, id, current)
|
263
|
-
if current
|
270
|
+
if current.is_a?(ActiveRecord::Base) && current.id.to_s == id
|
264
271
|
# modifying the current object of a singular association
|
265
272
|
current
|
266
|
-
elsif current
|
273
|
+
elsif current.respond_to?(:any?) && current.any? { |o| o.id.to_s == id }
|
267
274
|
# modifying one of the current objects in a plural association
|
268
275
|
current.detect { |o| o.id.to_s == id }
|
269
276
|
else # attaching an existing but not-current object
|
@@ -271,10 +278,13 @@ module ActiveScaffold
|
|
271
278
|
end
|
272
279
|
end
|
273
280
|
|
274
|
-
def save_record_to_association(record, association, value)
|
275
|
-
|
281
|
+
def save_record_to_association(record, association, value, reverse = nil)
|
282
|
+
return unless association
|
283
|
+
if association.collection?
|
276
284
|
record.send(association.name) << value
|
277
|
-
elsif
|
285
|
+
elsif reverse&.belongs_to?
|
286
|
+
value.send("#{reverse.name}=", record)
|
287
|
+
else
|
278
288
|
record.send("#{association.name}=", value)
|
279
289
|
end
|
280
290
|
end
|
@@ -282,25 +292,17 @@ module ActiveScaffold
|
|
282
292
|
# Determines whether the given attributes hash is "empty".
|
283
293
|
# This isn't a literal emptiness - it's an attempt to discern whether the user intended it to be empty or not.
|
284
294
|
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
295
|
hash.all? do |key, value|
|
289
296
|
# 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
|
297
|
+
column_name = key.to_s.split('(', 2)[0]
|
294
298
|
|
295
299
|
# datetimes will always have a value. so we ignore them when checking whether the hash is empty.
|
296
300
|
# this could be a bad idea. but the current situation (excess record entry) seems worse.
|
297
|
-
next true if
|
301
|
+
next true if mulitpart_ignored?(key, klass)
|
298
302
|
|
299
303
|
# defaults are pre-filled on the form. we can't use them to determine if the user intends a new row.
|
300
304
|
# 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
|
305
|
+
next true if default_value?(column_name, klass, value)
|
304
306
|
|
305
307
|
if params_hash? value
|
306
308
|
attributes_hash_is_empty?(value, klass)
|
@@ -312,18 +314,34 @@ module ActiveScaffold
|
|
312
314
|
end
|
313
315
|
end
|
314
316
|
|
315
|
-
|
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]
|
316
336
|
return unless column
|
317
337
|
if ActiveScaffold::OrmChecks.mongoid? klass
|
318
338
|
column.default_val
|
319
339
|
elsif ActiveScaffold::OrmChecks.active_record? klass
|
320
|
-
if Rails.version < '
|
321
|
-
column.default
|
322
|
-
elsif Rails.version < '5.0'
|
340
|
+
if Rails.version < '5.0'
|
323
341
|
column.type_cast_from_database(column.default)
|
324
342
|
else
|
325
343
|
column_type = ActiveScaffold::OrmChecks.column_type(klass, column_name)
|
326
|
-
cast_type =
|
344
|
+
cast_type = ActiveRecord::Type.lookup column_type
|
327
345
|
cast_type ? cast_type.deserialize(column.default) : column.default
|
328
346
|
end
|
329
347
|
end
|