formz 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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