active_scaffold 3.4.43 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -5,31 +5,85 @@ module ActiveScaffold
5
5
  end
6
6
 
7
7
  module ClassMethods
8
+ def self.extended(klass)
9
+ return unless klass.active_scaffold_config
10
+ if klass.active_scaffold_config.active_record?
11
+ klass.extend ActiveRecord
12
+ elsif klass.active_scaffold_config.mongoid?
13
+ klass.extend Mongoid
14
+ end
15
+ end
16
+
8
17
  # Takes a collection of search terms (the tokens) and creates SQL that
9
18
  # searches all specified ActiveScaffold columns. A row will match if each
10
19
  # token is found in at least one of the columns.
11
- def create_conditions_for_columns(tokens, columns, text_search = :full)
20
+ def conditions_for_columns(tokens, columns, text_search = :full)
12
21
  # if there aren't any columns, then just return a nil condition
13
22
  return unless columns.any?
14
- like_pattern = like_pattern(text_search)
15
23
 
16
24
  tokens = [tokens] if tokens.is_a? String
25
+ tokens = type_casted_tokens(tokens, columns, like_pattern(text_search))
26
+ create_conditions_for_columns(tokens, columns)
27
+ end
17
28
 
18
- where_clauses = []
19
- columns.each do |column|
20
- Array(column.search_sql).each do |search_sql|
21
- where_clauses << "#{search_sql} #{column.text? ? ActiveScaffold::Finder.like_operator : '='} ?"
29
+ def type_casted_tokens(tokens, columns, like_pattern)
30
+ tokens.map do |value|
31
+ columns.each_with_object({}) do |column, column_tokens|
32
+ column_tokens[column.name] = column.text? ? like_pattern.sub('?', value) : ActiveScaffold::Core.column_type_cast(value, column.column)
22
33
  end
23
34
  end
24
- phrase = where_clauses.join(' OR ')
35
+ end
25
36
 
26
- tokens.collect do |value|
27
- columns.each_with_object([phrase]) do |column, condition|
28
- Array(column.search_sql).size.times do
29
- condition.push(column.text? ? like_pattern.sub('?', value) : ActiveScaffold::Core.column_type_cast(value, column.column))
37
+ module ActiveRecord
38
+ def create_conditions_for_columns(tokens, columns)
39
+ where_clauses = []
40
+ columns.each do |column|
41
+ column.search_sql.each do |search_sql|
42
+ where_clauses << "#{search_sql} #{column.text? ? ActiveScaffold::Finder.like_operator : '='} ?"
43
+ end
44
+ end
45
+ phrase = where_clauses.join(' OR ')
46
+
47
+ tokens.map do |columns_token|
48
+ columns.each_with_object([phrase]) do |column, condition|
49
+ condition.concat([columns_token[column.name]] * column.search_sql.size)
30
50
  end
31
51
  end
32
52
  end
53
+
54
+ def like_pattern(text_search)
55
+ case text_search
56
+ when :full then '%?%'
57
+ when :start then '?%'
58
+ when :end then '%?'
59
+ else '?'
60
+ end
61
+ end
62
+ end
63
+
64
+ module Mongoid
65
+ def create_conditions_for_columns(tokens, columns)
66
+ conditions = tokens.map do |columns_token|
67
+ token_conditions = columns.map do |column|
68
+ value = columns_token[column.name]
69
+ value = /#{value}/ if column.text?
70
+ column.search_sql.map do |search_sql|
71
+ {search_sql => value}
72
+ end
73
+ end.flatten
74
+ active_scaffold_config.model.or(token_conditions).selector
75
+ end
76
+ [active_scaffold_config.model.and(conditions).selector]
77
+ end
78
+
79
+ def like_pattern(text_search)
80
+ case text_search
81
+ when :full then '?'
82
+ when :start then '^?'
83
+ when :end then '?$'
84
+ else '^?$'
85
+ end
86
+ end
33
87
  end
34
88
 
35
89
  # Generates an SQL condition for the given ActiveScaffold column based on
@@ -38,14 +92,14 @@ module ActiveScaffold
38
92
  def condition_for_column(column, value, text_search = :full)
39
93
  like_pattern = like_pattern(text_search)
40
94
  value = value.with_indifferent_access if value.is_a? Hash
41
- if self.respond_to?("condition_for_#{column.name}_column")
95
+ if respond_to?("condition_for_#{column.name}_column")
42
96
  return send("condition_for_#{column.name}_column", column, value, like_pattern)
43
97
  end
44
- return unless column && column.search_sql && !value.blank?
45
- search_ui = column.search_ui || column.column.try(:type)
98
+ return unless column && column.search_sql && value.present?
99
+ search_ui = column.search_ui || column.column_type
46
100
  begin
47
101
  sql, *values =
48
- if search_ui && self.respond_to?("condition_for_#{search_ui}_type")
102
+ if search_ui && respond_to?("condition_for_#{search_ui}_type")
49
103
  send("condition_for_#{search_ui}_type", column, value, like_pattern)
50
104
  else
51
105
  if column.search_sql.instance_of? Proc
@@ -60,7 +114,7 @@ module ActiveScaffold
60
114
  conditions += values * column.search_sql.size if values.present?
61
115
  conditions
62
116
  rescue StandardError => e
63
- logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{name}"
117
+ Rails.logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{name}"
64
118
  raise e
65
119
  end
66
120
  end
@@ -76,7 +130,8 @@ module ActiveScaffold
76
130
  when :date, :time, :datetime, :timestamp
77
131
  condition_for_datetime(column, value)
78
132
  when :select, :multi_select, :country, :usa_state, :chosen, :multi_chosen
79
- ['%{search_sql} in (?)', Array(value)]
133
+ values = Array(value).select(&:present?)
134
+ ['%{search_sql} in (?)', values] if values.present?
80
135
  else
81
136
  if column.text?
82
137
  ["%{search_sql} #{ActiveScaffold::Finder.like_operator} ?", like_pattern.sub('?', value)]
@@ -120,61 +175,89 @@ module ActiveScaffold
120
175
  end
121
176
  end
122
177
 
123
- def translate_days_and_months(value, format)
178
+ def tables_for_translating_days_and_months(format)
124
179
  keys = {
125
180
  '%A' => 'date.day_names',
126
181
  '%a' => 'date.abbr_day_names',
127
182
  '%B' => 'date.month_names',
128
183
  '%b' => 'date.abbr_month_names'
129
184
  }
130
- keys.each do |f, k|
131
- if format.include? f
132
- table = Hash[I18n.t(k).compact.zip(I18n.t(k, :locale => :en).compact)]
133
- value.gsub!(Regexp.union(table.keys)) { |s| table[s] }
134
- end
185
+ key_index = keys.keys.map { |key| [key, format.index(key)] }.to_h
186
+ keys.select! { |k, _| key_index[k] }
187
+ keys.sort_by { |k, _| key_index[k] }.map do |_, k|
188
+ I18n.t(k).compact.zip(I18n.t(k, :locale => :en).compact).to_h
135
189
  end
136
- value
137
190
  end
138
191
 
139
- def condition_value_for_datetime(column, value, conversion = :to_time)
140
- if value.is_a? Hash
141
- time = Time.zone.local(*[:year, :month, :day, :hour, :minute, :second].collect { |part| value[part].to_i }) rescue nil
142
- time.send(conversion) if time
143
- elsif value.respond_to?(:strftime)
144
- if conversion == :to_time
145
- # Explicitly get the current zone, because TimeWithZone#to_time in rails 3.2.3 returns UTC.
146
- # https://github.com/rails/rails/pull/2453
147
- value.to_time.in_time_zone
148
- else
149
- value.send(conversion)
192
+ def translate_days_and_months(value, format)
193
+ translated = ''
194
+ tables_for_translating_days_and_months(format).each do |table|
195
+ regexp = Regexp.union(table.keys)
196
+ index = value.index(regexp)
197
+ next unless index
198
+ translated << value.slice!(0...index)
199
+ value.sub!(regexp) do |str|
200
+ translated << table[str]
201
+ ''
150
202
  end
151
- elsif conversion == :to_date
152
- Date.strptime(value, I18n.t("date.formats.#{column.options[:format] || :default}")) rescue nil
203
+ end
204
+ translated << value
205
+ end
206
+
207
+ def format_for_datetime(column, value)
208
+ parts = Date._parse(value)
209
+ if ActiveScaffold.js_framework == :jquery
210
+ format = I18n.translate "time.formats.#{column.options[:format] || :picker}", :default => ''
211
+ end
212
+
213
+ if format.blank?
214
+ time_parts = [[:hour, '%H'], [:min, '%M'], [:sec, '%S']].map do |part, format_part|
215
+ format_part if parts[part].present?
216
+ end.compact
217
+ format = "#{I18n.t('date.formats.default')} #{time_parts.join(':')} #{'%z' if parts[:offset].present?}"
153
218
  else
154
- parts = Date._parse(value)
155
- format = I18n.translate "time.formats.#{column.options[:format] || :picker}", :default => '' if ActiveScaffold.js_framework == :jquery
156
- if format.blank?
157
- time_parts = [[:hour, '%H'], [:min, '%M'], [:sec, '%S']].collect { |part, format_part| format_part if parts[part].present? }.compact
158
- format = "#{I18n.t('date.formats.default')} #{time_parts.join(':')} #{'%z' if parts[:offset].present?}"
159
- else
160
- if parts[:hour]
161
- [[:min, '%M'], [:sec, '%S']].each { |part, f| format.gsub!(":#{f}", '') unless parts[part].present? }
219
+ [[:hour, '%H'], [:min, ':%M'], [:sec, ':%S']].each do |part, f|
220
+ format.gsub!(f, '') if parts[part].blank?
221
+ end
222
+ format += ' %z' if parts[:offset].present? && format !~ /%z/i
223
+ end
224
+
225
+ format.gsub!(/.*(?=%H)/, '') if !parts[:year] && !parts[:month] && !parts[:mday]
226
+ [format, parts[:offset]]
227
+ end
228
+
229
+ def condition_value_for_datetime(column, value, conversion = :to_time)
230
+ unless value.nil? || value.blank?
231
+ if value.is_a? Hash
232
+ time = Time.zone.local(*%i[year month day hour minute second].collect { |part| value[part].to_i }) rescue nil
233
+ time.send(conversion) if time
234
+ elsif value.respond_to?(:strftime)
235
+ if conversion == :to_time
236
+ # Explicitly get the current zone, because TimeWithZone#to_time in rails 3.2.3 returns UTC.
237
+ # https://github.com/rails/rails/pull/2453
238
+ value.to_time.in_time_zone
162
239
  else
163
- value += ' 00:00:00'
240
+ value.send(conversion)
164
241
  end
165
- format += ' %z' if parts[:offset].present? && format !~ /%z/i
166
- end
167
- if !parts[:year] && !parts[:month] && !parts[:mday]
168
- value = "#{Date.today.strftime(format.gsub(/%[HI].*/, ''))} #{value}"
169
- end
170
- value = translate_days_and_months(value, format) if I18n.locale != :en
171
- time = DateTime.strptime(value, format) rescue nil
172
- if time
173
- time = Time.zone.local_to_utc(time).in_time_zone unless parts[:offset]
174
- time = time.send(conversion) unless conversion == :to_time
242
+ elsif conversion == :to_date
243
+ format = I18n.t("date.formats.#{column.options[:format] || :default}")
244
+ format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
245
+ value = translate_days_and_months(value, format) if I18n.locale != :en
246
+ Date.strptime(value, format) rescue nil
247
+ elsif value.include?('T')
248
+ Time.zone.parse(value)
249
+ else # datetime
250
+ format, offset = format_for_datetime(column, value)
251
+ format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
252
+ value = translate_days_and_months(value, format) if I18n.locale != :en
253
+ time = DateTime.strptime(value, format) rescue nil
254
+ if time
255
+ time = Time.zone.local_to_utc(time).in_time_zone unless offset
256
+ time = time.send(conversion) unless conversion == :to_time
257
+ end
258
+ time
175
259
  end
176
- time
177
- end unless value.nil? || value.blank?
260
+ end
178
261
  end
179
262
 
180
263
  def condition_value_for_numeric(column, value)
@@ -185,9 +268,9 @@ module ActiveScaffold
185
268
  when :float then value.to_f
186
269
  when :decimal
187
270
  if Rails.version >= '4.2.0'
188
- ActiveRecord::Type::Decimal.new.type_cast_from_user(value)
271
+ ::ActiveRecord::Type::Decimal.new.type_cast_from_user(value)
189
272
  else
190
- ActiveRecord::ConnectionAdapters::Column.value_to_decimal(value)
273
+ ::ActiveRecord::ConnectionAdapters::Column.value_to_decimal(value)
191
274
  end
192
275
  else
193
276
  value
@@ -234,15 +317,6 @@ module ActiveScaffold
234
317
  ['%{search_sql} is not null', []]
235
318
  end
236
319
  end
237
-
238
- def like_pattern(text_search)
239
- case text_search
240
- when :full then '%?%'
241
- when :start then '?%'
242
- when :end then '%?'
243
- else '?'
244
- end
245
- end
246
320
  end
247
321
 
248
322
  NUMERIC_COMPARATORS = [
@@ -253,13 +327,13 @@ module ActiveScaffold
253
327
  '<',
254
328
  '!=',
255
329
  'BETWEEN'
256
- ]
330
+ ].freeze
257
331
  STRING_COMPARATORS = {
258
332
  :contains => '%?%',
259
333
  :begins_with => '?%',
260
334
  :ends_with => '%?'
261
- }
262
- NULL_COMPARATORS = %w(null not_null)
335
+ }.freeze
336
+ NULL_COMPARATORS = %w[null not_null].freeze
263
337
 
264
338
  def self.included(klass)
265
339
  klass.extend ClassMethods
@@ -277,16 +351,6 @@ module ActiveScaffold
277
351
  @active_scaffold_preload ||= []
278
352
  end
279
353
 
280
- def active_scaffold_includes=(value)
281
- ActiveSupport::Deprecation.warn "active_scaffold_includes doesn't exist anymore, use active_scaffold_preload, active_scaffold_outer_joins or active_scaffold_references"
282
- self.active_scaffold_preload = value
283
- end
284
-
285
- def active_scaffold_includes
286
- ActiveSupport::Deprecation.warn "active_scaffold_includes doesn't exist anymore, use active_scaffold_preload, active_scaffold_outer_joins or active_scaffold_references"
287
- active_scaffold_preload
288
- end
289
-
290
354
  attr_writer :active_scaffold_habtm_joins
291
355
  def active_scaffold_habtm_joins
292
356
  @active_scaffold_habtm_joins ||= []
@@ -303,18 +367,20 @@ module ActiveScaffold
303
367
  end
304
368
 
305
369
  # Override this method on your controller to define conditions to be used when querying a recordset (e.g. for List). The return of this method should be any format compatible with the :conditions clause of ActiveRecord::Base's find.
306
- def conditions_for_collection
307
- end
370
+ def conditions_for_collection; end
308
371
 
309
372
  # Override this method on your controller to define joins to be used when querying a recordset (e.g. for List). The return of this method should be any format compatible with the :joins clause of ActiveRecord::Base's find.
310
- def joins_for_collection
311
- end
373
+ def joins_for_collection; end
312
374
 
313
375
  # Override this method on your controller to provide custom finder options to the find() call. The return of this method should be a hash.
314
376
  def custom_finder_options
315
377
  {}
316
378
  end
317
379
 
380
+ def active_scaffold_embedded_conditions
381
+ params_hash active_scaffold_embedded_params[:conditions]
382
+ end
383
+
318
384
  def all_conditions
319
385
  [
320
386
  id_condition, # for list with id (e.g. /users/:id/index)
@@ -322,12 +388,12 @@ module ActiveScaffold
322
388
  conditions_for_collection, # from the dev
323
389
  conditions_from_params, # from the parameters (e.g. /users/list?first_name=Fred)
324
390
  conditions_from_constraints, # from any constraints (embedded scaffolds)
325
- active_scaffold_session_storage['conditions'] # embedding conditions (weaker constraints)
391
+ active_scaffold_embedded_conditions # embedding conditions (weaker constraints)
326
392
  ].reject(&:blank?)
327
393
  end
328
394
 
329
395
  def id_condition
330
- {active_scaffold_config.model.primary_key => params[:id]} if params[:id]
396
+ {active_scaffold_config.primary_key => params[:id]} if params[:id]
331
397
  end
332
398
 
333
399
  # returns a single record (the given id) but only if it's allowed for the specified security options.
@@ -339,26 +405,30 @@ module ActiveScaffold
339
405
  raise ActiveScaffold::RecordNotAllowed, "#{klass} with id = #{id}" unless record.authorized_for? security_options
340
406
  record
341
407
  end
408
+
342
409
  # valid options may include:
343
410
  # * :sorting - a Sorting DataStructure (basically an array of hashes of field => direction, e.g. [{:field1 => 'asc'}, {:field2 => 'desc'}]). please note that multi-column sorting has some limitations: if any column in a multi-field sort uses method-based sorting, it will be ignored. method sorting only works for single-column sorting.
344
411
  # * :per_page
345
412
  # * :page
346
413
  def finder_options(options = {})
347
414
  search_conditions = all_conditions
348
- full_includes = (active_scaffold_references.blank? ? nil : active_scaffold_references)
349
415
 
350
416
  # create a general-use options array that's compatible with Rails finders
351
417
  finder_options = {
352
- :reorder => options[:sorting].try(:clause),
353
- :conditions => search_conditions,
354
- :joins => joins_for_finder,
355
- :outer_joins => active_scaffold_outer_joins,
356
- :preload => active_scaffold_preload,
357
- :includes => full_includes,
358
- :select => options[:select]
418
+ :reorder => options[:sorting].try(:clause, (grouped_columns_calculations if grouped_search?)),
419
+ :conditions => search_conditions
359
420
  }
360
- if Rails::VERSION::MAJOR >= 4
361
- finder_options.merge!(:references => active_scaffold_references)
421
+ if active_scaffold_config.mongoid?
422
+ finder_options[:includes] = [active_scaffold_references, active_scaffold_preload].compact.flatten.uniq.presence
423
+ else
424
+ finder_options.merge!(
425
+ :joins => joins_for_finder,
426
+ :left_joins => active_scaffold_outer_joins,
427
+ :preload => active_scaffold_preload,
428
+ :includes => active_scaffold_references.presence,
429
+ :references => active_scaffold_references.presence,
430
+ :select => options[:select]
431
+ )
362
432
  end
363
433
 
364
434
  finder_options.merge! custom_finder_options
@@ -366,8 +436,8 @@ module ActiveScaffold
366
436
  end
367
437
 
368
438
  def count_items(query, find_options = {}, count_includes = nil)
369
- count_includes ||= find_options[:includes] unless find_options[:conditions].blank?
370
- options = find_options.reject { |k, _| [:select, :reorder].include? k }
439
+ count_includes ||= find_options[:includes] if find_options[:conditions].present?
440
+ options = find_options.reject { |k, _| %i[select reorder order].include? k }
371
441
  # NOTE: we must use includes in the count query, because some conditions may reference other tables
372
442
  options[:includes] = count_includes
373
443
 
@@ -396,20 +466,20 @@ module ActiveScaffold
396
466
 
397
467
  query = append_to_query(query, find_options)
398
468
  # we build the paginator differently for method- and sql-based sorting
399
- if options[:sorting] && options[:sorting].sorts_by_method?
400
- pager = ::Paginator.new(count, options[:per_page]) do |offset, per_page|
401
- calculate_last_modified(query)
402
- sorted_collection = sort_collection_by_column(query.to_a, *options[:sorting].first)
403
- sorted_collection = sorted_collection.slice(offset, per_page) if options[:pagination]
404
- sorted_collection
405
- end
406
- else
407
- pager = ::Paginator.new(count, options[:per_page]) do |offset, per_page|
408
- query = append_to_query(query, :offset => offset, :limit => per_page) if options[:pagination]
409
- calculate_last_modified(query)
410
- query
411
- end
412
- end
469
+ pager = if options[:sorting] && options[:sorting].sorts_by_method?
470
+ ::Paginator.new(count, options[:per_page]) do |offset, per_page|
471
+ calculate_last_modified(query)
472
+ sorted_collection = sort_collection_by_column(query.to_a, *options[:sorting].first)
473
+ sorted_collection = sorted_collection.slice(offset, per_page) if options[:pagination]
474
+ sorted_collection
475
+ end
476
+ else
477
+ ::Paginator.new(count, options[:per_page]) do |offset, per_page|
478
+ query = append_to_query(query, :offset => offset, :limit => per_page) if options[:pagination]
479
+ calculate_last_modified(query)
480
+ query
481
+ end
482
+ end
413
483
  pager.page(options[:page])
414
484
  end
415
485
 
@@ -421,38 +491,34 @@ module ActiveScaffold
421
491
  def calculate_query
422
492
  conditions = all_conditions
423
493
  includes = active_scaffold_config.list.count_includes
424
- includes ||= active_scaffold_references unless conditions.blank?
425
- outer_joins = active_scaffold_outer_joins
426
- outer_joins += includes if includes
427
- primary_key = active_scaffold_config.model.primary_key
428
- subquery = append_to_query(beginning_of_chain, :conditions => conditions, :joins => joins_for_finder, :outer_joins => outer_joins, :select => active_scaffold_config.columns[primary_key].field)
429
- subquery = subquery.unscope(:order) if Rails::VERSION::MAJOR >= 4
494
+ includes ||= active_scaffold_references if conditions.present?
495
+ left_joins = active_scaffold_outer_joins
496
+ left_joins += includes if includes
497
+ primary_key = active_scaffold_config.primary_key
498
+ subquery = append_to_query(beginning_of_chain, :conditions => conditions, :joins => joins_for_finder, :left_joins => left_joins, :select => active_scaffold_config.columns[primary_key].field)
499
+ subquery = subquery.unscope(:order)
430
500
  active_scaffold_config.model.where(primary_key => subquery)
431
501
  end
432
502
 
433
503
  def append_to_query(relation, options)
434
- options.assert_valid_keys :where, :select, :having, :group, :reorder, :limit, :offset, :joins, :outer_joins, :includes, :lock, :readonly, :from, :conditions, :preload, (:references if Rails::VERSION::MAJOR >= 4)
504
+ options.assert_valid_keys :where, :select, :having, :group, :reorder, :order, :limit, :offset, :joins, :left_joins, :left_outer_joins, :includes, :lock, :readonly, :from, :conditions, :preload, :references
435
505
  relation = options.reject { |_, v| v.blank? }.inject(relation) do |rel, (k, v)|
436
506
  k == :conditions ? apply_conditions(rel, *v) : rel.send(k, v)
437
507
  end
438
- if options[:outer_joins].present?
439
- if Rails::VERSION::MAJOR >= 4
440
- relation.distinct_value = true
441
- else
442
- relation = relation.uniq
443
- end
508
+ if options[:left_outer_joins].present? || options[:left_joins].present?
509
+ relation.distinct_value = true
444
510
  end
445
511
  relation
446
512
  end
447
513
 
448
514
  def joins_for_finder
449
515
  case joins_for_collection
450
- when String
451
- [joins_for_collection]
452
- when Array
453
- joins_for_collection
454
- else
455
- []
516
+ when String
517
+ [joins_for_collection]
518
+ when Array
519
+ joins_for_collection
520
+ else
521
+ []
456
522
  end + active_scaffold_habtm_joins
457
523
  end
458
524
 
@@ -474,7 +540,7 @@ module ActiveScaffold
474
540
  value = '' if value.nil?
475
541
  value
476
542
  end
477
- collection.reverse! if order.downcase == 'desc'
543
+ collection.reverse! if order.casecmp('DESC').zero?
478
544
  collection
479
545
  end
480
546
  end