From 2350ae3ba24216818487da4a5e273f89313142e1 Mon Sep 17 00:00:00 2001 From: nathanvda Date: Thu, 22 Nov 2012 01:32:33 +0100 Subject: [PATCH 01/11] Regenerate gemspec for version 1.1.1 --- cocoon.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocoon.gemspec b/cocoon.gemspec index e582d6c..138274c 100644 --- a/cocoon.gemspec +++ b/cocoon.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = "cocoon" - s.version = "1.1.0" + s.version = "1.1.1" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nathan Van der Auwera"] - s.date = "2012-10-08" + s.date = "2012-11-22" s.description = "Unobtrusive nested forms handling, using jQuery. Use this and discover cocoon-heaven." s.email = "nathan@dixis.com" s.extra_rdoc_files = [ From ed8154d2faf3a77a08c1d338ade4eca3d32a7b34 Mon Sep 17 00:00:00 2001 From: nathanvda Date: Thu, 22 Nov 2012 01:31:19 +0100 Subject: [PATCH 02/11] Added option `:force_non_association_create`. This option will allow users to use `link_to_add_association` in the nested fields loop, since it will not add the new object to the association. Fixes #66. By default we will still create the object in the association, as I believe that is the cleanest. --- README.markdown | 17 ++++++ lib/cocoon/view_helpers.rb | 28 ++++++---- spec/cocoon_spec.rb | 111 ++++++++++++++++++++++--------------- 3 files changed, 100 insertions(+), 56 deletions(-) diff --git a/README.markdown b/README.markdown index 3a798e6..104f9c8 100644 --- a/README.markdown +++ b/README.markdown @@ -217,6 +217,7 @@ It takes four parameters: - `render_options` : options passed through to the form-builder function (e.g. `simple_fields_for`, `semantic_fields_for` or `fields_for`). If it contains a `:locals` option containing a hash, that is handed to the partial. - `wrap_object` : a proc that will allow to wrap your object, especially useful if you are using decorators (e.g. draper). See example lower. + - `force_non_association_create`: if true, it will _not_ create the new object using the association (see lower) Optionally you could also leave out the name and supply a block that is captured to give the name (if you want to do something more complicated). @@ -288,6 +289,22 @@ link_to_add_association('add something', @form_obj, :comments, :wrap_object => Proc.new { |comment| comment.name = current_user.name; comment }) ``` +#### :force_non_association_create + +In normal cases we create a new nested object using the association relation itself. This is the cleanest way to create +a new nested object. But this has a side-effect: for each call of `link_to_add_association` a new element is added to the association. + +In most cases this is not a problem, but if you want to render a `link_to_add_association` for each nested element this will result +in an infinite loop. + +To resolve this, specify that `:force_non_association_create` should be `true`, as follows: + +``` +link_to_add_association('add something', @form_obj, :comments, :force_non_association_create => true) +``` + +By default `:force_non_association_create` is `false`. + > A cleaner option would be to call a function that performs this initialisation and returns `self` at the end. ### link_to_remove_association diff --git a/lib/cocoon/view_helpers.rb b/lib/cocoon/view_helpers.rb index f83a207..26767e9 100644 --- a/lib/cocoon/view_helpers.rb +++ b/lib/cocoon/view_helpers.rb @@ -50,6 +50,8 @@ module Cocoon # - *:render_options* : options passed to `simple_fields_for, semantic_fields_for or fields_for` # - *:locals* : the locals hash in the :render_options is handed to the partial # - *:partial* : explicitly override the default partial name + # - *:wrap_object : !!! document more here !!! + # - *!!!add some option to build in collection or not!!!* # - *&block*: see link_to def link_to_add_association(*args, &block) @@ -68,16 +70,14 @@ module Cocoon render_options ||= {} override_partial = html_options.delete(:partial) wrap_object = html_options.delete(:wrap_object) + force_non_association_create = html_options.delete(:force_non_association_create) || false html_options[:class] = [html_options[:class], "add_fields"].compact.join(' ') html_options[:'data-association'] = association.to_s.singularize html_options[:'data-associations'] = association.to_s.pluralize - if wrap_object.respond_to?(:call) - new_object = wrap_object.call(create_object(f, association)) - else - new_object = create_object(f, association) - end + new_object = create_object(f, association, force_non_association_create) + new_object = wrap_object.call(new_object) if wrap_object.respond_to?(:call) html_options[:'data-association-insertion-template'] = CGI.escapeHTML(render_association(association, f, new_object, render_options, override_partial)).html_safe @@ -89,10 +89,10 @@ module Cocoon # `` has_many :admin_comments, class_name: "Comment", conditions: { author: "Admin" } # will create new Comment with author "Admin" - def create_object(f, association) + def create_object(f, association, force_non_association_create=false) assoc = f.object.class.reflect_on_association(association) - assoc ? create_object_on_association(f, association, assoc) : create_object_on_non_association(f, association) + assoc ? create_object_on_association(f, association, assoc, force_non_association_create) : create_object_on_non_association(f, association) end def get_partial_path(partial, association) @@ -107,13 +107,12 @@ module Cocoon raise "Association #{association} doesn't exist on #{f.object.class}" end - def create_object_on_association(f, association, instance) - if instance.class.name == "Mongoid::Relations::Metadata" - conditions = instance.respond_to?(:conditions) ? instance.conditions.flatten : [] - instance.klass.new(*conditions) + def create_object_on_association(f, association, instance, force_non_association_create) + if instance.class.name == "Mongoid::Relations::Metadata" || force_non_association_create + create_object_with_conditions(instance) else # assume ActiveRecord or compatible - if instance.collection? + if instance.collection? f.object.send(association).build else f.object.send("build_#{association}") @@ -121,5 +120,10 @@ module Cocoon end end + def create_object_with_conditions(instance) + conditions = instance.respond_to?(:conditions) ? instance.conditions.flatten : [] + instance.klass.new(*conditions) + end + end end diff --git a/spec/cocoon_spec.rb b/spec/cocoon_spec.rb index ec46dca..161a4f6 100644 --- a/spec/cocoon_spec.rb +++ b/spec/cocoon_spec.rb @@ -10,11 +10,15 @@ describe Cocoon do it { should respond_to(:link_to_add_association) } it { should respond_to(:link_to_remove_association) } + before(:each) do + @tester = TestClass.new + @post = Post.new + @form_obj = stub(:object => @post, :object_name => @post.class.name) + end + + context "link_to_add_association" do before(:each) do - @tester = TestClass.new - @post = Post.new - @form_obj = stub(:object => @post) @tester.stub(:render_association).and_return('form') end @@ -41,6 +45,25 @@ describe Cocoon do @tester.should_receive(:render_association).with(anything(), anything(), kind_of(CommentDecorator), anything(), anything()).and_return('partiallll') @tester.link_to_add_association('add something', @form_obj, :comments, :wrap_object => Proc.new {|comment| CommentDecorator.new(comment) }) end + + context "force non association create" do + it "default it uses the association" do + @tester.should_receive(:create_object).with(anything, :comments , false) + result = @tester.link_to_add_association('add something', @form_obj, :comments) + result.to_s.should == 'add something' + end + it "specifying false is the same as default: create object on association" do + @tester.should_receive(:create_object).with(anything, :comments , false) + result = @tester.link_to_add_association('add something', @form_obj, :comments, :force_non_association_create => false) + result.to_s.should == 'add something' + end + it "specifying true will not create objects on association but using the conditions" do + @tester.should_receive(:create_object).with(anything, :comments , true) + result = @tester.link_to_add_association('add something', @form_obj, :comments, :force_non_association_create => true) + result.to_s.should == 'add something' + end + + end end context "with a block" do @@ -146,12 +169,6 @@ describe Cocoon do end context "link_to_remove_association" do - before(:each) do - @tester = TestClass.new - @post = Post.new - @form_obj = stub(:object => @post, :object_name => @post.class.name) - end - context "without a block" do it "accepts a name" do result = @tester.link_to_remove_association('remove something', @form_obj) @@ -180,44 +197,50 @@ describe Cocoon do result.to_s.should == "remove some long name" end end + end - context "create_object" do - it "should create correct association with conditions" do - result = @tester.create_object(@form_obj, :admin_comments) - result.author.should == "Admin" - end - - it "should create correct association for belongs_to associations" do - result = @tester.create_object(stub(:object => Comment.new), :post) - result.should be_a Post - end - - it "should raise error if cannot reflect on association" do - expect { @tester.create_object(stub(:object => Comment.new), :not_existing) }.to raise_error /association/i - end - - it "should create an association if object responds to 'build_association' as singular" do - object = Comment.new - object.should_receive(:build_custom_item).and_return 'custom' - @tester.create_object(stub(:object => object), :custom_item).should == 'custom' - end - - it "should create an association if object responds to 'build_association' as plural" do - object = Comment.new - object.should_receive(:build_custom_item).and_return 'custom' - @tester.create_object(stub(:object => object), :custom_items).should == 'custom' - end + context "create_object" do + it "creates correct association with conditions" do + @tester.should_not_receive(:create_object_with_conditions) + result = @tester.create_object(@form_obj, :admin_comments) + result.author.should == "Admin" end - context "get_partial_path" do - it "generates the default partial name if no partial given" do - result = @tester.get_partial_path(nil, :admin_comments) - result.should == "admin_comment_fields" - end - it "uses the given partial name" do - result = @tester.get_partial_path("comment_fields", :admin_comments) - result.should == "comment_fields" - end + it "creates correct association for belongs_to associations" do + result = @tester.create_object(stub(:object => Comment.new), :post) + result.should be_a Post + end + + it "raises an error if cannot reflect on association" do + expect { @tester.create_object(stub(:object => Comment.new), :not_existing) }.to raise_error /association/i + end + + it "creates an association if object responds to 'build_association' as singular" do + object = Comment.new + object.should_receive(:build_custom_item).and_return 'custom' + @tester.create_object(stub(:object => object), :custom_item).should == 'custom' + end + + it "creates an association if object responds to 'build_association' as plural" do + object = Comment.new + object.should_receive(:build_custom_item).and_return 'custom' + @tester.create_object(stub(:object => object), :custom_items).should == 'custom' + end + + it "can create using only conditions not the association" do + @tester.should_receive(:create_object_with_conditions).and_return('flappie') + @tester.create_object(@form_obj, :comments, true).should == 'flappie' + end + end + + context "get_partial_path" do + it "generates the default partial name if no partial given" do + result = @tester.get_partial_path(nil, :admin_comments) + result.should == "admin_comment_fields" + end + it "uses the given partial name" do + result = @tester.get_partial_path("comment_fields", :admin_comments) + result.should == "comment_fields" end end From 3c0c82ce90f9634cd96f6699ef94d56c570474c2 Mon Sep 17 00:00:00 2001 From: nathanvda Date: Thu, 22 Nov 2012 01:31:35 +0100 Subject: [PATCH 03/11] Updated history. --- History.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/History.md b/History.md index 0dcc9d9..3bb2a52 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,10 @@ # Change History / Release Notes +## Version 1.1.1 + +* added the to be added/deleted element to the event, this allows to add animations/actions onto them +* added extra option :wrap_object, allowing to use Decorators instead of the association object +* added an option :force_non_association_create, that will allow to use `link_to_add_association` inside the fields-partial ## Version 1.1.0 From a7a9147ad40e3f899fcf437568b507ad27a690a0 Mon Sep 17 00:00:00 2001 From: nathanvda Date: Thu, 22 Nov 2012 01:32:05 +0100 Subject: [PATCH 04/11] Version bump to 1.1.1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 1cc5f65..8cfbc90 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0 \ No newline at end of file +1.1.1 \ No newline at end of file From 7ce9fe91f359374c6e9dcd566aa158d9c31f280d Mon Sep 17 00:00:00 2001 From: Andy Waite Date: Thu, 13 Dec 2012 17:04:20 +0000 Subject: [PATCH 05/11] Fix broken TravisCI image --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 104f9c8..3dc7a2b 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,6 @@ # cocoon -[![Build Status](http://travis-ci.org/nathanvda/cocoon.png)](http://travis-ci.org/nathanvda/cocoon) +[![Build Status](https://travis-ci.org/nathanvda/cocoon.png)](https://travis-ci.org/nathanvda/cocoon) cocoon is a Rails3 gem to allow easier handling of nested forms. From 242d89a3b0bc58df64756275555fa1ac97e580ef Mon Sep 17 00:00:00 2001 From: John Bintz Date: Tue, 15 Jan 2013 21:55:31 -0500 Subject: [PATCH 06/11] don't automaticaly include jquery ui --- app/assets/javascripts/cocoon/ordered.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/cocoon/ordered.js b/app/assets/javascripts/cocoon/ordered.js index ca85f39..5a05e80 100644 --- a/app/assets/javascripts/cocoon/ordered.js +++ b/app/assets/javascripts/cocoon/ordered.js @@ -1,4 +1,3 @@ -//= require jquery.ui.all // (function($) { $.cocoon = { From 33f5df9d4c377e1f98fc8fb2b951dd37918706e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20=C3=96zkaya?= Date: Sat, 19 Jan 2013 23:48:02 +0200 Subject: [PATCH 07/11] Update app/assets/javascripts/cocoon.js ".live()" method replaced by ".on()" (jQuery 1.7+) method on jQuery 1.7 .live() method is deprecated on jQuery 1.9 .live() method is removed --- app/assets/javascripts/cocoon.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/cocoon.js b/app/assets/javascripts/cocoon.js index b4ec934..951b959 100644 --- a/app/assets/javascripts/cocoon.js +++ b/app/assets/javascripts/cocoon.js @@ -8,7 +8,7 @@ } - $('.add_fields').live('click', function(e) { + $(document).on('click', '.add_fields', function(e) { e.preventDefault(); var $this = $(this), assoc = $this.data('association'), @@ -55,7 +55,7 @@ }); - $('.remove_fields.dynamic, .remove_fields.existing').live('click', function(e) { + $(document).on('click', '.remove_fields.dynamic, .remove_fields.existing', function(e) { var $this = $(this); var node_to_delete = $this.closest(".nested-fields"); var trigger_node = node_to_delete.parent(); From bb6ffdbcdb7b54044f637743ec4c6d25dbe29df3 Mon Sep 17 00:00:00 2001 From: nathanvda Date: Mon, 21 Jan 2013 08:32:52 +0100 Subject: [PATCH 08/11] Version bump to 1.1.2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8cfbc90..8428158 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.1 \ No newline at end of file +1.1.2 \ No newline at end of file From c40d9becc371574d05dc5635773967d92b0b7dcb Mon Sep 17 00:00:00 2001 From: nathanvda Date: Mon, 21 Jan 2013 08:33:03 +0100 Subject: [PATCH 09/11] Regenerate gemspec for version 1.1.2 --- cocoon.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocoon.gemspec b/cocoon.gemspec index 138274c..df0b20c 100644 --- a/cocoon.gemspec +++ b/cocoon.gemspec @@ -5,11 +5,11 @@ Gem::Specification.new do |s| s.name = "cocoon" - s.version = "1.1.1" + s.version = "1.1.2" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Nathan Van der Auwera"] - s.date = "2012-11-22" + s.date = "2013-01-21" s.description = "Unobtrusive nested forms handling, using jQuery. Use this and discover cocoon-heaven." s.email = "nathan@dixis.com" s.extra_rdoc_files = [ From aac7105f04cb24ae0d9d306a9c193cd1469e16f3 Mon Sep 17 00:00:00 2001 From: nathanvda Date: Mon, 21 Jan 2013 08:39:01 +0100 Subject: [PATCH 10/11] Updated history. --- History.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/History.md b/History.md index 3bb2a52..dc52de1 100644 --- a/History.md +++ b/History.md @@ -1,5 +1,10 @@ # Change History / Release Notes +## Version 1.1.2 + +* pull #118 (thanks @ahmozkya): remove the deprecated `.live` function, and use `.on` instead. + Note: at least jquery 1.7 is required now! + ## Version 1.1.1 * added the to be added/deleted element to the event, this allows to add animations/actions onto them From de1acdf93089100ff012cb83b9b09db4fdcd3105 Mon Sep 17 00:00:00 2001 From: virusman Date: Wed, 23 Jan 2013 00:33:44 +0400 Subject: [PATCH 11/11] Make sure javascript_expansions config option exists --- lib/cocoon.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/cocoon.rb b/lib/cocoon.rb index ec9039c..3118fb7 100644 --- a/lib/cocoon.rb +++ b/lib/cocoon.rb @@ -4,7 +4,9 @@ module Cocoon class Engine < ::Rails::Engine config.before_initialize do - config.action_view.javascript_expansions[:cocoon] = %w(cocoon) + if config.action_view.javascript_expansions + config.action_view.javascript_expansions[:cocoon] = %w(cocoon) + end end # configure our plugin on boot