active_scaffold 4.2.2 → 4.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.rdoc +22 -0
- data/README.md +108 -7
- data/app/assets/javascripts/{jquery → active_scaffold}/active_scaffold.js +759 -762
- data/app/assets/javascripts/{jquery/date_picker_bridge.js.erb → active_scaffold/date_picker_bridge.js} +0 -3
- data/app/assets/javascripts/active_scaffold/load.js +102 -0
- data/app/assets/javascripts/active_scaffold.js.erb +3 -27
- data/app/assets/stylesheets/active_scaffold/_colours.scss +330 -0
- data/app/assets/stylesheets/active_scaffold/_images.scss +65 -0
- data/app/assets/stylesheets/{active_scaffold_layout.scss → active_scaffold/_layout.scss} +21 -1
- data/app/assets/stylesheets/active_scaffold/_variables.scss +194 -0
- data/app/assets/stylesheets/active_scaffold/core.scss +15 -0
- data/app/assets/stylesheets/active_scaffold.scss.erb +16 -0
- data/app/views/active_scaffold_overrides/_field_search_columns.html.erb +8 -0
- data/app/views/active_scaffold_overrides/_form.html.erb +8 -0
- data/app/views/active_scaffold_overrides/_form_association.html.erb +3 -1
- data/app/views/active_scaffold_overrides/_form_association_record.html.erb +4 -2
- data/app/views/active_scaffold_overrides/_show_association.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_show_association_horizontal.html.erb +2 -2
- data/app/views/active_scaffold_overrides/_show_association_vertical.html.erb +1 -1
- data/app/views/active_scaffold_overrides/edit_associated.js.erb +10 -8
- data/lib/active_scaffold/actions/core.rb +34 -3
- data/lib/active_scaffold/assets/css_deps_generator.rb +42 -0
- data/lib/active_scaffold/assets/jquery_ui_manifest.rb +77 -0
- data/lib/active_scaffold/assets/jquery_ui_theme_generator.rb +102 -0
- data/lib/active_scaffold/assets.rb +109 -0
- data/lib/active_scaffold/attribute_params.rb +11 -2
- data/lib/active_scaffold/bridges/active_storage/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/carrierwave/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/chosen.rb +1 -1
- data/lib/active_scaffold/bridges/dragonfly/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/file_column/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/logical_query_parser/keyword_query_parser.rb +23 -0
- data/lib/active_scaffold/bridges/logical_query_parser.rb +3 -2
- data/lib/active_scaffold/bridges/paperclip/form_ui.rb +1 -1
- data/lib/active_scaffold/bridges/record_select/helpers.rb +1 -1
- data/lib/active_scaffold/bridges/tiny_mce/helpers.rb +1 -0
- data/lib/active_scaffold/bridges/tiny_mce.rb +6 -1
- data/lib/active_scaffold/bridges.rb +7 -0
- data/lib/active_scaffold/config/core.rb +7 -3
- data/lib/active_scaffold/constraints.rb +37 -32
- data/lib/active_scaffold/data_structures/action_columns.rb +66 -0
- data/lib/active_scaffold/data_structures/bridge.rb +2 -0
- data/lib/active_scaffold/data_structures/column.rb +4 -1
- data/lib/active_scaffold/engine.rb +40 -0
- data/lib/active_scaffold/finder.rb +10 -7
- data/lib/active_scaffold/helpers/assets_helpers.rb +39 -0
- data/lib/active_scaffold/helpers/controller_helpers.rb +1 -1
- data/lib/active_scaffold/helpers/form_column_helpers.rb +57 -532
- data/lib/active_scaffold/helpers/form_ui_helpers.rb +530 -0
- data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -0
- data/lib/active_scaffold/helpers/list_column_helpers.rb +31 -11
- data/lib/active_scaffold/helpers/search_column_helpers.rb +7 -14
- data/lib/active_scaffold/helpers/show_column_helpers.rb +4 -2
- data/lib/active_scaffold/helpers/view_helpers.rb +12 -0
- data/lib/active_scaffold/railties/tasks.rake +10 -0
- data/lib/active_scaffold/testing/assert_embedded_load.rb +33 -0
- data/lib/active_scaffold/version.rb +2 -2
- data/lib/active_scaffold.rb +7 -2
- data/lib/tasks/active_scaffold/assets.rake +42 -0
- data/lib/tasks/bundle.rake +25 -0
- data/vendor/assets/stylesheets/{jquery-ui-theme.css.erb → jquery-ui-theme.css} +17 -17
- metadata +33 -20
- data/app/assets/stylesheets/active_scaffold.scss +0 -424
- data/app/assets/stylesheets/active_scaffold_extensions.css.erb +0 -2
- data/app/assets/stylesheets/active_scaffold_images.scss +0 -65
- data/app/assets/stylesheets/active_scaffold_jquery_ui.css.erb +0 -13
- /data/app/assets/javascripts/{jquery → active_scaffold}/active_scaffold_chosen.js +0 -0
- /data/app/assets/javascripts/{jquery → active_scaffold}/draggable_lists.js +0 -0
- /data/app/assets/javascripts/{jquery → active_scaffold}/jquery.editinplace.js +0 -0
- /data/app/assets/javascripts/{jquery → active_scaffold}/tiny_mce_bridge.js +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveScaffold
|
|
4
|
+
module Assets
|
|
5
|
+
autoload :CssDepsGenerator, 'active_scaffold/assets/css_deps_generator'
|
|
6
|
+
autoload :JqueryUiManifest, 'active_scaffold/assets/jquery_ui_manifest'
|
|
7
|
+
autoload :JqueryUiThemeGenerator, 'active_scaffold/assets/jquery_ui_theme_generator'
|
|
8
|
+
|
|
9
|
+
CORE_STYLESHEETS = ['active_scaffold/core'].freeze
|
|
10
|
+
CORE_JAVASCRIPTS = ['jquery.ba-throttle-debounce', 'jquery.visible.min', 'active_scaffold/jquery.editinplace', 'active_scaffold/active_scaffold'].freeze
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
# Returns array of stylesheet paths based on load mode
|
|
14
|
+
def active_scaffold_stylesheets(load = :all)
|
|
15
|
+
case load
|
|
16
|
+
when :all
|
|
17
|
+
CORE_STYLESHEETS + active_scaffold_stylesheets(:deps)
|
|
18
|
+
when :deps
|
|
19
|
+
jquery_ui_stylesheets + ActiveScaffold.stylesheets + bridge_stylesheets
|
|
20
|
+
when :core
|
|
21
|
+
CORE_STYLESHEETS
|
|
22
|
+
when :plugins
|
|
23
|
+
ActiveScaffold.stylesheets
|
|
24
|
+
when :bridges
|
|
25
|
+
bridge_stylesheets
|
|
26
|
+
when :jquery_ui
|
|
27
|
+
jquery_ui_stylesheets
|
|
28
|
+
else
|
|
29
|
+
raise ArgumentError, "Unknown load mode: #{load.inspect}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns array of javascript paths based on load mode
|
|
34
|
+
def active_scaffold_javascripts(load = :all)
|
|
35
|
+
case load
|
|
36
|
+
when :all
|
|
37
|
+
jquery_ui_javascripts + CORE_JAVASCRIPTS + ActiveScaffold.javascripts + bridge_javascripts
|
|
38
|
+
when :deps
|
|
39
|
+
jquery_ui_javascripts + ActiveScaffold.javascripts + bridge_javascripts
|
|
40
|
+
when :core
|
|
41
|
+
CORE_JAVASCRIPTS
|
|
42
|
+
when :plugins
|
|
43
|
+
ActiveScaffold.javascripts
|
|
44
|
+
when :bridges
|
|
45
|
+
bridge_javascripts
|
|
46
|
+
when :jquery_ui
|
|
47
|
+
jquery_ui_javascripts
|
|
48
|
+
else
|
|
49
|
+
raise ArgumentError, "Unknown load mode: #{load.inspect}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def active_scaffold_js_code(load = :all)
|
|
54
|
+
code =
|
|
55
|
+
case load
|
|
56
|
+
when :all
|
|
57
|
+
[jquery_ui_js_code, active_scaffold_js_config]
|
|
58
|
+
when :deps, :jquery_ui
|
|
59
|
+
[jquery_ui_js_code]
|
|
60
|
+
when :core
|
|
61
|
+
[active_scaffold_js_config]
|
|
62
|
+
when :plugins, :bridges
|
|
63
|
+
[]
|
|
64
|
+
else
|
|
65
|
+
raise ArgumentError, "Unknown load mode: #{load.inspect}"
|
|
66
|
+
end
|
|
67
|
+
code.join("\n")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def jquery_ui_stylesheets
|
|
73
|
+
return [] unless ActiveScaffold.jquery_ui_included?
|
|
74
|
+
|
|
75
|
+
sheets = []
|
|
76
|
+
if Object.const_defined?(:Jquery) && Jquery.const_defined?(:Ui)
|
|
77
|
+
sheets << 'active_scaffold/jquery-ui/theme' if defined?(Propshaft)
|
|
78
|
+
sheets << 'jquery-ui/datepicker'
|
|
79
|
+
end
|
|
80
|
+
sheets << 'jquery-ui-theme' if ActiveScaffold.jquery_ui_included?
|
|
81
|
+
sheets
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def bridge_stylesheets
|
|
85
|
+
ActiveScaffold::Bridges.all_stylesheets
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def jquery_ui_javascripts
|
|
89
|
+
return [] unless ActiveScaffold.jquery_ui_included?
|
|
90
|
+
|
|
91
|
+
# For Propshaft, we need all jQuery UI dependencies
|
|
92
|
+
ActiveScaffold::Assets::JqueryUiManifest.all_dependencies +
|
|
93
|
+
['jquery-ui-timepicker-addon', 'active_scaffold/date_picker_bridge', 'active_scaffold/draggable_lists']
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def jquery_ui_js_code
|
|
97
|
+
ActiveScaffold::Bridges[:date_picker].localization if ActiveScaffold.jquery_ui_included?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def active_scaffold_js_config
|
|
101
|
+
"ActiveScaffold.config = #{ActiveScaffold.js_config.to_json};"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def bridge_javascripts
|
|
105
|
+
ActiveScaffold::Bridges.all_javascripts
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -83,7 +83,7 @@ module ActiveScaffold
|
|
|
83
83
|
message = "on the ActiveScaffold column = :#{column.name} for #{parent_record.inspect} " \
|
|
84
84
|
"(value from params #{attributes[column.name].inspect})"
|
|
85
85
|
ActiveScaffold.log_exception(e, message)
|
|
86
|
-
raise e
|
|
86
|
+
raise e, "#{e.message} -- #{message}", e.backtrace
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
@@ -173,6 +173,9 @@ module ActiveScaffold
|
|
|
173
173
|
# convert empty strings into nil. this works better with 'null => true' columns (and validations),
|
|
174
174
|
# for 'null => false' columns is just converted to default value from column
|
|
175
175
|
column.default_for_empty_value
|
|
176
|
+
elsif value.is_a?(Array)
|
|
177
|
+
# for select_multiple or checkboxes in DB columns, needs to remove blank string used to clear the column
|
|
178
|
+
value.compact_blank!
|
|
176
179
|
else
|
|
177
180
|
value
|
|
178
181
|
end
|
|
@@ -224,12 +227,18 @@ module ActiveScaffold
|
|
|
224
227
|
@new_records[record.class][id] = record
|
|
225
228
|
end
|
|
226
229
|
|
|
230
|
+
def subform_columns(column, klass)
|
|
231
|
+
subform_cfg = active_scaffold_config_for(klass).subform
|
|
232
|
+
columns = (column.form_ui_options || column.options)[:subform_columns]
|
|
233
|
+
columns ? subform_cfg.build_action_columns(columns) : subform_cfg.columns
|
|
234
|
+
end
|
|
235
|
+
|
|
227
236
|
def manage_nested_record_from_params(parent_record, column, attributes, avoid_changes = false)
|
|
228
237
|
return nil unless avoid_changes || build_record_from_params?(attributes, column, parent_record)
|
|
229
238
|
|
|
230
239
|
record = find_or_create_for_params(attributes, column, parent_record, avoid_changes)
|
|
231
240
|
if record
|
|
232
|
-
record_columns =
|
|
241
|
+
record_columns = subform_columns(column, record.class)
|
|
233
242
|
prev_constraints = record_columns.constraint_columns
|
|
234
243
|
record_columns.constraint_columns = [column.association.reverse].compact
|
|
235
244
|
update_record_from_params(record, record_columns, attributes, avoid_changes)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module ActiveScaffold
|
|
4
4
|
module Helpers
|
|
5
5
|
# Helpers that assist with the rendering of a Form Column
|
|
6
|
-
module
|
|
6
|
+
module FormUiHelpers
|
|
7
7
|
def active_scaffold_input_active_storage_has_one(column, options, ui_options: column.options)
|
|
8
8
|
record = options[:object]
|
|
9
9
|
active_storage = record.send(column.name.to_s)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module ActiveScaffold
|
|
4
4
|
module Helpers
|
|
5
5
|
# Helpers that assist with the rendering of a Form Column
|
|
6
|
-
module
|
|
6
|
+
module FormUiHelpers
|
|
7
7
|
def active_scaffold_input_file_column(column, options, ui_options: column.options)
|
|
8
8
|
record = options[:object]
|
|
9
9
|
if record.send(column.name)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'method_source'
|
|
4
|
+
|
|
5
|
+
class ActiveScaffold::Bridges::LogicalQueryParser
|
|
6
|
+
class KeywordQueryParser
|
|
7
|
+
LogicalQueryParser.singleton_methods.each do |method_name|
|
|
8
|
+
method = LogicalQueryParser.method(method_name)
|
|
9
|
+
define_method(method_name, &method)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Copy search method from LogicalQueryParser
|
|
13
|
+
class_eval(LogicalQueryParser.method(:search).source, __FILE__, __LINE__)
|
|
14
|
+
|
|
15
|
+
def initialize(operator)
|
|
16
|
+
@operator = operator
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def new
|
|
20
|
+
ActiveScaffold::Bridges::LogicalQueryParser::TokensGrammar::Parser.new(@operator)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class ActiveScaffold::Bridges::LogicalQueryParser < ActiveScaffold::DataStructures::Bridge
|
|
4
|
+
autoload :TokensGrammar, 'active_scaffold/bridges/logical_query_parser/tokens_grammar'
|
|
5
|
+
autoload :KeywordQueryParser, 'active_scaffold/bridges/logical_query_parser/keyword_query_parser'
|
|
6
|
+
|
|
4
7
|
def self.install
|
|
5
|
-
require File.join(File.dirname(__FILE__), 'logical_query_parser/tokens_grammar')
|
|
6
|
-
ActiveScaffold::Finder.send(:remove_const, :LOGICAL_COMPARATORS)
|
|
7
8
|
ActiveScaffold::Finder.const_set :LOGICAL_COMPARATORS, %w[all_tokens any_token logical].freeze
|
|
8
9
|
end
|
|
9
10
|
end
|
|
@@ -87,7 +87,7 @@ class ActiveScaffold::Bridges::RecordSelect
|
|
|
87
87
|
rescue StandardError => e
|
|
88
88
|
message = "Sorry, we are not that smart yet. Attempted to restore search values to search fields :#{column.name} in #{controller.class}"
|
|
89
89
|
Rails.logger.error "#{e.class.name}: #{e.message} -- #{message}"
|
|
90
|
-
raise e
|
|
90
|
+
raise e, "#{e.message} -- #{message}", e.backtrace
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
end
|
|
@@ -11,7 +11,12 @@ class ActiveScaffold::Bridges::TinyMce < ActiveScaffold::DataStructures::Bridge
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def self.javascripts
|
|
14
|
-
['tinymce', '
|
|
14
|
+
lib = defined?(Sprockets) ? ['tinymce'] : ['tinymce/tinymce', 'tinymce/rails']
|
|
15
|
+
lib << 'active_scaffold/tiny_mce_bridge'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.javascript_tags
|
|
19
|
+
:tinymce_preinit unless defined?(Sprockets)
|
|
15
20
|
end
|
|
16
21
|
|
|
17
22
|
def self.stylesheets
|
|
@@ -64,6 +64,13 @@ module ActiveScaffold
|
|
|
64
64
|
bridge.javascripts if bridge&.install?
|
|
65
65
|
end.flatten
|
|
66
66
|
end
|
|
67
|
+
|
|
68
|
+
def self.all_javascript_tags
|
|
69
|
+
bridges.keys.filter_map do |bridge_name|
|
|
70
|
+
bridge = self[bridge_name]
|
|
71
|
+
bridge.javascript_tags if bridge&.install?
|
|
72
|
+
end.flatten
|
|
73
|
+
end
|
|
67
74
|
end
|
|
68
75
|
end
|
|
69
76
|
|
|
@@ -261,7 +261,8 @@ module ActiveScaffold::Config
|
|
|
261
261
|
|
|
262
262
|
underscored_name = action_name.to_s.underscore.to_sym
|
|
263
263
|
unless @actions.include? underscored_name
|
|
264
|
-
raise ArgumentError,
|
|
264
|
+
raise ArgumentError,
|
|
265
|
+
"#{action_name.to_s.camelcase} is not enabled for #{model.name}. Please enable it or remove any references in your configuration (e.g. config.#{underscored_name}.columns = [...])."
|
|
265
266
|
end
|
|
266
267
|
|
|
267
268
|
@action_configs ||= {}
|
|
@@ -283,7 +284,9 @@ module ActiveScaffold::Config
|
|
|
283
284
|
end
|
|
284
285
|
|
|
285
286
|
def self.config_class?(name)
|
|
286
|
-
ActiveScaffold::Config.const_defined? name.to_s.camelcase
|
|
287
|
+
ActiveScaffold::Config.const_defined? name.to_s.camelcase, false
|
|
288
|
+
rescue NameError
|
|
289
|
+
false
|
|
287
290
|
end
|
|
288
291
|
|
|
289
292
|
def self.respond_to_missing?(name, include_all = false)
|
|
@@ -331,7 +334,8 @@ module ActiveScaffold::Config
|
|
|
331
334
|
end
|
|
332
335
|
|
|
333
336
|
def method_missing(name, *args)
|
|
334
|
-
|
|
337
|
+
# check if it's an action instead of checking if action is enabled, so we get a better error message when the action is not setup in the controller
|
|
338
|
+
value = args.empty? && @conf.class.config_class?(name) ? @conf.send(name) : super
|
|
335
339
|
value.is_a?(Base) ? action_user_settings(value) : value
|
|
336
340
|
end
|
|
337
341
|
|
|
@@ -155,6 +155,41 @@ module ActiveScaffold
|
|
|
155
155
|
"Malformed constraint `#{klass}##{column_name}'. If it's a legitimate column, and you are using a nested scaffold, please specify or double-check the reverse association name."
|
|
156
156
|
end
|
|
157
157
|
|
|
158
|
+
def apply_constraint_on_association(record, association, value, allow_autosave: false)
|
|
159
|
+
if association.through_singular? && association.source_reflection.reverse
|
|
160
|
+
create_on_through_singular(record, association, association.klass.find(value))
|
|
161
|
+
elsif association.collection?
|
|
162
|
+
record.send(association.name).send(:<<, association.klass.find(value)) unless association.nested?
|
|
163
|
+
elsif association.polymorphic?
|
|
164
|
+
apply_constraint_on_polymorphic_association(record, association, value)
|
|
165
|
+
elsif !association.source_reflection&.through? && # regular singular association, or one-level through association
|
|
166
|
+
!value.is_a?(Array)
|
|
167
|
+
record.send(:"#{association.name}=", association.klass.find(value))
|
|
168
|
+
|
|
169
|
+
# setting the belongs_to side of a has_one isn't safe. if the has_one was already
|
|
170
|
+
# specified, rails won't automatically clear out the previous associated record.
|
|
171
|
+
#
|
|
172
|
+
# note that we can't take the extra step to correct this unless we're permitted to
|
|
173
|
+
# run operations where activerecord auto-saves the object.
|
|
174
|
+
reverse = association.reverse_association
|
|
175
|
+
if reverse&.singular? && !reverse.belongs_to? && allow_autosave
|
|
176
|
+
record.send(association.name).send(:"#{reverse.name}=", record)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def apply_constraint_on_polymorphic_association(record, association, value)
|
|
182
|
+
unless value.is_a?(Array) && value.size >= 2
|
|
183
|
+
raise ActiveScaffold::MalformedConstraint, polymorphic_constraint_error(association), caller
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
if value.size == 2
|
|
187
|
+
record.send(:"#{association.name}=", value[0].constantize.find(value[1]))
|
|
188
|
+
else
|
|
189
|
+
record.send(:"#{association.foreign_type}=", value[0])
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
158
193
|
# Applies constraints to the given record.
|
|
159
194
|
#
|
|
160
195
|
# Searches through the known columns for association columns. If the given constraint is an association,
|
|
@@ -163,42 +198,12 @@ module ActiveScaffold
|
|
|
163
198
|
#
|
|
164
199
|
# For some operations ActiveRecord will automatically update the database. That's not always ok.
|
|
165
200
|
# If it *is* ok (e.g. you're in a transaction), then set :allow_autosave to true.
|
|
166
|
-
def apply_constraints_to_record(record,
|
|
167
|
-
options[:allow_autosave] = false if options[:allow_autosave].nil?
|
|
168
|
-
constraints = options[:constraints] || active_scaffold_constraints
|
|
169
|
-
|
|
201
|
+
def apply_constraints_to_record(record, allow_autosave: false, constraints: active_scaffold_constraints)
|
|
170
202
|
config = record.is_a?(active_scaffold_config.model) ? active_scaffold_config : active_scaffold_config_for(record.class)
|
|
171
203
|
constraints.each do |k, v|
|
|
172
204
|
column = config.columns[k]
|
|
173
205
|
if column&.association
|
|
174
|
-
|
|
175
|
-
create_on_through_singular(record, column.association, column.association.klass.find(v))
|
|
176
|
-
elsif column.association.collection?
|
|
177
|
-
record.send(k.to_s).send(:<<, column.association.klass.find(v)) unless column.association.nested?
|
|
178
|
-
elsif column.association.polymorphic?
|
|
179
|
-
unless v.is_a?(Array) && v.size >= 2
|
|
180
|
-
raise ActiveScaffold::MalformedConstraint, polymorphic_constraint_error(column.association), caller
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
if v.size == 2
|
|
184
|
-
record.send(:"#{k}=", v[0].constantize.find(v[1]))
|
|
185
|
-
else
|
|
186
|
-
record.send(:"#{column.association.foreign_type}=", v[0])
|
|
187
|
-
end
|
|
188
|
-
elsif !column.association.source_reflection&.through? && # regular singular association, or one-level through association
|
|
189
|
-
!v.is_a?(Array)
|
|
190
|
-
record.send(:"#{k}=", column.association.klass.find(v))
|
|
191
|
-
|
|
192
|
-
# setting the belongs_to side of a has_one isn't safe. if the has_one was already
|
|
193
|
-
# specified, rails won't automatically clear out the previous associated record.
|
|
194
|
-
#
|
|
195
|
-
# note that we can't take the extra step to correct this unless we're permitted to
|
|
196
|
-
# run operations where activerecord auto-saves the object.
|
|
197
|
-
reverse = column.association.reverse_association
|
|
198
|
-
if reverse&.singular? && !reverse.belongs_to? && options[:allow_autosave]
|
|
199
|
-
record.send(k).send(:"#{reverse.name}=", record)
|
|
200
|
-
end
|
|
201
|
-
end
|
|
206
|
+
apply_constraint_on_association(record, column.association, v, allow_autosave: allow_autosave)
|
|
202
207
|
else
|
|
203
208
|
record.send(:"#{k}=", v)
|
|
204
209
|
end
|
|
@@ -41,8 +41,74 @@ module ActiveScaffold::DataStructures
|
|
|
41
41
|
# Whether this column set is collapsed by default in contexts where collapsing is supported
|
|
42
42
|
attr_accessor :collapsed
|
|
43
43
|
|
|
44
|
+
# Layout mode: nil/:single for flat columns, :multiple for column groups
|
|
45
|
+
attr_reader :layout
|
|
46
|
+
|
|
47
|
+
def layout=(value)
|
|
48
|
+
case value
|
|
49
|
+
when :multiple
|
|
50
|
+
if @layout != :multiple && !@set.empty?
|
|
51
|
+
group = ActiveScaffold::DataStructures::ActionColumns.new(*@set)
|
|
52
|
+
group.action = action
|
|
53
|
+
@set = [group]
|
|
54
|
+
end
|
|
55
|
+
when :single, nil
|
|
56
|
+
if @layout == :multiple
|
|
57
|
+
merged = []
|
|
58
|
+
@set.each do |item|
|
|
59
|
+
if item.is_a?(ActiveScaffold::DataStructures::ActionColumns)
|
|
60
|
+
item.each { |col| merged << col }
|
|
61
|
+
else
|
|
62
|
+
merged << item
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
@set = merged
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
@layout = value
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def set_values(*)
|
|
72
|
+
@layout = nil
|
|
73
|
+
super
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def add(*)
|
|
77
|
+
if @layout == :multiple
|
|
78
|
+
group = ActiveScaffold::DataStructures::ActionColumns.new(*)
|
|
79
|
+
group.action = action
|
|
80
|
+
@set << group
|
|
81
|
+
else
|
|
82
|
+
super
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
alias << add
|
|
86
|
+
|
|
87
|
+
def [](arg)
|
|
88
|
+
if @layout == :multiple && arg.is_a?(Integer)
|
|
89
|
+
@set[arg]
|
|
90
|
+
else
|
|
91
|
+
find_by_name(arg)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def []=(index, val)
|
|
96
|
+
raise '[]= is only supported when layout is :multiple' unless @layout == :multiple
|
|
97
|
+
raise ArgumentError, "index #{index} is out of range, max is #{@set.length}" if index > @set.length
|
|
98
|
+
|
|
99
|
+
if index == @set.length
|
|
100
|
+
group = ActiveScaffold::DataStructures::ActionColumns.new(*val)
|
|
101
|
+
group.action = action
|
|
102
|
+
@set << group
|
|
103
|
+
else
|
|
104
|
+
@set[index].set_values(*val)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
44
108
|
# nests a subgroup in the column set
|
|
45
109
|
def add_subgroup(label, &)
|
|
110
|
+
raise 'add_subgroup is not supported when layout is :multiple' if @layout == :multiple
|
|
111
|
+
|
|
46
112
|
columns = ActiveScaffold::DataStructures::ActionColumns.new
|
|
47
113
|
columns.label = label
|
|
48
114
|
columns.action = action
|
|
@@ -62,6 +62,9 @@ module ActiveScaffold::DataStructures
|
|
|
62
62
|
# config.columns[:my_column].hide_form_column_if = :hide_tractor_fields?
|
|
63
63
|
attr_accessor :hide_form_column_if
|
|
64
64
|
|
|
65
|
+
# works like hide_form_column_if, but the form will send an empty value to clear the column when is hidden
|
|
66
|
+
attr_accessor :clear_form_column_if
|
|
67
|
+
|
|
65
68
|
# text to display when the column is empty, defaults nil, so list.empty_field_text is used
|
|
66
69
|
attr_accessor :empty_field_text
|
|
67
70
|
|
|
@@ -346,7 +349,7 @@ module ActiveScaffold::DataStructures
|
|
|
346
349
|
end
|
|
347
350
|
|
|
348
351
|
def searchable?
|
|
349
|
-
search_sql.present? || (logical_search.present? && ActiveScaffold::Finder
|
|
352
|
+
search_sql.present? || (logical_search.present? && ActiveScaffold::Finder.logical_comparators.present?)
|
|
350
353
|
end
|
|
351
354
|
|
|
352
355
|
def link
|
|
@@ -56,5 +56,45 @@ module ActiveScaffold
|
|
|
56
56
|
require 'active_scaffold/extensions/localize'
|
|
57
57
|
require 'active_scaffold/extensions/paginator_extensions'
|
|
58
58
|
end
|
|
59
|
+
|
|
60
|
+
initializer 'active_scaffold.assets' do |app|
|
|
61
|
+
if defined?(::Sprockets)
|
|
62
|
+
# Tell sprockets where your assets are located
|
|
63
|
+
app.config.assets.precompile += %w[active_scaffold/manifest.js]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
initializer 'active_scaffold.testing' do
|
|
68
|
+
ActiveSupport.on_load(:action_dispatch_integration_test) do
|
|
69
|
+
include ActiveScaffold::Testing::AssertEmbeddedLoad
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
ActiveSupport.on_load(:action_controller_test_case) do
|
|
73
|
+
include ActiveScaffold::Testing::AssertEmbeddedLoad
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if defined?(RSpec)
|
|
77
|
+
RSpec.configure do |config|
|
|
78
|
+
config.include ActiveScaffold::Testing::AssertEmbeddedLoad, type: :request
|
|
79
|
+
config.include ActiveScaffold::Testing::AssertEmbeddedLoad, type: :controller
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
config.after_initialize do
|
|
85
|
+
if defined?(Propshaft)
|
|
86
|
+
ActiveScaffold::Assets::JqueryUiThemeGenerator.generate_if_needed if ActiveScaffold.jquery_ui_included?
|
|
87
|
+
ActiveScaffold::Assets::CssDepsGenerator.generate!
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Make rake tasks available to the host app
|
|
92
|
+
rake_tasks do
|
|
93
|
+
# Load all rake tasks
|
|
94
|
+
Dir[File.expand_path('../tasks/active_scaffold/**/*.rake', __dir__)].each { |f| load f }
|
|
95
|
+
|
|
96
|
+
# Load the precompile hook
|
|
97
|
+
load File.expand_path('railties/tasks.rake', __dir__)
|
|
98
|
+
end
|
|
59
99
|
end
|
|
60
100
|
end
|
|
@@ -6,6 +6,10 @@ module ActiveScaffold
|
|
|
6
6
|
@@like_operator ||= ::ActiveRecord::Base.connection.adapter_name.in?(%w[PostgreSQL PostGIS]) ? 'ILIKE' : 'LIKE'
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
def self.logical_comparators
|
|
10
|
+
ActiveScaffold::Finder::LOGICAL_COMPARATORS if ActiveScaffold::Finder.const_defined? :LOGICAL_COMPARATORS
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
module ClassMethods
|
|
10
14
|
def self.extended(klass)
|
|
11
15
|
return unless klass.active_scaffold_config
|
|
@@ -139,7 +143,7 @@ module ActiveScaffold
|
|
|
139
143
|
rescue StandardError => e
|
|
140
144
|
message = "on the ActiveScaffold column :#{column.name}, search_ui = #{search_ui} in #{name}"
|
|
141
145
|
ActiveScaffold.log_exception(e, message)
|
|
142
|
-
raise e
|
|
146
|
+
raise e, "#{e.message} -- #{message}", e.backtrace
|
|
143
147
|
end
|
|
144
148
|
end
|
|
145
149
|
|
|
@@ -231,21 +235,21 @@ module ActiveScaffold
|
|
|
231
235
|
['(%<search_sql>s BETWEEN ? AND ?)', value[:from], value[:to]]
|
|
232
236
|
elsif ActiveScaffold::Finder::NUMERIC_COMPARATORS.include?(value[:opt])
|
|
233
237
|
["%<search_sql>s #{value[:opt]} ?", value[:from]]
|
|
234
|
-
elsif ActiveScaffold::Finder
|
|
238
|
+
elsif ActiveScaffold::Finder.logical_comparators&.include?(value[:opt])
|
|
235
239
|
operator =
|
|
236
240
|
case value[:opt]
|
|
237
241
|
when 'all_tokens' then 'AND'
|
|
238
242
|
when 'any_token' then 'OR'
|
|
239
243
|
end
|
|
240
|
-
parser = ActiveScaffold::Bridges::LogicalQueryParser::
|
|
241
|
-
[logical_search_condition(column, value[:from], parser)]
|
|
244
|
+
parser = ActiveScaffold::Bridges::LogicalQueryParser::KeywordQueryParser.new(operator) if operator
|
|
245
|
+
[logical_search_condition(column, value[:from], parser || ::LogicalQueryParser)]
|
|
242
246
|
end
|
|
243
247
|
end
|
|
244
248
|
|
|
245
|
-
def logical_search_condition(column, search, parser
|
|
249
|
+
def logical_search_condition(column, search, parser)
|
|
246
250
|
model = column.active_record_class
|
|
247
251
|
subquery = alias_query_for_same_table_exists(model.all) if column.logical_search.any?(Hash)
|
|
248
|
-
query =
|
|
252
|
+
query = parser.search(search, subquery || model, column.logical_search)
|
|
249
253
|
if subquery
|
|
250
254
|
model.where(same_table_exists_subquery(query))
|
|
251
255
|
else
|
|
@@ -526,7 +530,6 @@ module ActiveScaffold
|
|
|
526
530
|
doesnt_begin_with: 'not_?%',
|
|
527
531
|
doesnt_end_with: 'not_%?'
|
|
528
532
|
}.freeze
|
|
529
|
-
LOGICAL_COMPARATORS = [].freeze
|
|
530
533
|
NULL_COMPARATORS = %w[null not_null].freeze
|
|
531
534
|
DATE_COMPARATORS = %w[PAST FUTURE RANGE].freeze
|
|
532
535
|
DATE_UNITS = %w[DAYS WEEKS MONTHS YEARS].freeze
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveScaffold
|
|
4
|
+
module Helpers
|
|
5
|
+
module AssetsHelpers
|
|
6
|
+
def active_scaffold_javascript_tag
|
|
7
|
+
js_tags = ActiveScaffold::Bridges.all_javascript_tags.map { |method| send(method) }
|
|
8
|
+
js_tags << active_scaffold_javascript_config
|
|
9
|
+
unless defined?(Importmap)
|
|
10
|
+
# Without importmap, active_scaffold/load must be added to the layout too
|
|
11
|
+
js_tags << javascript_include_tag('active_scaffold/load')
|
|
12
|
+
end
|
|
13
|
+
safe_join js_tags
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def active_scaffold_javascript_config
|
|
19
|
+
config = ActiveScaffold.js_config.merge(
|
|
20
|
+
jqueryUiIncluded: ActiveScaffold.jquery_ui_included?,
|
|
21
|
+
bridges: ActiveScaffold::Bridges.all_javascripts.map { |asset| asset_path(asset, extname: '.js') },
|
|
22
|
+
plugins: ActiveScaffold.javascripts.map { |asset| asset_path(asset, extname: '.js') }
|
|
23
|
+
)
|
|
24
|
+
if ActiveScaffold.jquery_ui_included?
|
|
25
|
+
config[:datepickerLocalization] = ActiveScaffold::Assets.active_scaffold_js_code(:jquery_ui)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if Object.const_defined?(:Jquery) && Jquery.const_defined?(:Ui)
|
|
29
|
+
config[:jqueryUi] = ActiveScaffold::Assets::JqueryUiManifest.all_dependencies.map { |asset| asset_path(asset, extname: '.js') }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
javascript_tag <<~JS
|
|
33
|
+
window.ActiveScaffold = window.ActiveScaffold || {};
|
|
34
|
+
window.ActiveScaffold.config = #{config.to_json};
|
|
35
|
+
JS
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -129,7 +129,7 @@ module ActiveScaffold
|
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
def main_form_controller
|
|
132
|
-
parent_controller_name.constantize if params[:parent_controller]
|
|
132
|
+
@main_form_controller ||= parent_controller_name.constantize if params[:parent_controller]
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
def render_parent?
|