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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG → CHANGELOG.rdoc} +39 -0
  3. data/app/assets/javascripts/active_scaffold.js.erb +0 -1
  4. data/app/assets/javascripts/jquery/active_scaffold.js +35 -4
  5. data/app/assets/stylesheets/active_scaffold_colors.scss +1 -1
  6. data/app/assets/stylesheets/active_scaffold_layout.css +52 -29
  7. data/app/views/active_scaffold_overrides/_list_header.html.erb +5 -7
  8. data/app/views/active_scaffold_overrides/_list_record.html.erb +4 -5
  9. data/app/views/active_scaffold_overrides/_list_with_header.html.erb +1 -1
  10. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +4 -0
  11. data/config/locales/de.yml +2 -1
  12. data/config/locales/en.yml +1 -0
  13. data/config/locales/es.yml +1 -0
  14. data/config/locales/fr.yml +2 -1
  15. data/config/locales/hu.yml +1 -0
  16. data/config/locales/ja.yml +1 -0
  17. data/config/locales/ru.yml +1 -0
  18. data/lib/active_scaffold.rb +8 -3
  19. data/lib/active_scaffold/actions/common_search.rb +11 -8
  20. data/lib/active_scaffold/actions/core.rb +79 -51
  21. data/lib/active_scaffold/actions/create.rb +27 -27
  22. data/lib/active_scaffold/actions/delete.rb +1 -1
  23. data/lib/active_scaffold/actions/field_search.rb +52 -42
  24. data/lib/active_scaffold/actions/list.rb +106 -23
  25. data/lib/active_scaffold/actions/nested.rb +59 -42
  26. data/lib/active_scaffold/actions/show.rb +3 -3
  27. data/lib/active_scaffold/actions/subform.rb +9 -16
  28. data/lib/active_scaffold/actions/update.rb +95 -77
  29. data/lib/active_scaffold/attribute_params.rb +93 -68
  30. data/lib/active_scaffold/bridges/active_storage.rb +6 -0
  31. data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +33 -0
  32. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +54 -0
  33. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +22 -0
  34. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +36 -0
  35. data/lib/active_scaffold/bridges/bitfields.rb +1 -0
  36. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
  37. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +6 -0
  38. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
  39. data/lib/active_scaffold/bridges/date_picker/helper.rb +46 -41
  40. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
  41. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +2 -2
  42. data/lib/active_scaffold/bridges/file_column/form_ui.rb +3 -3
  43. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +3 -1
  44. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
  45. data/lib/active_scaffold/bridges/record_select/helpers.rb +3 -7
  46. data/lib/active_scaffold/bridges/shared/date_bridge.rb +19 -18
  47. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  48. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +20 -3
  49. data/lib/active_scaffold/config/base.rb +58 -34
  50. data/lib/active_scaffold/config/core.rb +31 -12
  51. data/lib/active_scaffold/config/delete.rb +12 -1
  52. data/lib/active_scaffold/config/list.rb +17 -7
  53. data/lib/active_scaffold/config/mark.rb +1 -1
  54. data/lib/active_scaffold/configurable.rb +5 -3
  55. data/lib/active_scaffold/constraints.rb +21 -19
  56. data/lib/active_scaffold/core.rb +35 -26
  57. data/lib/active_scaffold/data_structures/action_columns.rb +1 -1
  58. data/lib/active_scaffold/data_structures/action_link.rb +34 -16
  59. data/lib/active_scaffold/data_structures/action_links.rb +9 -11
  60. data/lib/active_scaffold/data_structures/association/abstract.rb +35 -13
  61. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
  62. data/lib/active_scaffold/data_structures/association/active_record.rb +5 -1
  63. data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
  64. data/lib/active_scaffold/data_structures/column.rb +49 -58
  65. data/lib/active_scaffold/data_structures/columns.rb +3 -2
  66. data/lib/active_scaffold/data_structures/nested_info.rb +20 -18
  67. data/lib/active_scaffold/data_structures/sorting.rb +5 -0
  68. data/lib/active_scaffold/delayed_setup.rb +16 -6
  69. data/lib/active_scaffold/extensions/action_controller_rendering.rb +1 -1
  70. data/lib/active_scaffold/extensions/action_view_rendering.rb +34 -14
  71. data/lib/active_scaffold/extensions/cow_proxy.rb +50 -2
  72. data/lib/active_scaffold/extensions/localize.rb +3 -1
  73. data/lib/active_scaffold/extensions/routing_mapper.rb +2 -2
  74. data/lib/active_scaffold/extensions/to_label.rb +3 -2
  75. data/lib/active_scaffold/finder.rb +81 -46
  76. data/lib/active_scaffold/helpers/action_link_helpers.rb +47 -21
  77. data/lib/active_scaffold/helpers/association_helpers.rb +13 -11
  78. data/lib/active_scaffold/helpers/controller_helpers.rb +14 -11
  79. data/lib/active_scaffold/helpers/form_column_helpers.rb +133 -99
  80. data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
  81. data/lib/active_scaffold/helpers/id_helpers.rb +4 -0
  82. data/lib/active_scaffold/helpers/list_column_helpers.rb +76 -49
  83. data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
  84. data/lib/active_scaffold/helpers/search_column_helpers.rb +25 -30
  85. data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
  86. data/lib/active_scaffold/helpers/view_helpers.rb +31 -22
  87. data/lib/active_scaffold/orm_checks.rb +2 -2
  88. data/lib/active_scaffold/paginator.rb +1 -3
  89. data/lib/active_scaffold/registry.rb +11 -0
  90. data/lib/active_scaffold/responds_to_parent.rb +6 -5
  91. data/lib/active_scaffold/tableless.rb +6 -8
  92. data/lib/active_scaffold/version.rb +1 -1
  93. data/shoulda_macros/macros.rb +3 -1
  94. data/test/bridges/paperclip_test.rb +1 -1
  95. data/test/company.rb +2 -2
  96. data/test/data_structures/action_columns_test.rb +2 -2
  97. data/test/data_structures/column_test.rb +3 -6
  98. data/test/data_structures/columns_test.rb +2 -2
  99. data/test/extensions/active_record_test.rb +4 -4
  100. data/test/extensions/routing_mapper_test.rb +2 -2
  101. data/test/helpers/list_column_helpers_test.rb +3 -1
  102. data/test/misc/active_record_permissions_test.rb +2 -2
  103. data/test/misc/attribute_params_test.rb +4 -0
  104. data/test/misc/configurable_test.rb +10 -10
  105. data/test/misc/convert_numbers_format_test.rb +4 -0
  106. data/test/mock_app/app/assets/config/manifest.js +0 -0
  107. data/test/mock_app/app/controllers/cars_controller.rb +1 -0
  108. data/test/mock_app/app/controllers/people_controller.rb +3 -1
  109. data/test/mock_app/config/application.rb +1 -0
  110. data/test/mock_app/config/routes.rb +4 -1
  111. data/test/mock_app/db/schema.rb +2 -0
  112. data/test/performance/list_cars_performance_test.rb +34 -0
  113. data/test/performance/list_people_performance_test.rb +31 -0
  114. data/test/performance_test_help.rb +3 -0
  115. data/test/test_helper.rb +2 -1
  116. metadata +22 -12
  117. data/app/assets/javascripts/prototype/rico_corner.js +0 -370
  118. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -5
@@ -65,7 +65,7 @@ module ActiveScaffold::Actions
65
65
  private
66
66
 
67
67
  def delete_authorized_filter
68
- link = active_scaffold_config.delete.link || active_scaffold_config.delete.class.link
68
+ link = active_scaffold_config.delete.link || self.class.active_scaffold_config.delete.class.link
69
69
  raise ActiveScaffold::ActionNotAllowed unless Array(send(link.security_method))[0]
70
70
  end
71
71
 
@@ -23,11 +23,11 @@ module ActiveScaffold::Actions
23
23
  end
24
24
 
25
25
  def store_search_params_into_session
26
- set_field_search_default_params(active_scaffold_config.field_search.default_params) unless active_scaffold_config.field_search.default_params.nil?
26
+ init_field_search_params(active_scaffold_config.field_search.default_params) unless active_scaffold_config.field_search.default_params.nil?
27
27
  super
28
28
  end
29
29
 
30
- def set_field_search_default_params(default_params)
30
+ def init_field_search_params(default_params)
31
31
  return unless (params[:search].is_a?(String) || search_params.nil?) && params[:search].blank?
32
32
  params[:search] = default_params.is_a?(Proc) ? instance_eval(&default_params) : default_params
33
33
  end
@@ -57,23 +57,24 @@ module ActiveScaffold::Actions
57
57
  def custom_finder_options
58
58
  if grouped_search?
59
59
  group_sql = calculation_for_group_by(search_group_column&.field || search_group_name)
60
- group_by = group_sql.respond_to?(:to_sql) ? group_sql.to_sql : group_sql
61
-
62
- select_query = quoted_select_columns(search_group_column&.select_columns)
60
+ select_query = grouped_search_select
63
61
  select_query << group_sql.as(search_group_column.name.to_s) if search_group_column && group_sql.respond_to?(:to_sql)
64
- if active_scaffold_config.model.columns_hash.include?(active_scaffold_config.model.inheritance_column)
65
- select_query << active_scaffold_config.columns[active_scaffold_config.model.inheritance_column].field
66
- end
67
- grouped_columns_calculations.each do |name, part|
68
- select_query << (part.respond_to?(:as) ? part : Arel::Nodes::SqlLiteral.new(part)).as(name.to_s)
69
- end
70
-
71
- {group: group_by, select: select_query}
62
+ {group: group_sql, select: select_query}
72
63
  else
73
64
  super
74
65
  end
75
66
  end
76
67
 
68
+ def grouped_search_select
69
+ select_query = quoted_select_columns(search_group_column&.select_columns || [search_group_name])
70
+ if active_scaffold_config.model.columns_hash.include?(active_scaffold_config.model.inheritance_column)
71
+ select_query << active_scaffold_config.columns[active_scaffold_config.model.inheritance_column].field
72
+ end
73
+ grouped_columns_calculations.each do |name, part|
74
+ select_query << (part.respond_to?(:as) ? part : Arel::Nodes::SqlLiteral.new(part)).as(name.to_s)
75
+ end
76
+ end
77
+
77
78
  def grouped_columns_calculations
78
79
  @grouped_columns_calculations ||= list_columns[1..-1].each_with_object({}) do |c, h|
79
80
  h[c.name] = calculation_for_group_search(c)
@@ -113,12 +114,13 @@ module ActiveScaffold::Actions
113
114
  end
114
115
 
115
116
  def list_columns
116
- @list_columns ||= if grouped_search?
117
- columns = grouped_columns || super.select(&:calculation?)
118
- [search_group_column || search_group_name].concat columns
119
- else
120
- super
121
- end
117
+ @list_columns ||=
118
+ if grouped_search?
119
+ columns = grouped_columns || super.select(&:calculation?)
120
+ columns.unshift(search_group_column || search_group_name)
121
+ else
122
+ super
123
+ end
122
124
  end
123
125
 
124
126
  def grouped_columns
@@ -142,34 +144,42 @@ module ActiveScaffold::Actions
142
144
 
143
145
  def do_search
144
146
  if field_search_params.present?
145
- filtered_columns = []
146
- text_search = active_scaffold_config.field_search.text_search
147
- columns = active_scaffold_config.field_search.columns
148
- count_includes = active_scaffold_config.list.user.count_includes
149
- search_params.each do |key, value|
150
- next unless columns.include? key
151
- column = active_scaffold_config.columns[key]
152
- search_condition = self.class.condition_for_column(column, value, text_search)
153
- next if search_condition.blank?
154
-
155
- if count_includes.nil? && column.includes.present? && list_columns.include?(column) && !grouped_search?
156
- active_scaffold_references << column.includes
157
- elsif column.search_joins.present?
158
- active_scaffold_outer_joins << column.search_joins
159
- end
160
- active_scaffold_conditions << search_condition
161
- filtered_columns << column
162
- end
163
- if filtered_columns.present? || grouped_search?
164
- @filtered = active_scaffold_config.field_search.human_conditions ? filtered_columns : true
165
- end
166
-
167
- active_scaffold_config.list.user.page = nil
147
+ do_field_search
168
148
  else
169
149
  super
170
150
  end
171
151
  end
172
152
 
153
+ def do_field_search
154
+ filtered_columns = []
155
+ text_search = active_scaffold_config.field_search.text_search
156
+ columns = active_scaffold_config.field_search.columns
157
+ count_includes = active_scaffold_config.list.user.count_includes
158
+ search_params.each do |key, value|
159
+ next unless columns.include? key
160
+ column = active_scaffold_config.columns[key]
161
+ search_condition = self.class.condition_for_column(column, value, text_search)
162
+ next if search_condition.blank?
163
+
164
+ joins_for_search_on_column(column, count_includes)
165
+ active_scaffold_conditions << search_condition
166
+ filtered_columns << column
167
+ end
168
+ if filtered_columns.present? || grouped_search?
169
+ @filtered = active_scaffold_config.field_search.human_conditions ? filtered_columns : true
170
+ end
171
+
172
+ active_scaffold_config.list.user.page = nil
173
+ end
174
+
175
+ def joins_for_search_on_column(column, count_includes)
176
+ if count_includes.nil? && column.includes.present? && list_columns.include?(column) && !grouped_search?
177
+ active_scaffold_references << column.includes
178
+ elsif column.search_joins.present?
179
+ active_scaffold_outer_joins << column.search_joins
180
+ end
181
+ end
182
+
173
183
  def field_search_ignore?
174
184
  active_scaffold_config.list.always_show_search && active_scaffold_config.list.search_partial == 'field_search'
175
185
  end
@@ -2,7 +2,7 @@ module ActiveScaffold::Actions
2
2
  module List
3
3
  def self.included(base)
4
4
  base.before_action :list_authorized_filter, :only => :index
5
- base.helper_method :list_columns
5
+ base.helper_method :list_columns, :count_on_association_class?
6
6
  end
7
7
 
8
8
  def index
@@ -49,11 +49,11 @@ module ActiveScaffold::Actions
49
49
  end
50
50
 
51
51
  def list_respond_to_xml
52
- render :xml => response_object, :only => list_columns_names + [active_scaffold_config.model.primary_key], :include => association_columns(list_columns_names), :methods => virtual_columns(list_columns_names), :status => response_status
52
+ response_to_api(:xml, list_columns_names)
53
53
  end
54
54
 
55
55
  def list_respond_to_json
56
- render :json => response_object, :only => list_columns_names + [active_scaffold_config.model.primary_key], :include => association_columns(list_columns_names), :methods => virtual_columns(list_columns_names), :status => response_status
56
+ response_to_api(:json, list_columns_names)
57
57
  end
58
58
 
59
59
  def row_respond_to_html
@@ -82,7 +82,7 @@ module ActiveScaffold::Actions
82
82
  end
83
83
 
84
84
  def set_includes_for_sorting(columns, sorting)
85
- sorting.each_key do |col|
85
+ sorting.each_column do |col|
86
86
  next unless col.includes.present? && !columns.include?(col)
87
87
  if active_scaffold_config.model.connection.needs_order_expressions_in_select?
88
88
  active_scaffold_references << col.includes
@@ -93,7 +93,7 @@ module ActiveScaffold::Actions
93
93
  end
94
94
 
95
95
  def includes_need_join?(column, sorting = active_scaffold_config.list.user.sorting)
96
- sorting.sorts_on?(column) || scoped_habtm?(column)
96
+ (sorting.sorts_by_sql? && sorting.sorts_on?(column)) || scoped_habtm?(column)
97
97
  end
98
98
 
99
99
  def scoped_habtm?(column)
@@ -106,34 +106,111 @@ module ActiveScaffold::Actions
106
106
  super
107
107
  end
108
108
 
109
+ def current_page
110
+ set_includes_for_columns
111
+
112
+ page = find_page(find_page_options)
113
+ total_pages = page.pager.number_of_pages
114
+ if !page.pager.infinite? && !total_pages.zero? && page.number > total_pages
115
+ page = page.pager.last
116
+ active_scaffold_config.list.user.page = page.number
117
+ end
118
+ page
119
+ end
120
+
109
121
  # The actual algorithm to prepare for the list view
110
122
  def do_list
111
123
  # id: nil needed in params_for because rails reuse it even
112
124
  # if it was deleted from params (like do_refresh_list does)
113
125
  @remove_id_from_list_links = params[:id].blank?
114
- set_includes_for_columns
126
+ @page = current_page
127
+ @records = @page.items
128
+ cache_column_counts
129
+ end
130
+
131
+ def columns_to_cache_counts
132
+ list_columns.select do |col|
133
+ col.association&.collection? && col.includes.blank? && col.associated_number? &&
134
+ !ActiveScaffold::OrmChecks.tableless?(col.association.klass) &&
135
+ !col.association.reverse_association&.counter_cache
136
+ end
137
+ end
138
+
139
+ def cache_column_counts
140
+ @counts = columns_to_cache_counts.each_with_object({}) do |column, counts|
141
+ if ActiveScaffold::OrmChecks.active_record?(column.association.klass)
142
+ counts[column.name] = count_query_for_column(column).count
143
+ elsif ActiveScaffold::OrmChecks.mongoid?(column.association.klass)
144
+ counts[column.name] = mongoid_count_for_column(column)
145
+ end
146
+ end
147
+ end
148
+
149
+ def count_on_association_class?(column)
150
+ column.association.has_many? && !column.association.through? &&
151
+ (!column.association.as || column.association.reverse_association)
152
+ end
153
+
154
+ def count_query_for_column(column)
155
+ if count_on_association_class?(column)
156
+ count_query_on_association_class(column)
157
+ else
158
+ count_query_with_join(column)
159
+ end
160
+ end
161
+
162
+ def count_query_on_association_class(column)
163
+ key = column.association.primary_key || :id
164
+ query = column.association.klass.where(column.association.foreign_key => @records.map(&key))
165
+ if column.association.as
166
+ query.where!(column.association.reverse_association.foreign_type => active_scaffold_config.model.name)
167
+ end
168
+ if column.association.scope
169
+ query = query.instance_exec(&column.association.scope)
170
+ end
171
+ query.group(column.association.foreign_key)
172
+ end
173
+
174
+ def count_query_with_join(column)
175
+ klass = column.association.klass
176
+ query = active_scaffold_config.model.where(active_scaffold_config.primary_key => @records.map(&:id))
177
+ .joins(column.name).group(active_scaffold_config.primary_key)
178
+ .select("#{klass.quoted_table_name}.#{klass.quoted_primary_key}")
179
+ query = query.uniq if column.association.scope && klass.instance_exec(&column.association.scope).values[:distinct]
180
+ query
181
+ end
182
+
183
+ def mongoid_count_for_column(column)
184
+ matches = {column.association.foreign_key => {'$in': @records.map(&:id)}}
185
+ if column.association.as
186
+ matches[column.association.reverse_association.foreign_type] = {'$eq': active_scaffold_config.model.name}
187
+ end
188
+ group = {_id: "$#{column.association.foreign_key}", count: {'$sum' => 1}}
189
+ query = column.association.klass.collection.aggregate([{'$match' => matches}, {'$group' => group}])
190
+ query.each_with_object({}) do |row, hash|
191
+ hash[row['_id']] = row['count']
192
+ end
193
+ end
194
+
195
+ def find_page_options
196
+ options = {
197
+ :sorting => active_scaffold_config.list.user.sorting,
198
+ :count_includes => active_scaffold_config.list.user.count_includes
199
+ }
115
200
 
116
- options = {:sorting => active_scaffold_config.list.user.sorting,
117
- :count_includes => active_scaffold_config.list.user.count_includes}
118
- paginate = params[:format].nil? ? (accepts? :html, :js) : %w[html js].include?(params[:format])
201
+ paginate = params[:format].nil? ? accepts?(:html, :js) : %w[html js].include?(params[:format])
119
202
  options[:pagination] = active_scaffold_config.list.pagination if paginate
120
203
  if options[:pagination]
121
204
  options[:per_page] = active_scaffold_config.list.user.per_page
122
205
  options[:page] = active_scaffold_config.list.user.page
123
206
  end
207
+
124
208
  if active_scaffold_config.list.auto_select_columns
125
209
  auto_select_columns = list_columns + [active_scaffold_config.columns[active_scaffold_config.model.primary_key]]
126
210
  options[:select] = auto_select_columns.map { |c| quoted_select_columns(c.select_columns) }.compact.flatten
127
211
  end
128
212
 
129
- page = find_page(options)
130
- total_pages = page.pager.number_of_pages
131
- if !page.pager.infinite? && !total_pages.zero? && page.number > total_pages
132
- page = page.pager.last
133
- active_scaffold_config.list.user.page = page.number
134
- end
135
- @page = page
136
- @records = page.items
213
+ options
137
214
  end
138
215
 
139
216
  def quoted_select_columns(columns)
@@ -150,17 +227,23 @@ module ActiveScaffold::Actions
150
227
  end
151
228
 
152
229
  def each_record_in_page
153
- current_page = active_scaffold_config.list.user.page
154
- do_search if respond_to? :do_search, true
155
- active_scaffold_config.list.user.page = current_page
156
- do_list
157
- @page.items.each { |record| yield record }
230
+ page_items.each { |record| yield record }
158
231
  end
159
232
 
160
233
  def each_record_in_scope
161
234
  scoped_query.each { |record| yield record }
162
235
  end
163
236
 
237
+ def page_items
238
+ @page_items ||= begin
239
+ page_number = active_scaffold_config.list.user.page
240
+ do_search if respond_to? :do_search, true
241
+ active_scaffold_config.list.user.page = page_number
242
+ @page = current_page
243
+ @page.items
244
+ end
245
+ end
246
+
164
247
  def scoped_query
165
248
  @scoped_query ||= begin
166
249
  do_search if respond_to? :do_search, true
@@ -190,7 +273,7 @@ module ActiveScaffold::Actions
190
273
  {:etag => active_scaffold_config.list.user.sorting.clause}
191
274
  end
192
275
  end
193
- objects.present? ? objects : super
276
+ objects.presence || super
194
277
  end
195
278
 
196
279
  private
@@ -1,5 +1,7 @@
1
1
  module ActiveScaffold::Actions
2
- # The Nested module basically handles automatically linking controllers together. It does this by creating column links with the right parameters, and by providing any supporting systems (like a /:controller/nested action for returning associated scaffolds).
2
+ # The Nested module basically handles automatically linking controllers together.
3
+ # It does this by creating column links with the right parameters, and by providing
4
+ # any supporting systems (like a /:controller/nested action for returning associated scaffolds).
3
5
  module Nested
4
6
  def self.included(base)
5
7
  super
@@ -33,11 +35,11 @@ module ActiveScaffold::Actions
33
35
  def configure_nested
34
36
  return unless nested?
35
37
  register_constraints_with_action_columns(nested.constrained_fields)
38
+ return unless active_scaffold_config.actions.include? :list
36
39
  active_scaffold_config.list.user.label = nested_label
37
- unless active_scaffold_config.nested.ignore_order_from_association
38
- chain = beginning_of_chain
39
- active_scaffold_config.list.user.nested_default_sorting = nested_default_sorting(chain) if nested.sorted?(chain)
40
- end
40
+ return if active_scaffold_config.nested.ignore_order_from_association
41
+ chain = beginning_of_chain
42
+ active_scaffold_config.list.user.nested_default_sorting = nested_default_sorting(chain) if nested.sorted?(chain)
41
43
  end
42
44
 
43
45
  def nested_label
@@ -57,41 +59,39 @@ module ActiveScaffold::Actions
57
59
  end
58
60
 
59
61
  def include_habtm_actions
60
- if nested? && nested.habtm?
62
+ if nested&.habtm?
61
63
  # Production mode is ok with adding a link everytime the scaffold is nested - we are not ok with that.
62
- active_scaffold_config.action_links.add('new_existing', :label => :add_existing, :type => :collection, :security_method => :add_existing_authorized?) unless active_scaffold_config.action_links['new_existing']
63
- if active_scaffold_config.nested.shallow_delete
64
- active_scaffold_config.action_links.add('destroy_existing', :label => :remove, :type => :member, :confirm => :are_you_sure_to_delete, :method => :delete, :position => false, :security_method => :delete_existing_authorized?) unless active_scaffold_config.action_links['destroy_existing']
65
- if active_scaffold_config.actions.include?(:delete)
66
- active_scaffold_config.action_links.delete('destroy') if active_scaffold_config.action_links['destroy']
67
- end
68
- end
69
- else
70
- # Production mode is caching this link into a non nested scaffold
71
- active_scaffold_config.action_links.delete('new_existing') if active_scaffold_config.action_links['new_existing']
72
-
73
- if active_scaffold_config.nested.shallow_delete
74
- active_scaffold_config.action_links.delete('destroy_existing') if active_scaffold_config.action_links['destroy_existing']
75
- if active_scaffold_config.actions.include?(:delete) && active_scaffold_config.delete.link
76
- active_scaffold_config.action_links.add(active_scaffold_config.delete.link) unless active_scaffold_config.action_links['destroy']
77
- end
64
+ unless active_scaffold_config.action_links['new_existing']
65
+ active_scaffold_config.action_links.add('new_existing', :label => :add_existing, :type => :collection, :security_method => :add_existing_authorized?)
78
66
  end
67
+ add_shallow_links if active_scaffold_config.nested.shallow_delete
68
+ elsif !ActiveScaffold.threadsafe
69
+ # Production mode is caching this link into a non nested scaffold, when threadsafe is disabled
70
+ active_scaffold_config.action_links.delete('new_existing')
71
+ restore_shallow_links if active_scaffold_config.nested.shallow_delete
79
72
  end
80
73
  end
81
74
 
75
+ def add_shallow_links
76
+ unless active_scaffold_config.action_links['destroy_existing']
77
+ link_options = {:label => :remove, :type => :member, :confirm => :are_you_sure_to_delete, :method => :delete, :position => false, :security_method => :delete_existing_authorized?}
78
+ active_scaffold_config.action_links.add('destroy_existing', link_options)
79
+ end
80
+ active_scaffold_config.action_links.delete('destroy') if active_scaffold_config.actions.include?(:delete)
81
+ end
82
+
83
+ def restore_shallow_links
84
+ if active_scaffold_config.actions.include?(:delete) && active_scaffold_config.delete.link
85
+ link = active_scaffold_config.delete.link
86
+ active_scaffold_config.action_links.add(link) unless active_scaffold_config.action_links[link.action]
87
+ end
88
+ active_scaffold_config.action_links.delete('destroy_existing')
89
+ end
90
+
82
91
  def beginning_of_chain
83
- if nested? && nested.association
84
- if nested.association.collection?
85
- nested_parent_record.send(nested.association.name)
86
- elsif nested.association.through? # has_one :through
87
- active_scaffold_config.model.where(active_scaffold_config.model.primary_key => nested_parent_record.send(nested.association.name)&.id)
88
- elsif nested.association.has_one?
89
- active_scaffold_config.model.where(nested.child_association.name => nested_parent_record)
90
- elsif nested.association.belongs_to?
91
- nested_belongs_to_chain
92
- else # never should get here
93
- raise 'missing condition for nested beginning_of_chain'
94
- end
92
+ # only if nested is related to current controller, e.g. not when adding record in subform inside subform
93
+ if nested? && nested.association && nested.association.klass == active_scaffold_config.model
94
+ nested_chain_with_association
95
95
  elsif nested? && nested.scope
96
96
  nested_parent_record.send(nested.scope)
97
97
  else
@@ -99,23 +99,40 @@ module ActiveScaffold::Actions
99
99
  end
100
100
  end
101
101
 
102
- def nested_belongs_to_chain
103
- primary_key = active_scaffold_config.mongoid? ? '_id' : active_scaffold_config.model.primary_key
104
- active_scaffold_config.model.where(primary_key => nested_parent_record.send(nested.association.name))
102
+ def nested_chain_with_association
103
+ if nested.association.collection?
104
+ nested_parent_record.send(nested.association.name)
105
+ elsif nested.association.through? # has_one :through
106
+ active_scaffold_config.model.where(active_scaffold_config.model.primary_key => nested_parent_record.send(nested.association.name)&.id)
107
+ elsif nested.association.has_one?
108
+ active_scaffold_config.model.where(nested.child_association.name => nested_parent_record)
109
+ elsif nested.association.belongs_to?
110
+ primary_key = active_scaffold_config.mongoid? ? '_id' : active_scaffold_config.model.primary_key
111
+ active_scaffold_config.model.where(primary_key => nested_parent_record.send(nested.association.name))
112
+ else # never should get here
113
+ raise 'missing condition for nested beginning_of_chain'
114
+ end
105
115
  end
106
116
 
107
117
  def nested_parent_record(crud = :read)
108
118
  @nested_parent_record ||= find_if_allowed(nested.parent_id, crud, nested.parent_model)
109
119
  end
110
120
 
121
+ def create_association_with_parent?
122
+ # has_many is done by beginning_of_chain and rails if direct association, not in through associations
123
+ return false if nested.has_many? && !nested.association.through?
124
+ nested.child_association && nested_parent_record
125
+ end
126
+
111
127
  def create_association_with_parent(record)
112
- # has_many is done by beginning_of_chain and rails
113
- return unless (nested.belongs_to? || nested.has_one? || nested.habtm?) && nested.child_association
114
- return if (parent = nested_parent_record).nil?
128
+ return unless create_association_with_parent?
115
129
  if nested.child_association.singular?
116
- record.send("#{nested.child_association.name}=", parent)
130
+ record.send("#{nested.child_association.name}=", nested_parent_record)
131
+ elsif nested.association.through_singular? && nested.child_association.through_singular?
132
+ through = nested_parent_record.send(nested.association.through_reflection.name)
133
+ record.send("#{nested.child_association.through_reflection.name}=", through)
117
134
  else
118
- record.send(nested.child_association.name) << parent
135
+ record.send(nested.child_association.name) << nested_parent_record
119
136
  end
120
137
  end
121
138