awesome_nested_fields 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
+ demos/*/**
data/Gemfile.lock ADDED
@@ -0,0 +1,78 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ awesome_nested_fields (0.0.3)
5
+ rails (>= 3.0.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ abstract (1.0.0)
11
+ actionmailer (3.0.4)
12
+ actionpack (= 3.0.4)
13
+ mail (~> 2.2.15)
14
+ actionpack (3.0.4)
15
+ activemodel (= 3.0.4)
16
+ activesupport (= 3.0.4)
17
+ builder (~> 2.1.2)
18
+ erubis (~> 2.6.6)
19
+ i18n (~> 0.4)
20
+ rack (~> 1.2.1)
21
+ rack-mount (~> 0.6.13)
22
+ rack-test (~> 0.5.7)
23
+ tzinfo (~> 0.3.23)
24
+ activemodel (3.0.4)
25
+ activesupport (= 3.0.4)
26
+ builder (~> 2.1.2)
27
+ i18n (~> 0.4)
28
+ activerecord (3.0.4)
29
+ activemodel (= 3.0.4)
30
+ activesupport (= 3.0.4)
31
+ arel (~> 2.0.2)
32
+ tzinfo (~> 0.3.23)
33
+ activeresource (3.0.4)
34
+ activemodel (= 3.0.4)
35
+ activesupport (= 3.0.4)
36
+ activesupport (3.0.4)
37
+ arel (2.0.8)
38
+ builder (2.1.2)
39
+ erubis (2.6.6)
40
+ abstract (>= 1.0.0)
41
+ i18n (0.5.0)
42
+ mail (2.2.15)
43
+ activesupport (>= 2.3.6)
44
+ i18n (>= 0.4.0)
45
+ mime-types (~> 1.16)
46
+ treetop (~> 1.4.8)
47
+ mime-types (1.16)
48
+ polyglot (0.3.1)
49
+ rack (1.2.1)
50
+ rack-mount (0.6.13)
51
+ rack (>= 1.0.0)
52
+ rack-test (0.5.7)
53
+ rack (>= 1.0)
54
+ rails (3.0.4)
55
+ actionmailer (= 3.0.4)
56
+ actionpack (= 3.0.4)
57
+ activerecord (= 3.0.4)
58
+ activeresource (= 3.0.4)
59
+ activesupport (= 3.0.4)
60
+ bundler (~> 1.0)
61
+ railties (= 3.0.4)
62
+ railties (3.0.4)
63
+ actionpack (= 3.0.4)
64
+ activesupport (= 3.0.4)
65
+ rake (>= 0.8.7)
66
+ thor (~> 0.14.4)
67
+ rake (0.8.7)
68
+ thor (0.14.6)
69
+ treetop (1.4.9)
70
+ polyglot (>= 0.3.1)
71
+ tzinfo (0.3.24)
72
+
73
+ PLATFORMS
74
+ ruby
75
+
76
+ DEPENDENCIES
77
+ awesome_nested_fields!
78
+ bundler (>= 1.0.0)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Lailson Bandeira
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ Awesome Nested Fields
2
+ =====================
3
+
4
+ In Rails, you can create forms that have fields from nested models. For example, if a person has many phone numbers, you can easily create a form that receives data from the person and from a fixed number of phones. However, when you want to allow the person to insert multiple, indefinite phones, you're in trouble: it's [much harder](http://railscasts.com/episodes/196-nested-model-form-part-1) [than it](http://railscasts.com/episodes/197-nested-model-form-part-2) [should be](http://stackoverflow.com/questions/1704142/unobtrusive-dynamic-form-fields-in-rails-with-jquery). Well, not anymore.
5
+
6
+
7
+ Installation
8
+ ------------
9
+
10
+ 1. Add the gem to your Gemfile.
11
+
12
+ gem 'awesome_nested_fields'
13
+
14
+ 2. Run bundler to make sure the gem gets installed.
15
+
16
+ bundle install
17
+
18
+ 3. Include the `jquery.nested-fields.js` file in your template (or in the pages that will use nested fields).
19
+
20
+ <script src="/javascripts/jquery.nested-fields.js" type="text/javascript"></script>
21
+
22
+ Now you're ready to rock with nested models. Don't forget to include the javascript file _after_ you've included jQuery. And don't worry because this file isn't on the public folder: it comes bundled into the gem.
23
+
24
+
25
+ Basic Usage
26
+ -----------
27
+
28
+ ### Model
29
+
30
+ First, make sure the object that has the `has_many` or `has_and_belongs_to_many` relation accepts nested attributes for the collection you want. For example, if a person _has_many_ phones, we'll have a model like this:
31
+
32
+ class Person < ActiveRecord::Base
33
+ has_many :phones
34
+ accepts_nested_attributes_for :phones, allow_destroy: true
35
+ end
36
+
37
+ The `accepts_nested_attributes_for` is a method from Active Record that allows you to pass attributes of nested models directly to its parent, instead of instantiate each child object separately. In this case, `Person` gains a method called `phones_attributes=`, that accepts data for new and existing phones of a given person. The `allow_destroy` option enables us to also delete child objects. To know more about nested attributes, check out the [ActiveRecord::NestedAttribute](https://github.com/rails/rails/blob/master/activerecord/lib/active_record/nested_attributes.rb#L1) class.
38
+
39
+ ### View
40
+
41
+ The next step is set up the form view using the `nested_fields` helper method. It receives three parameters: the parent form builder, the association name and an optional hash of options (humm, a pun).
42
+ Proceeding with the person/phones example, we can have a form like this:
43
+
44
+ <%= form_for(@person) do |f| %>
45
+ <% # person fields... %>
46
+
47
+ <h2>Phones</h2>
48
+ <div class="container">
49
+ <%= nested_fields(f, :phones) %>
50
+ </div>
51
+ <a href="#" class="add">add phone</a>
52
+
53
+ <% # more person fields... %>
54
+ <% end %>
55
+
56
+ The `nested_fields` helper lists the phones this person has and also adds an empty template to the page for creating new phones. But where is the phone form? Well, awesome_nested_fields expects a partial with the association name in the singular (after all, the partial represents a single child object). In this case, it looks for the partial `phone` (we can change this name later). So, in the file `_phone.html.erb`, we can have:
57
+
58
+ <fieldset class="item">
59
+ <%= f.label :where %>
60
+ <%= f.text_field :where %><br/>
61
+
62
+ <%= f.label :number %>
63
+ <%= f.text_field :number %>
64
+
65
+ <a href="#" class="remove">remove</a>
66
+
67
+ <%= f.hidden_field :id %>
68
+ <%= f.hidden_field :_destroy %>
69
+ </fieldset>
70
+
71
+ If you're paying attention, you noticed the key elements are marked with a special class name. We need this for the javascript code, so it knows what to do with each HTML element: the one that have the children must have the class `container`; each child must be marked with the class `item`; inside an item, the link for removal must have the class `remove`; and the link to add new items must have the class `add`. We can change the names later, but these are the default choices. Finally, don't forget to add the `id` field, as it is needed by AR to identify if this is an existing or a new element, and the `_destroy` field to activate deletion when the user clicks on the remove link.
72
+
73
+ ### Javascript
74
+
75
+ This is the easiest part: just activate the nested fields actions when the page loads. We can put this in the `application.js` file (or in any other place that gets executed in the page):
76
+
77
+ $(document).ready(function(e) {
78
+ $('FORM').nestedFields();
79
+ });
80
+
81
+ Now enjoy your new nested model form!
82
+
83
+
84
+ Compatibility
85
+ -------------
86
+
87
+ awesome_nested_fields works only with Rails 3 and was tested with jQuery 1.5.0.
88
+
89
+
90
+ TODO
91
+ ----
92
+
93
+ * Write tests
94
+ * Write awesome demos
95
+ * Make sure it can degrade gracefully
96
+ * Return and API object on JS to make interaction easier
97
+ * Port JavaScript code to CoffeeScript
98
+
99
+
100
+ Copyleft
101
+ --------
102
+ Copyleft (c) 2011 Lailson Bandeira (http://lailsonbandeira.com/). See LICENSE for details.
@@ -1,4 +1,10 @@
1
1
  module AwesomeNestedFieldsHelper
2
+
3
+ def nested_fields(builder, association, options={})
4
+ nested_fields_items(builder, association, options) <<
5
+ nested_fields_template(builder, association, options)
6
+ end
7
+
2
8
  def nested_fields_items(builder, association, options={})
3
9
  options = nested_fields_process_default_options(options, builder, association)
4
10
 
@@ -8,7 +14,7 @@ module AwesomeNestedFieldsHelper
8
14
  end
9
15
 
10
16
  if options[:none_partial] and builder.object.send(association).empty?
11
- items << render(options[:none_partial], options[:builder_local] => f)
17
+ items << render(options[:none_partial])
12
18
  end
13
19
 
14
20
  items.html_safe
@@ -18,14 +24,14 @@ module AwesomeNestedFieldsHelper
18
24
  options = nested_fields_process_default_options(options, builder, association)
19
25
 
20
26
  templates = content_tag(:script, type: 'text/html', class: options[:item_template_class]) do
21
- builder.fields_for(association, options[:new_object], child_index: 'new_nested_item') do |f|
27
+ builder.fields_for(association, options[:new_object], child_index: options[:new_item_index]) do |f|
22
28
  render(options[:partial], options[:builder_local] => f)
23
29
  end
24
30
  end
25
31
 
26
32
  if options[:none_partial]
27
33
  templates << content_tag(:script, type: 'text/html', class: options[:none_template_class]) do
28
- builder.fields_for(association, options[:new_object], child_index: 'new_nested_item') do |f|
34
+ builder.fields_for(association, options[:new_object], child_index: options[:new_item_index]) do |f|
29
35
  render(options[:none_partial], options[:builder_local] => f)
30
36
  end
31
37
  end
@@ -38,9 +44,10 @@ protected
38
44
  def nested_fields_process_default_options(options, builder, association)
39
45
  options[:new_object] ||= builder.object.class.reflect_on_association(association).klass.new
40
46
  options[:partial] ||= association.to_s.singularize
41
- options[:builder_local] ||= :builder
47
+ options[:builder_local] ||= :f
42
48
  options[:item_template_class] ||= 'template item'
43
49
  options[:none_template_class] ||= 'template none'
50
+ options[:new_item_index] ||= 'new_nested_item'
44
51
  options
45
52
  end
46
53
  end
@@ -1,3 +1,3 @@
1
1
  module AwesomeNestedFields
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -10,8 +10,8 @@
10
10
  container: '.container',
11
11
  item: '.item',
12
12
  none: '.none',
13
- addHandler: '.add',
14
- removeHandler: '.remove',
13
+ add: '.add',
14
+ remove: '.remove',
15
15
  newItemIndex: 'new_nested_item'
16
16
  };
17
17
 
@@ -31,10 +31,10 @@
31
31
  options.itemTemplate = $(options.itemTemplate, $this);
32
32
  options.noneTemplate = $(options.noneTemplate, $this);
33
33
  options.container = $(options.container, $this);
34
- options.addHandler = $(options.addHandler, $this);
34
+ options.add = $(options.add, $this);
35
35
  $this.data('nested-fields.options', options);
36
36
 
37
- options.addHandler.bind('click.nested-fields', function(e) {
37
+ options.add.bind('click.nested-fields', function(e) {
38
38
  e.preventDefault();
39
39
  var newItem = prepareTemplate(options);
40
40
  insertItemWithCallbacks(newItem, null, options);
@@ -165,17 +165,20 @@
165
165
  }
166
166
 
167
167
  function bindRemoveEvent(item, options) {
168
- var removeHandler = $(item).find(options.removeHandler);
168
+ var removeHandler = $(item).find(options.remove);
169
169
  var needsConfirmation = removeHandler.attr('data-confirm');
170
170
 
171
- var event = needsConfirmation ? 'confirmed' : 'click';
172
- removeHandler.bind(event + '.nested-fields', function(e) {
173
- removeItem(item, options);
171
+ var event = needsConfirmation ? 'confirm:complete' : 'click';
172
+ removeHandler.bind(event + '.nested-fields', function(e, confirmed) {
173
+ e.preventDefault();
174
+ if(confirmed === undefined || confirmed === true) {
175
+ removeItem(item, options);
176
+ }
174
177
  });
175
178
  }
176
179
 
177
180
  function insertNone(options) {
178
- if(findItems(options).length == 0) {
181
+ if(findItems(options).length === 0) {
179
182
  options.container.append(options.noneTemplate.html());
180
183
  }
181
184
  }
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: awesome_nested_fields
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 0
9
- - 3
10
- version: 0.0.3
4
+ prerelease:
5
+ version: 0.1.0
11
6
  platform: ruby
12
7
  authors:
13
8
  - Lailson Bandeira
@@ -15,7 +10,7 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-02-12 00:00:00 -03:00
13
+ date: 2011-06-06 00:00:00 -03:00
19
14
  default_executable:
20
15
  dependencies:
21
16
  - !ruby/object:Gem::Dependency
@@ -26,11 +21,6 @@ dependencies:
26
21
  requirements:
27
22
  - - ">="
28
23
  - !ruby/object:Gem::Version
29
- hash: 23
30
- segments:
31
- - 1
32
- - 0
33
- - 0
34
24
  version: 1.0.0
35
25
  type: :development
36
26
  version_requirements: *id001
@@ -42,11 +32,6 @@ dependencies:
42
32
  requirements:
43
33
  - - ">="
44
34
  - !ruby/object:Gem::Version
45
- hash: 7
46
- segments:
47
- - 3
48
- - 0
49
- - 0
50
35
  version: 3.0.0
51
36
  type: :runtime
52
37
  version_requirements: *id002
@@ -61,6 +46,9 @@ extra_rdoc_files: []
61
46
  files:
62
47
  - .gitignore
63
48
  - Gemfile
49
+ - Gemfile.lock
50
+ - LICENSE
51
+ - README.md
64
52
  - Rakefile
65
53
  - app/helpers/awesome_nested_fields_helper.rb
66
54
  - awesome_nested_fields.gemspec
@@ -81,25 +69,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
81
69
  requirements:
82
70
  - - ">="
83
71
  - !ruby/object:Gem::Version
84
- hash: 3
85
- segments:
86
- - 0
87
72
  version: "0"
88
73
  required_rubygems_version: !ruby/object:Gem::Requirement
89
74
  none: false
90
75
  requirements:
91
76
  - - ">="
92
77
  - !ruby/object:Gem::Version
93
- hash: 23
94
- segments:
95
- - 1
96
- - 3
97
- - 6
98
78
  version: 1.3.6
99
79
  requirements: []
100
80
 
101
81
  rubyforge_project: awesome_nested_fields
102
- rubygems_version: 1.3.7
82
+ rubygems_version: 1.6.2
103
83
  signing_key:
104
84
  specification_version: 3
105
85
  summary: Awesome nested fields for Rails