cocooned 1.3.1 → 1.4.1

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: 0ff5fc052a1bf37f18e1f22b550f35bf876e0cc09c4c7d4ebef3667c7afde67a
4
- data.tar.gz: 55202ea14bda345f2ed5f20fc95ebb0de1bfa7831e9f70ba92b2a092c7ca210b
3
+ metadata.gz: 92137d75843c047e0aa1ffe5f385217e4ead03225356dc69d205320f948baa06
4
+ data.tar.gz: 4ad82cd191b91ed1d6f313fac56a66b6824bc0c2a0f11056247048d33988d92a
5
5
  SHA512:
6
- metadata.gz: ed672de23fce13326d63cb1e943689ea8f4df8be8873111daecb9a25df954ee06a47592a6b28399c95120e6079337c26125838b42a868395e0d5639d08461569
7
- data.tar.gz: 23b8d0ab6bdfda55e974f322bec4a0a39a9fd089f9bfbfa343144ccdc76639560286a4212ac3018e0ee10a567d70b8f363be5c3c3578e5000440de41a2464e04
6
+ metadata.gz: 02d0e6ea881b5459fa00cb87b8dcba12947805a6bc5a428f0660106dc9d7b0495e18937c50f5fc8fd15d27f994aa0ca5dfe830bfbb22051fbbf7d3a54795041b
7
+ data.tar.gz: b8a0a3a01e109ab0654fcb3b2aaaf8c166a6292f9ea90459ad88b0fe510ce6aac471f134e169395b9882c43bab79df9bc7cca04bcda13996e0d0f678da02c754
data/History.md CHANGED
@@ -1,5 +1,35 @@
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
+
24
+ ## Version 1.3.2
25
+
26
+ * Compatibility with Mongoid 7+
27
+
28
+ ## Version 1.3.1
29
+
30
+ * Use UMD pattern to load the Cocooned module
31
+ * Now publish packages on both rubygems.org and npmjs.com
32
+
3
33
  ## Version 1.3.0
4
34
 
5
35
  ### Breaking changes
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
 
@@ -33,7 +28,11 @@ Inside your `Gemfile` add the following:
33
28
  gem "cocooned"
34
29
  ```
35
30
 
36
- You must also require `cocooned` in your `application.js` and `application.css`, so it compiles with the asset pipeline.
31
+ ### Load Cocooned styles and scripts
32
+
33
+ If you use Sprockets, you have to require `cocooned` in your `application.js` and `application.css`, so it compiles with the asset pipeline.
34
+
35
+ If you use Yarn to manage your non-Ruby dependencies and/or Webpack to build your assets, you can install the [`@notus.sh/cocooned` companion package](https://www.npmjs.com/package/@notus.sh/cocooned).
37
36
 
38
37
  ## Usage
39
38
 
@@ -68,28 +67,34 @@ E.g. in your `ListsController`:
68
67
  end
69
68
  ```
70
69
 
71
- ### Basic form
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).
72
73
 
73
- _Please note examples in this section are written with the [`haml` templating language](http://haml.info/)._
74
+ See the [original merge request](https://github.com/nathanvda/cocoon/pull/247) for more details.
75
+
76
+ ### Basic form
74
77
 
75
78
  [Rails natively supports nested forms](https://guides.rubyonrails.org/form_helpers.html#nested-forms) but does not support adding or removing nested items.
76
79
 
77
- ```haml
78
- / `app/views/lists/_form.html.haml`
79
- = form_for @list do |f|
80
- = f.input :name
80
+ ```erb
81
+ <% # `app/views/lists/_form.html.erb` %>
82
+ <%= form_for @list do |f| %>
83
+ <%= f.input :name %>
81
84
 
82
- %h3 Items
83
- = f.fields_for :tasks do |item_form|
84
- / This block is repeated for every task in @list.items
85
- = item_form.label :description
86
- = item_form.text_field :description
87
- = item_form.check_box :done
88
-
89
- = f.submit "Save"
85
+ <h3>Items</h3>
86
+ <%= f.fields_for :tasks do |item_form| %>
87
+ <% # This block is repeated for every task in @list.items %>
88
+ <%= item_form.label :description %>
89
+ <%= item_form.text_field :description %>
90
+ <%= item_form.check_box :done %>
91
+ <% end %>
92
+
93
+ <%= f.submit "Save" %>
94
+ <% end %>
90
95
  ```
91
96
 
92
- To enable Cocooned on this first, we need to:
97
+ To enable Cocooned on this form, we need to:
93
98
 
94
99
  1. Move the nested form to a partial
95
100
  2. Add a way to add a new item to the collection
@@ -102,78 +107,91 @@ Let's do it.
102
107
 
103
108
  We now have two files:
104
109
 
105
- ```haml
106
- / `app/views/lists/_form.html.haml`
107
- = form_for @list do |form|
108
- = form.input :name
110
+ ```erb
111
+ <% # `app/views/lists/_form.html.erb` %>
112
+ <%= form_for @list do |form| %>
113
+ <%= form.input :name %>
109
114
 
110
- %h3 Items
111
- = form.fields_for :items do |item_form|
112
- = render 'item_fields', f: item_form
115
+ <h3>Items</h3>
116
+ <%= form.fields_for :items do |item_form|
117
+ <%= render 'item_fields', f: item_form %>
118
+ <% end %>
113
119
 
114
- = form.submit "Save"
120
+ <%= form.submit "Save" %>
121
+ <% end %>
115
122
  ```
116
123
 
117
- ```haml
118
- / `app/views/lists/_item_fields.html.haml`
119
- = f.label :description
120
- = f.text_field :description
121
- = f.check_box :done
124
+ ```erb
125
+ <% # `app/views/lists/_item_fields.html.erb` %>
126
+ <%= f.label :description %>
127
+ <%= f.text_field :description %>
128
+ <%= f.check_box :done %>
122
129
  ```
123
130
 
124
131
  #### 2. Add a way to add a new item to the collection
125
132
 
126
- ```haml
127
- / `app/views/lists/_form.html.haml`
128
- = form_for @list do |form|
129
- = form.input :name
133
+ ```erb
134
+ <% # `app/views/lists/_form.html.erb` %>
135
+ <%= form_for @list do |form| %>
136
+ <%= form.input :name %>
130
137
 
131
- %h3 Items
132
- #items
133
- = form.fields_for :tasks do |item_form|
134
- = render 'item_fields', f: item_form
135
- .links
136
- = cocooned_add_item_link 'Add an item', form, :items
138
+ <h3>Items</h3>
139
+ <div id="items">
140
+ <%= form.fields_for :tasks do |item_form| %>
141
+ <%= render 'item_fields', f: item_form %>
142
+ <% end %>
143
+
144
+ <div class="links">
145
+ <%= cocooned_add_item_link 'Add an item', form, :items %>
146
+ </div>
147
+ </div>
137
148
 
138
- = form.submit "Save"
149
+ <%= form.submit "Save" %>
150
+ <% end %>
139
151
  ```
140
152
 
141
153
  By default, a new item will be inserted just before the immediate parent of the 'Add an item' link. You can have a look at the documentation of `cocooned_add_item_link` for more information about how to change that but we'll keep it simple for now.
142
154
 
143
155
  #### 3. Add a way to remove an item from the collection
144
156
 
145
- ```haml
146
- / `app/views/lists/_item_fields.html.haml`
147
- .cocooned-item
148
- = f.label :description
149
- = f.text_field :description
150
- = f.check_box :done
151
- = cocooned_remove_item_link 'Remove', f
157
+ ```erb
158
+ <% # `app/views/lists/_item_fields.html.erb` %>
159
+ <div class="cocooned-item">
160
+ <%= f.label :description %>
161
+ <%= f.text_field :description %>
162
+ <%= f.check_box :done %>
163
+ <%= cocooned_remove_item_link 'Remove', f %>
164
+ </div>
152
165
  ```
153
166
 
154
- The `.cocooned-item` class is required for the `cocooned_remove_item_link` to work correctly.
167
+ The `cocooned-item` class is required for the `cocooned_remove_item_link` to work correctly.
155
168
 
156
169
  #### 4. Initialize Cocooned to handle this form
157
170
 
158
171
  Cocooned will detect on page load forms it should handle and initialize itself.
159
172
  This detection is based on the presence of a `data-cocooned-options` attribute on the nested forms container.
160
173
 
161
- ```haml
162
- / `app/views/lists/_form.html.haml`
163
- = form_for @list do |form|
164
- = form.input :name
174
+ ```erb
175
+ <% # `app/views/lists/_form.html.erb` %>
176
+ <%= form_for @list do |form| %>
177
+ <%= form.input :name %>
165
178
 
166
- %h3 Items
167
- #items{ :data => { cocooned_options: {}.to_json } }
168
- = form.fields_for :tasks do |item_form|
169
- = render 'item_fields', f: item_form
170
- .links
171
- = cocooned_add_item_link 'Add an item', form, :items
179
+ <h3>Items</h3>
180
+ <div id="items" data-cocooned-options="<%= {}.to_json %>">
181
+ <%= form.fields_for :tasks do |item_form| %>
182
+ <%= render 'item_fields', f: item_form %>
183
+ <% end %>
184
+
185
+ <div class="links">
186
+ <%= cocooned_add_item_link 'Add an item', form, :items %>
187
+ </div>
188
+ </div>
172
189
 
173
- = form.submit "Save"
190
+ <%= form.submit "Save" %>
191
+ </div>
174
192
  ```
175
193
 
176
- And we're done!
194
+ You're done!
177
195
 
178
196
  ### Wait, what's the point of `data-cocooned-options` if it's to be empty?
179
197
 
@@ -188,52 +206,63 @@ For now, Cocooned supports two plugins:
188
206
 
189
207
  The limit plugin is autoloaded when needed and does not require anything more than you specifiying the maximum number of items allowed in the association.
190
208
 
191
- ```haml
192
- / `app/views/lists/_form.html.haml`
193
- = form_for @list do |form|
194
- = form.input :name
209
+ ```erb
210
+ <% # `app/views/lists/_form.html.erb` %>
211
+ <%= form_for @list do |form| %>
212
+ <%= form.input :name %>
195
213
 
196
- %h3 Items
197
- #items{ :data => { cocooned_options: { limit: 12 }.to_json } }
198
- = form.fields_for :tasks do |item_form|
199
- = render 'item_fields', f: item_form
200
- .links
201
- = cocooned_add_item_link 'Add an item', form, :items
214
+ <h3>Items</h3>
215
+ <div id="items" data-cocooned-options="<%= { limit: 12 }.to_json %>">
216
+ <%= form.fields_for :tasks do |item_form| %>
217
+ <%= render 'item_fields', f: item_form %>
218
+ <% end %>
219
+
220
+ <div class="links">
221
+ <%= cocooned_add_item_link 'Add an item', form, :items %>
222
+ </div>
223
+ </div>
202
224
 
203
- = form.submit "Save"
225
+ <%= form.submit "Save" %>
226
+ <% end %>
204
227
  ```
205
228
 
206
229
  #### The reorderable plugin
207
230
 
208
231
  The reorderable plugin is autoloaded when activated and does not support any particular options.
209
232
 
210
- ```haml
211
- / `app/views/lists/_form.html.haml`
212
- = form_for @list do |form|
213
- = form.input :name
214
-
215
- %h3 Items
216
- #items{ :data => { cocooned_options: { reorderable: true }.to_json } }
217
- = form.fields_for :tasks do |item_form|
218
- = render 'item_fields', f: item_form
219
- .links
220
- = cocooned_add_item_link 'Add an item', form, :items
233
+ ```erb
234
+ <% # `app/views/lists/_form.html.haml` %>
235
+ <%= form_for @list do |form| %>
236
+ <%= form.input :name %>
221
237
 
222
- = form.submit "Save"
238
+ <h3>Items</h3>
239
+ <div id="items" data-cocooned-options="<%= { reorderable: true }.to_json %>">
240
+ <%= form.fields_for :tasks do |item_form| %>
241
+ <%= render 'item_fields', f: item_form %>
242
+ <% end %>
243
+
244
+ <div class="links">
245
+ <%= cocooned_add_item_link 'Add an item', form, :items %>
246
+ </div>
247
+ </div>
248
+
249
+ <%= form.submit "Save" %>
250
+ <% end %>
223
251
  ```
224
252
 
225
253
  However, you need to edit your nested partial to add the links that allow your users to move an item up or down in the collection and to add a `position` field.
226
254
 
227
- ```haml
228
- / `app/views/lists/_item_fields.html.haml`
229
- .cocooned-item
230
- = f.label :description
231
- = f.text_field :description
232
- = f.check_box :done
233
- = f.hidden_field :position
234
- = cocooned_move_item_up_link 'Up', f
235
- = cocooned_move_item_down_link 'Down', f
236
- = cocooned_remove_item_link 'Remove', f
255
+ ```erb
256
+ <% # `app/views/lists/_item_fields.html.erb` %>
257
+ <div class="cocooned-item">
258
+ <%= f.label :description %>
259
+ <%= f.text_field :description %>
260
+ <%= f.check_box :done %>
261
+ <%= f.hidden_field :position %>
262
+ <%= cocooned_move_item_up_link 'Up', f %>
263
+ <%= cocooned_move_item_down_link 'Down', f %>
264
+ <%= cocooned_remove_item_link 'Remove', f %>
265
+ </div>
237
266
  ```
238
267
 
239
268
  Also, remember the strong parameters gotcha we mentioned earlier.
@@ -284,6 +313,7 @@ The event `event` is an instance of `jQuery.Event` and carry some additional dat
284
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
285
314
  * `event.nodes`, the nested items that will be or just have been reindexed on `cocooned:*-reindex` events, as a jQuery object. Null otherwise.
286
315
  * `event.cocooned`, the Cocooned javascript object instance handling the nested association.
316
+ * `event.originalEvent`, the original (browser) event.
287
317
 
288
318
  The `node` argument is the same jQuery object as `event.node`.
289
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
@@ -79,6 +84,7 @@ namespace :npm do
79
84
  contributors = []
80
85
  spec.authors.each_with_index do |name, i|
81
86
  next if spec.email[i].nil?
87
+
82
88
  contributors << {
83
89
  name: name.dup.force_encoding('UTF-8'),
84
90
  email: spec.email[i].dup.force_encoding('UTF-8')
@@ -100,6 +106,7 @@ namespace :npm do
100
106
  task release: %i[build] do
101
107
  system("npm publish ./pkg/#{npm_scope}-#{spec.name}-#{spec.version}.tgz --access public")
102
108
  end
109
+ # rubocop:enable Rails/RakeEnvironment
103
110
  end
104
111
 
105
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);
@@ -1,5 +1,7 @@
1
1
  /* globals define */
2
2
 
3
+ // Use Universal Module Definition pattern to load Cocooned
4
+ // See https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js
3
5
  (function (root, factory) {
4
6
  if (typeof define === 'function' && define.amd) {
5
7
  // AMD. Register as an anonymous module.
@@ -18,24 +20,34 @@
18
20
  }(typeof self !== 'undefined' ? self : this, function ($) {
19
21
  var Cocooned = function (container, options) {
20
22
  this.container = $(container);
21
- this.options = $.extend({}, this.defaultOptions(), (options || {}));
23
+ var opts = $.extend({}, this.defaultOptions(), (options || {}));
22
24
 
23
25
  // Autoload plugins
24
- for (var moduleName in Cocooned.Plugins) {
25
- if (Cocooned.Plugins.hasOwnProperty(moduleName)) {
26
- var module = Cocooned.Plugins[moduleName];
27
- var optionName = moduleName.charAt(0).toLowerCase() + moduleName.slice(1);
28
-
29
- if (this.options[optionName]) {
30
- for (var method in module) {
31
- if (module.hasOwnProperty(method) && typeof module[method] === 'function') {
32
- 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;
33
39
  }
40
+ if (!plugin.hasOwnProperty(method) || typeof plugin[method] !== 'function') {
41
+ continue;
42
+ }
43
+
44
+ this[method] = plugin[method];
34
45
  }
35
46
  }
36
47
  }
37
48
  }
38
49
 
50
+ this.options = opts;
39
51
  this.init();
40
52
  };
41
53
 
@@ -45,13 +57,13 @@
45
57
  elementsCounter: 0,
46
58
 
47
59
  // Compatibility with Cocoon
48
- // TODO: Remove in 2.0 (Only Cocoon namespaces).
60
+ // TODO: Remove in 3.0 (Only Cocoon namespaces).
49
61
  namespaces: {
50
62
  events: ['cocooned', 'cocoon']
51
63
  },
52
64
 
53
65
  // Compatibility with Cocoon
54
- // TODO: Remove in 2.0 (Only Cocoon class names).
66
+ // TODO: Remove in 3.0 (Only Cocoon class names).
55
67
  classes: {
56
68
  // Actions link
57
69
  add: ['cocooned-add', 'add_fields'],
@@ -208,7 +220,7 @@
208
220
  this.container.addClass(this.classes['container'].join(' '));
209
221
 
210
222
  $(function () { self.hideMarkedForDestruction(); });
211
- $(document).on('page:load turbolinks:load', function () { self.hideMarkedForDestruction(); });
223
+ $(document).on('page:load turbolinks:load turbo:load', function () { self.hideMarkedForDestruction(); });
212
224
  },
213
225
 
214
226
  bindEvents: function () {
@@ -219,7 +231,9 @@
219
231
  this.namespacedNativeEvents('click'),
220
232
  function (e) {
221
233
  e.preventDefault();
222
- self.add(this);
234
+ e.stopPropagation();
235
+
236
+ self.add(this, e);
223
237
  });
224
238
 
225
239
  // Bind remove links
@@ -229,7 +243,9 @@
229
243
  this.selector('remove', '#' + this.container.attr('id') + ' &'),
230
244
  function (e) {
231
245
  e.preventDefault();
232
- self.remove(this);
246
+ e.stopPropagation();
247
+
248
+ self.remove(this, e);
233
249
  });
234
250
 
235
251
  // Bind options events
@@ -241,7 +257,7 @@
241
257
  });
242
258
  },
243
259
 
244
- add: function (adder) {
260
+ add: function (adder, originalEvent) {
245
261
  var $adder = $(adder);
246
262
  var insertionMethod = this.getInsertionMethod($adder);
247
263
  var insertionNode = this.getInsertionNode($adder);
@@ -250,7 +266,7 @@
250
266
 
251
267
  for (var i = 0; i < count; i++) {
252
268
  var contentNode = this.buildContentNode(contentTemplate);
253
- var eventData = { link: $adder, node: contentNode, cocooned: this };
269
+ var eventData = { link: $adder, node: contentNode, cocooned: this, originalEvent: originalEvent };
254
270
  var afterNode = (insertionMethod === 'replaceWith' ? contentNode : insertionNode);
255
271
 
256
272
  // Insertion can be prevented through a 'cocooned:before-insert' event handler
@@ -264,12 +280,12 @@
264
280
  }
265
281
  },
266
282
 
267
- remove: function (remover) {
283
+ remove: function (remover, originalEvent) {
268
284
  var self = this;
269
285
  var $remover = $(remover);
270
286
  var nodeToDelete = this.findItem($remover);
271
287
  var triggerNode = nodeToDelete.parent();
272
- var eventData = { link: $remover, node: nodeToDelete, cocooned: this };
288
+ var eventData = { link: $remover, node: nodeToDelete, cocooned: this, originalEvent: originalEvent };
273
289
 
274
290
  // Deletion can be prevented through a 'cocooned:before-remove' event handler
275
291
  if (!this.notify(triggerNode, 'before-remove', eventData)) {
@@ -314,7 +330,7 @@
314
330
  }
315
331
 
316
332
  e.stopPropagation();
317
- var eventData = { link: e.link, node: e.node, cocooned: cocooned };
333
+ var eventData = { link: e.link, node: e.node, cocooned: cocooned, originalEvent: e };
318
334
  cocooned.notify(cocooned.container, 'limit-reached', eventData);
319
335
  });
320
336
  },
@@ -326,16 +342,24 @@
326
342
 
327
343
  Cocooned.Plugins.Reorderable = {
328
344
 
329
- 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
+ },
330
354
 
331
355
  bindReorderable: function () {
332
356
  var self = this;
333
357
 
334
358
  // Maintain indexes
335
359
  this.container
336
- .on('cocooned:after-insert', function (e) { self.reindex(); })
337
- .on('cocooned:after-remove', function (e) { self.reindex(); })
338
- .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); });
339
363
 
340
364
  // Move items
341
365
  this.container.on(
@@ -343,22 +367,24 @@
343
367
  [this.selector('up'), this.selector('down')].join(', '),
344
368
  function (e) {
345
369
  e.preventDefault();
370
+ e.stopPropagation();
371
+
346
372
  var node = this;
347
373
  var up = self.classes['up'].some(function (klass) {
348
374
  return node.className.indexOf(klass) !== -1;
349
375
  });
350
- self.move(this, up ? 'up' : 'down');
376
+ self.move(this, up ? 'up' : 'down', e);
351
377
  });
352
378
 
353
379
  // Ensure positions are unique before save
354
380
  this.container.closest('form').on(
355
381
  this.namespacedNativeEvents('submit'),
356
382
  function (e) {
357
- self.reindex();
383
+ self.reindex(e);
358
384
  });
359
385
  },
360
386
 
361
- move: function (moveLink, direction) {
387
+ move: function (moveLink, direction, originalEvent) {
362
388
  var self = this;
363
389
  var $mover = $(moveLink);
364
390
  var node = $mover.closest(this.selector('item'));
@@ -371,7 +397,7 @@
371
397
  }
372
398
 
373
399
  // Move can be prevented through a 'cocooned:before-move' event handler
374
- var eventData = { link: $mover, node: node, cocooned: this };
400
+ var eventData = { link: $mover, node: node, cocooned: this, originalEvent: originalEvent };
375
401
  if (!self.notify(node, 'before-move', eventData)) {
376
402
  return false;
377
403
  }
@@ -391,17 +417,17 @@
391
417
  });
392
418
  },
393
419
 
394
- reindex: function () {
395
- var i = 0;
420
+ reindex: function (originalEvent) {
421
+ var i = this.options.reorderable.startAt;
396
422
  var nodes = this.getItems('&:visible');
397
- var eventData = { link: null, nodes: nodes, cocooned: this };
423
+ var eventData = { link: null, nodes: nodes, cocooned: this, originalEvent: originalEvent };
398
424
 
399
425
  // Reindex can be prevented through a 'cocooned:before-reindex' event handler
400
426
  if (!this.notify(this.container, 'before-reindex', eventData)) {
401
427
  return false;
402
428
  }
403
429
 
404
- nodes.each(function () { $('input[id$=_position]', this).val(++i); });
430
+ nodes.each(function () { $('input[id$=_position]', this).val(i++); });
405
431
  this.notify(this.container, 'after-reindex', eventData);
406
432
  },
407
433
 
@@ -433,7 +459,12 @@
433
459
  return;
434
460
  }
435
461
 
436
- 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);
437
468
  container.data('cocooned', cocooned);
438
469
  });
439
470
  };
@@ -441,7 +472,7 @@
441
472
  // On-load initialization
442
473
  $(function () {
443
474
  $('*[data-cocooned-options]').each(function (i, el) {
444
- $(el).cocooned($(el).data('cocooned-options'));
475
+ $(el).cocooned();
445
476
  });
446
477
  });
447
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,29 +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.1'
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'
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'
39
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
@@ -54,11 +52,12 @@ module Cocooned
54
52
  def build_without_reflection
55
53
  methods = %W[build_#{plural_name} build_#{singular_name}].select { |m| form.object.respond_to?(m) }
56
54
  raise "Association #{association} doesn't exist on #{form.object.class}" unless methods.any?
55
+
57
56
  form.object.send(methods.first)
58
57
  end
59
58
 
60
59
  def should_use_conditions?
61
- reflection.class.name == 'Mongoid::Relations::Metadata' || @options[:force_non_association_create]
60
+ reflection.class.name.starts_with?('Mongoid::') || @options[:force_non_association_create]
62
61
  end
63
62
 
64
63
  def build_with_conditions
@@ -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,18 +10,18 @@ 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",
17
- format('. It will dissapear in %s.', release),
19
+ format('. It will dissapear in %<release>s.', release: release),
18
20
  location.nil? ? '' : "\n#{target_and_name} called from #{location}"
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
@@ -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
  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.1'
4
+ VERSION = '1.4.1'
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.1
4
+ version: 1.4.1
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: 2018-08-24 00:00:00.000000000 Z
12
+ date: 2022-08-08 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.1'
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.1'
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
@@ -115,6 +115,62 @@ dependencies:
115
115
  - - ">="
116
116
  - !ruby/object:Gem::Version
117
117
  version: '0'
118
+ - !ruby/object:Gem::Dependency
119
+ name: rubocop-performance
120
+ requirement: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
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'
118
174
  description: Easier nested form. Supports standard Rails forms, Formtastic and SimpleForm.
119
175
  email:
120
176
  - gael-ian@notus.sh
@@ -144,7 +200,7 @@ licenses:
144
200
  - Apache-2.0
145
201
  metadata:
146
202
  allowed_push_host: https://rubygems.org
147
- post_install_message:
203
+ post_install_message:
148
204
  rdoc_options: []
149
205
  require_paths:
150
206
  - lib
@@ -152,16 +208,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
152
208
  requirements:
153
209
  - - ">="
154
210
  - !ruby/object:Gem::Version
155
- version: '0'
211
+ version: '2.5'
156
212
  required_rubygems_version: !ruby/object:Gem::Requirement
157
213
  requirements:
158
214
  - - ">="
159
215
  - !ruby/object:Gem::Version
160
216
  version: '0'
161
217
  requirements: []
162
- rubyforge_project:
163
- rubygems_version: 2.7.6
164
- signing_key:
218
+ rubygems_version: 3.3.7
219
+ signing_key:
165
220
  specification_version: 4
166
- summary: Unobtrusive nested forms handling using jQuery.
221
+ summary: Unobtrusive Rails nested forms handling, with or without jQuery.
167
222
  test_files: []