formz 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +16 -0
- data/Manifest +33 -0
- data/README.rdoc +54 -0
- data/Rakefile +18 -0
- data/benchmarks/small.rb +48 -0
- data/examples/model.rb +44 -0
- data/examples/small.rb +19 -0
- data/formz.gemspec +39 -0
- data/lib/formz.rb +39 -0
- data/lib/formz/autoencoding.rb +16 -0
- data/lib/formz/descriptions.rb +30 -0
- data/lib/formz/errors.rb +40 -0
- data/lib/formz/fauxmethod.rb +32 -0
- data/lib/formz/helpers.rb +159 -0
- data/lib/formz/import.rb +12 -0
- data/lib/formz/labels.rb +55 -0
- data/lib/formz/models.rb +94 -0
- data/lib/formz/version.rb +4 -0
- data/lib/formz/wrappers.rb +34 -0
- data/spec/autoencoding_spec.rb +14 -0
- data/spec/descriptions_spec.rb +11 -0
- data/spec/errors_spec.rb +21 -0
- data/spec/factories/user.rb +25 -0
- data/spec/fauxmethod_spec.rb +24 -0
- data/spec/helpers_spec.rb +149 -0
- data/spec/labels_spec.rb +18 -0
- data/spec/models_spec.rb +115 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/wrappers_spec.rb +20 -0
- data/tasks/docs.rake +13 -0
- data/tasks/gemspec.rake +3 -0
- data/tasks/spec.rake +25 -0
- metadata +135 -0
@@ -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
|
data/lib/formz/import.rb
ADDED
@@ -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
|
data/lib/formz/labels.rb
ADDED
@@ -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
|
data/lib/formz/models.rb
ADDED
@@ -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,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
|
data/spec/errors_spec.rb
ADDED
@@ -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
|