hexp 0.2.0 → 0.3.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.
- checksums.yaml +15 -0
- data/.gitignore +4 -0
- data/.travis.yml +2 -3
- data/.yardopts +1 -0
- data/Gemfile.devtools +17 -24
- data/Gemfile.lock +77 -82
- data/{LICENSE.md → LICENSE} +0 -0
- data/README.md +20 -10
- data/Rakefile +17 -1
- data/bench/node/rewrite_bench.rb +23 -0
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/reek.yml +6 -0
- data/config/yardstick.yml +7 -2
- data/hexp.gemspec +6 -5
- data/lib/hexp.rb +26 -18
- data/lib/hexp/builder.rb +54 -35
- data/lib/hexp/css_selector.rb +124 -25
- data/lib/hexp/css_selector/parser.rb +45 -2
- data/lib/hexp/css_selector/sass_parser.rb +16 -0
- data/lib/hexp/dom.rb +2 -0
- data/lib/hexp/dsl.rb +20 -21
- data/lib/hexp/errors.rb +2 -2
- data/lib/hexp/list.rb +14 -11
- data/lib/hexp/node.rb +89 -38
- data/lib/hexp/node/attributes.rb +43 -26
- data/lib/hexp/node/children.rb +59 -4
- data/lib/hexp/node/css_selection.rb +113 -7
- data/lib/hexp/node/domize.rb +22 -13
- data/lib/hexp/node/normalize.rb +3 -9
- data/lib/hexp/node/pp.rb +13 -9
- data/lib/hexp/node/rewriter.rb +28 -3
- data/lib/hexp/node/{selector.rb → selection.rb} +48 -2
- data/lib/hexp/nokogiri/reader.rb +2 -2
- data/lib/hexp/text_node.rb +1 -1
- data/lib/hexp/version.rb +1 -1
- data/spec/unit/hexp/css_selector/universal_spec.rb +7 -0
- data/spec/unit/hexp/node/domize_spec.rb +12 -0
- data/spec/unit/hexp/node/{selector_spec.rb → selection_spec.rb} +9 -9
- data/spec/unit/hexp/parse_spec.rb +10 -0
- metadata +40 -44
- data/SPEC.md +0 -53
- data/notes +0 -34
@@ -10,35 +10,75 @@ module Hexp
|
|
10
10
|
#
|
11
11
|
# The classes that make up the parse tree largely mimic the ones from SASS,
|
12
12
|
# like CommaSequence, SimpleSequence, Class, Id, etc. By having them in our
|
13
|
-
# own namespace however we can easily add Hexp-specific
|
13
|
+
# own namespace however we can easily add Hexp-specific functionality to them.
|
14
14
|
#
|
15
15
|
class Parser
|
16
|
+
# Initialize the parser with the selector to parse
|
17
|
+
#
|
18
|
+
# @param [String] selector
|
19
|
+
#
|
20
|
+
# @api private
|
16
21
|
def initialize(selector)
|
17
22
|
@selector = selector.freeze
|
18
23
|
end
|
19
24
|
|
25
|
+
# Parse the selector
|
26
|
+
#
|
27
|
+
# @return [Hexp::CssSelector::CommaSequence]
|
28
|
+
#
|
29
|
+
# @api private
|
20
30
|
def parse
|
21
31
|
rewrite_comma_sequence(SassParser.call(@selector))
|
22
32
|
end
|
23
33
|
|
34
|
+
# Parse a CSS selector in one go
|
35
|
+
#
|
36
|
+
# @param [String] selector
|
37
|
+
# @return [Hexp::CssSelector::CommaSequence]
|
38
|
+
#
|
39
|
+
# @api private
|
24
40
|
def self.call(selector)
|
25
41
|
new(selector).parse
|
26
42
|
end
|
27
43
|
|
28
44
|
private
|
29
45
|
|
46
|
+
# Map CommaSequence from the SASS namespace to our own
|
47
|
+
#
|
48
|
+
# @param [Sass::Selector::CommaSequence] comma_sequence
|
49
|
+
# @return [Hexp::CssSelector::CommaSequence]
|
50
|
+
#
|
51
|
+
# @api private
|
30
52
|
def rewrite_comma_sequence(comma_sequence)
|
31
53
|
CommaSequence.new(comma_sequence.members.map{|sequence| rewrite_sequence(sequence)})
|
32
54
|
end
|
33
55
|
|
56
|
+
# Map Sequence from the SASS namespace to our own
|
57
|
+
#
|
58
|
+
# @param [Sass::Selector::Sequence] comma_sequence
|
59
|
+
# @return [Hexp::CssSelector::Sequence]
|
60
|
+
#
|
61
|
+
# @api private
|
34
62
|
def rewrite_sequence(sequence)
|
35
63
|
Sequence.new(sequence.members.map{|simple_sequence| rewrite_simple_sequence(simple_sequence)})
|
36
64
|
end
|
37
65
|
|
66
|
+
# Map SimpleSequence from the SASS namespace to our own
|
67
|
+
#
|
68
|
+
# @param [Sass::Selector::SimpleSequence] comma_sequence
|
69
|
+
# @return [Hexp::CssSelector::SimpleSequence]
|
70
|
+
#
|
71
|
+
# @api private
|
38
72
|
def rewrite_simple_sequence(simple_sequence)
|
39
73
|
SimpleSequence.new(simple_sequence.members.map{|simple| rewrite_simple(simple)})
|
40
74
|
end
|
41
75
|
|
76
|
+
# Map Simple from the SASS namespace to our own
|
77
|
+
#
|
78
|
+
# @param [Sass::Selector::Simple] comma_sequence
|
79
|
+
# @return [Hexp::CssSelector::Simple]
|
80
|
+
#
|
81
|
+
# @api private
|
42
82
|
def rewrite_simple(simple)
|
43
83
|
case simple
|
44
84
|
when ::Sass::Selector::Element # span
|
@@ -50,7 +90,7 @@ module Hexp
|
|
50
90
|
when ::Sass::Selector::Attribute # [href^="http://"]
|
51
91
|
raise "CSS attribute selector flags are curently ignored by Hexp (not implemented)" unless simple.flags.nil?
|
52
92
|
raise "CSS attribute namespaces are curently ignored by Hexp (not implemented)" unless simple.namespace.nil?
|
53
|
-
raise "CSS attribute operator #{simple.operator} not understood by Hexp" unless %w[= ~= ^=].include?(simple.operator) || simple.operator.nil?
|
93
|
+
raise "CSS attribute operator #{simple.operator} not understood by Hexp" unless %w[= ~= ^= |= $= *=].include?(simple.operator) || simple.operator.nil?
|
54
94
|
Attribute.new(
|
55
95
|
simple.name.first,
|
56
96
|
simple.namespace,
|
@@ -58,10 +98,13 @@ module Hexp
|
|
58
98
|
simple.value ? simple.value.first : nil,
|
59
99
|
simple.flags
|
60
100
|
)
|
101
|
+
when ::Sass::Selector::Universal # *
|
102
|
+
Universal.new
|
61
103
|
else
|
62
104
|
raise "CSS selectors containing #{simple.class} are not implemented in Hexp"
|
63
105
|
end
|
64
106
|
|
107
|
+
# As of yet unimplemented
|
65
108
|
# when ::Sass::Selector::Universal # *
|
66
109
|
# when ::Sass::Selector::Parent # & in Sass
|
67
110
|
# when ::Sass::Selector::Interpolation # #{} in Sass
|
@@ -3,10 +3,20 @@ module Hexp
|
|
3
3
|
# A CSS Parser that only knows how to parse CSS selectors
|
4
4
|
#
|
5
5
|
class SassParser < ::Sass::SCSS::CssParser
|
6
|
+
# Initialize the parser with the selector to parse
|
7
|
+
#
|
8
|
+
# @param [String] selector
|
9
|
+
#
|
10
|
+
# @api private
|
6
11
|
def initialize(selector)
|
7
12
|
super(selector, '')
|
8
13
|
end
|
9
14
|
|
15
|
+
# Parse the selector
|
16
|
+
#
|
17
|
+
# @return [Sass::Selector::CommaSequence]
|
18
|
+
#
|
19
|
+
# @api private
|
10
20
|
def parse
|
11
21
|
init_scanner!
|
12
22
|
result = selector_comma_sequence
|
@@ -14,6 +24,12 @@ module Hexp
|
|
14
24
|
result
|
15
25
|
end
|
16
26
|
|
27
|
+
# Parse a CSS selector in one go
|
28
|
+
#
|
29
|
+
# @param [String] selector
|
30
|
+
# @return [Sass::Selector::CommaSequence]
|
31
|
+
#
|
32
|
+
# @api private
|
17
33
|
def self.call(selector)
|
18
34
|
self.new(selector).parse
|
19
35
|
end
|
data/lib/hexp/dom.rb
CHANGED
data/lib/hexp/dsl.rb
CHANGED
@@ -1,26 +1,25 @@
|
|
1
1
|
module Hexp
|
2
|
+
# Make the Hexp::Node DSL available to objects that implement `to_hexp`
|
3
|
+
#
|
4
|
+
# Mixing in {Hexp} has the same effect as mixing in {Hexp::DSL}, and is the
|
5
|
+
# recommended way.
|
6
|
+
#
|
2
7
|
module DSL
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
:
|
12
|
-
:
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
:text,
|
19
|
-
:remove_attr,
|
20
|
-
:set_attributes,
|
21
|
-
].each do |meth|
|
22
|
-
define_method meth do |*args, &blk|
|
23
|
-
to_hexp.public_send(meth, *args, &blk)
|
8
|
+
# The names of methods related to manipulating the list of children of a node
|
9
|
+
CHILDREN_METHODS = Hexp::Node::Children.public_instance_methods.freeze
|
10
|
+
|
11
|
+
# The names of methods related to a node's attributes
|
12
|
+
ATTRIBUTES_METHODS = Hexp::Node::Attributes.public_instance_methods.freeze
|
13
|
+
|
14
|
+
# Methods that are defined directly in {Hexp::Node}
|
15
|
+
NODE_METHODS = Hexp::Node.public_instance_methods(false) - [
|
16
|
+
:to_hexp,
|
17
|
+
:inspect
|
18
|
+
]
|
19
|
+
|
20
|
+
[CHILDREN_METHODS, ATTRIBUTES_METHODS, NODE_METHODS].flatten.each do |method|
|
21
|
+
define_method method do |*args, &blk|
|
22
|
+
to_hexp.public_send(method, *args, &blk)
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
data/lib/hexp/errors.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module Hexp
|
2
|
+
# Base class for exceptions raised by Hexp
|
3
|
+
#
|
2
4
|
Error = Class.new(StandardError)
|
3
5
|
|
4
6
|
# Raised when trying to stick things inside a Hexp where they don't belong
|
@@ -7,7 +9,6 @@ module Hexp
|
|
7
9
|
# Create a new FormatError
|
8
10
|
#
|
9
11
|
# @api private
|
10
|
-
#
|
11
12
|
def initialize(msg = 'You have illegal contents in your Hexp')
|
12
13
|
super
|
13
14
|
end
|
@@ -17,5 +18,4 @@ module Hexp
|
|
17
18
|
#
|
18
19
|
class ParseError < Error
|
19
20
|
end
|
20
|
-
|
21
21
|
end
|
data/lib/hexp/list.rb
CHANGED
@@ -8,10 +8,10 @@ module Hexp
|
|
8
8
|
# @example
|
9
9
|
# Hexp::List.new([H[:p], H[:div]])
|
10
10
|
#
|
11
|
-
# @param
|
11
|
+
# @param [#to_ary] nodes
|
12
|
+
# List of nodes
|
12
13
|
#
|
13
14
|
# @api public
|
14
|
-
#
|
15
15
|
def initialize(nodes)
|
16
16
|
super nodes.to_ary.freeze
|
17
17
|
end
|
@@ -24,11 +24,12 @@ module Hexp
|
|
24
24
|
# Hexp::Node[:hr],
|
25
25
|
# ]
|
26
26
|
#
|
27
|
-
# @param
|
27
|
+
# @param [Array] args
|
28
|
+
# individual nodes
|
28
29
|
#
|
29
30
|
# @return [Hexp::List]
|
30
|
-
# @api public
|
31
31
|
#
|
32
|
+
# @api public
|
32
33
|
def self.[](*args)
|
33
34
|
new(args)
|
34
35
|
end
|
@@ -39,21 +40,21 @@ module Hexp
|
|
39
40
|
# that this is a wrapping class. This is convenient when inspecting nested
|
40
41
|
# hexps, but probably something we want to solve differently.
|
41
42
|
#
|
42
|
-
# @
|
43
|
-
# @return string
|
43
|
+
# @return [String]
|
44
44
|
#
|
45
|
+
# @api private
|
45
46
|
def inspect
|
46
47
|
__getobj__.inspect
|
47
48
|
end
|
48
49
|
|
49
|
-
#
|
50
|
+
# Implicit conversion to Array
|
50
51
|
#
|
51
52
|
# @example
|
52
53
|
# Hexp::List[ H[:p], H[:span] ].to_ary #=> [H[:p], H[:span]]
|
53
54
|
#
|
54
55
|
# @return [Array<Hexp::Node>]
|
55
|
-
# @api public
|
56
56
|
#
|
57
|
+
# @api public
|
57
58
|
def to_ary
|
58
59
|
__getobj__
|
59
60
|
end
|
@@ -72,10 +73,12 @@ module Hexp
|
|
72
73
|
# H[:div, [[:span]]].children.eql? [H[:span]] #=> false
|
73
74
|
# H[:div, [[:span]]].children.eql? Hexp::List[H[:span]] #=> true
|
74
75
|
#
|
75
|
-
# @param
|
76
|
-
#
|
77
|
-
# @return [Boolean]
|
76
|
+
# @param [Object] other
|
77
|
+
# Object to compare with
|
78
78
|
#
|
79
|
+
# @return [true,false]
|
80
|
+
#
|
81
|
+
# @api public
|
79
82
|
def eql?(other)
|
80
83
|
self == other && self.class == other.class
|
81
84
|
end
|
data/lib/hexp/node.rb
CHANGED
@@ -1,5 +1,56 @@
|
|
1
1
|
module Hexp
|
2
|
-
# A Hexp
|
2
|
+
# A +Hexp::Node+ represents a single element in a HTML syntax tree. It
|
3
|
+
# consists of three parts : the {#tag}, the {#attributes} and the {#children}.
|
4
|
+
#
|
5
|
+
# Instances of +Hexp::Node+ are immutable. Because of this all methods that
|
6
|
+
# "alter" the node actually return a new instance, leaving the old one
|
7
|
+
# untouched.
|
8
|
+
#
|
9
|
+
# @example Immutable nodes : the old one is untouched
|
10
|
+
# node = Hexp::Node.new(:div)
|
11
|
+
# node2 = node.add_class('items')
|
12
|
+
# p node # => H[:div]
|
13
|
+
# p node2 # => H[:div, 'class' => 'items']
|
14
|
+
#
|
15
|
+
# The +Hexp::Node+ constructor takes a +Symbol+, a +Hash+ and an +Array+ for
|
16
|
+
# the {#tag}, {#attributes} and {#children} respectively. However the
|
17
|
+
# attributes and children can each be ommitted if they are empty.
|
18
|
+
#
|
19
|
+
# If the node contains a single child node, then it is not necessary to wrap
|
20
|
+
# that child node in an array. Just put the single node in place of the list
|
21
|
+
# of children.
|
22
|
+
#
|
23
|
+
# One can use +H[tag, attrs, children]+ as a
|
24
|
+
# shorthand syntax. Finally one can use {Hexp.build Hexp.build} to construct nodes using
|
25
|
+
# Ruby blocks, not unlike the Builder or Nokogiri gems.
|
26
|
+
#
|
27
|
+
# @example Creating Hexp : syntax alternatives and optional parameters
|
28
|
+
# Hexp::Node.new(:div, class: 'foo')
|
29
|
+
# Hexp::Node.new(:div, {class: 'foo'}, "A text node")
|
30
|
+
# Hexp::Node.new(:div, {class: 'foo'}, ["A text node"])
|
31
|
+
# H[:div, {class: 'foo'}, H[:span, {class: 'big'}, "good stuff"]]
|
32
|
+
# H[:div, {class: 'foo'}, [
|
33
|
+
# H[:span, {class: 'big'}, "good stuff"],
|
34
|
+
# H[:a, {href: '/index'}, "go home"]
|
35
|
+
# ]
|
36
|
+
# ]
|
37
|
+
# Hexp.build { div.strong { "Hello, world!" } }
|
38
|
+
#
|
39
|
+
# Methods that read or alter the attributes Hash are defined in
|
40
|
+
# {Hexp::Node::Attributes}. Methods that read or alter the list of child nodes
|
41
|
+
# are defined in {Hexp::Node::Children}
|
42
|
+
#
|
43
|
+
# == CSS selectors
|
44
|
+
#
|
45
|
+
# When working with large trees of {Hexp::Node} objects, it is convenient to
|
46
|
+
# be able to target specific nodes using CSS selector syntax. Hexp supports a
|
47
|
+
# subset of the CSS 3 selector syntax, see {Hexp::Node::CssSelection} for info
|
48
|
+
# on the supported syntax.
|
49
|
+
#
|
50
|
+
# For changing, replacing or removing specific nodes based on a CSS selector
|
51
|
+
# string, see {Hexp::Node#replace}. To iterate over nodes, see
|
52
|
+
# {Hexp::Node#select}.
|
53
|
+
#
|
3
54
|
class Node
|
4
55
|
include Equalizer.new(:tag, :attributes, :children)
|
5
56
|
extend Forwardable
|
@@ -149,66 +200,66 @@ module Hexp
|
|
149
200
|
false
|
150
201
|
end
|
151
202
|
|
203
|
+
# Return a new node, with a different tag
|
204
|
+
#
|
205
|
+
# @example
|
206
|
+
# H[:div, class: 'foo'].set_tag(:bar)
|
207
|
+
# # => H[:bar, class: 'foo']
|
208
|
+
#
|
209
|
+
# @param tag [#to_sym] The new tag
|
210
|
+
# @return [Hexp::Node]
|
211
|
+
#
|
212
|
+
# @api public
|
152
213
|
def set_tag(tag)
|
153
214
|
H[tag.to_sym, attributes, children]
|
154
215
|
end
|
155
216
|
|
156
|
-
#
|
157
|
-
#
|
158
|
-
# Since nodes are immutable, this is the main entry point for deriving nodes
|
159
|
-
# from others.
|
217
|
+
# Replace nodes in a tree
|
160
218
|
#
|
161
|
-
#
|
162
|
-
#
|
219
|
+
# With a CSS selector string like +"form.checkout"+ you specify the nodes
|
220
|
+
# you want to operate on. These will be passed one by one into the block.
|
221
|
+
# The block returns the {Hexp::Node} that will replace the old node, or it
|
222
|
+
# can replace an +Array+ of nodes to fill the place of the old node.
|
163
223
|
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
# nodes.
|
224
|
+
# Because of this you can add one or more nodes, or remove nodes by
|
225
|
+
# returning an empty array.
|
167
226
|
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
# needs to return one of these
|
227
|
+
# @example Remove all script tags
|
228
|
+
# tree.replace('script') {|_| [] }
|
171
229
|
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
#
|
175
|
-
#
|
230
|
+
# @example Wrap each +<input>+ tag into a +<p>+ tag
|
231
|
+
# tree.replace('input') do |input|
|
232
|
+
# H[:p, input]
|
233
|
+
# end
|
176
234
|
#
|
177
|
-
#
|
178
|
-
# currently referenced node where it is. This is very handy when you only want
|
179
|
-
# to act on certain nodes, just return nothing if you want to do nothing.
|
235
|
+
# @param [String] css_selector
|
180
236
|
#
|
181
|
-
#
|
237
|
+
# @yieldparam [Hexp::Node]
|
238
|
+
# The matching nodes
|
182
239
|
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
# @example
|
186
|
-
# tree.rewrite{|node| [] if node.tag == :script }
|
187
|
-
#
|
188
|
-
# Wrap each <input> tag into a <p> tag
|
240
|
+
# @return [Hexp::Node]
|
241
|
+
# The rewritten tree
|
189
242
|
#
|
190
|
-
# @example
|
191
|
-
# tree.rewrite do |node|
|
192
|
-
# if node.tag == :input
|
193
|
-
# [ H[:p, [ child ] ]
|
194
|
-
# end
|
195
|
-
# end
|
196
|
-
#
|
197
|
-
# @param blk [Proc] The rewrite action
|
198
|
-
# @return [Hexp::Node] The rewritten tree
|
199
243
|
# @api public
|
200
|
-
#
|
201
244
|
def rewrite(css_selector = nil, &block)
|
202
245
|
return Rewriter.new(self, block) if css_selector.nil?
|
203
246
|
CssSelection.new(self, css_selector).rewrite(&block)
|
204
247
|
end
|
205
248
|
alias :replace :rewrite
|
206
249
|
|
250
|
+
# Select nodes based on a css selector
|
251
|
+
#
|
252
|
+
# @param [String] css_selector
|
253
|
+
# @yieldparam [Hexp::Node]
|
254
|
+
#
|
255
|
+
# @return [Hexp::Selector,Hexp::CssSelector]
|
256
|
+
#
|
257
|
+
# @api public
|
207
258
|
def select(css_selector = nil, &block)
|
208
259
|
if css_selector
|
209
260
|
CssSelection.new(self, css_selector).each(&block)
|
210
261
|
else
|
211
|
-
|
262
|
+
Selection.new(self, block)
|
212
263
|
end
|
213
264
|
end
|
214
265
|
|
data/lib/hexp/node/attributes.rb
CHANGED
@@ -14,9 +14,10 @@ module Hexp
|
|
14
14
|
# H[:p, class: 'hello'].attr('id', 'para1') # => H[:p, {"class"=>"hello", "id"=>"para1"}]
|
15
15
|
# H[:p, class: 'hello'].attr('class', nil) # => H[:p]
|
16
16
|
#
|
17
|
+
# @param [Array<#to_s>] args
|
17
18
|
# @return [String|Hexp::Node]
|
18
|
-
# @api private
|
19
19
|
#
|
20
|
+
# @api private
|
20
21
|
def attr(*args)
|
21
22
|
arity = args.count
|
22
23
|
attr_name = args[0].to_s
|
@@ -38,10 +39,12 @@ module Hexp
|
|
38
39
|
# @example
|
39
40
|
# H[:option].has_attr?('selected') #=> false
|
40
41
|
#
|
41
|
-
# @param
|
42
|
-
#
|
43
|
-
# @api public
|
42
|
+
# @param [String|Symbol] name
|
43
|
+
# The name of the attribute
|
44
44
|
#
|
45
|
+
# @return [true,false]
|
46
|
+
#
|
47
|
+
# @api public
|
45
48
|
def has_attr?(name)
|
46
49
|
attributes.has_key? name.to_s
|
47
50
|
end
|
@@ -51,10 +54,13 @@ module Hexp
|
|
51
54
|
# @example
|
52
55
|
# H[:span, class: "banner strong"].class?("strong") #=> true
|
53
56
|
#
|
54
|
-
# @param
|
55
|
-
#
|
56
|
-
#
|
57
|
+
# @param [String] klass
|
58
|
+
# The name of the class to check for
|
59
|
+
#
|
60
|
+
# @return [Boolean]
|
61
|
+
# True if the class is present, false otherwise
|
57
62
|
#
|
63
|
+
# @api public
|
58
64
|
def class?(klass)
|
59
65
|
attr('class') && attr('class').split(' ').include?(klass.to_s)
|
60
66
|
end
|
@@ -64,10 +70,12 @@ module Hexp
|
|
64
70
|
# @example
|
65
71
|
# H[:div].add_class('foo') #=> H[:div, class: 'foo']
|
66
72
|
#
|
67
|
-
# @param
|
73
|
+
# @param [#to_s] klass
|
74
|
+
# The class to add
|
75
|
+
#
|
68
76
|
# @return [Hexp::Node]
|
69
|
-
# @api public
|
70
77
|
#
|
78
|
+
# @api public
|
71
79
|
def add_class(klass)
|
72
80
|
attr('class', [attr('class'), klass].compact.join(' '))
|
73
81
|
end
|
@@ -77,8 +85,8 @@ module Hexp
|
|
77
85
|
# Convenience method so you don't have to split the class list yourself.
|
78
86
|
#
|
79
87
|
# @return [Array<String>]
|
80
|
-
# @api public
|
81
88
|
#
|
89
|
+
# @api public
|
82
90
|
def class_list
|
83
91
|
@class_list ||= (attr('class') || '').split(' ').freeze
|
84
92
|
end
|
@@ -93,11 +101,12 @@ module Hexp
|
|
93
101
|
# Calling this on a node with a class attribute that is equal to an
|
94
102
|
# empty string will result in the class attribute being removed.
|
95
103
|
#
|
96
|
-
# @param
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
104
|
+
# @param [#to_s] klass
|
105
|
+
# The class to be removed
|
106
|
+
# @return [Hexp::Node]
|
107
|
+
# A node that is identical to this one, but with the given class removed
|
100
108
|
#
|
109
|
+
# @api public
|
101
110
|
def remove_class(klass)
|
102
111
|
return self unless has_attr?('class')
|
103
112
|
new_list = class_list - [klass.to_s]
|
@@ -107,10 +116,11 @@ module Hexp
|
|
107
116
|
|
108
117
|
# Set or override multiple attributes using a hash syntax
|
109
118
|
#
|
110
|
-
# @param
|
119
|
+
# @param [Hash<#to_s,#to_s>] attrs
|
120
|
+
#
|
111
121
|
# @return [Hexp::Node]
|
112
|
-
# @api public
|
113
122
|
#
|
123
|
+
# @api public
|
114
124
|
def set_attrs(attrs)
|
115
125
|
H[
|
116
126
|
self.tag,
|
@@ -123,10 +133,13 @@ module Hexp
|
|
123
133
|
|
124
134
|
# Remove an attribute by name
|
125
135
|
#
|
126
|
-
# @param
|
127
|
-
#
|
128
|
-
#
|
136
|
+
# @param [#to_s] name
|
137
|
+
# The attribute to be removed
|
138
|
+
#
|
139
|
+
# @return [Hexp::Node]
|
140
|
+
# A new node with the attribute removed
|
129
141
|
#
|
142
|
+
# @api public
|
130
143
|
def remove_attr(name)
|
131
144
|
H[
|
132
145
|
self.tag,
|
@@ -137,10 +150,13 @@ module Hexp
|
|
137
150
|
|
138
151
|
# Attribute accessor
|
139
152
|
#
|
140
|
-
# @
|
141
|
-
#
|
142
|
-
# @api public
|
153
|
+
# @param_name [#to_s] attr
|
154
|
+
# The name of the attribute
|
143
155
|
#
|
156
|
+
# @return [String]
|
157
|
+
# The value of the attribute
|
158
|
+
#
|
159
|
+
# @api public
|
144
160
|
def [](attr_name)
|
145
161
|
self.attributes[attr_name.to_s]
|
146
162
|
end
|
@@ -148,16 +164,17 @@ module Hexp
|
|
148
164
|
# Merge attributes into this Hexp
|
149
165
|
#
|
150
166
|
# Class attributes are treated special : the class lists are merged, rather
|
151
|
-
# than being overwritten. See {set_attrs} for a more basic version.
|
167
|
+
# than being overwritten. See {#set_attrs} for a more basic version.
|
152
168
|
#
|
153
|
-
# This method is analoguous with
|
169
|
+
# This method is analoguous with `Hash#merge`. As argument it can take a
|
154
170
|
# Hash, or another Hexp element, in which case that element's attributes
|
155
171
|
# are used.
|
156
172
|
#
|
157
|
-
# @
|
173
|
+
# @param_or_hash [#to_hexp|Hash] node
|
174
|
+
#
|
158
175
|
# @return [Hexp::Node]
|
159
|
-
# @api public
|
160
176
|
#
|
177
|
+
# @api public
|
161
178
|
def merge_attrs(node_or_hash)
|
162
179
|
hash = node_or_hash.respond_to?(:to_hexp) ?
|
163
180
|
node_or_hash.to_hexp.attributes : node_or_hash
|