form_forms 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.travis.yml +12 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +239 -0
- data/Rakefile +11 -0
- data/form_forms.gemspec +23 -0
- data/gemfiles/Gemfile.rails3_0 +10 -0
- data/gemfiles/Gemfile.rails3_1 +10 -0
- data/gemfiles/Gemfile.rails3_2 +10 -0
- data/lib/form_forms.rb +10 -0
- data/lib/form_forms/elements.rb +5 -0
- data/lib/form_forms/elements/base_element.rb +189 -0
- data/lib/form_forms/elements/block.rb +28 -0
- data/lib/form_forms/elements/field.rb +32 -0
- data/lib/form_forms/elements/fields.rb +29 -0
- data/lib/form_forms/elements/fieldset.rb +23 -0
- data/lib/form_forms/form_registry.rb +25 -0
- data/lib/form_forms/forms.rb +2 -0
- data/lib/form_forms/forms/base_form.rb +40 -0
- data/lib/form_forms/forms/form.rb +15 -0
- data/lib/form_forms/version.rb +3 -0
- data/test/form_forms/elements/block_test.rb +32 -0
- data/test/form_forms/elements/field_test.rb +98 -0
- data/test/form_forms/elements/fields_test.rb +57 -0
- data/test/form_forms/elements/fieldset_test.rb +53 -0
- data/test/form_forms/form_registry_test.rb +16 -0
- data/test/form_forms/forms/form_test.rb +15 -0
- data/test/support/misc_helpers.rb +15 -0
- data/test/support/mock_controller.rb +22 -0
- data/test/support/models.rb +134 -0
- data/test/test_helper.rb +51 -0
- metadata +131 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module FormForms
|
2
|
+
module Elements
|
3
|
+
class Block < BaseElement
|
4
|
+
allowed_sub_element :field
|
5
|
+
allowed_sub_element :block
|
6
|
+
allowed_sub_element :fieldset
|
7
|
+
allowed_sub_element :fields
|
8
|
+
allowed_sub_element :table_fields
|
9
|
+
|
10
|
+
def initialize(element, args = {})
|
11
|
+
@element = element.to_sym
|
12
|
+
self.args args
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
property :args
|
17
|
+
|
18
|
+
# Generate a fielset with a legend
|
19
|
+
def render(builder, view)
|
20
|
+
args = eval_property(:args, builder, view)
|
21
|
+
|
22
|
+
view.content_tag(@element, args) do
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module FormForms
|
2
|
+
module Elements
|
3
|
+
# This represents a single form field. Typically this is the most
|
4
|
+
# low-level part of a form. You can either return a form element with the
|
5
|
+
# help of the passed form builder or just return arbitrary html. The block
|
6
|
+
# is executed in the scope of the view where the form is rendered. You can
|
7
|
+
# use any helpers defined there.
|
8
|
+
#
|
9
|
+
# Note that you might need to mark your values as +html_safe+ to avoid
|
10
|
+
# superfluous escaping in the view.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# my_form = Form.new() do |form|
|
15
|
+
# form.field(:subject) {|f| f.input :subject}
|
16
|
+
# form.field(:link) do |f|
|
17
|
+
# content_tag :p do
|
18
|
+
# content_tag(:a, :href => hint_path){ "This is a hint" }
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
class Field
|
23
|
+
def initialize(&generator)
|
24
|
+
@generator = generator
|
25
|
+
end
|
26
|
+
|
27
|
+
def render(builder, view)
|
28
|
+
view.instance_exec(builder, &@generator)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module FormForms
|
2
|
+
module Elements
|
3
|
+
class Fields < BaseElement
|
4
|
+
allowed_sub_element :field
|
5
|
+
allowed_sub_element :block
|
6
|
+
allowed_sub_element :fieldset
|
7
|
+
allowed_sub_element :fields
|
8
|
+
allowed_sub_element :table_fields
|
9
|
+
|
10
|
+
def initialize(association=nil, form_args={})
|
11
|
+
self.association association
|
12
|
+
self.args form_args
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
property :association
|
17
|
+
property :args
|
18
|
+
|
19
|
+
def render(builder, view)
|
20
|
+
association = eval_property(:association, builder, view)
|
21
|
+
form_args = eval_property(:args, builder, view)
|
22
|
+
|
23
|
+
builder.association(association, form_args) do |sub|
|
24
|
+
render_elements(sub, view)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module FormForms
|
2
|
+
module Elements
|
3
|
+
class Fieldset < Block
|
4
|
+
def initialize(fieldset_args={}, &generator)
|
5
|
+
self.legend nil
|
6
|
+
super(:fieldset, fieldset_args, &generator)
|
7
|
+
end
|
8
|
+
|
9
|
+
property :legend
|
10
|
+
|
11
|
+
# Generate a fielset with a legend
|
12
|
+
def render(builder, view)
|
13
|
+
args = eval_property(:args, builder, view)
|
14
|
+
legend = eval_property(:legend, builder, view)
|
15
|
+
|
16
|
+
view.content_tag(:fieldset, args) do
|
17
|
+
view.concat view.content_tag(:legend, legend) unless legend.nil?
|
18
|
+
view.concat render_elements(builder, view)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module FormForms
|
2
|
+
class FormRegistry
|
3
|
+
class << self
|
4
|
+
def [](name)
|
5
|
+
forms[name.to_sym]
|
6
|
+
end
|
7
|
+
|
8
|
+
def []=(name, form)
|
9
|
+
forms[name.to_sym] = form
|
10
|
+
end
|
11
|
+
|
12
|
+
def delete(name)
|
13
|
+
forms.delete(name.to_sym)
|
14
|
+
end
|
15
|
+
|
16
|
+
def keys
|
17
|
+
forms.keys
|
18
|
+
end
|
19
|
+
protected
|
20
|
+
def forms
|
21
|
+
@forms ||= {}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module FormForms
|
2
|
+
module Forms
|
3
|
+
# The generic top-level form for building a simple_form..
|
4
|
+
#
|
5
|
+
# This class (and all its children) is only usable as a top-level form
|
6
|
+
# as it generates its own form builder object which is passed to its
|
7
|
+
# sub-elements on rendering
|
8
|
+
class BaseForm < FormForms::Elements::BaseElement
|
9
|
+
# +form_args+ are passed to the initializer of the form builder.
|
10
|
+
# They can be used to override its defaults.
|
11
|
+
def initialize(form_args = {})
|
12
|
+
@form_args = form_args
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set the form_args. These are static as we don't have a form yet
|
17
|
+
def args(form_args=nil)
|
18
|
+
if form_args
|
19
|
+
@form_args = form_args
|
20
|
+
else
|
21
|
+
@form_args
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Render the form.
|
26
|
+
# Notice that forms receive an ActiveModel object as its first
|
27
|
+
# parameter, unlike elements which receive the form builder which is
|
28
|
+
# created by a form.
|
29
|
+
def render(model, view)
|
30
|
+
view.simple_form_for(model, self.args) do |builder|
|
31
|
+
render_elements(builder, view)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.register(name, form_args={}, &generator)
|
36
|
+
::FormForms::FormRegistry[name] = self.new(form_args, &generator)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module FormForms
|
2
|
+
module Forms
|
3
|
+
# The generic form for building a simple_form..
|
4
|
+
#
|
5
|
+
# This class is only usable as a top-level form as it generates its own
|
6
|
+
# form builder object
|
7
|
+
class Form < BaseForm
|
8
|
+
allowed_sub_element :field
|
9
|
+
allowed_sub_element :block
|
10
|
+
allowed_sub_element :fieldset
|
11
|
+
allowed_sub_element :fields
|
12
|
+
allowed_sub_element :table_fields
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FieldsetTest < ActionView::TestCase
|
4
|
+
test "create a block" do
|
5
|
+
with_form_for(@user) do |form|
|
6
|
+
form.block(:red_box, :div, :class => "red_box") do |block|
|
7
|
+
block.field(:credit_card) {|f| f.input :credit_card}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
assert_select 'form > div.red_box input.string#user_credit_card'
|
12
|
+
end
|
13
|
+
|
14
|
+
test "allow nesting of blocks" do
|
15
|
+
with_form_for(@user) do |form|
|
16
|
+
form.block(:main, :div, :id => 'main') do |outer|
|
17
|
+
outer.fieldset(:payment, :id => "payment") do |inner|
|
18
|
+
inner.legend "These are some fields"
|
19
|
+
inner.field(:credit_card) {|f| f.input :credit_card}
|
20
|
+
end
|
21
|
+
|
22
|
+
outer.field(:name) {|f| f.input :name}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
assert_no_select 'fieldset#main > legend'
|
27
|
+
assert_select 'form > div#main > div > input.string#user_name'
|
28
|
+
|
29
|
+
assert_select 'form > div#main > fieldset#payment > legend', "These are some fields"
|
30
|
+
assert_select 'form > div#main > fieldset#payment input.string#user_credit_card'
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FieldTest < ActionView::TestCase
|
4
|
+
test "render a simple_form field" do
|
5
|
+
with_form_for(@user) do |form|
|
6
|
+
form.field(:name) {|f| f.input :name}
|
7
|
+
form.field(:description) {|f| f.input :description}
|
8
|
+
end
|
9
|
+
|
10
|
+
assert_select 'input.string#user_name'
|
11
|
+
assert_select 'textarea.text#user_description'
|
12
|
+
end
|
13
|
+
|
14
|
+
test "render a custom field" do
|
15
|
+
with_form_for(@user) do |form|
|
16
|
+
form.field(:something) {|f| '<span id="foo">Something</span>'.html_safe}
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_select 'span#foo', "Something"
|
20
|
+
end
|
21
|
+
|
22
|
+
test "escape unsafe output" do
|
23
|
+
with_form_for(@user) do |form|
|
24
|
+
form.field(:something) {|f| '<span id="foo">Something</span>'}
|
25
|
+
end
|
26
|
+
|
27
|
+
assert_no_select 'span#foo'
|
28
|
+
assert_select 'form', /<span id="foo">Something<\/span>/
|
29
|
+
end
|
30
|
+
|
31
|
+
test "allow the usage of view helpers" do
|
32
|
+
with_form_for(@user) do |form|
|
33
|
+
form.field(:something) {|f| content_tag(:span){"Something"}}
|
34
|
+
end
|
35
|
+
|
36
|
+
assert_select 'span', "Something"
|
37
|
+
end
|
38
|
+
|
39
|
+
test "allow appending elements" do
|
40
|
+
with_form_for(@user) do |form|
|
41
|
+
form.field(:name) {|f| f.input :name}
|
42
|
+
form.field(:description) {|f| f.input :description}
|
43
|
+
|
44
|
+
form.field_after(:name, :credit_card) {|f| f.input :credit_card}
|
45
|
+
form.field_last(:active) {|f| f.input :active}
|
46
|
+
end
|
47
|
+
|
48
|
+
# the first div in a form contains some hidden fields
|
49
|
+
assert_select 'form > div:nth-of-type(2) > input.string#user_name'
|
50
|
+
assert_select 'form > div:nth-of-type(3) > input.string#user_credit_card'
|
51
|
+
assert_select 'form > div:nth-of-type(4) > textarea.text#user_description'
|
52
|
+
assert_select 'form > div:nth-of-type(5) > input.boolean#user_active'
|
53
|
+
end
|
54
|
+
|
55
|
+
test "allow prepending elements" do
|
56
|
+
with_form_for(@user) do |form|
|
57
|
+
form.field(:name) {|f| f.input :name}
|
58
|
+
form.field(:description) {|f| f.input :description}
|
59
|
+
|
60
|
+
form.field_first(:active) {|f| f.input :active}
|
61
|
+
form.field_before(:description, :credit_card) {|f| f.input :credit_card}
|
62
|
+
end
|
63
|
+
|
64
|
+
# the first div in a form contains some hidden fields
|
65
|
+
assert_select 'form > div:nth-of-type(2) > input.boolean#user_active'
|
66
|
+
assert_select 'form > div:nth-of-type(3) > input.string#user_name'
|
67
|
+
assert_select 'form > div:nth-of-type(4) > input.string#user_credit_card'
|
68
|
+
assert_select 'form > div:nth-of-type(5) > textarea.text#user_description'
|
69
|
+
end
|
70
|
+
|
71
|
+
test "check existence of elements when inserting" do
|
72
|
+
assert_raise ArgumentError do
|
73
|
+
with_form_for(@user) do |form|
|
74
|
+
form.field(:name) {|f| f.input :name}
|
75
|
+
form.field_before(:description, :credit_card) {|f| f.input :credit_card}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
assert_raise ArgumentError do
|
80
|
+
with_form_for(@user) do |form|
|
81
|
+
form.field(:name) {|f| f.input :name}
|
82
|
+
form.field_after(:description, :credit_card) {|f| f.input :credit_card}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
test "delete field" do
|
88
|
+
with_form_for(@user) do |form|
|
89
|
+
form.field(:name) {|f| f.input :name}
|
90
|
+
form.field(:description) {|f| f.input :description}
|
91
|
+
|
92
|
+
form.delete(:description)
|
93
|
+
end
|
94
|
+
|
95
|
+
assert_select 'input.string#user_name'
|
96
|
+
assert_no_select 'textarea.text#user_description'
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class FieldsTest < ActionView::TestCase
|
4
|
+
test "render a sub-form" do
|
5
|
+
with_form_for(@user) do |form|
|
6
|
+
form.field(:name) {|f| f.input :name}
|
7
|
+
|
8
|
+
form.fields(:tags, :tags, :collection => Tag.all) do |tags|
|
9
|
+
tags.field(:name) {|f| f.input :name}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
assert_select 'input.string#user_name'
|
14
|
+
|
15
|
+
assert_select 'input.string#user_tags_attributes_0_name'
|
16
|
+
assert_select 'input[type=hidden]#user_tags_attributes_0_id'
|
17
|
+
|
18
|
+
assert_select 'input.string#user_tags_attributes_2_name'
|
19
|
+
assert_select 'input[type=hidden]#user_tags_attributes_2_id'
|
20
|
+
end
|
21
|
+
|
22
|
+
test "pass the collection tag as a proc" do
|
23
|
+
with_form_for(@user) do |form|
|
24
|
+
form.field(:name) {|f| f.input :name}
|
25
|
+
|
26
|
+
args = lambda{|f| {:collection => Tag.all} }
|
27
|
+
form.fields(:tags, :tags, args) do |tags|
|
28
|
+
tags.field(:name) {|f| f.input :name}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
assert_select 'input.string#user_name'
|
33
|
+
|
34
|
+
assert_select 'input.string#user_tags_attributes_0_name'
|
35
|
+
assert_select 'input[type=hidden]#user_tags_attributes_0_id'
|
36
|
+
|
37
|
+
assert_select 'input.string#user_tags_attributes_2_name'
|
38
|
+
assert_select 'input[type=hidden]#user_tags_attributes_2_id'
|
39
|
+
end
|
40
|
+
|
41
|
+
test "append field to sub-form" do
|
42
|
+
with_form_for(@user) do |form|
|
43
|
+
form.field(:name) {|f| f.input :name}
|
44
|
+
|
45
|
+
form.fields(:tags, :tags, :collection => Tag.all) do |tags|
|
46
|
+
tags.field(:name) {|f| f.input :name}
|
47
|
+
end
|
48
|
+
|
49
|
+
form.fields(:tags).field(:id) {|f| f.input :id}
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_select 'input.string#user_name'
|
53
|
+
|
54
|
+
assert_select 'input.string#user_tags_attributes_0_name'
|
55
|
+
assert_select 'input.string#user_tags_attributes_0_id'
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FieldsetTest < ActionView::TestCase
|
4
|
+
test "create a fieldset" do
|
5
|
+
with_form_for(@user) do |form|
|
6
|
+
form.fieldset(:payment, :id => "payment") do |fieldset|
|
7
|
+
fieldset.legend "These are some fields"
|
8
|
+
fieldset.field(:credit_card) {|f| f.input :credit_card}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
assert_select 'form > fieldset#payment legend', "These are some fields"
|
13
|
+
assert_select 'form > fieldset#payment input.string#user_credit_card'
|
14
|
+
end
|
15
|
+
|
16
|
+
test "allow nesting of fieldsets" do
|
17
|
+
with_form_for(@user) do |form|
|
18
|
+
form.fieldset(:main, :id => 'main') do |outer|
|
19
|
+
outer.fieldset(:payment, :id => "payment") do |inner|
|
20
|
+
inner.legend "These are some fields"
|
21
|
+
inner.field(:credit_card) {|f| f.input :credit_card}
|
22
|
+
end
|
23
|
+
|
24
|
+
outer.field(:name) {|f| f.input :name}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
assert_no_select 'fieldset#main > legend'
|
29
|
+
assert_select 'form > fieldset#main > div > input.string#user_name'
|
30
|
+
|
31
|
+
assert_select 'form > fieldset#main > fieldset#payment > legend', "These are some fields"
|
32
|
+
assert_select 'form > fieldset#main > fieldset#payment input.string#user_credit_card'
|
33
|
+
end
|
34
|
+
|
35
|
+
test "dynamically set args and legend" do
|
36
|
+
with_form_for(@user) do |form|
|
37
|
+
form.fieldset(:main) do |fieldset|
|
38
|
+
# static syntax
|
39
|
+
fieldset.args :id => 'main'
|
40
|
+
fieldset.legend "These are some fields"
|
41
|
+
end
|
42
|
+
|
43
|
+
form.fieldset(:block) do |fieldset|
|
44
|
+
# dynamic block syntax
|
45
|
+
fieldset.args{|f| {:id => 'block'} }
|
46
|
+
fieldset.legend{|f| "This is a #{f.object.class.name.underscore}"}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
assert_select 'form > fieldset#main > legend', 'These are some fields'
|
51
|
+
assert_select 'form > fieldset#block > legend', 'This is a user'
|
52
|
+
end
|
53
|
+
end
|