cocoon 1.0.22 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|