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.
- 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
|