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
data/Rakefile
CHANGED
@@ -13,8 +13,8 @@ namespace :ci do
|
|
13
13
|
metrics:yardstick:verify
|
14
14
|
metrics:flog
|
15
15
|
metrics:flay
|
16
|
-
metrics:reek
|
17
16
|
]
|
17
|
+
# metrics:reek
|
18
18
|
# metrics:rubocop
|
19
19
|
end
|
20
20
|
|
@@ -29,3 +29,19 @@ task :push => :gem do
|
|
29
29
|
sh "git push --tags"
|
30
30
|
sh "gem push pkg/hexp-#{Hexp::VERSION}.gem"
|
31
31
|
end
|
32
|
+
|
33
|
+
desc "update gh-pages"
|
34
|
+
task :doc2gh do
|
35
|
+
sh "git diff-files --quiet || exit 1"
|
36
|
+
sh "git diff-index --quiet --cached HEAD || exit 1"
|
37
|
+
sh "yardoc"
|
38
|
+
sh "[ -d /tmp/doc ] && rm -rf /tmp/doc"
|
39
|
+
sh "mv doc /tmp"
|
40
|
+
sh "git co gh-pages"
|
41
|
+
sh "rm -rf *"
|
42
|
+
sh "cp -r /tmp/doc/* ."
|
43
|
+
sh "git add ."
|
44
|
+
sh "git commit -m 'Update gh-pages with YARD docs'"
|
45
|
+
sh "git push origin gh-pages"
|
46
|
+
sh "git co master"
|
47
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'benchmark/ips'
|
2
|
+
|
3
|
+
def ten_times_ten(depth = 10)
|
4
|
+
if depth == 0
|
5
|
+
H[:foo]
|
6
|
+
else
|
7
|
+
H[:foo, [ten_times_ten(depth - 1)] * 10]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
Benchmark.ips do |x|
|
13
|
+
big_tree = ten_times_ten
|
14
|
+
|
15
|
+
x.report('rewrite') do
|
16
|
+
big_tree.rewrite {|x| x}
|
17
|
+
end
|
18
|
+
|
19
|
+
x.report('add_class') do
|
20
|
+
big_tree.rewrite {|x| x.add_class('hello')}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/config/flay.yml
CHANGED
data/config/flog.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
2
|
+
threshold: 47.1
|
data/config/reek.yml
CHANGED
@@ -91,6 +91,7 @@ TooManyStatements:
|
|
91
91
|
- Hexp::Builder#tag!
|
92
92
|
- Hexp::CssSelector::Attribute#matches?
|
93
93
|
- Hexp::CssSelector::Parser#rewrite_simple
|
94
|
+
- Hexp::Node::Normalize#normalized_children
|
94
95
|
max_statements: 6
|
95
96
|
UncommunicativeMethodName:
|
96
97
|
enabled: true
|
@@ -137,3 +138,8 @@ UtilityFunction:
|
|
137
138
|
- Hexp::TextNode#attributes
|
138
139
|
- Hexp::TextNode#children
|
139
140
|
max_helper_calls: 0
|
141
|
+
PrimaDonnaMethod:
|
142
|
+
exclude:
|
143
|
+
- Hexp::Builder#_raise_if_missing!
|
144
|
+
- Hexp::Builder#tag!
|
145
|
+
- Hexp::Builder#text!
|
data/config/yardstick.yml
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
2
|
+
threshold: 99.1
|
3
|
+
require_exact_threshold: false
|
3
4
|
rules:
|
4
5
|
ApiTag::Presence:
|
5
6
|
enabled: true
|
@@ -25,7 +26,11 @@ rules:
|
|
25
26
|
- Hexp::Nokogiri::Equality#equal_text?
|
26
27
|
ReturnTag:
|
27
28
|
enabled: true
|
28
|
-
exclude:
|
29
|
+
exclude:
|
30
|
+
- Hexp::CssSelector::Members#initialize
|
31
|
+
- Hexp::CssSelector::Members.included
|
32
|
+
- Hexp::CssSelector::Named#initialize
|
33
|
+
- Hexp::Builder#_raise_if_empty!
|
29
34
|
Summary::Presence:
|
30
35
|
enabled: true
|
31
36
|
exclude: []
|
data/hexp.gemspec
CHANGED
@@ -17,11 +17,12 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = `git ls-files -- spec`.split($/)
|
18
18
|
gem.extra_rdoc_files = %w[README.md]
|
19
19
|
|
20
|
-
gem.add_runtime_dependency 'sass'
|
21
|
-
gem.add_runtime_dependency 'nokogiri'
|
22
|
-
gem.add_runtime_dependency 'ice_nine'
|
23
|
-
gem.add_runtime_dependency 'equalizer'
|
20
|
+
gem.add_runtime_dependency 'sass', '~> 3.2'
|
21
|
+
gem.add_runtime_dependency 'nokogiri', '~> 1.6'
|
22
|
+
gem.add_runtime_dependency 'ice_nine', '~> 0.9'
|
23
|
+
gem.add_runtime_dependency 'equalizer', '~> 0.0'
|
24
24
|
|
25
|
-
gem.add_development_dependency 'rake',
|
25
|
+
gem.add_development_dependency 'rake', '~> 10.1'
|
26
26
|
gem.add_development_dependency 'rspec', '~> 2.14'
|
27
|
+
gem.add_development_dependency 'benchmark_suite', '~> 1.0'
|
27
28
|
end
|
data/lib/hexp.rb
CHANGED
@@ -9,11 +9,12 @@ require 'equalizer'
|
|
9
9
|
module Hexp
|
10
10
|
# Inject the Hexp::DSL module into classes that include Hexp
|
11
11
|
#
|
12
|
-
# @param
|
12
|
+
# @param [Class] klazz
|
13
|
+
# The class that included Hexp
|
13
14
|
#
|
14
15
|
# @return [Class]
|
15
|
-
# @api private
|
16
16
|
#
|
17
|
+
# @api private
|
17
18
|
def self.included(klazz)
|
18
19
|
klazz.send(:include, Hexp::DSL)
|
19
20
|
end
|
@@ -22,28 +23,30 @@ module Hexp
|
|
22
23
|
#
|
23
24
|
# Delegates to IceNine
|
24
25
|
#
|
25
|
-
# @param
|
26
|
-
#
|
27
|
-
# @
|
26
|
+
# @param [Array] args
|
27
|
+
# arguments to pass on
|
28
|
+
# @return [Object]
|
28
29
|
#
|
30
|
+
# @api private
|
29
31
|
def self.deep_freeze(*args)
|
30
32
|
IceNine.deep_freeze(*args)
|
31
33
|
end
|
32
34
|
|
33
35
|
# Variant of ::Array with slightly modified semantics
|
34
36
|
#
|
35
|
-
# Array() is often used to wrap a value in an Array, unless it's already
|
36
|
-
# an array. However if your object implements
|
37
|
+
# `Array()` is often used to wrap a value in an Array, unless it's already
|
38
|
+
# an array. However if your object implements `#to_a`, then `Array()` will use
|
37
39
|
# that value. Because of this objects that aren't Array-like will get
|
38
40
|
# converted as well, such as Struct objects.
|
39
41
|
#
|
40
42
|
# This implementation relies on #to_ary, which signals that the Object is
|
41
43
|
# a drop-in replacement for an actual Array.
|
42
44
|
#
|
43
|
-
# @param
|
45
|
+
# @param [Object] arg
|
46
|
+
#
|
44
47
|
# @return [Array]
|
45
|
-
# @api private
|
46
48
|
#
|
49
|
+
# @api private
|
47
50
|
def self.Array(arg)
|
48
51
|
if arg.respond_to? :to_ary
|
49
52
|
arg.to_ary
|
@@ -61,10 +64,12 @@ module Hexp
|
|
61
64
|
# @example
|
62
65
|
# Hexp.parse('<div>hello</div>') #=> H[:div, "hello"]
|
63
66
|
#
|
64
|
-
# @param
|
67
|
+
# @param [String] html
|
68
|
+
# A HTML document
|
69
|
+
#
|
65
70
|
# @return [Hexp::Node]
|
66
|
-
# @api public
|
67
71
|
#
|
72
|
+
# @api public
|
68
73
|
def self.parse(html)
|
69
74
|
root = Nokogiri(html).root
|
70
75
|
raise Hexp::ParseError, "Failed to parse HTML : no document root" if root.nil?
|
@@ -73,7 +78,7 @@ module Hexp
|
|
73
78
|
|
74
79
|
# Use builder syntax to create a Hexp
|
75
80
|
#
|
76
|
-
#
|
81
|
+
# @see Hexp::Builder
|
77
82
|
#
|
78
83
|
# @example
|
79
84
|
# list = Hexp.build do
|
@@ -84,10 +89,13 @@ module Hexp
|
|
84
89
|
# end
|
85
90
|
# end
|
86
91
|
#
|
87
|
-
# @param
|
92
|
+
# @param [Array] args
|
93
|
+
#
|
94
|
+
# @yieldparam [Hexp::Builder]
|
95
|
+
#
|
88
96
|
# @return [Hexp::Builder]
|
89
|
-
# @api public
|
90
97
|
#
|
98
|
+
# @api public
|
91
99
|
def self.build(*args, &block)
|
92
100
|
Hexp::Builder.new(*args, &block)
|
93
101
|
end
|
@@ -95,18 +103,18 @@ module Hexp
|
|
95
103
|
end
|
96
104
|
|
97
105
|
require 'hexp/version'
|
98
|
-
require 'hexp/dsl'
|
99
|
-
|
100
106
|
|
101
107
|
require 'hexp/node/attributes'
|
102
108
|
require 'hexp/node/children'
|
103
|
-
|
104
109
|
require 'hexp/node'
|
110
|
+
|
111
|
+
require 'hexp/dsl'
|
112
|
+
|
105
113
|
require 'hexp/node/normalize'
|
106
114
|
require 'hexp/node/domize'
|
107
115
|
require 'hexp/node/pp'
|
108
116
|
require 'hexp/node/rewriter'
|
109
|
-
require 'hexp/node/
|
117
|
+
require 'hexp/node/selection'
|
110
118
|
require 'hexp/node/css_selection'
|
111
119
|
|
112
120
|
require 'hexp/text_node'
|
data/lib/hexp/builder.rb
CHANGED
@@ -4,19 +4,24 @@ module Hexp
|
|
4
4
|
class Builder < BasicObject
|
5
5
|
include ::Hexp
|
6
6
|
|
7
|
-
# def inspect
|
8
|
-
# ::Kernel.puts ::Kernel.caller ; ::Kernel.exit
|
9
|
-
# end
|
10
|
-
|
11
7
|
# Construct a new builder, and start building
|
12
8
|
#
|
13
|
-
# The recommended way to call this is through `Hexp.build`.
|
9
|
+
# The recommended way to call this is through `Hexp.build`. If the block
|
10
|
+
# takes an argument, then builder methods need to be called on that variable.
|
11
|
+
#
|
12
|
+
# @example With an explicit builder
|
13
|
+
# hi = Hexp.build {|html| html.span "Hello" ; html.span " World"}
|
14
|
+
#
|
15
|
+
# @example Without a builder object
|
16
|
+
# hi = Hexp.build { span "Hello" ; span " World"}
|
14
17
|
#
|
15
|
-
# @param
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# @param [Symbol] tag
|
19
|
+
# The tag of the outermost element (optional)
|
20
|
+
# @param [Array<Hash,String>] args
|
21
|
+
# Extra arguments, a String for a text node, a Hash for attributes
|
22
|
+
#
|
23
|
+
# @yieldparam [Hexp::Builder]
|
24
|
+
# If the block takes an argument it will receive the builder object
|
20
25
|
#
|
21
26
|
# @api private
|
22
27
|
#
|
@@ -40,15 +45,17 @@ module Hexp
|
|
40
45
|
# end
|
41
46
|
# hexp.to_html #=> "<div><p>Oh the code, such sweet joy it brings</p></div>"
|
42
47
|
#
|
43
|
-
# @param
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# @
|
48
|
+
# @param [Symbol] tag
|
49
|
+
# The tag name, like 'div' or 'head'
|
50
|
+
# @param [Array<Hash|String>] args
|
51
|
+
# A hash of attributes, or a string to use inside the tag, or both. Multiple
|
52
|
+
# occurences of each can be specified
|
53
|
+
# @param [Proc] block
|
54
|
+
# Builder directives for the contents of the tag
|
49
55
|
#
|
50
|
-
# @
|
56
|
+
# @return [nil]
|
51
57
|
#
|
58
|
+
# @api public
|
52
59
|
def tag!(tag, *args, &block)
|
53
60
|
text, attributes = nil, {}
|
54
61
|
args.each do |arg|
|
@@ -84,10 +91,12 @@ module Hexp
|
|
84
91
|
# end
|
85
92
|
# end
|
86
93
|
#
|
87
|
-
# @param
|
94
|
+
# @param [String] text
|
95
|
+
# the text to add
|
96
|
+
#
|
88
97
|
# @return [Hexp::Builder] self
|
89
|
-
# @api public
|
90
98
|
#
|
99
|
+
# @api public
|
91
100
|
def text!(text)
|
92
101
|
_raise_if_empty! "Hexp::Builder needs a root element to add text elements to"
|
93
102
|
@stack.last[2] << text.to_s
|
@@ -113,7 +122,9 @@ module Hexp
|
|
113
122
|
# end
|
114
123
|
# node.to_html #=> <div><button>click me!</button></div>
|
115
124
|
#
|
116
|
-
# @
|
125
|
+
# @param [Array<#to_hexp>] args
|
126
|
+
# Hexpable objects to add to the current tag
|
127
|
+
#
|
117
128
|
# @return [Hexp::Builder]
|
118
129
|
#
|
119
130
|
# @api public
|
@@ -139,8 +150,8 @@ module Hexp
|
|
139
150
|
# Hexp.build { div { text! 'hello' } }.to_hexp # => H[:div, ["hello"]]
|
140
151
|
#
|
141
152
|
# @return [Hexp::Node]
|
142
|
-
# @api public
|
143
153
|
#
|
154
|
+
# @api public
|
144
155
|
def to_hexp
|
145
156
|
_raise_if_empty!
|
146
157
|
::Hexp::Node[*@stack.last]
|
@@ -157,9 +168,10 @@ module Hexp
|
|
157
168
|
# builder calls.
|
158
169
|
#
|
159
170
|
# @param block [Proc]
|
160
|
-
# @return [NilClass]
|
161
|
-
# @api private
|
162
171
|
#
|
172
|
+
# @return [nil]
|
173
|
+
#
|
174
|
+
# @api private
|
163
175
|
def _process(&block)
|
164
176
|
if block.arity == 1
|
165
177
|
block.call(self)
|
@@ -178,14 +190,15 @@ module Hexp
|
|
178
190
|
# end
|
179
191
|
#
|
180
192
|
# @api private
|
181
|
-
#
|
182
193
|
class NodeBuilder
|
183
194
|
# Create new NodeBuilder
|
184
195
|
#
|
185
|
-
# @param
|
186
|
-
#
|
187
|
-
# @
|
196
|
+
# @param [Array] node
|
197
|
+
# (tag, attrs, children) triplet
|
198
|
+
# @param [Hexp::Builder] builder
|
199
|
+
# The parent builder to delegate back
|
188
200
|
#
|
201
|
+
# @api private
|
189
202
|
def initialize(node, builder)
|
190
203
|
@node, @builder = node, builder
|
191
204
|
end
|
@@ -196,14 +209,16 @@ module Hexp
|
|
196
209
|
# Hexp.build { div.strong.warn }.to_hexp
|
197
210
|
# # => H[:div, class: 'strong warn']
|
198
211
|
#
|
199
|
-
# @param
|
212
|
+
# @param [Symbol] sym
|
213
|
+
# the class to add
|
214
|
+
#
|
200
215
|
# @return [Hexp::Builder::NodeBuilder] self
|
201
|
-
# @api public
|
202
216
|
#
|
217
|
+
# @api public
|
203
218
|
def method_missing(sym, &block)
|
204
219
|
attrs = @node[1]
|
205
220
|
@node[1] = attrs.merge class: [attrs[:class], sym.to_s].compact.join(' ')
|
206
|
-
@builder._process
|
221
|
+
@builder._process(&block) if block
|
207
222
|
self
|
208
223
|
end
|
209
224
|
end
|
@@ -218,10 +233,10 @@ module Hexp
|
|
218
233
|
# p Hexp.build { div }
|
219
234
|
#
|
220
235
|
# @return [String]
|
221
|
-
# @api public
|
222
236
|
#
|
237
|
+
# @api public
|
223
238
|
def inspect
|
224
|
-
"#<Hexp::Builder #{@stack.empty? ? '[]' :to_hexp.inspect}>"
|
239
|
+
"#<Hexp::Builder #{@stack.empty? ? '[]' : to_hexp.inspect}>"
|
225
240
|
end
|
226
241
|
|
227
242
|
# Gratefully borrowed from Builder.
|
@@ -245,10 +260,14 @@ module Hexp
|
|
245
260
|
|
246
261
|
# Raise an exception if nothing has been built yet
|
247
262
|
#
|
248
|
-
# @param
|
249
|
-
#
|
250
|
-
#
|
263
|
+
# @param [String] text
|
264
|
+
# The error message
|
265
|
+
#
|
266
|
+
# @raise [Hexp::FormatError]
|
267
|
+
# if the builder is converted to a {Hexp::Node} before a root element is
|
268
|
+
# defined.
|
251
269
|
#
|
270
|
+
# @api private
|
252
271
|
def _raise_if_empty!(text = 'Hexp::Builder is lacking a root element.')
|
253
272
|
::Kernel.raise ::Hexp::FormatError, text if @stack.empty?
|
254
273
|
end
|
data/lib/hexp/css_selector.rb
CHANGED
@@ -10,10 +10,14 @@ module Hexp
|
|
10
10
|
|
11
11
|
# Member nodes
|
12
12
|
#
|
13
|
+
# @return [Array]
|
14
|
+
#
|
15
|
+
# @api private
|
13
16
|
attr_reader :members
|
14
17
|
|
15
18
|
# Shared initializer for parse tree nodes with children (members)
|
16
19
|
#
|
20
|
+
# @api private
|
17
21
|
def initialize(members)
|
18
22
|
@members = Hexp.deep_freeze(members)
|
19
23
|
end
|
@@ -23,9 +27,9 @@ module Hexp
|
|
23
27
|
# @example
|
24
28
|
# CommaSequence[member1, member2]
|
25
29
|
#
|
26
|
-
# @param
|
27
|
-
# @api private
|
30
|
+
# @param [Class] klass
|
28
31
|
#
|
32
|
+
# @api private
|
29
33
|
def self.included(klass)
|
30
34
|
def klass.[](*members)
|
31
35
|
new(members)
|
@@ -35,8 +39,8 @@ module Hexp
|
|
35
39
|
# Return a debugging representation
|
36
40
|
#
|
37
41
|
# @return [String]
|
38
|
-
# @api private
|
39
42
|
#
|
43
|
+
# @api private
|
40
44
|
def inspect
|
41
45
|
"#{self.class.name.split('::').last}[#{self.members.map(&:inspect).join(', ')}]"
|
42
46
|
end
|
@@ -46,12 +50,29 @@ module Hexp
|
|
46
50
|
#
|
47
51
|
module Named
|
48
52
|
include Equalizer.new(:name)
|
53
|
+
|
54
|
+
# The name of this element
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
#
|
58
|
+
# @api private
|
49
59
|
attr_reader :name
|
50
60
|
|
61
|
+
# Shared constructor that sets a name
|
62
|
+
#
|
63
|
+
# @param [String] name
|
64
|
+
# the name of the element
|
65
|
+
#
|
66
|
+
# @api private
|
51
67
|
def initialize(name)
|
52
68
|
@name = name.freeze
|
53
69
|
end
|
54
70
|
|
71
|
+
# Return a representation convenient for debugging
|
72
|
+
#
|
73
|
+
# @return [String]
|
74
|
+
#
|
75
|
+
# @api private
|
55
76
|
def inspect
|
56
77
|
"<#{self.class.name.split('::').last} name=#{name}>"
|
57
78
|
end
|
@@ -61,24 +82,22 @@ module Hexp
|
|
61
82
|
#
|
62
83
|
# Contains a number of {Sequence} objects
|
63
84
|
#
|
64
|
-
# For example :
|
85
|
+
# For example : 'span .big, a'
|
65
86
|
#
|
66
87
|
class CommaSequence
|
67
88
|
include Members
|
68
89
|
|
69
|
-
# def inspect
|
70
|
-
# members.map(&:inspect).join(', ')
|
71
|
-
# end
|
72
|
-
|
73
90
|
# Does any sequence in this comma sequence fully match the given element
|
74
91
|
#
|
75
92
|
# This method does not recurse, it only checks if any of the sequences in
|
76
93
|
# this CommaSequence with a length of one can fully match the given
|
77
94
|
# element.
|
78
95
|
#
|
79
|
-
# @param
|
96
|
+
# @param [Hexp::Node] element
|
97
|
+
#
|
80
98
|
# @return [Boolean]
|
81
99
|
#
|
100
|
+
# @api private
|
82
101
|
def matches?(element)
|
83
102
|
members.any? do |sequence|
|
84
103
|
sequence.members.count == 1 &&
|
@@ -90,20 +109,29 @@ module Hexp
|
|
90
109
|
# A single CSS sequence like 'div span .foo'
|
91
110
|
#
|
92
111
|
class Sequence
|
93
|
-
|
94
112
|
include Members
|
95
113
|
|
114
|
+
# Does the first element of this sequence match the element
|
115
|
+
#
|
116
|
+
# @param [Hexp::Node] element
|
117
|
+
#
|
118
|
+
# @return [true, false]
|
119
|
+
#
|
120
|
+
# @api private
|
96
121
|
def head_matches?(element)
|
97
122
|
members.first.matches?(element)
|
98
123
|
end
|
99
124
|
|
125
|
+
# Drop the first element of this Sequence
|
126
|
+
#
|
127
|
+
# This returns a new Sequence, with one member less.
|
128
|
+
#
|
129
|
+
# @return [Hexp::CssSelector::Sequence]
|
130
|
+
#
|
131
|
+
# @api private
|
100
132
|
def drop_head
|
101
133
|
self.class.new(members.drop(1))
|
102
134
|
end
|
103
|
-
|
104
|
-
# def inspect
|
105
|
-
# members.map(&:inspect).join(' ')
|
106
|
-
# end
|
107
135
|
end
|
108
136
|
|
109
137
|
# A CSS sequence that relates to a single element, like 'div.caption:first'
|
@@ -111,28 +139,41 @@ module Hexp
|
|
111
139
|
class SimpleSequence
|
112
140
|
include Members
|
113
141
|
|
142
|
+
# Does the element match all parts of this SimpleSequence
|
143
|
+
#
|
144
|
+
# @params [Hexp::Node] element
|
145
|
+
#
|
146
|
+
# @return [true, false]
|
147
|
+
#
|
148
|
+
# @api private
|
114
149
|
def matches?(element)
|
115
150
|
members.all? do |simple|
|
116
151
|
simple.matches?(element)
|
117
152
|
end
|
118
153
|
end
|
119
|
-
|
120
|
-
# def inspect
|
121
|
-
# members.map(&:inspect).join
|
122
|
-
# end
|
123
154
|
end
|
124
155
|
|
125
156
|
# A CSS element declaration, like 'div'
|
126
157
|
class Element
|
127
158
|
include Named
|
128
159
|
|
160
|
+
# Does the node match this element selector
|
161
|
+
#
|
162
|
+
# @param [Hexp::Node] element
|
163
|
+
#
|
164
|
+
# @return [true, false]
|
165
|
+
#
|
166
|
+
# @api private
|
129
167
|
def matches?(element)
|
130
168
|
element.tag.to_s == name
|
131
169
|
end
|
170
|
+
end
|
132
171
|
|
133
|
-
|
134
|
-
|
135
|
-
|
172
|
+
# Match any element, '*'
|
173
|
+
class Universal
|
174
|
+
def matches?(element)
|
175
|
+
true
|
176
|
+
end
|
136
177
|
end
|
137
178
|
|
138
179
|
# A CSS class declaration, like '.foo'
|
@@ -140,6 +181,13 @@ module Hexp
|
|
140
181
|
class Class
|
141
182
|
include Named
|
142
183
|
|
184
|
+
# Does the node match this class selector
|
185
|
+
#
|
186
|
+
# @param [Hexp::Node] element
|
187
|
+
#
|
188
|
+
# @return [true, false]
|
189
|
+
#
|
190
|
+
# @api private
|
143
191
|
def matches?(element)
|
144
192
|
element.class?(name)
|
145
193
|
end
|
@@ -150,6 +198,13 @@ module Hexp
|
|
150
198
|
class Id
|
151
199
|
include Named
|
152
200
|
|
201
|
+
# Does the node match this id selector
|
202
|
+
#
|
203
|
+
# @param [Hexp::Node] element
|
204
|
+
#
|
205
|
+
# @return [true, false]
|
206
|
+
#
|
207
|
+
# @api private
|
153
208
|
def matches?(element)
|
154
209
|
element.attr('id') == name
|
155
210
|
end
|
@@ -157,22 +212,66 @@ module Hexp
|
|
157
212
|
|
158
213
|
# An attribute selector, like [href^="http://"]
|
159
214
|
#
|
215
|
+
# @!attribute [r] name
|
216
|
+
# @return [String] The attribute name
|
217
|
+
# @!attribute [r] namespace
|
218
|
+
# @return [String]
|
219
|
+
# @!attribute [r] operator
|
220
|
+
# @return [String] The operator that works on an attribute value
|
221
|
+
# @!attribute [r] value
|
222
|
+
# @return [String] The value to match against
|
223
|
+
# @!attribute [r] flags
|
224
|
+
# @return [String]
|
225
|
+
#
|
226
|
+
# @api private
|
160
227
|
class Attribute
|
161
228
|
include Equalizer.new(:name, :namespace, :operator, :value, :flags)
|
229
|
+
|
162
230
|
attr_reader :name, :namespace, :operator, :value, :flags
|
163
231
|
|
232
|
+
# Construct a new Attribute selector
|
233
|
+
#
|
234
|
+
# The attributes directly mimic those returned from the SASS parser, even
|
235
|
+
# though we don't use all of them.
|
236
|
+
#
|
237
|
+
# @param [String] name
|
238
|
+
# Name of the attribute, like 'href'
|
239
|
+
# @param [String] namespace
|
240
|
+
# unused
|
241
|
+
# @param [nil, String] operator
|
242
|
+
# Operator like '~=', '^=', ... Use blank to simply test attribute
|
243
|
+
# presence.
|
244
|
+
# @param [String] value
|
245
|
+
# Value to test for, operator dependent
|
246
|
+
# @param [Object] flags
|
247
|
+
# unused
|
248
|
+
#
|
249
|
+
# @api private
|
164
250
|
def initialize(name, namespace, operator, value, flags)
|
165
|
-
@name
|
251
|
+
@name = name.freeze
|
166
252
|
@namespace = namespace.freeze
|
167
|
-
@operator
|
168
|
-
@value
|
169
|
-
@flag
|
253
|
+
@operator = operator.freeze
|
254
|
+
@value = value.freeze
|
255
|
+
@flag = flags.freeze
|
170
256
|
end
|
171
257
|
|
258
|
+
# Debugging representation
|
259
|
+
#
|
260
|
+
# @return [String]
|
261
|
+
#
|
262
|
+
# @api private
|
172
263
|
def inspect
|
173
264
|
"<#{self.class.name.split('::').last} name=#{name} namespace=#{namespace.inspect} operator=#{operator.inspect} value=#{value.inspect} flags=#{flags.inspect}>"
|
174
265
|
end
|
175
266
|
|
267
|
+
# Does the node match this attribute selector
|
268
|
+
#
|
269
|
+
# @param [Hexp::Node] element
|
270
|
+
# node to test against
|
271
|
+
#
|
272
|
+
# @return [true, false]
|
273
|
+
#
|
274
|
+
# @api private
|
176
275
|
def matches?(element)
|
177
276
|
return false unless element[name]
|
178
277
|
attribute = element[name]
|