visionmedia-dm-forms 0.0.2
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 +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
|