cocoon 1.0.22 → 1.1.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.
- data/History.md +21 -0
- data/README.markdown +108 -12
- data/VERSION +1 -1
- data/app/assets/javascripts/cocoon.js +16 -9
- data/cocoon.gemspec +4 -3
- data/lib/cocoon/view_helpers.rb +28 -10
- data/spec/cocoon_spec.rb +35 -13
- data/spec/dummy/app/decorators/comment_decorator.rb +17 -0
- metadata +156 -158
data/History.md
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
# Change History / Release Notes
|
2
2
|
|
3
3
|
|
4
|
+
## Version 1.1.0
|
5
|
+
|
6
|
+
* BREAKING: the triggered javascript events `removal-callback`, `after-removal-callback`, and `insertion-callback` are renamed to the more correct and symmetric
|
7
|
+
`cocoon:after-insert, cocoon:before-insert, cocoon:after-remove, cocoon:before-remove`. Also the events are namespaced to prevent collisions with other libraries.
|
8
|
+
* allow created objects to be decorated with a callable. This is especially useful if you are using Draper or some decorator instead of the plain model in your views.
|
9
|
+
* it is now possible to specify a relative node, and use standard jquery traversal methods on insertion
|
10
|
+
* trigger insertion event on correct `insertionNode`
|
11
|
+
* thanks to #90 cocoon now supports non-AR associations and array-fields, you just have to supply your own `build_<association>` methods
|
12
|
+
|
13
|
+
I would really really like to thank all contributors, check them out https://github.com/nathanvda/cocoon/graphs/contributors
|
14
|
+
They made cocoon way more awesome than I could have done in my lonesome.
|
15
|
+
|
16
|
+
## Version 1.0.22
|
17
|
+
|
18
|
+
* Fix that it still works for mongoid
|
19
|
+
|
20
|
+
## Version 1.0.21
|
21
|
+
|
22
|
+
* Use association build methods instead of assoc.klass.new. This avoids mass-assignment errors and other misbehaviors around attribute accessibility.
|
23
|
+
|
24
|
+
|
4
25
|
## Version 1.0.20
|
5
26
|
|
6
27
|
* improved handing of the `:partial`: remove the extra options-hash, and just make it use the single hash, so now we can just write
|
data/README.markdown
CHANGED
@@ -9,6 +9,8 @@ For example a project with its tasks, an invoice with its ordered items.
|
|
9
9
|
|
10
10
|
It is formbuilder-agnostic, so it works with standard Rails, or Formtastic or simple_form.
|
11
11
|
|
12
|
+
This project is not related to [Apache Cocoon](http://cocoon.apache.org/)
|
13
|
+
|
12
14
|
## Prerequisites
|
13
15
|
|
14
16
|
This gem uses jQuery, it is most useful to use this gem in a rails3
|
@@ -26,7 +28,7 @@ Inside your `Gemfile` add the following:
|
|
26
28
|
gem "cocoon"
|
27
29
|
````
|
28
30
|
|
29
|
-
### Rails 3.1
|
31
|
+
### Rails 3.1+
|
30
32
|
|
31
33
|
Add the following to `application.js` so it compiles to the
|
32
34
|
asset_pipeline
|
@@ -35,7 +37,7 @@ asset_pipeline
|
|
35
37
|
//= require cocoon
|
36
38
|
````
|
37
39
|
|
38
|
-
### Rails 3.x
|
40
|
+
### Rails 3.0.x
|
39
41
|
|
40
42
|
If you are using Rails 3.0.x, you need to run the installation task (since rails 3.1 this is no longer needed):
|
41
43
|
|
@@ -96,7 +98,7 @@ We will show the sample usage with the different possible form-builders.
|
|
96
98
|
Inside our `projects/_form` partial we then write:
|
97
99
|
|
98
100
|
````haml
|
99
|
-
|
101
|
+
= f.inputs do
|
100
102
|
= f.input :name
|
101
103
|
= f.input :description
|
102
104
|
%h3 Tasks
|
@@ -105,7 +107,7 @@ Inside our `projects/_form` partial we then write:
|
|
105
107
|
= render 'task_fields', :f => task
|
106
108
|
.links
|
107
109
|
= link_to_add_association 'add task', f, :tasks
|
108
|
-
|
110
|
+
= f.actions do
|
109
111
|
= f.action :submit
|
110
112
|
````
|
111
113
|
|
@@ -125,14 +127,70 @@ There is an example project on github implementing it called [cocoon_formtastic_
|
|
125
127
|
|
126
128
|
### Using simple_form
|
127
129
|
|
128
|
-
|
130
|
+
Inside our `projects/_form` partial we then write:
|
129
131
|
|
130
|
-
|
132
|
+
````haml
|
133
|
+
= simple_form_for @project do |f|
|
134
|
+
= f.input :name
|
135
|
+
= f.input :description
|
136
|
+
%h3 Tasks
|
137
|
+
#tasks
|
138
|
+
= f.simple_fields_for :tasks do |task|
|
139
|
+
= render 'task_fields', :f => task
|
140
|
+
.links
|
141
|
+
= link_to_add_association 'add task', f, :tasks
|
142
|
+
= f.submit
|
143
|
+
````
|
144
|
+
|
145
|
+
and inside the `_task_fields` partial we write:
|
131
146
|
|
147
|
+
````haml
|
148
|
+
.nested-fields
|
149
|
+
= f.input :description
|
150
|
+
= f.input :done, :as => :boolean
|
151
|
+
= link_to_remove_association "remove task", f
|
152
|
+
````
|
153
|
+
|
154
|
+
There is an example project on github implementing it called [cocoon_simple_form_demo](https://github.com/nathanvda/cocoon_simple_form_demo).
|
132
155
|
|
133
156
|
### Using standard rails forms
|
134
157
|
|
135
|
-
|
158
|
+
Inside our `projects/_form` partial we then write:
|
159
|
+
|
160
|
+
````haml
|
161
|
+
- form_for @project do |f|
|
162
|
+
.field
|
163
|
+
= f.label :name
|
164
|
+
%br
|
165
|
+
= f.text_field :name
|
166
|
+
.field
|
167
|
+
= f.label :description
|
168
|
+
%br
|
169
|
+
= f.text_field :description
|
170
|
+
%h3 Tasks
|
171
|
+
#tasks
|
172
|
+
= f.fields_for :tasks do |task|
|
173
|
+
= render 'task_fields', :f => task
|
174
|
+
.links
|
175
|
+
= link_to_add_association 'add task', f, :tasks
|
176
|
+
= f.submit
|
177
|
+
````
|
178
|
+
|
179
|
+
and inside the `_task_fields` partial we write:
|
180
|
+
|
181
|
+
````haml
|
182
|
+
.nested-fields
|
183
|
+
.field
|
184
|
+
= f.label :description
|
185
|
+
%br
|
186
|
+
= f.text_field :description
|
187
|
+
.field
|
188
|
+
= f.check_box :done
|
189
|
+
= f.label :done
|
190
|
+
= link_to_remove_association "remove task", f
|
191
|
+
````
|
192
|
+
|
193
|
+
I will provide a sample project later.
|
136
194
|
|
137
195
|
## How it works
|
138
196
|
|
@@ -150,6 +208,7 @@ It takes four parameters:
|
|
150
208
|
- association: the name of the association (plural) of which a new instance needs to be added (symbol or string).
|
151
209
|
- html_options: extra html-options (see `link_to`)
|
152
210
|
There are some special options, the first three allow to control the placement of the new link-data:
|
211
|
+
- `data-association-insertion-traversal` : the jquery traversal method to allow node selection relative to the link. `closest`, `next`, `children`, etc. Default: absolute selection
|
153
212
|
- `data-association-insertion-node` : the jquery selector of the node
|
154
213
|
- `data-association-insertion-method` : jquery method that inserts the new data. `before`, `after`, `append`, `prepend`, etc. Default: `before`
|
155
214
|
- `data-association-insertion-position` : old method specifying where to insert new data.
|
@@ -165,6 +224,8 @@ Inside the `html_options` you can add an option `:render_options`, and the conta
|
|
165
224
|
form. E.g. especially when using `twitter-bootstrap` and `simple_form` together, the `simple_fields_for` needs the option `:wrapper => 'inline'` which can
|
166
225
|
be handed down as follows:
|
167
226
|
|
227
|
+
(Note: In certain newer versions of simple_form, the option to use is ':wrapper => 'bootstrap')
|
228
|
+
|
168
229
|
````haml
|
169
230
|
= link_to_add_association 'add something', f, :something, :render_options => {:wrapper => 'inline' }
|
170
231
|
````
|
@@ -201,7 +262,14 @@ Optionally you could also leave out the name and supply a block that is captured
|
|
201
262
|
|
202
263
|
### Callbacks (upon insert and remove of items)
|
203
264
|
|
204
|
-
|
265
|
+
On insertion or removal the following events are triggered:
|
266
|
+
|
267
|
+
* `cocoon:before-insert`: called before inserting a new nested child
|
268
|
+
* `cocoon:after-insert`: called after inserting
|
269
|
+
* `cocoon:before-remove`: called before removing the nested child
|
270
|
+
* `cocoon:after-remove`: called after removal
|
271
|
+
|
272
|
+
If in your view you have the following snippet to select an `owner`
|
205
273
|
(we use slim for demonstration purposes)
|
206
274
|
|
207
275
|
````haml
|
@@ -218,17 +286,21 @@ The callbacks can be added as follows:
|
|
218
286
|
|
219
287
|
````javascript
|
220
288
|
$(document).ready(function() {
|
221
|
-
$('#owner').bind('
|
289
|
+
$('#owner').bind('cocoon:before-insert',
|
222
290
|
function() {
|
223
291
|
$("#owner_from_list").hide();
|
224
292
|
$("#owner a.add_fields").hide();
|
225
293
|
});
|
226
|
-
$('#owner').bind(
|
294
|
+
$('#owner').bind('cocoon:after-insert',
|
295
|
+
function() {
|
296
|
+
/* ... do something ... */
|
297
|
+
});
|
298
|
+
$('#owner').bind("cocoon:before-remove",
|
227
299
|
function() {
|
228
300
|
$("#owner_from_list").show();
|
229
301
|
$("#owner a.add_fields").show();
|
230
302
|
});
|
231
|
-
$('#owner').bind("after-
|
303
|
+
$('#owner').bind("cocoon:after-remove",
|
232
304
|
function() {
|
233
305
|
/* e.g. recalculate order of child items */
|
234
306
|
});
|
@@ -236,7 +308,7 @@ $(document).ready(function() {
|
|
236
308
|
````
|
237
309
|
|
238
310
|
Do note that for the callbacks to work there has to be a surrounding container (div), where you can bind the callbacks to.
|
239
|
-
|
311
|
+
|
240
312
|
|
241
313
|
### Control the Insertion behaviour
|
242
314
|
|
@@ -256,6 +328,18 @@ The `association-insertion-node` will determine where to add it. You can choose
|
|
256
328
|
|
257
329
|
The `association-insertion-method` will determine where to add it in relation with the node. Any jQuery DOM Manipulation method can be set but we recommend sticking to any of the following: `before`, `after`, `append`, `prepend`. It is unknown at this time what others would do.
|
258
330
|
|
331
|
+
The `association-insertion-traversal` will allow node selection to be relative to the link.
|
332
|
+
|
333
|
+
For example:
|
334
|
+
|
335
|
+
````javascript
|
336
|
+
$(document).ready(function() {
|
337
|
+
$("#owner a.add_fields").
|
338
|
+
data("association-insertion-method", 'append').
|
339
|
+
data("association-insertion-traversal", 'closest').
|
340
|
+
data("association-insertion-node", '#parent_table');
|
341
|
+
});
|
342
|
+
````
|
259
343
|
|
260
344
|
### Partial
|
261
345
|
|
@@ -280,6 +364,12 @@ There is no limit to the amount of nesting, though.
|
|
280
364
|
* Send me a pull request. Bonus points for topic branches.
|
281
365
|
|
282
366
|
|
367
|
+
## Contributors
|
368
|
+
|
369
|
+
The list of contributors just keeps on growing. [Check it out](https://github.com/nathanvda/cocoon/graphs/contributors) !!
|
370
|
+
I would really really like to thank all of them,
|
371
|
+
they make cocoon more awesome every day. Thanks.
|
372
|
+
|
283
373
|
## Todo
|
284
374
|
|
285
375
|
* add more sample relations: `has_many :through`, `belongs_to`, ...
|
@@ -288,3 +378,9 @@ There is no limit to the amount of nesting, though.
|
|
288
378
|
## Copyright
|
289
379
|
|
290
380
|
Copyright (c) 2010 Nathan Van der Auwera. See LICENSE for details.
|
381
|
+
|
382
|
+
## Not Related To Apache Cocoon
|
383
|
+
|
384
|
+
Please note that this project is not related to the Apache Cocoon web framework project.
|
385
|
+
|
386
|
+
[Apache Cocoon](http://cocoon.apache.org/), Cocoon, and Apache are either registered trademarks or trademarks of the [Apache Software Foundation](http://www.apache.org/) in the United States and/or other countries.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0
|
1
|
+
1.1.0
|
@@ -5,12 +5,12 @@
|
|
5
5
|
content.replace(reg_exp, with_str);
|
6
6
|
}
|
7
7
|
|
8
|
-
function
|
9
|
-
node.trigger('
|
8
|
+
function trigger_before_removal_callback(node) {
|
9
|
+
node.trigger('cocoon:before-remove');
|
10
10
|
}
|
11
11
|
|
12
12
|
function trigger_after_removal_callback(node) {
|
13
|
-
node.trigger('after-
|
13
|
+
node.trigger('cocoon:after-remove');
|
14
14
|
}
|
15
15
|
|
16
16
|
$('.add_fields').live('click', function(e) {
|
@@ -18,9 +18,10 @@
|
|
18
18
|
var $this = $(this),
|
19
19
|
assoc = $this.data('association'),
|
20
20
|
assocs = $this.data('associations'),
|
21
|
-
content = $this.data('template'),
|
21
|
+
content = $this.data('association-insertion-template'),
|
22
22
|
insertionMethod = $this.data('association-insertion-method') || $this.data('association-insertion-position') || 'before';
|
23
23
|
insertionNode = $this.data('association-insertion-node'),
|
24
|
+
insertionTraversal = $this.data('association-insertion-traversal'),
|
24
25
|
regexp_braced = new RegExp('\\[new_' + assoc + '\\]', 'g'),
|
25
26
|
regexp_underscord = new RegExp('_new_' + assoc + '_', 'g'),
|
26
27
|
new_id = new Date().getTime(),
|
@@ -37,25 +38,31 @@
|
|
37
38
|
new_content = new_content.replace(regexp_underscord, newcontent_underscord);
|
38
39
|
|
39
40
|
if (insertionNode){
|
40
|
-
|
41
|
+
if (insertionTraversal){
|
42
|
+
insertionNode = $this[insertionTraversal](insertionNode)
|
43
|
+
} else {
|
44
|
+
insertionNode = insertionNode == "this" ? $this : $(insertionNode);
|
45
|
+
}
|
41
46
|
} else {
|
42
47
|
insertionNode = $this.parent();
|
43
48
|
}
|
44
49
|
|
45
50
|
var contentNode = $(new_content);
|
46
51
|
|
52
|
+
insertionNode.trigger('cocoon:before-insert');
|
53
|
+
|
47
54
|
// allow any of the jquery dom manipulation methods (after, before, append, prepend, etc)
|
48
55
|
// to be called on the node. allows the insertion node to be the parent of the inserted
|
49
56
|
// code and doesn't force it to be a sibling like after/before does. default: 'before'
|
50
57
|
insertionNode[insertionMethod](contentNode);
|
51
58
|
|
52
|
-
|
59
|
+
insertionNode.trigger('cocoon:after-insert');
|
53
60
|
});
|
54
61
|
|
55
62
|
$('.remove_fields.dynamic').live('click', function(e) {
|
56
63
|
var $this = $(this);
|
57
64
|
var trigger_node = $this.closest(".nested-fields").parent();
|
58
|
-
|
65
|
+
trigger_before_removal_callback(trigger_node);
|
59
66
|
e.preventDefault();
|
60
67
|
$this.closest(".nested-fields").remove();
|
61
68
|
trigger_after_removal_callback(trigger_node);
|
@@ -64,11 +71,11 @@
|
|
64
71
|
$('.remove_fields.existing').live('click', function(e) {
|
65
72
|
var $this = $(this);
|
66
73
|
var trigger_node = $this.closest(".nested-fields").parent().parent();
|
67
|
-
|
74
|
+
trigger_before_removal_callback(trigger_node);
|
68
75
|
e.preventDefault();
|
69
76
|
$this.prev("input[type=hidden]").val("1");
|
70
77
|
$this.closest(".nested-fields").hide();
|
71
78
|
trigger_after_removal_callback(trigger_node);
|
72
79
|
});
|
73
80
|
|
74
|
-
})(jQuery);
|
81
|
+
})(jQuery);
|
data/cocoon.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "cocoon"
|
8
|
-
s.version = "1.0
|
8
|
+
s.version = "1.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Nathan Van der Auwera"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-10-08"
|
13
13
|
s.description = "Unobtrusive nested forms handling, using jQuery. Use this and discover cocoon-heaven."
|
14
14
|
s.email = "nathan@dixis.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
"spec/cocoon_spec.rb",
|
32
32
|
"spec/dummy/Rakefile",
|
33
33
|
"spec/dummy/app/controllers/application_controller.rb",
|
34
|
+
"spec/dummy/app/decorators/comment_decorator.rb",
|
34
35
|
"spec/dummy/app/helpers/application_helper.rb",
|
35
36
|
"spec/dummy/app/models/comment.rb",
|
36
37
|
"spec/dummy/app/models/person.rb",
|
@@ -73,7 +74,7 @@ Gem::Specification.new do |s|
|
|
73
74
|
]
|
74
75
|
s.homepage = "http://github.com/nathanvda/cocoon"
|
75
76
|
s.require_paths = ["lib"]
|
76
|
-
s.rubygems_version = "1.8.
|
77
|
+
s.rubygems_version = "1.8.24"
|
77
78
|
s.summary = "gem that enables easier nested forms with standard forms, formtastic and simple-form"
|
78
79
|
|
79
80
|
if s.respond_to? :specification_version then
|
data/lib/cocoon/view_helpers.rb
CHANGED
@@ -67,13 +67,19 @@ module Cocoon
|
|
67
67
|
render_options = html_options.delete(:render_options)
|
68
68
|
render_options ||= {}
|
69
69
|
override_partial = html_options.delete(:partial)
|
70
|
+
wrap_object = html_options.delete(:wrap_object)
|
70
71
|
|
71
72
|
html_options[:class] = [html_options[:class], "add_fields"].compact.join(' ')
|
72
73
|
html_options[:'data-association'] = association.to_s.singularize
|
73
74
|
html_options[:'data-associations'] = association.to_s.pluralize
|
74
75
|
|
75
|
-
|
76
|
-
|
76
|
+
if wrap_object.respond_to?(:call)
|
77
|
+
new_object = wrap_object.call(create_object(f, association))
|
78
|
+
else
|
79
|
+
new_object = create_object(f, association)
|
80
|
+
end
|
81
|
+
|
82
|
+
html_options[:'data-association-insertion-template'] = CGI.escapeHTML(render_association(association, f, new_object, render_options, override_partial)).html_safe
|
77
83
|
|
78
84
|
link_to(name, '#', html_options )
|
79
85
|
end
|
@@ -86,12 +92,28 @@ module Cocoon
|
|
86
92
|
def create_object(f, association)
|
87
93
|
assoc = f.object.class.reflect_on_association(association)
|
88
94
|
|
89
|
-
|
90
|
-
|
91
|
-
|
95
|
+
assoc ? create_object_on_association(f, association, assoc) : create_object_on_non_association(f, association)
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_partial_path(partial, association)
|
99
|
+
partial ? partial : association.to_s.singularize + "_fields"
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def create_object_on_non_association(f, association)
|
105
|
+
builder_method = %W{build_#{association} build_#{association.to_s.singularize}}.select { |m| f.object.respond_to?(m) }.first
|
106
|
+
return f.object.send(builder_method) if builder_method
|
107
|
+
raise "Association #{association} doesn't exist on #{f.object.class}"
|
108
|
+
end
|
109
|
+
|
110
|
+
def create_object_on_association(f, association, instance)
|
111
|
+
if instance.class.name == "Mongoid::Relations::Metadata"
|
112
|
+
conditions = instance.respond_to?(:conditions) ? instance.conditions.flatten : []
|
113
|
+
instance.klass.new(*conditions)
|
92
114
|
else
|
93
115
|
# assume ActiveRecord or compatible
|
94
|
-
if
|
116
|
+
if instance.collection?
|
95
117
|
f.object.send(association).build
|
96
118
|
else
|
97
119
|
f.object.send("build_#{association}")
|
@@ -99,9 +121,5 @@ module Cocoon
|
|
99
121
|
end
|
100
122
|
end
|
101
123
|
|
102
|
-
def get_partial_path(partial, association)
|
103
|
-
partial ? partial : association.to_s.singularize + "_fields"
|
104
|
-
end
|
105
|
-
|
106
124
|
end
|
107
125
|
end
|
data/spec/cocoon_spec.rb
CHANGED
@@ -21,19 +21,25 @@ describe Cocoon do
|
|
21
21
|
context "without a block" do
|
22
22
|
it "accepts a name" do
|
23
23
|
result = @tester.link_to_add_association('add something', @form_obj, :comments)
|
24
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
24
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form<tag>" data-association="comment" data-associations="comments">add something</a>'
|
25
25
|
end
|
26
26
|
|
27
27
|
it "accepts html options and pass them to link_to" do
|
28
28
|
result = @tester.link_to_add_association('add something', @form_obj, :comments, {:class => 'something silly'})
|
29
|
-
result.to_s.should == '<a href="#" class="something silly add_fields" data-association="
|
29
|
+
result.to_s.should == '<a href="#" class="something silly add_fields" data-association-insertion-template="form<tag>" data-association="comment" data-associations="comments">add something</a>'
|
30
30
|
end
|
31
31
|
|
32
32
|
it "allows to explicitly hand the wanted partial" do
|
33
33
|
@tester.unstub(:render_association)
|
34
34
|
@tester.should_receive(:render_association).with(anything(), anything(), anything(), anything(), "shared/partial").and_return('partiallll')
|
35
35
|
result = @tester.link_to_add_association('add something', @form_obj, :comments, :partial => "shared/partial")
|
36
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
36
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="partiallll" data-association="comment" data-associations="comments">add something</a>'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "gives an opportunity to wrap/decorate created objects" do
|
40
|
+
@tester.unstub(:render_association)
|
41
|
+
@tester.should_receive(:render_association).with(anything(), anything(), kind_of(CommentDecorator), anything(), anything()).and_return('partiallll')
|
42
|
+
@tester.link_to_add_association('add something', @form_obj, :comments, :wrap_object => Proc.new {|comment| CommentDecorator.new(comment) })
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
@@ -42,14 +48,14 @@ describe Cocoon do
|
|
42
48
|
result = @tester.link_to_add_association(@form_obj, :comments) do
|
43
49
|
"some long name"
|
44
50
|
end
|
45
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
51
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form<tag>" data-association="comment" data-associations="comments">some long name</a>'
|
46
52
|
end
|
47
53
|
|
48
54
|
it "accepts html options and pass them to link_to" do
|
49
55
|
result = @tester.link_to_add_association(@form_obj, :comments, {:class => 'floppy disk'}) do
|
50
56
|
"some long name"
|
51
57
|
end
|
52
|
-
result.to_s.should == '<a href="#" class="floppy disk add_fields" data-association="
|
58
|
+
result.to_s.should == '<a href="#" class="floppy disk add_fields" data-association-insertion-template="form<tag>" data-association="comment" data-associations="comments">some long name</a>'
|
53
59
|
end
|
54
60
|
|
55
61
|
it "allows to explicitly hand the wanted partial" do
|
@@ -58,21 +64,21 @@ describe Cocoon do
|
|
58
64
|
result = @tester.link_to_add_association( @form_obj, :comments, :class => 'floppy disk', :partial => "shared/partial") do
|
59
65
|
"some long name"
|
60
66
|
end
|
61
|
-
result.to_s.should == '<a href="#" class="floppy disk add_fields" data-association="
|
67
|
+
result.to_s.should == '<a href="#" class="floppy disk add_fields" data-association-insertion-template="partiallll" data-association="comment" data-associations="comments">some long name</a>'
|
62
68
|
end
|
63
69
|
end
|
64
70
|
|
65
71
|
context "with an irregular plural" do
|
66
72
|
it "uses the correct plural" do
|
67
73
|
result = @tester.link_to_add_association('add something', @form_obj, :people)
|
68
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
74
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form<tag>" data-association="person" data-associations="people">add something</a>'
|
69
75
|
end
|
70
76
|
end
|
71
77
|
|
72
78
|
context "when using aliased association and class-name" do
|
73
79
|
it "uses the correct name" do
|
74
80
|
result = @tester.link_to_add_association('add something', @form_obj, :admin_comments)
|
75
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
81
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form<tag>" data-association="admin_comment" data-associations="admin_comments">add something</a>'
|
76
82
|
end
|
77
83
|
end
|
78
84
|
|
@@ -84,7 +90,7 @@ describe Cocoon do
|
|
84
90
|
it "uses the correct plural" do
|
85
91
|
@tester.should_receive(:render_association).with(:people, @form_obj, anything, {:wrapper => 'inline'}, nil)
|
86
92
|
result = @tester.link_to_add_association('add something', @form_obj, :people, :render_options => {:wrapper => 'inline'})
|
87
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
93
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form<tag>" data-association="person" data-associations="people">add something</a>'
|
88
94
|
end
|
89
95
|
end
|
90
96
|
|
@@ -94,14 +100,14 @@ describe Cocoon do
|
|
94
100
|
@form_obj.should_receive(:fields_for) { | association, new_object, options_hash, &block| block.call }
|
95
101
|
@tester.should_receive(:render).with("person_fields", {:f=>nil, :dynamic=>true, :alfred=>"Judoka"}).and_return ("partiallll")
|
96
102
|
result = @tester.link_to_add_association('add something', @form_obj, :people, :render_options => {:wrapper => 'inline', :locals => {:alfred => 'Judoka'}})
|
97
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
103
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="partiallll" data-association="person" data-associations="people">add something</a>'
|
98
104
|
end
|
99
105
|
it "if no locals are given it still works" do
|
100
106
|
@tester.unstub(:render_association)
|
101
107
|
@form_obj.should_receive(:fields_for) { | association, new_object, options_hash, &block| block.call }
|
102
108
|
@tester.should_receive(:render).with("person_fields", {:f=>nil, :dynamic=>true}).and_return ("partiallll")
|
103
109
|
result = @tester.link_to_add_association('add something', @form_obj, :people, :render_options => {:wrapper => 'inline'})
|
104
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
110
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="partiallll" data-association="person" data-associations="people">add something</a>'
|
105
111
|
end
|
106
112
|
end
|
107
113
|
|
@@ -116,7 +122,7 @@ describe Cocoon do
|
|
116
122
|
@form_obj.should_receive(:semantic_fields_for)
|
117
123
|
@form_obj.should_receive(:fields_for).never
|
118
124
|
result = @tester.link_to_add_association('add something', @form_obj, :people)
|
119
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
125
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form<tagzzz>" data-association="person" data-associations="people">add something</a>'
|
120
126
|
|
121
127
|
end
|
122
128
|
end
|
@@ -132,7 +138,7 @@ describe Cocoon do
|
|
132
138
|
@form_obj.should_receive(:simple_fields_for)
|
133
139
|
@form_obj.should_receive(:fields_for).never
|
134
140
|
result = @tester.link_to_add_association('add something', @form_obj, :people)
|
135
|
-
result.to_s.should == '<a href="#" class="add_fields" data-association="
|
141
|
+
result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form<tagxxx>" data-association="person" data-associations="people">add something</a>'
|
136
142
|
|
137
143
|
end
|
138
144
|
end
|
@@ -185,6 +191,22 @@ describe Cocoon do
|
|
185
191
|
result = @tester.create_object(stub(:object => Comment.new), :post)
|
186
192
|
result.should be_a Post
|
187
193
|
end
|
194
|
+
|
195
|
+
it "should raise error if cannot reflect on association" do
|
196
|
+
expect { @tester.create_object(stub(:object => Comment.new), :not_existing) }.to raise_error /association/i
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should create an association if object responds to 'build_association' as singular" do
|
200
|
+
object = Comment.new
|
201
|
+
object.should_receive(:build_custom_item).and_return 'custom'
|
202
|
+
@tester.create_object(stub(:object => object), :custom_item).should == 'custom'
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should create an association if object responds to 'build_association' as plural" do
|
206
|
+
object = Comment.new
|
207
|
+
object.should_receive(:build_custom_item).and_return 'custom'
|
208
|
+
@tester.create_object(stub(:object => object), :custom_items).should == 'custom'
|
209
|
+
end
|
188
210
|
end
|
189
211
|
|
190
212
|
context "get_partial_path" do
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CommentDecorator
|
2
|
+
def initialize(comment)
|
3
|
+
@comment = comment
|
4
|
+
end
|
5
|
+
|
6
|
+
def formatted_created_at
|
7
|
+
@comment.created_at.to_formatted_s(:short)
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(method_sym, *args)
|
11
|
+
if @comment.respond_to?(method_sym)
|
12
|
+
@comment.send(method_sym, *args)
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,181 +1,184 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: cocoon
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 22
|
10
|
-
version: 1.0.22
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Nathan Van der Auwera
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
requirement:
|
12
|
+
date: 2012-10-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
22
17
|
none: false
|
23
|
-
requirements:
|
24
|
-
- -
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
hash: 7
|
27
|
-
segments:
|
28
|
-
- 3
|
29
|
-
- 0
|
30
|
-
- 0
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
31
21
|
version: 3.0.0
|
32
|
-
version_requirements: *id001
|
33
|
-
name: rails
|
34
|
-
prerelease: false
|
35
22
|
type: :development
|
36
|
-
|
37
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
25
|
none: false
|
39
|
-
requirements:
|
40
|
-
- -
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
|
43
|
-
|
44
|
-
- 0
|
45
|
-
version: "0"
|
46
|
-
version_requirements: *id002
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.0.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
47
31
|
name: sqlite3-ruby
|
48
|
-
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
49
38
|
type: :development
|
50
|
-
|
51
|
-
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
41
|
none: false
|
53
|
-
requirements:
|
54
|
-
- -
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
|
57
|
-
|
58
|
-
- 0
|
59
|
-
version: "0"
|
60
|
-
version_requirements: *id003
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
61
47
|
name: json_pure
|
62
|
-
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
63
54
|
type: :development
|
64
|
-
|
65
|
-
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
57
|
none: false
|
67
|
-
requirements:
|
68
|
-
- -
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
|
71
|
-
|
72
|
-
- 0
|
73
|
-
version: "0"
|
74
|
-
version_requirements: *id004
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
75
63
|
name: jeweler
|
76
|
-
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
77
70
|
type: :development
|
78
|
-
|
79
|
-
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
73
|
none: false
|
81
|
-
requirements:
|
82
|
-
- -
|
83
|
-
- !ruby/object:Gem::Version
|
84
|
-
|
85
|
-
|
86
|
-
- 2
|
87
|
-
- 6
|
88
|
-
- 0
|
89
|
-
version: 2.6.0
|
90
|
-
version_requirements: *id005
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
91
79
|
name: rspec-rails
|
92
|
-
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.6.0
|
93
86
|
type: :development
|
94
|
-
|
95
|
-
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
89
|
none: false
|
97
|
-
requirements:
|
98
|
-
- -
|
99
|
-
- !ruby/object:Gem::Version
|
100
|
-
hash: 23
|
101
|
-
segments:
|
102
|
-
- 2
|
103
|
-
- 6
|
104
|
-
- 0
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
105
93
|
version: 2.6.0
|
106
|
-
|
94
|
+
- !ruby/object:Gem::Dependency
|
107
95
|
name: rspec
|
108
|
-
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.6.0
|
109
102
|
type: :development
|
110
|
-
|
111
|
-
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
112
105
|
none: false
|
113
|
-
requirements:
|
114
|
-
- -
|
115
|
-
- !ruby/object:Gem::Version
|
116
|
-
|
117
|
-
|
118
|
-
- 3
|
119
|
-
- 0
|
120
|
-
- 0
|
121
|
-
version: 3.0.0
|
122
|
-
version_requirements: *id007
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.6.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
123
111
|
name: actionpack
|
124
|
-
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 3.0.0
|
125
118
|
type: :development
|
126
|
-
|
127
|
-
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
121
|
none: false
|
129
|
-
requirements:
|
130
|
-
- -
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
|
133
|
-
|
134
|
-
- 0
|
135
|
-
version: "0"
|
136
|
-
version_requirements: *id008
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 3.0.0
|
126
|
+
- !ruby/object:Gem::Dependency
|
137
127
|
name: simplecov
|
138
|
-
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
139
134
|
type: :development
|
140
|
-
|
141
|
-
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
142
137
|
none: false
|
143
|
-
requirements:
|
144
|
-
- -
|
145
|
-
- !ruby/object:Gem::Version
|
146
|
-
|
147
|
-
|
148
|
-
- 0
|
149
|
-
version: "0"
|
150
|
-
version_requirements: *id009
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
151
143
|
name: generator_spec
|
152
|
-
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
153
150
|
type: :development
|
154
|
-
|
155
|
-
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
156
153
|
none: false
|
157
|
-
requirements:
|
158
|
-
- -
|
159
|
-
- !ruby/object:Gem::Version
|
160
|
-
|
161
|
-
|
162
|
-
- 2
|
163
|
-
- 0
|
164
|
-
- 0
|
165
|
-
version: 2.0.0
|
166
|
-
version_requirements: *id010
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
167
159
|
name: rspec
|
168
|
-
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: 2.0.0
|
169
166
|
type: :development
|
170
|
-
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 2.0.0
|
174
|
+
description: Unobtrusive nested forms handling, using jQuery. Use this and discover
|
175
|
+
cocoon-heaven.
|
171
176
|
email: nathan@dixis.com
|
172
177
|
executables: []
|
173
|
-
|
174
178
|
extensions: []
|
175
|
-
|
176
|
-
extra_rdoc_files:
|
179
|
+
extra_rdoc_files:
|
177
180
|
- README.markdown
|
178
|
-
files:
|
181
|
+
files:
|
179
182
|
- .travis.yml
|
180
183
|
- Gemfile
|
181
184
|
- History.md
|
@@ -191,6 +194,7 @@ files:
|
|
191
194
|
- spec/cocoon_spec.rb
|
192
195
|
- spec/dummy/Rakefile
|
193
196
|
- spec/dummy/app/controllers/application_controller.rb
|
197
|
+
- spec/dummy/app/decorators/comment_decorator.rb
|
194
198
|
- spec/dummy/app/helpers/application_helper.rb
|
195
199
|
- spec/dummy/app/models/comment.rb
|
196
200
|
- spec/dummy/app/models/person.rb
|
@@ -232,36 +236,30 @@ files:
|
|
232
236
|
- spec/spec_helper.rb
|
233
237
|
homepage: http://github.com/nathanvda/cocoon
|
234
238
|
licenses: []
|
235
|
-
|
236
239
|
post_install_message:
|
237
240
|
rdoc_options: []
|
238
|
-
|
239
|
-
require_paths:
|
241
|
+
require_paths:
|
240
242
|
- lib
|
241
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
243
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
242
244
|
none: false
|
243
|
-
requirements:
|
244
|
-
- -
|
245
|
-
- !ruby/object:Gem::Version
|
246
|
-
|
247
|
-
segments:
|
245
|
+
requirements:
|
246
|
+
- - ! '>='
|
247
|
+
- !ruby/object:Gem::Version
|
248
|
+
version: '0'
|
249
|
+
segments:
|
248
250
|
- 0
|
249
|
-
|
250
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
251
|
+
hash: -4012454906623253900
|
252
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
251
253
|
none: false
|
252
|
-
requirements:
|
253
|
-
- -
|
254
|
-
- !ruby/object:Gem::Version
|
255
|
-
|
256
|
-
segments:
|
257
|
-
- 0
|
258
|
-
version: "0"
|
254
|
+
requirements:
|
255
|
+
- - ! '>='
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: '0'
|
259
258
|
requirements: []
|
260
|
-
|
261
259
|
rubyforge_project:
|
262
|
-
rubygems_version: 1.8.
|
260
|
+
rubygems_version: 1.8.24
|
263
261
|
signing_key:
|
264
262
|
specification_version: 3
|
265
|
-
summary: gem that enables easier nested forms with standard forms, formtastic and
|
263
|
+
summary: gem that enables easier nested forms with standard forms, formtastic and
|
264
|
+
simple-form
|
266
265
|
test_files: []
|
267
|
-
|