active_scaffold 3.5.5 → 3.6.1

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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG → CHANGELOG.rdoc} +75 -0
  3. data/README.md +21 -10
  4. data/app/assets/javascripts/active_scaffold.js.erb +0 -1
  5. data/app/assets/javascripts/jquery/active_scaffold.js +98 -7
  6. data/app/assets/stylesheets/active_scaffold_colors.scss +1 -1
  7. data/app/assets/stylesheets/active_scaffold_layout.css +52 -29
  8. data/app/views/active_scaffold_overrides/_base_form.html.erb +2 -2
  9. data/app/views/active_scaffold_overrides/_form.html.erb +1 -1
  10. data/app/views/active_scaffold_overrides/_form_association.html.erb +2 -1
  11. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +3 -2
  12. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +26 -10
  13. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +4 -4
  14. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +2 -1
  15. data/app/views/active_scaffold_overrides/_list.html.erb +2 -1
  16. data/app/views/active_scaffold_overrides/_list_header.html.erb +5 -7
  17. data/app/views/active_scaffold_overrides/_list_messages.html.erb +1 -0
  18. data/app/views/active_scaffold_overrides/_list_record.html.erb +4 -5
  19. data/app/views/active_scaffold_overrides/_list_with_header.html.erb +1 -1
  20. data/app/views/active_scaffold_overrides/_messages.html.erb +1 -0
  21. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +4 -0
  22. data/app/views/active_scaffold_overrides/_render_field.js.erb +2 -1
  23. data/app/views/active_scaffold_overrides/_show_association_horizontal.html.erb +2 -1
  24. data/app/views/active_scaffold_overrides/_show_columns.html.erb +2 -2
  25. data/app/views/active_scaffold_overrides/_show_horizontal_record.html.erb +4 -4
  26. data/app/views/active_scaffold_overrides/_update_calculations.js.erb +1 -1
  27. data/app/views/active_scaffold_overrides/_update_column.js.erb +2 -2
  28. data/app/views/active_scaffold_overrides/_vertical_subform.html.erb +2 -2
  29. data/app/views/active_scaffold_overrides/action_confirmation.html.erb +2 -2
  30. data/app/views/active_scaffold_overrides/delete.html.erb +2 -2
  31. data/app/views/active_scaffold_overrides/on_action_update.js.erb +16 -6
  32. data/app/views/active_scaffold_overrides/on_update.js.erb +1 -1
  33. data/app/views/active_scaffold_overrides/row.js.erb +1 -1
  34. data/app/views/active_scaffold_overrides/update_column.js.erb +2 -2
  35. data/config/locales/de.yml +2 -1
  36. data/config/locales/en.yml +1 -0
  37. data/config/locales/es.yml +1 -0
  38. data/config/locales/fr.yml +2 -1
  39. data/config/locales/hu.yml +1 -0
  40. data/config/locales/ja.yml +1 -0
  41. data/config/locales/ru.yml +1 -0
  42. data/lib/active_scaffold.rb +19 -16
  43. data/lib/active_scaffold/actions/common_search.rb +11 -8
  44. data/lib/active_scaffold/actions/core.rb +91 -70
  45. data/lib/active_scaffold/actions/create.rb +28 -28
  46. data/lib/active_scaffold/actions/delete.rb +3 -3
  47. data/lib/active_scaffold/actions/field_search.rb +53 -43
  48. data/lib/active_scaffold/actions/list.rb +111 -27
  49. data/lib/active_scaffold/actions/nested.rb +65 -48
  50. data/lib/active_scaffold/actions/search.rb +1 -1
  51. data/lib/active_scaffold/actions/show.rb +4 -4
  52. data/lib/active_scaffold/actions/subform.rb +23 -22
  53. data/lib/active_scaffold/actions/update.rb +96 -77
  54. data/lib/active_scaffold/active_record_permissions.rb +2 -11
  55. data/lib/active_scaffold/attribute_params.rb +102 -94
  56. data/lib/active_scaffold/bridges.rb +8 -8
  57. data/lib/active_scaffold/bridges/active_storage.rb +6 -0
  58. data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +34 -0
  59. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +54 -0
  60. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +22 -0
  61. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +36 -0
  62. data/lib/active_scaffold/bridges/bitfields.rb +2 -1
  63. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +12 -15
  64. data/lib/active_scaffold/bridges/bitfields/list_ui.rb +19 -0
  65. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +1 -1
  66. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +3 -12
  67. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +1 -1
  68. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -2
  69. data/lib/active_scaffold/bridges/chosen/helpers.rb +7 -6
  70. data/lib/active_scaffold/bridges/date_picker/ext.rb +0 -13
  71. data/lib/active_scaffold/bridges/date_picker/helper.rb +49 -44
  72. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +1 -1
  73. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +1 -1
  74. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +3 -3
  75. data/lib/active_scaffold/bridges/file_column/form_ui.rb +3 -3
  76. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +10 -7
  77. data/lib/active_scaffold/bridges/paper_trail.rb +1 -1
  78. data/lib/active_scaffold/bridges/paper_trail/actions.rb +3 -1
  79. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  80. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +1 -1
  81. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +2 -2
  82. data/lib/active_scaffold/bridges/record_select/helpers.rb +15 -17
  83. data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -19
  84. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  85. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +21 -4
  86. data/lib/active_scaffold/config/base.rb +133 -41
  87. data/lib/active_scaffold/config/core.rb +146 -18
  88. data/lib/active_scaffold/config/delete.rb +14 -1
  89. data/lib/active_scaffold/config/field_search.rb +7 -1
  90. data/lib/active_scaffold/config/form.rb +10 -1
  91. data/lib/active_scaffold/config/list.rb +39 -13
  92. data/lib/active_scaffold/config/mark.rb +4 -2
  93. data/lib/active_scaffold/config/nested.rb +16 -17
  94. data/lib/active_scaffold/config/search.rb +9 -0
  95. data/lib/active_scaffold/config/show.rb +4 -0
  96. data/lib/active_scaffold/config/update.rb +4 -0
  97. data/lib/active_scaffold/configurable.rb +14 -7
  98. data/lib/active_scaffold/constraints.rb +22 -20
  99. data/lib/active_scaffold/core.rb +67 -28
  100. data/lib/active_scaffold/data_structures/action_columns.rb +50 -59
  101. data/lib/active_scaffold/data_structures/action_link.rb +50 -20
  102. data/lib/active_scaffold/data_structures/action_links.rb +15 -13
  103. data/lib/active_scaffold/data_structures/association/abstract.rb +38 -15
  104. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +2 -6
  105. data/lib/active_scaffold/data_structures/association/active_record.rb +6 -2
  106. data/lib/active_scaffold/data_structures/association/mongoid.rb +0 -3
  107. data/lib/active_scaffold/data_structures/column.rb +75 -66
  108. data/lib/active_scaffold/data_structures/columns.rb +3 -2
  109. data/lib/active_scaffold/data_structures/nested_info.rb +33 -19
  110. data/lib/active_scaffold/data_structures/set.rb +8 -0
  111. data/lib/active_scaffold/data_structures/sorting.rb +10 -2
  112. data/lib/active_scaffold/delayed_setup.rb +16 -5
  113. data/lib/active_scaffold/extensions/action_controller_rendering.rb +3 -2
  114. data/lib/active_scaffold/extensions/action_view_rendering.rb +93 -32
  115. data/lib/active_scaffold/extensions/cow_proxy.rb +95 -0
  116. data/lib/active_scaffold/extensions/ice_nine.rb +36 -0
  117. data/lib/active_scaffold/extensions/left_outer_joins.rb +8 -33
  118. data/lib/active_scaffold/extensions/localize.rb +3 -1
  119. data/lib/active_scaffold/extensions/routing_mapper.rb +6 -45
  120. data/lib/active_scaffold/extensions/to_label.rb +3 -2
  121. data/lib/active_scaffold/extensions/unsaved_record.rb +2 -4
  122. data/lib/active_scaffold/finder.rb +110 -77
  123. data/lib/active_scaffold/helpers/action_link_helpers.rb +62 -36
  124. data/lib/active_scaffold/helpers/association_helpers.rb +18 -16
  125. data/lib/active_scaffold/helpers/controller_helpers.rb +34 -10
  126. data/lib/active_scaffold/helpers/form_column_helpers.rb +196 -124
  127. data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -1
  128. data/lib/active_scaffold/helpers/id_helpers.rb +6 -2
  129. data/lib/active_scaffold/helpers/list_column_helpers.rb +90 -57
  130. data/lib/active_scaffold/helpers/pagination_helpers.rb +2 -2
  131. data/lib/active_scaffold/helpers/search_column_helpers.rb +43 -41
  132. data/lib/active_scaffold/helpers/show_column_helpers.rb +3 -5
  133. data/lib/active_scaffold/helpers/view_helpers.rb +39 -36
  134. data/lib/active_scaffold/marked_model.rb +2 -2
  135. data/lib/active_scaffold/orm_checks.rb +3 -7
  136. data/lib/active_scaffold/paginator.rb +7 -7
  137. data/lib/active_scaffold/registry.rb +33 -0
  138. data/lib/active_scaffold/responds_to_parent.rb +8 -11
  139. data/lib/active_scaffold/tableless.rb +83 -67
  140. data/lib/active_scaffold/version.rb +2 -2
  141. data/lib/generators/active_scaffold/controller_generator.rb +2 -2
  142. data/lib/generators/active_scaffold/install_generator.rb +52 -4
  143. data/lib/generators/active_scaffold/resource_generator.rb +2 -2
  144. data/shoulda_macros/macros.rb +3 -1
  145. data/test/bridges/date_picker_test.rb +1 -2
  146. data/test/bridges/paperclip_test.rb +6 -6
  147. data/test/class_with_finder.rb +2 -2
  148. data/test/company.rb +4 -4
  149. data/test/config/create_test.rb +4 -2
  150. data/test/config/nested_test.rb +1 -1
  151. data/test/config/show_test.rb +1 -1
  152. data/test/config/update_test.rb +7 -6
  153. data/test/data_structures/action_columns_test.rb +2 -2
  154. data/test/data_structures/action_links_test.rb +1 -1
  155. data/test/data_structures/column_test.rb +3 -6
  156. data/test/data_structures/columns_test.rb +2 -2
  157. data/test/data_structures/sorting_test.rb +7 -0
  158. data/test/extensions/action_view_rendering_test.rb +20 -0
  159. data/test/extensions/active_record_test.rb +4 -4
  160. data/test/extensions/routing_mapper_test.rb +2 -2
  161. data/test/helpers/list_column_helpers_test.rb +3 -1
  162. data/test/misc/active_record_permissions_test.rb +3 -11
  163. data/test/misc/attribute_params_test.rb +12 -8
  164. data/test/misc/calculation_test.rb +1 -1
  165. data/test/misc/configurable_test.rb +10 -10
  166. data/test/misc/constraints_test.rb +3 -3
  167. data/test/misc/convert_numbers_format_test.rb +7 -3
  168. data/test/misc/lang_test.rb +1 -1
  169. data/test/misc/parse_datetime_test.rb +3 -4
  170. data/test/misc/tableless_test.rb +14 -0
  171. data/test/mock_app/Rakefile +1 -1
  172. data/test/mock_app/app/assets/config/manifest.js +0 -0
  173. data/test/mock_app/app/controllers/cars_controller.rb +1 -0
  174. data/test/mock_app/app/controllers/people_controller.rb +5 -1
  175. data/test/mock_app/app/controllers/roles_controller.rb +4 -0
  176. data/test/mock_app/app/views/active_scaffold_overrides/_form.html.erb +2 -0
  177. data/test/mock_app/app/views/active_scaffold_overrides/list.html.erb +2 -0
  178. data/test/mock_app/app/views/people/_first_name_form_column.html.erb +2 -0
  179. data/test/mock_app/app/views/people/_form.html.erb +2 -0
  180. data/test/mock_app/app/views/people/list.html.erb +2 -0
  181. data/test/mock_app/config/application.rb +2 -1
  182. data/test/mock_app/config/boot.rb +1 -1
  183. data/test/mock_app/config/environment.rb +2 -2
  184. data/test/mock_app/config/routes.rb +4 -1
  185. data/test/mock_app/db/schema.rb +2 -0
  186. data/test/performance/list_cars_performance_test.rb +34 -0
  187. data/test/performance/list_people_performance_test.rb +31 -0
  188. data/test/performance_test_help.rb +3 -0
  189. data/test/test_helper.rb +12 -4
  190. metadata +71 -15
  191. data/app/assets/javascripts/prototype/rico_corner.js +0 -370
  192. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +0 -7
@@ -32,51 +32,51 @@ module ActiveScaffold::Actions
32
32
  render(:partial => 'create_form')
33
33
  end
34
34
 
35
+ def create_respond_on_iframe
36
+ do_refresh_list if successful? && active_scaffold_config.create.refresh_list && !render_parent?
37
+ responds_to_parent do
38
+ render :action => 'on_create', :formats => [:js], :layout => false
39
+ end
40
+ end
41
+
35
42
  def create_respond_to_html
36
- if params[:iframe] == 'true' # was this an iframe post ?
37
- do_refresh_list if successful? && active_scaffold_config.create.refresh_list && !render_parent?
38
- responds_to_parent do
39
- render :action => 'on_create', :formats => [:js], :layout => false
40
- end
41
- else
42
- if successful?
43
- flash[:info] = as_(:created_model, :model => ERB::Util.h(@record.to_label))
44
- if (action = active_scaffold_config.create.action_after_create)
45
- redirect_to params_for(:action => action, :id => @record.to_param)
46
- elsif params[:dont_close]
47
- redirect_to params_for(:action => 'new')
48
- else
49
- return_to_main
50
- end
43
+ if successful?
44
+ flash[:info] = as_(:created_model, :model => ERB::Util.h(@record.to_label))
45
+ if (action = active_scaffold_config.create.action_after_create)
46
+ redirect_to params_for(:action => action, :id => @record.to_param)
47
+ elsif params[:dont_close]
48
+ redirect_to params_for(:action => 'new')
51
49
  else
52
- if active_scaffold_config.actions.include?(:list) && active_scaffold_config.list.always_show_create
53
- list
54
- else
55
- render(:action => 'create')
56
- end
50
+ return_to_main
57
51
  end
52
+ elsif active_scaffold_config.actions.include?(:list) && active_scaffold_config.list.always_show_create
53
+ list
54
+ else
55
+ render(:action => 'create')
58
56
  end
59
57
  end
60
58
 
61
59
  def create_respond_to_js
62
- do_refresh_list if successful? && active_scaffold_config.create.refresh_list && !render_parent?
63
- if successful? && params[:dont_close] && !render_parent?
64
- @saved_record = @record
65
- do_new
60
+ if successful? && !render_parent?
61
+ do_refresh_list if active_scaffold_config.create.refresh_list
62
+ if params[:dont_close]
63
+ @saved_record = @record
64
+ do_new
65
+ end
66
66
  end
67
67
  render :action => 'on_create'
68
68
  end
69
69
 
70
70
  def create_respond_to_xml
71
- render :xml => response_object, :only => create_columns_names + [active_scaffold_config.model.primary_key], :include => association_columns(create_columns_names), :methods => virtual_columns(create_columns_names), :status => response_status, :location => response_location
71
+ response_to_api(:xml, create_columns_names, location: response_location)
72
72
  end
73
73
 
74
74
  def create_respond_to_json
75
- render :json => response_object, :only => create_columns_names + [active_scaffold_config.model.primary_key], :include => association_columns(create_columns_names), :methods => virtual_columns(create_columns_names), :status => response_status, :location => response_location
75
+ response_to_api(:json, create_columns_names, location: response_location)
76
76
  end
77
77
 
78
78
  def create_columns_names
79
- active_scaffold_config.create.columns.names
79
+ active_scaffold_config.create.columns.visible_columns_names
80
80
  end
81
81
 
82
82
  # A simple method to find and prepare an example new record for the form
@@ -136,7 +136,7 @@ module ActiveScaffold::Actions
136
136
  private
137
137
 
138
138
  def create_authorized_filter
139
- link = active_scaffold_config.create.link || active_scaffold_config.create.class.link
139
+ link = active_scaffold_config.create.link || self.class.active_scaffold_config.create.class.link
140
140
  raise ActiveScaffold::ActionNotAllowed unless send(link.security_method)
141
141
  end
142
142
 
@@ -24,11 +24,11 @@ module ActiveScaffold::Actions
24
24
  end
25
25
 
26
26
  def destroy_respond_to_xml
27
- render :xml => successful? ? '' : response_object, :only => active_scaffold_config.list.columns.names, :status => response_status
27
+ render :xml => successful? ? '' : response_object, :only => active_scaffold_config.list.columns.visible_columns_names, :status => response_status
28
28
  end
29
29
 
30
30
  def destroy_respond_to_json
31
- render :json => successful? ? '' : response_object, :only => active_scaffold_config.list.columns.names, :status => response_status
31
+ render :json => successful? ? '' : response_object, :only => active_scaffold_config.list.columns.visible_columns_names, :status => response_status
32
32
  end
33
33
 
34
34
  def destroy_find_record
@@ -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
@@ -56,24 +56,25 @@ module ActiveScaffold::Actions
56
56
 
57
57
  def custom_finder_options
58
58
  if grouped_search?
59
- group_sql = calculation_for_group_by(search_group_column.try(: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.try(:select_columns))
59
+ group_sql = calculation_for_group_by(search_group_column&.field || search_group_name)
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
@@ -71,7 +71,7 @@ module ActiveScaffold::Actions
71
71
  if respond_to?(:"#{action}_columns", true)
72
72
  send(:"#{action}_columns")
73
73
  else
74
- active_scaffold_config.send(action).columns.collect_visible(:flatten => true)
74
+ active_scaffold_config.send(action).columns.visible_columns(flatten: true)
75
75
  end
76
76
  joins_cols, preload_cols = columns.select { |c| c.includes.present? }.partition do |col|
77
77
  includes_need_join?(col, sorting) && !grouped_search?
@@ -82,7 +82,7 @@ module ActiveScaffold::Actions
82
82
  end
83
83
 
84
84
  def set_includes_for_sorting(columns, sorting)
85
- sorting.each 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,12 +93,12 @@ 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)
100
- assoc = column.association if column.association.try :collection?
101
- assoc && assoc.habtm? && assoc.scope
100
+ assoc = column.association if column.association&.collection?
101
+ assoc&.habtm? && assoc.scope
102
102
  end
103
103
 
104
104
  def get_row(crud_type_or_security_options = :read)
@@ -106,37 +106,115 @@ 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, @records = page, page.items
213
+ options
136
214
  end
137
215
 
138
216
  def quoted_select_columns(columns)
139
- columns.map { |c| active_scaffold_config.columns[c].try(:field) || c } if columns
217
+ columns&.map { |c| active_scaffold_config.columns[c]&.field || c }
140
218
  end
141
219
 
142
220
  def do_refresh_list
@@ -149,17 +227,23 @@ module ActiveScaffold::Actions
149
227
  end
150
228
 
151
229
  def each_record_in_page
152
- current_page = active_scaffold_config.list.user.page
153
- do_search if respond_to? :do_search, true
154
- active_scaffold_config.list.user.page = current_page
155
- do_list
156
- @page.items.each { |record| yield record }
230
+ page_items.each { |record| yield record }
157
231
  end
158
232
 
159
233
  def each_record_in_scope
160
234
  scoped_query.each { |record| yield record }
161
235
  end
162
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
+
163
247
  def scoped_query
164
248
  @scoped_query ||= begin
165
249
  do_search if respond_to? :do_search, true
@@ -189,7 +273,7 @@ module ActiveScaffold::Actions
189
273
  {:etag => active_scaffold_config.list.user.sorting.clause}
190
274
  end
191
275
  end
192
- objects.present? ? objects : super
276
+ objects.presence || super
193
277
  end
194
278
 
195
279
  private
@@ -216,7 +300,7 @@ module ActiveScaffold::Actions
216
300
  end
217
301
 
218
302
  def list_columns
219
- @list_columns ||= active_scaffold_config.list.columns.collect_visible
303
+ @list_columns ||= active_scaffold_config.list.columns.visible_columns
220
304
  end
221
305
 
222
306
  def list_columns_names