Class: Demeler

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

Constant Summary collapse

MethodsLikeInputText =

These calls are effectively generated in the same way as ‘text’ input tags. Method_missing just does a substitution to implement them.

[:button, :color, :date, :datetime_local, :email, \
:hidden, :image, :month, :number, :password, :range, :reset, :search, \
:submit, :tel, :text, :time, :url, :week]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(obj = nil, usr = nil, &block) ⇒ Demeler

Demeler.new builds HTML from Ruby code. You can either access #out, .to_s or .to_html to return the actual markup.

A note of warning: you’ll get extra spaces in textareas if you use .to_html.

To use this without Sequel, you can use an object like this: class Obj<Hash

attr_accessor :errors
def initialize
  @errors = {}
end

end

Parameters:

  • obj--a (object)

    Sequel::Model object, or Hash object with an added ‘errors’ field.

  • usr (*) (defaults to: nil)

    The usr variable from the caller; it can be anything because Demeler doesn’t use it.

  • block (Proc)

Raises:

  • (ArgumentError)


67
68
69
70
71
72
73
# File 'lib/demeler.rb', line 67

def initialize(obj=nil, usr=nil, &block)
  raise ArgumentError.new("The object passed to Demeler must have an errors field containing a Hash") if obj && !defined?(obj.errors)
  @obj = obj
  @usr = usr
  clear
  instance_eval(&block) if block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object

Catch tag calls that have not been pre-defined.

Parameters:

  • meth (String)

    The method that was called.

  • args (Hash)

    Additional arguments passed to the called method.

  • block. (Proc)


92
93
94
95
96
97
98
99
100
101
# File 'lib/demeler.rb', line 92

def method_missing(meth, *args, &block)
  # This code allows for some input tags that work like <input type="text" ...> to
  # work--for example g.password works in place of g.input(:type=>:password, ...)
  if MethodsLikeInputText.index(meth) # TODO!
    args << {} if !args[-1].kind_of?(Hash)
    args.last[:type] = meth
    meth = :input
  end
  tag_generator(meth, args, &block)
end

Instance Attribute Details

#objObject (readonly)

Returns the value of attribute obj.



26
27
28
# File 'lib/demeler.rb', line 26

def obj
  @obj
end

#outObject (readonly)

Returns the value of attribute out.



26
27
28
# File 'lib/demeler.rb', line 26

def out
  @out
end

#usrObject (readonly)

Returns the value of attribute usr.



26
27
28
# File 'lib/demeler.rb', line 26

def usr
  @usr
end

Class Method Details

.build(obj = nil, gen_html = false, usr = nil, &block) ⇒ Object

The default way to start building your markup. Takes a block and returns the markup.

Parameters:

  • obj (object) (defaults to: nil)

    a Sequel::Model object, or Hash object with an added ‘errors’ field.

  • gen_html (boolean) (defaults to: false)

    A flag to control final output: true=>formatted, false=>compressed.

  • usr (*) (defaults to: nil)

    The usr variable from the caller, although it can be anything because Demeler doesn’t use it.

  • block (Proc)


43
44
45
46
# File 'lib/demeler.rb', line 43

def self.build(obj=nil, gen_html=false, usr=nil, &block)
  demeler = self.new(obj, usr, &block)
  if gen_html then demeler.to_html else demeler.to_s end
end

Instance Method Details

The #alink method simplyfies the generation of <a>…</a> tags.

Parameters:

  • The (String)

    link line to be displayed

  • args (Array) (defaults to: {})

    Extra arguments that should be processed before creating the ‘a’ tag.

  • block (Proc)

Raises:

  • (ArgumentError)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/demeler.rb', line 122

def alink(text, args={}, parms={})Hash
  raise ArgumentError.new("In Demeler#alink, expected String for argument 1, text") if !text.kind_of?(String)
  raise ArgumentError.new("In Demeler#alink, expected Hash for argument 2, opts") if !args.kind_of?(Hash)
  raise ArgumentError.new("In Demeler#alink, expected an href option in opts") if !args[:href]

  # I had to use class.name because the Ruby case doesn't work
  # properly with comparing value.class with Symbol or String.
  # It was necessary to compare value.class.name with "Symbol" and "String".
  opts = args.clone
  value = opts.delete(:href)
  case value.class.name
  when "Symbol"
    # convert to string
    href = value.to_s
  when "String"
    # clone string
    href = String.new(value)
  else
    href = value
  end

  if !parms.empty?
    href << '?'
    parms.each do |k,v|
      href << k.to_s
      href << '='
      href << CGI::escape(v.to_s)
      href << '&'
    end
  else
    href << '&' # will be removed
  end
  opts[:href] = href[0..-2] # remove last '&'
  opts[:text] = text
  tag_generator(:a, opts)
end

#checkbox(name, opts, values) ⇒ Object

The checkbox shortcut

@note: first argument becomes the :name plus a number starting at 1, i.e., “vehicle1”, etc.

Examples:

g.checkbox(:vehicle, opts, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")

Parameters:

  • name (Symbol)

    Base Name of the control (numbers 1..n will be added)

  • opts (Hash)

    Attributes for the control

  • value=>nomenclature (Hash)

    pairs

Raises:

  • (ArgumentError)


173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/demeler.rb', line 173

def checkbox(name, opts, values)
  raise ArgumentError.new("In Demeler#checkbox, expected Symbol for argument 1, name") if !name.kind_of?(Symbol)
  raise ArgumentError.new("In Demeler#checkbox, expected Hash for argument 2, opts") if !opts.kind_of?(Hash)
  raise ArgumentError.new("In Demeler#checkbox, expected Hash for argument 3, values") if !values.kind_of?(Hash)
  n = 0
  data = case
  when @obj
    @obj[name]
  when (default = opts.delete(:default))
    default.to_s
  else
    nil
  end
  case data.class.name
  when "String"
    data = data.split(",")
  when "Array"
    # it's alreay in the form we want
  when "Hash"
    data = data.values
  else
    data = nil
  end
  values.each do |value,nomenclature|
    sets = opts.clone
    sets[:name] = "#{name}[#{n+=1}]".to_sym
    sets[:type] = :checkbox
    sets[:value] = value
    sets[:text] = nomenclature
    sets[:checked] = 'true' if data && data.index(value.to_s)
    tag_generator(:input, sets)
  end
end

#clearObject

Clear out the data in order to start over with the same Demeler obj



78
79
80
81
82
83
# File 'lib/demeler.rb', line 78

def clear
  @level = 0
  @out = []
  @labels = []
  self
end

#p(*args, &block) ⇒ Object

Workaround for Kernel#p to make <p /> tags possible.

Parameters:

  • args (Hash)

    Extra arguments that should be processed before creating the paragraph tag.

  • block (Proc)


110
111
112
# File 'lib/demeler.rb', line 110

def p(*args, &block)
  tag_generator(:p, args, &block)
end

#radio(name, opts, values) ⇒ Object

The radio shortcut

@note: first argument is the :name; without the name, the radio control won’t work

Examples:

g.radio(:vehicle, {}, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")

Parameters:

  • name (Symbol)

    Base Name of the control (numbers 1..n will be added)

  • opts (Hash)

    Attributes for the control

  • value=>nomenclature (Hash)

    pairs

Raises:

  • (ArgumentError)


219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/demeler.rb', line 219

def radio(name, opts, values)
  raise ArgumentError.new("In Demeler#radio, expected Symbol for argument 1, name") if !name.kind_of?(Symbol)
  raise ArgumentError.new("In Demeler#radio, expected Hash for argument 2, opts") if !opts.kind_of?(Hash)
  raise ArgumentError.new("In Demeler#radio, expected Hash for argument 3, values") if !values.kind_of?(Hash)
  data = case
  when @obj
    @obj[name]
  when (default = opts.delete(:default))
    default.to_s
  else
    nil
  end
  values.each do |value,nomenclature|
    sets = opts.clone
    sets[:name] = "#{name}".to_sym
    sets[:type] = :radio
    sets[:value] = value
    sets[:text] = nomenclature
    sets[:checked] = 'true' if data==value.to_s
    tag_generator(:input, sets)
  end
end

#reset(text, opts = {}) ⇒ Object

The reset shortcut

Examples:

g.reset("Reset", {})

Parameters:

  • text (String)

    The text which the button will display

  • opts (Hash) (defaults to: {})

    Options for the RESET statement



251
252
253
254
255
256
# File 'lib/demeler.rb', line 251

def reset(text, opts={})
  attr = {:type=>:reset}
  attr[:value] = text
  attr.merge!(opts)
  tag_generator(:input, attr)
end

#select(name, args, values) ⇒ Object

The select (select_tag) shortcut

@note: first argument is the :name=>“vehicle” @note: the second argument is a Hash or nil

Examples:

g.select(:vehicle, {}, :volvo=>"Volvo", :saab=>"Saab", :mercedes=>"Mercedes", :audi=>"Audi")

Parameters:

  • name (Symbol)

    The name of the SELECT statement

  • opts (Hash)

    Options for the SELECT statement

  • values (Hash)

    A list of :name=>value pairs the control will have

Raises:

  • (ArgumentError)


271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/demeler.rb', line 271

def select(name, args, values)
  raise ArgumentError.new("In Demeler#select, expected Symbol for argument 1, name") if !name.kind_of?(Symbol)
  raise ArgumentError.new("In Demeler#select, expected Hash for argument 2, args") if !args.kind_of?(Hash)
  raise ArgumentError.new("In Demeler#select, expected Hash for argument 3, values") if !values.kind_of?(Hash)
  opts = {:name=>name}.merge(args)
  data = case
  when @obj
    @obj[name]
  when (default = opts.delete(:default))
    default.to_s
  else
    nil
  end
  tag_generator(:select, opts) do
    values.each do |value,nomenclature|
      sets = {:value=>value}
      sets[:selected] = 'true' if data.to_s==value.to_s
      sets[:text] = nomenclature
      tag_generator(:option, [sets])
    end
  end
end

#submit(text, opts = {}) ⇒ Object

The submit shortcut

Examples:

g.submit("Accept Changes", {})

Parameters:

  • text (String)

    The text which the button will display

  • opts (Hash) (defaults to: {})

    Options for the SUBMIT statement



303
304
305
306
307
308
# File 'lib/demeler.rb', line 303

def submit(text, opts={})
  attr = {:type=>:submit}
  attr[:value] = text
  attr.merge!(opts)
  tag_generator(:input, attr)
end

#tag_generator(meth, args = [], &block) ⇒ Object

Note:

The :text option will insert text between the opening and closing tag; It’s useful to create one-line tags with text inserted.

The tag_generator method

Parameters:

  • meth (Symbol)

    The type of control to be generated

  • args (Hash) (defaults to: [])

    Options for the tag being generated

  • block (Proc)

    A block which will be called to get input or nest tags

Raises:

  • (StandardError)


320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/demeler.rb', line 320

def tag_generator(meth, args=[], &block)
  # this check catches a loop before it bombs the Demeler class
  raise StandardError.new("looping on #{meth.inspect}, @out=#{@out.inspect}") if (@level += 1)>500

  # This part examines the variations in possible inputs,
  # and rearranges them to suit tag_generator
  case
  when args.kind_of?(Hash)
    # args is a hash (attributes only)
    attr = args
  when args.size==0
    # args is empty array
    attr = {}
  when args.size==1 && args[0].kind_of?(String)
    # args is array of 1 string
    attr = {:text=>args[0]}
  when args.size==1 && args[0].kind_of?(Symbol)
    # args is array of 1 symbol (used as 'name')
    attr = {:name=>args[0]}
  when args.size==1 && args[0].kind_of?(Hash)
    # args is array of 1 hash (same as args is a hash)
    attr = args[0]
  when args.size==2 && args[0].kind_of?(Symbol) && args[1].kind_of?(Hash)
    # args is an array of symbol ('name') and hash ('attributes')
    # both name and attributes, i.e., g.div(:list, :class=>'list-class')
    attr = {:name=>args[0]}.merge(args[1])
  when args.size==2 && args[0].kind_of?(Symbol) && args[1].kind_of?(String)
    # args is array of symbol ('name') and string ('text')
    # both name and text, i.e., g.label(:list, "List")
    case meth
    when :label
      attr = {:for=>args[0]}.merge({:text=>args[1]})
      @labels << args[0]
    else
      attr = {:name=>args[0]}.merge({:text=>args[1]})
    end
  else
    raise ArgumentError.new("Too many arguments in Demeler#tag_generator: meth=>#{meth.inspect}, args=>#{args.inspect}")
  end

  # This part extracts a value out of the form's object (if any)
  name = attr[:name]
  case
  when name.nil?
  when @obj.nil?
  when !attr[:value].nil?
  when @obj[name].nil?
  when @obj[name].kind_of?(String) && @obj[name].empty?
  when meth==:textarea
    attr[:text] = @obj[name] if !attr.has_key?(:text)
  else
    attr[:value] = @obj[name] if !attr.has_key?(:value)
  end

  # If a label was previously defined for this :input,
  # add an :id attribute automatically
  attr[:id] = name if meth==:input && !attr.has_key?(:id) && @labels.index(name)

  # This part extracts the text (if any)--the text
  # is used in place of a block for tags like 'label'
  text = attr.delete(:text)
  case
  when text.nil?
    text = []
  when text.kind_of?(String)
    text = [text]
  when text.kind_of?(Array)
  else
    raise ArgumentError.new("In Demeler#tag_generator, expected Array or String for text (value for textarea, or ")
  end

  # make sure there's at least one (empty) string for textarea because
  # a textarea tag with no "block" makes the browser act wierd, even if it's
  # self-closing, i.e., <textarea ... />
  text = [""] if meth==:textarea && text.empty? && !block_given?

  # In case there is an error message for this field,
  # prepare the message now to add following the field
  if @obj && (list = @obj.errors[name])
    raise ArgumentError.new("The error message, if any, must be an array of Strings") if !list.kind_of?(Array)
    error = if [:input, :select].index(meth) then list.first else nil end
    message = if error then "<warn> <-- #{error}</warn>" else nil end
  else
    message = ""
  end

  # This is where the actual HTML is generated--it's structured this
  # way to be sure that only WHOLE tags are placed into @out--it's
  # done this way to facilitate #to_html
  case
  when !text.empty?
    temp = text.join("\n")
    @out << "<#{meth}#{attr.map{|k,v| %[ #{k}="#{v}"]}.join}>#{temp}</#{meth}>#{message}"
  when block_given?
    @out << "<#{meth}#{attr.map{|k,v| %[ #{k}="#{v}"]}.join}>"
    temp = yield
    @out << temp if temp && temp.kind_of?(String)
    @out << "</#{meth}>#{message}"
  else
    @out << "<#{meth}#{attr.map{|k,v| %[ #{k}="#{v}"]}.join} />#{message}"
  end

  @level -= 1
  nil
end

#to_htmlString

Method for converting the results of Demeler to a human readable string. This isn’t recommended for production because it requires much more time to generate the HTML output than to_s.

Returns:

  • (String)

    The formatted form output



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/demeler.rb', line 444

def to_html
  # output the segments, but adjust the indentation
  indent = 0
  html = "<!-- begin generated output -->\n"
  @out.each do |part|
    case
    when part =~ /^<\/.*>$/
      indent -= 1
      html << write_html(indent,part)
    when part =~ /^<.*<\/.*>$/
      html << write_html(indent,part)
    when part =~ /^<.*\/>$/
      html << write_html(indent,part)
    when part =~ /^<.*>$/
      html << write_html(indent,part)
      indent += 1
    else
      html << write_html(indent,part)
    end
  end
  # return the formatted string
  html << "<!-- end generated output -->\n"
  return html
end

#to_sString

Convert the final output of Demeler to a string. This method has the following alias: “to_str”.

Returns:

  • (String)


432
433
434
# File 'lib/demeler.rb', line 432

def to_s
  @out.join
end