active_scaffold 4.1.6 → 4.2.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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.rdoc +27 -0
  3. data/README.md +6 -5
  4. data/app/assets/javascripts/jquery/active_scaffold.js +98 -47
  5. data/app/assets/javascripts/jquery/tiny_mce_bridge.js +15 -2
  6. data/app/assets/stylesheets/active_scaffold_images.scss +6 -0
  7. data/app/assets/stylesheets/{active_scaffold_layout.css → active_scaffold_layout.scss} +104 -4
  8. data/app/assets/stylesheets/tiny_mce_bridge.scss +11 -0
  9. data/app/views/active_scaffold_overrides/_base_form.html.erb +3 -2
  10. data/app/views/active_scaffold_overrides/_field_search.html.erb +2 -2
  11. data/app/views/active_scaffold_overrides/_form.html.erb +14 -4
  12. data/app/views/active_scaffold_overrides/_form_association.html.erb +1 -1
  13. data/app/views/active_scaffold_overrides/_form_association_record.html.erb +5 -11
  14. data/app/views/active_scaffold_overrides/_horizontal_subform.html.erb +1 -1
  15. data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +1 -3
  16. data/app/views/active_scaffold_overrides/_new_record.js.erb +3 -1
  17. data/app/views/active_scaffold_overrides/_refresh_list.js.erb +1 -1
  18. data/app/views/active_scaffold_overrides/_render_field.js.erb +67 -36
  19. data/app/views/active_scaffold_overrides/_update_field_on_create.js.erb +41 -6
  20. data/app/views/active_scaffold_overrides/action_links_menu.js.erb +1 -0
  21. data/config/locales/de.yml +9 -0
  22. data/config/locales/en.yml +11 -0
  23. data/config/locales/es.yml +8 -0
  24. data/config/locales/fr.yml +8 -0
  25. data/config/locales/hu.yml +8 -0
  26. data/config/locales/ja.yml +8 -0
  27. data/config/locales/ru.yml +8 -0
  28. data/lib/active_scaffold/actions/common_search.rb +2 -0
  29. data/lib/active_scaffold/actions/core.rb +47 -23
  30. data/lib/active_scaffold/actions/create.rb +2 -0
  31. data/lib/active_scaffold/actions/delete.rb +6 -0
  32. data/lib/active_scaffold/actions/field_search.rb +36 -11
  33. data/lib/active_scaffold/actions/list.rb +26 -8
  34. data/lib/active_scaffold/actions/mark.rb +6 -0
  35. data/lib/active_scaffold/actions/nested.rb +2 -0
  36. data/lib/active_scaffold/actions/search.rb +7 -0
  37. data/lib/active_scaffold/actions/show.rb +6 -0
  38. data/lib/active_scaffold/actions/subform.rb +2 -0
  39. data/lib/active_scaffold/actions/update.rb +8 -1
  40. data/lib/active_scaffold/active_record_permissions.rb +3 -3
  41. data/lib/active_scaffold/attribute_params.rb +35 -17
  42. data/lib/active_scaffold/bridges/active_storage/active_storage_bridge.rb +2 -0
  43. data/lib/active_scaffold/bridges/active_storage/active_storage_helpers.rb +10 -9
  44. data/lib/active_scaffold/bridges/active_storage/form_ui.rb +10 -3
  45. data/lib/active_scaffold/bridges/active_storage/list_ui.rb +2 -0
  46. data/lib/active_scaffold/bridges/active_storage.rb +2 -0
  47. data/lib/active_scaffold/bridges/ancestry/ancestry_bridge.rb +2 -0
  48. data/lib/active_scaffold/bridges/ancestry.rb +2 -0
  49. data/lib/active_scaffold/bridges/bitfields/bitfields_bridge.rb +2 -0
  50. data/lib/active_scaffold/bridges/bitfields/list_ui.rb +2 -0
  51. data/lib/active_scaffold/bridges/bitfields.rb +2 -0
  52. data/lib/active_scaffold/bridges/cancan/cancan_bridge.rb +9 -6
  53. data/lib/active_scaffold/bridges/cancan.rb +2 -0
  54. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge.rb +2 -0
  55. data/lib/active_scaffold/bridges/carrierwave/carrierwave_bridge_helpers.rb +2 -0
  56. data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +3 -1
  57. data/lib/active_scaffold/bridges/carrierwave/list_ui.rb +2 -0
  58. data/lib/active_scaffold/bridges/carrierwave.rb +2 -0
  59. data/lib/active_scaffold/bridges/chosen/helpers.rb +13 -4
  60. data/lib/active_scaffold/bridges/chosen.rb +2 -0
  61. data/lib/active_scaffold/bridges/country_select/country_select_bridge_helper.rb +2 -0
  62. data/lib/active_scaffold/bridges/country_select.rb +2 -0
  63. data/lib/active_scaffold/bridges/date_picker/ext.rb +6 -0
  64. data/lib/active_scaffold/bridges/date_picker/helper.rb +7 -3
  65. data/lib/active_scaffold/bridges/date_picker.rb +2 -0
  66. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge.rb +2 -0
  67. data/lib/active_scaffold/bridges/dragonfly/dragonfly_bridge_helpers.rb +2 -0
  68. data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +3 -1
  69. data/lib/active_scaffold/bridges/dragonfly/list_ui.rb +2 -0
  70. data/lib/active_scaffold/bridges/dragonfly.rb +2 -0
  71. data/lib/active_scaffold/bridges/file_column/as_file_column_bridge.rb +2 -0
  72. data/lib/active_scaffold/bridges/file_column/file_column_helpers.rb +10 -9
  73. data/lib/active_scaffold/bridges/file_column/form_ui.rb +2 -0
  74. data/lib/active_scaffold/bridges/file_column/list_ui.rb +2 -0
  75. data/lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb +2 -0
  76. data/lib/active_scaffold/bridges/file_column/test/mock_model.rb +2 -0
  77. data/lib/active_scaffold/bridges/file_column.rb +2 -0
  78. data/lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.rb +65 -0
  79. data/lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.treetop +31 -0
  80. data/lib/active_scaffold/bridges/logical_query_parser.rb +9 -0
  81. data/lib/active_scaffold/bridges/paper_trail/actions.rb +2 -0
  82. data/lib/active_scaffold/bridges/paper_trail/config.rb +2 -0
  83. data/lib/active_scaffold/bridges/paper_trail/helper.rb +2 -0
  84. data/lib/active_scaffold/bridges/paper_trail/paper_trail_bridge.rb +2 -0
  85. data/lib/active_scaffold/bridges/paper_trail.rb +2 -0
  86. data/lib/active_scaffold/bridges/paperclip/form_ui.rb +3 -1
  87. data/lib/active_scaffold/bridges/paperclip/list_ui.rb +2 -0
  88. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge.rb +2 -0
  89. data/lib/active_scaffold/bridges/paperclip/paperclip_bridge_helpers.rb +12 -12
  90. data/lib/active_scaffold/bridges/paperclip.rb +2 -0
  91. data/lib/active_scaffold/bridges/record_select/helpers.rb +19 -11
  92. data/lib/active_scaffold/bridges/record_select.rb +2 -0
  93. data/lib/active_scaffold/bridges/semantic_attributes/column.rb +2 -0
  94. data/lib/active_scaffold/bridges/semantic_attributes.rb +2 -0
  95. data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +3 -1
  96. data/lib/active_scaffold/bridges/tiny_mce.rb +6 -0
  97. data/lib/active_scaffold/bridges/usa_state_select/usa_state_select_helper.rb +2 -0
  98. data/lib/active_scaffold/bridges/usa_state_select.rb +2 -0
  99. data/lib/active_scaffold/bridges.rb +2 -0
  100. data/lib/active_scaffold/config/base.rb +12 -7
  101. data/lib/active_scaffold/config/core.rb +26 -23
  102. data/lib/active_scaffold/config/create.rb +2 -0
  103. data/lib/active_scaffold/config/delete.rb +2 -0
  104. data/lib/active_scaffold/config/field_search.rb +2 -0
  105. data/lib/active_scaffold/config/form.rb +11 -1
  106. data/lib/active_scaffold/config/list.rb +7 -7
  107. data/lib/active_scaffold/config/mark.rb +2 -0
  108. data/lib/active_scaffold/config/nested.rb +28 -0
  109. data/lib/active_scaffold/config/search.rb +2 -0
  110. data/lib/active_scaffold/config/show.rb +2 -0
  111. data/lib/active_scaffold/config/subform.rb +2 -0
  112. data/lib/active_scaffold/config/update.rb +3 -1
  113. data/lib/active_scaffold/configurable.rb +4 -2
  114. data/lib/active_scaffold/constraints.rb +2 -0
  115. data/lib/active_scaffold/core.rb +14 -4
  116. data/lib/active_scaffold/data_structures/action_columns.rb +3 -1
  117. data/lib/active_scaffold/data_structures/action_link.rb +10 -0
  118. data/lib/active_scaffold/data_structures/action_link_separator.rb +2 -0
  119. data/lib/active_scaffold/data_structures/action_links.rb +32 -21
  120. data/lib/active_scaffold/data_structures/actions.rb +4 -2
  121. data/lib/active_scaffold/data_structures/association/abstract.rb +4 -2
  122. data/lib/active_scaffold/data_structures/association/active_mongoid.rb +4 -2
  123. data/lib/active_scaffold/data_structures/association/active_record.rb +3 -9
  124. data/lib/active_scaffold/data_structures/association/mongoid.rb +4 -2
  125. data/lib/active_scaffold/data_structures/association.rb +2 -0
  126. data/lib/active_scaffold/data_structures/bridge.rb +3 -1
  127. data/lib/active_scaffold/data_structures/column.rb +37 -3
  128. data/lib/active_scaffold/data_structures/columns.rb +4 -2
  129. data/lib/active_scaffold/data_structures/filter.rb +3 -3
  130. data/lib/active_scaffold/data_structures/filter_option.rb +2 -0
  131. data/lib/active_scaffold/data_structures/filters.rb +3 -3
  132. data/lib/active_scaffold/data_structures/nested_info.rb +4 -2
  133. data/lib/active_scaffold/data_structures/set.rb +8 -10
  134. data/lib/active_scaffold/data_structures/sorting.rb +5 -7
  135. data/lib/active_scaffold/engine.rb +3 -4
  136. data/lib/active_scaffold/extensions/action_controller_rendering.rb +2 -0
  137. data/lib/active_scaffold/extensions/action_controller_rescueing.rb +2 -0
  138. data/lib/active_scaffold/extensions/action_view_rendering.rb +2 -0
  139. data/lib/active_scaffold/extensions/connection_adapter.rb +2 -0
  140. data/lib/active_scaffold/extensions/ice_nine.rb +2 -0
  141. data/lib/active_scaffold/extensions/localize.rb +2 -0
  142. data/lib/active_scaffold/extensions/name_option_for_datetime.rb +2 -0
  143. data/lib/active_scaffold/extensions/paginator_extensions.rb +3 -1
  144. data/lib/active_scaffold/extensions/routing_mapper.rb +2 -0
  145. data/lib/active_scaffold/extensions/to_label.rb +2 -0
  146. data/lib/active_scaffold/extensions/unsaved_associated.rb +10 -8
  147. data/lib/active_scaffold/extensions/unsaved_record.rb +2 -0
  148. data/lib/active_scaffold/finder.rb +57 -18
  149. data/lib/active_scaffold/helpers/action_link_helpers.rb +112 -37
  150. data/lib/active_scaffold/helpers/association_helpers.rb +4 -2
  151. data/lib/active_scaffold/helpers/controller_helpers.rb +2 -0
  152. data/lib/active_scaffold/helpers/filter_helpers.rb +11 -3
  153. data/lib/active_scaffold/helpers/form_column_helpers.rb +98 -71
  154. data/lib/active_scaffold/helpers/human_condition_helpers.rb +2 -0
  155. data/lib/active_scaffold/helpers/id_helpers.rb +2 -0
  156. data/lib/active_scaffold/helpers/list_column_helpers.rb +9 -5
  157. data/lib/active_scaffold/helpers/pagination_helpers.rb +3 -1
  158. data/lib/active_scaffold/helpers/search_column_helpers.rb +19 -9
  159. data/lib/active_scaffold/helpers/show_column_helpers.rb +4 -2
  160. data/lib/active_scaffold/helpers/tabs_helpers.rb +5 -3
  161. data/lib/active_scaffold/helpers/view_helpers.rb +3 -3
  162. data/lib/active_scaffold/marked_model.rb +6 -5
  163. data/lib/active_scaffold/orm_checks.rb +2 -0
  164. data/lib/active_scaffold/paginator.rb +4 -1
  165. data/lib/active_scaffold/registry.rb +2 -0
  166. data/lib/active_scaffold/responds_to_parent.rb +2 -0
  167. data/lib/active_scaffold/tableless.rb +23 -13
  168. data/lib/active_scaffold/version.rb +4 -2
  169. data/lib/active_scaffold.rb +10 -2
  170. data/lib/generators/active_scaffold/controller/USAGE +19 -0
  171. data/lib/generators/active_scaffold/controller/controller_generator.rb +29 -0
  172. data/lib/generators/active_scaffold/install/USAGE +2 -0
  173. data/lib/generators/active_scaffold/{install_generator.rb → install/install_generator.rb} +10 -6
  174. data/lib/generators/active_scaffold/resource/USAGE +29 -0
  175. data/lib/generators/active_scaffold/resource/resource_generator.rb +30 -0
  176. data/lib/tasks/brakeman.rake +2 -0
  177. data/shoulda_macros/macros.rb +2 -0
  178. metadata +19 -11
  179. data/lib/generators/active_scaffold/controller_generator.rb +0 -49
  180. data/lib/generators/active_scaffold/resource_generator.rb +0 -56
  181. /data/lib/generators/{templates → active_scaffold/controller/templates}/controller.rb +0 -0
  182. /data/lib/generators/{templates → active_scaffold/controller/templates}/helper.rb +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  module Association
3
5
  autoload :Abstract, 'active_scaffold/data_structures/association/abstract.rb'
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  class Bridge
3
5
  def self.install
4
- raise RunTimeError, "install not defined for bridge #{name}"
6
+ raise "install not defined for bridge #{name}"
5
7
  end
6
8
 
7
9
  def self.prepare; end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  class Column
3
5
  module ProxyableMethods
@@ -92,8 +94,16 @@ module ActiveScaffold::DataStructures
92
94
  # a collection of associations to pre-load when finding the records on a page
93
95
  attr_reader :includes
94
96
 
97
+ # a collection of associations to pre-load when the column is used as a subform,
98
+ # defaults to true, which means get associations from subform columns in the associated controller
99
+ # set to any other value to avoid checking the associated controller, false or nil to prevent preloading
100
+ attr_reader :subform_includes
101
+
95
102
  # a place to store dev's column specific options
96
103
  attr_writer :options
104
+
105
+ # define the fields to use with logical search
106
+ attr_accessor :logical_search
97
107
  end
98
108
 
99
109
  def inplace_edit=(value)
@@ -150,7 +160,7 @@ module ActiveScaffold::DataStructures
150
160
  # update_columns = :name
151
161
  # update_columns = [:name, :age]
152
162
  def update_columns=(column_names)
153
- @update_columns = Array(column_names)
163
+ @update_columns = column_names.is_a?(Array) ? column_names : [column_names]
154
164
  end
155
165
 
156
166
  # sorting on a column can be configured four ways:
@@ -169,7 +179,7 @@ module ActiveScaffold::DataStructures
169
179
 
170
180
  def sort
171
181
  initialize_sort if @sort == true
172
- @sort
182
+ @sort if @sort
173
183
  end
174
184
 
175
185
  def sortable?
@@ -181,6 +191,19 @@ module ActiveScaffold::DataStructures
181
191
  self.sort = options
182
192
  end
183
193
 
194
+ # a collection of associations to do left join when the list is sorted by this column
195
+ def sort_joins
196
+ @sort_joins || includes
197
+ end
198
+
199
+ def sort_joins=(value)
200
+ @sort_joins =
201
+ case value
202
+ when Array then value
203
+ else [value] # automatically convert to an array
204
+ end
205
+ end
206
+
184
207
  def associated_number?
185
208
  @associated_number
186
209
  end
@@ -283,6 +306,14 @@ module ActiveScaffold::DataStructures
283
306
  end
284
307
  end
285
308
 
309
+ def subform_includes=(value)
310
+ @subform_includes =
311
+ case value
312
+ when Array, TrueClass then value
313
+ else value ? [value] : value # not convert nil to [nil]
314
+ end
315
+ end
316
+
286
317
  # a collection of associations to do left join when this column is included on search
287
318
  def search_joins
288
319
  @search_joins || includes
@@ -315,7 +346,7 @@ module ActiveScaffold::DataStructures
315
346
  end
316
347
 
317
348
  def searchable?
318
- search_sql.present?
349
+ search_sql.present? || (logical_search.present? && ActiveScaffold::Finder::LOGICAL_COMPARATORS.present?)
319
350
  end
320
351
 
321
352
  def link
@@ -374,6 +405,7 @@ module ActiveScaffold::DataStructures
374
405
 
375
406
  include ActiveScaffold::Configurable
376
407
  include ActiveScaffold::OrmChecks
408
+
377
409
  NO_PARAMS = Set.new.freeze
378
410
  NO_OPTIONS = {}.freeze
379
411
 
@@ -512,6 +544,7 @@ module ActiveScaffold::DataStructures
512
544
  if delegated_association
513
545
  self.includes = includes ? [delegated_association.name => includes] : [delegated_association.name]
514
546
  end
547
+ self.subform_includes = true if association
515
548
 
516
549
  # default all the configurable variables
517
550
  self.css_class = ''
@@ -522,6 +555,7 @@ module ActiveScaffold::DataStructures
522
555
  validators_force_require_on.reject { |opt| opt == true }.flatten.presence
523
556
  self.sort = true
524
557
  self.search_sql = true
558
+ self.logical_search = [name] unless virtual? || association || tableless?
525
559
 
526
560
  @weight = estimate_weight
527
561
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  class Columns
3
5
  include Enumerable
@@ -19,13 +21,13 @@ module ActiveScaffold::DataStructures
19
21
  # This accessor is used by ActionColumns to create new Column objects without adding them to this set
20
22
  attr_reader :active_record_class
21
23
 
22
- def initialize(active_record_class, *args)
24
+ def initialize(active_record_class, *)
23
25
  @active_record_class = active_record_class
24
26
  @_inheritable = ::Set.new
25
27
  @set = {}
26
28
  @sorted = nil
27
29
 
28
- add(*args)
30
+ add(*)
29
31
  end
30
32
 
31
33
  # the way to add columns to the set. this is primarily useful for virtual columns.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  class Filter
3
5
  include Enumerable
@@ -52,9 +54,7 @@ module ActiveScaffold::DataStructures
52
54
  @options.each(&)
53
55
  end
54
56
 
55
- def empty?
56
- @options.empty?
57
- end
57
+ delegate :empty?, to: :@options
58
58
 
59
59
  def label(*)
60
60
  case @label
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  class FilterOption < ActionLink
3
5
  attr_reader :name, :filter_name
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  class Filters
3
5
  include Enumerable
@@ -37,9 +39,7 @@ module ActiveScaffold::DataStructures
37
39
  @set.each(&)
38
40
  end
39
41
 
40
- def empty?
41
- @set.empty?
42
- end
42
+ delegate :empty?, to: :@set
43
43
 
44
44
  # default filter type for all app filters, can be :links or :select
45
45
  cattr_accessor :default_type
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  class NestedInfo
3
5
  def self.get(model, params)
@@ -31,7 +33,7 @@ module ActiveScaffold::DataStructures
31
33
  false
32
34
  end
33
35
 
34
- def has_many? # rubocop:disable Naming/PredicateName
36
+ def has_many? # rubocop:disable Naming/PredicatePrefix
35
37
  false
36
38
  end
37
39
 
@@ -39,7 +41,7 @@ module ActiveScaffold::DataStructures
39
41
  false
40
42
  end
41
43
 
42
- def has_one? # rubocop:disable Naming/PredicateName
44
+ def has_one? # rubocop:disable Naming/PredicatePrefix
43
45
  false
44
46
  end
45
47
 
@@ -1,19 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  class Set
3
5
  include Enumerable
4
6
  include ActiveScaffold::Configurable
5
7
 
6
- def initialize(*args)
7
- set_values(*args)
8
+ def initialize(*)
9
+ set_values(*)
8
10
  end
9
11
 
10
12
  def initialize_dup(other)
11
13
  @set = other.set.dup
12
14
  end
13
15
 
14
- def set_values(*args)
16
+ def set_values(*)
15
17
  @set = []
16
- add(*args)
18
+ add(*)
17
19
  end
18
20
 
19
21
  # the way to add items to the set.
@@ -52,13 +54,9 @@ module ActiveScaffold::DataStructures
52
54
  end
53
55
 
54
56
  # returns the number of items in the set
55
- def length
56
- @set.length
57
- end
57
+ delegate :length, to: :@set
58
58
 
59
- def empty?
60
- @set.empty?
61
- end
59
+ delegate :empty?, to: :@set
62
60
 
63
61
  def +(other)
64
62
  self.class.new(@set, *other)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold::DataStructures
2
4
  # encapsulates the column sorting configuration for the List view
3
5
  class Sorting
@@ -22,7 +24,7 @@ module ActiveScaffold::DataStructures
22
24
  # fallback to setting primary key ordering
23
25
  setup_primary_key_order_clause
24
26
  model_scope = model.send(:build_default_scope)
25
- order_clause = model_scope.order_values.join(',') if model_scope
27
+ order_clause = model_scope.order_values.map { |ordering| ordering.respond_to?(:to_sql) ? ordering.to_sql : ordering }.join(',') if model_scope
26
28
  return unless order_clause
27
29
 
28
30
  # If an ORDER BY clause is found set default sorting according to it
@@ -115,13 +117,9 @@ module ActiveScaffold::DataStructures
115
117
  end
116
118
 
117
119
  # provides quick access to the first (and sometimes only) clause
118
- def first
119
- @clauses.first
120
- end
120
+ delegate :first, to: :@clauses
121
121
 
122
- def size
123
- @clauses.size
124
- end
122
+ delegate :size, to: :@clauses
125
123
 
126
124
  # builds an order-by clause
127
125
  def clause(grouped_columns = nil)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  class Engine < ::Rails::Engine
3
5
  initializer 'active_scaffold.action_controller' do
@@ -30,6 +32,7 @@ module ActiveScaffold
30
32
  require 'active_scaffold/extensions/unsaved_associated'
31
33
  require 'active_scaffold/extensions/unsaved_record'
32
34
  include ActiveScaffold::ActiveRecordPermissions::ModelUserAccess::Model
35
+
33
36
  ActiveRecord::Associations.module_eval do
34
37
  self::Association.include ActiveScaffold::Tableless::Association
35
38
  self::CollectionAssociation.include ActiveScaffold::Tableless::CollectionAssociation
@@ -48,10 +51,6 @@ module ActiveScaffold
48
51
  end
49
52
  end
50
53
 
51
- initializer 'active_scaffold.assets' do
52
- config.assets.precompile << 'active_scaffold_manifest.js' if Rails::VERSION::MAJOR < 7
53
- end
54
-
55
54
  initializer 'active_scaffold.extensions' do
56
55
  require 'active_scaffold/extensions/ice_nine'
57
56
  require 'active_scaffold/extensions/localize'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # wrap the action rendering for ActiveScaffold controllers
2
4
  module ActiveScaffold
3
5
  module ActionController # :nodoc:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionController # :nodoc:
2
4
  class Base
3
5
  # adding to ActionController::Base so it can overrided in ApplicationController
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module LookupContext
3
5
  attr_accessor :last_template
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module ConnectionAdapters
3
5
  module AbstractAdapter
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module IceNine
2
4
  class Freezer
3
5
  def self.find(name)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Object
2
4
  def as_(key, options = {})
3
5
  if key.present?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module DateSelectExtension
3
5
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_scaffold/paginator'
2
4
 
3
5
  module ActiveScaffold
@@ -27,7 +29,7 @@ module ActiveScaffold
27
29
  if @pager.infinite?
28
30
  items.to_a.empty?
29
31
  else
30
- @pager.count.zero?
32
+ @pager.count.zero? # rubocop:disable Style/CollectionQuerying -- @pager doesn't have none? method
31
33
  end
32
34
  end
33
35
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Routing
3
5
  ACTIVE_SCAFFOLD_CORE_ROUTING = {
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # the ever-useful to_label method
2
4
  class ActiveRecord::Base
3
5
  def to_label
@@ -1,16 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # save and validation support for associations.
2
4
  class ActiveRecord::Base
3
- def associated_valid?(path = [])
5
+ def associated_valid?(path = ::Set.new)
4
6
  return true if path.include?(self) # prevent recursion (if associated and parent are new records)
5
7
 
6
8
  path << self
7
9
  # using [].all? syntax to avoid a short-circuit
8
10
  # errors to associated record can be added by update_record_from_params when association fails to set and ActiveRecord::RecordNotSaved is raised
9
- with_unsaved_associated { |a| [a.keeping_errors { a.valid? }, a.associated_valid?(path)].all? }
11
+ with_unsaved_associated { |a| [a.keeping_errors { a.valid? }, a.associated_valid?(path)].all? }.all?
10
12
  end
11
13
 
12
- def save_associated
13
- with_unsaved_associated { |a| a.save && a.save_associated }
14
+ def save_associated # rubocop:disable Naming/PredicateMethod
15
+ with_unsaved_associated { |a| a.save && a.save_associated }.all?
14
16
  end
15
17
 
16
18
  def save_associated!
@@ -18,7 +20,7 @@ class ActiveRecord::Base
18
20
  end
19
21
 
20
22
  def no_errors_in_associated?
21
- with_unsaved_associated { |a| a.errors.count.zero? && a.no_errors_in_associated? }
23
+ with_unsaved_associated { |a| a.errors.none? && a.no_errors_in_associated? }.all?
22
24
  end
23
25
 
24
26
  protected
@@ -49,16 +51,16 @@ class ActiveRecord::Base
49
51
  # returns false if any yield returns false.
50
52
  # returns true otherwise, even when none of the associations have been instantiated. build wrapper methods accordingly.
51
53
  def with_unsaved_associated(&block)
52
- associations_for_update.map do |assoc|
54
+ associations_for_update.flat_map do |assoc|
53
55
  association_proxy = association(assoc.name)
54
56
  if association_proxy.target.present?
55
57
  records = association_proxy.target
56
58
  records = [records] unless records.is_a? Array # convert singular associations into collections for ease of use
57
59
  # must use select instead of find_all, which Rails overrides on association proxies for db access
58
- records.select { |r| r.unsaved? && !r.readonly? }.map(&block).all?
60
+ records.select { |r| r.unsaved? && !r.readonly? }.map(&block)
59
61
  else
60
62
  true
61
63
  end
62
- end.all?
64
+ end
63
65
  end
64
66
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # a simple (manual) unsaved? flag and method. at least it automatically reverts after a save!
2
4
  module ActiveScaffold::UnsavedRecord
3
5
  # acts like a dirty? flag, manually thrown during update_record_from_params.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveScaffold
2
4
  module Finder
3
5
  def self.like_operator
@@ -106,7 +108,7 @@ module ActiveScaffold
106
108
  args << session if method(column_method).arity == 4
107
109
  return send(:"condition_for_#{column.name}_column", *args)
108
110
  end
109
- return unless column.search_sql && value.present?
111
+ return unless column.searchable? && value.present?
110
112
 
111
113
  search_ui = column.search_ui || column.column_type
112
114
  begin
@@ -119,6 +121,7 @@ module ActiveScaffold
119
121
  condition_for_search_ui(column, value, like_pattern, search_ui)
120
122
  end
121
123
  return nil unless sql
124
+ return sql if sql.is_a? ::ActiveRecord::Relation
122
125
 
123
126
  where_values = []
124
127
  sql_conditions = []
@@ -134,8 +137,9 @@ module ActiveScaffold
134
137
  end
135
138
  [sql_conditions.join(' OR '), *where_values]
136
139
  rescue StandardError => e
137
- Rails.logger.error "#{e.class.name}: #{e.message} -- on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{name}"
138
- raise e
140
+ message = "on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{name}"
141
+ ActiveScaffold.log_exception(e, message)
142
+ raise e.class, "#{e.message} -- #{message}", e.backtrace
139
143
  end
140
144
  end
141
145
 
@@ -227,9 +231,40 @@ module ActiveScaffold
227
231
  ['(%<search_sql>s BETWEEN ? AND ?)', value[:from], value[:to]]
228
232
  elsif ActiveScaffold::Finder::NUMERIC_COMPARATORS.include?(value[:opt])
229
233
  ["%<search_sql>s #{value[:opt]} ?", value[:from]]
234
+ elsif ActiveScaffold::Finder::LOGICAL_COMPARATORS.include?(value[:opt])
235
+ operator =
236
+ case value[:opt]
237
+ when 'all_tokens' then 'AND'
238
+ when 'any_token' then 'OR'
239
+ end
240
+ parser = ActiveScaffold::Bridges::LogicalQueryParser::TokensGrammar::Parser.new(operator) if operator
241
+ [logical_search_condition(column, value[:from], parser)]
230
242
  end
231
243
  end
232
244
 
245
+ def logical_search_condition(column, search, parser = nil)
246
+ model = column.active_record_class
247
+ subquery = alias_query_for_same_table_exists(model.all) if column.logical_search.any?(Hash)
248
+ query = ::LogicalQueryParser.search(search, subquery || model, columns: column.logical_search, parser: parser)
249
+ if subquery
250
+ model.where(same_table_exists_subquery(query))
251
+ else
252
+ query
253
+ end
254
+ end
255
+
256
+ def alias_query_for_same_table_exists(query)
257
+ query.instance_variable_set(:@table, query.table.dup)
258
+ query.table.instance_variable_set(:@table_alias, "_#{query.table_name}_exists")
259
+ query
260
+ end
261
+
262
+ def same_table_exists_subquery(query)
263
+ alias_query_for_same_table_exists(query) unless query.table.table_alias
264
+ subquery = query.where(query.arel_table[query.primary_key].eq(query.table[query.primary_key]))
265
+ subquery.select(1).arel.exists
266
+ end
267
+
233
268
  def tables_for_translating_days_and_months(format)
234
269
  keys = {
235
270
  '%A' => 'date.day_names',
@@ -245,7 +280,8 @@ module ActiveScaffold
245
280
  end
246
281
 
247
282
  def translate_days_and_months(value, format)
248
- translated = ''
283
+ translated = +''
284
+ value = value.dup # ensure the string can be changed
249
285
  tables_for_translating_days_and_months(format).each do |table|
250
286
  regexp = Regexp.union(table.keys)
251
287
  index = value.index(regexp)
@@ -490,6 +526,7 @@ module ActiveScaffold
490
526
  doesnt_begin_with: 'not_?%',
491
527
  doesnt_end_with: 'not_%?'
492
528
  }.freeze
529
+ LOGICAL_COMPARATORS = [].freeze
493
530
  NULL_COMPARATORS = %w[null not_null].freeze
494
531
  DATE_COMPARATORS = %w[PAST FUTURE RANGE].freeze
495
532
  DATE_UNITS = %w[DAYS WEEKS MONTHS YEARS].freeze
@@ -502,12 +539,17 @@ module ActiveScaffold
502
539
 
503
540
  protected
504
541
 
505
- attr_writer :active_scaffold_conditions, :active_scaffold_preload, :active_scaffold_joins, :active_scaffold_outer_joins, :active_scaffold_references
542
+ attr_writer :active_scaffold_conditions, :active_scaffold_preload, :active_scaffold_joins,
543
+ :active_scaffold_outer_joins, :active_scaffold_references, :active_scaffold_relations
506
544
 
507
545
  def active_scaffold_conditions
508
546
  @active_scaffold_conditions ||= []
509
547
  end
510
548
 
549
+ def active_scaffold_relations
550
+ @active_scaffold_relations ||= []
551
+ end
552
+
511
553
  def active_scaffold_preload
512
554
  @active_scaffold_preload ||= []
513
555
  end
@@ -516,16 +558,6 @@ module ActiveScaffold
516
558
  @active_scaffold_joins ||= []
517
559
  end
518
560
 
519
- def active_scaffold_habtm_joins
520
- ActiveScaffold.deprecator.warn 'use active_scaffold_joins'
521
- active_scaffold_joins
522
- end
523
-
524
- def active_scaffold_habtm_joins=(value)
525
- ActiveScaffold.deprecator.warn 'use active_scaffold_joins='
526
- self.active_scaffold_joins = value
527
- end
528
-
529
561
  def active_scaffold_outer_joins
530
562
  @active_scaffold_outer_joins ||= []
531
563
  end
@@ -603,6 +635,7 @@ module ActiveScaffold
603
635
  preload: active_scaffold_preload,
604
636
  includes: active_scaffold_references.presence,
605
637
  references: active_scaffold_references.presence,
638
+ relations: active_scaffold_relations.presence,
606
639
  select: options[:select]
607
640
  )
608
641
  end
@@ -684,9 +717,15 @@ module ActiveScaffold
684
717
  def append_to_query(relation, options)
685
718
  options.assert_valid_keys :where, :select, :having, :group, :reorder, :order, :limit, :offset,
686
719
  :joins, :left_joins, :left_outer_joins, :includes, :lock, :readonly,
687
- :from, :conditions, :preload, :references
720
+ :from, :conditions, :preload, :references, :relations
688
721
  relation = options.compact_blank.inject(relation) do |rel, (k, v)|
689
- k == :conditions ? apply_conditions(rel, *v) : rel.send(k, v)
722
+ if k == :conditions
723
+ apply_conditions(rel, *v)
724
+ elsif k == :relations
725
+ v.reduce(rel, :merge)
726
+ else
727
+ rel.send(k, v)
728
+ end
690
729
  end
691
730
  relation.distinct_value = true if options[:left_outer_joins].present? || options[:left_joins].present?
692
731
  relation
@@ -717,7 +756,7 @@ module ActiveScaffold
717
756
  def sort_collection_by_column(collection, column, order)
718
757
  sorter = column.sort[:method]
719
758
  collection = collection.sort_by do |record|
720
- value = sorter.is_a?(Proc) ? record.instance_eval(&sorter) : record.instance_eval(sorter.to_s)
759
+ value = sorter.is_a?(Proc) ? record.instance_exec(record, &sorter) : record.instance_eval(sorter.to_s)
721
760
  value = '' if value.nil?
722
761
  value
723
762
  end