Q 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +18 -0
- data/README.rdoc +37 -0
- data/Rakefile +9 -0
- data/lib/q.rb +99 -0
- data/lib/q/version.rb +3 -0
- data/q.gemspec +24 -0
- data/test/q_test.rb +34 -0
- data/test/teststrap.rb +11 -0
- metadata +112 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
= Q
|
2
|
+
|
3
|
+
== Programmatic generation of HTML
|
4
|
+
|
5
|
+
Often, within helpers, I want to output some HTML without resorting to a templating language. Here is my attempt to do so:
|
6
|
+
|
7
|
+
=> Q {_div { __'this my divs contents' }}
|
8
|
+
<= <div>This is my divs contents</div>
|
9
|
+
|
10
|
+
Or, with attributes
|
11
|
+
|
12
|
+
=> Q {_div(:id => 'test') { __'this my divs contents' }}
|
13
|
+
<= <div id="test">This is my divs contents</div>
|
14
|
+
|
15
|
+
Of course, this is all escaped
|
16
|
+
|
17
|
+
=> Q {_div { __'<tag! tag!>' }}
|
18
|
+
<= <div><tag! tag!></div>
|
19
|
+
|
20
|
+
Or, you can turn that off
|
21
|
+
|
22
|
+
=> Q {_div { __no_escape'<tag! tag!>' }}
|
23
|
+
<= <div><tag! tag!></div>
|
24
|
+
|
25
|
+
Or, you can indent too
|
26
|
+
|
27
|
+
=> Q(:indent => 2) {_div { __'some awesome contents' }}
|
28
|
+
<= <div>
|
29
|
+
Some awesome contents
|
30
|
+
</div>
|
31
|
+
|
32
|
+
And, you can one-line those values
|
33
|
+
|
34
|
+
=> Q {_div 'some awesome contents' }}
|
35
|
+
<= <div>Some awesome contents</div>
|
36
|
+
|
37
|
+
Nest as much as you want, and use the QX method for XHTML based output.
|
data/Rakefile
ADDED
data/lib/q.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'escape_utils'
|
2
|
+
|
3
|
+
def Q(opts = nil, &blk)
|
4
|
+
Q.new(opts, &blk).to_s
|
5
|
+
end
|
6
|
+
|
7
|
+
def QX(opts, &blk)
|
8
|
+
Q(opts ? opts.merge(:xml_compat => true) : nil, &blk).to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
class Q
|
12
|
+
VALID_TAGS = %w(a abbr acronym address area b base bdo big blockquote body br button caption cite code col colgroup dd del dfn div dl dt em fieldset form h1 h2 h3 h4 h5 h6 head html hr i img input ins kbd label legend li link map meta noscript object ol optgroup option p param pre q samp script select small span strong style sub sup table tbody td textarea tfoot th thead title tr tt ul var article aside audio canvas command datalist details embed figcaption figure footer header hgroup keygen mark meter nav output progress rp rt ruby section source summary time video)
|
13
|
+
def initialize(opts = nil, &block)
|
14
|
+
@__xml_compat = opts && opts.key?(:xml) ? opts[:xml] : false
|
15
|
+
@__indent = opts && opts.key?(:indent) ? opts[:indent] : false
|
16
|
+
@__default_doctype = opts && opts.key?(:doctype) ? opts[:doctype] : (@__xml_compat ? :xhtml_11 : :html5)
|
17
|
+
@__out = ''
|
18
|
+
@__level = 0
|
19
|
+
instance_eval(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
VALID_TAGS.each do |tag|
|
23
|
+
module_eval "
|
24
|
+
def _#{tag}(value = nil, opts = nil, &blk)
|
25
|
+
_(#{tag.inspect}, value, opts, &blk)
|
26
|
+
end
|
27
|
+
"
|
28
|
+
end
|
29
|
+
|
30
|
+
def div(class_or_id, value = nil, opts = nil, &blk)
|
31
|
+
attr_name, attr_val = case class_or_id[0]
|
32
|
+
when ?.
|
33
|
+
['class', class_or_id[1, class_or_id.size - 1]]
|
34
|
+
when ?#
|
35
|
+
['class', class_or_id[1, class_or_id.size - 1]]
|
36
|
+
end
|
37
|
+
_('div', value, opts ? {attr_name => attr_val}.merge(opts) : {attr_name => attr_val}, &blk)
|
38
|
+
end
|
39
|
+
|
40
|
+
def _doctype(type = @__default_doctype)
|
41
|
+
@__out << case type
|
42
|
+
when :html_401_strict then '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
|
43
|
+
when :html_401_transitional then '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
|
44
|
+
when :html_401_frameset then '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
|
45
|
+
when :xhtml_10_strict then '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
|
46
|
+
when :xhtml_10_transitional then '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
47
|
+
when :xhtml_10_frameset then '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
|
48
|
+
when :xhtml_11 then '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
|
49
|
+
when :xhtml_11_basic then '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
|
50
|
+
when :html5 then '<!DOCTYPE HTML>'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def _(tagname, value = nil, opts = nil, &blk)
|
55
|
+
if @__indent and !@__level.zero?
|
56
|
+
@__out << "\n"
|
57
|
+
@__level.times {__(@__indent)}
|
58
|
+
end
|
59
|
+
if value.is_a?(Hash)
|
60
|
+
opts = value
|
61
|
+
value = nil
|
62
|
+
end
|
63
|
+
@__out << "<#{tagname}"
|
64
|
+
if opts and !opts.empty?
|
65
|
+
opts.each do |k,v|
|
66
|
+
@__out << " #{EscapeUtils.escape_html(k.to_s)}=\"#{EscapeUtils.escape_html(v.to_s)}\""
|
67
|
+
end
|
68
|
+
end
|
69
|
+
if blk or value
|
70
|
+
@__out << '>'
|
71
|
+
__(value) if value
|
72
|
+
@__level += 1
|
73
|
+
instance_eval(&blk) if blk
|
74
|
+
@__level -= 1
|
75
|
+
if @__indent && blk
|
76
|
+
@__out << "\n"
|
77
|
+
@__level.times {__(@__indent)}
|
78
|
+
end
|
79
|
+
@__out << "</#{tagname}>"
|
80
|
+
else
|
81
|
+
@__out << (@__xml_compat ? '/>' : '>')
|
82
|
+
end
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def __no_escape(text)
|
87
|
+
@__out << text
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def __(text)
|
92
|
+
__no_escape EscapeUtils.escape_html(text)
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
@__out
|
98
|
+
end
|
99
|
+
end
|
data/lib/q/version.rb
ADDED
data/q.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
3
|
+
require 'q/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "Q"
|
7
|
+
s.version = Q::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = []
|
10
|
+
s.email = []
|
11
|
+
s.homepage = "http://rubygems.org/gems/q"
|
12
|
+
s.summary = "Fast, fun, east HTML generation from Ruby"
|
13
|
+
s.description = "Fast, fun, east HTML generation from Ruby"
|
14
|
+
|
15
|
+
s.required_rubygems_version = ">= 1.3.6"
|
16
|
+
s.rubyforge_project = "Q"
|
17
|
+
|
18
|
+
s.add_dependency 'escape_utils', ">= 0.1.5"
|
19
|
+
s.add_development_dependency "bundler", ">= 1.0.0.rc.3"
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
s.executables = `git ls-files`.split("\n").select{|f| f =~ /^bin/}
|
23
|
+
s.require_path = 'lib'
|
24
|
+
end
|
data/test/q_test.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),'teststrap')
|
2
|
+
|
3
|
+
context "Q" do
|
4
|
+
|
5
|
+
context "generating div" do
|
6
|
+
setup { Q{_div{}} }
|
7
|
+
asserts_topic.equals("<div></div>")
|
8
|
+
end
|
9
|
+
|
10
|
+
context "generating div with contents" do
|
11
|
+
setup { Q{_div{ __"<this is contents>"}} }
|
12
|
+
asserts_topic.equals("<div><this is contents></div>")
|
13
|
+
end
|
14
|
+
|
15
|
+
context "generating div with non-escaped contents" do
|
16
|
+
setup { Q{_div{ __no_escape"<this is contents>"}} }
|
17
|
+
asserts_topic.equals("<div><this is contents></div>")
|
18
|
+
end
|
19
|
+
|
20
|
+
context "generating nested HTML" do
|
21
|
+
setup { Q{_div{ _span{__'this is awesome'}}} }
|
22
|
+
asserts_topic.equals("<div><span>this is awesome</span></div>")
|
23
|
+
end
|
24
|
+
|
25
|
+
context "generating with indent" do
|
26
|
+
setup { Q(:indent => ' '){_div{ _span{__'this is awesome'}}} }
|
27
|
+
asserts_topic.equals("<div>\n <span>this is awesome\n </span>\n</div>")
|
28
|
+
end
|
29
|
+
|
30
|
+
context "generating divs with attrs" do
|
31
|
+
setup { Q{_div(:id => '"this is my attribute"'){ __"<this is contents>"}} }
|
32
|
+
asserts_topic.equals("<div id=\""this is my attribute"\"><this is contents></div>")
|
33
|
+
end
|
34
|
+
end
|
data/test/teststrap.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'riot'
|
5
|
+
require 'mocha'
|
6
|
+
|
7
|
+
$LOAD_PATH << File.basename(__FILE__)
|
8
|
+
$LOAD_PATH << File.join(File.basename(__FILE__), '..', 'lib')
|
9
|
+
require 'q'
|
10
|
+
|
11
|
+
Riot.reporter = Riot::DotMatrixReporter
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Q
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors: []
|
13
|
+
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-08 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: escape_utils
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 17
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 1
|
33
|
+
- 5
|
34
|
+
version: 0.1.5
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: bundler
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 15424051
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
- 0
|
50
|
+
- rc
|
51
|
+
- 3
|
52
|
+
version: 1.0.0.rc.3
|
53
|
+
type: :development
|
54
|
+
version_requirements: *id002
|
55
|
+
description: Fast, fun, east HTML generation from Ruby
|
56
|
+
email: []
|
57
|
+
|
58
|
+
executables: []
|
59
|
+
|
60
|
+
extensions: []
|
61
|
+
|
62
|
+
extra_rdoc_files: []
|
63
|
+
|
64
|
+
files:
|
65
|
+
- .gitignore
|
66
|
+
- Gemfile
|
67
|
+
- Gemfile.lock
|
68
|
+
- README.rdoc
|
69
|
+
- Rakefile
|
70
|
+
- lib/q.rb
|
71
|
+
- lib/q/version.rb
|
72
|
+
- q.gemspec
|
73
|
+
- test/q_test.rb
|
74
|
+
- test/teststrap.rb
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://rubygems.org/gems/q
|
77
|
+
licenses: []
|
78
|
+
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
hash: 23
|
99
|
+
segments:
|
100
|
+
- 1
|
101
|
+
- 3
|
102
|
+
- 6
|
103
|
+
version: 1.3.6
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project: Q
|
107
|
+
rubygems_version: 1.3.7
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Fast, fun, east HTML generation from Ruby
|
111
|
+
test_files: []
|
112
|
+
|