jig 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/History.txt +5 -0
- data/Manifest.txt +12 -0
- data/README.txt +114 -0
- data/Rakefile +39 -0
- data/lib/jig.rb +903 -0
- data/lib/jig/css.rb +307 -0
- data/lib/jig/xhtml.rb +283 -0
- data/lib/jig/xml.rb +320 -0
- data/test/test_css.rb +143 -0
- data/test/test_jig.rb +513 -0
- data/test/test_xhtml.rb +26 -0
- data/test/test_xml.rb +148 -0
- metadata +67 -0
data/lib/jig/xml.rb
ADDED
@@ -0,0 +1,320 @@
|
|
1
|
+
require 'jig'
|
2
|
+
|
3
|
+
class Jig
|
4
|
+
=begin rdoc
|
5
|
+
Jig::XML is a subclass of Jig designed to simplify construction of
|
6
|
+
strings containing XML text. Several class methods are defined to
|
7
|
+
construct standard XML elements. Unknown class method calls are
|
8
|
+
interpreted as XML element constructions:
|
9
|
+
|
10
|
+
j = Jig::XML.xml
|
11
|
+
j.inspect # => #<Jig: ["<?xml", " version=\"1.0\"", " ?>\n"]>
|
12
|
+
j.to_s # => <?xml version="1.0" ?>\n
|
13
|
+
|
14
|
+
j = Jig::XML.book(Jig::XMLtitle('War and Peace'))
|
15
|
+
j.inspect # => #<Jig: ["<book", nil, ">", "<title", nil, ">\n", "War and Peace", "</title>\n", "</book>\n"]>
|
16
|
+
j.to_s # => <book><title>\nWar and Peace</title>\n </book>
|
17
|
+
|
18
|
+
For most element constructors, arguments to the constructor are inserted as
|
19
|
+
the contents of the element. An optional block argument is inserted as a
|
20
|
+
proc. If there are no arguments and no block, the element is constructed
|
21
|
+
with the default gap, :___, as its contents. XML attributes are specified by
|
22
|
+
passing a Hash as the final argument. See the Attributes section below for
|
23
|
+
a detailed description of attribute processing.
|
24
|
+
Examples:
|
25
|
+
|
26
|
+
X = Jig::XHTML
|
27
|
+
puts X.span('some text') # <span>some text</span>
|
28
|
+
|
29
|
+
puts X.h3('rendered at: ') do # <h3>rendered at: Mon Apr 16 23:02:13 EDT 2007</h3>
|
30
|
+
Time.now
|
31
|
+
end
|
32
|
+
|
33
|
+
j = X.p
|
34
|
+
puts j # <p>\n</p>
|
35
|
+
puts j.plug('Four score...') # <p>Four score...\n</p>
|
36
|
+
|
37
|
+
=Attributes
|
38
|
+
|
39
|
+
A hash passed as a final argument to an XML element constructor is interpreted
|
40
|
+
as potential XML attributes for the element. Each key/value pair is considered
|
41
|
+
seperately. Pairs are processed as follows:
|
42
|
+
* a nil value causes the pair to be silently discarded
|
43
|
+
* a symbol value causes an attribute gap to be inserted into the XML element
|
44
|
+
* a gap value is inserted as is into the XML element attribute area, the key is discarded in this case (XXX)
|
45
|
+
* a proc is inserted as a deferred attribute
|
46
|
+
* a jig is inserted as deferred attribute
|
47
|
+
* any other value is inserted as an attribute with the value converted via #to_s
|
48
|
+
|
49
|
+
=Attribute Gaps
|
50
|
+
|
51
|
+
If an attribute gap remains unfilled it will not appear in the rendered jig. When
|
52
|
+
an attribute gap is filled, the result is processed as described for the key/value
|
53
|
+
pairs of a hash.
|
54
|
+
|
55
|
+
=Deferred Attributes
|
56
|
+
|
57
|
+
A deferred attribute is not finalized until the jig is rendered via #to_s. If a
|
58
|
+
deferred proc evaluates to nil, the attribute pair is silently discarded otherwise
|
59
|
+
the resulting value is converted to a string and an XML attribute is rendered.
|
60
|
+
A deferred jig is rendered and the result used as the XML attribute value.
|
61
|
+
|
62
|
+
X = Jig::XHTML
|
63
|
+
X.div('inner', :class => 'urgent') # => <div class="urgent">inner</div>
|
64
|
+
j = X.div('inner', :class => :class) # => <div>inner</div>
|
65
|
+
j.plug(:class, 'urgent') # => <div class="urgent">inner</div>
|
66
|
+
j.plug(:class, nil) # => <div>inner</div>
|
67
|
+
|
68
|
+
j = X.input(:type => :type) # => <input/>
|
69
|
+
j.plug(:type, "") # => <input type="" />
|
70
|
+
j.plug(:type, 'password') # => <input type="password" />
|
71
|
+
|
72
|
+
css = nil
|
73
|
+
j = X.div('inner', :class => proc { css }) # => <div>inner</div>
|
74
|
+
css = 'urgent'
|
75
|
+
j.to_s # => <div class="header">inner</div>
|
76
|
+
|
77
|
+
color = Jig.new('color: ', { %w{reb blue green}[rand(3)] }
|
78
|
+
j = X.div('inner', :style=> color) # => <div style="color: red">inner</div>
|
79
|
+
j.to_s # => <div style="color: green">inner</div>
|
80
|
+
=end
|
81
|
+
class XML < Jig
|
82
|
+
# Converts _hash_ into attribute value pairs and pushes them
|
83
|
+
# on the end of the jig.
|
84
|
+
def push_hash(hash)
|
85
|
+
push(*hash.map { |k,v| self.class.attribute(k, v) })
|
86
|
+
end
|
87
|
+
protected :push_hash
|
88
|
+
|
89
|
+
class <<self
|
90
|
+
# These elements will have newlines inserted into the default constructions to
|
91
|
+
# increase the readability of the generated XML.
|
92
|
+
Newlines = [:html, :head, :body, :title, :div, :p, :table, :script, :form]
|
93
|
+
Encode = Hash[*%w{& amp " quot > gt < lt}] # :nodoc:
|
94
|
+
Entities = Encode.keys.join # :nodoc:
|
95
|
+
|
96
|
+
# Prepare +aname+ and +value+ for use as an attribute pair in an XML jig:
|
97
|
+
# * If +value+ is nil or false, the empty string is returned.
|
98
|
+
# * If +value+ is a symbol, an attribute gap is returned.
|
99
|
+
# * If +value+ is a gap, the gap is returned.
|
100
|
+
# * If +value+ is a proc, method or jig, the construction of the attribute
|
101
|
+
# is deferred by wrapping it in a proc inside a jig.
|
102
|
+
# * Otherwise, +aname+ and +value+ are converted to strings and rendered as an XML
|
103
|
+
# attribute pair.
|
104
|
+
# Examples:
|
105
|
+
# attribute('value', :firstname) # => Gap.new(:firstname) {...}
|
106
|
+
# attribute('type', 'password') # => 'type="password"'
|
107
|
+
# attribute('type', nil) # => ''
|
108
|
+
# attribute('lastname', 'Einstein') # => 'lastname="Einstein"'
|
109
|
+
# a = attribute('lastname', Jig.new('Einstein')) # => #<Jig: [#<Proc:0x00058624>]>
|
110
|
+
# a.to_s # => 'lastname="Einstein"'
|
111
|
+
# b = attribute('lastname', Jig.new { }) # => #<Jig: [#<Proc:0x000523dc]>
|
112
|
+
# b.to_s # => ''
|
113
|
+
# c = attribute('lastname', Jig.new {""}) # => #<Jig: [#<Proc:0x00055a3c]>
|
114
|
+
# c.to_s # => 'lastname=""'
|
115
|
+
def attribute(aname, value)
|
116
|
+
case value
|
117
|
+
when nil, false
|
118
|
+
""
|
119
|
+
when Symbol
|
120
|
+
Gap.new(value) { |fill| attribute(aname, fill) }
|
121
|
+
when Gap
|
122
|
+
value
|
123
|
+
when Proc, Method
|
124
|
+
Jig.new { attribute(aname, value.call) }
|
125
|
+
when Jig
|
126
|
+
Jig.new { attribute(aname, value.to_s) }
|
127
|
+
else
|
128
|
+
" #{aname}=\"#{value}\""
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# In addition to the parsing done by Jig.parse, Jig::XML.parse recognizes and
|
133
|
+
# constructs attribute gaps from text of the form: %{=attribute,gapname=}
|
134
|
+
# Jig.parse("<input%{=type,itype} />").plug(:itype, 'password') # <input type="password" />
|
135
|
+
def parse(*)
|
136
|
+
super
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns a new string with <, >, and & converted to their HTML entity codes.
|
140
|
+
def escape(target)
|
141
|
+
new(target.to_s.gsub(/[#{Entities}]/) {|m| "&#{Encode[m]};" })
|
142
|
+
end
|
143
|
+
|
144
|
+
# Extend Jig.parse to recognize attribute gaps as %{=attrname,gapname=}.
|
145
|
+
# An attribute gap is returned.
|
146
|
+
def parse_other(delim, stripped)
|
147
|
+
if delim == '='
|
148
|
+
if stripped =~ /\A(.*),(.*)\z/
|
149
|
+
new({ $1 => $2.to_sym})
|
150
|
+
else
|
151
|
+
raise ArgumentError, "invalid gap syntax: #{quoted}"
|
152
|
+
end
|
153
|
+
else
|
154
|
+
super
|
155
|
+
end
|
156
|
+
end
|
157
|
+
private :parse_other
|
158
|
+
|
159
|
+
ATTRS = Gap::ATTRS # :nodoc:
|
160
|
+
ATTRS_GAP = Gap.new(ATTRS) { |h| h && h.map { |k,v| Jig::XML.attribute(k, v) } } # :nodoc:
|
161
|
+
|
162
|
+
Element_Cache = {} # :nodoc:
|
163
|
+
# Construct a generic XML element with two gaps:
|
164
|
+
# * +:__a+ filters a hash into an XML attribute list
|
165
|
+
# * +:___+ which is a default gap
|
166
|
+
# Jig::XML._element('div') # => #<Jig: ["<div", :"__a{}", ">\n", :___, "</div>\n"]>
|
167
|
+
def _element(tag)
|
168
|
+
Element_Cache[tag] ||= begin
|
169
|
+
whitespace = Newlines.include?(tag.to_sym) && "\n" || ""
|
170
|
+
new("<#{tag}".freeze, ATTRS_GAP, ">#{whitespace}".freeze, GAP, "</#{tag}>\n".freeze).freeze
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Construct a generic XML element with four gaps:
|
175
|
+
# * +:__a+ filters a hash into an XML attribute list
|
176
|
+
# * +:___+ which is a default gap
|
177
|
+
# * +tag+ which acts a placeholder for the element's opening and closing tag
|
178
|
+
# Jig::XML._element(:tag) # => #<Jig: ["<", :tag, :"__a{}", ">\n", :___, "</", :tag, ">\n"]>
|
179
|
+
def _anonymous(tag) # :nodoc:
|
180
|
+
whitespace = Newlines.include?(tag.to_sym) && "\n" || ""
|
181
|
+
new("<", tag.to_sym, ATTRS_GAP, ">#{whitespace}", GAP, "</", tag.to_sym, ">\n")
|
182
|
+
end
|
183
|
+
|
184
|
+
Empty_Element_Cache = {} # :nodoc:
|
185
|
+
# Construct an XML empty element with one gap:
|
186
|
+
# * +:__a+ filters a hash into an XML attribute list
|
187
|
+
# * +:___+ which is a default gap
|
188
|
+
# Jig::XML._element!('br') # => #<Jig: ["<br", :"__a{}", "/>"]>
|
189
|
+
def _element!(tag) # :nodoc:
|
190
|
+
Empty_Element_Cache[tag] ||= begin
|
191
|
+
new("<#{tag}".freeze, ATTRS_GAP, "/>\n".freeze).freeze
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Construct an HTML element using the method name as the element tag.
|
196
|
+
# If a method ends with '!', the element is constructed as an empty element.
|
197
|
+
# If a method ends with '?', the element is constructed as an anonymous element.
|
198
|
+
# If a method ends with '_', it is stripped and the result used as a the tag.
|
199
|
+
# If a method contains an '_', it is converted to a ':' to provide XML namespace tags.
|
200
|
+
# If a method contains an '__', it is converted to a single '_'.
|
201
|
+
#
|
202
|
+
# Jig::XML.div.to_s # => "<div></div>"
|
203
|
+
# Jig::XML.div_.to_s # => "<div></div>"
|
204
|
+
# Jig::XML.br!to_s # => <br />"
|
205
|
+
# Jig::XML.heading? # => Jig["<", :heading, ">", :___, "</", :heading, ">"]
|
206
|
+
# Jig::XML.xhtml_h1 # => "<xhtml:h1></xhtml:h1>"
|
207
|
+
# Jig::XML.xhtml__h1 # => "<xhtml_h1></xhtml_h1>"
|
208
|
+
def method_missing(symbol, *args, &block)
|
209
|
+
constructor = :element
|
210
|
+
text = symbol.to_s
|
211
|
+
if text =~ /!\z/
|
212
|
+
text.chop!
|
213
|
+
constructor = :element!
|
214
|
+
elsif text =~ /\?\z/
|
215
|
+
text.chop!
|
216
|
+
constructor = :anonymous
|
217
|
+
end
|
218
|
+
if text =~ /_$/ # alternate for clashes with existing methods
|
219
|
+
text.chop!
|
220
|
+
end
|
221
|
+
if text =~ /_/
|
222
|
+
# Single _ gets converted to : for XML name spaces
|
223
|
+
# Double _ gets converted to single _
|
224
|
+
text = text.gsub(/([^_])_([^_])/){|x| "#{$1}:#{$2}"}.gsub(/__/, '_')
|
225
|
+
end
|
226
|
+
send(constructor, text, *args, &block)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Construct an anonymous XML element. The single argument provides a name for a
|
230
|
+
# gap that replaces the XML start and end tags. Use plug to replace the gaps
|
231
|
+
# with an actual tag.
|
232
|
+
#
|
233
|
+
# a = anonymous(:heading) # => #<Jig: ["<", :heading, ">", :___, "</", :heading, ">\n"]>
|
234
|
+
# b = a.plug(:heading, 'h1') # => #<Jig: ["<", "h1", ">", :___, "</", "h1", ">\n"]>
|
235
|
+
# b.plug('contents') # => #<Jig: ["<", "h1", ">", "contents", "</", "h1", ">\n"]>
|
236
|
+
def anonymous(tag='div', *args)
|
237
|
+
attrs = args.last.respond_to?(:fetch) && args.pop || nil
|
238
|
+
args.push(lambda{|*x| yield(*x) }) if block_given?
|
239
|
+
args.push GAP if args.empty?
|
240
|
+
_anonymous(tag).plug(ATTRS => attrs, GAP => args)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Construct a standard XML element with +tag+ as the XML tag
|
244
|
+
# and a default gap for the contents of the element.
|
245
|
+
# Jig::XML.element('h1') # => #<Jig: ["<h1", ">", :___, "</h1>\n"]>
|
246
|
+
# Jig::XML.element('p', :class => 'body') # => #<Jig: ["<p", "class=\"body\"", ">", :___, "</h1>\n"]>
|
247
|
+
def element(tag='div', *args)
|
248
|
+
attrs = args.last.respond_to?(:fetch) && args.pop || nil
|
249
|
+
args.push(lambda(&Proc.new)) if block_given?
|
250
|
+
args.push GAP if args.empty?
|
251
|
+
_element(tag).plug(ATTRS => attrs, GAP => args)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Construct a standard XML empty element with _tag_ as the XML tag.
|
255
|
+
#
|
256
|
+
# Jig::XHTML.element!('br') # => '<br />'
|
257
|
+
#
|
258
|
+
# h = { :name => 'year', :maxsize => 4, :type => :type }
|
259
|
+
#
|
260
|
+
# j = Jig::XHTML.element!('input', h) # => '<input name="year" maxsize="4"/>'
|
261
|
+
# j.plug(:type => 'hidden') # => '<input name="year" maxsize="4" type="hidden"/>'
|
262
|
+
def element!(tag, *args)
|
263
|
+
attrs = args.last.respond_to?(:fetch) && args.pop || nil
|
264
|
+
_element!(tag).plug(ATTRS => attrs, GAP => nil)
|
265
|
+
end
|
266
|
+
|
267
|
+
# Construct an XML declaration tag.
|
268
|
+
#
|
269
|
+
# Jig::XML.xml # => '<?xml version="1.0">'
|
270
|
+
# Jig::XML.xml(:lang => 'jp') # => '<?xml version="1.0" lang="jp">'
|
271
|
+
def xml(*args)
|
272
|
+
attrs = { :version => '1.0' }
|
273
|
+
attrs.merge!(args.pop) if args.last.respond_to?(:fetch)
|
274
|
+
args.push(lambda{|*x| yield(*x) }) if block_given?
|
275
|
+
new("<?xml", attrs, " ?>\n", *args)
|
276
|
+
end
|
277
|
+
|
278
|
+
Cache = {} # :nodoc:
|
279
|
+
# Construct a CDATA block
|
280
|
+
#
|
281
|
+
# Jig::XML.cdata('This data can have < & >')
|
282
|
+
#
|
283
|
+
# <![CDATA[
|
284
|
+
# This data can have < & > ]]>
|
285
|
+
def cdata(*args)
|
286
|
+
args.push(lambda{|*x| yield(*x) }) if block_given?
|
287
|
+
args.push GAP if args.empty?
|
288
|
+
jig = (Cache[:cdata] ||= new("<![CDATA[\n".freeze, GAP, " ]]>\n".freeze).freeze)
|
289
|
+
jig.plug(GAP, *args)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Construct an XML comment element.
|
293
|
+
#
|
294
|
+
# Jig::XML.comment("This is a comment")
|
295
|
+
#
|
296
|
+
# \<!-- This is a comment -->
|
297
|
+
def comment(*args)
|
298
|
+
args.push(lambda{|*x| yield(*x) }) if block_given?
|
299
|
+
args.push GAP if args.empty?
|
300
|
+
jig = (Cache[:comment] ||= new("<!-- ".freeze, GAP, " -->\n".freeze).freeze)
|
301
|
+
jig.plug(GAP, *args)
|
302
|
+
end
|
303
|
+
|
304
|
+
# Construct a multiline XML comment element.
|
305
|
+
#
|
306
|
+
# Jig::XML.comment("first line\nsecond line")
|
307
|
+
#
|
308
|
+
# <!--
|
309
|
+
# first line
|
310
|
+
# second line
|
311
|
+
# -->
|
312
|
+
def comments(*args)
|
313
|
+
args.push(lambda{|*x| yield(*x) }) if block_given?
|
314
|
+
args.push GAP if args.empty?
|
315
|
+
args.push "\n"
|
316
|
+
comment("\n", *args)
|
317
|
+
end
|
318
|
+
end # class <<self
|
319
|
+
end # class XML
|
320
|
+
end # module Jig
|
data/test/test_css.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
|
2
|
+
require 'jig'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'test/jig'
|
5
|
+
|
6
|
+
CSS = Jig::CSS
|
7
|
+
|
8
|
+
class CSS
|
9
|
+
class TestCSS < Test::Unit::TestCase
|
10
|
+
include Asserts
|
11
|
+
def setup
|
12
|
+
@div = CSS.new('div')
|
13
|
+
@gaps = [:__s, :__ds, :__de]
|
14
|
+
end
|
15
|
+
def test_default
|
16
|
+
assert_as_string('* {}', CSS.new, 'no selector')
|
17
|
+
assert_equal(@gaps, CSS.new.gaps, 'two gaps with new rule')
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_new
|
21
|
+
assert_as_string('div {}', CSS.new('div'), 'type selector')
|
22
|
+
assert_as_string('div p {}', CSS.new('div p'), 'string as selector')
|
23
|
+
assert_equal(@gaps, CSS.new('div').gaps, 'selector and declarations gaps available')
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_declarations
|
27
|
+
assert_as_string('div {color: red; }', CSS.new('div', :color => 'red'), 'explicit plist')
|
28
|
+
red = CSS.new('div') | {:color => 'red'}
|
29
|
+
assert_as_string('div {color: red; }', red, 'added plist')
|
30
|
+
assert_equal(@gaps, red.gaps, 'plist leaves gaps')
|
31
|
+
background, color = 'background: olive; ', 'color: red; '
|
32
|
+
assert_as_string(/div \{(#{color}#{background}|#{background}#{color})\}/,
|
33
|
+
@div | {:color => 'red', :background => 'olive'},
|
34
|
+
'added declarations')
|
35
|
+
assert_as_string('div {color: red; background: olive; }',
|
36
|
+
@div | {:color => 'red'} | {:background => 'olive'},
|
37
|
+
'two declarations')
|
38
|
+
|
39
|
+
blue = CSS.new('div') | {:color => 'blue'}
|
40
|
+
assert_as_string('div {color: blue; }', blue, 'declaration list merge via &')
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_open?
|
44
|
+
assert(CSS.div.open?, 'gaps remain')
|
45
|
+
assert(CSS.div.plug.closed?, 'no gaps')
|
46
|
+
assert(!CSS.div.plug.open?, 'no gaps')
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_method_missing
|
50
|
+
assert_as_string('div {}', CSS.div, 'unknown method generates type selector')
|
51
|
+
assert_equal(@gaps, CSS.div.gaps, 'unknown method generates new jig')
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_universal_selector
|
55
|
+
assert_as_string('* {}', CSS.new, 'universal selector')
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_descendent_combinator
|
59
|
+
assert_as_string('h1 li {}', CSS.h1 >> CSS.li , 'descendent combinator')
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_child_combinator
|
63
|
+
assert_as_string('div > h1 {}', CSS.div > CSS.h1, 'child combinator')
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_sibling_combinator
|
67
|
+
assert_as_string('div + h1 {}', CSS.div + CSS.h1, 'sibling combinator')
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_id_selector
|
71
|
+
assert_as_string('div#home {}', CSS.div * 'home', 'id selector')
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_pseudo_selector
|
75
|
+
assert_as_string('div:home {}', CSS.div/'home', 'pseudo selector')
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_class_selector
|
79
|
+
assert_as_string('h1.urgent {}', CSS.h1.urgent, 'class selector')
|
80
|
+
end
|
81
|
+
def test_attribute_selector
|
82
|
+
assert_as_string('h1[class] {}', CSS.h1['class'], 'attribute selector')
|
83
|
+
end
|
84
|
+
def test_exact_attribute_selector
|
85
|
+
assert_as_string('h1[class="urgent"] {}', CSS.h1['class' => "urgent"], 'exact attribute selector')
|
86
|
+
end
|
87
|
+
def test_partial_attribute_selector
|
88
|
+
assert_as_string('h1[class~="urgent"] {}', CSS.h1['class' => /urgent/], 'partial attribute selector')
|
89
|
+
end
|
90
|
+
def test_language_attribute_selector
|
91
|
+
assert_as_string('h1[lang|="lc"] {}', CSS.h1[:lang => /lc/], 'language attribute selector')
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_selector_list
|
95
|
+
assert_as_string('h1, h2 {}', CSS.h1.merge(CSS.h2), 'selector list')
|
96
|
+
assert_as_string('h1, h2 {}', CSS.h1 | CSS.h2, 'selector list operator')
|
97
|
+
assert_as_string('h1, h2, h3 {}', CSS.h1.merge(CSS.h2).merge(CSS.h3), 'selector list')
|
98
|
+
assert_as_string('*, h1 {}', CSS.new | CSS.h1, 'adding to the default rule')
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_units
|
102
|
+
units = [:in, :cm, :mm, :pt, :pc, :em, :ex, :px]
|
103
|
+
units.each {|u| assert_equal("1#{u}", 1.send(u)) }
|
104
|
+
assert_equal("50%", 50.pct)
|
105
|
+
assert_equal("50.00%", 0.5.pct)
|
106
|
+
assert_equal("99.99%", 0.9999.pct)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_declarations_merge
|
110
|
+
div = CSS.div
|
111
|
+
h1 = CSS.h1(:color => 'red')
|
112
|
+
result = div * h1
|
113
|
+
args = [[:>>, ' ', h1], [:>, ' > ', h1], [:+, ' + ', h1]]
|
114
|
+
args.each { |op1, text, arg2|
|
115
|
+
assert_as_string("div#{text}h1 {color: red; }", div.send(op1, arg2))
|
116
|
+
}
|
117
|
+
args = [[:*, '#', 'header'], [:/, ':', 'first-child']]
|
118
|
+
args.each { |op1, text, arg2|
|
119
|
+
assert_as_string("div#{text}#{arg2} {}", div.send(op1, arg2))
|
120
|
+
}
|
121
|
+
assert_as_string("div[onclick] {color: red; }", div['onclick'] |{:color => 'red'})
|
122
|
+
assert_as_string("div[onclick] {color: red; background: blue; }", div['onclick'].|(:color => 'red')|{:background => 'blue'})
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_extract_selector
|
126
|
+
assert_as_string("div", CSS.div.selector)
|
127
|
+
assert_as_string("div, h1", (CSS.div | CSS.h1).selector)
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_extract_declarations
|
131
|
+
assert_as_string("", CSS.div.declarations)
|
132
|
+
assert_as_string("color: red; ", (CSS.div |{'color' => 'red'}).declarations)
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_declartion_with_gaps
|
136
|
+
redgap = CSS.div | {'color' => :red }
|
137
|
+
assert(redgap.gaps.include?(:red))
|
138
|
+
assert_as_string('div {}', redgap , 'gap for property value')
|
139
|
+
assert_as_string('div {color: red; }', redgap.plug(:red, 'red'), 'plug gap')
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|