cocooned 1.3.2 → 1.4.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/History.md +21 -0
- data/README.md +16 -14
- data/Rakefile +6 -0
- data/app/assets/javascripts/cocoon.js +1 -1
- data/app/assets/javascripts/cocooned.js +63 -34
- data/app/assets/stylesheets/cocoon.css +1 -1
- data/cocooned.gemspec +13 -11
- data/lib/cocooned/association_builder.rb +1 -3
- data/lib/cocooned/helpers.rb +29 -26
- data/lib/cocooned/helpers/cocoon_compatibility.rb +1 -1
- data/lib/cocooned/helpers/deprecate.rb +4 -6
- data/lib/cocooned/railtie.rb +1 -1
- data/lib/cocooned/version.rb +1 -1
- metadata +60 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36c6a4d550d1edf6eddd5e46468395ef58ac3674d59c35c00c6a00ee4b0cc0ba
|
4
|
+
data.tar.gz: 210057657d4ebe5b9ce81f4734573b64917fab7a9fa347ad560daff4853f973d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e442dea49111a7cee949d1650497a1936473e1dcb740ab718743dd61fef620423bbb062f36c2deb97f008a1ea190b223c7c60a0480ac8a04037efbc67e22cb4e
|
7
|
+
data.tar.gz: 89314bc46f9fcd2aecf20f4ebb1509309f42578b4b2cfb2b27faf6c03d6d3f3f1bd1526efb7dcae76d212b65fc77026e4d404daf2dbf12154d9ae264b64d9b2b
|
data/History.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Change History / Release Notes
|
2
2
|
|
3
|
+
## Version 2.0.0 (Not released yet)
|
4
|
+
|
5
|
+
* Remove dependency to jQuery
|
6
|
+
|
7
|
+
## Version 1.4.0
|
8
|
+
|
9
|
+
### Breaking changes
|
10
|
+
|
11
|
+
* Drop support for Rails < 5.0 and Ruby < 2.5
|
12
|
+
* Pass the original browser event to all event handlers _via_ e.originalEvent. (See [jQuery Event object](https://api.jquery.com/category/events/event-object/)).
|
13
|
+
|
14
|
+
### New features
|
15
|
+
|
16
|
+
* Add support for @hotwired/turbo
|
17
|
+
|
18
|
+
### Bug fixes and other changes
|
19
|
+
|
20
|
+
* Prevent side effects on options passed to view helpers.
|
21
|
+
* Use form builder to add the hidden `_destroy` field instead of `hidden_field`.
|
22
|
+
* Update deprecation warnings to postpone compatibility drop with the original `cocoon` to 3.0 (instead of 2.0)
|
23
|
+
|
3
24
|
## Version 1.3.2
|
4
25
|
|
5
26
|
* Compatibility with Mongoid 7+
|
data/README.md
CHANGED
@@ -4,26 +4,21 @@
|
|
4
4
|
|
5
5
|
Cocooned makes it easier to handle nested forms in a Rails project.
|
6
6
|
|
7
|
-
Cocooned is form builder-agnostic: it works with standard Rails (>=
|
7
|
+
Cocooned is form builder-agnostic: it works with standard Rails (>= 5.0, < 7.0) form helpers, [Formtastic](https://github.com/justinfrench/formtastic) or [SimpleForm](https://github.com/plataformatec/simple_form).
|
8
8
|
|
9
9
|
## Some Background
|
10
10
|
|
11
|
-
Cocooned is a fork of [Cocoon](https://github.com/nathanvda/cocoon) by [Nathan Van der Auwera](https://github.com/nathanvda).
|
11
|
+
Cocooned is a fork of [Cocoon](https://github.com/nathanvda/cocoon) by [Nathan Van der Auwera](https://github.com/nathanvda). He and all Cocoon contributors did a great job to maintain it for years. Many thanks to them!
|
12
12
|
|
13
|
-
|
13
|
+
However, the project seems to have only received minimal fixes since 2018 and many pull requests, even simple ones, have been on hold for a long time. In 2019, as I needed a more than what Cocoon provided at this time, I had the choice to either maintain an extension or to fork it and integrate everything that was waiting and more.
|
14
14
|
|
15
|
-
|
15
|
+
Cocooned is almost a complete rewrite of Cocoon, with more functionnalities, a more fluent API (I hope) and integration with modern toolchains (including webpacker).
|
16
16
|
|
17
|
-
Cocooned is
|
17
|
+
**For now, Cocooned is completely compatible with Cocoon and can be used as a drop-in replacement** as long as we talk about Ruby code. Just change the name of the gem in your Gemfile and you're done. It will work the same (but will add a bunch of deprecation warning to your logs).
|
18
18
|
|
19
|
-
**
|
20
|
-
Just change the name of the gem in your Gemfile and you're done. It will work the same (but will add a bunch of deprecation warning to your logs).
|
19
|
+
**This compatibility layer with the original Cocoon API will be dropped in Cocooned 3.0.**
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
## Prerequisites
|
25
|
-
|
26
|
-
Cocooned depends on jQuery, Ruby (>= 2.2) and Rails (>= 4.0, < 6.0).
|
21
|
+
On the JavaScript side, Cocoon 1.2.13 introduced the original browser event as a third parameter to all event handlers. Meanwhile, Cocooned already started to use this positional parameter to pass the Cocooned object instance (since 1.3.0). To get access to the original event, [you'll have to change your handlers and use `event.originalEvent`](#javascript-callbacks).
|
27
22
|
|
28
23
|
## Installation
|
29
24
|
|
@@ -72,6 +67,12 @@ E.g. in your `ListsController`:
|
|
72
67
|
end
|
73
68
|
```
|
74
69
|
|
70
|
+
### Has One Gotcha
|
71
|
+
|
72
|
+
If you have a `has_one` association, then you (probably) need to set `force_non_association_create: true` on `link_to_add_association` or the associated object will be destroyed every time the edit form is rendered (which is probably not what you expect).
|
73
|
+
|
74
|
+
See the [original merge request](https://github.com/nathanvda/cocoon/pull/247) for more details.
|
75
|
+
|
75
76
|
### Basic form
|
76
77
|
|
77
78
|
[Rails natively supports nested forms](https://guides.rubyonrails.org/form_helpers.html#nested-forms) but does not support adding or removing nested items.
|
@@ -93,7 +94,7 @@ E.g. in your `ListsController`:
|
|
93
94
|
<% end %>
|
94
95
|
```
|
95
96
|
|
96
|
-
To enable Cocooned on this
|
97
|
+
To enable Cocooned on this form, we need to:
|
97
98
|
|
98
99
|
1. Move the nested form to a partial
|
99
100
|
2. Add a way to add a new item to the collection
|
@@ -190,7 +191,7 @@ This detection is based on the presence of a `data-cocooned-options` attribute o
|
|
190
191
|
</div>
|
191
192
|
```
|
192
193
|
|
193
|
-
|
194
|
+
You're done!
|
194
195
|
|
195
196
|
### Wait, what's the point of `data-cocooned-options` if it's to be empty?
|
196
197
|
|
@@ -312,6 +313,7 @@ The event `event` is an instance of `jQuery.Event` and carry some additional dat
|
|
312
313
|
* `event.node`, the nested item that will be added, removed or moved, as a jQuery object. This is null for `cocooned:limit-reached` and `cocooned:*-reindex` events
|
313
314
|
* `event.nodes`, the nested items that will be or just have been reindexed on `cocooned:*-reindex` events, as a jQuery object. Null otherwise.
|
314
315
|
* `event.cocooned`, the Cocooned javascript object instance handling the nested association.
|
316
|
+
* `event.originalEvent`, the original (browser) event.
|
315
317
|
|
316
318
|
The `node` argument is the same jQuery object as `event.node`.
|
317
319
|
The `cocooned` argument is the same as `event.cocooned`.
|
data/Rakefile
CHANGED
@@ -20,6 +20,8 @@ RuboCop::RakeTask.new do |task|
|
|
20
20
|
end
|
21
21
|
|
22
22
|
# JavaScript
|
23
|
+
# rubocop:disable Rails/RakeEnvironment
|
24
|
+
# eslint related tasks does not need to load Rails environment
|
23
25
|
eslint_args = ['--no-eslintrc', '--config config/linters/js.json']
|
24
26
|
eslint_path = ['app/assets/**/*.js', 'spec/javascripts/**/*.js', 'spec/dummy/app/assets/**/*.js']
|
25
27
|
|
@@ -34,6 +36,7 @@ desc 'Run eslint'
|
|
34
36
|
task :eslint do
|
35
37
|
system("yarnpkg run eslint #{eslint_args.join(' ')} #{eslint_path.join(' ')}")
|
36
38
|
end
|
39
|
+
# rubocop:enable Rails/RakeEnvironment
|
37
40
|
|
38
41
|
# Both
|
39
42
|
namespace :linters do
|
@@ -69,6 +72,8 @@ npm_files = {
|
|
69
72
|
}
|
70
73
|
|
71
74
|
namespace :npm do
|
75
|
+
# rubocop:disable Rails/RakeEnvironment
|
76
|
+
# npm related tasks does not need to load Rails environment
|
72
77
|
npm_files.each do |dest, src|
|
73
78
|
file dest => src do
|
74
79
|
cp src, dest
|
@@ -101,6 +106,7 @@ namespace :npm do
|
|
101
106
|
task release: %i[build] do
|
102
107
|
system("npm publish ./pkg/#{npm_scope}-#{spec.name}-#{spec.version}.tgz --access public")
|
103
108
|
end
|
109
|
+
# rubocop:enable Rails/RakeEnvironment
|
104
110
|
end
|
105
111
|
|
106
112
|
desc 'Build packages and push them to their respective repository'
|
@@ -2,7 +2,7 @@
|
|
2
2
|
//= require 'cocooned'
|
3
3
|
|
4
4
|
// Compatibility with the original Cocoon
|
5
|
-
// TODO: Remove in
|
5
|
+
// TODO: Remove in 3.0
|
6
6
|
function initCocoon () {
|
7
7
|
$(Cocooned.prototype.selector('add')).each(function (_i, addLink) {
|
8
8
|
var container = Cocooned.prototype.findContainer(addLink);
|
@@ -20,24 +20,34 @@
|
|
20
20
|
}(typeof self !== 'undefined' ? self : this, function ($) {
|
21
21
|
var Cocooned = function (container, options) {
|
22
22
|
this.container = $(container);
|
23
|
-
|
23
|
+
var opts = $.extend({}, this.defaultOptions(), (options || {}));
|
24
24
|
|
25
25
|
// Autoload plugins
|
26
|
-
for (var
|
27
|
-
if (Cocooned.Plugins.hasOwnProperty(
|
28
|
-
var
|
29
|
-
var optionName =
|
30
|
-
|
31
|
-
if (
|
32
|
-
|
33
|
-
|
34
|
-
|
26
|
+
for (var pluginName in Cocooned.Plugins) {
|
27
|
+
if (Cocooned.Plugins.hasOwnProperty(pluginName)) {
|
28
|
+
var plugin = Cocooned.Plugins[pluginName];
|
29
|
+
var optionName = pluginName.charAt(0).toLowerCase() + pluginName.slice(1);
|
30
|
+
|
31
|
+
if (opts[optionName] !== false) {
|
32
|
+
if (plugin.hasOwnProperty('normalizeConfig') && typeof plugin['normalizeConfig'] === 'function') {
|
33
|
+
opts[optionName] = plugin.normalizeConfig(opts[optionName]);
|
34
|
+
}
|
35
|
+
|
36
|
+
for (var method in plugin) {
|
37
|
+
if (method === 'normalizeConfig') {
|
38
|
+
continue;
|
35
39
|
}
|
40
|
+
if (!plugin.hasOwnProperty(method) || typeof plugin[method] !== 'function') {
|
41
|
+
continue;
|
42
|
+
}
|
43
|
+
|
44
|
+
this[method] = plugin[method];
|
36
45
|
}
|
37
46
|
}
|
38
47
|
}
|
39
48
|
}
|
40
49
|
|
50
|
+
this.options = opts;
|
41
51
|
this.init();
|
42
52
|
};
|
43
53
|
|
@@ -47,13 +57,13 @@
|
|
47
57
|
elementsCounter: 0,
|
48
58
|
|
49
59
|
// Compatibility with Cocoon
|
50
|
-
// TODO: Remove in
|
60
|
+
// TODO: Remove in 3.0 (Only Cocoon namespaces).
|
51
61
|
namespaces: {
|
52
62
|
events: ['cocooned', 'cocoon']
|
53
63
|
},
|
54
64
|
|
55
65
|
// Compatibility with Cocoon
|
56
|
-
// TODO: Remove in
|
66
|
+
// TODO: Remove in 3.0 (Only Cocoon class names).
|
57
67
|
classes: {
|
58
68
|
// Actions link
|
59
69
|
add: ['cocooned-add', 'add_fields'],
|
@@ -210,7 +220,7 @@
|
|
210
220
|
this.container.addClass(this.classes['container'].join(' '));
|
211
221
|
|
212
222
|
$(function () { self.hideMarkedForDestruction(); });
|
213
|
-
$(document).on('page:load turbolinks:load', function () { self.hideMarkedForDestruction(); });
|
223
|
+
$(document).on('page:load turbolinks:load turbo:load', function () { self.hideMarkedForDestruction(); });
|
214
224
|
},
|
215
225
|
|
216
226
|
bindEvents: function () {
|
@@ -221,7 +231,9 @@
|
|
221
231
|
this.namespacedNativeEvents('click'),
|
222
232
|
function (e) {
|
223
233
|
e.preventDefault();
|
224
|
-
|
234
|
+
e.stopPropagation();
|
235
|
+
|
236
|
+
self.add(this, e);
|
225
237
|
});
|
226
238
|
|
227
239
|
// Bind remove links
|
@@ -231,7 +243,9 @@
|
|
231
243
|
this.selector('remove', '#' + this.container.attr('id') + ' &'),
|
232
244
|
function (e) {
|
233
245
|
e.preventDefault();
|
234
|
-
|
246
|
+
e.stopPropagation();
|
247
|
+
|
248
|
+
self.remove(this, e);
|
235
249
|
});
|
236
250
|
|
237
251
|
// Bind options events
|
@@ -243,7 +257,7 @@
|
|
243
257
|
});
|
244
258
|
},
|
245
259
|
|
246
|
-
add: function (adder) {
|
260
|
+
add: function (adder, originalEvent) {
|
247
261
|
var $adder = $(adder);
|
248
262
|
var insertionMethod = this.getInsertionMethod($adder);
|
249
263
|
var insertionNode = this.getInsertionNode($adder);
|
@@ -252,7 +266,7 @@
|
|
252
266
|
|
253
267
|
for (var i = 0; i < count; i++) {
|
254
268
|
var contentNode = this.buildContentNode(contentTemplate);
|
255
|
-
var eventData = { link: $adder, node: contentNode, cocooned: this };
|
269
|
+
var eventData = { link: $adder, node: contentNode, cocooned: this, originalEvent: originalEvent };
|
256
270
|
var afterNode = (insertionMethod === 'replaceWith' ? contentNode : insertionNode);
|
257
271
|
|
258
272
|
// Insertion can be prevented through a 'cocooned:before-insert' event handler
|
@@ -266,12 +280,12 @@
|
|
266
280
|
}
|
267
281
|
},
|
268
282
|
|
269
|
-
remove: function (remover) {
|
283
|
+
remove: function (remover, originalEvent) {
|
270
284
|
var self = this;
|
271
285
|
var $remover = $(remover);
|
272
286
|
var nodeToDelete = this.findItem($remover);
|
273
287
|
var triggerNode = nodeToDelete.parent();
|
274
|
-
var eventData = { link: $remover, node: nodeToDelete, cocooned: this };
|
288
|
+
var eventData = { link: $remover, node: nodeToDelete, cocooned: this, originalEvent: originalEvent };
|
275
289
|
|
276
290
|
// Deletion can be prevented through a 'cocooned:before-remove' event handler
|
277
291
|
if (!this.notify(triggerNode, 'before-remove', eventData)) {
|
@@ -316,7 +330,7 @@
|
|
316
330
|
}
|
317
331
|
|
318
332
|
e.stopPropagation();
|
319
|
-
var eventData = { link: e.link, node: e.node, cocooned: cocooned };
|
333
|
+
var eventData = { link: e.link, node: e.node, cocooned: cocooned, originalEvent: e };
|
320
334
|
cocooned.notify(cocooned.container, 'limit-reached', eventData);
|
321
335
|
});
|
322
336
|
},
|
@@ -328,16 +342,24 @@
|
|
328
342
|
|
329
343
|
Cocooned.Plugins.Reorderable = {
|
330
344
|
|
331
|
-
defaultOptionValue:
|
345
|
+
defaultOptionValue: false,
|
346
|
+
defaultConfig: { startAt: 1 },
|
347
|
+
|
348
|
+
normalizeConfig: function(config) {
|
349
|
+
if (typeof config === 'boolean' && config) {
|
350
|
+
return this.defaultConfig;
|
351
|
+
}
|
352
|
+
return config;
|
353
|
+
},
|
332
354
|
|
333
355
|
bindReorderable: function () {
|
334
356
|
var self = this;
|
335
357
|
|
336
358
|
// Maintain indexes
|
337
359
|
this.container
|
338
|
-
.on('cocooned:after-insert', function (e) { self.reindex(); })
|
339
|
-
.on('cocooned:after-remove', function (e) { self.reindex(); })
|
340
|
-
.on('cocooned:after-move', function (e) { self.reindex(); });
|
360
|
+
.on('cocooned:after-insert', function (e) { self.reindex(e); })
|
361
|
+
.on('cocooned:after-remove', function (e) { self.reindex(e); })
|
362
|
+
.on('cocooned:after-move', function (e) { self.reindex(e); });
|
341
363
|
|
342
364
|
// Move items
|
343
365
|
this.container.on(
|
@@ -345,22 +367,24 @@
|
|
345
367
|
[this.selector('up'), this.selector('down')].join(', '),
|
346
368
|
function (e) {
|
347
369
|
e.preventDefault();
|
370
|
+
e.stopPropagation();
|
371
|
+
|
348
372
|
var node = this;
|
349
373
|
var up = self.classes['up'].some(function (klass) {
|
350
374
|
return node.className.indexOf(klass) !== -1;
|
351
375
|
});
|
352
|
-
self.move(this, up ? 'up' : 'down');
|
376
|
+
self.move(this, up ? 'up' : 'down', e);
|
353
377
|
});
|
354
378
|
|
355
379
|
// Ensure positions are unique before save
|
356
380
|
this.container.closest('form').on(
|
357
381
|
this.namespacedNativeEvents('submit'),
|
358
382
|
function (e) {
|
359
|
-
self.reindex();
|
383
|
+
self.reindex(e);
|
360
384
|
});
|
361
385
|
},
|
362
386
|
|
363
|
-
move: function (moveLink, direction) {
|
387
|
+
move: function (moveLink, direction, originalEvent) {
|
364
388
|
var self = this;
|
365
389
|
var $mover = $(moveLink);
|
366
390
|
var node = $mover.closest(this.selector('item'));
|
@@ -373,7 +397,7 @@
|
|
373
397
|
}
|
374
398
|
|
375
399
|
// Move can be prevented through a 'cocooned:before-move' event handler
|
376
|
-
var eventData = { link: $mover, node: node, cocooned: this };
|
400
|
+
var eventData = { link: $mover, node: node, cocooned: this, originalEvent: originalEvent };
|
377
401
|
if (!self.notify(node, 'before-move', eventData)) {
|
378
402
|
return false;
|
379
403
|
}
|
@@ -393,17 +417,17 @@
|
|
393
417
|
});
|
394
418
|
},
|
395
419
|
|
396
|
-
reindex: function () {
|
397
|
-
var i =
|
420
|
+
reindex: function (originalEvent) {
|
421
|
+
var i = this.options.reorderable.startAt;
|
398
422
|
var nodes = this.getItems('&:visible');
|
399
|
-
var eventData = { link: null, nodes: nodes, cocooned: this };
|
423
|
+
var eventData = { link: null, nodes: nodes, cocooned: this, originalEvent: originalEvent };
|
400
424
|
|
401
425
|
// Reindex can be prevented through a 'cocooned:before-reindex' event handler
|
402
426
|
if (!this.notify(this.container, 'before-reindex', eventData)) {
|
403
427
|
return false;
|
404
428
|
}
|
405
429
|
|
406
|
-
nodes.each(function () { $('input[id$=_position]', this).val(++
|
430
|
+
nodes.each(function () { $('input[id$=_position]', this).val(i++); });
|
407
431
|
this.notify(this.container, 'after-reindex', eventData);
|
408
432
|
},
|
409
433
|
|
@@ -435,7 +459,12 @@
|
|
435
459
|
return;
|
436
460
|
}
|
437
461
|
|
438
|
-
var
|
462
|
+
var opts = options;
|
463
|
+
if (typeof container.data('cocooned-options') !== 'undefined') {
|
464
|
+
opts = $.extend(opts, container.data('cocooned-options'));
|
465
|
+
}
|
466
|
+
|
467
|
+
var cocooned = new Cocooned(container, opts);
|
439
468
|
container.data('cocooned', cocooned);
|
440
469
|
});
|
441
470
|
};
|
@@ -443,7 +472,7 @@
|
|
443
472
|
// On-load initialization
|
444
473
|
$(function () {
|
445
474
|
$('*[data-cocooned-options]').each(function (i, el) {
|
446
|
-
$(el).cocooned(
|
475
|
+
$(el).cocooned();
|
447
476
|
});
|
448
477
|
});
|
449
478
|
|
data/cocooned.gemspec
CHANGED
@@ -11,30 +11,32 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.authors = ['Gaël-Ian Havard', 'Nathan Van der Auwera']
|
12
12
|
spec.email = ['gael-ian@notus.sh', 'nathan@dixis.com']
|
13
13
|
|
14
|
-
spec.summary = 'Unobtrusive nested forms handling
|
14
|
+
spec.summary = 'Unobtrusive Rails nested forms handling, with or without jQuery.'
|
15
15
|
spec.description = 'Easier nested form. Supports standard Rails forms, Formtastic and SimpleForm.'
|
16
16
|
spec.homepage = 'http://github.com/notus-sh/cocooned'
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
raise 'RubyGems 2.0 or newer is required.'
|
22
|
-
end
|
18
|
+
raise 'RubyGems 2.0 or newer is required.' unless spec.respond_to?(:metadata)
|
19
|
+
|
20
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
23
21
|
|
24
22
|
spec.require_paths = ['lib']
|
25
|
-
spec.files
|
23
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
26
24
|
f.match(%r{^(config|gemfiles|npm|spec)/}) ||
|
27
25
|
%w[.gitignore .rspec .travis.yml].include?(f) ||
|
28
26
|
%w[Gemfile Gemfile.lock package.json yarn.lock].include?(f)
|
29
27
|
end
|
28
|
+
spec.required_ruby_version = '>= 2.5'
|
30
29
|
|
31
|
-
spec.add_dependency 'rails', '>=
|
30
|
+
spec.add_dependency 'rails', '>= 5.0', '<= 7.0'
|
32
31
|
|
33
|
-
spec.add_development_dependency 'bundler', '~> 1
|
32
|
+
spec.add_development_dependency 'bundler', '~> 2.1'
|
34
33
|
spec.add_development_dependency 'jasmine', '~> 3.2'
|
35
34
|
spec.add_development_dependency 'rake'
|
36
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
37
|
-
spec.add_development_dependency 'rspec-rails', '~>
|
35
|
+
spec.add_development_dependency 'rspec', '~> 3.10.0'
|
36
|
+
spec.add_development_dependency 'rspec-rails', '~> 5.0.0'
|
38
37
|
spec.add_development_dependency 'rubocop'
|
39
38
|
spec.add_development_dependency 'rubocop-performance'
|
39
|
+
spec.add_development_dependency 'rubocop-rails'
|
40
|
+
spec.add_development_dependency 'rubocop-rake'
|
41
|
+
spec.add_development_dependency 'rubocop-rspec'
|
40
42
|
end
|
data/lib/cocooned/helpers.rb
CHANGED
@@ -5,17 +5,17 @@ require 'cocooned/helpers/cocoon_compatibility'
|
|
5
5
|
require 'cocooned/association_builder'
|
6
6
|
|
7
7
|
module Cocooned
|
8
|
-
# TODO: Remove in
|
8
|
+
# TODO: Remove in 3.0 (Only Cocoon class names).
|
9
9
|
HELPER_CLASSES = {
|
10
|
-
add:
|
11
|
-
remove: [
|
12
|
-
up:
|
13
|
-
down:
|
10
|
+
add: %w[cocooned-add add_fields],
|
11
|
+
remove: %w[cocooned-remove remove_fields],
|
12
|
+
up: %w[cocooned-move-up],
|
13
|
+
down: %w[cocooned-move-down]
|
14
14
|
}.freeze
|
15
15
|
|
16
|
-
module Helpers
|
16
|
+
module Helpers # rubocop:disable Metrics/ModuleLength
|
17
17
|
# Create aliases to old Cocoon method name
|
18
|
-
# TODO: Remove in
|
18
|
+
# TODO: Remove in 3.0
|
19
19
|
include Cocooned::Helpers::CocoonCompatibility
|
20
20
|
|
21
21
|
# Output an action link to add an item in a nested form.
|
@@ -118,7 +118,7 @@ module Cocooned
|
|
118
118
|
else
|
119
119
|
name, form, association, html_options = *args
|
120
120
|
html_options ||= {}
|
121
|
-
html_options = html_options.with_indifferent_access
|
121
|
+
html_options = html_options.dup.with_indifferent_access
|
122
122
|
|
123
123
|
builder_options = cocooned_extract_builder_options!(html_options)
|
124
124
|
render_options = cocooned_extract_render_options!(html_options)
|
@@ -167,12 +167,12 @@ module Cocooned
|
|
167
167
|
association = form.object.class.to_s.tableize
|
168
168
|
return cocooned_remove_item_link(cocooned_default_label(:remove, association), form, html_options) if name.nil?
|
169
169
|
|
170
|
-
|
171
|
-
html_options[:class]
|
172
|
-
|
170
|
+
link_options = html_options.dup
|
171
|
+
link_options[:class] = [html_options[:class], Cocooned::HELPER_CLASSES[:remove]].flatten.compact
|
172
|
+
link_options[:class] << (form.object.new_record? ? 'dynamic' : 'existing')
|
173
|
+
link_options[:class] << 'destroyed' if form.object.marked_for_destruction?
|
173
174
|
|
174
|
-
|
175
|
-
link_to(name, '#', html_options)
|
175
|
+
form.hidden_field(:_destroy, value: form.object._destroy) << link_to(name, '#', link_options)
|
176
176
|
end
|
177
177
|
|
178
178
|
# Output an action link to move an item up.
|
@@ -220,12 +220,13 @@ module Cocooned
|
|
220
220
|
return cocooned_move_item_link(direction, capture(&block), form, html_options) if block_given?
|
221
221
|
return cocooned_move_item_link(direction, cocooned_default_label(direction), form, html_options) if name.nil?
|
222
222
|
|
223
|
-
|
224
|
-
|
223
|
+
link_options = html_options.dup
|
224
|
+
link_options[:class] = [html_options[:class], Cocooned::HELPER_CLASSES[direction]].flatten.compact.join(' ')
|
225
|
+
link_to name, '#', link_options
|
225
226
|
end
|
226
227
|
|
227
228
|
def cocooned_default_label(action, association = nil)
|
228
|
-
# TODO: Remove in
|
229
|
+
# TODO: Remove in 3.0
|
229
230
|
if I18n.exists?(:cocoon)
|
230
231
|
msg = Cocooned::Helpers::Deprecate.deprecate_release_message('the :cocoon i18n scope', ':cocooned')
|
231
232
|
warn msg
|
@@ -239,9 +240,10 @@ module Cocooned
|
|
239
240
|
I18n.translate(keys.take(1).first, default: keys.drop(1))
|
240
241
|
end
|
241
242
|
|
242
|
-
def cocooned_render_association(builder,
|
243
|
+
def cocooned_render_association(builder, options = {})
|
244
|
+
render_options = options.dup
|
243
245
|
locals = (render_options.delete(:locals) || {}).symbolize_keys
|
244
|
-
partial = render_options.delete(:partial) || builder.singular_name
|
246
|
+
partial = render_options.delete(:partial) || "#{builder.singular_name}_fields"
|
245
247
|
form_name = render_options.delete(:form_name)
|
246
248
|
form_options = (render_options.delete(:form_options) || {}).symbolize_keys
|
247
249
|
form_options.reverse_merge!(child_index: "new_#{builder.association}")
|
@@ -250,7 +252,6 @@ module Cocooned
|
|
250
252
|
builder.association,
|
251
253
|
builder.build_object,
|
252
254
|
form_options) do |form_builder|
|
253
|
-
|
254
255
|
partial_options = { form_name.to_sym => form_builder, :dynamic => true }.merge(locals)
|
255
256
|
render(partial, partial_options)
|
256
257
|
end
|
@@ -312,19 +313,21 @@ module Cocooned
|
|
312
313
|
data.compact
|
313
314
|
end
|
314
315
|
|
315
|
-
def cocooned_extract_single_data!(
|
316
|
+
def cocooned_extract_single_data!(hash, key)
|
316
317
|
k = key.to_s
|
317
|
-
return
|
318
|
+
return hash.delete(k) if hash.key?(k)
|
318
319
|
|
319
320
|
# Compatibility alternatives
|
320
321
|
# TODO: Remove in 2.0
|
321
|
-
return
|
322
|
-
return
|
323
|
-
return
|
324
|
-
return nil unless
|
325
|
-
|
322
|
+
return hash.delete("association_#{k}") if hash.key?("association_#{k}")
|
323
|
+
return hash.delete("data_association_#{k}") if hash.key?("data_association_#{k}")
|
324
|
+
return hash.delete("data-association-#{k.tr('_', '-')}") if hash.key?("data-association-#{k.tr('_', '-')}")
|
325
|
+
return nil unless hash.key?(:data)
|
326
|
+
|
327
|
+
d = hash[:data].with_indifferent_access
|
326
328
|
return d.delete("association_#{k}") if d.key?("association_#{k}")
|
327
329
|
return d.delete("association-#{k.tr('_', '-')}") if d.key?("association-#{k.tr('_', '-')}")
|
330
|
+
|
328
331
|
nil
|
329
332
|
end
|
330
333
|
end
|
@@ -7,7 +7,7 @@ module Cocooned
|
|
7
7
|
# Provide aliases to old Cocoon method for backward compatibility.
|
8
8
|
# Cocoon methods are deprecated and will be removed in next major release.
|
9
9
|
#
|
10
|
-
# TODO: Remove in
|
10
|
+
# TODO: Remove in 3.0
|
11
11
|
module CocoonCompatibility
|
12
12
|
extend Cocooned::Helpers::Deprecate
|
13
13
|
|
@@ -10,7 +10,9 @@ module Cocooned
|
|
10
10
|
module Deprecate
|
11
11
|
extend Gem::Deprecate
|
12
12
|
|
13
|
-
|
13
|
+
module_function
|
14
|
+
|
15
|
+
def deprecate_release_message(target_and_name, replacement, release = '3.0', location = nil)
|
14
16
|
[
|
15
17
|
"NOTE: #{target_and_name} is deprecated",
|
16
18
|
replacement == :none ? ' with no replacement' : "; use #{replacement} instead",
|
@@ -19,9 +21,7 @@ module Cocooned
|
|
19
21
|
].join.strip
|
20
22
|
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
def deprecate_release(name, replacement, release = '2.0')
|
24
|
+
def deprecate_release(name, replacement, release = '3.0')
|
25
25
|
class_eval do
|
26
26
|
old = "_deprecated_#{name}"
|
27
27
|
alias_method old, name
|
@@ -42,8 +42,6 @@ module Cocooned
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
45
|
-
|
46
|
-
module_function :deprecate_release
|
47
45
|
end
|
48
46
|
end
|
49
47
|
end
|
data/lib/cocooned/railtie.rb
CHANGED
data/lib/cocooned/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cocooned
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gaël-Ian Havard
|
8
8
|
- Nathan Van der Auwera
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-08-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -17,34 +17,34 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '5.0'
|
21
21
|
- - "<="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: '
|
23
|
+
version: '7.0'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
27
27
|
requirements:
|
28
28
|
- - ">="
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
version: '
|
30
|
+
version: '5.0'
|
31
31
|
- - "<="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '7.0'
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: bundler
|
36
36
|
requirement: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1
|
40
|
+
version: '2.1'
|
41
41
|
type: :development
|
42
42
|
prerelease: false
|
43
43
|
version_requirements: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1
|
47
|
+
version: '2.1'
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: jasmine
|
50
50
|
requirement: !ruby/object:Gem::Requirement
|
@@ -79,28 +79,28 @@ dependencies:
|
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 3.
|
82
|
+
version: 3.10.0
|
83
83
|
type: :development
|
84
84
|
prerelease: false
|
85
85
|
version_requirements: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 3.
|
89
|
+
version: 3.10.0
|
90
90
|
- !ruby/object:Gem::Dependency
|
91
91
|
name: rspec-rails
|
92
92
|
requirement: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 5.0.0
|
97
97
|
type: :development
|
98
98
|
prerelease: false
|
99
99
|
version_requirements: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 5.0.0
|
104
104
|
- !ruby/object:Gem::Dependency
|
105
105
|
name: rubocop
|
106
106
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,6 +129,48 @@ dependencies:
|
|
129
129
|
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '0'
|
132
|
+
- !ruby/object:Gem::Dependency
|
133
|
+
name: rubocop-rails
|
134
|
+
requirement: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
type: :development
|
140
|
+
prerelease: false
|
141
|
+
version_requirements: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
- !ruby/object:Gem::Dependency
|
147
|
+
name: rubocop-rake
|
148
|
+
requirement: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
type: :development
|
154
|
+
prerelease: false
|
155
|
+
version_requirements: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
- !ruby/object:Gem::Dependency
|
161
|
+
name: rubocop-rspec
|
162
|
+
requirement: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
type: :development
|
168
|
+
prerelease: false
|
169
|
+
version_requirements: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
132
174
|
description: Easier nested form. Supports standard Rails forms, Formtastic and SimpleForm.
|
133
175
|
email:
|
134
176
|
- gael-ian@notus.sh
|
@@ -158,7 +200,7 @@ licenses:
|
|
158
200
|
- Apache-2.0
|
159
201
|
metadata:
|
160
202
|
allowed_push_host: https://rubygems.org
|
161
|
-
post_install_message:
|
203
|
+
post_install_message:
|
162
204
|
rdoc_options: []
|
163
205
|
require_paths:
|
164
206
|
- lib
|
@@ -166,16 +208,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
166
208
|
requirements:
|
167
209
|
- - ">="
|
168
210
|
- !ruby/object:Gem::Version
|
169
|
-
version: '
|
211
|
+
version: '2.5'
|
170
212
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
213
|
requirements:
|
172
214
|
- - ">="
|
173
215
|
- !ruby/object:Gem::Version
|
174
216
|
version: '0'
|
175
217
|
requirements: []
|
176
|
-
|
177
|
-
|
178
|
-
signing_key:
|
218
|
+
rubygems_version: 3.2.3
|
219
|
+
signing_key:
|
179
220
|
specification_version: 4
|
180
|
-
summary: Unobtrusive nested forms handling
|
221
|
+
summary: Unobtrusive Rails nested forms handling, with or without jQuery.
|
181
222
|
test_files: []
|