cocooned 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4494f16e78b35fe2847ef24c3b8ec95183ce63cf05fc41a81ed9aa6b316ca1a1
4
- data.tar.gz: 4f554493b8c3aa8fe6e9e89a1eb762d186336783ad24f948e31d4d97922f405b
3
+ metadata.gz: 36c6a4d550d1edf6eddd5e46468395ef58ac3674d59c35c00c6a00ee4b0cc0ba
4
+ data.tar.gz: 210057657d4ebe5b9ce81f4734573b64917fab7a9fa347ad560daff4853f973d
5
5
  SHA512:
6
- metadata.gz: 8c09909ed1c2e7b9db436be0918b19bc28815568c804aeb76e9caba91809baabea55a3a732bcfedeac5226d550820e79b85f12d69ffced86aa2844bfaca16e91
7
- data.tar.gz: 0c41cef34455f05fb7bc278f563d063f40ca3e5c74f31dbc2ec642f60b694041657d9f35ca35cdf8579ba685ca0923d8a16994dacbdc34a56c65520e4d44cad3
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 (>= 4.0, < 6.0), [Formtastic](https://github.com/justinfrench/formtastic) or [SimpleForm](https://github.com/plataformatec/simple_form).
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
- He and all Cocoon contributors did a great job to maintain it for years. Many thanks to them!
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
- But last time I checked, the project seemed to not have been actively maintained for a long time and many pull requests, even simple ones, were on hold. 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.
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 almost a complete rewrite of Cocoon, with more functionnalities and (I hope) a more fluent API.
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
- **For now, Cocooned is completely compatible with Cocoon and can be used as a drop-in replacement.**
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
- **The compatibility layer with the original Cocoon API will be dropped in Cocooned 2.0.**
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 first, we need to:
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
- And we're done!
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 2.0
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
- this.options = $.extend({}, this.defaultOptions(), (options || {}));
23
+ var opts = $.extend({}, this.defaultOptions(), (options || {}));
24
24
 
25
25
  // Autoload plugins
26
- for (var moduleName in Cocooned.Plugins) {
27
- if (Cocooned.Plugins.hasOwnProperty(moduleName)) {
28
- var module = Cocooned.Plugins[moduleName];
29
- var optionName = moduleName.charAt(0).toLowerCase() + moduleName.slice(1);
30
-
31
- if (this.options[optionName]) {
32
- for (var method in module) {
33
- if (module.hasOwnProperty(method) && typeof module[method] === 'function') {
34
- this[method] = module[method];
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 2.0 (Only Cocoon namespaces).
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 2.0 (Only Cocoon class names).
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
- self.add(this);
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
- self.remove(this);
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: true,
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 = 0;
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(++i); });
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 cocooned = new Cocooned(container, options);
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($(el).data('cocooned-options'));
475
+ $(el).cocooned();
447
476
  });
448
477
  });
449
478
 
@@ -2,4 +2,4 @@
2
2
  *= require cocooned
3
3
  */
4
4
 
5
- /* TODO: Remove in 2.0 */
5
+ /* TODO: Remove in 3.0 */
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 using jQuery.'
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
- if spec.respond_to?(:metadata)
19
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
- else
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 = `git ls-files -z`.split("\x0").reject do |f|
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', '>= 4.0', '<= 6.0'
30
+ spec.add_dependency 'rails', '>= 5.0', '<= 7.0'
32
31
 
33
- spec.add_development_dependency 'bundler', '~> 1.16'
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.8.0'
37
- spec.add_development_dependency 'rspec-rails', '~> 3.8.0'
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
@@ -2,9 +2,7 @@
2
2
 
3
3
  module Cocooned
4
4
  class AssociationBuilder
5
- attr_accessor :association
6
- attr_accessor :form
7
- attr_accessor :options
5
+ attr_accessor :association, :form, :options
8
6
 
9
7
  def initialize(form, association, options = {})
10
8
  self.form = form
@@ -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 2.0 (Only Cocoon class names).
8
+ # TODO: Remove in 3.0 (Only Cocoon class names).
9
9
  HELPER_CLASSES = {
10
- add: ['cocooned-add', 'add_fields'],
11
- remove: ['cocooned-remove', 'remove_fields'],
12
- up: ['cocooned-move-up'],
13
- down: ['cocooned-move-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 2.0
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
- html_options[:class] = [html_options[:class], Cocooned::HELPER_CLASSES[:remove]].flatten.compact
171
- html_options[:class] << (form.object.new_record? ? 'dynamic' : 'existing')
172
- html_options[:class] << 'destroyed' if form.object.marked_for_destruction?
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
- hidden_field_tag("#{form.object_name}[_destroy]", form.object._destroy) <<
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
- html_options[:class] = [html_options[:class], Cocooned::HELPER_CLASSES[direction]].flatten.compact.join(' ')
224
- link_to name, '#', html_options
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 2.0
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, render_options = {})
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 + '_fields'
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!(h, key)
316
+ def cocooned_extract_single_data!(hash, key)
316
317
  k = key.to_s
317
- return h.delete(k) if h.key?(k)
318
+ return hash.delete(k) if hash.key?(k)
318
319
 
319
320
  # Compatibility alternatives
320
321
  # TODO: Remove in 2.0
321
- return h.delete("association_#{k}") if h.key?("association_#{k}")
322
- return h.delete("data_association_#{k}") if h.key?("data_association_#{k}")
323
- return h.delete("data-association-#{k.tr('_', '-')}") if h.key?("data-association-#{k.tr('_', '-')}")
324
- return nil unless h.key?(:data)
325
- d = h[:data].with_indifferent_access
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 2.0
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
- def deprecate_release_message(target_and_name, replacement, release = '2.0', location = nil)
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
- module_function :deprecate_release_message
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
@@ -7,7 +7,7 @@ module Cocooned
7
7
  class Railtie < ::Rails::Engine
8
8
  initializer 'cocooned.initialize' do |_app|
9
9
  ActiveSupport.on_load :action_view do
10
- ActionView::Base.send :include, Cocooned::Helpers
10
+ ActionView::Base.include Cocooned::Helpers
11
11
  end
12
12
  end
13
13
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cocooned
4
- VERSION = '1.3.2'
4
+ VERSION = '1.4.0'
5
5
  end
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.3.2
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: 2019-04-05 00:00:00.000000000 Z
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: '4.0'
20
+ version: '5.0'
21
21
  - - "<="
22
22
  - !ruby/object:Gem::Version
23
- version: '6.0'
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: '4.0'
30
+ version: '5.0'
31
31
  - - "<="
32
32
  - !ruby/object:Gem::Version
33
- version: '6.0'
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.16'
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.16'
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.8.0
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.8.0
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: 3.8.0
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: 3.8.0
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: '0'
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
- rubyforge_project:
177
- rubygems_version: 2.7.6
178
- signing_key:
218
+ rubygems_version: 3.2.3
219
+ signing_key:
179
220
  specification_version: 4
180
- summary: Unobtrusive nested forms handling using jQuery.
221
+ summary: Unobtrusive Rails nested forms handling, with or without jQuery.
181
222
  test_files: []