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.
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