visionmedia-dm-forms 0.0.2 → 0.0.3

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