padrino-helpers 0.10.5 → 0.10.6.a

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.
@@ -153,7 +153,7 @@ module Padrino
153
153
  # @api public
154
154
  def feed_tag(mime, url, options={})
155
155
  full_mime = (mime == :atom) ? 'application/atom+xml' : 'application/rss+xml'
156
- content_tag(:link, options.reverse_merge(:rel => 'alternate', :type => full_mime, :title => mime, :href => url))
156
+ tag(:link, options.reverse_merge(:rel => 'alternate', :type => full_mime, :title => mime, :href => url))
157
157
  end
158
158
 
159
159
  ##
@@ -291,9 +291,9 @@ module Padrino
291
291
  # @api public
292
292
  def javascript_include_tag(*sources)
293
293
  options = sources.extract_options!.symbolize_keys
294
- options.reverse_merge!(:type => 'text/javascript', :content => "")
294
+ options.reverse_merge!(:type => 'text/javascript')
295
295
  sources.flatten.map { |source|
296
- tag(:script, options.reverse_merge(:src => asset_path(:js, source)))
296
+ content_tag(:script, nil, options.reverse_merge(:src => asset_path(:js, source)))
297
297
  }.join("\n")
298
298
  end
299
299
 
@@ -42,6 +42,37 @@ module Padrino
42
42
  @template.text_field_tag field_name(field), options
43
43
  end
44
44
 
45
+ def number_field(field, options={})
46
+ options.reverse_merge!(:value => field_value(field), :id => field_id(field))
47
+ options.merge!(:class => field_error(field, options))
48
+ @template.number_field_tag field_name(field), options
49
+ end
50
+
51
+ def telephone_field(field, options={})
52
+ options.reverse_merge!(:value => field_value(field), :id => field_id(field))
53
+ options.merge!(:class => field_error(field, options))
54
+ @template.telephone_field_tag field_name(field), options
55
+ end
56
+ alias_method :phone_field, :telephone_field
57
+
58
+ def email_field(field, options={})
59
+ options.reverse_merge!(:value => field_value(field), :id => field_id(field))
60
+ options.merge!(:class => field_error(field, options))
61
+ @template.email_field_tag field_name(field), options
62
+ end
63
+
64
+ def search_field(field, options={})
65
+ options.reverse_merge!(:value => field_value(field), :id => field_id(field))
66
+ options.merge!(:class => field_error(field, options))
67
+ @template.search_field_tag field_name(field), options
68
+ end
69
+
70
+ def url_field(field, options={})
71
+ options.reverse_merge!(:value => field_value(field), :id => field_id(field))
72
+ options.merge!(:class => field_error(field, options))
73
+ @template.url_field_tag field_name(field), options
74
+ end
75
+
45
76
  # f.text_area :summary, :value => "(enter summary)", :id => 'summary'
46
77
  def text_area(field, options={})
47
78
  options.reverse_merge!(:value => field_value(field), :id => field_id(field))
@@ -140,9 +140,9 @@ module Padrino
140
140
  # @param [Hash] options Error message display options.
141
141
  # @option options [String] :header_tag ("h2")
142
142
  # Used for the header of the error div
143
- # @option options [String] :id ("errorExplanation")
143
+ # @option options [String] :id ("field-errors")
144
144
  # The id of the error div.
145
- # @option options [String] :class ("errorExplanation")
145
+ # @option options [String] :class ("field-errors")
146
146
  # The class of the error div.
147
147
  # @option options [Array<Object>] :object
148
148
  # The object (or array of objects) for which to display errors,
@@ -222,7 +222,7 @@ module Padrino
222
222
  # The field on the +object+ to display the error for.
223
223
  # @param [Hash] options
224
224
  # The options to control the error display.
225
- # @option options [String] :tag ("div")
225
+ # @option options [String] :tag ("span")
226
226
  # The tag that encloses the error.
227
227
  # @option options [String] :prepend ("")
228
228
  # The text to prepend before the field error.
@@ -289,29 +289,216 @@ module Padrino
289
289
  end
290
290
 
291
291
  ##
292
- # Constructs a text field input from the given options
292
+ # Creates a text field input with the given name and options
293
293
  #
294
- # @macro [new] input_field_doc
295
- # @param [String] name
296
- # The name of the input field.
294
+ # @macro [new] text_field
295
+ # @param [Symbol] name
296
+ # The name of the input to create.
297
297
  # @param [Hash] options
298
- # The html options for the input field.
299
- #
300
- # @return [String] The html input field based on the +options+ specified
298
+ # The HTML options to include in this field.
299
+ #
300
+ # @option options [String] :id
301
+ # Specifies a unique identifier for the field.
302
+ # @option options [String] :class
303
+ # Specifies the stylesheet class of the field.
304
+ # @option options [String] :name
305
+ # Specifies the name of the field.
306
+ # @option options [String] :accesskey
307
+ # Specifies a shortcut key to access the field.
308
+ # @option options [Integer] :tabindex
309
+ # Specifies the tab order of the field.
310
+ # @option options [Integer] :maxlength
311
+ # Specifies the maximum length, in characters, of the field.
312
+ # @option options [Integer] :size
313
+ # Specifies the width, in characters, of the field.
314
+ # @option options [String] :placeholder
315
+ # Specifies a short hint that describes the expected value of the field.
316
+ # @option options [Boolean] :hidden
317
+ # Specifies whether or not the field is hidden from view.
318
+ # @option options [Boolean] :spellcheck
319
+ # Specifies whether or not the field should have it's spelling and grammar checked for errors.
320
+ # @option options [Boolean] :draggable
321
+ # Specifies whether or not the field is draggable. (true, false, :auto)
322
+ # @option options [String] :pattern
323
+ # Specifies the regular expression pattern that the field's value is checked against.
324
+ # @option options [Symbol] :autocomplete
325
+ # Specifies whether or not the field should have autocomplete enabled. (:on, :off)
326
+ # @option options [Boolean] :autofocus
327
+ # Specifies whether or not the field should automatically get focus when the page loads.
328
+ # @option options [Boolean] :required
329
+ # Specifies whether or not the field is required to be completeled before the form is submitted.
330
+ # @option options [Boolean] :readonly
331
+ # Specifies whether or not the field is read only.
332
+ # @option options [Boolean] :disabled
333
+ # Specifies whether or not the field is disabled.
334
+ #
335
+ # @return [String]
336
+ # Generated HTML with specified +options+
301
337
  #
302
338
  # @example
303
- # text_field_tag :username, :class => 'long'
339
+ # text_field_tag :first_name, :maxlength => 40, :required => true
340
+ # # => <input name="first_name" maxlength="40" required type="text">
341
+ #
342
+ # text_field_tag :last_name, :class => 'string', :size => 40
343
+ # # => <input name="last_name" class="string" size="40" type="text">
344
+ #
345
+ # text_field_tag :username, :placeholder => 'Your Username'
346
+ # # => <input name="username" placeholder="Your Username" type="text">
304
347
  #
305
348
  # @api public
306
349
  def text_field_tag(name, options={})
307
- options.reverse_merge!(:name => name)
308
- input_tag(:text, options)
350
+ input_tag(:text, options.reverse_merge!(:name => name))
351
+ end
352
+
353
+ ##
354
+ # Creates a number field input with the given name and options
355
+ #
356
+ # @macro [new] number_field
357
+ # @param [Symbol] name
358
+ # The name of the input to create.
359
+ # @param [Hash] options
360
+ # The HTML options to include in this field.
361
+ #
362
+ # @option options [String] :id
363
+ # Specifies a unique identifier for the field.
364
+ # @option options [String] :class
365
+ # Specifies the stylesheet class of the field.
366
+ # @option options [String] :name
367
+ # Specifies the name of the field.
368
+ # @option options [String] :accesskey
369
+ # Specifies a shortcut key to access the field.
370
+ # @option options [Integer] :tabindex
371
+ # Specifies the tab order of the field.
372
+ # @option options [Integer] :min
373
+ # Specifies the minimum value of the field.
374
+ # @option options [Integer] :max
375
+ # Specifies the maximum value of the field.
376
+ # @option options [Integer] :step
377
+ # Specifies the legal number intervals of the field.
378
+ # @option options [Boolean] :hidden
379
+ # Specifies whether or not the field is hidden from view.
380
+ # @option options [Boolean] :spellcheck
381
+ # Specifies whether or not the field should have it's spelling and grammar checked for errors.
382
+ # @option options [Boolean] :draggable
383
+ # Specifies whether or not the field is draggable. (true, false, :auto)
384
+ # @option options [String] :pattern
385
+ # Specifies the regular expression pattern that the field's value is checked against.
386
+ # @option options [Symbol] :autocomplete
387
+ # Specifies whether or not the field should have autocomplete enabled. (:on, :off)
388
+ # @option options [Boolean] :autofocus
389
+ # Specifies whether or not the field should automatically get focus when the page loads.
390
+ # @option options [Boolean] :required
391
+ # Specifies whether or not the field is required to be completeled before the form is submitted.
392
+ # @option options [Boolean] :readonly
393
+ # Specifies whether or not the field is read only.
394
+ # @option options [Boolean] :disabled
395
+ # Specifies whether or not the field is disabled.
396
+ #
397
+ # @return [String]
398
+ # Generated HTML with specified +options+
399
+ #
400
+ # @example
401
+ # number_field_tag :quanity, :class => 'numeric'
402
+ # # => <input name="quanity" class="numeric" type="number">
403
+ #
404
+ # number_field_tag :zip_code, :pattern => /[0-9]{5}/
405
+ # # => <input name="zip_code" pattern="[0-9]{5}" type="number">
406
+ #
407
+ # number_field_tag :credit_card, :autocomplete => :off
408
+ # # => <input name="credit_card" autocomplete="off" type="number">
409
+ #
410
+ # number_field_tag :age, :min => 18, :max => 120, :step => 1
411
+ # # => <input name="age" min="18" max="120" step="1" type="number">
412
+ #
413
+ # @api public
414
+ def number_field_tag(name, options={})
415
+ input_tag(:number, options.reverse_merge(:name => name))
416
+ end
417
+
418
+ ##
419
+ # Creates a telephone field input with the given name and options
420
+ #
421
+ # @macro text_field
422
+ #
423
+ # @example
424
+ # telephone_field_tag :phone_number, :class => 'string'
425
+ # # => <input name="phone_number" class="string" type="tel">
426
+ #
427
+ # telephone_field_tag :cell_phone, :tabindex => 1
428
+ # telephone_field_tag :work_phone, :tabindex => 2
429
+ # telephone_field_tag :home_phone, :tabindex => 3
430
+ #
431
+ # # => <input name="cell_phone" tabindex="1" type="tel">
432
+ # # => <input name="work_phone" tabindex="2" type="tel">
433
+ # # => <input name="home_phone" tabindex="3" type="tel">
434
+ #
435
+ # @api public
436
+ def telephone_field_tag(name, options={})
437
+ input_tag(:tel, options.reverse_merge(:name => name))
438
+ end
439
+ alias_method :phone_field_tag, :telephone_field_tag
440
+
441
+ ##
442
+ # Creates an email field input with the given name and options
443
+ #
444
+ # @macro text_field
445
+ #
446
+ # @example
447
+ # email_field_tag :email, :placeholder => 'you@example.com'
448
+ # # => <input name="email" placeholder="you@example.com" type="email">
449
+ #
450
+ # email_field_tag :email, :value => 'padrinorb@gmail.com', :readonly => true
451
+ # # => <input name="email" value="padrinorb@gmail.com" readonly type="email">
452
+ #
453
+ # @api public
454
+ def email_field_tag(name, options={})
455
+ input_tag(:email, options.reverse_merge(:name => name))
456
+ end
457
+
458
+ ##
459
+ # Creates a search field input with the given name and options
460
+ #
461
+ # @macro text_field
462
+ #
463
+ # @example
464
+ # search_field_tag :search, :placeholder => 'Search this website...'
465
+ # # => <input name="search" placeholder="Search this website..." type="search">
466
+ #
467
+ # search_field_tag :search, :maxlength => 15, :class => ['search', 'string']
468
+ # # => <input name="search" maxlength="15" class="search string">
469
+ #
470
+ # search_field_tag :search, :id => 'search'
471
+ # # => <input name="search" id="search" type="search">
472
+ #
473
+ # search_field_tag :search, :autofocus => true
474
+ # # => <input name="search" autofocus type="search">
475
+ #
476
+ # @api public
477
+ def search_field_tag(name, options={})
478
+ input_tag(:search, options.reverse_merge(:name => name))
479
+ end
480
+
481
+ ##
482
+ # Creates a url field input with the given name and options
483
+ #
484
+ # @macro text_field
485
+ #
486
+ # @example
487
+ # url_field_tag :favorite_website, :placeholder => 'http://padrinorb.com'
488
+ # <input name="favorite_website" placeholder="http://padrinorb.com." type="url">
489
+ #
490
+ # url_field_tag :home_page, :class => 'string url'
491
+ # <input name="home_page" class="string url", type="url">
492
+ #
493
+ # @api public
494
+ def url_field_tag(name, options={})
495
+ input_tag(:url, options.reverse_merge(:name => name))
309
496
  end
310
497
 
311
498
  ##
312
499
  # Constructs a hidden field input from the given options
313
500
  #
314
- # @macro input_field_doc
501
+ # @macro text_field
315
502
  #
316
503
  # @example
317
504
  # hidden_field_tag :session_key, :value => "__secret__"
@@ -325,7 +512,7 @@ module Padrino
325
512
  ##
326
513
  # Constructs a text area input from the given options
327
514
  #
328
- # @macro input_field_doc
515
+ # @macro text_field
329
516
  #
330
517
  # @example
331
518
  # text_area_tag :username, :class => 'long', :value => "Demo?"
@@ -339,7 +526,7 @@ module Padrino
339
526
  ##
340
527
  # Constructs a password field input from the given options
341
528
  #
342
- # @macro input_field_doc
529
+ # @macro text_field
343
530
  #
344
531
  # @example
345
532
  # password_field_tag :password, :class => 'long'
@@ -353,7 +540,7 @@ module Padrino
353
540
  ##
354
541
  # Constructs a check_box from the given options
355
542
  #
356
- # @macro input_field_doc
543
+ # @macro text_field
357
544
  #
358
545
  # @example
359
546
  # check_box_tag :remember_me, :value => 'Yes'
@@ -367,7 +554,7 @@ module Padrino
367
554
  ##
368
555
  # Constructs a radio_button from the given options
369
556
  #
370
- # @macro input_field_doc
557
+ # @macro text_field
371
558
  #
372
559
  # @example
373
560
  # radio_button_tag :remember_me, :value => 'true'
@@ -381,7 +568,7 @@ module Padrino
381
568
  ##
382
569
  # Constructs a file field input from the given options
383
570
  #
384
- # @macro input_field_doc
571
+ # @macro text_field
385
572
  #
386
573
  # @example
387
574
  # file_field_tag :photo, :class => 'long'
@@ -81,11 +81,11 @@ module Padrino
81
81
  # @api public
82
82
  def simple_format(text, options={})
83
83
  t = options.delete(:tag) || :p
84
- start_tag = tag(t, options.merge(:open => true))
84
+ start_tag = tag(t, options)
85
85
  text = text.to_s.dup
86
86
  text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n
87
87
  text.gsub!(/\n\n+/, "</#{t}>\n\n#{start_tag}") # 2+ newline -> paragraph
88
- text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
88
+ text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br>') # 1 newline -> br
89
89
  text.insert 0, start_tag
90
90
  text << "</#{t}>"
91
91
  end
@@ -2,99 +2,212 @@ module Padrino
2
2
  module Helpers
3
3
  ##
4
4
  # Helpers related to producing html tags within templates.
5
- #
5
+ ##
6
6
  module TagHelpers
7
7
  ##
8
8
  # Tag values escaped to html entities
9
- #
9
+ ##
10
10
  ESCAPE_VALUES = {
11
11
  "<" => "&lt;",
12
12
  ">" => "&gt;",
13
13
  '"' => "&quot;"
14
14
  }
15
15
 
16
+ BOOLEAN_ATTRIBUTES = [
17
+ :autoplay,
18
+ :autofocus,
19
+ :formnovalidate,
20
+ :checked,
21
+ :disabled,
22
+ :hidden,
23
+ :loop,
24
+ :multiple,
25
+ :muted,
26
+ :readonly,
27
+ :required,
28
+ :selected
29
+ ]
30
+
16
31
  ##
17
- # Creates an html tag with given name, content and options
32
+ # Creates an HTML tag with given name, content, and options
18
33
  #
19
- # @overload content_tag(name, content, options)
20
- # @param [Symbol] name The html type of tag.
21
- # @param [String] content The contents in the tag.
22
- # @param [Hash] options The html options to include in this tag.
23
- # @overload content_tag(name, options, &block)
24
- # @param [Symbol] name The html type of tag.
25
- # @param [Hash] options The html options to include in this tag.
26
- # @param [Proc] block The block returning html content
34
+ # @overload content_tag(name, content, options = nil)
35
+ # @param [Symbol] name
36
+ # The name of the HTML tag to create.
37
+ # @param [String] content
38
+ # The content inside of the the tag.
39
+ # @param [Hash] options
40
+ # The HTML options to include in this tag.
27
41
  #
28
- # @return [String] The html generated for the tag.
42
+ # @overload content_tag(name, options = nil, &block)
43
+ # @param [Symbol] name
44
+ # The name of the HTML tag to create.
45
+ # @param [Hash] options
46
+ # The HTML options to include in this tag.
47
+ # @param [Proc] block
48
+ # The block returning HTML content.
49
+ #
50
+ # @macro [new] global_html_attributes
51
+ # @option options [String] :id
52
+ # Specifies a unique identifier for the element.
53
+ # @option options [String] :class
54
+ # Specifies the stylesheet class of the element.
55
+ # @option options [String] :title
56
+ # Specifies the title for the element.
57
+ # @option options [String] :accesskey
58
+ # Specifies a shortcut key to access the element.
59
+ # @option options [Symbol] :dropzone
60
+ # Specifies what happens when dragged items are dropped on the element. (:copy, :link, :move)
61
+ # @option options [Boolean] :hidden
62
+ # Specifies whether or not the element is hidden from view.
63
+ # @option options [Boolean] :draggable
64
+ # Specifies whether or not the element is draggable. (true, false, :auto)
65
+ # @option options [Boolean] :contenteditable
66
+ # Specifies whether or not the element is editable.
67
+ #
68
+ # @return [String]
69
+ # Generated HTML with specified +options+
29
70
  #
30
71
  # @example
31
- # content_tag(:p, "hello", :class => 'light')
32
- # content_tag(:p, :class => 'dark') { ... }
72
+ # content_tag(:p, 'Hello World', :class => 'light')
73
+ #
74
+ # # => <p class="light">
75
+ # # => Hello World
76
+ # # => </p>
77
+ #
78
+ # content_tag(:p, :class => 'dark') do
79
+ # link_to 'Padrino', 'http://www.padrinorb.com'
80
+ # end
81
+ #
82
+ # # => <p class="dark">
83
+ # # => <a href="http://www.padrinorb.com">Padrino</a>
84
+ # # => </p>
33
85
  #
34
86
  # @api public
35
- def content_tag(*args, &block)
36
- name = args.first
37
- options = args.extract_options!
38
- tag_html = block_given? ? capture_html(&block) : args[1]
39
- tag_result = tag(name, options.merge(:content => tag_html))
40
- block_is_template?(block) ? concat_content(tag_result) : tag_result
87
+ def content_tag(name, content = nil, options = nil, &block)
88
+ if block_given?
89
+ options = content if content.is_a?(Hash)
90
+ content = capture_html(&block)
91
+ end
92
+
93
+ content = content.join("\n") if content.respond_to?(:join)
94
+
95
+ output = "<#{name}#{tag_options(options) if options}>#{content}</#{name}>"
96
+ block_is_template?(block) ? concat_content(output) : output
41
97
  end
42
98
 
43
99
  ##
44
- # Creates an html input field with given type and options
100
+ # Creates an HTML input field with the given type and options
45
101
  #
46
102
  # @param [Symbol] type
47
- # The html type of tag to create.
103
+ # The type of input to create.
48
104
  # @param [Hash] options
49
- # The html options to include in this tag.
105
+ # The HTML options to include in this input.
50
106
  #
51
- # @return [String] The html for the input tag.
107
+ # @option options [String] :id
108
+ # Specifies a unique identifier for the input.
109
+ # @option options [String] :class
110
+ # Specifies the stylesheet class of the input.
111
+ # @option options [String] :name
112
+ # Specifies the name of the input.
113
+ # @option options [String] :accesskey
114
+ # Specifies a shortcut key to access the input.
115
+ # @option options [Integer] :tabindex
116
+ # Specifies the tab order of the input.
117
+ # @option options [Boolean] :hidden
118
+ # Specifies whether or not the input is hidden from view.
119
+ # @option options [Boolean] :spellcheck
120
+ # Specifies whether or not the input should have it's spelling and grammar checked for errors.
121
+ # @option options [Boolean] :draggable
122
+ # Specifies whether or not the input is draggable. (true, false, :auto)
123
+ # @option options [String] :pattern
124
+ # Specifies the regular expression pattern that the input's value is checked against.
125
+ # @option options [Symbol] :autocomplete
126
+ # Specifies whether or not the input should have autocomplete enabled. (:on, :off)
127
+ # @option options [Boolean] :autofocus
128
+ # Specifies whether or not the input should automatically get focus when the page loads.
129
+ # @option options [Boolean] :required
130
+ # Specifies whether or not the input is required to be completeled before the form is submitted.
131
+ # @option options [Boolean] :readonly
132
+ # Specifies whether or not the input is read only.
133
+ # @option options [Boolean] :disabled
134
+ # Specifies whether or not the input is disabled.
135
+ #
136
+ # @return [String]
137
+ # Generated HTML with specified +options+
52
138
  #
53
139
  # @example
54
- # input_tag :text, :class => "test"
55
- # input_tag :password, :size => "20"
140
+ # input_tag :text, :name => 'handle'
141
+ # # => <input type="test" name="handle">
142
+ #
143
+ # input_tag :password, :name => 'password', :size => 20
144
+ # # => <input type="password" name="password" size="20">
145
+ #
146
+ # input_tag :text, :name => 'username', :required => true, :autofocus => true
147
+ # # => <input type="text" name="username" required autofocus>
148
+ #
149
+ # input_tag :number, :name => 'credit_card', :autocomplete => :off
150
+ # # => <input type="number" autocomplete="off">
56
151
  #
57
152
  # @api semipublic
58
153
  def input_tag(type, options = {})
59
- options.reverse_merge!(:type => type)
60
- tag(:input, options)
154
+ tag(:input, options.reverse_merge!(:type => type))
61
155
  end
62
156
 
63
157
  ##
64
- # Creates an html tag with the given name and options
158
+ # Creates an HTML tag with the given name and options
65
159
  #
66
- # @param [Symbol] type
67
- # The html type of tag to create.
160
+ # @param [Symbol] name
161
+ # The name of the HTML tag to create.
68
162
  # @param [Hash] options
69
- # The html options to include in this tag.
163
+ # The HTML options to include in this tag.
164
+ #
165
+ # @macro global_html_attributes
70
166
  #
71
- # @return [String] The html for the input tag.
167
+ # @return [String]
168
+ # Generated HTML with specified +options+
72
169
  #
73
170
  # @example
74
- # tag(:br, :style => 'clear:both')
75
- # tag(:p, :content => "hello", :class => 'large')
171
+ # tag :hr, :class => 'dotted'
172
+ # # => <hr class="dotted">
173
+ #
174
+ # tag :input, :name => 'username', :type => :text
175
+ # # => <input name="username" type="text">
176
+ #
177
+ # tag :img, :src => 'images/pony.jpg', :alt => 'My Little Pony'
178
+ # # => <img src="images/pony.jpg" alt="My Little Pony">
179
+ #
180
+ # tag :img, :src => 'sinatra.jpg, :data => { :nsfw => false, :geo => [34.087, -118.407] }
181
+ # # => <img src="sinatra.jpg" data-nsfw="false" data-geo="34.087 -118.407">
76
182
  #
77
183
  # @api public
78
- def tag(name, options={})
79
- content, open_tag = options.delete(:content), options.delete(:open)
80
- content = content.join("\n") if content.respond_to?(:join)
81
- identity_tag_attributes.each { |attr| options[attr] = attr.to_s if options[attr] }
82
- html_attrs = options.map { |a, v| v.nil? || v == false ? nil : "#{a}=\"#{escape_value(v)}\"" }.compact.join(" ")
83
- base_tag = (html_attrs.present? ? "<#{name} #{html_attrs}" : "<#{name}")
84
- base_tag << (open_tag ? ">" : (content ? ">#{content}</#{name}>" : " />"))
184
+ def tag(name, options = nil)
185
+ "<#{name}#{tag_options(options) if options}>"
85
186
  end
86
187
 
87
188
  private
88
189
  ##
89
- # Returns a list of attributes which can only contain an identity value (i.e selected)
90
- #
91
- def identity_tag_attributes
92
- [:checked, :disabled, :selected, :multiple]
190
+ # Returns a compiled list of HTML attributes
191
+ ##
192
+ def tag_options(options)
193
+ return if options.blank?
194
+ attributes = []
195
+ options.each do |attribute, value|
196
+ next if value.nil? || value == false
197
+ if attribute == :data && value.is_a?(Hash)
198
+ value.each { |k, v| attributes << %[data-#{k.to_s.dasherize}="#{escape_value(v)}"] }
199
+ elsif BOOLEAN_ATTRIBUTES.include?(attribute)
200
+ attributes << attribute.to_s
201
+ else
202
+ attributes << %[#{attribute}="#{escape_value(value)}"]
203
+ end
204
+ end
205
+ " #{attributes.join(' ')}"
93
206
  end
94
207
 
95
208
  ##
96
209
  # Escape tag values to their HTML/XML entities.
97
- #
210
+ ##
98
211
  def escape_value(string)
99
212
  string.to_s.gsub(Regexp.union(*ESCAPE_VALUES.keys)){|c| ESCAPE_VALUES[c] }
100
213
  end