hexp 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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]
|