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 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: []