active_scaffold 4.3.2 → 4.4.1
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 +26 -0
- data/README.md +4 -6
- data/app/assets/javascripts/active_scaffold/active_scaffold.js +104 -12
- data/app/assets/javascripts/active_scaffold/draggable_lists.js +4 -3
- data/app/assets/javascripts/active_scaffold/load.js +20 -5
- data/app/assets/stylesheets/active_scaffold/_images.scss +53 -1
- data/app/assets/stylesheets/active_scaffold/_layout.scss +1 -66
- data/app/views/active_scaffold_overrides/_add_existing_form.html.erb +17 -11
- data/app/views/active_scaffold_overrides/_base_form.html.erb +29 -19
- data/app/views/active_scaffold_overrides/_field_search.html.erb +9 -9
- data/app/views/active_scaffold_overrides/_field_search_columns.html.erb +29 -33
- data/app/views/active_scaffold_overrides/_form.html.erb +43 -34
- data/app/views/active_scaffold_overrides/_form_association.html.erb +3 -3
- data/app/views/active_scaffold_overrides/_form_association_footer.html.erb +20 -16
- data/app/views/active_scaffold_overrides/_form_association_record.html.erb +3 -4
- data/app/views/active_scaffold_overrides/_horizontal_subform_header.html.erb +2 -1
- data/app/views/active_scaffold_overrides/_list.html.erb +6 -4
- data/app/views/active_scaffold_overrides/_list_calculations.html.erb +1 -1
- data/app/views/active_scaffold_overrides/_list_header.html.erb +3 -3
- data/app/views/active_scaffold_overrides/_list_messages.html.erb +19 -19
- data/app/views/active_scaffold_overrides/_list_messages_content.html.erb +5 -5
- data/app/views/active_scaffold_overrides/_list_pagination.html.erb +7 -5
- data/app/views/active_scaffold_overrides/_list_pagination_links.html.erb +5 -3
- data/app/views/active_scaffold_overrides/_list_record.html.erb +13 -8
- data/app/views/active_scaffold_overrides/_list_with_header.html.erb +8 -8
- data/app/views/active_scaffold_overrides/_messages.html.erb +3 -3
- data/app/views/active_scaffold_overrides/_render_field.js.erb +8 -11
- data/app/views/active_scaffold_overrides/_search.html.erb +7 -6
- data/app/views/active_scaffold_overrides/_show.html.erb +4 -4
- data/app/views/active_scaffold_overrides/_show_actions.html.erb +2 -2
- data/app/views/active_scaffold_overrides/_show_columns.html.erb +28 -30
- data/app/views/active_scaffold_overrides/add_existing.js.erb +7 -4
- data/app/views/active_scaffold_overrides/render_field.js.erb +1 -1
- data/config/locales/de.yml +2 -1
- data/config/locales/en.yml +33 -32
- data/config/locales/es.yml +15 -14
- data/config/locales/fr.yml +3 -2
- data/config/locales/hu.yml +3 -2
- data/config/locales/ja.yml +2 -1
- data/config/locales/ro.yml +169 -0
- data/config/locales/ru.yml +8 -7
- data/config/locales/ta.yml +7 -6
- data/config/locales/zh_Hans.yml +14 -13
- data/lib/active_scaffold/actions/common_search.rb +1 -14
- data/lib/active_scaffold/actions/core.rb +55 -16
- data/lib/active_scaffold/actions/field_search.rb +2 -2
- data/lib/active_scaffold/actions/list.rb +35 -13
- data/lib/active_scaffold/actions/nested.rb +31 -7
- data/lib/active_scaffold/actions/show.rb +1 -1
- data/lib/active_scaffold/active_record_permissions.rb +1 -1
- data/lib/active_scaffold/attribute_params.rb +2 -1
- data/lib/active_scaffold/bridges/date_picker/helper.rb +14 -0
- data/lib/active_scaffold/bridges/date_picker.rb +10 -0
- data/lib/active_scaffold/config/list.rb +10 -1
- data/lib/active_scaffold/core.rb +9 -6
- data/lib/active_scaffold/data_structures/action_columns.rb +2 -0
- data/lib/active_scaffold/data_structures/column.rb +27 -6
- data/lib/active_scaffold/data_structures/filter.rb +4 -0
- data/lib/active_scaffold/data_structures/nested_info.rb +8 -1
- data/lib/active_scaffold/data_structures/sorting.rb +1 -1
- data/lib/active_scaffold/engine.rb +6 -0
- data/lib/active_scaffold/extensions/action_view_rendering.rb +1 -1
- data/lib/active_scaffold/extensions/active_record_session_store.rb +49 -0
- data/lib/active_scaffold/finder.rb +73 -21
- data/lib/active_scaffold/helpers/action_link_helpers.rb +43 -18
- data/lib/active_scaffold/helpers/assets_helpers.rb +4 -4
- data/lib/active_scaffold/helpers/filter_helpers.rb +3 -3
- data/lib/active_scaffold/helpers/form_column_helpers.rb +34 -22
- data/lib/active_scaffold/helpers/form_ui_helpers.rb +15 -10
- data/lib/active_scaffold/helpers/framework_ui_helpers.rb +22 -0
- data/lib/active_scaffold/helpers/human_condition_helpers.rb +1 -0
- data/lib/active_scaffold/helpers/list_column_helpers.rb +19 -0
- data/lib/active_scaffold/helpers/pagination_helpers.rb +28 -7
- data/lib/active_scaffold/helpers/search_column_helpers.rb +32 -5
- data/lib/active_scaffold/helpers/tabs_helpers.rb +7 -7
- data/lib/active_scaffold/helpers/view_helpers.rb +12 -0
- data/lib/active_scaffold/tableless.rb +11 -8
- data/lib/active_scaffold/version.rb +2 -2
- data/lib/active_scaffold.rb +169 -0
- data/lib/generators/active_scaffold/install/install_generator.rb +216 -111
- data/lib/tasks/bundle.rake +2 -1
- data/lib/tasks/mock_app.rake +43 -0
- metadata +21 -7
- data/shoulda_macros/macros.rb +0 -145
data/lib/active_scaffold.rb
CHANGED
|
@@ -58,6 +58,174 @@ module ActiveScaffold
|
|
|
58
58
|
mattr_accessor :javascripts, instance_writer: false
|
|
59
59
|
self.javascripts = []
|
|
60
60
|
|
|
61
|
+
# A way to define the tags and html attributes used in different places.
|
|
62
|
+
# Changing the class or replacing the attributes may break the default AS stylesheets, but not JS or functionality.
|
|
63
|
+
#
|
|
64
|
+
# The keys in the hash are the names used by ActiveScaffold to identify the
|
|
65
|
+
# elements.
|
|
66
|
+
# The values are a hash of attributes used to create the markup, supporting the following keys:
|
|
67
|
+
# * The :tag key can be used to specify a different tag name for the element.
|
|
68
|
+
# * The :attributes key can be used to specify additional html attributes.
|
|
69
|
+
# * The :proc key can be used to specify a method to dinamically return the tag and attributes hash, as an array.
|
|
70
|
+
# When :proc is present, :tag and :attributes won't be used. The proc will receive an options hash, which usually
|
|
71
|
+
# will be a hash with html attributes, but in some cases may have other options:
|
|
72
|
+
# * For :action_link_group the hash will have keys such as :level, :first_action, and other options supported by
|
|
73
|
+
# +display_action_links+
|
|
74
|
+
# * For :action_link_separator the hash will have keys such as :level, :level_0_tag, and other options supported by
|
|
75
|
+
# +display_action_links+
|
|
76
|
+
# * Form and search elements receive the column and the configured UI in the options hash.
|
|
77
|
+
# * Action link elements receive the link, record, rendering options, and authorized state.
|
|
78
|
+
# * Pagination elements receive the page number and, for links, the optional text key.
|
|
79
|
+
# Some keys don't have a :tag key, because only attributes are used, the tag is hardcoded, usually elements that are
|
|
80
|
+
# rendered as a form or table, or a table's element (thead, tbody, tr, td, th, tfoot, etc.). For those elements,
|
|
81
|
+
# the :tag key will be ignored if it's set, so the tag can't be changed.
|
|
82
|
+
mattr_reader :ui_elements
|
|
83
|
+
@@ui_elements ||= {
|
|
84
|
+
list: {tag: :div},
|
|
85
|
+
list_header: {tag: :div},
|
|
86
|
+
list_title: {tag: :h2},
|
|
87
|
+
list_actions: {tag: :div},
|
|
88
|
+
filters: {tag: :div, attributes: {class: 'filters'}},
|
|
89
|
+
before_header_table: {tag: :table},
|
|
90
|
+
list_content: {tag: :div},
|
|
91
|
+
list_table: {}, # table
|
|
92
|
+
list_table_head: {}, # thead
|
|
93
|
+
list_table_head_row: {}, # tr
|
|
94
|
+
list_records: {}, # tbody
|
|
95
|
+
list_messages: {attributes: {class: 'messages'}}, # tbody
|
|
96
|
+
list_messages_container: {}, # td
|
|
97
|
+
list_action_messages: {tag: :div},
|
|
98
|
+
filtered_message: {tag: :div, attributes: {class: 'filtered-message'}},
|
|
99
|
+
applied_filters_message: {tag: :div, attributes: {class: 'filtered-message applied-filters'}},
|
|
100
|
+
empty_message: {tag: :p},
|
|
101
|
+
message_reset: {tag: :div, attributes: {class: 'reset'}},
|
|
102
|
+
message_close: {tag: :a, attributes: {href: '#', class: 'close'}},
|
|
103
|
+
server_error: {tag: :p, attributes: {class: 'error-message message server-error'}},
|
|
104
|
+
info_message: {tag: :div, attributes: {class: 'info-message message'}},
|
|
105
|
+
warning_message: {tag: :div, attributes: {class: 'warning-message message'}},
|
|
106
|
+
error_message: {tag: :div, attributes: {class: 'error-message message'}},
|
|
107
|
+
list_footer: {tag: :div, attributes: {class: 'active-scaffold-footer'}},
|
|
108
|
+
list_found: {tag: :div},
|
|
109
|
+
list_found_count: {tag: :span},
|
|
110
|
+
list_calculations: {}, # tr
|
|
111
|
+
pagination_links: {tag: :div},
|
|
112
|
+
pagination_link: {}, # a
|
|
113
|
+
pagination_active_page: {tag: :span},
|
|
114
|
+
record_actions_cell: {}, # td
|
|
115
|
+
record_action_links: {}, # table
|
|
116
|
+
action_link_group: {
|
|
117
|
+
proc: lambda do |options|
|
|
118
|
+
if options[:level]&.zero?
|
|
119
|
+
tag = :div
|
|
120
|
+
attributes = {class: 'action_group'}
|
|
121
|
+
else
|
|
122
|
+
tag = :li
|
|
123
|
+
attributes = {class: ('top' if options[:first_action]).to_s}
|
|
124
|
+
end
|
|
125
|
+
attributes[:class] += ' hover_click' if hover_via_click?
|
|
126
|
+
[tag, attributes]
|
|
127
|
+
end
|
|
128
|
+
},
|
|
129
|
+
action_link_separator: {
|
|
130
|
+
proc: lambda do |options|
|
|
131
|
+
tag = options[:level_0_tag] || :a if options[:level]&.zero?
|
|
132
|
+
[tag || :li, {class: 'separator'}]
|
|
133
|
+
end
|
|
134
|
+
},
|
|
135
|
+
action_link_group_title: {tag: :div},
|
|
136
|
+
action_link_group_content: {tag: :ul},
|
|
137
|
+
action_link: {}, # a
|
|
138
|
+
action_link_item: {tag: :li},
|
|
139
|
+
dynamic_action_group: {tag: :ul, attributes: {class: 'dynamic-menu'}},
|
|
140
|
+
dynamic_action_group_element: {tag: :li},
|
|
141
|
+
search_form: {}, # form
|
|
142
|
+
search_field: {attributes: {class: 'text-input', size: 50, autocomplete: :off}}, # input type=search
|
|
143
|
+
search_control: {}, # input, select or textarea
|
|
144
|
+
search_submit: {attributes: {class: 'submit'}}, # input type=submit
|
|
145
|
+
search_reset: {attributes: {class: 'reset'}}, # a
|
|
146
|
+
field_search_form: {}, # form
|
|
147
|
+
field_search_fields_columns: {tag: :div, attributes: {class: 'form-columns'}},
|
|
148
|
+
field_search_fields_container: {tag: :ol},
|
|
149
|
+
field_search_subsection: {tag: :li, attributes: {class: 'sub-section'}},
|
|
150
|
+
field_search_element: {tag: :li},
|
|
151
|
+
field_search_submit: {attributes: {class: 'submit'}}, # input type=submit
|
|
152
|
+
field_search_reset: {attributes: {class: 'reset'}}, # a
|
|
153
|
+
field_search_footer: {tag: :p},
|
|
154
|
+
form: {}, # form
|
|
155
|
+
form_title: {tag: :h4},
|
|
156
|
+
form_messages_container: {tag: :div},
|
|
157
|
+
form_fields_columns: {tag: :div, attributes: {class: 'form-columns'}},
|
|
158
|
+
fields_container: {tag: :ol},
|
|
159
|
+
form_subsection: {tag: :li, attributes: {class: 'sub-section'}},
|
|
160
|
+
form_subsection_header: {tag: :h5},
|
|
161
|
+
subform: {tag: :li},
|
|
162
|
+
subform_header: {tag: :h5},
|
|
163
|
+
subform_footer: {tag: :div, attributes: {class: 'footer'}},
|
|
164
|
+
subform_record_remove: {}, # a
|
|
165
|
+
subform_record_remove_reason: {tag: :span, attributes: {class: 'destroy reason'}},
|
|
166
|
+
subform_create_another: {}, # a
|
|
167
|
+
subform_replace_with_new: {}, # a
|
|
168
|
+
subform_add_existing: {}, # a
|
|
169
|
+
form_element: {tag: :li},
|
|
170
|
+
form_attribute: {tag: :dl},
|
|
171
|
+
form_attribute_label: {tag: :dt},
|
|
172
|
+
form_attribute_field: {tag: :dd},
|
|
173
|
+
search_attribute: {tag: :dl},
|
|
174
|
+
search_attribute_label: {tag: :dt},
|
|
175
|
+
search_attribute_field: {tag: :dd},
|
|
176
|
+
form_control: {}, # input, select or textarea
|
|
177
|
+
form_field_description: {tag: :span, attributes: {class: 'description'}},
|
|
178
|
+
form_field_description_close: {tag: :span, attributes: {class: 'close'}},
|
|
179
|
+
form_footer: {tag: :p},
|
|
180
|
+
form_submit: {attributes: {class: 'submit'}}, # input type=submit
|
|
181
|
+
form_apply: {attributes: {class: 'submit'}}, # input type=submit
|
|
182
|
+
form_cancel: {}, # a
|
|
183
|
+
field_add_new_subform: {},
|
|
184
|
+
field_add_new_popup: {},
|
|
185
|
+
show_title: {tag: :h4},
|
|
186
|
+
show_actions: {tag: :div},
|
|
187
|
+
show_columns_block: {tag: :dl},
|
|
188
|
+
show_label_block: {tag: :dt},
|
|
189
|
+
show_columns_group: {tag: :dd},
|
|
190
|
+
show_value_block: {tag: :dd},
|
|
191
|
+
show_footer: {tag: :p, attributes: {class: 'form-footer'}},
|
|
192
|
+
show_cancel: {} # a
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
def self.set_element_tag(name, tag)
|
|
196
|
+
(ui_elements[name] ||= {})[:tag] = tag
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Replace an element's complete definition. Use a block instead of +tag+ and
|
|
200
|
+
# +attributes+ to define a dynamic element.
|
|
201
|
+
def self.set_element(name, tag: nil, attributes: nil, &proc)
|
|
202
|
+
if proc && (tag || attributes)
|
|
203
|
+
raise ArgumentError, 'tag and attributes cannot be used with a proc'
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
ui_elements[name] = if proc
|
|
207
|
+
{proc: proc}
|
|
208
|
+
else
|
|
209
|
+
{}.tap do |element|
|
|
210
|
+
element[:tag] = tag if tag
|
|
211
|
+
element[:attributes] = attributes.as_html_attrs if attributes
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def self.set_element_proc(name, &block)
|
|
217
|
+
(ui_elements[name] ||= {})[:proc] = block
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def self.set_element_attributes(name, attributes)
|
|
221
|
+
(ui_elements[name] ||= {})[:attributes] = attributes.as_html_attrs
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def self.add_element_attributes(name, attributes)
|
|
225
|
+
attrs = (ui_elements[name] ||= {})[:attributes] || {}
|
|
226
|
+
ui_elements[name][:attributes] = attrs.smart_merge(attributes)
|
|
227
|
+
end
|
|
228
|
+
|
|
61
229
|
def self.threadsafe!; end
|
|
62
230
|
|
|
63
231
|
mattr_writer :jquery_ui_loaded, instance_writer: false
|
|
@@ -106,3 +274,4 @@ require 'active_scaffold/engine'
|
|
|
106
274
|
require 'ice_nine'
|
|
107
275
|
require 'ice_nine/core_ext/object'
|
|
108
276
|
require 'request_store'
|
|
277
|
+
require 'html_attrs'
|
|
@@ -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
|
data/lib/tasks/bundle.rake
CHANGED
|
@@ -16,7 +16,8 @@ namespace :bundle do
|
|
|
16
16
|
gemfiles.each do |gemfile|
|
|
17
17
|
puts "Locking #{File.basename(gemfile)}..."
|
|
18
18
|
Bundler.with_unbundled_env do
|
|
19
|
-
|
|
19
|
+
command = ['bundle', 'lock', "--gemfile=#{gemfile}", '--add-platform', 'ruby']
|
|
20
|
+
system(*command, exception: true)
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :mock_app do
|
|
4
|
+
desc 'Build the mock app stylesheets'
|
|
5
|
+
task :assets do
|
|
6
|
+
environment = {
|
|
7
|
+
'BUNDLE_GEMFILE' => File.expand_path(ENV.fetch('BUNDLE_GEMFILE', 'Gemfile'))
|
|
8
|
+
}
|
|
9
|
+
Dir.chdir('test/mock_app') do
|
|
10
|
+
system(environment, 'rails', 'dartsass:build', exception: true)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc 'Start the mock_app server and Dart Sass watcher (default port 3000)'
|
|
15
|
+
task :server do
|
|
16
|
+
args = ARGV.drop_while { |a| a != '--' }.drop(1)
|
|
17
|
+
Dir.chdir('test/mock_app') do
|
|
18
|
+
environment = {'BUNDLE_GEMFILE' => File.expand_path('../../Gemfile', __dir__)}
|
|
19
|
+
system(environment, 'rails', 'dartsass:build', exception: true)
|
|
20
|
+
watcher = Process.spawn(environment, 'rails', 'dartsass:watch')
|
|
21
|
+
_pid, status = Process.wait2(Process.spawn(environment, 'rails', 'server', *args))
|
|
22
|
+
exit status.exitstatus
|
|
23
|
+
ensure
|
|
24
|
+
begin
|
|
25
|
+
if watcher
|
|
26
|
+
Process.kill('TERM', watcher)
|
|
27
|
+
Process.wait(watcher)
|
|
28
|
+
end
|
|
29
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
|
30
|
+
# The watcher already exited.
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc 'Start the mock_app console'
|
|
36
|
+
task :console do
|
|
37
|
+
args = ARGV.drop_while { |a| a != '--' }.drop(1)
|
|
38
|
+
Dir.chdir('test/mock_app') do
|
|
39
|
+
exec({'BUNDLE_GEMFILE' => File.expand_path('../../Gemfile', __dir__)},
|
|
40
|
+
'rails', 'console', *args)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|