autoforme 1.9.1 → 1.12.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 +26 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +72 -12
- data/autoforme.js +97 -56
- data/lib/autoforme/action.rb +29 -24
- data/lib/autoforme/frameworks/rails.rb +10 -2
- data/lib/autoforme/frameworks/roda.rb +16 -5
- data/lib/autoforme/frameworks/sinatra.rb +9 -2
- data/lib/autoforme/model.rb +1 -1
- data/lib/autoforme/models/sequel.rb +36 -7
- data/lib/autoforme/version.rb +2 -2
- data/lib/autoforme.rb +7 -8
- data/lib/roda/plugins/autoforme.rb +1 -1
- metadata +26 -19
- data/Rakefile +0 -69
- data/spec/all.rb +0 -2
- data/spec/associations_spec.rb +0 -802
- data/spec/basic_spec.rb +0 -1150
- data/spec/mtm_spec.rb +0 -509
- data/spec/rails_spec_helper.rb +0 -78
- data/spec/roda_spec.rb +0 -72
- data/spec/roda_spec_helper.rb +0 -87
- data/spec/sequel_spec_helper.rb +0 -43
- data/spec/sinatra_spec_helper.rb +0 -55
- data/spec/spec_helper.rb +0 -79
- data/spec/unit_spec.rb +0 -511
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08b9c95c4e096845f520e6543543f644d9fc7f558ae6c8674ac508a5a392e18a'
|
4
|
+
data.tar.gz: 51b362ce883daf920aaa4035d1f5cd2073b96c78f54aad0c1411308d08b794d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9ed3dece202d2d48cc6b65746ba979acc57d3b1f67843b4d123d531313162bbf5806c1200ce9bce932eef9dd3aefd7ba1ff6582083b99dc171c8fb90e725c16
|
7
|
+
data.tar.gz: 7ad255b23d6ebbf43b6ff5caf43c7772b40386451aed7cfe456a09707b05b849e41bb763fe58f7df91940987602cf11972f41e9237b7d49c2ca554d55621769d
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
=== 1.12.0 (2022-06-29)
|
2
|
+
|
3
|
+
* Handle autocomplete queries with an empty query by returning empty string (jeremyevans)
|
4
|
+
|
5
|
+
* Switch autocomplete support to depend on Pixabay/JavaScript-autoComplete (jeremyevans)
|
6
|
+
|
7
|
+
* Make autoforme.js file no longer require JQuery (jeremyevans)
|
8
|
+
|
9
|
+
* Make navigation tab li tag use nav-item, for Bootstrap 5 compatibility (jeremyevans)
|
10
|
+
|
11
|
+
=== 1.11.0 (2021-11-30)
|
12
|
+
|
13
|
+
* Require forme 2.0.0 (jeremyevans)
|
14
|
+
|
15
|
+
* Add support for view_options framework option (nmb, jeremyevans) (#42)
|
16
|
+
|
17
|
+
* Drop support for Ruby 1.8 (jeremyevans)
|
18
|
+
|
19
|
+
=== 1.10.0 (2021-08-27)
|
20
|
+
|
21
|
+
* Do not consider read_only many_to_many associations to be editable (jeremyevans)
|
22
|
+
|
23
|
+
* Ignore unique constraint violations when adding associated objects in mtm_update (jeremyevans)
|
24
|
+
|
25
|
+
* Handle search fields that cannot be typecast correctly by returning no results (jeremyevans)
|
26
|
+
|
1
27
|
=== 1.9.1 (2019-07-22)
|
2
28
|
|
3
29
|
* [SECURITY] Escape object display name when displaying association links (adam12)
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -6,12 +6,7 @@ integrate easily into web frameworks, and currently supports
|
|
6
6
|
Roda, Sinatra, and Rails. The only currently supported ORM is
|
7
7
|
Sequel::Model.
|
8
8
|
|
9
|
-
AutoForme's UI and capabilities are modeled on
|
10
|
-
scaffolding_extensions [2], though AutoForme is considerably more
|
11
|
-
flexible in terms of configuration.
|
12
|
-
|
13
9
|
1. https://github.com/jeremyevans/forme
|
14
|
-
2. https://github.com/jeremyevans/scaffolding_extensions
|
15
10
|
|
16
11
|
= Installation
|
17
12
|
|
@@ -22,8 +17,7 @@ flexible in terms of configuration.
|
|
22
17
|
Demo Site :: http://autoforme-demo.jeremyevans.net
|
23
18
|
RDoc :: http://autoforme.jeremyevans.net
|
24
19
|
Source :: https://github.com/jeremyevans/autoforme
|
25
|
-
|
26
|
-
Google Group :: https://groups.google.com/forum/#!forum/ruby-forme
|
20
|
+
Discussion Forum :: https://github.com/jeremyevans/autoforme/discussions
|
27
21
|
Bug Tracker :: https://github.com/jeremyevans/autoforme/issues
|
28
22
|
|
29
23
|
= Features
|
@@ -161,12 +155,13 @@ These options are related to displayed output:
|
|
161
155
|
|
162
156
|
form_attributes :: Hash of attributes to use for any form tags
|
163
157
|
form_options :: Hash of Forme::Form options to pass for any forms created
|
164
|
-
class_display_name :: The string to use when referring to the model class
|
158
|
+
class_display_name :: The string to use on pages when referring to the model class.
|
159
|
+
This defaults to the full class name.
|
165
160
|
display_name :: The string to use when referring to a model instance. Can either be a symbol
|
166
161
|
representing an instance method call, or a Proc called with the model object,
|
167
162
|
the model object and type symbol, or the model object, type symbol, and request,
|
168
163
|
depending on the arity of the Proc.
|
169
|
-
link_name :: The string to use in links for the class
|
164
|
+
link_name :: The string to use in links for the class. This defaults to +class_display_name+.
|
170
165
|
edit_html :: The html to use for a particular object edit field. Should be a proc that takes the
|
171
166
|
model object, column symbol, type symbol, and request and returns the html to use.
|
172
167
|
page_footer :: Override the default footer used for pages
|
@@ -174,6 +169,8 @@ page_header :: Override the default header used for pages
|
|
174
169
|
show_html :: The html to use for displaying the value for an object field. Should be a proc that takes the
|
175
170
|
model object, column symbol, type symbol, and request and returns the html to use.
|
176
171
|
table_class :: The html class string to use for the browse and search tables
|
172
|
+
view_options :: Hash with options passed when rendering the view (how these options are used varies
|
173
|
+
in each of the supported web frameworks), e.g. <tt>view_options: {:layout => :formelayout}</tt>
|
177
174
|
|
178
175
|
These hook options should be callable objects that are called with the model object and the request.
|
179
176
|
|
@@ -212,9 +209,12 @@ AutoForme also has javascript support for the progressive enhancement of the fol
|
|
212
209
|
* Loading association links via ajax if lazy_load_association_links is true.
|
213
210
|
* Adding and removing many-to-many associated objects via ajax for inline_mtm_associations
|
214
211
|
|
215
|
-
AutoForme's javascript support is contained in the autoforme.js file in the root
|
216
|
-
of the
|
217
|
-
https://github.com/
|
212
|
+
AutoForme's javascript support is contained in the +autoforme.js+ file in the root
|
213
|
+
of the repository. AutoForme's autocompleting support also requires
|
214
|
+
https://github.com/Pixabay/JavaScript-autoComplete
|
215
|
+
|
216
|
+
Make sure to load the +autoforme.js+ file after the DOM content has been loaded, such
|
217
|
+
as at the end of the body.
|
218
218
|
|
219
219
|
= Reloading Code
|
220
220
|
|
@@ -238,6 +238,66 @@ use AutoForme in Rails development mode. The best way to handle it is to call
|
|
238
238
|
+AutoForme.for+ in the related controller file, and have an initializer reference
|
239
239
|
the controller class, causing the controller file to be loaded.
|
240
240
|
|
241
|
+
= Roda
|
242
|
+
|
243
|
+
Because Roda uses a routing tree, unlike Rails and Sinatra, with Roda you need to
|
244
|
+
dispatch to the autoforme routes at the point in the routing tree where you want
|
245
|
+
to mount them. Additionally, the Roda support offers a Roda plugin for easier
|
246
|
+
configuration.
|
247
|
+
|
248
|
+
To mount the autoforme routes in the root of the application, you could do:
|
249
|
+
|
250
|
+
class App < Roda
|
251
|
+
plugin :autoforme do
|
252
|
+
model Artist
|
253
|
+
end
|
254
|
+
|
255
|
+
route do
|
256
|
+
# rest of routing tree
|
257
|
+
autoforme
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
To mount the routes in a subpath:
|
262
|
+
|
263
|
+
class App < Roda
|
264
|
+
plugin :autoforme do
|
265
|
+
model Artist
|
266
|
+
end
|
267
|
+
|
268
|
+
route do
|
269
|
+
r.on "admin" do
|
270
|
+
autoforme
|
271
|
+
end
|
272
|
+
|
273
|
+
# rest of routing tree
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
To handle multiple autoforme configurations, mounted at different subpaths:
|
278
|
+
|
279
|
+
class App < Roda
|
280
|
+
plugin :autoforme
|
281
|
+
|
282
|
+
autoforme(name: 'artists') do
|
283
|
+
model Artist
|
284
|
+
end
|
285
|
+
autoforme(name: 'albums') do
|
286
|
+
model Album
|
287
|
+
end
|
288
|
+
|
289
|
+
route do
|
290
|
+
r.on "artists" do
|
291
|
+
autoforme('artists')
|
292
|
+
end
|
293
|
+
r.on "albums" do
|
294
|
+
autoforme('albums')
|
295
|
+
end
|
296
|
+
|
297
|
+
# rest of routing tree
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
241
301
|
= TODO
|
242
302
|
|
243
303
|
* capybara-webkit tests for ajax behavior
|
data/autoforme.js
CHANGED
@@ -1,81 +1,122 @@
|
|
1
|
-
|
2
|
-
var autoforme =
|
3
|
-
|
1
|
+
(function() {
|
2
|
+
var autoforme = document.querySelector('#autoforme_content');
|
3
|
+
if (!autoforme) {
|
4
|
+
return;
|
5
|
+
}
|
6
|
+
var base_url = autoforme.getAttribute('data-url');
|
7
|
+
var xhr_headers = {'X-Requested-With': 'XMLHttpRequest'};
|
4
8
|
|
5
|
-
function autoforme_fix_autocomplete(
|
6
|
-
|
7
|
-
|
9
|
+
function autoforme_fix_autocomplete(form) {
|
10
|
+
form.querySelectorAll('.autoforme_autocomplete').forEach((e) => {
|
11
|
+
e.value = e.value.split(' - ', 2)[0];
|
8
12
|
});
|
9
13
|
}
|
10
14
|
|
11
|
-
function autoforme_setup_autocomplete() {
|
12
|
-
|
13
|
-
var
|
14
|
-
var
|
15
|
-
var exclude = e.data('exclude');
|
15
|
+
function autoforme_setup_autocomplete(content) {
|
16
|
+
content.querySelectorAll('.autoforme_autocomplete').forEach((e) => {
|
17
|
+
var column = e.getAttribute('data-column');
|
18
|
+
var exclude = e.getAttribute('data-exclude');
|
16
19
|
var url = base_url + 'autocomplete';
|
17
20
|
if (column) {
|
18
21
|
url += '/' + column;
|
19
22
|
}
|
20
|
-
url += '?type=' + e.
|
23
|
+
url += '?type=' + e.getAttribute('data-type');
|
21
24
|
if (exclude) {
|
22
25
|
url += '&exclude=' + exclude;
|
23
26
|
}
|
24
|
-
|
27
|
+
|
28
|
+
new autoComplete({
|
29
|
+
selector: '[id="'+e.getAttribute("id")+'"]',
|
30
|
+
source: function(term, suggest) {
|
31
|
+
fetch((url + '&q=' + term), {headers: xhr_headers}).
|
32
|
+
then(function(response) {
|
33
|
+
return response.text();
|
34
|
+
}).
|
35
|
+
then(function(body) {
|
36
|
+
suggest(body.split("\n"));
|
37
|
+
});
|
38
|
+
}
|
39
|
+
});
|
25
40
|
});
|
26
41
|
}
|
27
42
|
|
28
|
-
autoforme_setup_autocomplete();
|
43
|
+
autoforme_setup_autocomplete(autoforme);
|
29
44
|
|
30
|
-
autoforme.
|
31
|
-
|
45
|
+
autoforme.querySelectorAll("form").forEach((form) => {
|
46
|
+
form.addEventListener('submit', function(){
|
47
|
+
autoforme_fix_autocomplete(form);
|
48
|
+
}, {passive: true});
|
32
49
|
});
|
33
50
|
|
51
|
+
var lazy_load_association_links = autoforme.querySelector('#lazy_load_association_links');
|
52
|
+
if (lazy_load_association_links) {
|
53
|
+
load_association_links = function(e) {
|
54
|
+
e.preventDefault();
|
55
|
+
lazy_load_association_links.removeEventListener('click', load_association_links);
|
34
56
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
57
|
+
var url = base_url + "association_links/" + load_association_links.getAttribute('data-object') + "?type=" + load_association_links.getAttribute('data-type');
|
58
|
+
fetch(url, {headers: xhr_headers}).
|
59
|
+
then(function(response) {
|
60
|
+
return response.text();
|
61
|
+
}).
|
62
|
+
then(function(body) {
|
63
|
+
lazy_load_association_links.innerHTML = body;
|
64
|
+
autoforme_setup_autocomplete(lazy_load_association_links);
|
65
|
+
});
|
66
|
+
};
|
67
|
+
lazy_load_association_links.addEventListener('click', load_association_links);
|
68
|
+
}
|
41
69
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
$(form.data('remove')).append(data);
|
70
|
+
setup_remove_hooks = function() {
|
71
|
+
autoforme.querySelectorAll('.inline_mtm_remove_associations form.mtm_remove_associations').forEach((form) => {
|
72
|
+
if (form.remove_hook_setup) {return;}
|
73
|
+
form.remove_hook_setup = true;
|
74
|
+
|
75
|
+
form.addEventListener('submit', function(e){
|
76
|
+
fetch(form.action, {method: 'post', body: new FormData(form), headers: xhr_headers}).
|
77
|
+
then(function(response) {
|
78
|
+
return response.text();
|
79
|
+
}).
|
80
|
+
then(function(body) {
|
81
|
+
document.querySelector(form.getAttribute('data-add')).insertAdjacentHTML('beforeend', body);
|
82
|
+
form.parentElement.remove();
|
83
|
+
});
|
84
|
+
e.preventDefault();
|
58
85
|
});
|
59
|
-
}
|
60
|
-
|
61
|
-
|
86
|
+
});
|
87
|
+
};
|
88
|
+
setup_remove_hooks();
|
89
|
+
|
90
|
+
autoforme.querySelectorAll('.mtm_add_associations').forEach((form) => {
|
91
|
+
form.addEventListener('submit', function(e){
|
92
|
+
var form_ac = form.querySelector('.autoforme_autocomplete');
|
93
|
+
if (!form_ac) {
|
94
|
+
var select = form.querySelector('select');
|
62
95
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
96
|
+
fetch(form.action, {method: 'post', body: new FormData(form), headers: xhr_headers}).
|
97
|
+
then(function(response) {
|
98
|
+
return response.text();
|
99
|
+
}).
|
100
|
+
then(function(body) {
|
101
|
+
select.remove(select.selectedIndex);
|
102
|
+
select.selectedIndex = 0;
|
103
|
+
document.querySelector(form.getAttribute('data-remove')).insertAdjacentHTML('beforeend', body);
|
104
|
+
setup_remove_hooks();
|
105
|
+
});
|
70
106
|
} else {
|
71
|
-
|
107
|
+
autoforme_fix_autocomplete(form);
|
108
|
+
|
109
|
+
fetch(form.action, {method: 'post', body: new FormData(form), headers: xhr_headers}).
|
110
|
+
then(function(response) {
|
111
|
+
return response.text();
|
112
|
+
}).
|
113
|
+
then(function(body) {
|
114
|
+
form_ac.value = '';
|
115
|
+
document.querySelector(form.getAttribute('data-remove')).insertAdjacentHTML('beforeend', body);
|
116
|
+
setup_remove_hooks();
|
117
|
+
});
|
72
118
|
}
|
73
|
-
|
119
|
+
e.preventDefault();
|
74
120
|
});
|
75
|
-
e.preventDefault();
|
76
121
|
});
|
77
|
-
};
|
78
|
-
|
79
|
-
|
80
|
-
$(document).ready(ready);
|
81
|
-
$(document).on('page:load', ready);
|
122
|
+
})();
|
data/lib/autoforme/action.rb
CHANGED
@@ -87,9 +87,7 @@ module AutoForme
|
|
87
87
|
else
|
88
88
|
return false unless model.supported_action?(normalized_type, request)
|
89
89
|
|
90
|
-
|
91
|
-
@title = "#{model.class_name} - #{title}"
|
92
|
-
end
|
90
|
+
@title = "#{model.class_name} - #{TITLE_MAP[@normalized_type]}"
|
93
91
|
end
|
94
92
|
|
95
93
|
true
|
@@ -134,7 +132,7 @@ module AutoForme
|
|
134
132
|
type.to_s
|
135
133
|
when :edit
|
136
134
|
"edit/#{model.primary_key_value(obj)}"
|
137
|
-
when :mtm_edit
|
135
|
+
else # when :mtm_edit
|
138
136
|
"mtm_edit/#{model.primary_key_value(obj)}?association=#{params_association}"
|
139
137
|
end
|
140
138
|
path = url_for(path)
|
@@ -195,7 +193,7 @@ module AutoForme
|
|
195
193
|
content << '<ul class="nav nav-tabs">'
|
196
194
|
Model::DEFAULT_SUPPORTED_ACTIONS.each do |action_type|
|
197
195
|
if model.supported_action?(action_type, request)
|
198
|
-
content << "<li class=\"#{'active' if type == action_type}\"><a href=\"#{url_for(action_type)}\">#{tab_name(action_type)}</a></li>"
|
196
|
+
content << "<li class=\"nav-item #{'active' if type == action_type}\"><a class=\"nav-link\" href=\"#{url_for(action_type)}\">#{tab_name(action_type)}</a></li>"
|
199
197
|
end
|
200
198
|
end
|
201
199
|
content << '</ul>'
|
@@ -227,10 +225,15 @@ module AutoForme
|
|
227
225
|
# Options to use for the form. If the form uses POST, automatically adds the CSRF token.
|
228
226
|
def form_opts(action=nil)
|
229
227
|
opts = model.form_options_for(type, request).dup
|
230
|
-
|
231
|
-
|
232
|
-
|
228
|
+
|
229
|
+
opts[:_before_post] = lambda do |form|
|
230
|
+
if csrf_hash = request.csrf_token_hash(action)
|
231
|
+
csrf_hash.each do |name, value|
|
232
|
+
form.tag(:input, :type=>:hidden, :name=>name, :value=>value)
|
233
|
+
end
|
234
|
+
end
|
233
235
|
end
|
236
|
+
|
234
237
|
opts
|
235
238
|
end
|
236
239
|
|
@@ -310,19 +313,19 @@ module AutoForme
|
|
310
313
|
if html = model.show_html_for(obj, column, type, request)
|
311
314
|
col_opts = col_opts.merge(:html=>html)
|
312
315
|
end
|
313
|
-
t << f.input(column, col_opts)
|
316
|
+
t << f.input(column, col_opts)
|
314
317
|
end
|
315
318
|
t << '</table>'
|
316
319
|
if type == :show && model.supported_action?(:edit, request)
|
317
320
|
t << Forme.form(form_attributes(:action=>url_for("edit/#{model.primary_key_value(obj)}")), form_opts) do |f1|
|
318
321
|
f1.button(:value=>'Edit', :class=>'btn btn-primary')
|
319
|
-
end
|
322
|
+
end
|
320
323
|
end
|
321
324
|
if type == :delete
|
322
325
|
form_attr = form_attributes(:action=>url_for("destroy/#{model.primary_key_value(obj)}"), :method=>:post)
|
323
326
|
t << Forme.form(form_attr, form_opts(form_attr[:action])) do |f1|
|
324
327
|
f1.button(:value=>'Delete', :class=>'btn btn-danger')
|
325
|
-
end
|
328
|
+
end
|
326
329
|
else
|
327
330
|
t << association_links(obj)
|
328
331
|
end
|
@@ -353,11 +356,11 @@ module AutoForme
|
|
353
356
|
f.input(column, col_opts)
|
354
357
|
end
|
355
358
|
f.button(:value=>'Update', :class=>'btn btn-primary')
|
356
|
-
end
|
359
|
+
end
|
357
360
|
if model.supported_action?(:delete, request)
|
358
361
|
t << Forme.form(form_attributes(:action=>url_for("delete/#{model.primary_key_value(obj)}")), form_opts) do |f|
|
359
362
|
f.button(:value=>'Delete', :class=>'btn btn-danger')
|
360
|
-
end
|
363
|
+
end
|
361
364
|
end
|
362
365
|
t << association_links(obj)
|
363
366
|
end
|
@@ -487,15 +490,15 @@ module AutoForme
|
|
487
490
|
opts = model.column_options_for(:mtm_edit, request, assoc)
|
488
491
|
add_opts = opts[:add] ? opts.merge(opts.delete(:add)) : opts
|
489
492
|
remove_opts = opts[:remove] ? opts.merge(opts.delete(:remove)) : opts
|
490
|
-
add_opts = {:name=>'add[]', :id=>'add', :label=>'Associate With'}.merge(add_opts)
|
493
|
+
add_opts = {:name=>'add[]', :id=>'add', :label=>'Associate With'}.merge!(add_opts)
|
491
494
|
if model.association_autocomplete?(assoc, request)
|
492
|
-
f.input(assoc, {:type=>'text', :class=>'autoforme_autocomplete', :attr=>{'data-type'=>'association', 'data-column'=>assoc, 'data-exclude'=>model.primary_key_value(obj)}, :value=>''}.merge(add_opts))
|
495
|
+
f.input(assoc, {:type=>'text', :class=>'autoforme_autocomplete', :attr=>{'data-type'=>'association', 'data-column'=>assoc, 'data-exclude'=>model.primary_key_value(obj)}, :value=>''}.merge!(add_opts))
|
493
496
|
else
|
494
|
-
f.input(assoc, {:dataset=>model.unassociated_mtm_objects(request, assoc, obj), :size=>10}.merge(add_opts))
|
497
|
+
f.input(assoc, {:dataset=>model.unassociated_mtm_objects(request, assoc, obj), :size=>10}.merge!(add_opts))
|
495
498
|
end
|
496
|
-
f.input(assoc, {:name=>'remove[]', :id=>'remove', :label=>'Disassociate From', :dataset=>model.associated_mtm_objects(request, assoc, obj), :value=>[], :size=>10}.merge(remove_opts))
|
499
|
+
f.input(assoc, {:name=>'remove[]', :id=>'remove', :label=>'Disassociate From', :dataset=>model.associated_mtm_objects(request, assoc, obj), :value=>[], :size=>10}.merge!(remove_opts))
|
497
500
|
f.button(:value=>'Update', :class=>'btn btn-primary')
|
498
|
-
end
|
501
|
+
end
|
499
502
|
end
|
500
503
|
else
|
501
504
|
page do
|
@@ -540,7 +543,9 @@ module AutoForme
|
|
540
543
|
|
541
544
|
# Handle autocomplete action by returning a string with one line per model object.
|
542
545
|
def handle_autocomplete
|
543
|
-
|
546
|
+
if (query = request.params['q'].to_s).empty?
|
547
|
+
''
|
548
|
+
else
|
544
549
|
model.autocomplete(:type=>@subtype, :request=>request, :association=>params_association, :query=>query, :exclude=>request.params['exclude']).join("\n")
|
545
550
|
end
|
546
551
|
end
|
@@ -648,14 +653,14 @@ module AutoForme
|
|
648
653
|
t << Forme.form(obj, form_attr, form_opts(form_attr[:action])) do |f|
|
649
654
|
opts = model.column_options_for(:mtm_edit, request, assoc)
|
650
655
|
add_opts = opts[:add] ? opts.merge(opts.delete(:add)) : opts.dup
|
651
|
-
add_opts = {:name=>'add[]', :id=>"add_#{assoc}"}.merge(add_opts)
|
656
|
+
add_opts = {:name=>'add[]', :id=>"add_#{assoc}"}.merge!(add_opts)
|
652
657
|
if model.association_autocomplete?(assoc, request)
|
653
|
-
f.input(assoc, {:type=>'text', :class=>'autoforme_autocomplete', :attr=>{'data-type'=>'association', 'data-column'=>assoc, 'data-exclude'=>model.primary_key_value(obj)}, :value=>''}.merge(add_opts))
|
658
|
+
f.input(assoc, {:type=>'text', :class=>'autoforme_autocomplete', :attr=>{'data-type'=>'association', 'data-column'=>assoc, 'data-exclude'=>model.primary_key_value(obj)}, :value=>''}.merge!(add_opts))
|
654
659
|
else
|
655
|
-
f.input(assoc, {:dataset=>model.unassociated_mtm_objects(request, assoc, obj), :multiple=>false, :add_blank=>true}.merge(add_opts))
|
660
|
+
f.input(assoc, {:dataset=>model.unassociated_mtm_objects(request, assoc, obj), :multiple=>false, :add_blank=>true}.merge!(add_opts))
|
656
661
|
end
|
657
662
|
f.button(:value=>'Add', :class=>'btn btn-xs btn-primary')
|
658
|
-
end
|
663
|
+
end
|
659
664
|
end
|
660
665
|
t << "</div>"
|
661
666
|
t << "<div class='inline_mtm_remove_associations'><ul>"
|
@@ -680,7 +685,7 @@ module AutoForme
|
|
680
685
|
form_attr = form_attributes(:action=>url_for("mtm_update/#{model.primary_key_value(obj)}?association=#{assoc}&remove%5b%5d=#{model.primary_key_value(assoc_obj)}&redir=edit"), :method=>'post', :class => 'mtm_remove_associations', 'data-add'=>"#add_#{assoc}")
|
681
686
|
t << Forme.form(form_attr, form_opts(form_attr[:action])) do |f|
|
682
687
|
f.button(:value=>'Remove', :class=>'btn btn-xs btn-danger')
|
683
|
-
end
|
688
|
+
end
|
684
689
|
t << "</li>"
|
685
690
|
end
|
686
691
|
end
|
@@ -31,7 +31,11 @@ module AutoForme
|
|
31
31
|
# Use Rails's form_authenticity_token for CSRF protection.
|
32
32
|
def csrf_token_hash(action=nil)
|
33
33
|
vc = @controller.view_context
|
34
|
-
|
34
|
+
# :nocov:
|
35
|
+
if vc.protect_against_forgery?
|
36
|
+
# :nocov:
|
37
|
+
{vc.request_forgery_protection_token.to_s=>vc.form_authenticity_token}
|
38
|
+
end
|
35
39
|
end
|
36
40
|
end
|
37
41
|
|
@@ -58,7 +62,11 @@ module AutoForme
|
|
58
62
|
elsif @autoforme_action.request.xhr?
|
59
63
|
render :html=>@autoforme_text.html_safe
|
60
64
|
else
|
61
|
-
|
65
|
+
opts = framework.opts[:view_options]
|
66
|
+
opts = opts ? opts.dup : {}
|
67
|
+
opts[:layout] = true unless opts.has_key?(:layout)
|
68
|
+
opts[:inline] = "<%=raw @autoforme_text %>"
|
69
|
+
render opts
|
62
70
|
end
|
63
71
|
else
|
64
72
|
render :plain=>'Unhandled Request', :status=>404
|
@@ -40,29 +40,31 @@ module AutoForme
|
|
40
40
|
# Set the flash at notice level when redirecting, so it shows
|
41
41
|
# up on the redirected page.
|
42
42
|
def set_flash_notice(message)
|
43
|
-
@controller.flash[
|
43
|
+
@controller.flash[flash_key(:notice)] = message
|
44
44
|
end
|
45
45
|
|
46
46
|
# Set the current flash at error level, used when displaying
|
47
47
|
# pages when there is an error.
|
48
48
|
def set_flash_now_error(message)
|
49
|
-
@controller.flash.now[
|
49
|
+
@controller.flash.now[flash_key(:error)] = message
|
50
50
|
end
|
51
51
|
|
52
52
|
# Use Rack::Csrf for csrf protection if it is defined.
|
53
53
|
def csrf_token_hash(action=nil)
|
54
54
|
if @controller.respond_to?(:check_csrf!)
|
55
55
|
# Using route_csrf plugin
|
56
|
-
# :nocov:
|
57
56
|
token = if @controller.use_request_specific_csrf_tokens?
|
58
57
|
@controller.csrf_token(@controller.csrf_path(action))
|
58
|
+
# :nocov:
|
59
59
|
else
|
60
60
|
@controller.csrf_token
|
61
|
+
# :nocov:
|
61
62
|
end
|
62
63
|
{@controller.csrf_field=>token}
|
63
64
|
# :nocov:
|
64
|
-
elsif defined?(::Rack::Csrf)
|
65
|
+
elsif defined?(::Rack::Csrf) && !@controller.opts[:no_csrf]
|
65
66
|
{::Rack::Csrf.field=>::Rack::Csrf.token(@env)}
|
67
|
+
# :nocov:
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
@@ -71,6 +73,12 @@ module AutoForme
|
|
71
73
|
def flash_symbol_keys?
|
72
74
|
!@controller.opts[:sessions_convert_symbols]
|
73
75
|
end
|
76
|
+
|
77
|
+
def flash_key(key)
|
78
|
+
# :nocov:
|
79
|
+
flash_symbol_keys? ? key : key.to_s
|
80
|
+
# :nocov:
|
81
|
+
end
|
74
82
|
end
|
75
83
|
|
76
84
|
attr_reader :route_proc
|
@@ -106,7 +114,10 @@ module AutoForme
|
|
106
114
|
elsif @autoforme_action.request.xhr?
|
107
115
|
@autoforme_text
|
108
116
|
else
|
109
|
-
|
117
|
+
opts = framework.opts[:view_options]
|
118
|
+
opts = opts ? opts.dup : {}
|
119
|
+
opts[:content] = @autoforme_text
|
120
|
+
view(opts)
|
110
121
|
end
|
111
122
|
end
|
112
123
|
end
|
@@ -30,7 +30,11 @@ module AutoForme
|
|
30
30
|
|
31
31
|
# Use Rack::Csrf for csrf protection if it is defined.
|
32
32
|
def csrf_token_hash(action=nil)
|
33
|
-
|
33
|
+
# :nocov:
|
34
|
+
if defined?(::Rack::Csrf)
|
35
|
+
# :nocov:
|
36
|
+
{::Rack::Csrf.field=>::Rack::Csrf.token(@env)}
|
37
|
+
end
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
@@ -50,7 +54,8 @@ module AutoForme
|
|
50
54
|
elsif @autoforme_action.request.xhr?
|
51
55
|
@autoforme_text
|
52
56
|
else
|
53
|
-
|
57
|
+
opts = framework.opts[:view_options]
|
58
|
+
erb("<%= @autoforme_text %>".dup, opts ? opts.dup : {})
|
54
59
|
end
|
55
60
|
else
|
56
61
|
pass
|
@@ -58,10 +63,12 @@ module AutoForme
|
|
58
63
|
end
|
59
64
|
|
60
65
|
prefix = Regexp.escape(framework.prefix) if framework.prefix
|
66
|
+
# :nocov:
|
61
67
|
if ::Sinatra::VERSION < '2'
|
62
68
|
prefix = "\\A#{prefix}"
|
63
69
|
suffix = "\\z"
|
64
70
|
end
|
71
|
+
# :nocov:
|
65
72
|
regexp = %r{#{prefix}/([\w:]+)/(\w+)(?:/([\w-]+))?#{suffix}}
|
66
73
|
@controller.get regexp, &block
|
67
74
|
@controller.post regexp, &block
|