autoforme 1.9.1 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|