visionmedia-dm-forms 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +4 -0
- data/Manifest +27 -0
- data/README.rdoc +62 -0
- data/Rakefile +16 -0
- data/Todo.rdoc +27 -0
- data/dm-forms.gemspec +37 -0
- data/examples/benchmarks.rb +118 -0
- data/examples/datamapper.rb +37 -0
- data/examples/elements.rb +31 -0
- data/examples/haml.rb +21 -0
- data/examples/login.haml +4 -0
- data/lib/dm-forms/core_ext.rb +39 -0
- data/lib/dm-forms/elements.rb +210 -0
- data/lib/dm-forms/model_elements.rb +33 -0
- data/lib/dm-forms/tag.rb +156 -0
- data/lib/dm-forms/version.rb +9 -0
- data/lib/dm-forms.rb +32 -0
- data/spec/functional/core_ext_spec.rb +56 -0
- data/spec/functional/elements_spec.rb +209 -0
- data/spec/functional/tag_spec.rb +49 -0
- data/spec/integration/datamapper_spec.rb +70 -0
- data/spec/integration/haml_spec.rb +29 -0
- data/spec/spec_helper.rb +10 -0
- data/tasks/benchmarks.rake +5 -0
- data/tasks/docs.rake +13 -0
- data/tasks/gemspec.rake +3 -0
- data/tasks/spec.rake +25 -0
- metadata +111 -0
data/History.rdoc
ADDED
data/Manifest
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
History.rdoc
|
2
|
+
Manifest
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
Todo.rdoc
|
6
|
+
examples/benchmarks.rb
|
7
|
+
examples/datamapper.rb
|
8
|
+
examples/elements.rb
|
9
|
+
examples/haml.rb
|
10
|
+
examples/login.haml
|
11
|
+
lib/dm-forms.rb
|
12
|
+
lib/dm-forms/core_ext.rb
|
13
|
+
lib/dm-forms/elements.rb
|
14
|
+
lib/dm-forms/model_elements.rb
|
15
|
+
lib/dm-forms/tag.rb
|
16
|
+
lib/dm-forms/version.rb
|
17
|
+
spec/functional/core_ext_spec.rb
|
18
|
+
spec/functional/elements_spec.rb
|
19
|
+
spec/functional/tag_spec.rb
|
20
|
+
spec/integration/datamapper_spec.rb
|
21
|
+
spec/integration/haml_spec.rb
|
22
|
+
spec/spec_helper.rb
|
23
|
+
tasks/benchmarks.rake
|
24
|
+
tasks/docs.rake
|
25
|
+
tasks/gemspec.rake
|
26
|
+
tasks/spec.rake
|
27
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
= DataMapper Forms
|
3
|
+
|
4
|
+
DataMapper model form generation.
|
5
|
+
|
6
|
+
== Features:
|
7
|
+
|
8
|
+
* Integrates with DataMapper
|
9
|
+
* Fast; over 1000 elements in 0.1 seconds (rake benchmark)
|
10
|
+
* Error reporting
|
11
|
+
* Handles restful HTTP verbs
|
12
|
+
* Provides low-level form elements uncoupled from DataMapper
|
13
|
+
|
14
|
+
== Examples:
|
15
|
+
|
16
|
+
Pretend in a magical world we have a User model, and only the email is valid while we are updating.
|
17
|
+
Based on the situation above, the form would render similar to the markup beneath the
|
18
|
+
example.
|
19
|
+
|
20
|
+
errors_for @user
|
21
|
+
form_for @user, :action => '/user' do |f|
|
22
|
+
f.textfield :name, :label => 'Name'
|
23
|
+
f.textfield :email, :label => 'Email'
|
24
|
+
f.submit :op, :value => 'Update'
|
25
|
+
end
|
26
|
+
|
27
|
+
<ul class="messages error">
|
28
|
+
<li>Name has an invalid format</li>
|
29
|
+
</ul>
|
30
|
+
<form action="/user" method="post" id="form-user">
|
31
|
+
<input type="hidden" name="_method" value="put" />
|
32
|
+
<label for="name">Name:</label>
|
33
|
+
<input type="textfield" class="error form-textfield form-name" name="name" />
|
34
|
+
<label for="email">Email:</label>
|
35
|
+
<input type="textfield" class="form-textfield form-email" name="email" />
|
36
|
+
<input type="submit" class="form-submit form-op" value="Update" name="op" />
|
37
|
+
</form>
|
38
|
+
|
39
|
+
== License:
|
40
|
+
|
41
|
+
(The MIT License)
|
42
|
+
|
43
|
+
Copyright (c) 2008 TJ Holowaychuk
|
44
|
+
|
45
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
46
|
+
a copy of this software and associated documentation files (the
|
47
|
+
'Software'), to deal in the Software without restriction, including
|
48
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
49
|
+
distribute, sublicense, an d/or sell copies of the Software, and to
|
50
|
+
permit persons to whom the Software is furnished to do so, subject to
|
51
|
+
the following conditions:
|
52
|
+
|
53
|
+
The above copyright notice and this permission notice shall be
|
54
|
+
included in all copies or substantial portions of the Software.
|
55
|
+
|
56
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
57
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
58
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
59
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
60
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
61
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
62
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'echoe'
|
5
|
+
require './lib/dm-forms.rb'
|
6
|
+
|
7
|
+
Echoe.new("dm-forms", DataMapper::Form::VERSION::STRING) do |p|
|
8
|
+
p.author = "TJ Holowaychuk"
|
9
|
+
p.email = "tj@vision-media.ca"
|
10
|
+
p.summary = "DataMapper model form generation"
|
11
|
+
p.url = "http://github.com/visionmedia/dm-forms"
|
12
|
+
p.runtime_dependencies = ['dm-core']
|
13
|
+
p.development_dependencies = ['rspec_hpricot_matchers']
|
14
|
+
end
|
15
|
+
|
16
|
+
Dir['tasks/**/*.rake'].sort.each { |lib| load lib }
|
data/Todo.rdoc
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
== Major:
|
3
|
+
|
4
|
+
* match messages markup to jquery ui if reasonable
|
5
|
+
* <optgroup label
|
6
|
+
* assign :required based on model?
|
7
|
+
* clean up examples
|
8
|
+
* use have_tag matcher
|
9
|
+
* add enctype automatically
|
10
|
+
* Fix select option ordering ... poor unordered hashes :(
|
11
|
+
* Add nesting to select options, and select groups
|
12
|
+
* escape xml in attrs
|
13
|
+
* Support both instance_eval and block
|
14
|
+
* wrap all in div class="form-TYPE" and adjust spec...
|
15
|
+
* XHTML ... validate
|
16
|
+
* perform some indenting (or all but make it optional)
|
17
|
+
* increase performance
|
18
|
+
* preview rdoc / finish documenting ...
|
19
|
+
|
20
|
+
== Minor:
|
21
|
+
|
22
|
+
* Aggregate / faux elements ...
|
23
|
+
* date_field :bday, :format => 'YYYY-MM-DD', :value => '1987-05-25'
|
24
|
+
|
25
|
+
== Brainstorming:
|
26
|
+
|
27
|
+
* Nothing
|
data/dm-forms.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{dm-forms}
|
5
|
+
s.version = "0.0.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["TJ Holowaychuk"]
|
9
|
+
s.date = %q{2009-01-15}
|
10
|
+
s.description = %q{DataMapper model form generation}
|
11
|
+
s.email = %q{tj@vision-media.ca}
|
12
|
+
s.extra_rdoc_files = ["README.rdoc", "lib/dm-forms.rb", "lib/dm-forms/core_ext.rb", "lib/dm-forms/elements.rb", "lib/dm-forms/model_elements.rb", "lib/dm-forms/tag.rb", "lib/dm-forms/version.rb", "tasks/benchmarks.rake", "tasks/docs.rake", "tasks/gemspec.rake", "tasks/spec.rake"]
|
13
|
+
s.files = ["History.rdoc", "Manifest", "README.rdoc", "Rakefile", "Todo.rdoc", "examples/benchmarks.rb", "examples/datamapper.rb", "examples/elements.rb", "examples/haml.rb", "examples/login.haml", "lib/dm-forms.rb", "lib/dm-forms/core_ext.rb", "lib/dm-forms/elements.rb", "lib/dm-forms/model_elements.rb", "lib/dm-forms/tag.rb", "lib/dm-forms/version.rb", "spec/functional/core_ext_spec.rb", "spec/functional/elements_spec.rb", "spec/functional/tag_spec.rb", "spec/integration/datamapper_spec.rb", "spec/integration/haml_spec.rb", "spec/spec_helper.rb", "tasks/benchmarks.rake", "tasks/docs.rake", "tasks/gemspec.rake", "tasks/spec.rake", "dm-forms.gemspec"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://github.com/visionmedia/dm-forms}
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Dm-forms", "--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{dm-forms}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{DataMapper model form generation}
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 2
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<dm-core>, [">= 0"])
|
28
|
+
s.add_development_dependency(%q<rspec_hpricot_matchers>, [">= 0"])
|
29
|
+
else
|
30
|
+
s.add_dependency(%q<dm-core>, [">= 0"])
|
31
|
+
s.add_dependency(%q<rspec_hpricot_matchers>, [">= 0"])
|
32
|
+
end
|
33
|
+
else
|
34
|
+
s.add_dependency(%q<dm-core>, [">= 0"])
|
35
|
+
s.add_dependency(%q<rspec_hpricot_matchers>, [">= 0"])
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
require 'dm-forms'
|
4
|
+
require 'benchmark'
|
5
|
+
require 'dm-core'
|
6
|
+
require 'dm-validations'
|
7
|
+
DataMapper.setup :default, 'sqlite3::memory:'
|
8
|
+
include DataMapper::Form::ModelElements
|
9
|
+
|
10
|
+
#--
|
11
|
+
# Models
|
12
|
+
#++
|
13
|
+
|
14
|
+
class User
|
15
|
+
include DataMapper::Resource
|
16
|
+
property :id, Serial
|
17
|
+
property :name, String, :format => /^[\w]+$/
|
18
|
+
property :email, String, :format => :email_address
|
19
|
+
end
|
20
|
+
|
21
|
+
DataMapper.auto_migrate!
|
22
|
+
|
23
|
+
$user = User.new :name => 'tj', :email => 'invalid email@lame.com'
|
24
|
+
|
25
|
+
#--
|
26
|
+
# Benchmarks
|
27
|
+
#++
|
28
|
+
|
29
|
+
puts
|
30
|
+
puts 'Single element'
|
31
|
+
Benchmark.bm(25) do |x|
|
32
|
+
x.report("10 elements") {
|
33
|
+
10.times do
|
34
|
+
textarea :comments, :value => 'Enter your comments here', :label => 'Comments:', :required => true
|
35
|
+
end
|
36
|
+
}
|
37
|
+
x.report("100 elements") {
|
38
|
+
100.times do
|
39
|
+
textarea :comments, :value => 'Enter your comments here', :label => 'Comments:', :required => true
|
40
|
+
end
|
41
|
+
}
|
42
|
+
x.report("1000 elements") {
|
43
|
+
1000.times do
|
44
|
+
textarea :comments, :value => 'Enter your comments here', :label => 'Comments:', :required => true
|
45
|
+
end
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
puts
|
50
|
+
puts 'Capture elements within a fieldset'
|
51
|
+
Benchmark.bm(25) do |x|
|
52
|
+
x.report("10 elements") {
|
53
|
+
5.times do
|
54
|
+
fieldset :comments do |f|
|
55
|
+
f.textarea :comments, :value => 'Enter your comments here', :label => 'Comments:', :required => true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
}
|
59
|
+
x.report("100 elements") {
|
60
|
+
50.times do
|
61
|
+
fieldset :comments do |f|
|
62
|
+
f.textarea :comments, :value => 'Enter your comments here', :label => 'Comments:', :required => true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
}
|
66
|
+
x.report("1000 elements") {
|
67
|
+
500.times do
|
68
|
+
fieldset :comments do |f|
|
69
|
+
f.textarea :comments, :value => 'Enter your comments here', :label => 'Comments:', :required => true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Not real-world examples ... just for benchmarking purposes
|
76
|
+
puts
|
77
|
+
puts 'Entire forms'
|
78
|
+
Benchmark.bm(25) do |x|
|
79
|
+
x.report("Login") {
|
80
|
+
form :login do |f|
|
81
|
+
f.textfield :name, :label => 'Username', :required => true
|
82
|
+
f.textfield :email, :label => 'Email', :required => true
|
83
|
+
f.textfield :pass, :label => 'Password', :required => true
|
84
|
+
f.submit :op, :value => 'Login'
|
85
|
+
end
|
86
|
+
}
|
87
|
+
|
88
|
+
x.report("Register") {
|
89
|
+
form :register do |f|
|
90
|
+
f.fieldset :general do |f|
|
91
|
+
f.textfield :name, :label => 'Username', :required => true
|
92
|
+
f.textfield :email, :label => 'Email', :required => true
|
93
|
+
f.textfield :pass, :label => 'Password', :required => true
|
94
|
+
f.password :pass_confirm
|
95
|
+
end
|
96
|
+
f.fieldset :details do |f|
|
97
|
+
f.textfield :city, :label => 'City'
|
98
|
+
f.textfield :zip, :label => 'Postal Code'
|
99
|
+
end
|
100
|
+
f.fieldset :forums do |f|
|
101
|
+
f.textarea :signature, :label => 'Signature', :description => 'Enter a signature which will appear below your forum posts.'
|
102
|
+
end
|
103
|
+
f.submit :op, :value => 'Register'
|
104
|
+
end
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
puts
|
109
|
+
puts 'Entires form with #form_for'
|
110
|
+
Benchmark.bm(25) do |x|
|
111
|
+
x.report("User") {
|
112
|
+
form_for $user do |f|
|
113
|
+
f.textfield :name, :label => 'Username', :required => true
|
114
|
+
f.textfield :email, :label => 'Email', :required => true
|
115
|
+
f.submit :op, :value => 'Login'
|
116
|
+
end
|
117
|
+
}
|
118
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
require 'dm-forms'
|
4
|
+
require 'dm-core'
|
5
|
+
require 'dm-validations'
|
6
|
+
|
7
|
+
include DataMapper::Form::ModelElements
|
8
|
+
DataMapper.setup :default, 'sqlite3::memory:'
|
9
|
+
|
10
|
+
class User
|
11
|
+
include DataMapper::Resource
|
12
|
+
property :id, Serial
|
13
|
+
property :name, String, :format => /^[\w]+$/
|
14
|
+
property :email, String, :format => :email_address
|
15
|
+
end
|
16
|
+
|
17
|
+
DataMapper.auto_migrate!
|
18
|
+
user = User.new :name => 'tj', :email => 'invalid email@lame.com'
|
19
|
+
|
20
|
+
s = errors_for(user)
|
21
|
+
s << form_for(user, :action => '/user') do |f|
|
22
|
+
f.textarea :name
|
23
|
+
f.textarea :email
|
24
|
+
f.submit :op, :value => 'Add'
|
25
|
+
end
|
26
|
+
puts s
|
27
|
+
|
28
|
+
user.email = 'tj@vision-media.ca'
|
29
|
+
user.save
|
30
|
+
|
31
|
+
s = errors_for(user)
|
32
|
+
s << form_for(user, :action => '/user') do |f|
|
33
|
+
f.textarea :name, :label => 'Name'
|
34
|
+
f.textarea :email, :label => 'Email'
|
35
|
+
f.submit :op, :value => 'Update'
|
36
|
+
end
|
37
|
+
puts s
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
require 'dm-forms'
|
4
|
+
|
5
|
+
include DataMapper::Form::Elements
|
6
|
+
|
7
|
+
def example before, &block
|
8
|
+
puts before << "\n\n"
|
9
|
+
puts yield.gsub!(/^/, ' ')
|
10
|
+
puts
|
11
|
+
end
|
12
|
+
|
13
|
+
example %(textarea :comments) do
|
14
|
+
textarea :comments
|
15
|
+
end
|
16
|
+
|
17
|
+
example %(textarea :comments, :label => 'Comments', :description => 'Tell us what you think') do
|
18
|
+
textarea :comments, :label => 'Comments', :description => 'Tell us what you think'
|
19
|
+
end
|
20
|
+
|
21
|
+
example %(textfield :email, :label => 'Email', :required => true) do
|
22
|
+
textfield :email, :label => 'Email', :required => true
|
23
|
+
end
|
24
|
+
|
25
|
+
example 'Login' do
|
26
|
+
form :login do |f|
|
27
|
+
f.textfield :name, :label => 'Username'
|
28
|
+
f.textfield :pass, :label => 'Password'
|
29
|
+
f.submit :op, :value => 'Login'
|
30
|
+
end
|
31
|
+
end
|
data/examples/haml.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
require 'rubygems'
|
4
|
+
require 'haml'
|
5
|
+
require 'dm-forms'
|
6
|
+
|
7
|
+
include DataMapper::Form::ModelElements
|
8
|
+
|
9
|
+
# Haml does not play very nice with nested Ruby, but then again
|
10
|
+
# there is relatively nothing that cannot be altered via styling
|
11
|
+
# so placing forms directly within a Haml view is not entirely
|
12
|
+
# necessary.
|
13
|
+
def login_form
|
14
|
+
form :login, :action => '/user' do |f|
|
15
|
+
f.textarea :name, :label => 'Name'
|
16
|
+
f.textarea :email, :label => 'Email'
|
17
|
+
f.submit :op, :value => 'Login'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
puts Haml::Engine.new(File.read(File.dirname(__FILE__) + '/login.haml')).render
|
data/examples/login.haml
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
class NilClass
|
3
|
+
def to_xml_attributes #:nodoc:
|
4
|
+
''
|
5
|
+
end
|
6
|
+
alias :to_html_attributes :to_xml_attributes
|
7
|
+
end
|
8
|
+
|
9
|
+
class String
|
10
|
+
|
11
|
+
##
|
12
|
+
# Convert to a human readable string.
|
13
|
+
#
|
14
|
+
# === Examples:
|
15
|
+
#
|
16
|
+
# 'im_a_simple.String'.humanize # => 'im a simple String'
|
17
|
+
#
|
18
|
+
|
19
|
+
def humanize
|
20
|
+
gsub(/[^a-zA-Z\d]/, ' ') || self
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Indent a string with pseudo +tabs+ (spaces). Defaults to a single tab.
|
25
|
+
|
26
|
+
def indent tabs = 1
|
27
|
+
gsub /^/, ' ' * tabs
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Symbol
|
32
|
+
|
33
|
+
##
|
34
|
+
# Convert to a human readable string. See String#humanize
|
35
|
+
|
36
|
+
def humanize
|
37
|
+
to_s.humanize
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
|
2
|
+
module DataMapper
|
3
|
+
module Form
|
4
|
+
module Elements
|
5
|
+
|
6
|
+
##
|
7
|
+
# Proxy object for capturing elements. See Elements#capture_elements.
|
8
|
+
|
9
|
+
class Proxy
|
10
|
+
def initialize model = nil
|
11
|
+
@model = model
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing meth, *args, &block
|
15
|
+
add_error_class_to args if has_a_model_with_errors_on? args.first
|
16
|
+
(@elements ||= []) << Elements.send(meth, *args, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_error_class_to args
|
20
|
+
((args[1] ||= {})[:class] ||= '') << ' error'
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_a_model_with_errors_on? meth
|
24
|
+
@model and !@model.valid? and @model.errors.on meth
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module_function
|
29
|
+
|
30
|
+
##
|
31
|
+
# Generates a generic HTML +name+ tag. Although this method is
|
32
|
+
# generally used internally by dm-forms, you may utilize it directly
|
33
|
+
# passing any of the following +options+.
|
34
|
+
#
|
35
|
+
# === Options:
|
36
|
+
#
|
37
|
+
# :self_closing Wither or not the element should self-close (<br />)
|
38
|
+
# :attributes Hash of attributes such as :type => :textfield
|
39
|
+
#
|
40
|
+
|
41
|
+
def tag name, options = {}, &block
|
42
|
+
Tag.new(name, options, &block).render
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Generates a label.
|
47
|
+
|
48
|
+
def label value, options = {}
|
49
|
+
value << ':'
|
50
|
+
value << '<em>*</em>' if options.delete :required
|
51
|
+
%(<label for="#{options[:for]}">#{value}</label>\n)
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Generates a legend.
|
56
|
+
|
57
|
+
def legend value
|
58
|
+
%(<legend>#{value}</legend>)
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Generates a description.
|
63
|
+
|
64
|
+
def desc text
|
65
|
+
%(\n<p class="description">#{text}</p>) unless text.blank?
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Generates an option.
|
70
|
+
|
71
|
+
def option value, title
|
72
|
+
%(<option value="#{value}">#{title}</option>\n)
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Generates a form.
|
77
|
+
|
78
|
+
def form name, options = {}, &block
|
79
|
+
options = { :method => :post, :id => "form-#{name}" }.merge options
|
80
|
+
unless valid_http_verb? options
|
81
|
+
old_value = options[:value] || ''
|
82
|
+
options[:value] = hidden_method(options[:method]) << old_value
|
83
|
+
options[:method] = :post
|
84
|
+
end
|
85
|
+
tag :form, :attributes => options, &block
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Generates a fieldset.
|
90
|
+
|
91
|
+
def fieldset name, options = {}, &block
|
92
|
+
legend_value = options.has_key?(:legend) ? options.delete(:legend) : name.humanize.capitalize
|
93
|
+
options = { :class => "fieldset-#{name}" }.merge options
|
94
|
+
options[:value] = "\n" << legend(legend_value) << (options.delete(:value) || '')
|
95
|
+
tag :fieldset, :attributes => options, &block
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Generates a textfield.
|
100
|
+
|
101
|
+
def textfield name, options = {}
|
102
|
+
options = { :type => :textfield, :name => name }.merge options
|
103
|
+
tag :input, :self_closing => true, :attributes => options
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Generates a password field.
|
108
|
+
|
109
|
+
def password name, options = {}
|
110
|
+
options = { :type => :password, :name => name }.merge options
|
111
|
+
tag :input, :self_closing => true, :attributes => options
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Generates a checkbox.
|
116
|
+
|
117
|
+
def checkbox name, options = {}
|
118
|
+
options = { :type => :checkbox, :name => name }.merge options
|
119
|
+
tag :input, :self_closing => true, :attributes => options
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Generates a hidden field.
|
124
|
+
|
125
|
+
def hidden name, options = {}
|
126
|
+
options = { :type => :hidden, :name => name }.merge options
|
127
|
+
tag :input, :self_closing => true, :attributes => options
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Creates hidden _method, with value of +method+.
|
132
|
+
|
133
|
+
def hidden_method method
|
134
|
+
hidden :_method, :value => method
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Generates a radio button.
|
139
|
+
|
140
|
+
def radio name, options = {}
|
141
|
+
options = { :type => :radio, :name => name }.merge options
|
142
|
+
tag :input, :self_closing => true, :attributes => options
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Generates a file field.
|
147
|
+
|
148
|
+
def file name, options = {}
|
149
|
+
options = { :type => :file, :name => name }.merge options
|
150
|
+
tag :input, :self_closing => true, :attributes => options
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# Generates a select field.
|
155
|
+
|
156
|
+
def select name, options = {}, &block
|
157
|
+
options = { :name => name, :value => "\n" }.merge options
|
158
|
+
options[:value] << capture_elements(&block) if block_given?
|
159
|
+
options[:value] << select_options(options) if options.include? :options
|
160
|
+
tag :select, :attributes => options
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Generates a textarea.
|
165
|
+
|
166
|
+
def textarea name, options = {}, &block
|
167
|
+
options = { :name => name }.merge options
|
168
|
+
tag :textarea, :attributes => options, &block
|
169
|
+
end
|
170
|
+
|
171
|
+
##
|
172
|
+
# Generates a submit button.
|
173
|
+
|
174
|
+
def submit name, options = {}
|
175
|
+
options = { :type => :submit, :name => name }.merge options
|
176
|
+
tag :input, :self_closing => true, :attributes => options
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# Generates a button.
|
181
|
+
|
182
|
+
def button name, options = {}
|
183
|
+
type = options.has_key?(:src) ? :image : :button
|
184
|
+
options = { :type => type, :name => name }.merge options
|
185
|
+
tag :input, :self_closing => true, :attributes => options
|
186
|
+
end
|
187
|
+
|
188
|
+
##
|
189
|
+
# Capture results of elements called within +block+. Optionally
|
190
|
+
# a DataMapper model may be passed, at which point error classes
|
191
|
+
# will be applied to invalid elements.
|
192
|
+
|
193
|
+
def capture_elements model = nil, &block
|
194
|
+
elements = yield Proxy.new(model)
|
195
|
+
elements.join
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def select_options options #:nodoc:
|
201
|
+
options.delete(:options).collect { |value, title| option(value, title) }.join
|
202
|
+
end
|
203
|
+
|
204
|
+
def valid_http_verb? options #:nodoc:
|
205
|
+
[:get, :post].include? options[:method]
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
module DataMapper
|
3
|
+
module Form
|
4
|
+
module ModelElements
|
5
|
+
|
6
|
+
include Elements
|
7
|
+
|
8
|
+
##
|
9
|
+
# Generates a form.
|
10
|
+
|
11
|
+
def form_for model, options = {}, &block
|
12
|
+
id = model.class.to_s.downcase
|
13
|
+
method = model.new_record? ? :post : :put
|
14
|
+
options = { :model => model, :method => method }.merge options
|
15
|
+
form id, options, &block
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Return markup for errors on +model+.
|
20
|
+
|
21
|
+
def errors_for model
|
22
|
+
if not model.all_valid?
|
23
|
+
s = %(<ul class="messages error">\n)
|
24
|
+
s << model.errors.collect { |error| "<li>#{error.first}</li>" }.join("\n")
|
25
|
+
s << "\n</ul>\n"
|
26
|
+
else
|
27
|
+
''
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|