active_scaffold 3.4.43 → 3.5.0

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +39 -0
  3. data/{LICENSE → LICENSE.md} +1 -1
  4. data/README.md +27 -19
  5. data/app/assets/javascripts/active_scaffold.js.erb +1 -1
  6. data/app/assets/javascripts/jquery/active_scaffold.js +95 -43
  7. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +30 -6
  8. data/app/assets/javascripts/prototype/tiny_mce_bridge.js +11 -1
  9. data/app/assets/stylesheets/active_scaffold_colors.scss +2 -2
  10. data/app/assets/stylesheets/active_scaffold_layout.css +36 -28
  11. data/app/views/active_scaffold_overrides/_base_form.html.erb +2 -3
  12. data/app/views/active_scaffold_overrides/_field_search.html.erb +8 -7
  13. data/app/views/active_scaffold_overrides/_form_association.html.erb +9 -9
  14. data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +6 -6
  15. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +52 -50
  16. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +1 -1
  17. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +1 -1
  18. data/app/views/active_scaffold_overrides/_human_conditions.html.erb +3 -1
  19. data/app/views/active_scaffold_overrides/_list_calculations.html.erb +1 -1
  20. data/app/views/active_scaffold_overrides/_list_column_headings.html.erb +2 -0
  21. data/app/views/active_scaffold_overrides/_list_messages.html.erb +5 -3
  22. data/app/views/active_scaffold_overrides/_list_record.html.erb +3 -1
  23. data/app/views/active_scaffold_overrides/_list_with_header.html.erb +9 -9
  24. data/app/views/active_scaffold_overrides/_messages.html.erb +1 -1
  25. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +18 -10
  26. data/app/views/active_scaffold_overrides/_render_field.js.erb +3 -3
  27. data/app/views/active_scaffold_overrides/_search.html.erb +7 -6
  28. data/app/views/active_scaffold_overrides/_show_actions.html.erb +14 -0
  29. data/app/views/active_scaffold_overrides/_show_association.html.erb +1 -1
  30. data/app/views/active_scaffold_overrides/_update_actions.html.erb +6 -2
  31. data/app/views/active_scaffold_overrides/_update_column.js.erb +1 -1
  32. data/app/views/active_scaffold_overrides/_update_form.html.erb +1 -1
  33. data/app/views/active_scaffold_overrides/destroy.js.erb +2 -3
  34. data/app/views/active_scaffold_overrides/edit_associated.js.erb +4 -3
  35. data/app/views/active_scaffold_overrides/on_action_update.js.erb +5 -3
  36. data/app/views/active_scaffold_overrides/on_create.js.erb +4 -4
  37. data/app/views/active_scaffold_overrides/on_update.js.erb +6 -6
  38. data/app/views/active_scaffold_overrides/show.html.erb +6 -0
  39. data/app/views/active_scaffold_overrides/update.html.erb +1 -1
  40. data/app/views/active_scaffold_overrides/update_column.js.erb +1 -1
  41. data/config/brakeman.ignore +26 -0
  42. data/config/brakeman.yml +3 -0
  43. data/config/i18n-tasks.yml +121 -0
  44. data/config/locales/de.yml +81 -70
  45. data/config/locales/en.yml +83 -74
  46. data/config/locales/es.yml +82 -73
  47. data/config/locales/fr.yml +86 -75
  48. data/config/locales/hu.yml +81 -70
  49. data/config/locales/ja.yml +71 -60
  50. data/config/locales/ru.yml +85 -74
  51. data/lib/active_scaffold.rb +3 -0
  52. data/lib/active_scaffold/actions/common_search.rb +11 -7
  53. data/lib/active_scaffold/actions/core.rb +119 -47
  54. data/lib/active_scaffold/actions/create.rb +1 -1
  55. data/lib/active_scaffold/actions/delete.rb +11 -8
  56. data/lib/active_scaffold/actions/field_search.rb +104 -6
  57. data/lib/active_scaffold/actions/list.rb +25 -21
  58. data/lib/active_scaffold/actions/mark.rb +12 -4
  59. data/lib/active_scaffold/actions/nested.rb +26 -26
  60. data/lib/active_scaffold/actions/search.rb +2 -2
  61. data/lib/active_scaffold/actions/show.rb +4 -5
  62. data/lib/active_scaffold/actions/subform.rb +9 -7
  63. data/lib/active_scaffold/actions/update.rb +20 -13
  64. data/lib/active_scaffold/active_record_permissions.rb +24 -5
  65. data/lib/active_scaffold/attribute_params.rb +68 -49
  66. data/lib/active_scaffold/bridges.rb +1 -1
  67. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +15 -19
  68. data/lib/active_scaffold/bridges/bitfields.rb +1 -1
  69. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +10 -14
  70. data/lib/active_scaffold/bridges/calendar_date_select.rb +0 -7
  71. data/lib/active_scaffold/bridges/calendar_date_select/as_cds_bridge.rb +19 -22
  72. data/lib/active_scaffold/bridges/cancan.rb +4 -3
  73. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +11 -21
  74. data/lib/active_scaffold/bridges/carrierwave.rb +2 -1
  75. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +2 -6
  76. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +6 -39
  77. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +1 -1
  78. data/lib/active_scaffold/bridges/chosen.rb +4 -1
  79. data/lib/active_scaffold/bridges/chosen/helpers.rb +3 -2
  80. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +2 -2
  81. data/lib/active_scaffold/bridges/date_picker.rb +3 -0
  82. data/lib/active_scaffold/bridges/date_picker/ext.rb +43 -38
  83. data/lib/active_scaffold/bridges/date_picker/helper.rb +24 -23
  84. data/lib/active_scaffold/bridges/dragonfly.rb +1 -1
  85. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge.rb +3 -7
  86. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -25
  87. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +2 -2
  88. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +6 -8
  89. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +1 -1
  90. data/lib/active_scaffold/bridges/file_column/form_ui.rb +0 -2
  91. data/lib/active_scaffold/bridges/file_column/list_ui.rb +2 -1
  92. data/lib/active_scaffold/bridges/file_column/test/test_helper.rb +1 -1
  93. data/lib/active_scaffold/bridges/paper_trail/actions.rb +1 -1
  94. data/lib/active_scaffold/bridges/paper_trail/helper.rb +1 -2
  95. data/lib/active_scaffold/bridges/paper_trail/paper_trail_bridge.rb +3 -7
  96. data/lib/active_scaffold/bridges/paperclip.rb +1 -1
  97. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -28
  98. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +1 -1
  99. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +3 -7
  100. data/lib/active_scaffold/bridges/record_select.rb +2 -0
  101. data/lib/active_scaffold/bridges/record_select/helpers.rb +14 -18
  102. data/lib/active_scaffold/bridges/semantic_attributes/column.rb +4 -8
  103. data/lib/active_scaffold/bridges/shared/date_bridge.rb +20 -20
  104. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +7 -22
  105. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +14 -14
  106. data/lib/active_scaffold/config/base.rb +9 -6
  107. data/lib/active_scaffold/config/core.rb +30 -21
  108. data/lib/active_scaffold/config/create.rb +2 -1
  109. data/lib/active_scaffold/config/delete.rb +2 -2
  110. data/lib/active_scaffold/config/field_search.rb +9 -3
  111. data/lib/active_scaffold/config/form.rb +4 -4
  112. data/lib/active_scaffold/config/list.rb +27 -23
  113. data/lib/active_scaffold/config/nested.rb +4 -4
  114. data/lib/active_scaffold/config/search.rb +6 -6
  115. data/lib/active_scaffold/config/show.rb +11 -1
  116. data/lib/active_scaffold/config/subform.rb +1 -1
  117. data/lib/active_scaffold/config/update.rb +4 -2
  118. data/lib/active_scaffold/constraints.rb +39 -36
  119. data/lib/active_scaffold/core.rb +36 -15
  120. data/lib/active_scaffold/data_structures/action_columns.rb +14 -9
  121. data/lib/active_scaffold/data_structures/action_link.rb +4 -5
  122. data/lib/active_scaffold/data_structures/action_links.rb +5 -4
  123. data/lib/active_scaffold/data_structures/actions.rb +2 -2
  124. data/lib/active_scaffold/data_structures/association.rb +8 -0
  125. data/lib/active_scaffold/data_structures/association/abstract.rb +147 -0
  126. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +42 -0
  127. data/lib/active_scaffold/data_structures/association/active_record.rb +94 -0
  128. data/lib/active_scaffold/data_structures/association/mongoid.rb +45 -0
  129. data/lib/active_scaffold/data_structures/bridge.rb +3 -6
  130. data/lib/active_scaffold/data_structures/column.rb +100 -82
  131. data/lib/active_scaffold/data_structures/columns.rb +21 -3
  132. data/lib/active_scaffold/data_structures/nested_info.rb +22 -37
  133. data/lib/active_scaffold/data_structures/set.rb +4 -4
  134. data/lib/active_scaffold/data_structures/sorting.rb +29 -15
  135. data/lib/active_scaffold/engine.rb +3 -1
  136. data/lib/active_scaffold/extensions/action_controller_rendering.rb +10 -5
  137. data/lib/active_scaffold/extensions/action_view_rendering.rb +65 -59
  138. data/lib/active_scaffold/extensions/left_outer_joins.rb +48 -53
  139. data/lib/active_scaffold/extensions/localize.rb +3 -4
  140. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +7 -11
  141. data/lib/active_scaffold/extensions/paginator_extensions.rb +20 -18
  142. data/lib/active_scaffold/extensions/routing_mapper.rb +104 -40
  143. data/lib/active_scaffold/extensions/to_label.rb +1 -1
  144. data/lib/active_scaffold/extensions/unsaved_associated.rb +4 -13
  145. data/lib/active_scaffold/extensions/unsaved_record.rb +12 -1
  146. data/lib/active_scaffold/finder.rb +200 -134
  147. data/lib/active_scaffold/helpers/action_link_helpers.rb +398 -0
  148. data/lib/active_scaffold/helpers/association_helpers.rb +12 -30
  149. data/lib/active_scaffold/helpers/controller_helpers.rb +74 -24
  150. data/lib/active_scaffold/helpers/form_column_helpers.rb +205 -112
  151. data/lib/active_scaffold/helpers/human_condition_helpers.rb +21 -11
  152. data/lib/active_scaffold/helpers/id_helpers.rb +1 -1
  153. data/lib/active_scaffold/helpers/list_column_helpers.rb +117 -39
  154. data/lib/active_scaffold/helpers/pagination_helpers.rb +11 -14
  155. data/lib/active_scaffold/helpers/search_column_helpers.rb +69 -32
  156. data/lib/active_scaffold/helpers/show_column_helpers.rb +9 -3
  157. data/lib/active_scaffold/helpers/view_helpers.rb +41 -426
  158. data/lib/active_scaffold/orm_checks.rb +109 -0
  159. data/lib/active_scaffold/paginator.rb +1 -1
  160. data/lib/active_scaffold/responds_to_parent.rb +12 -10
  161. data/lib/active_scaffold/tableless.rb +81 -43
  162. data/lib/active_scaffold/version.rb +2 -2
  163. data/lib/generators/active_scaffold/controller_generator.rb +49 -0
  164. data/lib/generators/active_scaffold/install_generator.rb +45 -0
  165. data/lib/generators/active_scaffold/resource_generator.rb +56 -0
  166. data/lib/generators/{active_scaffold_controller/templates → templates}/controller.rb +0 -0
  167. data/lib/generators/{active_scaffold_controller/templates → templates}/helper.rb +0 -0
  168. data/shoulda_macros/macros.rb +3 -3
  169. data/test/active_scaffold_config_mock.rb +33 -0
  170. data/test/bridges/bridge_test.rb +9 -9
  171. data/test/bridges/date_picker_test.rb +3 -1
  172. data/test/bridges/paper_trail_test.rb +2 -3
  173. data/test/bridges/paperclip_test.rb +21 -10
  174. data/test/bridges/tiny_mce_test.rb +20 -21
  175. data/test/class_with_finder.rb +42 -0
  176. data/test/company.rb +6 -4
  177. data/test/config/core_test.rb +1 -1
  178. data/test/config/create_test.rb +1 -1
  179. data/test/config/list_test.rb +3 -3
  180. data/test/config/update_test.rb +3 -3
  181. data/test/data_structures/action_columns_test.rb +3 -3
  182. data/test/data_structures/association_column_test.rb +5 -5
  183. data/test/data_structures/column_test.rb +14 -14
  184. data/test/data_structures/columns_test.rb +2 -2
  185. data/test/data_structures/set_test.rb +2 -2
  186. data/test/data_structures/sorting_test.rb +6 -4
  187. data/test/extensions/active_record_test.rb +1 -1
  188. data/test/extensions/routing_mapper_test.rb +64 -13
  189. data/test/helpers/form_column_helpers_test.rb +6 -6
  190. data/test/helpers/list_column_helpers_test.rb +9 -5
  191. data/test/helpers/pagination_helpers_test.rb +1 -0
  192. data/test/misc/active_record_permissions_test.rb +18 -1
  193. data/test/misc/attribute_params_test.rb +26 -17
  194. data/test/misc/calculation_test.rb +8 -31
  195. data/test/misc/configurable_test.rb +3 -2
  196. data/test/misc/constraints_test.rb +33 -22
  197. data/test/misc/convert_numbers_format_test.rb +28 -10
  198. data/test/misc/finder_test.rb +6 -29
  199. data/test/misc/parse_datetime_test.rb +160 -0
  200. data/test/misc/render_test.rb +1 -1
  201. data/test/misc/tableless_test.rb +24 -0
  202. data/test/mock_app/app/models/building.rb +2 -1
  203. data/test/mock_app/config.ru +1 -1
  204. data/test/mock_app/config/environments/test.rb +1 -1
  205. data/test/mock_app/config/routes.rb +11 -3
  206. data/test/model_stub.rb +11 -6
  207. data/test/run_all.rb +1 -1
  208. data/test/test_helper.rb +19 -4
  209. metadata +42 -23
  210. data/lib/active_scaffold/data_structures/error_message.rb +0 -22
  211. data/lib/active_scaffold/extensions/reverse_associations.rb +0 -119
  212. data/lib/generators/active_scaffold/USAGE +0 -29
  213. data/lib/generators/active_scaffold/active_scaffold_generator.rb +0 -21
  214. data/lib/generators/active_scaffold_controller/USAGE +0 -19
  215. data/lib/generators/active_scaffold_controller/active_scaffold_controller_generator.rb +0 -29
  216. data/test/data_structures/error_message_test.rb +0 -25
@@ -15,9 +15,17 @@ module ActiveScaffold
15
15
  end
16
16
  end
17
17
 
18
+ def active_scaffold_grouped_by_label
19
+ text, = active_scaffold_config.field_search.group_options.find do |text, value|
20
+ (value || text).to_s == field_search_params['active_scaffold_group']
21
+ end
22
+ active_scaffold_translated_option(active_scaffold_group_column, text).first if text
23
+ end
24
+
18
25
  def format_human_condition(column, opt, from = nil, to = nil)
19
26
  attribute = column.active_record_class.human_attribute_name(column.name)
20
- opt ||= from && to ? :between : (from ? :'>=' : :'<=')
27
+ opt ||= :between if from && to
28
+ opt ||= from ? '>=' : '<='
21
29
  "#{attribute} #{as_(opt).downcase} #{from} #{to}"
22
30
  end
23
31
 
@@ -26,8 +34,8 @@ module ActiveScaffold
26
34
  to = "- #{format_number_value(controller.class.condition_value_for_numeric(column, value['to']), column.options)}" if value['opt'] == 'BETWEEN'
27
35
  format_human_condition column, value['opt'].downcase, from, to
28
36
  end
29
- alias_method :active_scaffold_human_condition_decimal, :active_scaffold_human_condition_integer
30
- alias_method :active_scaffold_human_condition_float, :active_scaffold_human_condition_integer
37
+ alias active_scaffold_human_condition_decimal active_scaffold_human_condition_integer
38
+ alias active_scaffold_human_condition_float active_scaffold_human_condition_integer
31
39
 
32
40
  def active_scaffold_human_condition_string(column, value)
33
41
  opt = ActiveScaffold::Finder::STRING_COMPARATORS.key(value['opt']) || value['opt']
@@ -43,16 +51,16 @@ module ActiveScaffold
43
51
  to = "- #{I18n.l to}" if to
44
52
  format_human_condition column, value['opt'], from, to
45
53
  end
46
- alias_method :active_scaffold_human_condition_time, :active_scaffold_human_condition_date
47
- alias_method :active_scaffold_human_condition_datetime, :active_scaffold_human_condition_date
48
- alias_method :active_scaffold_human_condition_timestamp, :active_scaffold_human_condition_date
54
+ alias active_scaffold_human_condition_time active_scaffold_human_condition_date
55
+ alias active_scaffold_human_condition_datetime active_scaffold_human_condition_date
56
+ alias active_scaffold_human_condition_timestamp active_scaffold_human_condition_date
49
57
 
50
58
  def active_scaffold_human_condition_boolean(column, value)
51
59
  attribute = column.active_record_class.human_attribute_name(column.name)
52
60
  label = as_(ActiveScaffold::Core.column_type_cast(value, column.column) ? :true : :false)
53
61
  as_(:boolean, :scope => :human_conditions, :column => attribute, :value => label)
54
62
  end
55
- alias_method :active_scaffold_human_condition_checkbox, :active_scaffold_human_condition_boolean
63
+ alias active_scaffold_human_condition_checkbox active_scaffold_human_condition_boolean
56
64
 
57
65
  def active_scaffold_human_condition_null(column, value)
58
66
  format_human_condition column, value.to_sym
@@ -66,21 +74,23 @@ module ActiveScaffold
66
74
  associated = column.association.klass.where(:id => associated.map(&:to_i)).map(&method)
67
75
  elsif column.options[:options]
68
76
  associated = associated.collect do |value|
69
- text, val = column.options[:options].find { |text, val| (val.nil? ? text : val).to_s == value.to_s }
77
+ text, val = column.options[:options].find { |t, v| (v.nil? ? t : v).to_s == value.to_s }
70
78
  value = active_scaffold_translated_option(column, text, val).first if text
71
79
  value
72
80
  end
73
81
  end
74
82
  as_(:association, :scope => :human_conditions, :column => attribute, :value => associated.join(', '))
75
83
  end
76
- alias_method :active_scaffold_human_condition_multi_select, :active_scaffold_human_condition_select
77
- alias_method :active_scaffold_human_condition_record_select, :active_scaffold_human_condition_select
84
+ alias active_scaffold_human_condition_multi_select active_scaffold_human_condition_select
85
+ alias active_scaffold_human_condition_record_select active_scaffold_human_condition_select
86
+ alias active_scaffold_human_condition_chosen active_scaffold_human_condition_select
87
+ alias active_scaffold_human_condition_multi_chosen active_scaffold_human_condition_select
78
88
 
79
89
  # the naming convention for overriding form fields with helpers
80
90
  def override_human_condition_column(column)
81
91
  override_helper column, 'human_condition_column'
82
92
  end
83
- alias_method :override_human_condition_column?, :override_human_condition_column
93
+ alias override_human_condition_column? override_human_condition_column
84
94
 
85
95
  def override_human_condition?(search_ui)
86
96
  respond_to?(override_human_condition(search_ui))
@@ -130,7 +130,7 @@ module ActiveScaffold
130
130
 
131
131
  # whitelists id-safe characters
132
132
  def clean_id(val)
133
- val.gsub /[^-_0-9a-zA-Z]/, '-'
133
+ val.gsub(/[^-_0-9a-zA-Z]/, '-')
134
134
  end
135
135
  end
136
136
  end
@@ -1,11 +1,15 @@
1
- # coding: utf-8
2
1
  module ActiveScaffold
3
2
  module Helpers
4
3
  # Helpers that assist with the rendering of a List Column
5
4
  module ListColumnHelpers
6
5
  def get_column_value(record, column)
7
- method = get_column_method(record, column)
8
- value = send(method, record, column)
6
+ record = record.send(column.delegated_association.name) if column.delegated_association
7
+ if record
8
+ method = get_column_method(record, column)
9
+ value = send(method, record, column)
10
+ else
11
+ value = nil
12
+ end
9
13
  value = '&nbsp;'.html_safe if value.nil? || value.blank? # fix for IE 6
10
14
  return value
11
15
  rescue StandardError => e
@@ -38,7 +42,9 @@ module ActiveScaffold
38
42
  if column.link && !skip_action_link?(column.link, record)
39
43
  link = column.link
40
44
  associated = record.send(column.association.name) if column.association
41
- render_action_link(link, record, :link => text, :authorized => link.action.nil? || column_link_authorized?(link, column, record, associated))
45
+ authorized = link.action.nil?
46
+ authorized, reason = column_link_authorized?(link, column, record, associated) unless authorized
47
+ render_action_link(link, record, :link => text, :authorized => authorized, :not_authorized_reason => reason)
42
48
  elsif inplace_edit?(record, column)
43
49
  active_scaffold_inplace_edit(record, column, :formatted_column => text)
44
50
  elsif active_scaffold_config.actions.include?(:list) && active_scaffold_config.list.wrap_tag
@@ -84,7 +90,7 @@ module ActiveScaffold
84
90
  options.delete(:disabled) if inplace_edit?(record, column)
85
91
  check_box(:record, column.name, options)
86
92
  end
87
-
93
+
88
94
  def active_scaffold_column_percentage(record, column)
89
95
  options = column.options[:slider] || {}
90
96
  options = options.merge(min: record.send(options[:min_method])) if options[:min_method]
@@ -93,10 +99,31 @@ module ActiveScaffold
93
99
  as_slider options.merge(value: value || record.send(column.name))
94
100
  end
95
101
 
102
+ def active_scaffold_column_month(record, column)
103
+ l record.send(column.name), format: :year_month
104
+ end
105
+
106
+ def active_scaffold_column_week(record, column)
107
+ l record.send(column.name), format: :week
108
+ end
109
+
110
+ def tel_to(text)
111
+ groups = text.to_s.scan(/(?:^\+)?\d+/)
112
+ extension = groups.pop if text.to_s =~ /\s*[^\d\s]+\s*\d+$/
113
+ link_to text, "tel:#{[groups.join('-'), extension].compact.join(',')}"
114
+ end
115
+
116
+ def active_scaffold_column_telephone(record, column)
117
+ phone = record.send column.name
118
+ return if phone.blank?
119
+ phone = number_to_phone(phone) unless column.options[:format] == false
120
+ tel_to phone
121
+ end
122
+
96
123
  def column_override(column)
97
124
  override_helper column, 'column'
98
125
  end
99
- alias_method :column_override?, :column_override
126
+ alias column_override? column_override
100
127
 
101
128
  # the naming convention for overriding column types with helpers
102
129
  def override_column_ui(list_ui)
@@ -105,7 +132,7 @@ module ActiveScaffold
105
132
  method = "active_scaffold_column_#{list_ui}"
106
133
  @_column_ui_overrides[list_ui] = (method if respond_to? method)
107
134
  end
108
- alias_method :override_column_ui?, :override_column_ui
135
+ alias override_column_ui? override_column_ui
109
136
 
110
137
  ##
111
138
  ## Formatting
@@ -113,19 +140,23 @@ module ActiveScaffold
113
140
  def format_column_value(record, column, value = nil)
114
141
  value ||= record.send(column.name) unless record.nil?
115
142
  if column.association.nil?
116
- if [:select, :radio].include?(column.form_ui) && column.options[:options]
117
- text, val = column.options[:options].find { |text, val| (val.nil? ? text : val).to_s == value.to_s }
143
+ if %i[select radio].include?(column.form_ui) && column.options[:options]
144
+ text, val = column.options[:options].find { |t, v| (v.nil? ? t : v).to_s == value.to_s }
118
145
  value = active_scaffold_translated_option(column, text, val).first if text
119
146
  end
120
- if value.is_a? Numeric
147
+ if grouped_search? && column == search_group_column && search_group_function
148
+ format_grouped_search_column(value, column.options)
149
+ elsif value.is_a? Numeric
121
150
  format_number_value(value, column.options)
122
151
  else
123
152
  format_value(value, column.options)
124
153
  end
125
154
  else
126
- if column.plural_association?
155
+ if column.association.collection?
127
156
  associated_size = value.size if column.associated_number? # get count before cache association
128
- cache_association(record.association(column.name), column, associated_size) unless value.loaded?
157
+ if column.association.respond_to_target? && !value.loaded?
158
+ cache_association(record.association(column.name), column, associated_size)
159
+ end
129
160
  end
130
161
  format_association_value(value, column, associated_size)
131
162
  end
@@ -134,38 +165,55 @@ module ActiveScaffold
134
165
  def format_number_value(value, options = {})
135
166
  if value
136
167
  value = case options[:format]
137
- when :size
138
- number_to_human_size(value, options[:i18n_options] || {})
139
- when :percentage
140
- number_to_percentage(value, options[:i18n_options] || {})
141
- when :currency
142
- number_to_currency(value, options[:i18n_options] || {})
143
- when :i18n_number
144
- send("number_with_#{value.is_a?(Integer) ? 'delimiter' : 'precision'}", value, options[:i18n_options] || {})
145
- else
146
- value
168
+ when :size
169
+ number_to_human_size(value, options[:i18n_options] || {})
170
+ when :percentage
171
+ number_to_percentage(value, options[:i18n_options] || {})
172
+ when :currency
173
+ number_to_currency(value, options[:i18n_options] || {})
174
+ when :i18n_number
175
+ send("number_with_#{value.is_a?(Integer) ? 'delimiter' : 'precision'}", value, options[:i18n_options] || {})
176
+ else
177
+ value
147
178
  end
148
179
  end
149
180
  clean_column_value(value)
150
181
  end
151
182
 
183
+ def format_grouped_search_column(value, options = {})
184
+ case search_group_function
185
+ when 'year_month'
186
+ year, month = value.to_s.scan(/(\d*)(\d{2})/)[0]
187
+ I18n.l(Date.new(year.to_i, month.to_i, 1), format: options[:group_format] || search_group_function.to_sym)
188
+ when 'year_quarter'
189
+ year, quarter = value.to_s.scan(/(\d*)(\d)/)[0]
190
+ I18n.t(options[:group_format] || search_group_function, scope: 'date.formats', year: year, quarter: quarter)
191
+ when 'quarter'
192
+ I18n.t(options[:group_format] || search_group_function, scope: 'date.formats', num: value)
193
+ when 'month'
194
+ I18n.l(Date.new(Time.zone.today.year, value, 1), format: options[:group_format] || search_group_function.to_sym)
195
+ else value
196
+ end
197
+ end
198
+
152
199
  def format_collection_association_value(value, column, label_method, size)
153
200
  if column.associated_limit.nil?
154
201
  firsts = value.collect(&label_method)
155
- elsif column.associated_limit == 0
202
+ safe_join firsts, active_scaffold_config.list.association_join_text
203
+ elsif column.associated_limit.zero?
156
204
  size if column.associated_number?
157
205
  else
158
- firsts = value.first(column.associated_limit)
159
- firsts.collect!(&label_method)
206
+ firsts = value.loaded? ? value[0, column.associated_limit] : value.limit(column.associated_limit)
207
+ firsts = firsts.map(&label_method)
160
208
  firsts << '…' if value.size > column.associated_limit
161
- text = firsts.join(h(active_scaffold_config.list.association_join_text)).html_safe
209
+ text = safe_join firsts, active_scaffold_config.list.association_join_text
162
210
  text << " (#{size})" if column.associated_number? && column.associated_limit && value.size > column.associated_limit
163
211
  text
164
212
  end
165
213
  end
166
214
 
167
215
  def format_singular_association_value(value, column, label_method)
168
- if column.polymorphic_association?
216
+ if column.association.polymorphic?
169
217
  "#{value.class.model_name.human}: #{value.send(label_method)}"
170
218
  else
171
219
  value.send(label_method)
@@ -215,7 +263,14 @@ module ActiveScaffold
215
263
 
216
264
  def inplace_edit?(record, column)
217
265
  return unless column.inplace_edit
218
- editable = controller.send(:update_authorized?, record) if controller.respond_to?(:update_authorized?, true)
266
+ if controller.respond_to?(:update_authorized?, true)
267
+ if controller.method(:update_authorized?).parameters.size == 2
268
+ return Array(controller.send(:update_authorized?, record, column.name))[0]
269
+ else
270
+ ActiveSupport::Deprecation.warn 'add column = nil parameter to update_authorized? on your controller'
271
+ editable = Array(controller.send(:update_authorized?, record))[0]
272
+ end
273
+ end
219
274
  editable || record.authorized_for?(:crud_type => :update, :column => column.name)
220
275
  end
221
276
 
@@ -239,17 +294,14 @@ module ActiveScaffold
239
294
 
240
295
  def inplace_edit_control(column)
241
296
  return unless inplace_edit?(active_scaffold_config.model, column) && inplace_edit_cloning?(column)
242
- old_record, @record = @record, active_scaffold_config.model.new # TODO: remove when relying on @record is removed
243
297
  column = column.clone
244
298
  column.options = column.options.clone
245
299
  column.form_ui = :select if column.association && column.form_ui.nil?
246
- options = active_scaffold_input_options(column).merge(:object => active_scaffold_config.model.new)
300
+ options = active_scaffold_input_options(column).merge(:object => column.active_record_class.new)
247
301
  options[:class] = "#{options[:class]} inplace_field"
248
302
  options[:"data-id"] = options[:id]
249
303
  options[:id] = nil
250
- content_tag(:div, active_scaffold_input_for(column, nil, options), :style => 'display:none;', :class => inplace_edit_control_css_class).tap do
251
- @record = old_record # TODO: remove when relying on @record is removed
252
- end
304
+ content_tag(:div, active_scaffold_input_for(column, nil, options), :style => 'display:none;', :class => inplace_edit_control_css_class)
253
305
  end
254
306
 
255
307
  def inplace_edit_control_css_class
@@ -273,8 +325,8 @@ module ActiveScaffold
273
325
  elsif inplace_edit_cloning?(column)
274
326
  data[:ie_mode] = :clone
275
327
  elsif column.inplace_edit == :ajax
276
- url = url_for(:controller => params_for[:controller], :action => 'render_field', :id => '__id__', :update_column => column.name)
277
- plural = column.plural_association? && !override_form_field?(column) && [:select, :record_select].include?(column.form_ui)
328
+ url = url_for(params_for(:controller => params_for[:controller], :action => 'render_field', :id => '__id__', :update_column => column.name))
329
+ plural = column.association.try(:collection?) && !override_form_field?(column) && %i[select record_select].include?(column.form_ui)
278
330
  data[:ie_render_url] = url
279
331
  data[:ie_mode] = :ajax
280
332
  data[:ie_plural] = plural
@@ -282,6 +334,8 @@ module ActiveScaffold
282
334
  data
283
335
  end
284
336
 
337
+ # MARK
338
+
285
339
  def all_marked?
286
340
  if active_scaffold_config.mark.mark_all_mode == :page
287
341
  @page.items.detect { |record| !marked_records.include?(record.id) }.nil?
@@ -298,6 +352,8 @@ module ActiveScaffold
298
352
  content_tag(:span, check_box_tag("#{controller_id}_mark_heading_span_input", '1', all_marked?), tag_options)
299
353
  end
300
354
 
355
+ # COLUMN HEADINGS
356
+
301
357
  def column_heading_attributes(column, sorting, sort_direction)
302
358
  {:id => active_scaffold_column_header_id(column), :class => column_heading_class(column, sorting), :title => strip_tags(column.description).presence}
303
359
  end
@@ -322,11 +378,12 @@ module ActiveScaffold
322
378
  options = {:id => nil, :class => 'as_sort',
323
379
  'data-page-history' => controller_id,
324
380
  :remote => true, :method => :get}
325
- # :id needed because rails reuse it even if we delete from params (like do_refresh_list does)
326
- url_options = params_for(:action => :index, :page => 1, :id => params[:id],
327
- :sort => column.name, :sort_direction => sort_direction)
381
+ url_options = {action: :index, page: 1, sort: column.name, sort_direction: sort_direction}
382
+ # :id needed because rails reuse it even if it was deleted from params (like do_refresh_list does)
383
+ url_options[:id] = nil if @remove_id_from_list_links
384
+ url_options = params_for(url_options)
328
385
  unless active_scaffold_config.store_user_settings
329
- url_options.merge!(:search => search_params) if search_params.present?
386
+ url_options[:search] = search_params if respond_to?(:search_params) && search_params.present?
330
387
  end
331
388
  link_to column_heading_label(column), url_options, options
332
389
  else
@@ -337,6 +394,27 @@ module ActiveScaffold
337
394
  def column_heading_label(column)
338
395
  column.label
339
396
  end
397
+
398
+ # CALCULATIONS
399
+
400
+ def column_calculation(column)
401
+ if column.calculate.instance_of? Proc
402
+ column.calculate.call(@records)
403
+ else
404
+ calculate_query.calculate(column.calculate, column.name)
405
+ end
406
+ end
407
+
408
+ def render_column_calculation(column)
409
+ calculation = column_calculation(column)
410
+ override_formatter = "render_#{column.name}_#{column.calculate.is_a?(Proc) ? :calculate : column.calculate}"
411
+ calculation = send(override_formatter, calculation) if respond_to? override_formatter
412
+ format_column_calculation(column, calculation)
413
+ end
414
+
415
+ def format_column_calculation(column, calculation)
416
+ "#{"#{as_(column.calculate)}: " unless column.calculate.is_a? Proc}#{format_column_value nil, column, calculation}"
417
+ end
340
418
  end
341
419
  end
342
420
  end
@@ -6,14 +6,11 @@ module ActiveScaffold
6
6
  end
7
7
 
8
8
  def pagination_url_options(url_options = nil)
9
- # :id needed because rails reuse it even if we delete from params (like do_refresh_list does)
10
- url_options ||= params_for(:action => @pagination_action || :index, :id => params[:id])
11
- unless active_scaffold_config.store_user_settings
12
- url_options.merge!(:search => search_params) if search_params.present?
13
- if active_scaffold_config.list.user.user_sorting?
14
- column, direction = active_scaffold_config.list.user.sorting.first
15
- url_options.merge!(:sort => column.name, :sort_direction => direction)
16
- end
9
+ if url_options.nil?
10
+ url_options = {action: @pagination_action || :index}
11
+ # :id needed because rails reuse it even if it was deleted from params (like do_refresh_list does)
12
+ url_options[:id] = nil if @remove_id_from_list_links
13
+ url_options = params_for(url_options)
17
14
  end
18
15
  url_options
19
16
  end
@@ -51,11 +48,11 @@ module ActiveScaffold
51
48
  html << '..' if start_number > last_page + 1
52
49
 
53
50
  [start_number, last_page + 1].max.upto(end_number) do |num|
54
- if current_page.number == num
55
- html << content_tag(:span, num.to_s, :class => 'as_paginate current')
56
- else
57
- html << pagination_ajax_link(num, url_options, options)
58
- end
51
+ html << if current_page.number == num
52
+ content_tag(:span, num.to_s, :class => 'as_paginate current')
53
+ else
54
+ pagination_ajax_link(num, url_options, options)
55
+ end
59
56
  end
60
57
 
61
58
  if current_page.pager.infinite?
@@ -68,7 +65,7 @@ module ActiveScaffold
68
65
  html << pagination_ajax_link(num, url_options, options)
69
66
  end
70
67
  end
71
- html.join(' ').html_safe
68
+ safe_join html, ' '
72
69
  end
73
70
  end
74
71
  end
@@ -7,8 +7,10 @@ module ActiveScaffold
7
7
  def active_scaffold_search_for(column, options = nil)
8
8
  options ||= active_scaffold_search_options(column)
9
9
  record = options[:object]
10
- ActiveSupport::Deprecation.warn 'Relying on @record is deprecated, include :object in options with record.', caller if record.nil? # TODO: Remove when relying on @record is removed
11
- record ||= @record # TODO: Remove when relying on @record is removed
10
+ if column.delegated_association
11
+ record = record.send(column.delegated_association.name) || column.active_record_class.new
12
+ options[:object] = record
13
+ end
12
14
 
13
15
  # first, check if the dev has created an override for this specific field for search
14
16
  if (method = override_search_field(column))
@@ -41,7 +43,7 @@ module ActiveScaffold
41
43
  # final ultimate fallback: use rails' generic input method
42
44
  else
43
45
  # for textual fields we pass different options
44
- text_types = [:text, :string, :integer, :float, :decimal]
46
+ text_types = %i[text string integer float decimal]
45
47
  options = active_scaffold_input_text_options(options) if text_types.include?(column.column.type)
46
48
  text_field(:record, column.name, options.merge(column.options))
47
49
  end
@@ -64,7 +66,7 @@ module ActiveScaffold
64
66
  end
65
67
 
66
68
  def search_label_for(column, options)
67
- options[:id] unless [:range, :integer, :decimal, :float, :string, :date_picker, :datetime_picker, :calendar_date_select].include? column.search_ui
69
+ options[:id] unless %i[range integer decimal float string date_picker datetime_picker calendar_date_select].include? column.search_ui
68
70
  end
69
71
 
70
72
  ##
@@ -73,8 +75,6 @@ module ActiveScaffold
73
75
 
74
76
  def active_scaffold_search_multi_select(column, options)
75
77
  record = options.delete(:object)
76
- ActiveSupport::Deprecation.warn 'Relying on @record is deprecated, include :object in options with record.', caller if record.nil? # TODO: Remove when relying on @record is removed
77
- record ||= @record # TODO: Remove when relying on @record is removed
78
78
  associated = options.delete :value
79
79
  associated = [associated].compact unless associated.is_a? Array
80
80
 
@@ -96,12 +96,10 @@ module ActiveScaffold
96
96
 
97
97
  def active_scaffold_search_select(column, html_options, options = {})
98
98
  record = html_options.delete(:object)
99
- ActiveSupport::Deprecation.warn 'Relying on @record is deprecated, include :object in html_options with record.', caller if record.nil? # TODO: Remove when relying on @record is removed
100
- record ||= @record # TODO: Remove when relying on @record is removed
101
99
  associated = html_options.delete :value
102
100
  if column.association
103
101
  associated = associated.is_a?(Array) ? associated.map(&:to_i) : associated.to_i unless associated.nil?
104
- method = column.association.macro == :belongs_to ? column.association.foreign_key : column.name
102
+ method = column.association.belongs_to? ? column.association.foreign_key : column.name
105
103
  select_options = sorted_association_options_find(column.association, false, record)
106
104
  else
107
105
  method = column.name
@@ -113,7 +111,7 @@ module ActiveScaffold
113
111
  options = options.merge(:selected => associated).merge column.options
114
112
  html_options.merge! column.options[:html_options] || {}
115
113
  if html_options[:multiple]
116
- html_options[:name] += '[]'
114
+ active_scaffold_select_name_with_multiple html_options
117
115
  else
118
116
  options[:include_blank] ||= as_(:_select_)
119
117
  active_scaffold_translate_select_options(options)
@@ -143,7 +141,27 @@ module ActiveScaffold
143
141
  select_tag(options[:name], options_for_select(select_options, ActiveScaffold::Core.column_type_cast(options[:value], column.column)), :id => options[:id])
144
142
  end
145
143
  # we can't use checkbox ui because it's not possible to decide whether search for this field or not
146
- alias_method :active_scaffold_search_checkbox, :active_scaffold_search_boolean
144
+ alias active_scaffold_search_checkbox active_scaffold_search_boolean
145
+
146
+ def active_scaffold_group_search_column(record, options)
147
+ select_tag 'search[active_scaffold_group]', options_for_select(active_scaffold_group_search_options, selected: field_search_params['active_scaffold_group'])
148
+ end
149
+
150
+ def active_scaffold_group_search_options
151
+ options = active_scaffold_config.field_search.group_options.collect do |text, value|
152
+ active_scaffold_translated_option(active_scaffold_group_column, text, value)
153
+ end
154
+ [[as_(:no_group), '']].concat options
155
+ end
156
+
157
+ def active_scaffold_group_column
158
+ return if active_scaffold_config.field_search.group_options.blank?
159
+ @_active_scaffold_group_column ||= begin
160
+ column = ActiveScaffold::DataStructures::Column.new(:active_scaffold_group, active_scaffold_config.model)
161
+ column.label = :group_by
162
+ column
163
+ end
164
+ end
147
165
 
148
166
  def active_scaffold_search_null(column, options)
149
167
  select_options = []
@@ -165,10 +183,11 @@ module ActiveScaffold
165
183
  def active_scaffold_search_range_comparator_options(column)
166
184
  select_options = ActiveScaffold::Finder::NUMERIC_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] }
167
185
  if active_scaffold_search_range_string?(column)
168
- select_options.unshift *ActiveScaffold::Finder::STRING_COMPARATORS.collect { |title, comp| [as_(title), comp] }
186
+ comparators = ActiveScaffold::Finder::STRING_COMPARATORS.collect { |title, comp| [as_(title), comp] }
187
+ select_options.unshift(*comparators)
169
188
  end
170
189
  if include_null_comparators? column
171
- select_options += ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] }
190
+ select_options.concat ActiveScaffold::Finder::NULL_COMPARATORS.collect { |comp| [as_(comp), comp] }
172
191
  end
173
192
  select_options
174
193
  end
@@ -176,13 +195,13 @@ module ActiveScaffold
176
195
  def include_null_comparators?(column)
177
196
  return column.options[:null_comparators] if column.options.key? :null_comparators
178
197
  if column.association
179
- column.association.macro != :belongs_to || active_scaffold_config.columns[column.association.foreign_key].column.try(:null)
198
+ !column.association.belongs_to? || active_scaffold_config.columns[column.association.foreign_key].column.try(:null)
180
199
  else
181
200
  column.column.try(:null)
182
201
  end
183
202
  end
184
203
 
185
- def active_scaffold_search_range(column, options)
204
+ def active_scaffold_search_range(column, options, input_method = :text_field_tag, input_options = {})
186
205
  opt_value, from_value, to_value = field_search_params_range_values(column)
187
206
 
188
207
  select_options = active_scaffold_search_range_comparator_options(column)
@@ -199,34 +218,48 @@ module ActiveScaffold
199
218
  to_value = format_number_value(to_value, column.options) if to_value.is_a?(Numeric)
200
219
  html = select_tag("#{options[:name]}[opt]", options_for_select(select_options, opt_value),
201
220
  :id => "#{options[:id]}_opt", :class => 'as_search_range_option')
221
+ from_options = active_scaffold_input_text_options(input_options.merge(:id => options[:id], :size => text_field_size))
222
+ to_options = from_options.merge(:id => "#{options[:id]}_to")
202
223
  html << content_tag('span', :id => "#{options[:id]}_numeric", :style => ActiveScaffold::Finder::NULL_COMPARATORS.include?(opt_value) ? 'display: none' : nil) do
203
- text_field_tag("#{options[:name]}[from]", from_value, active_scaffold_input_text_options(:id => options[:id], :size => text_field_size)) <<
224
+ send(input_method, "#{options[:name]}[from]", from_value, input_options) <<
204
225
  content_tag(
205
- :span, (
206
- ' - ' + text_field_tag("#{options[:name]}[to]", to_value,
207
- active_scaffold_input_text_options(:id => "#{options[:id]}_to", :size => text_field_size))
208
- ).html_safe,
226
+ :span,
227
+ safe_join([' - ', send(input_method, "#{options[:name]}[to]", to_value, to_options)]),
209
228
  :id => "#{options[:id]}_between", :class => 'as_search_range_between', :style => (opt_value == 'BETWEEN') ? nil : 'display: none'
210
229
  )
211
230
  end
212
231
  content_tag :span, html, :class => 'search_range'
213
232
  end
214
- alias_method :active_scaffold_search_integer, :active_scaffold_search_range
215
- alias_method :active_scaffold_search_decimal, :active_scaffold_search_range
216
- alias_method :active_scaffold_search_float, :active_scaffold_search_range
217
- alias_method :active_scaffold_search_string, :active_scaffold_search_range
233
+ alias active_scaffold_search_string active_scaffold_search_range
234
+
235
+ def active_scaffold_search_integer(column, options)
236
+ active_scaffold_search_range(column, options, :number_field_tag, step: '1')
237
+ end
238
+
239
+ def active_scaffold_search_decimal(column, options)
240
+ active_scaffold_search_range(column, options, :number_field_tag, step: :any)
241
+ end
242
+ alias active_scaffold_search_float active_scaffold_search_decimal
218
243
 
219
244
  def field_search_datetime_value(value)
220
- DateTime.new(value[:year].to_i, value[:month].to_i, value[:day].to_i, value[:hour].to_i, value[:minute].to_i, value[:second].to_i) unless value.nil? || value[:year].blank?
245
+ Time.zone.local(value[:year].to_i, value[:month].to_i, value[:day].to_i, value[:hour].to_i, value[:minute].to_i, value[:second].to_i) unless value.nil? || value[:year].blank?
221
246
  end
222
247
 
223
248
  def active_scaffold_search_datetime(column, options)
224
249
  _, from_value, to_value = field_search_params_range_values(column)
225
250
  options = column.options.merge(options)
226
- helper = "select_#{'date' unless options[:discard_date]}#{'time' unless options[:discard_time]}"
251
+ type = "#{'date' unless options[:discard_date]}#{'time' unless options[:discard_time]}"
252
+ use_select = options.delete(:use_select)
253
+ helper = use_select ? "select_#{type}" : "#{type}#{'_local' if type == 'datetime'}_field"
254
+ if use_select
255
+ default_from_options = {include_blank: true, prefix: "#{options[:name]}[from]"}
256
+ default_to_options = {include_blank: true, prefix: "#{options[:name]}[to]"}
257
+ end
227
258
 
228
- send(helper, field_search_datetime_value(from_value), {:include_blank => true, :prefix => "#{options[:name]}[from]"}.merge(options)) <<
229
- ' - '.html_safe << send(helper, field_search_datetime_value(to_value), {:include_blank => true, :prefix => "#{options[:name]}[to]"}.merge(options))
259
+ safe_join [
260
+ send(helper, field_search_datetime_value(from_value), options.reverse_merge(default_from_options || {})),
261
+ send(helper, field_search_datetime_value(to_value), options.reverse_merge(default_to_options || {}))
262
+ ], ' - '
230
263
  end
231
264
 
232
265
  def active_scaffold_search_date(column, options)
@@ -236,7 +269,7 @@ module ActiveScaffold
236
269
  def active_scaffold_search_time(column, options)
237
270
  active_scaffold_search_datetime(column, options.merge!(:discard_date => true))
238
271
  end
239
- alias_method :active_scaffold_search_timestamp, :active_scaffold_search_datetime
272
+ alias active_scaffold_search_timestamp active_scaffold_search_datetime
240
273
 
241
274
  ##
242
275
  ## Search column override signatures
@@ -267,16 +300,20 @@ module ActiveScaffold
267
300
  visibles << column
268
301
  end
269
302
  end
303
+ if active_scaffold_group_column
304
+ columns = grouped_search? || search_config.optional_columns.empty? ? visibles : hiddens
305
+ columns << active_scaffold_group_column
306
+ end
270
307
  [visibles, hiddens]
271
308
  end
272
309
 
273
310
  def searched_by?(column)
274
- value = field_search_params[column.name]
311
+ value = field_search_params[column.name.to_s]
275
312
  case value
276
313
  when Hash
277
- !value['from'].blank?
314
+ value['from'].present?
278
315
  when String
279
- !value.blank?
316
+ value.present?
280
317
  else
281
318
  false
282
319
  end