formz 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,159 @@
1
+
2
+ module Formz
3
+
4
+ ##
5
+ # = Formz::Helpers
6
+ #
7
+ # The Formz:Helpers module provides unbound form related
8
+ # helper methods such as #checkbox, #password and others.
9
+
10
+ module Helpers
11
+
12
+ ##
13
+ # Return a form.
14
+
15
+ def form name, attrs = {}, &block
16
+ Tagz.tag :form, { :id => "form-#{name}" }.merge(attrs), &block
17
+ end
18
+
19
+ ##
20
+ # Return a legend with _contents_.
21
+
22
+ def legend contents, attrs = {}, &block
23
+ Tagz.tag :legend, contents, attrs, &block
24
+ end
25
+
26
+ ##
27
+ # Return a fieldset with optional _legend_.
28
+
29
+ def fieldset name, legend = nil, attrs = {}, &block
30
+ attrs, legend = legend, nil if legend.is_a? Hash
31
+ Tagz.tag :fieldset, legend ? legend(legend) : nil, { :id => "fieldset-#{name}" }.merge(attrs), &block
32
+ end
33
+
34
+ ##
35
+ # Return a textarea.
36
+
37
+ def textarea name, contents = nil, attrs = {}, &block
38
+ attrs, contents = contents, nil if contents.is_a? Hash
39
+ Tagz.tag :textarea, contents, { :name => name }.merge(attrs), &block
40
+ end
41
+
42
+ ##
43
+ # Wrap form elements in div _name_.
44
+
45
+ def group name, &block
46
+ Tagz.tag :div, { :class => "group-#{name}" }, &block
47
+ end
48
+
49
+ ##
50
+ # Wrap form buttons defined within _block_ in a div.
51
+
52
+ def buttons attrs = {}, &block
53
+ Tagz.tag :div, { :class => 'form-buttons' }.merge(attrs), &block
54
+ end
55
+
56
+ %w( checkbox radio text submit reset hidden button password file ).each do |name|
57
+ class_eval <<-END
58
+ def #{name} name, value = nil, attrs = {}
59
+ attrs, value = value, nil if value.is_a? Hash
60
+ attrs[:value] ||= value
61
+ Tagz.tag :input, { :type => :#{name}, :name => name }.merge(attrs)
62
+ end
63
+ END
64
+ end
65
+
66
+ %w( checkbox radio ).each do |name|
67
+ class_eval <<-END
68
+ def #{name}_group name, values, options = {}
69
+ values.map do |key, value|
70
+ #{name} name, key, :label => value, :checked => option_selected?(key, options)
71
+ end.join
72
+ end
73
+ END
74
+ end
75
+
76
+ ##
77
+ # Return select element _name_ with _options_ hash.
78
+
79
+ def select name, options, attrs = {}
80
+ options = select_options options, attrs
81
+ attrs.delete :selected
82
+ Tagz.tag :select, options, { :name => name }.merge(attrs)
83
+ end
84
+
85
+ ##
86
+ # Return select option _contents_ with _value_.
87
+
88
+ def select_option value, contents, attrs = {}
89
+ Tagz.tag :option, contents, { :value => value }.merge(attrs)
90
+ end
91
+
92
+ ##
93
+ # Return select option elements from _values_ with
94
+ # _options_ passed.
95
+ #
96
+ # === Options
97
+ #
98
+ # :selected string, symbol, or array of options selected
99
+ #
100
+
101
+ def select_options values, options = {}
102
+ normalize_select_prompt values, options
103
+ values.map do |key, value|
104
+ if value.is_a? Hash
105
+ Tagz.tag :optgroup, select_options(value, options), :label => key
106
+ elsif option_selected? key, options[:selected]
107
+ select_option key, value, :selected => true
108
+ else
109
+ select_option key, value
110
+ end
111
+ end.join
112
+ end
113
+
114
+ ##
115
+ # Check if option _key_ is _selected_.
116
+
117
+ def option_selected? key, selected
118
+ if Array === selected
119
+ selected.any? do |value|
120
+ key.to_s == value.to_s
121
+ end
122
+ else
123
+ key.to_s == selected.to_s
124
+ end
125
+ end
126
+
127
+ ##
128
+ # Normalize select prompt.
129
+ #
130
+ # * When _options_ contains a :prompt string it is assigned as the prompt
131
+ # * When :prompt is true the default of '- Select -' will become the prompt
132
+ # * The prompt is selected unless a specific option is explicitly selected.
133
+ #
134
+
135
+ def normalize_select_prompt values, options = {}
136
+ return unless :prompt.in? options
137
+ prompt = options.delete :prompt
138
+ options[:selected] = '' unless :selected.in? options
139
+ values[''] = String === prompt ? prompt : '- Select -'
140
+ end
141
+
142
+ #--
143
+ # Delegates
144
+ #++
145
+
146
+ module Delegates
147
+ %w( select_options select_option select checkbox radio text submit reset hidden
148
+ button password file checkbox_group radio_group buttons textarea fieldset
149
+ legend form group ).each do |meth|
150
+ class_eval <<-EOF
151
+ def #{meth} *args, &block
152
+ @contents << Formz::Helpers.#{meth}(*args, &block)
153
+ end
154
+ EOF
155
+ end
156
+ end
157
+
158
+ end
159
+ end
@@ -0,0 +1,12 @@
1
+
2
+ require 'formz'
3
+ require 'tagz/import'
4
+
5
+ include Formz::Labels
6
+ include Formz::Descriptions
7
+ include Formz::Errors
8
+ include Formz::Wrappers
9
+ include Formz::Helpers
10
+ include Formz::AutoEncoding
11
+ include Formz::FauxMethod
12
+ include Formz::Models
@@ -0,0 +1,55 @@
1
+
2
+ module Formz
3
+
4
+ ##
5
+ # = Formz::Labels
6
+ #
7
+ # The Formz:Labels module allows a :label attribute
8
+ # to be passed, which then adds a label tag to the
9
+ # markup output. Tags are nested nested within its
10
+ # label, in cases such as for checkboxes or radio
11
+ # buttons.
12
+ #
13
+ # When using labels, a :required attribute may also
14
+ # be passed, which will alter the markup to indicate
15
+ # the input field is required.
16
+ #
17
+ # === Examples
18
+ #
19
+ # tag :input, :type => :file, :name => :upload, :label => 'Upload', :required => true
20
+ #
21
+ # <div class="form-upload form-file">
22
+ # <label for="upload">Upload<em>*</em>:</label>
23
+ # <input type="file" name="upload" />
24
+ # </div>
25
+ #
26
+
27
+ module Labels
28
+ NESTED_LABEL_INPUT_TYPES = :checkbox, :radio
29
+
30
+ def create_tag name, contents, attrs, &block
31
+ if name != :optgroup && string = attrs.delete(:label)
32
+ label_attrs = { :for => attrs[:name], :required => attrs.delete(:required) }
33
+ if attrs[:type].in? NESTED_LABEL_INPUT_TYPES
34
+ label string, super, label_attrs
35
+ else
36
+ label(string, label_attrs) << super
37
+ end
38
+ else
39
+ super
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Return a label with _string_. When _contents_
45
+ # is present the label will act as a wrapper for
46
+ # checkboxes, radios etc.
47
+
48
+ def label string, contents = nil, attrs = {}
49
+ attrs, contents = contents, nil if contents.is_a? Hash
50
+ Tagz.tag :label, contents.to_s + string + (contents ? '' :
51
+ attrs.delete(:required) ? '<em>*</em>:' :
52
+ ':'), attrs
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,94 @@
1
+
2
+ module Formz
3
+
4
+ ##
5
+ # = Formz::Models
6
+ #
7
+
8
+ module Models
9
+
10
+ ##
11
+ # Form context model stack.
12
+
13
+ def form_context
14
+ $__form_context ||= []
15
+ end
16
+
17
+ ##
18
+ # Push _model_ as the current context while executing
19
+ # _block_; returning _block_'s results.
20
+
21
+ def with_form_context model, &block
22
+ form_context.push model
23
+ result = yield
24
+ form_context.pop
25
+ result
26
+ end
27
+
28
+ ##
29
+ # Return a form in context to _model_. _model_ may be
30
+ # an object, class, or symbol.
31
+
32
+ def form_for model, attrs = {}, &block
33
+ model = Object.const_get model.to_s.capitalize if model.is_a? Symbol
34
+ model = model.new if model.is_a? Class
35
+ with_form_context model do
36
+ form model_name(model), attrs, &block
37
+ end
38
+ end
39
+
40
+ def select name, options, attrs = {}
41
+ if model = form_context.last
42
+ if model_has_property? model, name
43
+ if value = model.send(name)
44
+ attrs[:selected] ||= value
45
+ end
46
+ end
47
+ end
48
+ super
49
+ end
50
+
51
+ def create_tag name, contents, attrs, &block
52
+ unless name == :form || form_context.blank?
53
+ model, tag_name = form_context.last, attrs[:name]
54
+ if attrs[:value].nil?
55
+ if attrs[:name] && model_has_property?(model, attrs[:name])
56
+ # Errors
57
+ if errors = model.errors.on(attrs[:name])
58
+ attrs[:error] = errors.first
59
+ end
60
+ # Values
61
+ attrs[:name] = '%s[%s]' % [model_name(model), tag_name]
62
+ value = model.send tag_name
63
+ case name
64
+ when :textarea ; contents = value
65
+ else attrs[:value] = value
66
+ end
67
+ end
68
+ end
69
+ if default = attrs.delete(:default)
70
+ attrs[:value] = default if attrs[:value].nil?
71
+ end
72
+ end
73
+ super
74
+ end
75
+
76
+ ##
77
+ # Check if _model_ has _property_name_.
78
+
79
+ def model_has_property? model, property_name
80
+ model.send(:properties).any? do |property|
81
+ property.name == property_name.to_sym
82
+ end
83
+ end
84
+
85
+ ##
86
+ # Return lowercase name of _model_. Forum::Post
87
+ # will become :post, etc.
88
+
89
+ def model_name model
90
+ model.class.to_s.split('::').last.downcase.to_sym
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module Formz
3
+ VERSION = '0.0.4'
4
+ end
@@ -0,0 +1,34 @@
1
+
2
+ module Formz
3
+
4
+ ##
5
+ # = Formz::Wrappers
6
+ #
7
+ # The Formz:Wrappers module wraps input, textarea
8
+ # and select tags with wrapping div elements to aid
9
+ # in styling.
10
+ #
11
+ # === Examples
12
+ #
13
+ # tag :input, :type => :file, :name => :upload
14
+ #
15
+ # <div class="field-upload field-file">
16
+ # <input type="file" name="upload" />
17
+ # </div>
18
+ #
19
+
20
+ module Wrappers
21
+ WRAP_TAGS = :input, :textarea, :select
22
+
23
+ def create_tag name, contents, attrs, &block
24
+ if name.in? WRAP_TAGS
25
+ classes = "field-#{attrs[:name].to_s.gsub('[', '-').gsub(']', '')}"
26
+ classes.add_class "field-#{attrs[:type]}" if :type.in? attrs
27
+ classes.add_class 'field'
28
+ Tagz.tag :div, super, :class => classes
29
+ else
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,14 @@
1
+
2
+ require File.dirname(__FILE__) + '/spec_helper'
3
+
4
+ describe Formz do
5
+ describe "autoencoding" do
6
+ it "should set the forms enctype properly when a file field is present" do
7
+ markup = form :image_upload do
8
+ file :image
9
+ submit :op, 'Upload'
10
+ end
11
+ markup.should have_tag('form[@enctype="form/multi-part"]')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+
2
+ require File.dirname(__FILE__) + '/spec_helper'
3
+
4
+ describe Formz do
5
+ describe "descriptions" do
6
+ it "should add descriptions to a field" do
7
+ text(:name, :description => 'Enter your firstname.').
8
+ should have_tag('span.description', 'Enter your firstname.')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+
2
+ require File.dirname(__FILE__) + '/spec_helper'
3
+
4
+ describe Formz do
5
+ describe "errors" do
6
+ before :each do
7
+ @markup = form :login do
8
+ text :username, :label => 'Username'
9
+ password :password, :label => 'Password', :error => 'Invalid Password'
10
+ end
11
+ end
12
+
13
+ it "should add error classes" do
14
+ @markup.should have_tag('input[@type=password].error')
15
+ end
16
+
17
+ it "should add error messages after failing fields" do
18
+ @markup.should have_tag('span.error-message', 'Invalid Password')
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+
2
+ class User
3
+ include DataMapper::Resource
4
+ property :uid, Serial
5
+ property :name, String, :length => 5..20
6
+ property :email, String, :format => :email_address
7
+ property :role, String
8
+ property :signature, Text, :default => 'Enter your forum signature'
9
+ end
10
+
11
+ Factory.define :user do |user|
12
+ user.uid 1
13
+ user.name 'tjholowaychuk'
14
+ user.email 'tj@vision-media.ca'
15
+ user.role 'admin'
16
+ user.signature 'Foo bar'
17
+ end
18
+
19
+ Factory.define :invalid_user, :class => :user do |user|
20
+ user.uid 2
21
+ user.name 'tjholowaychuk'
22
+ user.email 'foobar'
23
+ user.role 'admin'
24
+ user.signature 'Foo bar'
25
+ end
@@ -0,0 +1,24 @@
1
+
2
+ require File.dirname(__FILE__) + '/spec_helper'
3
+
4
+ describe Formz do
5
+ describe "faux method" do
6
+ it "should add a hidden _method field when verb is not POST or GET" do
7
+ form(:save, :method => :put).should have_tag('form[@method=post]') do |form|
8
+ form.should have_tag('input[@type=hidden]') do |hidden|
9
+ hidden['name'].should == '_method'
10
+ hidden['value'].should == 'put'
11
+ end
12
+ end
13
+ end
14
+
15
+ it "should not duplicate fields" do
16
+ form(:save, :method => :put).should have_tag('input[@name=_method]', :times => 1)
17
+ end
18
+
19
+ it "should not add a hidden field when GET or POST" do
20
+ form(:search, :method => :post).should_not have_tag('input[@type=hidden]')
21
+ form(:search, :method => :get).should_not have_tag('input[@type=hidden]')
22
+ end
23
+ end
24
+ end