Skip to content

Commit

Permalink
Replace TagArray class with Serialized module
Browse files Browse the repository at this point in the history
Instead of wrapping all arrays in TagArray instances, just
extend the arrays with a module that overrides to_s to use
the form's serializer.

This is currently only needed if Form#input can return an array of
inputs, which the Sequel plugin does for the associations as
multiple radios/checkboxes option.
  • Loading branch information
jeremyevans committed Jun 17, 2011
1 parent 3a1a748 commit bb5d3ef
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 33 deletions.
1 change: 0 additions & 1 deletion README.rdoc
Expand Up @@ -80,7 +80,6 @@ strings. Here are the main classes used by the library:
<tt>Forme::Form</tt> :: main object
<tt>Forme::Input</tt> :: high level abstract tag (a single +Input+ could represent a select box with a bunch of options)
<tt>Forme::Tag</tt> :: low level abstract tag representing an html tag (there would be a separate +Tag+ for each option in a select box)
<tt>Forme::TagArray</tt> :: array of Tags, Inputs, and Strings

The group of objects that perform the transformations to
the abstract syntax trees are known as transformers.
Expand Down
55 changes: 25 additions & 30 deletions lib/forme.rb
Expand Up @@ -321,6 +321,7 @@ def input(field, opts={})
else
_input(field, opts)
end
use_serializer(input) if input.is_a?(Array)
self << input
input
end
Expand Down Expand Up @@ -403,6 +404,15 @@ def serialize(tag)

private

# Extend +obj+ with +Serialized+ and associate it with the receiver, such
# that calling +to_s+ on the object will use the receiver's serializer
# to generate the resulting string.
def use_serializer(obj)
obj.extend(Serialized)
obj._form = self
obj
end

# Make the given tag the currently open tag, and yield. After the
# block returns, make the previously open tag the currently open
# tag.
Expand All @@ -415,7 +425,7 @@ def nest(tag)
end

# High level abstract tag form, transformed by formatters into the lower
# level +Tag+ form (or a +TagArray+ of them).
# level +Tag+ form (or an array of them).
class Input
# The +Form+ object related to the receiver.
attr_reader :form
Expand Down Expand Up @@ -448,7 +458,7 @@ def to_s
form.serialize(self)
end

# Transform the receiver into a lower level +Tag+ form (or a +TagArray+
# Transform the receiver into a lower level +Tag+ form (or an array
# of them).
def format
form.format(self)
Expand All @@ -467,21 +477,19 @@ class Tag
# The attributes hash of this receiver.
attr_reader :attr

# A +TagArray+ instance representing the children of the receiver,
# An array instance representing the children of the receiver,
# or possibly +nil+ if the receiver has no children.
attr_reader :children

# Set the +form+, +type+, +attr+, and +children+.
def initialize(form, type, attr={}, children=nil)
case children
when TagArray
@children = children
when Array
@children = TagArray.new(form, children)
@children = children
when nil
@children = nil
else
@children = TagArray.new(form, [children])
@children = [children]
end
@form, @type, @attr = form, type, attr
end
Expand All @@ -491,7 +499,7 @@ def <<(child)
if children
children << child
else
@children = TagArray.new(form, [child])
@children = [child]
end
end

Expand All @@ -507,22 +515,16 @@ def to_s
end
end

# Array subclass related to a specific +Form+ instance.
class TagArray < Array
# Module that can extend objects associating them with a specific
# +Form+ instance. Calling +to_s+ on the object will then use the
# form's serializer to return a string.
module Serialized
# The +Form+ instance related to the receiver.
attr_accessor :form

# Create a new instance using +contents+, associated to
# the given +form+.
def self.new(form, contents)
a = super(contents)
a.form = form
a
end
attr_accessor :_form

# Return a string containing the serialized content of the receiver.
def to_s
form.serialize(self)
_form.serialize(self)
end
end

Expand Down Expand Up @@ -551,7 +553,7 @@ def self.call(input)
attr_reader :form

# The +Input+ instance for the receiver. This is what the receiver
# converts to the lower level +Tag+ form (or a +TagArray+ of them).
# converts to the lower level +Tag+ form (or an array of them).
attr_reader :input

# The attributes to to set on the lower level +Tag+ form returned.
Expand All @@ -575,7 +577,7 @@ def self.call(input)
CHECKBOX_MAP = Hash.new(0)
CHECKBOX_MAP['t'] = 'f'

# Transform the +input+ into a +Tag+ instance (or +TagArray+ of them),
# Transform the +input+ into a +Tag+ instance (or an array of them),
# wrapping it with the +form+'s wrapper, and the form's +error_handler+
# and +labeler+ if the +input+ has an <tt>:error</tt> or <tt>:label</tt>
# options.
Expand All @@ -589,7 +591,7 @@ def call(input)
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))
wrap_tag(tag)
end

private
Expand Down Expand Up @@ -737,13 +739,6 @@ def format_textarea
end
end

# If +tag+ is an +Array+ and not a +TagArray+, turn it into
# a +TagArray+ related to the receiver's +form+. Otherwise,
# return +tag+.
def handle_array(tag)
(tag.is_a?(Array) && !tag.is_a?(TagArray)) ? TagArray.new(form, tag) : tag
end

# Normalize the options used for all input types. Handles:
# :required :: Sets the +required+ attribute on the resulting tag if true.
# :disabled :: Sets the +disabled+ attribute on the resulting tag if true.
Expand Down
4 changes: 2 additions & 2 deletions lib/sequel/plugins/forme.rb
Expand Up @@ -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), _input(:radio, opts)) : radios
wrapper ? wrapper.call(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]
Expand Down Expand Up @@ -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), _input(:checkbox, opts)) : cbs
wrapper ? wrapper.call(cbs, _input(:checkbox, opts)) : cbs
else
opts[:id] = form.namespaced_id(field) unless opts.has_key?(:id)
opts[:multiple] = true
Expand Down

0 comments on commit bb5d3ef

Please sign in to comment.