visionmedia-dm-forms 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,8 @@
1
1
 
2
+ === 0.0.3 / 2009-03-23
3
+
4
+ * Refactored everything :)
5
+
2
6
  === 0.0.1 / 2009-01-13
3
7
 
4
8
  * Initial release
data/Manifest CHANGED
@@ -1,27 +1,30 @@
1
- History.rdoc
2
- Manifest
3
- README.rdoc
4
- Rakefile
5
- Todo.rdoc
1
+ dm-forms.gemspec
6
2
  examples/benchmarks.rb
7
- examples/datamapper.rb
8
- examples/elements.rb
9
- examples/haml.rb
10
- examples/login.haml
11
- lib/dm-forms.rb
3
+ examples/login.rb
4
+ History.rdoc
5
+ lib/dm-forms/base.rb
12
6
  lib/dm-forms/core_ext.rb
13
- lib/dm-forms/elements.rb
14
- lib/dm-forms/model_elements.rb
7
+ lib/dm-forms/helpers.rb
8
+ lib/dm-forms/mixins/descriptions.rb
9
+ lib/dm-forms/mixins/errors.rb
10
+ lib/dm-forms/mixins/labels.rb
11
+ lib/dm-forms/mixins/wrappers.rb
12
+ lib/dm-forms/mixins.rb
15
13
  lib/dm-forms/tag.rb
16
14
  lib/dm-forms/version.rb
17
- spec/functional/core_ext_spec.rb
18
- spec/functional/elements_spec.rb
15
+ lib/dm-forms.rb
16
+ Manifest
17
+ Rakefile
18
+ README.rdoc
19
+ spec/functional/helpers_spec.rb
20
+ spec/functional/mixins/descriptions_spec.rb
21
+ spec/functional/mixins/labels_spec.rb
22
+ spec/functional/mixins/wrappers_spec.rb
19
23
  spec/functional/tag_spec.rb
20
24
  spec/integration/datamapper_spec.rb
21
- spec/integration/haml_spec.rb
22
25
  spec/spec_helper.rb
23
26
  tasks/benchmarks.rake
24
27
  tasks/docs.rake
25
28
  tasks/gemspec.rake
26
29
  tasks/spec.rake
27
-
30
+ Todo.rdoc
@@ -3,44 +3,15 @@
3
3
 
4
4
  DataMapper model form generation.
5
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>
6
+ == Features
7
+
8
+ * Coming soon
38
9
 
39
10
  == License:
40
11
 
41
12
  (The MIT License)
42
13
 
43
- Copyright (c) 2008 TJ Holowaychuk
14
+ Copyright (c) 2008-2009 TJ Holowaychuk <tj@vision-media.ca>
44
15
 
45
16
  Permission is hereby granted, free of charge, to any person obtaining
46
17
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -2,15 +2,17 @@
2
2
  require 'rubygems'
3
3
  require 'rake'
4
4
  require 'echoe'
5
- require './lib/dm-forms.rb'
5
+ $:.unshift 'lib'
6
+ require 'dm-forms'
6
7
 
7
- Echoe.new("dm-forms", DataMapper::Form::VERSION::STRING) do |p|
8
+ Echoe.new "dm-forms", DataMapper::Form::VERSION do |p|
8
9
  p.author = "TJ Holowaychuk"
9
10
  p.email = "tj@vision-media.ca"
10
11
  p.summary = "DataMapper model form generation"
11
12
  p.url = "http://github.com/visionmedia/dm-forms"
12
- p.runtime_dependencies = ['dm-core']
13
- p.development_dependencies = ['rspec_hpricot_matchers']
13
+ p.runtime_dependencies << 'dm-core'
14
+ p.runtime_dependencies << 'visionmedia-rext'
15
+ p.development_dependencies << 'rspec_hpricot_matchers'
14
16
  end
15
17
 
16
18
  Dir['tasks/**/*.rake'].sort.each { |lib| load lib }
data/Todo.rdoc CHANGED
@@ -1,24 +1,22 @@
1
1
 
2
2
  == Major:
3
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 ...
4
+ * major refactor with processing of elements since some allow contents param, and some do not etc
5
+ * get rid of origin.buffer..what is merbs technique?
6
+ * select optgroups
7
+ * use stack (array) method for contexts ... cleaner than manual toggling
8
+ * allow explicit context names, not just auto-generated from class names
9
+ * view merb form helper specs to see what was missed
10
+ * abstract ORM ? rename from dm-forms ... have ORM mixins
11
+ * only one context so dont use all those methods? just capture with optional builder base?
12
+ * self closing tags auto self-close to maximize mixin DRYness
13
+ * Make sure othe frameworks can plug in their functionality
14
+ * documentation per method generated
15
+ * better naming conventions / refactor / release
19
16
 
20
17
  == Minor:
21
18
 
19
+ * have these optional as mixins?
22
20
  * Aggregate / faux elements ...
23
21
  * date_field :bday, :format => 'YYYY-MM-DD', :value => '1987-05-25'
24
22
 
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{dm-forms}
5
- s.version = "0.0.2"
5
+ s.version = "0.0.3"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["TJ Holowaychuk"]
9
- s.date = %q{2009-01-15}
9
+ s.date = %q{2009-03-23}
10
10
  s.description = %q{DataMapper model form generation}
11
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"]
12
+ s.extra_rdoc_files = ["lib/dm-forms/base.rb", "lib/dm-forms/core_ext.rb", "lib/dm-forms/helpers.rb", "lib/dm-forms/mixins/descriptions.rb", "lib/dm-forms/mixins/errors.rb", "lib/dm-forms/mixins/labels.rb", "lib/dm-forms/mixins/wrappers.rb", "lib/dm-forms/mixins.rb", "lib/dm-forms/tag.rb", "lib/dm-forms/version.rb", "lib/dm-forms.rb", "README.rdoc", "tasks/benchmarks.rake", "tasks/docs.rake", "tasks/gemspec.rake", "tasks/spec.rake"]
13
+ s.files = ["dm-forms.gemspec", "examples/benchmarks.rb", "examples/login.rb", "History.rdoc", "lib/dm-forms/base.rb", "lib/dm-forms/core_ext.rb", "lib/dm-forms/helpers.rb", "lib/dm-forms/mixins/descriptions.rb", "lib/dm-forms/mixins/errors.rb", "lib/dm-forms/mixins/labels.rb", "lib/dm-forms/mixins/wrappers.rb", "lib/dm-forms/mixins.rb", "lib/dm-forms/tag.rb", "lib/dm-forms/version.rb", "lib/dm-forms.rb", "Manifest", "Rakefile", "README.rdoc", "spec/functional/helpers_spec.rb", "spec/functional/mixins/descriptions_spec.rb", "spec/functional/mixins/labels_spec.rb", "spec/functional/mixins/wrappers_spec.rb", "spec/functional/tag_spec.rb", "spec/integration/datamapper_spec.rb", "spec/spec_helper.rb", "tasks/benchmarks.rake", "tasks/docs.rake", "tasks/gemspec.rake", "tasks/spec.rake", "Todo.rdoc"]
14
14
  s.has_rdoc = true
15
15
  s.homepage = %q{http://github.com/visionmedia/dm-forms}
16
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Dm-forms", "--main", "README.rdoc"]
@@ -25,13 +25,16 @@ Gem::Specification.new do |s|
25
25
 
26
26
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
27
  s.add_runtime_dependency(%q<dm-core>, [">= 0"])
28
+ s.add_runtime_dependency(%q<visionmedia-rext>, [">= 0"])
28
29
  s.add_development_dependency(%q<rspec_hpricot_matchers>, [">= 0"])
29
30
  else
30
31
  s.add_dependency(%q<dm-core>, [">= 0"])
32
+ s.add_dependency(%q<visionmedia-rext>, [">= 0"])
31
33
  s.add_dependency(%q<rspec_hpricot_matchers>, [">= 0"])
32
34
  end
33
35
  else
34
36
  s.add_dependency(%q<dm-core>, [">= 0"])
37
+ s.add_dependency(%q<visionmedia-rext>, [">= 0"])
35
38
  s.add_dependency(%q<rspec_hpricot_matchers>, [">= 0"])
36
39
  end
37
40
  end
@@ -1,118 +1,34 @@
1
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
2
+ require File.dirname(__FILE__) + '/../lib/dm-forms'
9
3
 
10
- #--
11
- # Models
12
- #++
4
+ require 'rubygems'
5
+ require 'rgauge'
13
6
 
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
7
+ include DataMapper::Form::Helpers
48
8
 
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
- }
9
+ def params
10
+ Hash.new { |h, k| k }
73
11
  end
12
+ public :params
74
13
 
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'
14
+ benchmark 'Entire forms', :times => 400 do
15
+ report 'Login' do
16
+ form :action => '/login' do
17
+ textfield :name, :id => 'username'
18
+ textfield :pass, :id => 'password'
19
+ submit :value => 'Login'
85
20
  end
86
- }
21
+ end
87
22
 
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
23
+ DataMapper::Form::Base.send :include, DataMapper::Form::Labels
24
+ DataMapper::Form::Base.send :include, DataMapper::Form::Descriptions
25
+ DataMapper::Form::Base.send :include, DataMapper::Form::Wrappers
26
+
27
+ report 'Login with mixins' do
28
+ form :action => '/login' do
29
+ textfield :name, :id => 'username', :label => 'Username'
30
+ textfield :pass, :id => 'password', :label => 'Password', :description => 'Enter a valid password.'
31
+ submit :value => 'Login'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+
2
+ require File.dirname(__FILE__) + '/../lib/dm-forms'
3
+
4
+ include DataMapper::Form::Helpers
5
+ DataMapper::Form::Base.send :include, DataMapper::Form::Labels
6
+ DataMapper::Form::Base.send :include, DataMapper::Form::Descriptions
7
+ DataMapper::Form::Base.send :include, DataMapper::Form::Wrappers
8
+
9
+ def params
10
+ Hash.new { |h, k| k }
11
+ end
12
+ public :params
13
+
14
+ markup = form :action => '/login' do
15
+ textfield :name, :label => 'Username', :description => 'Enter your username.'
16
+ textfield :pass, :label => 'Password', :description => 'Enter your password.'
17
+ submit 'Login'
18
+ end
19
+
20
+ puts markup
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2008 TJ Holowaychuk
2
+ # Copyright (c) 2008-2009 TJ Holowaychuk <tj@vision-media.ca>
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -21,12 +21,11 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #++
23
23
 
24
- $:.unshift File.dirname(__FILE__)
25
-
26
- require 'rubygems'
27
24
  require 'extlib'
25
+ require 'rext/all'
28
26
  require 'dm-forms/version'
29
27
  require 'dm-forms/core_ext'
30
28
  require 'dm-forms/tag'
31
- require 'dm-forms/elements'
32
- require 'dm-forms/model_elements'
29
+ require 'dm-forms/helpers'
30
+ require 'dm-forms/mixins'
31
+ require 'dm-forms/base'
@@ -0,0 +1,115 @@
1
+
2
+ module DataMapper
3
+ module Form
4
+ class Base
5
+
6
+ include Tag
7
+
8
+ REGULAR_ELEMENTS = :textarea, :select
9
+ SELF_CLOSING_ELEMENTS = :textfield, :submit, :file, :button, :hidden, :password, :radio, :checkbox
10
+ DEFAULT_VALUE_ELEMENTS = :textarea, :select, :textfield, :radio, :checkbox
11
+
12
+ attr_reader :model, :origin, :name
13
+
14
+ def initialize model = nil, origin = nil
15
+ @model, @origin = model, origin
16
+ @name = model.class.name.downcase.split('::').last
17
+ end
18
+
19
+ def form attrs = {}, &block
20
+ origin.clear_buffer
21
+ captured = origin.capture &block
22
+ faux_method = process_form_attrs attrs
23
+ tag :form, faux_method + captured, attrs
24
+ end
25
+
26
+ def fieldset attrs = {}, &block
27
+ legend_tag = (legend = attrs.delete(:legend)) ? tag(:legend, legend) : ''
28
+ origin.buffer << tag(:fieldset, legend_tag + origin.capture(&block), attrs)
29
+ end
30
+
31
+ def select_options options, selected = nil, prompt = nil
32
+ options[''] = prompt unless prompt.nil?
33
+ selected ||= prompt || nil
34
+ options.map do |value, contents|
35
+ tag :option, contents, :value => value, :selected => (value == selected or contents == selected)
36
+ end.join("\n")
37
+ end
38
+
39
+ def unbound_textarea *args
40
+ contents, attrs = split_args *args
41
+ process_unbound_element :textarea, attrs
42
+ origin.buffer << tag(:textarea, element_value(attrs) || contents, attrs)
43
+ end
44
+
45
+ def unbound_select attrs = {}
46
+ options = select_options *attrs.extract!(:options, :selected, :prompt)
47
+ process_unbound_element :select, attrs
48
+ origin.buffer << tag(:select, options, attrs)
49
+ end
50
+
51
+ SELF_CLOSING_ELEMENTS.each do |type|
52
+ define_method :"unbound_#{type}" do |attrs|
53
+ process_unbound_element type, attrs
54
+ origin.buffer << self_closing_tag(:input, attrs)
55
+ end
56
+ end
57
+
58
+ (SELF_CLOSING_ELEMENTS | REGULAR_ELEMENTS).each do |type|
59
+ define_method :"bound_#{type}" do |*args|
60
+ method, attrs = args.shift, args.shift || {}
61
+ process_bound_element type, method, attrs
62
+ send :"unbound_#{type}", attrs
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def split_args *args
69
+ return nil, args.shift if args.first.is_a? Hash
70
+ return *args
71
+ end
72
+
73
+ def process_bound_element type, method, attrs
74
+ attrs[:name] = element_name method
75
+ end
76
+
77
+ def process_unbound_element type, attrs
78
+ attrs ||= {}
79
+ attrs[:type] = type if type.in? SELF_CLOSING_ELEMENTS
80
+ attrs[:value] = element_value attrs if type.in?(DEFAULT_VALUE_ELEMENTS - REGULAR_ELEMENTS)
81
+ case type
82
+ when :file
83
+ @multipart = true
84
+ when :submit
85
+ attrs[:name] ||= 'submit'
86
+ attrs[:value] ||= 'Submit'
87
+ end
88
+ raise ArgumentError, 'form elements must have a name', caller unless attrs.include? :name
89
+ end
90
+
91
+ def process_form_attrs attrs = {}
92
+ attrs[:method] ||= :post
93
+ method = attrs[:method]
94
+ attrs[:method] = :post if attrs[:method] != :get
95
+ attrs[:enctype] = 'multipart/form-data' if @multipart || attrs.delete(:multipart)
96
+ method.in?(:post, :get) ? '' : faux_method(method)
97
+ end
98
+
99
+ def faux_method method
100
+ unbound_hidden :name => '_method', :value => method
101
+ end
102
+
103
+ def element_name method
104
+ model ? "#{name}[#{method}]" : method
105
+ end
106
+
107
+ def element_value attrs = {}
108
+ # TODO: escape HTML
109
+ # TODO: dont use this name hack... better positioning to assign element_name to prevent this
110
+ model ? model.send(attrs[:name].gsub(/\w+\[|\]/, '')) : origin.params[attrs[:name]]
111
+ end
112
+
113
+ end
114
+ end
115
+ end