render_async 2.1.5 → 2.1.10
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/.all-contributorsrc +38 -0
- data/.github/CONTRIBUTING.md +40 -0
- data/.gitignore +1 -0
- data/.gitmodules +4 -0
- data/CHANGELOG.md +57 -1
- data/README.md +316 -72
- data/app/views/render_async/_render_async.html.erb +19 -25
- data/app/views/render_async/_request_jquery.js.erb +90 -21
- data/app/views/render_async/_request_vanilla.js.erb +84 -16
- data/bin/integration-tests +7 -0
- data/lib/render_async/configuration.rb +4 -1
- data/lib/render_async/version.rb +1 -1
- data/lib/render_async/view_helper.rb +36 -8
- metadata +7 -7
@@ -5,38 +5,32 @@
|
|
5
5
|
<%= placeholder %>
|
6
6
|
</<%= html_element_name %>>
|
7
7
|
|
8
|
-
<% content_for
|
8
|
+
<% content_for content_for_name do %>
|
9
9
|
<%= javascript_tag html_options do %>
|
10
|
+
<% locals = { container_id: container_id,
|
11
|
+
replace_container: replace_container,
|
12
|
+
path: path,
|
13
|
+
method: method,
|
14
|
+
data: data,
|
15
|
+
event_name: event_name,
|
16
|
+
toggle: toggle,
|
17
|
+
headers: headers,
|
18
|
+
error_message: error_message,
|
19
|
+
error_event_name: error_event_name,
|
20
|
+
retry_count: retry_count,
|
21
|
+
retry_delay: retry_delay,
|
22
|
+
interval: interval,
|
23
|
+
turbolinks: RenderAsync.configuration.turbolinks,
|
24
|
+
turbo: RenderAsync.configuration.turbo} %>
|
25
|
+
|
10
26
|
<% if RenderAsync.configuration.jquery %>
|
11
27
|
<%= render partial: 'render_async/request_jquery',
|
12
28
|
formats: [:js],
|
13
|
-
locals:
|
14
|
-
path: path,
|
15
|
-
method: method,
|
16
|
-
data: data,
|
17
|
-
event_name: event_name,
|
18
|
-
toggle: toggle,
|
19
|
-
headers: headers,
|
20
|
-
error_message: error_message,
|
21
|
-
error_event_name: error_event_name,
|
22
|
-
retry_count: retry_count,
|
23
|
-
interval: interval,
|
24
|
-
turbolinks: RenderAsync.configuration.turbolinks } %>
|
29
|
+
locals: locals %>
|
25
30
|
<% else %>
|
26
31
|
<%= render partial: 'render_async/request_vanilla',
|
27
32
|
formats: [:js],
|
28
|
-
locals:
|
29
|
-
path: path,
|
30
|
-
method: method,
|
31
|
-
data: data,
|
32
|
-
event_name: event_name,
|
33
|
-
toggle: toggle,
|
34
|
-
headers: headers,
|
35
|
-
error_message: error_message,
|
36
|
-
error_event_name: error_event_name,
|
37
|
-
retry_count: retry_count,
|
38
|
-
interval: interval,
|
39
|
-
turbolinks: RenderAsync.configuration.turbolinks } %>
|
33
|
+
locals: locals %>
|
40
34
|
<% end %>
|
41
35
|
<% end %>
|
42
36
|
<% end %>
|
@@ -5,6 +5,11 @@ if (window.jQuery) {
|
|
5
5
|
return;
|
6
6
|
}
|
7
7
|
<% end %>
|
8
|
+
<% if turbo %>
|
9
|
+
if (document.documentElement.hasAttribute("data-turbo-preview")) {
|
10
|
+
return;
|
11
|
+
}
|
12
|
+
<% end %>
|
8
13
|
function createEvent(name, container) {
|
9
14
|
var event = undefined;
|
10
15
|
if (typeof(Event) === 'function') {
|
@@ -18,11 +23,18 @@ if (window.jQuery) {
|
|
18
23
|
}
|
19
24
|
|
20
25
|
function _runAfterDocumentLoaded(callback) {
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
27
|
+
// Handle a case where nested partials get loaded after the document loads
|
28
|
+
callback();
|
29
|
+
} else {
|
30
|
+
<% if turbolinks %>
|
31
|
+
$(document).one('turbolinks:load', callback);
|
32
|
+
<% elsif turbo %>
|
33
|
+
$(document).one('turbo:load', callback);
|
34
|
+
<% else %>
|
35
|
+
$(document).ready(callback);
|
36
|
+
<% end %>
|
37
|
+
}
|
26
38
|
}
|
27
39
|
|
28
40
|
function _makeRequest(currentRetryCount) {
|
@@ -38,11 +50,15 @@ if (window.jQuery) {
|
|
38
50
|
headers: headers
|
39
51
|
}).done(function(response) {
|
40
52
|
var container = $("#<%= container_id %>");
|
41
|
-
|
53
|
+
|
54
|
+
// If user navigated away before the request completed
|
55
|
+
if (!container.length) return;
|
56
|
+
|
57
|
+
<% if !interval && replace_container %>
|
58
|
+
container.replaceWith(response);
|
59
|
+
<% else %>
|
42
60
|
container.empty();
|
43
61
|
container.append(response);
|
44
|
-
<% else %>
|
45
|
-
container.replaceWith(response);
|
46
62
|
<% end %>
|
47
63
|
|
48
64
|
var loadEvent = createEvent('render_async_load', container);
|
@@ -61,48 +77,97 @@ if (window.jQuery) {
|
|
61
77
|
if (skipErrorMessage) return;
|
62
78
|
|
63
79
|
var container = $("#<%= container_id %>");
|
80
|
+
if (!container.length) return;
|
81
|
+
|
64
82
|
container.replaceWith("<%= error_message.try(:html_safe) %>");
|
65
83
|
|
66
|
-
var errorEvent = createEvent(
|
67
|
-
|
68
|
-
|
84
|
+
var errorEvent = createEvent(
|
85
|
+
"<%= error_event_name || 'render_async_error' %>",
|
86
|
+
container
|
87
|
+
)
|
88
|
+
errorEvent.retryCount = currentRetryCount
|
69
89
|
|
70
|
-
|
71
|
-
var event = createEvent("<%= error_event_name %>", container)
|
72
|
-
document.dispatchEvent(event);
|
73
|
-
<% end %>
|
90
|
+
document.dispatchEvent(errorEvent);
|
74
91
|
});
|
75
92
|
};
|
76
93
|
|
77
94
|
<% if retry_count > 0 %>
|
95
|
+
var _retryMakeRequest = _makeRequest
|
96
|
+
|
97
|
+
<% if retry_delay %>
|
98
|
+
_retryMakeRequest = function(currentRetryCount) {
|
99
|
+
setTimeout(function() {
|
100
|
+
_makeRequest(currentRetryCount)
|
101
|
+
}, <%= retry_delay %>)
|
102
|
+
}
|
103
|
+
<% end %>
|
104
|
+
|
78
105
|
function retry(currentRetryCount) {
|
79
106
|
if (typeof(currentRetryCount) === 'number') {
|
80
107
|
if (currentRetryCount >= <%= retry_count %>)
|
81
108
|
return false;
|
82
109
|
|
83
|
-
|
110
|
+
_retryMakeRequest(currentRetryCount + 1);
|
84
111
|
return true;
|
85
112
|
}
|
86
113
|
|
87
|
-
|
114
|
+
_retryMakeRequest(1);
|
88
115
|
return true;
|
89
116
|
}
|
90
117
|
<% end %>
|
91
118
|
|
92
119
|
var _renderAsyncFunction = _makeRequest;
|
93
120
|
|
94
|
-
<% if interval %>
|
95
121
|
var _interval;
|
122
|
+
<% if interval %>
|
96
123
|
var _renderAsyncFunction = function() {
|
124
|
+
// If interval is already set, return
|
125
|
+
if (typeof(_interval) === 'number') return
|
126
|
+
|
97
127
|
_makeRequest();
|
98
128
|
_interval = setInterval(_makeRequest, <%= interval %>);
|
99
129
|
}
|
130
|
+
|
131
|
+
var _clearRenderAsyncInterval = function() {
|
132
|
+
if (typeof(_interval) === 'number'){
|
133
|
+
clearInterval(_interval)
|
134
|
+
_interval = undefined;
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
function _setUpControlEvents() {
|
139
|
+
var container = $("#<%= container_id %>");
|
140
|
+
|
141
|
+
// Register a stop polling event on the container
|
142
|
+
$(container).on('async-stop', _clearRenderAsyncInterval)
|
143
|
+
|
144
|
+
// Register a start polling event on the container
|
145
|
+
$(container).on('async-start', _renderAsyncFunction)
|
146
|
+
}
|
147
|
+
|
148
|
+
_runAfterDocumentLoaded(_setUpControlEvents)
|
149
|
+
|
150
|
+
<% if turbolinks %>
|
151
|
+
$(document).one('turbolinks:visit', _clearRenderAsyncInterval);
|
152
|
+
<% end %>
|
153
|
+
<% if turbo %>
|
154
|
+
$(document).one('turbo:visit', _clearRenderAsyncInterval);
|
155
|
+
<% end %>
|
156
|
+
<% end %>
|
157
|
+
|
158
|
+
<% if !replace_container %>
|
159
|
+
function _setUpRefreshEvent() {
|
160
|
+
var container = $("#<%= container_id %>");
|
161
|
+
|
162
|
+
$(container).on('refresh', _renderAsyncFunction)
|
163
|
+
}
|
164
|
+
|
165
|
+
_runAfterDocumentLoaded(_setUpRefreshEvent)
|
100
166
|
<% end %>
|
101
167
|
|
102
168
|
<% if toggle %>
|
103
169
|
function _setUpToggle() {
|
104
170
|
$(document).<%= toggle[:once] ? 'one' : 'on' %>('<%= toggle[:event] || 'click' %>', '<%= toggle[:selector] %>', function(event) {
|
105
|
-
event.preventDefault();
|
106
171
|
if (typeof(_interval) === 'number') {
|
107
172
|
clearInterval(_interval);
|
108
173
|
_interval = undefined;
|
@@ -110,13 +175,17 @@ if (window.jQuery) {
|
|
110
175
|
_renderAsyncFunction();
|
111
176
|
}
|
112
177
|
});
|
178
|
+
|
179
|
+
<% if toggle[:start] %>
|
180
|
+
_renderAsyncFunction()
|
181
|
+
<% end %>
|
113
182
|
}
|
114
183
|
|
115
|
-
_runAfterDocumentLoaded(_setUpToggle)
|
184
|
+
_runAfterDocumentLoaded(_setUpToggle);
|
116
185
|
<% elsif !toggle %>
|
117
186
|
_runAfterDocumentLoaded(_renderAsyncFunction)
|
118
187
|
<% end %>
|
119
188
|
}(jQuery));
|
120
189
|
} else {
|
121
|
-
console.warn("Looks like you've enabled jQuery for render_async, but jQuery is not defined");
|
190
|
+
console.warn("Looks like you've enabled jQuery for render_async, but jQuery is not defined on the window object");
|
122
191
|
};
|
@@ -4,6 +4,11 @@
|
|
4
4
|
return;
|
5
5
|
}
|
6
6
|
<% end %>
|
7
|
+
<% if turbo %>
|
8
|
+
if (document.documentElement.hasAttribute("data-turbo-preview")) {
|
9
|
+
return;
|
10
|
+
}
|
11
|
+
<% end %>
|
7
12
|
function createEvent(name, container) {
|
8
13
|
var event = undefined;
|
9
14
|
if (typeof(Event) === 'function') {
|
@@ -22,6 +27,11 @@
|
|
22
27
|
e.target.removeEventListener(e.type, arguments.callee);
|
23
28
|
callback();
|
24
29
|
});
|
30
|
+
<% elsif turbo %>
|
31
|
+
document.addEventListener("turbo:load", function(e) {
|
32
|
+
e.target.removeEventListener(e.type, arguments.callee);
|
33
|
+
callback();
|
34
|
+
});
|
25
35
|
<% else %>
|
26
36
|
document.addEventListener("DOMContentLoaded", callback);
|
27
37
|
<% end %>
|
@@ -44,14 +54,20 @@
|
|
44
54
|
request.setRequestHeader(key, headers[key]);
|
45
55
|
});
|
46
56
|
|
57
|
+
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
58
|
+
|
47
59
|
request.onreadystatechange = function() {
|
48
60
|
if (request.readyState === 4) {
|
49
61
|
if (request.status >= SUCCESS && request.status < ERROR) {
|
50
62
|
var container = document.getElementById('<%= container_id %>');
|
51
|
-
|
52
|
-
|
53
|
-
|
63
|
+
|
64
|
+
// If user navigated away before the request completed
|
65
|
+
if (!container) return;
|
66
|
+
|
67
|
+
<% if !interval && replace_container %>
|
54
68
|
container.outerHTML = request.response;
|
69
|
+
<% else %>
|
70
|
+
container.innerHTML = request.response;
|
55
71
|
<% end %>
|
56
72
|
|
57
73
|
var loadEvent = createEvent('render_async_load', container);
|
@@ -70,16 +86,17 @@
|
|
70
86
|
if (skipErrorMessage) return;
|
71
87
|
|
72
88
|
var container = document.getElementById('<%= container_id %>');
|
89
|
+
if (!container) return;
|
90
|
+
|
73
91
|
container.outerHTML = '<%= error_message.try(:html_safe) %>';
|
74
92
|
|
75
|
-
|
76
|
-
|
77
|
-
|
93
|
+
var errorEvent = createEvent(
|
94
|
+
"<%= error_event_name || 'render_async_error' %>",
|
95
|
+
container
|
96
|
+
);
|
97
|
+
errorEvent.retryCount = currentRetryCount
|
78
98
|
|
79
|
-
|
80
|
-
var event = createEvent('<%= error_event_name %>', container);
|
81
|
-
document.dispatchEvent(event);
|
82
|
-
<% end %>
|
99
|
+
document.dispatchEvent(errorEvent);
|
83
100
|
}
|
84
101
|
}
|
85
102
|
};
|
@@ -89,35 +106,82 @@
|
|
89
106
|
};
|
90
107
|
|
91
108
|
<% if retry_count > 0 %>
|
109
|
+
|
110
|
+
<% if retry_delay %>
|
111
|
+
_retryMakeRequest = function(currentRetryCount) {
|
112
|
+
setTimeout(function() {
|
113
|
+
_makeRequest(currentRetryCount)
|
114
|
+
}, <%= retry_delay %>)
|
115
|
+
}
|
116
|
+
<% end %>
|
117
|
+
|
92
118
|
function retry(currentRetryCount) {
|
93
119
|
if (typeof(currentRetryCount) === 'number') {
|
94
120
|
if (currentRetryCount >= <%= retry_count %>)
|
95
121
|
return false;
|
96
122
|
|
97
|
-
|
123
|
+
_retryMakeRequest(currentRetryCount + 1);
|
98
124
|
return true;
|
99
125
|
}
|
100
126
|
|
101
|
-
|
127
|
+
_retryMakeRequest(1);
|
102
128
|
return true;
|
103
129
|
}
|
104
130
|
<% end %>
|
105
131
|
|
106
132
|
var _renderAsyncFunction = _makeRequest;
|
107
133
|
|
108
|
-
<% if interval %>
|
109
134
|
var _interval;
|
135
|
+
<% if interval %>
|
110
136
|
var _renderAsyncFunction = function() {
|
137
|
+
// If interval is already set, return
|
138
|
+
if (typeof(_interval) === 'number') return
|
139
|
+
|
111
140
|
_makeRequest();
|
112
141
|
_interval = setInterval(_makeRequest, <%= interval %>);
|
113
142
|
}
|
143
|
+
|
144
|
+
var _clearRenderAsyncInterval = function() {
|
145
|
+
if (typeof(_interval) === 'number'){
|
146
|
+
clearInterval(_interval)
|
147
|
+
_interval = undefined;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
function _setUpControlEvents() {
|
152
|
+
var container = document.getElementById('<%= container_id %>');
|
153
|
+
|
154
|
+
// Register a polling stop event on the container
|
155
|
+
container.addEventListener("async-stop", _clearRenderAsyncInterval)
|
156
|
+
|
157
|
+
// Register a start polling event on the container
|
158
|
+
container.addEventListener("async-start", _renderAsyncFunction)
|
159
|
+
}
|
160
|
+
|
161
|
+
_runAfterDocumentLoaded(_setUpControlEvents)
|
162
|
+
|
163
|
+
<% if turbolinks %>
|
164
|
+
document.addEventListener("turbolinks:visit", _clearRenderAsyncInterval)
|
165
|
+
<% end %>
|
166
|
+
<% if turbo %>
|
167
|
+
document.addEventListener("turbo:visit", _clearRenderAsyncInterval)
|
168
|
+
<% end %>
|
169
|
+
<% end %>
|
170
|
+
|
171
|
+
<% if !replace_container %>
|
172
|
+
function _setUpRefreshEvent() {
|
173
|
+
var container = document.getElementById('<%= container_id %>');
|
174
|
+
|
175
|
+
container.addEventListener('refresh', _renderAsyncFunction)
|
176
|
+
}
|
177
|
+
|
178
|
+
_runAfterDocumentLoaded(_setUpRefreshEvent)
|
114
179
|
<% end %>
|
115
180
|
|
116
181
|
<% if toggle %>
|
117
182
|
function _setUpToggle() {
|
118
|
-
var selectors = document.querySelectorAll('<%= toggle[:selector] %>')
|
183
|
+
var selectors = document.querySelectorAll('<%= toggle[:selector] %>');
|
119
184
|
var handler = function(event) {
|
120
|
-
event.preventDefault();
|
121
185
|
if (typeof(_interval) === 'number') {
|
122
186
|
clearInterval(_interval);
|
123
187
|
_interval = undefined;
|
@@ -129,7 +193,11 @@
|
|
129
193
|
<% end %>
|
130
194
|
};
|
131
195
|
|
132
|
-
|
196
|
+
<% if toggle[:start] %>
|
197
|
+
_renderAsyncFunction()
|
198
|
+
<% end %>
|
199
|
+
|
200
|
+
for (var i = 0; i < selectors.length; ++i) {
|
133
201
|
selectors[i].addEventListener('<%= toggle[:event] || 'click' %>', handler)
|
134
202
|
}
|
135
203
|
}
|
data/bin/integration-tests
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
module RenderAsync
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :jquery, :turbolinks
|
3
|
+
attr_accessor :jquery, :turbolinks, :turbo, :replace_container, :nonces
|
4
4
|
|
5
5
|
def initialize
|
6
6
|
@jquery = false
|
7
7
|
@turbolinks = false
|
8
|
+
@turbo = false
|
9
|
+
@replace_container = true
|
10
|
+
@nonces = false
|
8
11
|
end
|
9
12
|
end
|
10
13
|
end
|
data/lib/render_async/version.rb
CHANGED
@@ -2,7 +2,6 @@ require 'securerandom'
|
|
2
2
|
|
3
3
|
module RenderAsync
|
4
4
|
module ViewHelper
|
5
|
-
|
6
5
|
def render_async_cache_key(path)
|
7
6
|
"render_async_#{path}"
|
8
7
|
end
|
@@ -20,18 +19,17 @@ module RenderAsync
|
|
20
19
|
def render_async(path, options = {}, &placeholder)
|
21
20
|
event_name = options.delete(:event_name)
|
22
21
|
placeholder = capture(&placeholder) if block_given?
|
23
|
-
retry_count = options.delete(:retry_count) || 0
|
24
|
-
html_options = options.delete(:html_options) || {}
|
25
22
|
|
26
23
|
render 'render_async/render_async', **container_element_options(options),
|
27
24
|
path: path,
|
28
|
-
html_options: html_options,
|
25
|
+
html_options: html_options(options),
|
29
26
|
event_name: event_name,
|
30
27
|
placeholder: placeholder,
|
31
28
|
**request_options(options),
|
32
29
|
**error_handling_options(options),
|
33
|
-
|
34
|
-
**polling_options(options)
|
30
|
+
**retry_options(options),
|
31
|
+
**polling_options(options),
|
32
|
+
**content_for_options(options)
|
35
33
|
end
|
36
34
|
|
37
35
|
private
|
@@ -39,7 +37,16 @@ module RenderAsync
|
|
39
37
|
def container_element_options(options)
|
40
38
|
{ html_element_name: options[:html_element_name] || 'div',
|
41
39
|
container_id: options[:container_id] || generate_container_id,
|
42
|
-
container_class: options[:container_class]
|
40
|
+
container_class: options[:container_class],
|
41
|
+
replace_container: replace_container(options) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def html_options(options)
|
45
|
+
set_options = options.delete(:html_options) || {}
|
46
|
+
|
47
|
+
set_options[:nonce] = configuration.nonces if set_options[:nonce].nil?
|
48
|
+
|
49
|
+
set_options
|
43
50
|
end
|
44
51
|
|
45
52
|
def request_options(options)
|
@@ -53,15 +60,36 @@ module RenderAsync
|
|
53
60
|
error_event_name: options[:error_event_name] }
|
54
61
|
end
|
55
62
|
|
63
|
+
def retry_options(options)
|
64
|
+
{
|
65
|
+
retry_count: options.delete(:retry_count) || 0,
|
66
|
+
retry_delay: options.delete(:retry_delay)
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
56
70
|
def polling_options(options)
|
57
71
|
{ interval: options[:interval],
|
58
72
|
toggle: options[:toggle] }
|
59
73
|
end
|
60
74
|
|
61
|
-
|
75
|
+
def content_for_options(options)
|
76
|
+
{
|
77
|
+
content_for_name: options[:content_for_name] || :render_async
|
78
|
+
}
|
79
|
+
end
|
62
80
|
|
63
81
|
def generate_container_id
|
64
82
|
"render_async_#{SecureRandom.hex(5)}#{Time.now.to_i}"
|
65
83
|
end
|
84
|
+
|
85
|
+
def replace_container(options)
|
86
|
+
return options[:replace_container] unless options[:replace_container].nil?
|
87
|
+
|
88
|
+
configuration.replace_container
|
89
|
+
end
|
90
|
+
|
91
|
+
def configuration
|
92
|
+
RenderAsync.configuration
|
93
|
+
end
|
66
94
|
end
|
67
95
|
end
|