Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Make error_handler, labeler, and wrapper take tag and input

This unifies handling of the error_handler, labeler, and wrapper
transformers so they call take the same types of arguments. This
removes the need to scan TagArrays looking for tags which was
previously done by some of the transformers.  It will enable the
ability for those transformers to modify the transformations
based on the initial input, which previously was not possible.

Remove TagArray#tag and add Input#tag, and have all of the
transformers call #tag on the input argument instead of the
tag argument.

Add Input#merge_opts for merging new options into the input's
opts without modifying the input's opts hash.  This is necessary
for cases where the formatter needs to modify the Input's opts
in order for correct behavior of another transformer, currently
used by the date to multiple select options code.

Only turn arrays of tags into TagArrays at the very end of the
formatter, since the other transformers should no longer care.
TagArray is still needed, but hopefully it can go away in the
future.
  • Loading branch information...
commit 3a1a748c0005cc1f9ecac7bfbc2a321033db1a22 1 parent a4fa65e
@jeremyevans authored
View
16 README.rdoc
@@ -91,16 +91,16 @@ API, so you can use a +Proc+ for any custom transformer.
+serializer+ :: tags input/tag, returns string
+formatter+ :: takes input, returns tag
-+error_handler+ :: takes error message and tag, returns version with errors noted
-+labeler+ :: takes label and tag, returns labeled version
-+wrapper+ :: takes tag, returns wrapped version
-+inputs_wrapper+ :: takes form, options, and block, wrapping block in a tag
++error_handler+ :: takes tag and input, returns version of tag with errors noted
++labeler+ :: takes tag and input, returns labeled version of tag
++wrapper+ :: takes tag and input, returns wrapped version of tag
++inputs_wrapper+ :: takes form, options hash, and block, wrapping block in a tag
-The +serializer+ is the base of the library. It turns +Tag+ into strings. If it comes across
+The +serializer+ is the base of the transformations. It turns +Tag+ instances into strings. If it comes across
an +Input+, it calls the +formatter+ on the +Input+ to turn it into a +Tag+, and then serializes
-that +Tag+. The +formatter+ first converts the +Input+ to a +Tag+, and calls the
-+error_handler+ and +labeler+ if necessary with the +Tag+ to add the error handling and
-labeling. Finally, it calls the +wrapper+ to wrap the resulting tag before returning it.
+that +Tag+. The +formatter+ first converts the +Input+ to a +Tag+, and then calls the
++error_handler+ if the <tt>:error</tt> option is set and the +labeler+ if the <tt>:label</tt>
+option is set. Finally, it calls the +wrapper+ to wrap the resulting tag before returning it.
The +inputs_wrapper+ is called by <tt>Forme::Form#inputs</tt> and serves to wrap a bunch
of related inputs.
View
78 lib/forme.rb
@@ -226,17 +226,17 @@ def initialize(obj=nil, opts={})
# If there is a related transformer, call it with the given +args+ and +block+.
# Otherwise, attempt to return the initial input without modifying it.
- def transform(type, trans, *args, &block)
- if trans = transformer(type, trans)
+ def transform(type, trans_name, *args, &block)
+ if trans = transformer(type, trans_name)
trans.call(*args, &block)
else
case type
when :inputs_wrapper
yield
- when :labeler, :error_handler
- args[1]
+ when :labeler, :error_handler, :wrapper
+ args.first
else
- args[0]
+ raise Error, "No matching #{type}: #{trans_name.inspect}"
end
end
end
@@ -431,6 +431,18 @@ def initialize(form, type, opts={})
@form, @type, @opts = form, type, opts
end
+ # Replace the +opts+ by merging the given +hash+ into +opts+,
+ # without modifying +opts+.
+ def merge_opts(hash)
+ @opts = @opts.merge(hash)
+ end
+
+ # Create a new +Tag+ instance with the given arguments and block
+ # related to the receiver's +form+.
+ def tag(*a, &block)
+ form._tag(*a, &block)
+ end
+
# Return a string containing the serialized content of the receiver.
def to_s
form.serialize(self)
@@ -508,12 +520,6 @@ def self.new(form, contents)
a
end
- # Create a new +Tag+ instance with the given arguments and block
- # related to the receiver's +form+.
- def tag(*a, &block)
- form._tag(*a, &block)
- end
-
# Return a string containing the serialized content of the receiver.
def to_s
form.serialize(self)
@@ -580,15 +586,9 @@ def call(input)
@opts = {}
normalize_options
- tag = handle_array(convert_to_tag(input.type))
-
- if error = @opts[:error]
- tag = handle_array(wrap_tag_with_error(error, tag))
- end
- if label = @opts[:label]
- tag = handle_array(wrap_tag_with_label(label, tag))
- end
-
+ tag = convert_to_tag(input.type)
+ tag = wrap_tag_with_error(tag) if input.opts[:error]
+ tag = wrap_tag_with_label(tag) if input.opts[:label]
handle_array(wrap_tag(tag))
end
@@ -649,6 +649,7 @@ def format_date
values[:year], values[:month], values[:day] = v.year, v.month, v.day
end
ops = {:year=>1900..2050, :month=>1..12, :day=>1..31}
+ input.merge_opts(:label_id=>"#{id}_year")
[:year, :month, :day].map{|x| form._input(:select, @attr.dup.merge(:label=>nil, :wrapper=>nil, :error=>nil, :name=>"#{name}[#{x}]", :id=>"#{id}_#{x}", :value=>values[x], :options=>ops[x].to_a.map{|x| [x, x]})).format}
else
_format_input(:date)
@@ -767,17 +768,17 @@ def tag(type, attr=@attr, children=nil)
# Wrap the tag with the form's +wrapper+.
def wrap_tag(tag)
- form.transform(:wrapper, @opts, tag)
+ form.transform(:wrapper, @opts, tag, input)
end
# Wrap the tag with the form's +error_handler+.
- def wrap_tag_with_error(error, tag)
- form.transform(:error_handler, @opts, error, tag)
+ def wrap_tag_with_error(tag)
+ form.transform(:error_handler, @opts, tag, input)
end
# Wrap the tag with the form's +labeler+.
- def wrap_tag_with_label(label, tag)
- form.transform(:labeler, @opts, label, tag)
+ def wrap_tag_with_label(tag)
+ form.transform(:labeler, @opts, tag, input)
end
end
@@ -850,8 +851,8 @@ class ErrorHandler
Forme.register_transformer(:error_handler, :default, new)
# Return a label tag wrapping the given tag.
- def call(err_msg, tag)
- msg_tag = tag.tag(:span, {:class=>'error_message'}, err_msg)
+ def call(tag, input)
+ msg_tag = tag.tag(:span, {:class=>'error_message'}, input.opts[:error])
if tag.is_a?(Tag)
attr = tag.attr
if attr[:class]
@@ -874,13 +875,14 @@ class Labeler
# Return a label tag wrapping the given tag. For radio and checkbox
# inputs, the label occurs directly after the tag, for all other types,
# the label occurs before the tag.
- def call(label, tag)
- t = if tag.is_a?(Tag) && tag.type == :input && [:radio, :checkbox].include?(tag.attr[:type])
+ def call(tag, input)
+ label = input.opts[:label]
+ t = if [:radio, :checkbox].include?(input.type)
[tag, " #{label}"]
else
["#{label}: ", tag]
end
- tag.tag(:label, {}, t)
+ input.tag(:label, {}, t)
end
end
@@ -896,21 +898,19 @@ class Labeler::Explicit
# a second entry. If +tag+ is an array, scan it for the first +Tag+
# instance that isn't hidden (since hidden tags shouldn't have labels).
# If the +tag+ doesnt' have an id attribute, an +Error+ is raised.
- def call(label, tag)
- t = tag.is_a?(Tag) ? tag : tag.find{|tg| tg.is_a?(Tag) && tg.attr[:type] != :hidden}
- id = t.attr[:id]
- raise Error, "Explicit labels require an id field" unless id
- [tag.tag(:label, {:for=>id}, [label]), tag]
+ def call(tag, input)
+ raise Error, "Explicit labels require an id field" unless id = input.opts.fetch(:label_id, input.opts[:id])
+ [input.tag(:label, {:for=>id}, [input.opts[:label]]), tag]
end
end
- Forme.register_transformer(:wrapper, :default){|tag| tag}
+ Forme.register_transformer(:wrapper, :default){|tag, input| tag}
[:li, :p, :div, :span].each do |x|
- Forme.register_transformer(:wrapper, x){|tag| tag.tag(x, {}, Array(tag))}
+ Forme.register_transformer(:wrapper, x){|tag, input| input.tag(x, {}, Array(tag))}
end
- Forme.register_transformer(:wrapper, :trtd) do |tag|
+ Forme.register_transformer(:wrapper, :trtd) do |tag, input|
a = Array(tag)
- tag.tag(:tr, {}, a.length == 1 ? tag.tag(:td, {}, a) : [tag.tag(:td, {}, [a.first]), tag.tag(:td, {}, a[1..-1])])
+ input.tag(:tr, {}, a.length == 1 ? input.tag(:td, {}, a) : [input.tag(:td, {}, [a.first]), input.tag(:td, {}, a[1..-1])])
end
# Default inputs_wrapper used by the library, uses a fieldset.
View
4 lib/sequel/plugins/forme.rb
@@ -226,7 +226,7 @@ def association_many_to_one(ref)
opts.delete(:wrapper)
radios = opts.delete(:options).map{|l, pk| _input(:radio, opts.merge(:value=>pk, :wrapper=>tag_wrapper, :label=>l, :checked=>(pk == val)))}
radios.unshift("#{label}: ")
- wrapper ? wrapper.call(TagArray.new(form, radios)) : radios
+ wrapper ? wrapper.call(TagArray.new(form, radios), _input(:radio, opts)) : radios
else
opts[:id] = form.namespaced_id(key) unless opts.has_key?(:id)
opts[:add_blank] = true if !opts.has_key?(:add_blank) && (sch = obj.model.db_schema[key]) && sch[:allow_null]
@@ -255,7 +255,7 @@ def association_one_to_many(ref)
opts.delete(:wrapper)
cbs = opts.delete(:options).map{|l, pk| _input(:checkbox, opts.merge(:value=>pk, :wrapper=>tag_wrapper, :label=>l, :checked=>val.include?(pk), :no_hidden=>true))}
cbs.unshift("#{label}: ")
- wrapper ? wrapper.call(TagArray.new(form, cbs)) : cbs
+ wrapper ? wrapper.call(TagArray.new(form, cbs), _input(:checkbox, opts)) : cbs
else
opts[:id] = form.namespaced_id(field) unless opts.has_key?(:id)
opts[:multiple] = true
View
14 spec/forme_spec.rb
@@ -221,7 +221,7 @@
end
specify "inputs should accept a :error_handler option to use a custom error_handler" do
- @f.input(:textarea, :error_handler=>proc{|e, t| [t, "!!! #{e}"]}, :error=>'bar', :id=>:foo).to_s.should == '<textarea id="foo"></textarea>!!! bar'
+ @f.input(:textarea, :error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}, :error=>'bar', :id=>:foo).to_s.should == '<textarea id="foo"></textarea>!!! bar'
end
specify "#inputs should accept a :inputs_wrapper option to use a custom inputs_wrapper" do
@@ -260,15 +260,15 @@
end
specify "labelers can be specified as a proc" do
- Forme::Form.new(:labeler=>proc{|l, t| ["#{l}: ", t]}).input(:textarea, :NAME=>'foo', :label=>'bar').to_s.should == 'bar: <textarea NAME="foo"></textarea>'
+ Forme::Form.new(:labeler=>proc{|t, i| ["#{i.opts[:label]}: ", t]}).input(:textarea, :NAME=>'foo', :label=>'bar').to_s.should == 'bar: <textarea NAME="foo"></textarea>'
end
specify "error_handlers can be specified as a proc" do
- Forme::Form.new(:error_handler=>proc{|e, t| [t, "!!! #{e}"]}).input(:textarea, :NAME=>'foo', :error=>'bar').to_s.should == '<textarea NAME="foo"></textarea>!!! bar'
+ Forme::Form.new(:error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}).input(:textarea, :NAME=>'foo', :error=>'bar').to_s.should == '<textarea NAME="foo"></textarea>!!! bar'
end
specify "wrappers can be specified as a proc" do
- Forme::Form.new(:wrapper=>proc{|t| t.tag(:div, {}, t)}).input(:textarea, :NAME=>'foo').to_s.should == '<div><textarea NAME="foo"></textarea></div>'
+ Forme::Form.new(:wrapper=>proc{|t, i| t.tag(:div, {:bar=>i.opts[:NAME]}, t)}).input(:textarea, :NAME=>'foo').to_s.should == '<div bar="foo"><textarea NAME="foo"></textarea></div>'
end
specify "inputs_wrappers can be specified as a proc" do
@@ -375,18 +375,18 @@
describe "Forme registering custom transformers" do
specify "should have #register_transformer register a transformer object for later use" do
- Forme.register_transformer(:wrapper, :div, proc{|t| t.tag(:div, {}, [t])})
+ Forme.register_transformer(:wrapper, :div, proc{|t, i| t.tag(:div, {}, [t])})
Forme::Form.new(:wrapper=>:div).input(:textarea).to_s.should == '<div><textarea></textarea></div>'
end
specify "should have #register_transformer register a transformer block for later use" do
- Forme.register_transformer(:wrapper, :div1){|t| t.tag(:div1, {}, [t])}
+ Forme.register_transformer(:wrapper, :div1){|t, i| t.tag(:div1, {}, [t])}
Forme::Form.new(:wrapper=>:div1).input(:textarea).to_s.should == '<div1><textarea></textarea></div1>'
end
specify "should have #register_transformer raise an error if given a block and an object" do
proc do
- Forme.register_transformer(:wrapper, :div1, proc{|t| t}){|t| t.tag(:div1, {}, [t])}
+ Forme.register_transformer(:wrapper, :div1, proc{|t, i| t}){|t| t.tag(:div1, {}, [t])}
end.should raise_error(Forme::Error)
end
end
View
8 spec/sequel_plugin_spec.rb
@@ -118,8 +118,8 @@ class AlbumInfo < Sequel::Model; end
end
specify "should use a checkbox for dual-valued boolean fields" do
- @b.input(:platinum).to_s.should == '<label>Platinum: <input id="album_platinum_hidden" name="album[platinum]" type="hidden" value="f"/><input id="album_platinum" name="album[platinum]" type="checkbox" value="t"/></label>'
- @c.input(:platinum).to_s.should == '<label>Platinum: <input id="album_platinum_hidden" name="album[platinum]" type="hidden" value="f"/><input checked="checked" id="album_platinum" name="album[platinum]" type="checkbox" value="t"/></label>'
+ @b.input(:platinum).to_s.should == '<label><input id="album_platinum_hidden" name="album[platinum]" type="hidden" value="f"/><input id="album_platinum" name="album[platinum]" type="checkbox" value="t"/> Platinum</label>'
+ @c.input(:platinum).to_s.should == '<label><input id="album_platinum_hidden" name="album[platinum]" type="hidden" value="f"/><input checked="checked" id="album_platinum" name="album[platinum]" type="checkbox" value="t"/> Platinum</label>'
end
specify "should use a select box for many_to_one associations" do
@@ -139,7 +139,7 @@ class AlbumInfo < Sequel::Model; end
specify "should support custom wrappers for many_to_one associations with :as=>:radio via :tag_wrapper option" do
@b = Forme::Form.new(@ab, :wrapper=>:li)
- @b.input(:artist, :as=>:radio, :wrapper=>proc{|t| t.tag(:div, {}, [t])}, :tag_wrapper=>proc{|t| t.tag(:span, {}, [t])}).to_s.should == '<div>Artist: <span><label><input checked="checked" name="album[artist_id]" type="radio" value="1"/> a</label></span><span><label><input name="album[artist_id]" type="radio" value="2"/> d</label></span></div>'
+ @b.input(:artist, :as=>:radio, :wrapper=>proc{|t, i| i.tag(:div, {}, [t])}, :tag_wrapper=>proc{|t, i| i.tag(:span, {}, [t])}).to_s.should == '<div>Artist: <span><label><input checked="checked" name="album[artist_id]" type="radio" value="1"/> a</label></span><span><label><input name="album[artist_id]" type="radio" value="2"/> d</label></span></div>'
end
specify "should respect an :options entry" do
@@ -207,7 +207,7 @@ class AlbumInfo < Sequel::Model; end
specify "should support custom wrappers for one_to_many associations with :as=>:checkbox via :tag_wrapper option" do
@b = Forme::Form.new(@ab, :wrapper=>:li)
- @b.input(:tracks, :as=>:checkbox, :wrapper=>proc{|t| t.tag(:div, {}, [t])}, :tag_wrapper=>proc{|t| t.tag(:span, {}, [t])}).to_s.should == '<div>Tracks: <span><label><input checked="checked" name="album[track_pks][]" type="checkbox" value="1"/> m</label></span><span><label><input checked="checked" name="album[track_pks][]" type="checkbox" value="2"/> n</label></span><span><label><input name="album[track_pks][]" type="checkbox" value="3"/> o</label></span></div>'
+ @b.input(:tracks, :as=>:checkbox, :wrapper=>proc{|t, i| i.tag(:div, {}, [t])}, :tag_wrapper=>proc{|t, i| i.tag(:span, {}, [t])}).to_s.should == '<div>Tracks: <span><label><input checked="checked" name="album[track_pks][]" type="checkbox" value="1"/> m</label></span><span><label><input checked="checked" name="album[track_pks][]" type="checkbox" value="2"/> n</label></span><span><label><input name="album[track_pks][]" type="checkbox" value="3"/> o</label></span></div>'
end
specify "should use a text field methods not backed by columns" do
Please sign in to comment.
Something went wrong with that request. Please try again.