active_scaffold 4.4.0 → 4.4.4
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 +18 -0
- data/README.md +3 -5
- data/app/assets/javascripts/active_scaffold/active_scaffold.js +11 -4
- data/app/assets/javascripts/active_scaffold/load.js +41 -22
- data/lib/active_scaffold/actions/common_search.rb +1 -1
- data/lib/active_scaffold/constraints.rb +3 -2
- data/lib/active_scaffold/engine.rb +6 -0
- data/lib/active_scaffold/extensions/active_record_session_store.rb +49 -0
- data/lib/active_scaffold/finder.rb +8 -1
- data/lib/active_scaffold/helpers/form_column_helpers.rb +3 -3
- data/lib/active_scaffold/helpers/search_column_helpers.rb +2 -2
- data/lib/active_scaffold/helpers/tabs_helpers.rb +5 -3
- data/lib/active_scaffold/version.rb +1 -1
- data/lib/generators/active_scaffold/install/install_generator.rb +216 -111
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b212c764ce6307b7968f811cd89c2042b8cb5d1f79b448ddd838360efc8375ab
|
|
4
|
+
data.tar.gz: 4f44ea2e0e00189d67f4027e95decea8b3589e5f3d372d1c53cd289ca70188c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 264149ca1905e0bae13b93a71a3720d9e09d3b2bae57b15a702474dfb40d2d8fad33ea04a8708991d6c983ef6c45159a5fd7133ad143cf95e3d5c74f5b830a34
|
|
7
|
+
data.tar.gz: b8bf1c62d6fc8e9063ccb46df88709630a0ce6d1eb9edebcb3058df8e43535f7e0420a4781614bd479197f00f5cdcce423bf1298b15ddd264515701fa7c83b03
|
data/CHANGELOG.rdoc
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
= 4.4.4
|
|
2
|
+
- Fix the inconsistent behaviour for constraints containing an array with only one value
|
|
3
|
+
- Wait for dynamically loaded plugins and bridges to finish registering their ready and setup callbacks before initial setup, fixing initialization on directly rendered forms
|
|
4
|
+
- Show all association records in select and multi-select search UIs instead of applying form association conditions
|
|
5
|
+
|
|
6
|
+
= 4.4.3 (yanked)
|
|
7
|
+
- Yanked because it accidentally included unreleased 4.5 functionality
|
|
8
|
+
|
|
9
|
+
= 4.4.2 (yanked)
|
|
10
|
+
- Yanked because it accidentally included unreleased 4.5 functionality
|
|
11
|
+
|
|
12
|
+
= 4.4.1
|
|
13
|
+
- Avoid unnecessary DISTINCT in searches without joins or with only belongs_to joins, fixing PostgreSQL searches on models with json columns
|
|
14
|
+
- Update install generator for the supported rails versions and asset pipelines
|
|
15
|
+
- Fix rendering field descriptions and tabbed forms when custom input helpers return frozen strings
|
|
16
|
+
- Fix inline action links with rails-ujs on Rails 8.1 by inserting the raw HTML response instead of its parsed Document
|
|
17
|
+
- Preserve nested ActiveScaffold session changes with activerecord-session_store 2.3.0
|
|
18
|
+
|
|
1
19
|
= 4.4.0
|
|
2
20
|
- Add support for local sorting using JS when the list has no pagination
|
|
3
21
|
- Add UI rendering customization for CSS framework and theme integration: configure tags and HTML attributes for named elements, use context-aware procs, and override focused helpers when structural markup must change
|
data/README.md
CHANGED
|
@@ -41,11 +41,9 @@ Add jquery-rails to Gemfile, or handle jquery with other tools like webpack or i
|
|
|
41
41
|
gem 'jquery-rails'
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
On supported Rails versions (7.2 and 8.x), the install generator adds the ActiveScaffold route concerns and the lines required to load its JavaScript and stylesheets for the detected asset pipeline. With import maps, this includes both the pins in `config/importmap.rb` and the imports in `app/javascript/application.js`. With Propshaft, it adds the required helpers to the application layout; with Sprockets, it updates the asset manifests instead.
|
|
45
45
|
|
|
46
|
-
For
|
|
47
|
-
|
|
48
|
-
-- TODO: Add notes about dartsass-rails for propshaft or dartsass-sprockets for sprockets.
|
|
46
|
+
For Dart Sass, the generator adds the appropriate `@use` line to `app/assets/stylesheets/application.scss`. It can also update an older `application.css` or `application.css.erb` that contains Sprockets require directives. If `jquery-rails` is installed, the generator adds `jquery` and `jquery_ujs` to the same JavaScript setup. The exact lines for each supported setup are described in [Stylesheet Loading](#stylesheet-loading) and [Javascript Loading](#javascript-loading).
|
|
49
47
|
|
|
50
48
|
Run the following commands
|
|
51
49
|
|
|
@@ -131,7 +129,7 @@ In the layout, load the importmap modules and call active_scaffold_javascript_ta
|
|
|
131
129
|
<%= active_scaffold_javascript_tag %>
|
|
132
130
|
```
|
|
133
131
|
|
|
134
|
-
### Propshaft
|
|
132
|
+
### Propshaft without import maps
|
|
135
133
|
|
|
136
134
|
If you're not using importmaps, then you can load the JS files individually, but you only need to load jquery and jquery_ujs or rails_ujs, and then call active_scaffold_javascript_tag:
|
|
137
135
|
|
|
@@ -1339,7 +1339,12 @@
|
|
|
1339
1339
|
return true;
|
|
1340
1340
|
});
|
|
1341
1341
|
jQuery(document).on('ajax:success', 'a.as_action', function(event, response) {
|
|
1342
|
-
if (event.detail && !response)
|
|
1342
|
+
if (event.detail && !response) {
|
|
1343
|
+
response = event.detail[0];
|
|
1344
|
+
// rails-ujs parses text/html responses into a Document, which cannot be inserted and
|
|
1345
|
+
// loses table rows parsed outside a table, so use the raw response from the XHR.
|
|
1346
|
+
if (response instanceof Document && event.detail[2]) response = event.detail[2].responseText;
|
|
1347
|
+
}
|
|
1343
1348
|
var action_link = ActiveScaffold.ActionLink.get(jQuery(this));
|
|
1344
1349
|
if (action_link) {
|
|
1345
1350
|
if (action_link.position) {
|
|
@@ -1690,8 +1695,10 @@
|
|
|
1690
1695
|
|
|
1691
1696
|
// call setup on document.ready if Turbolinks not enabled
|
|
1692
1697
|
if ((typeof(Turbolinks) == 'undefined' || !Turbolinks.supported) && typeof(Turbo) == 'undefined') {
|
|
1693
|
-
ActiveScaffold.
|
|
1694
|
-
|
|
1698
|
+
if (!ActiveScaffold.config.deferInitialSetup) {
|
|
1699
|
+
ActiveScaffold.setup_history_state();
|
|
1700
|
+
ActiveScaffold.setup(document);
|
|
1701
|
+
}
|
|
1695
1702
|
}
|
|
1696
1703
|
if (ActiveScaffold.config.warn_changes) ActiveScaffold.setup_warn_changes();
|
|
1697
1704
|
jQuery(document).on('as:element_updated as:element_created', function(e, action_link) {
|
|
@@ -1727,7 +1734,7 @@
|
|
|
1727
1734
|
});
|
|
1728
1735
|
|
|
1729
1736
|
jQuery(document).on('turbolinks:load turbo:load', function($) {
|
|
1730
|
-
ActiveScaffold.setup(document);
|
|
1737
|
+
if (!ActiveScaffold.config.deferInitialSetup) ActiveScaffold.setup(document);
|
|
1731
1738
|
});
|
|
1732
1739
|
jQuery(window).on('scroll resize', ActiveScaffold.update_floating_form_footer);
|
|
1733
1740
|
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// ActiveScaffold main entry point
|
|
2
2
|
(function() {
|
|
3
3
|
const config = window.ActiveScaffold?.config || {};
|
|
4
|
+
config.deferInitialSetup = true;
|
|
5
|
+
const scriptLoads = new Map();
|
|
6
|
+
const readyHeld = typeof window.jQuery?.holdReady === 'function';
|
|
7
|
+
if (readyHeld) window.jQuery.holdReady(true);
|
|
4
8
|
|
|
5
9
|
// Load dependencies based on configuration
|
|
6
10
|
const loadDependencies = async () => {
|
|
@@ -17,23 +21,14 @@
|
|
|
17
21
|
await loadScript(RAILS_ASSET_URL('/active_scaffold/active_scaffold.js'));
|
|
18
22
|
await loadScript(RAILS_ASSET_URL('/active_scaffold/jquery.editinplace.js'));
|
|
19
23
|
|
|
20
|
-
// Load plugins
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Load bridges
|
|
28
|
-
if (config.bridges) {
|
|
29
|
-
for (const bridge of config.bridges) {
|
|
30
|
-
await loadScript(bridge);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
24
|
+
// Load plugins and bridges in order. Some of them depend on scripts loaded
|
|
25
|
+
// before them and register callbacks while they are being evaluated.
|
|
26
|
+
await loadScripts(config.plugins);
|
|
27
|
+
await loadScripts(config.bridges);
|
|
33
28
|
|
|
34
29
|
// ALL DEPENDENCIES ARE LOADED NOW
|
|
35
30
|
// Initialize everything
|
|
36
|
-
initializeActiveScaffold();
|
|
31
|
+
await initializeActiveScaffold();
|
|
37
32
|
};
|
|
38
33
|
|
|
39
34
|
const loadJQueryUI = async () => {
|
|
@@ -57,8 +52,16 @@
|
|
|
57
52
|
initializeJQueryUI();
|
|
58
53
|
};
|
|
59
54
|
|
|
55
|
+
const loadScripts = async (paths = []) => {
|
|
56
|
+
for (const path of paths || []) {
|
|
57
|
+
await loadScript(path);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
60
61
|
const loadScript = (path) => {
|
|
61
|
-
|
|
62
|
+
if (scriptLoads.has(path)) return scriptLoads.get(path);
|
|
63
|
+
|
|
64
|
+
const promise = new Promise((resolve, reject) => {
|
|
62
65
|
// Check if already loaded
|
|
63
66
|
if (document.querySelector(`script[src*="${path}"]`)) {
|
|
64
67
|
resolve();
|
|
@@ -72,6 +75,9 @@
|
|
|
72
75
|
script.onerror = reject;
|
|
73
76
|
document.head.appendChild(script);
|
|
74
77
|
});
|
|
78
|
+
|
|
79
|
+
scriptLoads.set(path, promise);
|
|
80
|
+
return promise;
|
|
75
81
|
};
|
|
76
82
|
|
|
77
83
|
const initializeJQueryUI = function() {
|
|
@@ -100,13 +106,26 @@
|
|
|
100
106
|
}
|
|
101
107
|
};
|
|
102
108
|
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (window.jQuery) {
|
|
107
|
-
|
|
109
|
+
// Initialize only after plugins and bridges are available. This is especially
|
|
110
|
+
// important when this file is loaded as a regular script after DOM ready.
|
|
111
|
+
const initializeActiveScaffold = () => new Promise((resolve) => {
|
|
112
|
+
if (!window.jQuery || !window.ActiveScaffold) {
|
|
113
|
+
resolve();
|
|
114
|
+
return;
|
|
108
115
|
}
|
|
109
|
-
|
|
116
|
+
|
|
117
|
+
jQuery(function() {
|
|
118
|
+
config.deferInitialSetup = false;
|
|
119
|
+
if ((typeof(Turbolinks) == 'undefined' || !Turbolinks.supported) && typeof(Turbo) == 'undefined') {
|
|
120
|
+
ActiveScaffold.setup_history_state();
|
|
121
|
+
}
|
|
122
|
+
ActiveScaffold.setup(document);
|
|
123
|
+
jQuery(document).trigger('active-scaffold:loaded');
|
|
124
|
+
resolve();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (readyHeld) window.jQuery.holdReady(false);
|
|
128
|
+
});
|
|
110
129
|
|
|
111
130
|
// Start loading when DOM is ready
|
|
112
131
|
if (document.readyState === 'loading') {
|
|
@@ -114,4 +133,4 @@
|
|
|
114
133
|
} else {
|
|
115
134
|
loadDependencies();
|
|
116
135
|
}
|
|
117
|
-
})();
|
|
136
|
+
})();
|
|
@@ -35,7 +35,7 @@ module ActiveScaffold::Actions
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def set_outer_joins_for_search(columns) # rubocop:disable Naming/AccessorMethodName
|
|
38
|
-
active_scaffold_outer_joins.concat columns.flat_map(&:search_joins).uniq
|
|
38
|
+
active_scaffold_outer_joins.concat columns.flat_map(&:search_joins).compact.uniq
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def store_search_params_into_session
|
|
@@ -163,8 +163,9 @@ module ActiveScaffold
|
|
|
163
163
|
elsif association.polymorphic?
|
|
164
164
|
apply_constraint_on_polymorphic_association(record, association, value)
|
|
165
165
|
elsif !association.source_reflection&.through? && # regular singular association, or one-level through association
|
|
166
|
-
!value.is_a?(Array)
|
|
167
|
-
|
|
166
|
+
(!value.is_a?(Array) || value.size == 1)
|
|
167
|
+
constraint_value = value.is_a?(Array) ? value.first : value
|
|
168
|
+
record.send(:"#{association.name}=", association.klass.find(constraint_value))
|
|
168
169
|
|
|
169
170
|
# setting the belongs_to side of a has_one isn't safe. if the has_one was already
|
|
170
171
|
# specified, rails won't automatically clear out the previous associated record.
|
|
@@ -57,6 +57,12 @@ module ActiveScaffold
|
|
|
57
57
|
require 'active_scaffold/extensions/paginator_extensions'
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
initializer 'active_scaffold.active_record_session_store', after: :load_config_initializers do |app|
|
|
61
|
+
require 'active_scaffold/extensions/active_record_session_store'
|
|
62
|
+
extension = ActiveScaffold::Extensions::ActiveRecordSessionStore
|
|
63
|
+
ActiveScaffold::Core.prepend(extension::Core) if extension.affected?(app)
|
|
64
|
+
end
|
|
65
|
+
|
|
60
66
|
initializer 'active_scaffold.assets' do |app|
|
|
61
67
|
if defined?(::Sprockets)
|
|
62
68
|
# Tell sprockets where your assets are located
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveScaffold
|
|
4
|
+
module Extensions
|
|
5
|
+
module ActiveRecordSessionStore
|
|
6
|
+
AFFECTED_VERSION = Gem::Version.new('2.3.0')
|
|
7
|
+
|
|
8
|
+
# activerecord-session_store 2.3.0 keeps references to nested values in
|
|
9
|
+
# the request session, so in-place changes can also alter the value used
|
|
10
|
+
# for dirty comparison. Keep ActiveScaffold's request storage detached.
|
|
11
|
+
module Core
|
|
12
|
+
def active_scaffold_session_storage(id = nil)
|
|
13
|
+
session_index = active_scaffold_session_storage_key(id)
|
|
14
|
+
@active_scaffold_session_storage ||= {}
|
|
15
|
+
|
|
16
|
+
storage = @active_scaffold_session_storage[session_index]
|
|
17
|
+
return storage if storage && session[session_index].equal?(storage)
|
|
18
|
+
|
|
19
|
+
storage = session[session_index]&.deep_dup || {}
|
|
20
|
+
session[session_index] = storage
|
|
21
|
+
@active_scaffold_session_storage[session_index] = storage
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.affected?(application = Rails.application)
|
|
26
|
+
store_class = active_record_store
|
|
27
|
+
version = session_store_version
|
|
28
|
+
return false unless store_class && version
|
|
29
|
+
|
|
30
|
+
configured_store = application.config.session_store
|
|
31
|
+
uses_active_record_store =
|
|
32
|
+
configured_store == store_class ||
|
|
33
|
+
(configured_store.is_a?(Class) && configured_store < store_class)
|
|
34
|
+
|
|
35
|
+
uses_active_record_store && Gem::Version.new(version) == AFFECTED_VERSION
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.active_record_store
|
|
39
|
+
::ActionDispatch::Session::ActiveRecordStore if defined?(::ActionDispatch::Session::ActiveRecordStore)
|
|
40
|
+
end
|
|
41
|
+
private_class_method :active_record_store
|
|
42
|
+
|
|
43
|
+
def self.session_store_version
|
|
44
|
+
::ActiveRecord::SessionStore::VERSION if defined?(::ActiveRecord::SessionStore::VERSION)
|
|
45
|
+
end
|
|
46
|
+
private_class_method :session_store_version
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -776,7 +776,14 @@ module ActiveScaffold
|
|
|
776
776
|
end
|
|
777
777
|
|
|
778
778
|
def distinct_query?(options)
|
|
779
|
-
options[:left_outer_joins]
|
|
779
|
+
joins = [options[:left_outer_joins], options[:left_joins]].flatten.compact
|
|
780
|
+
model = active_scaffold_config.model
|
|
781
|
+
|
|
782
|
+
joins.any? do |join|
|
|
783
|
+
next true unless join.is_a?(Symbol)
|
|
784
|
+
|
|
785
|
+
model.reflect_on_association(join)&.macro != :belongs_to
|
|
786
|
+
end
|
|
780
787
|
end
|
|
781
788
|
|
|
782
789
|
def joins_for_finder
|
|
@@ -259,12 +259,12 @@ module ActiveScaffold
|
|
|
259
259
|
if column.association.nil? || column.association.belongs_to?
|
|
260
260
|
# hidden field probably not needed, but leaving it just in case
|
|
261
261
|
# but it isn't working for assocations which are not belongs_to
|
|
262
|
-
field
|
|
262
|
+
field = safe_join([field, hidden_field(:record, column.association&.foreign_key || column.name, column_options)])
|
|
263
263
|
end
|
|
264
264
|
else
|
|
265
265
|
field ||= active_scaffold_input_for column, scope, column_options, form_columns: form_columns
|
|
266
266
|
end
|
|
267
|
-
field
|
|
267
|
+
field = safe_join([field, loading_indicator_tag(action: :render_field, id: params[:id])]) if field && column.update_columns
|
|
268
268
|
description = column_description(column, record, scope)
|
|
269
269
|
|
|
270
270
|
label = label_tag(label_for(column, column_options), form_column_label(column, record, scope))
|
|
@@ -273,7 +273,7 @@ module ActiveScaffold
|
|
|
273
273
|
end
|
|
274
274
|
|
|
275
275
|
def form_attribute_html(column, label, field, description, attributes, collapsible_id: nil)
|
|
276
|
-
|
|
276
|
+
field = safe_join([field, description].compact) if description.present?
|
|
277
277
|
context = {column: column}
|
|
278
278
|
as_element :form_attribute, proc_options: context, **attributes do
|
|
279
279
|
as_element(:form_attribute_label, label, proc_options: context) <<
|
|
@@ -93,7 +93,7 @@ module ActiveScaffold
|
|
|
93
93
|
associated.collect!(&:to_i)
|
|
94
94
|
method = ui_options[:label_method] || :to_label
|
|
95
95
|
helper_method = association_helper_method(column.association, :sorted_association_options_find)
|
|
96
|
-
select_options = send(helper_method, column.association,
|
|
96
|
+
select_options = send(helper_method, column.association, {}, record).collect do |r|
|
|
97
97
|
[r.send(method), r.id]
|
|
98
98
|
end
|
|
99
99
|
else
|
|
@@ -119,7 +119,7 @@ module ActiveScaffold
|
|
|
119
119
|
associated = associated.is_a?(Array) ? associated.map(&:to_i) : associated.to_i unless associated.nil?
|
|
120
120
|
method = column.association.belongs_to? ? column.association.foreign_key : column.name
|
|
121
121
|
helper_method = association_helper_method(column.association, :sorted_association_options_find)
|
|
122
|
-
select_options = send(helper_method, column.association,
|
|
122
|
+
select_options = send(helper_method, column.association, {}, record)
|
|
123
123
|
else
|
|
124
124
|
method = column.name
|
|
125
125
|
select_options = active_scaffold_translated_enum_options(column, record, ui_options: ui_options)
|
|
@@ -10,9 +10,11 @@ module ActiveScaffold
|
|
|
10
10
|
tab_options = send(override_helper_per_model(:active_scaffold_tab_options, record.class), column, record)
|
|
11
11
|
used_tabs = send(override_helper_per_model(:active_scaffold_current_tabs, record.class), column, record, tab_options)
|
|
12
12
|
input_helper = override_helper_per_model(:active_scaffold_input_for_tabbed, record.class)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
safe_join([
|
|
14
|
+
send(input_helper, column, record, subsection_id, tab_options, used_tabs.map { |v, _| v }),
|
|
15
|
+
active_scaffold_refresh_link(nil, {'data-update_url' => url_for(add_tab_url)}, record, refresh_opts),
|
|
16
|
+
active_scaffold_tabs_for(column, record, subsection_id, tab_options, used_tabs, &)
|
|
17
|
+
])
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def active_scaffold_input_for_tabbed(column, record, subsection_id, tab_options, used_tabs)
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'rails/generators/base'
|
|
4
|
-
# require 'generators/active_scaffold_controller/active_scaffold_controller_generator'
|
|
5
4
|
|
|
6
5
|
module ActiveScaffold
|
|
7
6
|
module Generators
|
|
8
7
|
class InstallGenerator < Rails::Generators::Base
|
|
8
|
+
IMPORTMAP = 'config/importmap.rb'
|
|
9
|
+
JS_ASSET = 'app/assets/javascripts/application.js'
|
|
10
|
+
JS_APP = 'app/javascript/application.js'
|
|
11
|
+
LAYOUT = 'app/views/layouts/application.html.erb'
|
|
12
|
+
MANIFEST = 'app/assets/config/manifest.js'
|
|
13
|
+
SCSS_ASSET = 'app/assets/stylesheets/application.scss'
|
|
14
|
+
CSS_ASSETS = %w[app/assets/stylesheets/application.css app/assets/stylesheets/application.css.erb].freeze
|
|
15
|
+
|
|
9
16
|
def self.base_root
|
|
10
17
|
File.expand_path '../..', __dir__
|
|
11
18
|
end
|
|
@@ -15,151 +22,249 @@ module ActiveScaffold
|
|
|
15
22
|
route 'concern :active_scaffold_association, ActiveScaffold::Routing::Association.new'
|
|
16
23
|
end
|
|
17
24
|
|
|
18
|
-
IMPORTMAP = 'config/importmap.rb'
|
|
19
|
-
JS_ASSET = 'app/assets/javascripts/application.js'
|
|
20
|
-
JS_APP = 'app/javascript/application.js'
|
|
21
|
-
MANIFEST = 'app/assets/config/manifest.js'
|
|
22
|
-
|
|
23
25
|
def add_javascript
|
|
24
26
|
if File.exist?(IMPORTMAP)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
say_status('skipped', "insert into #{JS_ASSET}", :yellow)
|
|
31
|
-
else
|
|
32
|
-
insert_into_file JS_ASSET, after: %r{//= require +.*ujs['"]?\n} do
|
|
33
|
-
"//= require active_scaffold\n"
|
|
34
|
-
end
|
|
35
|
-
add_to_manifest 'active_scaffold/manifest.js'
|
|
36
|
-
end
|
|
37
|
-
setup_jquery JS_ASSET, original_js
|
|
27
|
+
add_importmap_javascript
|
|
28
|
+
elsif use_sprockets_javascript?
|
|
29
|
+
add_sprockets_javascript
|
|
30
|
+
elsif propshaft? || File.exist?(JS_APP)
|
|
31
|
+
add_propshaft_javascript_tags
|
|
38
32
|
else
|
|
39
|
-
|
|
33
|
+
raise Thor::Error, 'Unable to configure ActiveScaffold JavaScript: no import map, Propshaft setup, ' \
|
|
34
|
+
"#{JS_APP}, or #{JS_ASSET} was found"
|
|
40
35
|
end
|
|
41
36
|
end
|
|
42
37
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
original_css = File.binread(file)
|
|
48
|
-
if original_css.match?(/require active_scaffold$/)
|
|
49
|
-
say_status('skipped', 'insert into app/assets/stylesheets/application.css', :yellow)
|
|
38
|
+
def add_stylesheet
|
|
39
|
+
if File.exist?(SCSS_ASSET)
|
|
40
|
+
add_to_scss_asset
|
|
50
41
|
else
|
|
51
|
-
|
|
52
|
-
" *= require active_scaffold\n"
|
|
53
|
-
end
|
|
42
|
+
add_to_css_asset
|
|
54
43
|
end
|
|
55
44
|
end
|
|
56
45
|
|
|
57
46
|
protected
|
|
58
47
|
|
|
48
|
+
def add_importmap_javascript
|
|
49
|
+
unless File.exist?(JS_APP)
|
|
50
|
+
raise Thor::Error, "Unable to configure import maps: #{JS_APP} does not exist"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
add_to_importmap
|
|
54
|
+
add_to_js_app
|
|
55
|
+
add_importmap_layout_tags if propshaft?
|
|
56
|
+
end
|
|
57
|
+
|
|
59
58
|
def add_to_importmap
|
|
60
59
|
original_js = File.binread(IMPORTMAP)
|
|
61
|
-
|
|
60
|
+
pin = propshaft? ? "pin 'active_scaffold', to: 'active_scaffold/load.js'" : "pin 'active_scaffold'"
|
|
61
|
+
current_pin = original_js[/^pin +['"]active_scaffold['"].*$/, 0]
|
|
62
|
+
if current_pin == pin
|
|
62
63
|
say_status('skipped', "append active_scaffold to #{IMPORTMAP}", :yellow)
|
|
64
|
+
elsif current_pin && generated_active_scaffold_pin?(current_pin)
|
|
65
|
+
gsub_file IMPORTMAP, current_pin, pin
|
|
63
66
|
else
|
|
64
|
-
append_to_file IMPORTMAP
|
|
65
|
-
"pin 'active_scaffold'\n"
|
|
66
|
-
end
|
|
67
|
+
append_to_file IMPORTMAP, "#{pin}\n"
|
|
67
68
|
end
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
append_to_file IMPORTMAP do
|
|
73
|
-
"pin 'jquery'\n"
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
if original_js.match?(%r{^pin +['"](jquery_ujs|@rails/ujs)['"](?=,|$)})
|
|
77
|
-
say_status('skipped', "append jquery_ujs to #{IMPORTMAP}", :yellow)
|
|
78
|
-
else
|
|
79
|
-
append_to_file IMPORTMAP do
|
|
80
|
-
"pin 'jquery_ujs'\n"
|
|
81
|
-
end
|
|
82
|
-
end
|
|
69
|
+
|
|
70
|
+
if jquery?
|
|
71
|
+
add_importmap_pin original_js, 'jquery'
|
|
72
|
+
add_importmap_pin original_js, 'jquery_ujs', alternatives: ['@rails/ujs']
|
|
83
73
|
else
|
|
84
|
-
say_status('missing', 'no jquery-rails gem
|
|
85
|
-
"to Gemfile and add pin \"jquery\" and pin \"jquery_ujs\" or pin \"@rails/ujs\", to #{IMPORTMAP}", :red)
|
|
74
|
+
say_status('missing', 'no jquery-rails gem; load jquery and jquery_ujs or @rails/ujs manually', :red)
|
|
86
75
|
end
|
|
87
76
|
end
|
|
88
77
|
|
|
89
|
-
def
|
|
90
|
-
|
|
91
|
-
if original_js.match?(/^
|
|
92
|
-
say_status('skipped', "append
|
|
78
|
+
def add_importmap_pin(original_js, name, alternatives: [])
|
|
79
|
+
names = [name, *alternatives].map { |item| Regexp.escape(item) }.join('|')
|
|
80
|
+
if original_js.match?(/^pin +['"](?:#{names})['"](?=,|$)/)
|
|
81
|
+
say_status('skipped', "append #{name} to #{IMPORTMAP}", :yellow)
|
|
93
82
|
else
|
|
94
|
-
append_to_file
|
|
95
|
-
"import 'active_scaffold'\n"
|
|
96
|
-
end
|
|
83
|
+
append_to_file IMPORTMAP, "pin '#{name}'\n"
|
|
97
84
|
end
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def generated_active_scaffold_pin?(pin)
|
|
88
|
+
pin.match?(%r{\Apin +['"]active_scaffold['"](?:, *to: *['"]active_scaffold/load\.js['"])?\z})
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def add_to_js_app
|
|
92
|
+
original_js = File.binread(JS_APP)
|
|
93
|
+
add_js_import original_js, 'active_scaffold'
|
|
94
|
+
manifest_files = ['active_scaffold/manifest.js']
|
|
95
|
+
|
|
96
|
+
if jquery?
|
|
97
|
+
unless js_import?(original_js, 'jquery')
|
|
98
|
+
add_js_import original_js, 'jquery', before: 'active_scaffold'
|
|
99
|
+
manifest_files << 'jquery.js'
|
|
107
100
|
end
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
manifest << 'jquery_ujs.js'
|
|
112
|
-
insert_into_file JS_APP, before: /import +['"]active_scaffold['"]\n/ do
|
|
113
|
-
"import 'jquery_ujs'\n"
|
|
114
|
-
end
|
|
101
|
+
unless original_js.match?(%r{^import +(['"]jquery_ujs['"]|Rails from ['"]@rails/ujs['"])})
|
|
102
|
+
add_js_import original_js, 'jquery_ujs', before: 'active_scaffold'
|
|
103
|
+
manifest_files << 'jquery_ujs.js'
|
|
115
104
|
end
|
|
116
105
|
end
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
106
|
+
|
|
107
|
+
add_to_manifest(*manifest_files) if sprockets?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def add_js_import(original_js, name, before: nil)
|
|
111
|
+
if js_import?(original_js, name)
|
|
112
|
+
say_status('skipped', "add #{name} to #{JS_APP}", :yellow)
|
|
113
|
+
elsif before
|
|
114
|
+
insert_into_file JS_APP, "import '#{name}'\n", before: /^import +['"]#{Regexp.escape(before)}['"]\n/
|
|
115
|
+
else
|
|
116
|
+
append_to_file JS_APP, "import '#{name}'\n"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def js_import?(content, name)
|
|
121
|
+
content.match?(/^import +['"]#{Regexp.escape(name)}['"]/)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def add_importmap_layout_tags
|
|
125
|
+
tags = []
|
|
126
|
+
content = layout_content
|
|
127
|
+
tags << '<%= javascript_importmap_tags %>' unless content.include?('javascript_importmap_tags')
|
|
128
|
+
tags << '<%= active_scaffold_javascript_tag %>' unless content.include?('active_scaffold_javascript_tag')
|
|
129
|
+
add_layout_tags tags
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def add_propshaft_javascript_tags
|
|
133
|
+
tags = []
|
|
134
|
+
content = layout_content
|
|
135
|
+
if jquery? && !content.match?(/javascript_include_tag .*jquery/)
|
|
136
|
+
tags << "<%= javascript_include_tag 'jquery', 'jquery_ujs' %>"
|
|
137
|
+
elsif !jquery?
|
|
138
|
+
say_status('missing', 'no jquery-rails gem; load jquery and jquery_ujs or @rails/ujs in the layout', :red)
|
|
139
|
+
end
|
|
140
|
+
tags << '<%= active_scaffold_javascript_tag %>' unless content.include?('active_scaffold_javascript_tag')
|
|
141
|
+
add_layout_tags tags
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def layout_content
|
|
145
|
+
unless File.exist?(LAYOUT)
|
|
146
|
+
raise Thor::Error, "Unable to configure ActiveScaffold JavaScript: #{LAYOUT} does not exist"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
File.binread(LAYOUT)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def add_layout_tags(tags)
|
|
153
|
+
return if tags.empty?
|
|
154
|
+
|
|
155
|
+
content = File.binread(LAYOUT)
|
|
156
|
+
unless content.include?('</head>')
|
|
157
|
+
raise Thor::Error, "Unable to configure ActiveScaffold JavaScript: no </head> tag found in #{LAYOUT}"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
insert_into_file LAYOUT, tags.map { |tag| " #{tag}\n" }.join, before: %r{^[ \t]*</head>}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def add_sprockets_javascript
|
|
164
|
+
original_js = File.binread(JS_ASSET)
|
|
165
|
+
unless original_js.match?(%r{//= require +active_scaffold(?:\s|$)})
|
|
166
|
+
append_to_file JS_ASSET, "\n//= require active_scaffold\n"
|
|
167
|
+
end
|
|
168
|
+
setup_sprockets_jquery original_js
|
|
169
|
+
add_to_manifest 'active_scaffold/manifest.js'
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def use_sprockets_javascript?
|
|
173
|
+
File.exist?(JS_ASSET) && (sprockets? || (!propshaft? && !File.exist?(JS_APP)))
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def setup_sprockets_jquery(original_js)
|
|
177
|
+
if jquery?
|
|
178
|
+
return if original_js.match?(%r{//= require +jquery(?:\s|$)})
|
|
179
|
+
|
|
180
|
+
insert_into_file JS_ASSET, "//= require jquery\n", before: %r{//= require +active_scaffold(?:\s|$)}
|
|
181
|
+
else
|
|
182
|
+
say_status('missing', 'no jquery-rails gem; load jquery manually before ActiveScaffold', :red)
|
|
183
|
+
end
|
|
143
184
|
end
|
|
144
185
|
|
|
145
186
|
def add_to_manifest(*files)
|
|
146
|
-
|
|
147
|
-
|
|
187
|
+
original_manifest = File.exist?(MANIFEST) ? File.binread(MANIFEST) : ''
|
|
188
|
+
files.each do |file|
|
|
189
|
+
next if original_manifest.match?(%r{^//= link #{Regexp.escape(file)}$})
|
|
190
|
+
|
|
191
|
+
append_to_file MANIFEST, "//= link #{file}\n"
|
|
148
192
|
end
|
|
149
193
|
end
|
|
150
194
|
|
|
151
|
-
def
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
195
|
+
def add_to_scss_asset
|
|
196
|
+
dependency = scss_dependency
|
|
197
|
+
unless dependency
|
|
198
|
+
say_status('missing', "could not detect dartsass-rails or dartsass-sprockets; update #{SCSS_ASSET} manually", :red)
|
|
199
|
+
return
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
original_scss = File.binread(SCSS_ASSET)
|
|
203
|
+
if original_scss.match?(%r{^@use +['"]active_scaffold(?:/core)?['"]})
|
|
204
|
+
say_status('skipped', "add ActiveScaffold to #{SCSS_ASSET}", :yellow)
|
|
205
|
+
else
|
|
206
|
+
prepend_to_file SCSS_ASSET, "@use '#{dependency}';\n"
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def scss_dependency
|
|
211
|
+
if gem_loaded?('dartsass-sprockets')
|
|
212
|
+
'active_scaffold'
|
|
213
|
+
elsif gem_loaded?('dartsass-rails')
|
|
214
|
+
'active_scaffold/core'
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def add_to_css_asset
|
|
219
|
+
file = CSS_ASSETS.find do |path|
|
|
220
|
+
File.exist?(path) && File.binread(path).match?(/\*= require(?:_tree)?\b/)
|
|
221
|
+
end
|
|
222
|
+
unless file
|
|
223
|
+
say_status('missing', 'no Dart Sass entrypoint or Sprockets stylesheet manifest found', :red)
|
|
224
|
+
return
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
original_css = File.binread(file)
|
|
228
|
+
if original_css.match?(/\*= require +active_scaffold(?:\s|$)/)
|
|
229
|
+
say_status('skipped', "add ActiveScaffold to #{file}", :yellow)
|
|
159
230
|
else
|
|
160
|
-
|
|
231
|
+
insert_into_file file, " *= require active_scaffold\n", after: /^[ \t]*\*= require(?:_tree)?\b.*\n/
|
|
161
232
|
end
|
|
162
233
|
end
|
|
234
|
+
|
|
235
|
+
def propshaft?
|
|
236
|
+
asset_pipeline == :propshaft
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def sprockets?
|
|
240
|
+
asset_pipeline == :sprockets
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def asset_pipeline
|
|
244
|
+
return :propshaft if propshaft_active?
|
|
245
|
+
return :sprockets if sprockets_active?
|
|
246
|
+
return :propshaft if gem_loaded?('propshaft')
|
|
247
|
+
|
|
248
|
+
:sprockets if gem_loaded?('sprockets-rails')
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def propshaft_active?
|
|
252
|
+
defined?(Propshaft::Assembly) && Rails.application.respond_to?(:assets) &&
|
|
253
|
+
Rails.application.assets.is_a?(Propshaft::Assembly)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def sprockets_active?
|
|
257
|
+
defined?(Sprockets::Environment) && Rails.application.respond_to?(:assets) &&
|
|
258
|
+
Rails.application.assets.is_a?(Sprockets::Environment)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def jquery?
|
|
262
|
+
Object.const_defined?(:Jquery)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def gem_loaded?(name)
|
|
266
|
+
Gem.loaded_specs.key?(name)
|
|
267
|
+
end
|
|
163
268
|
end
|
|
164
269
|
end
|
|
165
270
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_scaffold
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.4.
|
|
4
|
+
version: 4.4.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Many, see README
|
|
@@ -325,6 +325,7 @@ files:
|
|
|
325
325
|
- lib/active_scaffold/extensions/action_controller_rendering.rb
|
|
326
326
|
- lib/active_scaffold/extensions/action_controller_rescueing.rb
|
|
327
327
|
- lib/active_scaffold/extensions/action_view_rendering.rb
|
|
328
|
+
- lib/active_scaffold/extensions/active_record_session_store.rb
|
|
328
329
|
- lib/active_scaffold/extensions/connection_adapter.rb
|
|
329
330
|
- lib/active_scaffold/extensions/ice_nine.rb
|
|
330
331
|
- lib/active_scaffold/extensions/localize.rb
|