sinatra-tags 0.1.0
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.
- data/.document +5 -0
- data/.gitignore +22 -0
- data/CHANGES +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +284 -0
- data/Rakefile +91 -0
- data/VERSION +1 -0
- data/lib/sinatra/tags.rb +587 -0
- data/sinatra-tags.gemspec +64 -0
- data/spec/sinatra/tags_spec.rb +463 -0
- data/spec/spec_helper.rb +57 -0
- metadata +129 -0
data/.document
ADDED
data/.gitignore
ADDED
data/CHANGES
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 kematzy
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
= Sinatra::Tags
|
2
|
+
|
3
|
+
A Sinatra Extension that provides easy creation of flexible HTML tags.
|
4
|
+
|
5
|
+
== Why was this gem created ?
|
6
|
+
|
7
|
+
To enable the Sinatra community to quickly and easily add this functionality to any
|
8
|
+
app / extension they wish to create. ie: preventing time waste or the 're-invention of the wheel'.
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
== Installation
|
13
|
+
|
14
|
+
# Add RubyGems.org (former Gemcutter) to your RubyGems sources
|
15
|
+
$ gem sources -a http://rubygems.org
|
16
|
+
|
17
|
+
$ (sudo)? gem install sinatra-tags
|
18
|
+
|
19
|
+
== Dependencies
|
20
|
+
|
21
|
+
This Gem depends upon the following:
|
22
|
+
|
23
|
+
=== Runtime:
|
24
|
+
|
25
|
+
* sinatra ( >= 1.0.a )
|
26
|
+
* sinatra-outputbuffer[http://github.com/kematzy/sinatra-outputbuffer] (>= 0.1.0)
|
27
|
+
|
28
|
+
Optionals:
|
29
|
+
|
30
|
+
* sinatra-settings[http://github.com/kematzy/sinatra-settings] (>= 0.1.1) # to view default settings in a browser display.
|
31
|
+
|
32
|
+
|
33
|
+
=== Development & Tests:
|
34
|
+
|
35
|
+
* sinatra-tests (>= 0.1.6)
|
36
|
+
* rspec (>= 1.3.0 )
|
37
|
+
* rack-test (>= 0.5.3)
|
38
|
+
* rspec_hpricot_matchers (>= 0.1.0)
|
39
|
+
|
40
|
+
== Getting Started
|
41
|
+
|
42
|
+
To start using Sinatra::Tags, just require and register the extension
|
43
|
+
|
44
|
+
...in your App...
|
45
|
+
|
46
|
+
require 'sinatra/tags'
|
47
|
+
|
48
|
+
class YourApp < Sinatra::Base
|
49
|
+
register(Sinatra::Tags)
|
50
|
+
|
51
|
+
<snip...>
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
...or your Extension...
|
56
|
+
|
57
|
+
require 'sinatra/tags'
|
58
|
+
|
59
|
+
module Sinatra
|
60
|
+
module YourExtension
|
61
|
+
|
62
|
+
<snip...>
|
63
|
+
|
64
|
+
def self.registered(app)
|
65
|
+
app.register Sinatra::Tags
|
66
|
+
<snip...>
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
== Usage
|
74
|
+
|
75
|
+
Sinatra::Tags has only <b>one public method</b>, with this <b>dynamic</b> syntax:
|
76
|
+
|
77
|
+
tag(name)
|
78
|
+
tag(name, &block)
|
79
|
+
|
80
|
+
tag(name, content)
|
81
|
+
tag(name, content, attributes)
|
82
|
+
tag(name, content, attributes, &block)
|
83
|
+
|
84
|
+
tag(name, attributes)
|
85
|
+
tag(name, attributes, &block)
|
86
|
+
|
87
|
+
|
88
|
+
This dynamic syntax provides a very flexible method as you can see in the examples below:
|
89
|
+
|
90
|
+
|
91
|
+
<b>Self closing tags:</b>
|
92
|
+
|
93
|
+
tag(:br) # =>
|
94
|
+
|
95
|
+
<br> or <br/> if XHTML
|
96
|
+
|
97
|
+
tag(:hr, :class => "space") # =>
|
98
|
+
|
99
|
+
<hr class="space">
|
100
|
+
|
101
|
+
<b>Multi line tags:</b>
|
102
|
+
|
103
|
+
tag(:div) # =>
|
104
|
+
|
105
|
+
<div></div>
|
106
|
+
|
107
|
+
tag(:div, 'content') # =>
|
108
|
+
|
109
|
+
<div>
|
110
|
+
content
|
111
|
+
</div>
|
112
|
+
|
113
|
+
tag(:div, 'content', :id => 'comment') # =>
|
114
|
+
|
115
|
+
<div id="comment">
|
116
|
+
content
|
117
|
+
</div>
|
118
|
+
|
119
|
+
# NB! no content
|
120
|
+
tag(:div, :id => 'comment') # =>
|
121
|
+
|
122
|
+
<div id="comment"></div>
|
123
|
+
|
124
|
+
|
125
|
+
<b>Single line tags:</b>
|
126
|
+
|
127
|
+
tag(:h1,'Header') # =>
|
128
|
+
|
129
|
+
<h1>Header</h1>
|
130
|
+
|
131
|
+
tag(:abbr, 'WHO', :title => "World Health Organization") # =>
|
132
|
+
|
133
|
+
<abbr title="World Health Organization">WHO</abbr>
|
134
|
+
|
135
|
+
|
136
|
+
<b>Working with blocks</b>
|
137
|
+
|
138
|
+
tag(:div) do
|
139
|
+
tag(:p, 'Hello World')
|
140
|
+
end
|
141
|
+
# =>
|
142
|
+
|
143
|
+
<div>
|
144
|
+
<p>Hello World</p>
|
145
|
+
</div>
|
146
|
+
|
147
|
+
<% tag(:ul) do %>
|
148
|
+
<li>item 1</li>
|
149
|
+
<%= tag(:li, 'item 2') %>
|
150
|
+
<li>item 3</li>
|
151
|
+
<% end %>
|
152
|
+
# =>
|
153
|
+
|
154
|
+
<ul>
|
155
|
+
<li>item 1</li>
|
156
|
+
<li>item 2</li>
|
157
|
+
<li>item 3</li>
|
158
|
+
</ul>
|
159
|
+
|
160
|
+
|
161
|
+
# NB! ignored tag contents if given a block
|
162
|
+
<% tag(:div, 'ignored tag-content') do %>
|
163
|
+
<%= tag(:label, 'Comments:', :for => :comments) %>
|
164
|
+
<%= tag(:textarea,'textarea contents', :id => :comments) %>
|
165
|
+
<% end %>
|
166
|
+
# =>
|
167
|
+
|
168
|
+
<div>
|
169
|
+
<label for="comments">Comments:</label>
|
170
|
+
<textarea id="comments">
|
171
|
+
textarea contents
|
172
|
+
</textarea>
|
173
|
+
</div>
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
<b>Boolean attributes:</b>
|
178
|
+
|
179
|
+
tag(:input, :type => :checkbox, :checked => true)
|
180
|
+
# =>
|
181
|
+
|
182
|
+
<input type="checkbox" checked="checked" />
|
183
|
+
|
184
|
+
|
185
|
+
tag(:option, 'Sinatra', :value => "1" :selected => true)
|
186
|
+
# =>
|
187
|
+
|
188
|
+
<option value="1">Sinatra</option>
|
189
|
+
|
190
|
+
|
191
|
+
tag(:option, 'PHP', :value => "0" :selected => false)
|
192
|
+
# =>
|
193
|
+
|
194
|
+
<option value="0">PHP</option>
|
195
|
+
|
196
|
+
|
197
|
+
That's more or less it. Try it out and you'll see what it can do for you.
|
198
|
+
|
199
|
+
|
200
|
+
== Configuration Settings
|
201
|
+
|
202
|
+
The default settings should help you get moving quickly, and are fairly common sense based.
|
203
|
+
|
204
|
+
|
205
|
+
==== <tt>:tags_output_format_is_xhtml</tt>
|
206
|
+
|
207
|
+
Sets the HTML output format, toggling between HTML vs XHTML.
|
208
|
+
Default is: <tt>false</tt>
|
209
|
+
|
210
|
+
I prefer to output in HTML 4.0.1 Strict, but you can easily switch to XHTML
|
211
|
+
by setting the value in your App or Extension:
|
212
|
+
|
213
|
+
set :tags_output_format_is_xhtml, true
|
214
|
+
|
215
|
+
...or on the fly like this
|
216
|
+
|
217
|
+
YourApp.tags_output_format_is_xhtml = true / false
|
218
|
+
|
219
|
+
self.class.tags_output_format_is_xhtml = true / false
|
220
|
+
|
221
|
+
settings.tags_output_format_is_xhtml = true / false
|
222
|
+
|
223
|
+
|
224
|
+
==== <tt>:tags_add_newlines_after_tags</tt>
|
225
|
+
|
226
|
+
Sets the formatting of the HTML output, whether it should be more compact in nature
|
227
|
+
or slightly better layed out.
|
228
|
+
Default is: <tt>true</tt>
|
229
|
+
|
230
|
+
|
231
|
+
== RTFM
|
232
|
+
|
233
|
+
If the above is not clear enough, please check the Specs for a better understanding.
|
234
|
+
|
235
|
+
|
236
|
+
== Errors / Bugs
|
237
|
+
|
238
|
+
If something is not behaving intuitively, it is a bug, and should be reported.
|
239
|
+
Report it here: http://github.com/kematzy/sinatra-tags/issues
|
240
|
+
|
241
|
+
|
242
|
+
== TODOs
|
243
|
+
|
244
|
+
* Keep it up to date with any changes in Sinatra.
|
245
|
+
|
246
|
+
* Decide on if it's worth it to do validity checks on all attributes passed to tags
|
247
|
+
ie: reject attributes based upon what is allowed for the tag.
|
248
|
+
|
249
|
+
tag(:base, :href => 'url', :target => '_self', :id => 'is-ignored')
|
250
|
+
# =>
|
251
|
+
|
252
|
+
<base href="url", target="_self">
|
253
|
+
|
254
|
+
* Decide on whether to add a number of convenience tags (methods), such as:
|
255
|
+
|
256
|
+
- meta(name, contents)
|
257
|
+
|
258
|
+
- img(src, attrs)
|
259
|
+
|
260
|
+
|
261
|
+
* Any other improvements I or You can think of.
|
262
|
+
|
263
|
+
|
264
|
+
== Note on Patches/Pull Requests
|
265
|
+
|
266
|
+
* Fork the project.
|
267
|
+
* Make your feature addition or bug fix.
|
268
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
269
|
+
* Commit, do not mess with rakefile, version, or history.
|
270
|
+
* (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
271
|
+
* Send me a pull request. Bonus points for topic branches.
|
272
|
+
|
273
|
+
== Copyright
|
274
|
+
|
275
|
+
Copyright (c) 2010 Kematzy
|
276
|
+
|
277
|
+
Released under the MIT License.
|
278
|
+
|
279
|
+
See LICENSE for further details.
|
280
|
+
|
281
|
+
=== Code Inspirations:
|
282
|
+
|
283
|
+
* The ActiveSupport gem by DHH & Rails Core Team
|
284
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "sinatra-tags"
|
8
|
+
gem.summary = %Q{A Sinatra Extension that provides easy creation of flexible HTML tags.}
|
9
|
+
gem.description = %Q{A Sinatra Extension that provides easy creation of flexible HTML tags.}
|
10
|
+
gem.email = "kematzy@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/kematzy/sinatra-tags"
|
12
|
+
gem.authors = ["kematzy"]
|
13
|
+
gem.add_dependency('sinatra', '>=1.0.a')
|
14
|
+
gem.add_dependency('sinatra-outputbuffer', '>=0.1.0')
|
15
|
+
gem.add_development_dependency "sinatra-tests", ">= 0.1.6"
|
16
|
+
gem.add_development_dependency "rspec", ">= 1.3.0"
|
17
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'spec/rake/spectask'
|
25
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.spec_opts = ["--color", "--format", "specdoc", "--require", "spec/spec_helper.rb"]
|
28
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.spec_opts = ["--color", "--format", "specdoc", "--require", "spec/spec_helper.rb"]
|
34
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
35
|
+
spec.rcov = true
|
36
|
+
end
|
37
|
+
|
38
|
+
namespace :spec do
|
39
|
+
|
40
|
+
desc "Run all specifications quietly"
|
41
|
+
Spec::Rake::SpecTask.new(:quiet) do |t|
|
42
|
+
t.libs << "lib"
|
43
|
+
t.spec_opts = ["--color", "--require", "spec/spec_helper.rb"]
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Run specific spec verbosely (SPEC=/path/2/file)"
|
47
|
+
Spec::Rake::SpecTask.new(:select) do |t|
|
48
|
+
t.libs << "lib"
|
49
|
+
t.spec_files = [ENV["SPEC"]]
|
50
|
+
t.spec_opts = ["--color", "--format", "specdoc", "--require", "spec/spec_helper.rb"]
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
task :spec => :check_dependencies
|
56
|
+
|
57
|
+
task :default => :spec
|
58
|
+
|
59
|
+
require 'rake/rdoctask'
|
60
|
+
Rake::RDocTask.new do |rdoc|
|
61
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
62
|
+
|
63
|
+
rdoc.rdoc_dir = 'rdoc'
|
64
|
+
rdoc.title = "Sinatra::Tags #{version}"
|
65
|
+
rdoc.rdoc_files.include('README*')
|
66
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
67
|
+
end
|
68
|
+
|
69
|
+
desc 'Build the rdoc HTML Files'
|
70
|
+
task :docs do
|
71
|
+
version = File.exist?('VERSION') ? IO.read('VERSION').chomp : "[Unknown]"
|
72
|
+
|
73
|
+
sh "sdoc -N --title 'Sinatra::Tags v#{version}' lib/ README.rdoc"
|
74
|
+
end
|
75
|
+
|
76
|
+
namespace :docs do
|
77
|
+
|
78
|
+
desc 'Remove rdoc products'
|
79
|
+
task :remove => [:clobber_rdoc]
|
80
|
+
|
81
|
+
desc 'Force a rebuild of the RDOC files'
|
82
|
+
task :rebuild => [:rerdoc]
|
83
|
+
|
84
|
+
desc 'Build docs, and open in browser for viewing (specify BROWSER)'
|
85
|
+
task :open => [:docs] do
|
86
|
+
browser = ENV["BROWSER"] || "safari"
|
87
|
+
sh "open -a #{browser} doc/index.html"
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/sinatra/tags.rb
ADDED
@@ -0,0 +1,587 @@
|
|
1
|
+
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'sinatra/outputbuffer'
|
4
|
+
|
5
|
+
# :stopdoc:
|
6
|
+
class Hash
|
7
|
+
|
8
|
+
# remove any other version of #to_html_attributes
|
9
|
+
undef_method :to_html_attributes if method_defined?(:to_html_attributes)
|
10
|
+
|
11
|
+
def to_html_attributes(include_empties=nil)
|
12
|
+
hash = self.dup
|
13
|
+
hash.reject! {|k,v| v.blank? } unless include_empties.nil?
|
14
|
+
out = ''
|
15
|
+
hash.keys.each do |key|
|
16
|
+
val = hash[key].is_a?(Array) ? hash[key].join('_') : hash[key].to_s
|
17
|
+
out << "#{key.to_s}=\"#{val}\" "
|
18
|
+
end
|
19
|
+
out.strip
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
# :startdoc:
|
24
|
+
|
25
|
+
module Sinatra
|
26
|
+
|
27
|
+
# = Sinatra::Tags
|
28
|
+
#
|
29
|
+
# A Sinatra Extension that provides easy creation of flexible HTML tags.
|
30
|
+
#
|
31
|
+
# == Installation
|
32
|
+
#
|
33
|
+
# # Add RubyGems.org (former Gemcutter) to your RubyGems sources
|
34
|
+
# $ gem sources -a http://rubygems.org
|
35
|
+
#
|
36
|
+
# $ (sudo)? gem install sinatra-tags
|
37
|
+
#
|
38
|
+
# == Dependencies
|
39
|
+
#
|
40
|
+
# This Gem depends upon the following:
|
41
|
+
#
|
42
|
+
# === Runtime:
|
43
|
+
#
|
44
|
+
# * sinatra ( >= 1.0.a )
|
45
|
+
# * sinatra-outputbuffer[http://github.com/kematzy/sinatra-outputbuffer] (>= 0.1.0)
|
46
|
+
#
|
47
|
+
# Optionals:
|
48
|
+
#
|
49
|
+
# * sinatra-settings[http://github.com/kematzy/sinatra-settings] (>= 0.1.1) # to view default settings in a browser display.
|
50
|
+
#
|
51
|
+
#
|
52
|
+
# === Development & Tests:
|
53
|
+
#
|
54
|
+
# * sinatra-tests (>= 0.1.6)
|
55
|
+
# * rspec (>= 1.3.0 )
|
56
|
+
# * rack-test (>= 0.5.3)
|
57
|
+
# * rspec_hpricot_matchers (>= 0.1.0)
|
58
|
+
#
|
59
|
+
# == Getting Started
|
60
|
+
#
|
61
|
+
# To start using Sinatra::Tags, just require and register the extension
|
62
|
+
#
|
63
|
+
# ...in your App...
|
64
|
+
#
|
65
|
+
# require 'sinatra/tags'
|
66
|
+
#
|
67
|
+
# class YourApp < Sinatra::Base
|
68
|
+
# register(Sinatra::Tags)
|
69
|
+
#
|
70
|
+
# <snip...>
|
71
|
+
#
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# ...or your Extension...
|
75
|
+
#
|
76
|
+
# require 'sinatra/tags'
|
77
|
+
#
|
78
|
+
# module Sinatra
|
79
|
+
# module YourExtension
|
80
|
+
#
|
81
|
+
# <snip...>
|
82
|
+
#
|
83
|
+
# def self.registered(app)
|
84
|
+
# app.register Sinatra::Tags
|
85
|
+
# <snip...>
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
#
|
92
|
+
# == Usage
|
93
|
+
#
|
94
|
+
# Sinatra::Tags has only <b>one public method</b>, with this <b>dynamic</b> syntax:
|
95
|
+
#
|
96
|
+
# tag(name)
|
97
|
+
# tag(name, &block)
|
98
|
+
#
|
99
|
+
# tag(name, content)
|
100
|
+
# tag(name, content, attributes)
|
101
|
+
# tag(name, content, attributes, &block)
|
102
|
+
#
|
103
|
+
# tag(name, attributes)
|
104
|
+
# tag(name, attributes, &block)
|
105
|
+
#
|
106
|
+
#
|
107
|
+
# This dynamic syntax provides a very flexible method as you can see in the examples below:
|
108
|
+
#
|
109
|
+
#
|
110
|
+
# <b>Self closing tags:</b>
|
111
|
+
#
|
112
|
+
# tag(:br) # =>
|
113
|
+
#
|
114
|
+
# <br> or <br/> if XHTML
|
115
|
+
#
|
116
|
+
# tag(:hr, :class => "space") # =>
|
117
|
+
#
|
118
|
+
# <hr class="space">
|
119
|
+
#
|
120
|
+
# <b>Multi line tags:</b>
|
121
|
+
#
|
122
|
+
# tag(:div) # =>
|
123
|
+
#
|
124
|
+
# <div></div>
|
125
|
+
#
|
126
|
+
# tag(:div, 'content') # =>
|
127
|
+
#
|
128
|
+
# <div>
|
129
|
+
# content
|
130
|
+
# </div>
|
131
|
+
#
|
132
|
+
# tag(:div, 'content', :id => 'comment') # =>
|
133
|
+
#
|
134
|
+
# <div id="comment">
|
135
|
+
# content
|
136
|
+
# </div>
|
137
|
+
#
|
138
|
+
# # NB! no content
|
139
|
+
# tag(:div, :id => 'comment') # =>
|
140
|
+
#
|
141
|
+
# <div id="comment"></div>
|
142
|
+
#
|
143
|
+
#
|
144
|
+
# <b>Single line tags:</b>
|
145
|
+
#
|
146
|
+
# tag(:h1,'Header') # =>
|
147
|
+
#
|
148
|
+
# <h1>Header</h1>
|
149
|
+
#
|
150
|
+
# tag(:abbr, 'WHO', :title => "World Health Organization") # =>
|
151
|
+
#
|
152
|
+
# <abbr title="World Health Organization">WHO</abbr>
|
153
|
+
#
|
154
|
+
#
|
155
|
+
# <b>Working with blocks</b>
|
156
|
+
#
|
157
|
+
# tag(:div) do
|
158
|
+
# tag(:p, 'Hello World')
|
159
|
+
# end
|
160
|
+
# # =>
|
161
|
+
#
|
162
|
+
# <div>
|
163
|
+
# <p>Hello World</p>
|
164
|
+
# </div>
|
165
|
+
#
|
166
|
+
# <% tag(:ul) do %>
|
167
|
+
# <li>item 1</li>
|
168
|
+
# <%= tag(:li, 'item 2') %>
|
169
|
+
# <li>item 3</li>
|
170
|
+
# <% end %>
|
171
|
+
# # =>
|
172
|
+
#
|
173
|
+
# <ul>
|
174
|
+
# <li>item 1</li>
|
175
|
+
# <li>item 2</li>
|
176
|
+
# <li>item 3</li>
|
177
|
+
# </ul>
|
178
|
+
#
|
179
|
+
#
|
180
|
+
# # NB! ignored tag contents if given a block
|
181
|
+
# <% tag(:div, 'ignored tag-content') do %>
|
182
|
+
# <%= tag(:label, 'Comments:', :for => :comments) %>
|
183
|
+
# <%= tag(:textarea,'textarea contents', :id => :comments) %>
|
184
|
+
# <% end %>
|
185
|
+
# # =>
|
186
|
+
#
|
187
|
+
# <div>
|
188
|
+
# <label for="comments">Comments:</label>
|
189
|
+
# <textarea id="comments">
|
190
|
+
# textarea contents
|
191
|
+
# </textarea>
|
192
|
+
# </div>
|
193
|
+
#
|
194
|
+
#
|
195
|
+
#
|
196
|
+
# <b>Boolean attributes:</b>
|
197
|
+
#
|
198
|
+
# tag(:input, :type => :checkbox, :checked => true)
|
199
|
+
# # =>
|
200
|
+
#
|
201
|
+
# <input type="checkbox" checked="checked" />
|
202
|
+
#
|
203
|
+
#
|
204
|
+
# tag(:option, 'Sinatra', :value => "1" :selected => true)
|
205
|
+
# # =>
|
206
|
+
#
|
207
|
+
# <option value="1">Sinatra</option>
|
208
|
+
#
|
209
|
+
#
|
210
|
+
# tag(:option, 'PHP', :value => "0" :selected => false)
|
211
|
+
# # =>
|
212
|
+
#
|
213
|
+
# <option value="0">PHP</option>
|
214
|
+
#
|
215
|
+
#
|
216
|
+
# That's more or less it. Try it out and you'll see what it can do for you.
|
217
|
+
#
|
218
|
+
#
|
219
|
+
# == Configuration Settings
|
220
|
+
#
|
221
|
+
# The default settings should help you get moving quickly, and are fairly common sense based.
|
222
|
+
#
|
223
|
+
#
|
224
|
+
# ==== <tt>:tags_output_format_is_xhtml</tt>
|
225
|
+
#
|
226
|
+
# Sets the HTML output format, toggling between HTML vs XHTML.
|
227
|
+
# Default is: <tt>false</tt>
|
228
|
+
#
|
229
|
+
# I prefer to output in HTML 4.0.1 Strict, but you can easily switch to XHTML
|
230
|
+
# by setting the value in your App or Extension:
|
231
|
+
#
|
232
|
+
# set :tags_output_format_is_xhtml, true
|
233
|
+
#
|
234
|
+
# ...or on the fly like this
|
235
|
+
#
|
236
|
+
# YourApp.tags_output_format_is_xhtml = true / false
|
237
|
+
#
|
238
|
+
# self.class.tags_output_format_is_xhtml = true / false
|
239
|
+
#
|
240
|
+
# settings.tags_output_format_is_xhtml = true / false
|
241
|
+
#
|
242
|
+
#
|
243
|
+
# ==== <tt>:tags_add_newlines_after_tags</tt>
|
244
|
+
#
|
245
|
+
# Sets the formatting of the HTML output, whether it should be more compact in nature
|
246
|
+
# or slightly better layed out.
|
247
|
+
# Default is: <tt>true</tt>
|
248
|
+
#
|
249
|
+
#
|
250
|
+
# == Copyright
|
251
|
+
#
|
252
|
+
# Copyright (c) 2010 Kematzy
|
253
|
+
#
|
254
|
+
# Released under the MIT License.
|
255
|
+
#
|
256
|
+
# See LICENSE for further details.
|
257
|
+
#
|
258
|
+
# === Code Inspirations:
|
259
|
+
#
|
260
|
+
# * The ActiveSupport gem by DHH & Rails Core Team
|
261
|
+
#
|
262
|
+
module Tags
|
263
|
+
|
264
|
+
VERSION = '0.1.0' unless const_defined?(:VERSION)
|
265
|
+
|
266
|
+
##
|
267
|
+
# Returns the version string for this extension
|
268
|
+
#
|
269
|
+
# ==== Examples
|
270
|
+
#
|
271
|
+
# Sinatra::Tags.version => 'Sinatra::Tags v0.1.0'
|
272
|
+
#
|
273
|
+
def self.version; "Sinatra::Tags v#{VERSION}"; end
|
274
|
+
|
275
|
+
|
276
|
+
module Helpers
|
277
|
+
|
278
|
+
##
|
279
|
+
# Tags that should be rendered in multiple lines, like...
|
280
|
+
#
|
281
|
+
# <body>
|
282
|
+
# <snip...>
|
283
|
+
# </body>
|
284
|
+
#
|
285
|
+
MULTI_LINE_TAGS = %w(
|
286
|
+
a address applet bdo big blockquote body button caption center
|
287
|
+
colgroup dd dir div dl dt fieldset form frameset head html iframe
|
288
|
+
map noframes noscript object ol optgroup pre script select small
|
289
|
+
style table tbody td textarea tfoot th thead title tr tt ul
|
290
|
+
)
|
291
|
+
|
292
|
+
##
|
293
|
+
# Self closing tags, like...
|
294
|
+
#
|
295
|
+
# <hr> or <hr />
|
296
|
+
#
|
297
|
+
SELF_CLOSING_TAGS = %w( area base br col frame hr img input link meta param )
|
298
|
+
|
299
|
+
##
|
300
|
+
# Tags that should be rendered in a single line, like...
|
301
|
+
#
|
302
|
+
# <h1>Header</h1>
|
303
|
+
#
|
304
|
+
SINGLE_LINE_TAGS = %w(
|
305
|
+
abbr acronym b cite code del dfn em h1 h2 h3 h4 h5 h6 i kbd
|
306
|
+
label legend li option p q samp span strong sub sup var
|
307
|
+
)
|
308
|
+
|
309
|
+
##
|
310
|
+
# Boolean attributes, ie: attributes like...
|
311
|
+
#
|
312
|
+
# <input type="text" selected="selected"...>
|
313
|
+
#
|
314
|
+
BOOLEAN_ATTRIBUTES = %w( selected checked disabled readonly multiple )
|
315
|
+
|
316
|
+
##
|
317
|
+
# Returns markup for tag _name_.
|
318
|
+
#
|
319
|
+
# Optionally _contents_ may be passed, which is literal content for
|
320
|
+
# spanning tags such as <tt>textarea</tt>, etc.
|
321
|
+
#
|
322
|
+
# A hash of _attrs_ may be passed as the *second* or *third* argument.
|
323
|
+
#
|
324
|
+
# Self closing tags such as <tt><br/></tt>, <tt><input/></tt>, etc
|
325
|
+
# are automatically closed depending on output format, HTML vs XHTML.
|
326
|
+
#
|
327
|
+
# Boolean attributes like "<tt>selected</tt>", "<tt>checked</tt>" etc,
|
328
|
+
# are mirrored or removed when <tt>true</tt> or <tt>false</tt>.
|
329
|
+
#
|
330
|
+
# ==== Examples
|
331
|
+
#
|
332
|
+
# Self closing tags:
|
333
|
+
#
|
334
|
+
# tag(:br)
|
335
|
+
# # => <br> or <br/> if XHTML
|
336
|
+
#
|
337
|
+
# tag(:hr, :class => "space")
|
338
|
+
# # => <hr class="space">
|
339
|
+
#
|
340
|
+
# Multi line tags:
|
341
|
+
#
|
342
|
+
# tag(:div)
|
343
|
+
# # => <div></div>
|
344
|
+
#
|
345
|
+
# tag(:div, 'content')
|
346
|
+
# # => <div>content</div>
|
347
|
+
#
|
348
|
+
# tag(:div, 'content', :id => 'comment')
|
349
|
+
# # => <div id="comment">content</div>
|
350
|
+
#
|
351
|
+
# tag(:div, :id => 'comment') # NB! no content
|
352
|
+
# # => <div id="comment"></div>
|
353
|
+
#
|
354
|
+
# Single line tags:
|
355
|
+
#
|
356
|
+
# tag(:h1,'Header')
|
357
|
+
# # => <h1>Header</h1>
|
358
|
+
#
|
359
|
+
# tag(:abbr, 'WHO', :title => "World Health Organization")
|
360
|
+
# # => <abbr title="World Health Organization">WHO</abbr>
|
361
|
+
#
|
362
|
+
# Working with blocks
|
363
|
+
#
|
364
|
+
# tag(:div) do
|
365
|
+
# tag(:p, 'Hello World')
|
366
|
+
# end
|
367
|
+
# # => <div><p>Hello World</p></div>
|
368
|
+
#
|
369
|
+
# <% tag(:div) do %>
|
370
|
+
# <p>Paragraph 1</p>
|
371
|
+
# <%= tag(:p, 'Paragraph 2') %>
|
372
|
+
# <p>Paragraph 3</p>
|
373
|
+
# <% end %>
|
374
|
+
# # =>
|
375
|
+
# <div>
|
376
|
+
# <p>Paragraph 1</p>
|
377
|
+
# <p>Paragraph 2</p>
|
378
|
+
# <p>Paragraph 3</p>
|
379
|
+
# </div>
|
380
|
+
#
|
381
|
+
#
|
382
|
+
# # NB! ignored tag contents if given a block
|
383
|
+
# <% tag(:div, 'ignored tag-content') do %>
|
384
|
+
# <%= tag(:label, 'Comments:', :for => :comments) %>
|
385
|
+
# <%= tag(:textarea,'textarea contents', :id => :comments) %>
|
386
|
+
# <% end %>
|
387
|
+
# # =>
|
388
|
+
# <div>
|
389
|
+
# <label for="comments">Comments:</label>
|
390
|
+
# <textarea id="comments">
|
391
|
+
# textarea contents
|
392
|
+
# </textarea>
|
393
|
+
# </div>
|
394
|
+
#
|
395
|
+
#
|
396
|
+
#
|
397
|
+
# Boolean attributes:
|
398
|
+
#
|
399
|
+
# tag(:input, :type => :checkbox, :checked => true)
|
400
|
+
# # => <input type="checkbox" checked="checked" />
|
401
|
+
#
|
402
|
+
# tag(:option, 'Sinatra', :value => "1" :selected => true)
|
403
|
+
# # => <option value="1">Sinatra</option>
|
404
|
+
#
|
405
|
+
# tag(:option, 'PHP', :value => "0" :selected => false)
|
406
|
+
# # => <option value="0">PHP</option>
|
407
|
+
#
|
408
|
+
#
|
409
|
+
# @api public
|
410
|
+
def tag(*args, &block)
|
411
|
+
name = args.first
|
412
|
+
attrs = args.last.is_a?(::Hash) ? args.pop : {}
|
413
|
+
newline = attrs[:newline] # save before it gets tainted
|
414
|
+
|
415
|
+
tag_content = block_given? ? capture_html(&block) : args[1] # content
|
416
|
+
|
417
|
+
if self_closing_tag?(name)
|
418
|
+
tag_html = self_closing_tag(name, attrs)
|
419
|
+
else
|
420
|
+
tag_html = open_tag(name, attrs) + tag_contents_for(name, tag_content, newline) + closing_tag(name)
|
421
|
+
end
|
422
|
+
block_is_template?(block) ? concat_content(tag_html) : tag_html
|
423
|
+
end
|
424
|
+
|
425
|
+
|
426
|
+
private
|
427
|
+
|
428
|
+
##
|
429
|
+
# Return an opening tag of _name_, with _attrs_.
|
430
|
+
#
|
431
|
+
# @api private
|
432
|
+
def open_tag(name, attrs = {})
|
433
|
+
"<#{name}#{normalize_html_attributes(attrs)}>"
|
434
|
+
end
|
435
|
+
|
436
|
+
##
|
437
|
+
# Return closing tag of _name_.
|
438
|
+
#
|
439
|
+
# @api private
|
440
|
+
def closing_tag(name)
|
441
|
+
"</#{name}>#{add_newline?}"
|
442
|
+
end
|
443
|
+
|
444
|
+
##
|
445
|
+
# Creates a self closing tag. Like <br/> or <img src="..."/>
|
446
|
+
#
|
447
|
+
# ==== Options
|
448
|
+
# +name+ : the name of the tag to create
|
449
|
+
# +attrs+ : a hash where all members will be mapped to key="value"
|
450
|
+
#
|
451
|
+
# @api private
|
452
|
+
def self_closing_tag(name, attrs = {})
|
453
|
+
newline = (attrs[:newline].nil?) ? nil : attrs.delete(:newline)
|
454
|
+
"<#{name}#{normalize_html_attributes(attrs)}#{is_xhtml?}#{add_newline?(newline)}"
|
455
|
+
end
|
456
|
+
|
457
|
+
##
|
458
|
+
# Based upon the context, wraps the tag content in '\n' (newlines)
|
459
|
+
#
|
460
|
+
# ==== Examples
|
461
|
+
#
|
462
|
+
# tag_contents_for(:div, 'content', nil)
|
463
|
+
# # => <div>\ncontent\n</div>
|
464
|
+
#
|
465
|
+
# tag_contents_for(:div, 'content', false)
|
466
|
+
# # => <div>content</div>
|
467
|
+
#
|
468
|
+
# Single line tag
|
469
|
+
# tag_contents_for(:option, 'content', true)
|
470
|
+
# # => <option...>\ncontent\n</option>
|
471
|
+
#
|
472
|
+
# @api private
|
473
|
+
def tag_contents_for(name, content, newline = nil )
|
474
|
+
if multi_line_tag?(name)
|
475
|
+
"#{add_newline?(newline)}#{content.to_s}#{add_newline?(newline)}"
|
476
|
+
elsif single_line_tag?(name) && newline === true
|
477
|
+
"#{add_newline?(newline)}#{content.to_s}#{add_newline?(newline)}"
|
478
|
+
else
|
479
|
+
content.to_s
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
##
|
484
|
+
# Normalize _attrs_, replacing boolean keys
|
485
|
+
# with their mirrored values.
|
486
|
+
#
|
487
|
+
# @api private
|
488
|
+
def normalize_html_attributes(attrs = {})
|
489
|
+
return if attrs.blank?
|
490
|
+
attrs.delete(:newline) # remove newline from attributes
|
491
|
+
attrs.each do |name, value|
|
492
|
+
if boolean_attribute?(name)
|
493
|
+
value === true ? attrs[name] = name : attrs.delete(name)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
return attrs.empty? ? '' : ' ' + attrs.to_html_attributes
|
497
|
+
end
|
498
|
+
|
499
|
+
##
|
500
|
+
# Check if _name_ is a boolean attribute.
|
501
|
+
#
|
502
|
+
# @api private
|
503
|
+
def boolean_attribute?(name)
|
504
|
+
BOOLEAN_ATTRIBUTES.include?(name.to_s)
|
505
|
+
end
|
506
|
+
|
507
|
+
##
|
508
|
+
# Check if tag _name_ is a self-closing tag.
|
509
|
+
#
|
510
|
+
# @api private
|
511
|
+
def self_closing_tag?(name)
|
512
|
+
SELF_CLOSING_TAGS.include?(name.to_s)
|
513
|
+
end
|
514
|
+
|
515
|
+
##
|
516
|
+
# Check if tag _name_ is a single line tag.
|
517
|
+
#
|
518
|
+
# @api private
|
519
|
+
def single_line_tag?(name)
|
520
|
+
SINGLE_LINE_TAGS.include?(name.to_s)
|
521
|
+
end
|
522
|
+
|
523
|
+
##
|
524
|
+
# Check if tag _name_ is a multi line tag.
|
525
|
+
#
|
526
|
+
# @api private
|
527
|
+
def multi_line_tag?(name)
|
528
|
+
MULTI_LINE_TAGS.include?(name.to_s)
|
529
|
+
end
|
530
|
+
|
531
|
+
##
|
532
|
+
# Returns '>' or ' />' based on the output format used,
|
533
|
+
# ie: HTML vs XHTML
|
534
|
+
#
|
535
|
+
# @api private
|
536
|
+
def is_xhtml?
|
537
|
+
settings.tags_output_format_is_xhtml ? " />" : ">"
|
538
|
+
end
|
539
|
+
|
540
|
+
##
|
541
|
+
#
|
542
|
+
#
|
543
|
+
# @api private
|
544
|
+
def add_newline?(add_override=nil)
|
545
|
+
add = (add_override.nil?) ? settings.tags_add_newlines_after_tags : add_override
|
546
|
+
add === true ? "\n" : ''
|
547
|
+
end
|
548
|
+
|
549
|
+
end #/ Helpers
|
550
|
+
|
551
|
+
##
|
552
|
+
# Registers these Extensions:
|
553
|
+
#
|
554
|
+
# * Sinatra::OutputBuffer
|
555
|
+
#
|
556
|
+
# Default Settings:
|
557
|
+
#
|
558
|
+
# * +:tags_output_format_is_xhtml+ => sets the HTML output format. Default is: +false+
|
559
|
+
#
|
560
|
+
# * +:tags_add_newlines_after_tags+ => sets whether to add new lines to the HTML tags. Default is: +true+
|
561
|
+
#
|
562
|
+
#
|
563
|
+
# @api public
|
564
|
+
def self.registered(app)
|
565
|
+
app.register(Sinatra::OutputBuffer)
|
566
|
+
app.helpers Sinatra::Tags::Helpers
|
567
|
+
|
568
|
+
# set the HTML output formats
|
569
|
+
app.set :tags_output_format_is_xhtml, false
|
570
|
+
# set the HTML output formats
|
571
|
+
app.set :tags_add_newlines_after_tags, true
|
572
|
+
|
573
|
+
## add the extension specific options to those inspectable by :settings_inspect method
|
574
|
+
# provided by the Sinatra::Settings extension
|
575
|
+
if app.respond_to?(:sinatra_settings_for_inspection)
|
576
|
+
%w( tags_add_newlines_after_tags tags_output_format_is_xhtml ).each do |m|
|
577
|
+
app.sinatra_settings_for_inspection << m
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
end #/ self.registered
|
582
|
+
|
583
|
+
end #/ Tags
|
584
|
+
|
585
|
+
register(Sinatra::Tags)
|
586
|
+
|
587
|
+
end #/ Sinatra
|