form 0.0.0 → 0.0.1.alpha1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/form/version.rb CHANGED
@@ -2,7 +2,7 @@ module Form
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- PATCH = 0
6
- STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
5
+ PATCH = 1
6
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}.alpha1"
7
7
  end
8
8
  end
data/lib/form.rb CHANGED
@@ -1,3 +1,25 @@
1
+ require "i18n"
2
+ require "forwardable"
3
+
1
4
  module Form
5
+ autoload :Builder, "form/builder"
6
+ autoload :Component, "form/component"
7
+ autoload :Tag, "form/tag"
2
8
  autoload :Version, "form/version"
9
+
10
+ # Lazily add locale file to I18n.
11
+ #
12
+ def self.add_locale(locale)
13
+ I18n.load_path << File.expand_path("../form/locales/#{locale}.yml", __FILE__)
14
+ end
15
+
16
+ add_locale :en
17
+
18
+ # Initialize a new Form::Builder object.
19
+ #
20
+ # form = Form.new(params[:user], "user")
21
+ #
22
+ def self.new(data = nil, base_name = nil)
23
+ Builder.new(data, base_name)
24
+ end
3
25
  end
@@ -0,0 +1,95 @@
1
+ require "spec_helper"
2
+
3
+ describe Form::Builder do
4
+ describe "#initialize" do
5
+ let(:data) { mock }
6
+ let(:base_name) { mock }
7
+ subject { described_class.new(data, base_name) }
8
+
9
+ its(:data) { should eql(data) }
10
+ its(:base_name) { should eql(base_name) }
11
+ end
12
+
13
+ context "building fields" do
14
+ describe "#text" do
15
+ let(:component) { Form::Component::Input }
16
+ let(:type) { "text" }
17
+ let(:helper) { "text" }
18
+ it_behaves_like "input instantiation"
19
+ end
20
+
21
+ describe "#password" do
22
+ let(:component) { Form::Component::Input }
23
+ let(:type) { "password" }
24
+ let(:helper) { "password" }
25
+ it_behaves_like "input instantiation"
26
+ end
27
+
28
+ describe "#file" do
29
+ let(:component) { Form::Component::Input }
30
+ let(:type) { "file" }
31
+ let(:helper) { "file" }
32
+ it_behaves_like "input instantiation"
33
+ end
34
+
35
+ describe "#hidden" do
36
+ let(:component) { Form::Component::Input }
37
+ let(:type) { "hidden" }
38
+ let(:helper) { "hidden" }
39
+ it_behaves_like "input instantiation"
40
+ end
41
+
42
+ describe "#number" do
43
+ let(:component) { Form::Component::Input }
44
+ let(:type) { "number" }
45
+ let(:helper) { "number" }
46
+ it_behaves_like "input instantiation"
47
+ end
48
+
49
+ describe "#email" do
50
+ let(:component) { Form::Component::Input }
51
+ let(:type) { "email" }
52
+ let(:helper) { "email" }
53
+ it_behaves_like "input instantiation"
54
+ end
55
+
56
+ describe "#url" do
57
+ let(:component) { Form::Component::Input }
58
+ let(:type) { "url" }
59
+ let(:helper) { "url" }
60
+ it_behaves_like "input instantiation"
61
+ end
62
+
63
+ describe "#search" do
64
+ let(:component) { Form::Component::Input }
65
+ let(:type) { "search" }
66
+ let(:helper) { "search" }
67
+ it_behaves_like "input instantiation"
68
+ end
69
+
70
+ describe "#phone" do
71
+ let(:component) { Form::Component::Input }
72
+ let(:type) { "tel" }
73
+ let(:helper) { "phone" }
74
+ it_behaves_like "input instantiation"
75
+ end
76
+
77
+ describe "#textarea" do
78
+ let(:data) { mock("dataset").as_null_object }
79
+ let(:options) { mock("textarea options").as_null_object }
80
+ let(:textarea) { mock("textarea").as_null_object }
81
+ subject { described_class.new(data, :user) }
82
+
83
+ it "instantiates Form::Component::TextArea" do
84
+ Form::Component::TextArea.should_receive(:new).with(subject, :bio, options).and_return(textarea)
85
+ subject.textarea :bio, options
86
+ end
87
+
88
+ it "calls #to_html" do
89
+ Form::Component::TextArea.stub :new => textarea
90
+ textarea.should_receive(:to_html)
91
+ subject.textarea :bio
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,80 @@
1
+ require "spec_helper"
2
+
3
+ describe Form::Component::Base do
4
+ subject { described_class.new(nil, nil) }
5
+
6
+ describe "#composed_name" do
7
+ let(:form) { mock }
8
+ let(:name) { "name" }
9
+ subject { described_class.new(form, name) }
10
+
11
+ context "without base name" do
12
+ before { form.stub base_name: nil }
13
+ specify { subject.composed_name.should eql("name") }
14
+ end
15
+
16
+ context "for single base name" do
17
+ before { form.stub base_name: "user" }
18
+ specify { subject.composed_name.should eql("user[name]") }
19
+ end
20
+
21
+ context "for multiple base name" do
22
+ before { form.stub base_name: ["user", "profile"] }
23
+ specify { subject.composed_name.should eql("user[profile][name]") }
24
+ end
25
+ end
26
+
27
+ describe "#data" do
28
+ let(:form) { mock.as_null_object }
29
+ subject { described_class.new(form, "name") }
30
+
31
+ it "delegates to form's data" do
32
+ form.should_receive(:data).once
33
+ subject.data
34
+ end
35
+ end
36
+
37
+ describe "#value" do
38
+ let(:form) { mock }
39
+ let(:name) { "name" }
40
+ subject { described_class.new(form, name) }
41
+
42
+ context "with objects that respond to []" do
43
+ it "returns value for string key" do
44
+ form.stub data: {"name" => "John Doe"}
45
+ subject.value.should eql("John Doe")
46
+ end
47
+
48
+ it "returns value for symbol key" do
49
+ form.stub data: {:name => "John Doe"}
50
+ subject.value.should eql("John Doe")
51
+ end
52
+ end
53
+
54
+ context "with objects that respond to the attribute" do
55
+ it "returns value" do
56
+ form.stub data: mock(name: "John Doe")
57
+ subject.value.should eql("John Doe")
58
+ end
59
+ end
60
+
61
+ context "with no data objects" do
62
+ it "returns nil" do
63
+ form.stub :data
64
+ subject.value.should be_nil
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "#t" do
70
+ it "proxies arguments to I18n.t" do
71
+ I18n.should_receive(:t).with(:a, :b)
72
+ subject.t(:a, :b)
73
+ end
74
+ end
75
+
76
+ describe "#humanize" do
77
+ specify { subject.humanize("name").should eql("Name") }
78
+ specify { subject.humanize("full_name").should eql("Full name") }
79
+ end
80
+ end
@@ -0,0 +1,4 @@
1
+ require "spec_helper"
2
+
3
+ describe Form::Component::Input do
4
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ describe Form::Component::Label do
4
+ describe "#text" do
5
+ let(:form) { mock(base_name: nil) }
6
+ subject { described_class.new(form, :email) }
7
+
8
+ context "with simple name" do
9
+ it "returns specified text" do
10
+ subject.options[:text] = "E-mail address"
11
+ subject.text.should eql("E-mail address")
12
+ end
13
+
14
+ it "returns text for full scope" do
15
+ i18n :en, {form: {
16
+ labels: {email: "E-mail"}
17
+ }}
18
+
19
+ subject.text.should eql("E-mail")
20
+ end
21
+
22
+ it "returns humanized text" do
23
+ subject.text.should eql("Email")
24
+ end
25
+ end
26
+
27
+ context "with composed name" do
28
+ before { form.stub base_name: %w[user profile] }
29
+
30
+ it "returns specified text" do
31
+ subject.options[:text] = "E-mail address"
32
+ subject.text.should eql("E-mail address")
33
+ end
34
+
35
+ it "returns text for full scope" do
36
+ i18n :en, {form: {
37
+ labels: {user: {profile: {email: "E-mail"}}}
38
+ }}
39
+
40
+ subject.text.should eql("E-mail")
41
+ end
42
+
43
+ it "returns text for root scope" do
44
+ i18n :en, {form: {
45
+ labels: {email: "E-mail"}
46
+ }}
47
+
48
+ subject.text.should eql("E-mail")
49
+ end
50
+
51
+ it "returns humanized text" do
52
+ subject.text.should eql("Email")
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,62 @@
1
+ require "spec_helper"
2
+
3
+ describe Form::Component::TextArea do
4
+ context "with defaults" do
5
+ let(:form) { mock(:base_name => nil, :data => nil) }
6
+ subject { described_class.new(form, :bio) }
7
+
8
+ it "includes cols" do
9
+ subject.attributes[:cols].should eql(50)
10
+ end
11
+
12
+ it "includes rows" do
13
+ subject.attributes[:rows].should eql(5)
14
+ end
15
+
16
+ it "includes name" do
17
+ subject.attributes[:name].should eql("bio")
18
+ end
19
+
20
+ it "returns html" do
21
+ html = subject.to_html
22
+
23
+ html.should have_tag(:textarea, :count => 1)
24
+ html.should have_tag("[cols='50']")
25
+ html.should have_tag("[rows='5']")
26
+ html.should have_tag("[name='bio']")
27
+ end
28
+ end
29
+
30
+ context "with custom options" do
31
+ let(:form) { mock(:base_name => :user, :data => nil) }
32
+ subject {
33
+ described_class.new(form, :bio,
34
+ :autofocus => true,
35
+ :rows => 10,
36
+ :cols => 25
37
+ )
38
+ }
39
+
40
+ it "includes cols" do
41
+ subject.attributes[:cols].should eql(25)
42
+ end
43
+
44
+ it "includes rows" do
45
+ subject.attributes[:rows].should eql(10)
46
+ end
47
+
48
+ it "includes name" do
49
+ subject.attributes[:name].should eql("user[bio]")
50
+ end
51
+
52
+ it "returns html" do
53
+ html = subject.to_html
54
+
55
+ html.should have_tag(:textarea, :count => 1)
56
+ html.should have_tag("[cols='25']")
57
+ html.should have_tag("[rows='10']")
58
+ html.should have_tag("[name='user[bio]']")
59
+ html.should have_tag("[autofocus]")
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,9 @@
1
+ require "spec_helper"
2
+
3
+ describe Hash, "extensions" do
4
+ describe "#except" do
5
+ it "skips specified keys" do
6
+ {a: 1, b: 2, c: 3}.except(:a, :c).should eql(b: 2)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,119 @@
1
+ require "spec_helper"
2
+
3
+ describe Form::Tag do
4
+ subject { described_class.new(:p) }
5
+
6
+ describe "#initialize" do
7
+ it "yields instance" do
8
+ tag = nil
9
+ described_class.new(:p) {|t| tag = t}
10
+ tag.should be_a(described_class)
11
+ end
12
+
13
+ it "evaluates block" do
14
+ tag = nil
15
+ described_class.new(:p) { tag = self }
16
+ tag.should be_a(described_class)
17
+ end
18
+
19
+ context "argument position" do
20
+ it "moves position" do
21
+ tag = described_class.new(:p, class: "hello")
22
+ tag.attributes.should eql(class: "hello")
23
+ tag.content.should eql("")
24
+ end
25
+
26
+ it "keeps position" do
27
+ tag = described_class.new(:p, "Hello", class: "hello")
28
+ tag.attributes.should eql(class: "hello")
29
+ tag.content.should eql("Hello")
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "#<<" do
35
+ it "appends content" do
36
+ tag = described_class.new(:p)
37
+ tag << "Hello"
38
+ tag.to_s.should eql("<p>Hello</p>")
39
+ end
40
+ end
41
+
42
+ describe "#void?" do
43
+ it "detects void tag" do
44
+ subject = described_class.new(:br)
45
+ subject.should be_void
46
+ end
47
+
48
+ it "rejects as open tag" do
49
+ subject = described_class.new(:p)
50
+ subject.should_not be_void
51
+ end
52
+ end
53
+
54
+ describe "#to_s" do
55
+ it "renders tag" do
56
+ subject = described_class.new(:p, "Hello")
57
+ subject.to_s.should eql("<p>Hello</p>")
58
+ end
59
+
60
+ it "renders tag with attributes" do
61
+ subject = described_class.new(:p, "Hello", class: "hello")
62
+ subject.to_s.should eql(%[<p class="hello">Hello</p>])
63
+ end
64
+
65
+ it "renders open tag" do
66
+ subject = described_class.new(:hr)
67
+ subject.to_s.should eql("<hr>")
68
+ end
69
+
70
+ it "renders open tag with attributes" do
71
+ subject = described_class.new(:hr, class: "separator")
72
+ subject.to_s.should eql(%[<hr class="separator">])
73
+ end
74
+
75
+ it "renders boolean attributes" do
76
+ subject = described_class.new(:input, autofocus: true)
77
+ subject.to_s.should eql("<input autofocus>")
78
+ end
79
+
80
+ it "skips attributes with false value" do
81
+ subject = described_class.new(:input, autofocus: false)
82
+ subject.to_s.should eql("<input>")
83
+ end
84
+
85
+ it "skips attributes with empty value" do
86
+ subject = described_class.new(:input, value: "")
87
+ subject.to_s.should eql("<input>")
88
+ end
89
+
90
+ it "escapes attribute value" do
91
+ subject = described_class.new(:img, alt: "<3")
92
+ subject.to_s.should eql(%[<img alt="&lt;3">])
93
+ end
94
+ end
95
+
96
+ describe "#tag" do
97
+ it "renders simple tag" do
98
+ subject = described_class.new(:p)
99
+ subject.tag(:strong, "Hello")
100
+ subject.to_s.should eql("<p><strong>Hello</strong></p>")
101
+ end
102
+
103
+ it "renders complex tag" do
104
+ subject = described_class.new(:form, action: "some/path") do |form|
105
+ form.tag :p do |p|
106
+ p.tag :label, "E-mail", for: "user_email"
107
+ p.tag :input, type: "email", autofocus: true
108
+ end
109
+ end
110
+
111
+ subject.to_s.should have_tag("form[action='some/path']") do |form|
112
+ form.should have_tag("p") do |p|
113
+ p.should have_tag("label[for=user_email]")
114
+ p.should have_tag("input[type=email][autofocus]")
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end