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 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
- - f.inputs do
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
- -f.actions do
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
- This is almost identical to formtastic, instead of writing `semantic_fields_for` you write `simple_fields_for`.
130
+ Inside our `projects/_form` partial we then write:
129
131
 
130
- There is an example project on github implementing it called [cocoon_simple_form_demo](https://github.com/nathanvda/cocoon_simple_form_demo).
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
- I will provide a full example (and a sample project) later.
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
- There is an option to add a callback on insertion or removal. If in your view you have the following snippet to select an `owner`
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('insertion-callback',
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("removal-callback",
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-removal-callback",
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
- Note that the default `removal-callback` is called _before_ removing the nested item.
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.22
1
+ 1.1.0
@@ -5,12 +5,12 @@
5
5
  content.replace(reg_exp, with_str);
6
6
  }
7
7
 
8
- function trigger_removal_callback(node) {
9
- node.trigger('removal-callback');
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-removal-callback');
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
- insertionNode = insertionNode == "this" ? $this : $(insertionNode);
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
- $this.parent().trigger('insertion-callback');
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
- trigger_removal_callback(trigger_node);
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
- trigger_removal_callback(trigger_node);
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.22"
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-05-20"
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.15"
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
@@ -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
- new_object = create_object(f, association)
76
- html_options[:'data-template'] = CGI.escapeHTML(render_association(association, f, new_object, render_options, override_partial)).html_safe
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
- if assoc.class.name == "Mongoid::Relations::Metadata"
90
- conditions = assoc.respond_to?(:conditions) ? assoc.conditions.flatten : []
91
- assoc.klass.new(*conditions)
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 assoc.collection?
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="comment" data-associations="comments" data-template="form&lt;tag&gt;">add something</a>'
24
+ result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form&lt;tag&gt;" 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="comment" data-associations="comments" data-template="form&lt;tag&gt;">add something</a>'
29
+ result.to_s.should == '<a href="#" class="something silly add_fields" data-association-insertion-template="form&lt;tag&gt;" 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="comment" data-associations="comments" data-template="partiallll">add something</a>'
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="comment" data-associations="comments" data-template="form&lt;tag&gt;">some long name</a>'
51
+ result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form&lt;tag&gt;" 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="comment" data-associations="comments" data-template="form&lt;tag&gt;">some long name</a>'
58
+ result.to_s.should == '<a href="#" class="floppy disk add_fields" data-association-insertion-template="form&lt;tag&gt;" 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="comment" data-associations="comments" data-template="partiallll">some long name</a>'
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="person" data-associations="people" data-template="form&lt;tag&gt;">add something</a>'
74
+ result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form&lt;tag&gt;" 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="admin_comment" data-associations="admin_comments" data-template="form&lt;tag&gt;">add something</a>'
81
+ result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form&lt;tag&gt;" 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="person" data-associations="people" data-template="form&lt;tag&gt;">add something</a>'
93
+ result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form&lt;tag&gt;" 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="person" data-associations="people" data-template="partiallll">add something</a>'
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="person" data-associations="people" data-template="partiallll">add something</a>'
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="person" data-associations="people" data-template="form&lt;tagzzz&gt;">add something</a>'
125
+ result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form&lt;tagzzz&gt;" 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="person" data-associations="people" data-template="form&lt;tagxxx&gt;">add something</a>'
141
+ result.to_s.should == '<a href="#" class="add_fields" data-association-insertion-template="form&lt;tagxxx&gt;" 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
- hash: 59
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
- date: 2012-05-20 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- requirement: &id001 !ruby/object:Gem::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
- - !ruby/object:Gem::Dependency
37
- requirement: &id002 !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
38
25
  none: false
39
- requirements:
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- hash: 3
43
- segments:
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
- prerelease: false
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
- - !ruby/object:Gem::Dependency
51
- requirement: &id003 !ruby/object:Gem::Requirement
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
52
41
  none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- hash: 3
57
- segments:
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
- prerelease: false
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
- - !ruby/object:Gem::Dependency
65
- requirement: &id004 !ruby/object:Gem::Requirement
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
66
57
  none: false
67
- requirements:
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- hash: 3
71
- segments:
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
- prerelease: false
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
- - !ruby/object:Gem::Dependency
79
- requirement: &id005 !ruby/object:Gem::Requirement
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
80
73
  none: false
81
- requirements:
82
- - - ">="
83
- - !ruby/object:Gem::Version
84
- hash: 23
85
- segments:
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
- prerelease: false
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
- - !ruby/object:Gem::Dependency
95
- requirement: &id006 !ruby/object:Gem::Requirement
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
- version_requirements: *id006
94
+ - !ruby/object:Gem::Dependency
107
95
  name: rspec
108
- prerelease: false
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
- - !ruby/object:Gem::Dependency
111
- requirement: &id007 !ruby/object:Gem::Requirement
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
112
105
  none: false
113
- requirements:
114
- - - ">="
115
- - !ruby/object:Gem::Version
116
- hash: 7
117
- segments:
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
- prerelease: false
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
- - !ruby/object:Gem::Dependency
127
- requirement: &id008 !ruby/object:Gem::Requirement
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
128
121
  none: false
129
- requirements:
130
- - - ">="
131
- - !ruby/object:Gem::Version
132
- hash: 3
133
- segments:
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
- prerelease: false
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
- - !ruby/object:Gem::Dependency
141
- requirement: &id009 !ruby/object:Gem::Requirement
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
142
137
  none: false
143
- requirements:
144
- - - ">="
145
- - !ruby/object:Gem::Version
146
- hash: 3
147
- segments:
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
- prerelease: false
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
- - !ruby/object:Gem::Dependency
155
- requirement: &id010 !ruby/object:Gem::Requirement
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
156
153
  none: false
157
- requirements:
158
- - - ">="
159
- - !ruby/object:Gem::Version
160
- hash: 15
161
- segments:
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
- prerelease: false
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
- description: Unobtrusive nested forms handling, using jQuery. Use this and discover cocoon-heaven.
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
- hash: 3
247
- segments:
245
+ requirements:
246
+ - - ! '>='
247
+ - !ruby/object:Gem::Version
248
+ version: '0'
249
+ segments:
248
250
  - 0
249
- version: "0"
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
- hash: 3
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.15
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 simple-form
263
+ summary: gem that enables easier nested forms with standard forms, formtastic and
264
+ simple-form
266
265
  test_files: []
267
-