Class: Forme::Form

Inherits:
Object
  • Object
show all
Defined in:
lib/forme.rb

Overview

The Form class is the main entry point to the library.

Using the form, input, tag, and inputs methods, one can easily build an abstract syntax tree of Tag and Input instances, which can be serialized to a string using to_s.

Direct Known Subclasses

Rails::Form, Sinatra::Form

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(obj = nil, opts = {}) ⇒ Form

Creates a Form object. Arguments:

obj

Sets the obj for the form. If a hash, is merged with the opts argument to set the opts.

opts

A hash of options for the form, see opts attribute for details on available options.



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/forme.rb', line 238

def initialize(obj=nil, opts={})
  if obj.is_a?(Hash)
    @opts = obj.merge(opts)
    @obj = @opts.delete(:obj)
  else
    @obj = obj
    @opts = opts
  end
  if @obj && @obj.respond_to?(:forme_config)
    @obj.forme_config(self)
  end
  config = CONFIGURATIONS[@opts[:config]||Forme.default_config]
  TRANSFORMER_TYPES.each{|k| instance_variable_set(:"@#{k}", transformer(k, @opts.fetch(k, config[k])))}
  @input_defaults = @opts[:input_defaults] || {}
  @hidden_tags = @opts[:hidden_tags]
  @nesting = []
end

Instance Attribute Details

#error_handlerObject (readonly)

The error_handler determines how to to mark tags as containing errors. Must respond to call or be a registered symbol.



163
164
165
# File 'lib/forme.rb', line 163

def error_handler
  @error_handler
end

#formatterObject (readonly)

The formatter determines how the Inputs created are transformed into Tag objects. Must respond to call or be a registered symbol.



159
160
161
# File 'lib/forme.rb', line 159

def formatter
  @formatter
end

#hidden_tagsObject (readonly)

The hidden tags to automatically add to the form. If set, this should be an array, where elements are one of the following types:

String, Array, Forme::Tag

Added directly as a child of the form tag.

Hash

Adds a hidden tag for each entry, with keys as the name of the hidden tag and values as the value of the hidden tag.

Proc

Will be called with the form tag object, and should return an instance of one of the handled types (or nil to not add a tag).



192
193
194
# File 'lib/forme.rb', line 192

def hidden_tags
  @hidden_tags
end

#input_defaultsObject (readonly)

Set the default options for inputs by type. This should be a hash with input type string keys and values that are hashes of input options.



175
176
177
# File 'lib/forme.rb', line 175

def input_defaults
  @input_defaults
end

#inputs_wrapperObject (readonly)

The inputs_wrapper determines how calls to inputs are wrapped. Must respond to call or be a registered symbol.



179
180
181
# File 'lib/forme.rb', line 179

def inputs_wrapper
  @inputs_wrapper
end

#labelerObject (readonly)

The labeler determines how to label tags. Must respond to call or be a registered symbol.



167
168
169
# File 'lib/forme.rb', line 167

def labeler
  @labeler
end

#objObject (readonly)

The object related to the receiver, if any. If the Form has an associated obj, then calls to input are assumed to be accessing fields of the object instead to directly representing input types.



142
143
144
# File 'lib/forme.rb', line 142

def obj
  @obj
end

#optsObject (readonly)

A hash of options for the receiver. Currently, the following are recognized by default:

:obj

Sets the obj attribute

:error_handler

Sets the error_handler for the form

:formatter

Sets the formatter for the form

:hidden_tags

Sets the hidden tags to automatically add to this form.

:input_defaults

Sets the default options for each input type

:inputs_wrapper

Sets the inputs_wrapper for the form

:labeler

Sets the labeler for the form

:wrapper

Sets the wrapper for the form

:serializer

Sets the serializer for the form



155
156
157
# File 'lib/forme.rb', line 155

def opts
  @opts
end

#serializerObject (readonly)

The serializer determines how Tag objects are transformed into strings. Must respond to call or be a registered symbol.



183
184
185
# File 'lib/forme.rb', line 183

def serializer
  @serializer
end

#wrapperObject (readonly)

The wrapper determines how (potentially labeled) tags are wrapped. Must respond to call or be a registered symbol.



171
172
173
# File 'lib/forme.rb', line 171

def wrapper
  @wrapper
end

Class Method Details

.form(obj = nil, attr = {}, opts = {}, &block) ⇒ Object

Create a Form instance and yield it to the block, injecting the opening form tag before yielding and the closing form tag after yielding.

Argument Handling:

No args

Creates a Form object with no options and not associated to an obj, and with no attributes in the opening tag.

1 hash arg

Treated as opening form tag attributes, creating a Form object with no options.

1 non-hash arg

Treated as the Form‘s obj, with empty options and no attributes in the opening tag.

2 hash args

First hash is opening attributes, second hash is Form options.

1 non-hash arg, 1-2 hash args

First argument is Form‘s obj, second is opening attributes, third if provided is Form’s options.



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/forme.rb', line 210

def self.form(obj=nil, attr={}, opts={}, &block)
  f = if obj.is_a?(Hash)
    raise Error, "Can't provide 3 hash arguments to form" unless opts.empty?
    opts = attr
    attr = obj
    new(opts)
  else
    new(obj, opts)
  end

  ins = opts[:inputs]
  button = opts[:button]
  if ins || button
    block = Proc.new do |form|
      form._inputs(ins, opts) if ins
      yield form if block_given?
      form.emit(form.button(button)) if button
    end
  end

  f.form(attr, &block)
end

Instance Method Details

#<<(tag) ⇒ Object

Add the Input/Tag instance given to the currently open tag.



437
438
439
440
441
# File 'lib/forme.rb', line 437

def <<(tag)
  if n = @nesting.last
    n << tag
  end
end

#_input(*a) ⇒ Object

Create a new Input associated with the receiver with the given arguments, doing no other processing.



360
361
362
# File 'lib/forme.rb', line 360

def _input(*a)
  Input.new(self, *a)
end

#_inputs(inputs = [], opts = {}) ⇒ Object

Internals of #inputs, should be used internally by the library, where #inputs is designed for external use.



381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/forme.rb', line 381

def _inputs(inputs=[], opts={})
  if inputs.is_a?(Hash)
    opts = inputs.merge(opts)
    inputs = []
  end
  transform(:inputs_wrapper, opts, self, opts) do
    inputs.each do |i|
      emit(input(*i))
    end
    yield if block_given?
  end
end

#_tag(*a, &block) ⇒ Object

Create a Tag associated to the receiver with the given arguments and block, doing no other processing.



408
409
410
# File 'lib/forme.rb', line 408

def _tag(*a, &block)
  tag = Tag.new(self, *a, &block)
end

#button(opts = {}) ⇒ Object

Creates a :submit Input with the given opts, adding it to the list of children for the currently open tag.



429
430
431
432
433
434
# File 'lib/forme.rb', line 429

def button(opts={})
  opts = {:value=>opts} if opts.is_a?(String)
  input = _input(:submit, opts)
  self << input
  input
end

#closeObject

Returns a string representing the closing of the form tag, for serializers that support closing tags.



402
403
404
# File 'lib/forme.rb', line 402

def close
  serializer.serialize_close(_tag(:form)) if serializer.respond_to?(:serialize_close)
end

#emit(tag) ⇒ Object

Empty method designed to ease integration with other libraries where Forme is used in template code and some output implicitly created by Forme needs to be injected into the template output.



317
318
# File 'lib/forme.rb', line 317

def emit(tag)
end

#form(attr = {}, &block) ⇒ Object

Create a form tag with the given attributes.



305
306
307
# File 'lib/forme.rb', line 305

def form(attr={}, &block)
  tag(:form, attr, method(:hidden_form_tags), &block)
end

#format(input) ⇒ Object

Formats the input using the formatter.



310
311
312
# File 'lib/forme.rb', line 310

def format(input)
  transform(:formatter, input.opts, input)
end

#input(field, opts = {}) ⇒ Object

Creates an Input with the given field and opts associated with the receiver, and add it to the list of children to the currently open tag.

If the form is associated with an obj, or the :obj key exists in the opts argument, treats the field as a call to the obj. If obj responds to forme_input, that method is called with the field and a copy of opts. Otherwise, the field is used as a method call on the obj and a text input is created with the result.

If no obj is associated with the receiver, field represents an input type (e.g. :text, :textarea, :select), and an input is created directly with the field and opts.



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/forme.rb', line 333

def input(field, opts={})
  if opts.has_key?(:obj)
    opts = opts.dup
    obj = opts.delete(:obj)
  else
    obj = self.obj
  end
  input = if obj
    if obj.respond_to?(:forme_input)
      obj.forme_input(self, field, opts.dup)
    else
      opts = opts.dup
      opts[:name] = field unless opts.has_key?(:name)
      opts[:id] = field unless opts.has_key?(:id)
      opts[:value] = obj.send(field) unless opts.has_key?(:value)
      _input(:text, opts)
    end
  else
    _input(field, opts)
  end
  use_serializer(input) if input.is_a?(Array)
  self << input
  input
end

#inputs(*a, &block) ⇒ Object

Creates a tag using the inputs_wrapper (a fieldset by default), calls input on each element of inputs, and yields to if given a block. You can use array arguments if you want inputs to be created with specific options:

inputs([:field1, :field2])
inputs([[:field1, {:name=>'foo'}], :field2])

The given opts are passed to the inputs_wrapper, and the default inputs_wrapper supports a :legend option that is used to set the legend for the fieldset.



375
376
377
# File 'lib/forme.rb', line 375

def inputs(*a, &block)
  _inputs(*a, &block)
end

#open(attr) ⇒ Object

Returns a string representing the opening of the form tag for serializers that support opening tags.



396
397
398
# File 'lib/forme.rb', line 396

def open(attr)
  serializer.serialize_open(_tag(:form, attr)) if serializer.respond_to?(:serialize_open)
end

#serialize(tag) ⇒ Object

Serializes the tag using the serializer.



444
445
446
# File 'lib/forme.rb', line 444

def serialize(tag)
  serializer.call(tag)
end

#tag(*a, &block) ⇒ Object

Creates a Tag associated to the receiver with the given arguments. Add the tag to the the list of children for the currently open tag. If a block is given, make this tag the currently open tag while inside the block.



416
417
418
419
420
421
# File 'lib/forme.rb', line 416

def tag(*a, &block)
  tag = _tag(*a)
  self << tag
  nest(tag, &block) if block
  tag
end

#tag_(*a, &block) ⇒ Object



423
424
425
# File 'lib/forme.rb', line 423

def tag_(*a, &block)
  tag(*a, &block)
end

#transform(type, trans_name, *args, &block) ⇒ Object

If there is a related transformer, call it with the given args and block. Otherwise, attempt to return the initial input without modifying it.



258
259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/forme.rb', line 258

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, :wrapper
      args.first
    else
      raise Error, "No matching #{type}: #{trans_name.inspect}"
    end
  end
end

#transformer(type, trans) ⇒ Object

Get the related transformer for the given transformer type. Output depends on the type of trans:

Symbol

Assume a request for a registered transformer, so look it up in the TRANSFORRMERS hash.

Hash

If type is also a key in trans, return the related value from trans, unless the related value is nil, in which case, return nil. If type is not a key in trans, use the default transformer for the receiver.

nil

Assume the default transformer for this receiver.

otherwise

return trans directly if it responds to call, and raise an Error if not.



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/forme.rb', line 281

def transformer(type, trans)
  case trans
  when Symbol
    TRANSFORMERS[type][trans] || raise(Error, "invalid #{type}: #{trans.inspect} (valid #{type}s: #{TRANSFORMERS[type].keys.map{|k| k.inspect}.join(', ')})")
  when Hash
    if trans.has_key?(type)
      if v = trans[type]
        transformer(type, v)
      end
    else
      transformer(type, nil)
    end
  when nil
    send(type)
  else
    if trans.respond_to?(:call)
      trans
    else
      raise Error, "#{type} #{trans.inspect} must respond to #call"
    end
  end
end