hexp 0.0.1.pre → 0.0.1.pre2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.travis.yml +0 -1
- data/Gemfile +5 -1
- data/Gemfile.lock +15 -7
- data/README.md +104 -5
- data/config/devtools.yml +2 -0
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/mutant.yml +3 -2
- data/config/reek.yml +26 -14
- data/config/yardstick.yml +1 -1
- data/examples/filter.rb +54 -0
- data/lib/hexp.rb +21 -1
- data/lib/hexp/dom.rb +9 -0
- data/lib/hexp/h.rb +2 -0
- data/lib/hexp/list.rb +25 -0
- data/lib/hexp/node.rb +63 -0
- data/lib/hexp/node/domize.rb +45 -0
- data/lib/hexp/node/normalize.rb +89 -0
- data/lib/hexp/node/pp.rb +38 -0
- data/lib/hexp/nokogiri/equality.rb +65 -0
- data/lib/hexp/text_node.rb +26 -0
- data/lib/hexp/version.rb +1 -1
- data/spec/shared_helper.rb +7 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/hexp/node/domize_spec.rb +66 -0
- data/spec/unit/hexp/node/normalize_spec.rb +81 -0
- data/spec/unit/hexp/node/pp_spec.rb +18 -0
- data/spec/unit/hexp/node/to_a_spec.rb +8 -0
- data/spec/unit/hexp/node/to_dom_spec.rb +10 -0
- data/spec/unit/hexp/node/to_hexp_spec.rb +8 -0
- data/spec/unit/hexp/node/to_html_spec.rb +9 -0
- data/spec/unit/hexp/nokogiri/equality_spec.rb +69 -0
- metadata +30 -6
- data/lib/hexp/array.rb +0 -43
- data/spec/unit/hexp/array_spec.rb +0 -61
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
+
gem 'nokogiri'
|
4
|
+
gem 'equalizer', github: 'dkubb/equalizer'
|
5
|
+
|
3
6
|
group :development do
|
4
7
|
gem 'devtools', :git => 'https://github.com/datamapper/devtools.git'
|
5
|
-
gem 'json', '~> 1.
|
8
|
+
gem 'json', '~> 1.8.0'
|
6
9
|
eval File.read('Gemfile.devtools')
|
7
10
|
end
|
11
|
+
|
data/Gemfile.lock
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/dkubb/equalizer.git
|
3
|
+
revision: 7b0a30dee57ed0378df6b32e5defda64adaff0da
|
4
|
+
specs:
|
5
|
+
equalizer (0.0.5)
|
6
|
+
adamantium (~> 0.0.7)
|
7
|
+
backports (~> 3.2, >= 3.2.0)
|
8
|
+
|
1
9
|
GIT
|
2
10
|
remote: https://github.com/datamapper/devtools.git
|
3
11
|
revision: f0e3a3a42673266c46b983bc79a4ab6e1623bccc
|
@@ -7,7 +15,7 @@ GIT
|
|
7
15
|
|
8
16
|
GIT
|
9
17
|
remote: https://github.com/troessner/reek.git
|
10
|
-
revision:
|
18
|
+
revision: f302390f533dd6b59f837821d63249d0c729f931
|
11
19
|
specs:
|
12
20
|
reek (1.3.1)
|
13
21
|
ruby2ruby (~> 2.0.2)
|
@@ -33,9 +41,6 @@ GEM
|
|
33
41
|
thor
|
34
42
|
descendants_tracker (0.0.1)
|
35
43
|
diff-lcs (1.2.4)
|
36
|
-
equalizer (0.0.5)
|
37
|
-
adamantium (~> 0.0.6)
|
38
|
-
backports (~> 3.0, >= 3.0.3)
|
39
44
|
ffi (1.8.1)
|
40
45
|
ffi-hunspell (0.3.0)
|
41
46
|
ffi (~> 1.0)
|
@@ -60,7 +65,7 @@ GEM
|
|
60
65
|
rspec (~> 2.11)
|
61
66
|
ice_nine (0.7.0)
|
62
67
|
inflecto (0.0.2)
|
63
|
-
json (1.
|
68
|
+
json (1.8.0)
|
64
69
|
kramdown (1.0.2)
|
65
70
|
libnotify (0.8.0)
|
66
71
|
ffi (>= 1.0.11)
|
@@ -71,7 +76,7 @@ GEM
|
|
71
76
|
lumberjack (1.0.3)
|
72
77
|
method_source (0.8.1)
|
73
78
|
mime-types (1.23)
|
74
|
-
multi_json (1.7.
|
79
|
+
multi_json (1.7.4)
|
75
80
|
mutant (0.2.20)
|
76
81
|
abstract_type (~> 0.0.3)
|
77
82
|
adamantium (~> 0.0.7)
|
@@ -84,6 +89,7 @@ GEM
|
|
84
89
|
rspec (~> 2.13.0)
|
85
90
|
to_source (~> 0.2.19)
|
86
91
|
mutant-melbourne (2.0.3)
|
92
|
+
nokogiri (1.5.9)
|
87
93
|
pelusa (0.2.2)
|
88
94
|
pry (0.9.12.2)
|
89
95
|
coderay (~> 1.0.5)
|
@@ -142,17 +148,19 @@ DEPENDENCIES
|
|
142
148
|
backports (~> 3.3, >= 3.3.0)
|
143
149
|
coveralls (~> 0.6.6)
|
144
150
|
devtools!
|
151
|
+
equalizer!
|
145
152
|
flay (~> 2.2.0)
|
146
153
|
flog (~> 4.0.0)
|
147
154
|
guard (~> 1.8.0)
|
148
155
|
guard-bundler (~> 1.0.0)
|
149
156
|
guard-rspec (~> 2.5.4)
|
150
157
|
jruby-openssl (~> 0.8.5)
|
151
|
-
json (~> 1.
|
158
|
+
json (~> 1.8.0)
|
152
159
|
kramdown (~> 1.0.1)
|
153
160
|
libnotify (~> 0.8.0)
|
154
161
|
listen (~> 1.0.2)
|
155
162
|
mutant (~> 0.2.20)
|
163
|
+
nokogiri
|
156
164
|
pelusa (~> 0.2.2)
|
157
165
|
rake (~> 10.0.4)
|
158
166
|
rb-fchange (~> 0.0.6)
|
data/README.md
CHANGED
@@ -1,16 +1,115 @@
|
|
1
|
-
hexp
|
2
|
-
====
|
3
|
-
|
4
1
|
[![Gem Version](https://badge.fury.io/rb/hexp.png)][gem]
|
5
2
|
[![Build Status](https://secure.travis-ci.org/plexus/hexp.png?branch=master)][travis]
|
6
3
|
[![Dependency Status](https://gemnasium.com/plexus/hexp.png)][gemnasium]
|
7
4
|
[![Code Climate](https://codeclimate.com/github/plexus/hexp.png)][codeclimate]
|
8
5
|
[![Coverage Status](https://coveralls.io/repos/plexus/hexp/badge.png?branch=master)][coveralls]
|
9
6
|
|
10
|
-
[gem]: https://rubygems.org/gems/
|
7
|
+
[gem]: https://rubygems.org/gems/hexp
|
11
8
|
[travis]: https://travis-ci.org/plexus/hexp
|
12
9
|
[gemnasium]: https://gemnasium.com/plexus/hexp
|
13
10
|
[codeclimate]: https://codeclimate.com/github/plexus/hexp
|
14
11
|
[coveralls]: https://coveralls.io/r/plexus/hexp
|
15
12
|
|
16
|
-
|
13
|
+
A bit about hexps
|
14
|
+
-----------------
|
15
|
+
|
16
|
+
**What on earth is a Hexp?**
|
17
|
+
|
18
|
+
Hexps are basically snippets of HTML written in nothing but Ruby, here's an example.
|
19
|
+
|
20
|
+
````ruby
|
21
|
+
@message = "Hexps are fun for the whole family, from 9 to 99 years old."
|
22
|
+
@hexp = H[:div, {class: 'hexp-intro'}, [
|
23
|
+
[:p, @message]
|
24
|
+
]
|
25
|
+
]
|
26
|
+
````
|
27
|
+
|
28
|
+
**Don't people use templates for this kind of thing?**
|
29
|
+
|
30
|
+
They do, this is an alternative approach. With templates you need to think about which parts need to be HTML-escaped, or you can make errors like forgetting a closing tag. With hexps you no longer need to think about escaping.
|
31
|
+
|
32
|
+
**Wait how is that?**
|
33
|
+
|
34
|
+
With traditional approaches you can insert plain text in your template, or snippets of HTML. The first must be escaped, the second should not. For your template they are all just strings, so you, the programmer, need to distinguish between the two in a way. For example by using `html_escape` on one (explicit escaping), or `html_safe` on the other (implicit escaping).
|
35
|
+
|
36
|
+
When using hexps you never deal with strings that actually contain HTML. Helper methods would return hexps instead, and you can combine those into bigger hexps. Strings inside a hexp are always just that, so they will always be escaped without you thinking about it.
|
37
|
+
|
38
|
+
**So that's it, easier escaping?**
|
39
|
+
|
40
|
+
Well that's not all, by having a simple lightweight representation of HTML that is _a real data structure_, you can really start programming your HTML. If you have an object that responds to `to_hexp`, you can use that object inside a hexp, so you can use Object Orientation for your user interface. Like so
|
41
|
+
|
42
|
+
````ruby
|
43
|
+
class ProfileLink < Struct.new(:user)
|
44
|
+
def to_hexp
|
45
|
+
H[:a, {class: "profile-link", href: "/user/#{user.id}"}, user.name]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Layout < Struct.new(:content)
|
50
|
+
def to_hexp
|
51
|
+
H[:html, [
|
52
|
+
[:body, [content]]
|
53
|
+
]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
render inline: Layout.new(ProfileLink.new(@user)).to_html
|
58
|
+
````
|
59
|
+
|
60
|
+
**Does it get any better?**
|
61
|
+
|
62
|
+
It does! The really neat part is having filters that process this HTML tree before it gets serialized to text. This could be good for
|
63
|
+
|
64
|
+
- populating form fields
|
65
|
+
- adding extra admin buttons when logged in
|
66
|
+
- cleanly separate aspects of your app (e.g. discount codes) from 'core' implementation
|
67
|
+
- becoming filthy rich and/or ridiculously happy
|
68
|
+
|
69
|
+
**What's up with the funny name?**
|
70
|
+
|
71
|
+
Hexp stands for HTML expressions. It's a reference to s-expressions as they are known in LISP languages, a simple way to represent data as nested lists.
|
72
|
+
|
73
|
+
How to use it
|
74
|
+
-------------
|
75
|
+
|
76
|
+
Hexp objects come in two flavors : `Hexp::Node` and `Hexp::List`. A `Node` consists of three parts : its `tag`, `attributes` and `children`. A `List` is just that, a list (of nodes).
|
77
|
+
|
78
|
+
To construct a `Node` use `H[tag, attributes, children]`. Use a `Symbol` for the `tag`, a `Hash` for the `attributes`, and an `Array` for the `children`. Attributes or children can be omitted when they are empty.
|
79
|
+
|
80
|
+
The list of children will automatically be converted to `Hexp::List`, similarly for any nested nodes you can simply use `[tag, attributes, children]` without the `H`, all nodes in the tree will be converted to proper `Hexp::Node` objects.
|
81
|
+
|
82
|
+
The entire API is centered around these two classes, and one of them you can think of as essentially just an `Array`, in other words Hexp is super easy to learn. Try it out in `irb`, have a look at the examples, and *build cool stuff*!
|
83
|
+
|
84
|
+
A note on immutability
|
85
|
+
----------------------
|
86
|
+
|
87
|
+
All Hexp objects are deep frozen on creation, you can never alter them afterwards. Operations always return a new `Hexp::Node` rather than working in place.
|
88
|
+
|
89
|
+
This might seem stringent when you are not used to this style of coding, but it's a pattern that generally promotes good code.
|
90
|
+
|
91
|
+
Can I already use it
|
92
|
+
--------------------
|
93
|
+
|
94
|
+
Hold your horses, this is *very* alpha level code! Feedback is very much appreciated, so try it out, and let me know what you think. Basic functionality is there, but it's far from finished, and the API will probably still change based on feedback. You have been warned.
|
95
|
+
|
96
|
+
Is it any good?
|
97
|
+
---------------
|
98
|
+
|
99
|
+
Yes
|
100
|
+
|
101
|
+
How to install
|
102
|
+
--------------
|
103
|
+
|
104
|
+
At this point you're best off grabbing the Git repo, e.g. with bundler
|
105
|
+
|
106
|
+
````sh
|
107
|
+
# Gemfile
|
108
|
+
|
109
|
+
gem 'hexp', github: 'plexus/hexp'
|
110
|
+
````
|
111
|
+
|
112
|
+
Who is behind this
|
113
|
+
------------------
|
114
|
+
|
115
|
+
Hexp is conceived and created by [Arne Brasseur](http://arnebrasseur.net)
|
data/config/devtools.yml
ADDED
data/config/flay.yml
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
3
|
-
total_score:
|
2
|
+
threshold: 7
|
3
|
+
total_score: 75.0
|
data/config/flog.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
2
|
+
threshold: 17.4
|
data/config/mutant.yml
CHANGED
@@ -1,2 +1,3 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
---
|
2
|
+
name: your_lib
|
3
|
+
namespace: YourLib
|
data/config/reek.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
---
|
2
2
|
Attribute:
|
3
|
-
enabled:
|
3
|
+
enabled: false
|
4
4
|
exclude: []
|
5
5
|
BooleanParameter:
|
6
6
|
enabled: true
|
@@ -14,33 +14,41 @@ ControlParameter:
|
|
14
14
|
DataClump:
|
15
15
|
enabled: true
|
16
16
|
exclude: []
|
17
|
-
max_copies:
|
18
|
-
min_clump_size:
|
17
|
+
max_copies: 2
|
18
|
+
min_clump_size: 2
|
19
19
|
DuplicateMethodCall:
|
20
20
|
enabled: true
|
21
21
|
exclude: []
|
22
|
-
max_calls:
|
22
|
+
max_calls: 2
|
23
23
|
allow_calls: []
|
24
24
|
FeatureEnvy:
|
25
25
|
enabled: true
|
26
|
-
exclude:
|
26
|
+
exclude:
|
27
|
+
- Hexp::Node::Domize#set_attributes
|
28
|
+
- Hexp::Node::Normalize#children
|
29
|
+
- Hexp::Node::Normalize#normalized_children
|
30
|
+
- Hexp::Node::PP#pp_attributes
|
31
|
+
- Hexp::Node::PP#pp_children
|
27
32
|
IrresponsibleModule:
|
28
33
|
enabled: true
|
29
34
|
exclude: []
|
30
35
|
LongParameterList:
|
31
36
|
enabled: true
|
32
37
|
exclude: []
|
33
|
-
max_params:
|
34
|
-
overrides:
|
38
|
+
max_params: 2
|
39
|
+
overrides:
|
40
|
+
initialize:
|
41
|
+
max_params: 3
|
35
42
|
LongYieldList:
|
36
43
|
enabled: true
|
37
44
|
exclude: []
|
38
|
-
max_params:
|
45
|
+
max_params: 2
|
39
46
|
NestedIterators:
|
40
47
|
enabled: true
|
41
48
|
exclude: []
|
42
49
|
max_allowed_nesting: 1
|
43
|
-
ignore_iterators:
|
50
|
+
ignore_iterators:
|
51
|
+
- any?
|
44
52
|
NilCheck:
|
45
53
|
enabled: true
|
46
54
|
exclude: []
|
@@ -51,15 +59,16 @@ RepeatedConditional:
|
|
51
59
|
TooManyInstanceVariables:
|
52
60
|
enabled: true
|
53
61
|
exclude: []
|
54
|
-
max_instance_variables:
|
62
|
+
max_instance_variables: 3
|
55
63
|
TooManyMethods:
|
56
64
|
enabled: true
|
57
65
|
exclude: []
|
58
|
-
max_methods:
|
66
|
+
max_methods: 10
|
59
67
|
TooManyStatements:
|
60
68
|
enabled: true
|
61
|
-
exclude:
|
62
|
-
|
69
|
+
exclude:
|
70
|
+
- each
|
71
|
+
max_statements: 4
|
63
72
|
UncommunicativeMethodName:
|
64
73
|
enabled: true
|
65
74
|
exclude: []
|
@@ -96,5 +105,8 @@ UnusedParameters:
|
|
96
105
|
exclude: []
|
97
106
|
UtilityFunction:
|
98
107
|
enabled: true
|
99
|
-
exclude:
|
108
|
+
exclude:
|
109
|
+
- Hexp::List#initialize
|
110
|
+
- Hexp::Node::Domize#set_attributes
|
111
|
+
- Hexp::TextNode#attributes
|
100
112
|
max_helper_calls: 0
|
data/config/yardstick.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
threshold:
|
2
|
+
threshold: 81
|
data/examples/filter.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
$:.unshift('/home/arne/github/hexp/lib')
|
2
|
+
require 'hexp/h'
|
3
|
+
|
4
|
+
class X
|
5
|
+
def to_hexp
|
6
|
+
[:p, {class: 'foo'}, [
|
7
|
+
[:br],
|
8
|
+
'awesome',
|
9
|
+
[:br]]]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
hexp = H[:p, [
|
14
|
+
[:div, {id: 'main'}, [
|
15
|
+
X.new,
|
16
|
+
X.new]],
|
17
|
+
[:hr]]]
|
18
|
+
|
19
|
+
|
20
|
+
hexp = hexp.filter do |node|
|
21
|
+
if node.attributes['class'] == 'foo'
|
22
|
+
[[:p, 'foo coming up!'], node]
|
23
|
+
else
|
24
|
+
[node]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
puts hexp.pp
|
29
|
+
|
30
|
+
puts hexp.to_html
|
31
|
+
|
32
|
+
|
33
|
+
# >> H[:p, [
|
34
|
+
# >> H[:div, {"id"=>"main"}, [
|
35
|
+
# >> H[:p, [
|
36
|
+
# >> "foo coming up!"]],
|
37
|
+
# >> H[:p, {"class"=>"foo"}, [
|
38
|
+
# >> H[:br],
|
39
|
+
# >> "awesome",
|
40
|
+
# >> H[:br]]],
|
41
|
+
# >> H[:p, [
|
42
|
+
# >> "foo coming up!"]],
|
43
|
+
# >> H[:p, {"class"=>"foo"}, [
|
44
|
+
# >> H[:br],
|
45
|
+
# >> "awesome",
|
46
|
+
# >> H[:br]]]]],
|
47
|
+
# >> H[:hr]]]
|
48
|
+
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
|
49
|
+
# >> <p><div id="main">
|
50
|
+
# >> <p>foo coming up!</p>
|
51
|
+
# >> <p class="foo"><br>awesome<br></p>
|
52
|
+
# >> <p>foo coming up!</p>
|
53
|
+
# >> <p class="foo"><br>awesome<br></p>
|
54
|
+
# >> </div><hr></p>
|
data/lib/hexp.rb
CHANGED
@@ -1,5 +1,25 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'ice_nine'
|
6
|
+
require 'equalizer'
|
7
|
+
|
1
8
|
module Hexp
|
9
|
+
def self.deep_freeze(*args)
|
10
|
+
IceNine.deep_freeze(*args)
|
11
|
+
end
|
2
12
|
end
|
3
13
|
|
4
14
|
require 'hexp/version'
|
5
|
-
|
15
|
+
|
16
|
+
require 'hexp/node'
|
17
|
+
require 'hexp/node/normalize'
|
18
|
+
require 'hexp/node/domize'
|
19
|
+
require 'hexp/node/pp'
|
20
|
+
|
21
|
+
require 'hexp/text_node'
|
22
|
+
require 'hexp/list'
|
23
|
+
require 'hexp/dom'
|
24
|
+
|
25
|
+
require 'hexp/nokogiri/equality'
|
data/lib/hexp/dom.rb
ADDED
data/lib/hexp/h.rb
ADDED
data/lib/hexp/list.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Hexp
|
2
|
+
# A list of nodes
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# Hexp::List[
|
6
|
+
# Hexp::Node[:marquee, "Try Hexp for instanst satisfaction!"],
|
7
|
+
# Hexp::Node[:hr],
|
8
|
+
# ]
|
9
|
+
#
|
10
|
+
class List < SimpleDelegator
|
11
|
+
include Equalizer.new(:__getobj__)
|
12
|
+
|
13
|
+
def initialize(nodes)
|
14
|
+
super Hexp.deep_freeze nodes
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.[](*args)
|
18
|
+
new(args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
__getobj__.inspect
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/hexp/node.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Hexp
|
2
|
+
# A Hexp Node
|
3
|
+
class Node
|
4
|
+
include Equalizer.new(:tag, :attributes, :children)
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :tag, :attributes, :children
|
8
|
+
def_delegators :@children, :empty?
|
9
|
+
|
10
|
+
# Normalize the arguments
|
11
|
+
#
|
12
|
+
# @param args [Array] args a Hexp node
|
13
|
+
# @return [Hexp::Node]
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# Hexp::Node[:p, {'class' => 'foo'}, [[:b, "Hello, World!"]]]
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def initialize(*args)
|
20
|
+
@tag, @attributes, @children = Hexp.deep_freeze(
|
21
|
+
Normalize.new(args).call
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.[](*args)
|
26
|
+
new(*args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_hexp
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_html
|
34
|
+
to_dom.to_html
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_dom
|
38
|
+
Domize.new(self).call
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
self.class.inspect_name + to_a.reject(&:empty?).inspect
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_a
|
46
|
+
[tag, attributes, children]
|
47
|
+
end
|
48
|
+
|
49
|
+
def pp
|
50
|
+
self.class::PP.new(self).call
|
51
|
+
end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
def inspect_name
|
55
|
+
if defined?(H) && H == self
|
56
|
+
'H'
|
57
|
+
else
|
58
|
+
self.name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Hexp
|
2
|
+
class Node
|
3
|
+
# Turn nodes into DOM objects
|
4
|
+
class Domize
|
5
|
+
attr_reader :dom
|
6
|
+
|
7
|
+
def initialize(hexp, dom = Hexp::DOM)
|
8
|
+
@raw = hexp
|
9
|
+
@dom = dom
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
dom::Document.new.tap do |doc|
|
14
|
+
@doc = doc
|
15
|
+
doc << domize(@raw)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def domize(hexp)
|
22
|
+
dom::Node.new(hexp.tag.to_s, @doc).tap do |node|
|
23
|
+
set_attributes(node, hexp.attributes)
|
24
|
+
set_children(node, hexp.children)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_attributes(node, attributes)
|
29
|
+
attributes.each do |key,value|
|
30
|
+
node[key] = value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_children(node, children)
|
35
|
+
children.each do |child|
|
36
|
+
if child.instance_of?(TextNode)
|
37
|
+
node << dom::Text.new(child, @doc)
|
38
|
+
else
|
39
|
+
node << domize(child)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Hexp
|
2
|
+
class Node
|
3
|
+
# Normalize a node
|
4
|
+
#
|
5
|
+
class Normalize
|
6
|
+
# Set a node to be normalized
|
7
|
+
#
|
8
|
+
# @param [Array] node A non-strict hexp
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Hexp::Node::Normalize.new([:p, {class:'foo'}])
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
#
|
15
|
+
def initialize(node)
|
16
|
+
@raw = node
|
17
|
+
end
|
18
|
+
|
19
|
+
# Normalize to strict hexp nodes, cfr SPEC.md for details
|
20
|
+
#
|
21
|
+
# @return [Array] strict hexp node
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
def call
|
26
|
+
[@raw.first, normalized_attributes, normalized_children]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Pulls the attributes hash out of a non-strict hexp
|
32
|
+
#
|
33
|
+
# @return [Hash] the attributes hash
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
def attributes
|
38
|
+
attrs = @raw[1]
|
39
|
+
return attrs if attrs.instance_of?(Hash)
|
40
|
+
{}
|
41
|
+
end
|
42
|
+
|
43
|
+
def normalized_attributes
|
44
|
+
Hash[*
|
45
|
+
attributes.flat_map do |key, value|
|
46
|
+
[key, value].map(&:to_s)
|
47
|
+
end
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Pulls the children list out of a non-strict hexp
|
52
|
+
#
|
53
|
+
# @return [Array] the list of child hexps, non-strict
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
#
|
57
|
+
def children
|
58
|
+
@raw[1..2].each do |arg|
|
59
|
+
return Array(arg) unless [Symbol, Hash].any?{|klz| arg.instance_of?(klz)}
|
60
|
+
end
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Normalize the third element of a hexp node, the list of children
|
65
|
+
#
|
66
|
+
# @return [Array] list of normalized hexps
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
#
|
70
|
+
def normalized_children
|
71
|
+
Hexp::List[*
|
72
|
+
children.map do |child|
|
73
|
+
case child
|
74
|
+
when String, TextNode
|
75
|
+
Hexp::TextNode.new(child)
|
76
|
+
when Array
|
77
|
+
Hexp::Node[*child]
|
78
|
+
else
|
79
|
+
if child.respond_to? :to_hexp
|
80
|
+
Hexp::Node[*child.to_hexp]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
data/lib/hexp/node/pp.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Hexp
|
2
|
+
class Node
|
3
|
+
# Pretty-print a node and its contents
|
4
|
+
class PP
|
5
|
+
def initialize(node)
|
6
|
+
@node = node
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
[
|
11
|
+
@node.class.inspect_name,
|
12
|
+
pp_tag,
|
13
|
+
PP.indent(pp_attributes + pp_children).strip
|
14
|
+
].join
|
15
|
+
end
|
16
|
+
|
17
|
+
def pp_tag
|
18
|
+
"[#{@node.tag.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def pp_attributes
|
22
|
+
attrs = @node.attributes
|
23
|
+
return '' if attrs.empty?
|
24
|
+
', ' + attrs.inspect
|
25
|
+
end
|
26
|
+
|
27
|
+
def pp_children
|
28
|
+
children = @node.children
|
29
|
+
return ']' if children.empty?
|
30
|
+
", [\n#{ children.map(&:pp).join(",\n") }]]"
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.indent(string, indent = 2)
|
34
|
+
string.lines.map {|line| " "*indent + line}.join
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Hexp
|
2
|
+
module Nokogiri
|
3
|
+
# Used in test to see if two Nokogiri objects have the same content,
|
4
|
+
# i.e. are equivalent as far as we are concerned
|
5
|
+
class Equality
|
6
|
+
CLASSES = [
|
7
|
+
::Nokogiri::HTML::Document,
|
8
|
+
::Nokogiri::HTML::DocumentFragment,
|
9
|
+
::Nokogiri::XML::Document,
|
10
|
+
::Nokogiri::XML::Node,
|
11
|
+
::Nokogiri::XML::Text,
|
12
|
+
::Nokogiri::XML::Element,
|
13
|
+
::Nokogiri::XML::DocumentFragment,
|
14
|
+
::Nokogiri::XML::DTD,
|
15
|
+
]
|
16
|
+
|
17
|
+
def initialize(this, that)
|
18
|
+
@this, @that = this, that
|
19
|
+
[this, that].each do |input|
|
20
|
+
raise "#{input.class} is not a Nokogiri element." unless CLASSES.include?(input.class)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def call
|
25
|
+
[ equal_class?,
|
26
|
+
equal_name?,
|
27
|
+
equal_children?,
|
28
|
+
equal_attributes?,
|
29
|
+
equal_text? ].all?
|
30
|
+
end
|
31
|
+
|
32
|
+
def equal_class?
|
33
|
+
@this.class == @that.class
|
34
|
+
end
|
35
|
+
|
36
|
+
def equal_name?
|
37
|
+
@this.name == @that.name
|
38
|
+
end
|
39
|
+
|
40
|
+
def equal_children?
|
41
|
+
return true unless @this.respond_to? :children
|
42
|
+
@this.children.count == @that.children.count &&
|
43
|
+
compare_children.all?
|
44
|
+
end
|
45
|
+
|
46
|
+
def compare_children
|
47
|
+
@this.children.map.with_index do |child, idx|
|
48
|
+
self.class.new(child, @that.children[idx]).call
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def equal_attributes?
|
53
|
+
return true unless @this.respond_to? :attributes
|
54
|
+
@this.attributes.keys.all? do |key|
|
55
|
+
@this[key] == @that[key]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def equal_text?
|
60
|
+
return true unless @this.instance_of?(::Nokogiri::XML::Text)
|
61
|
+
@this.text == @that.text
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Hexp
|
2
|
+
# Represents text inside HTML, at the moment a wrapper
|
3
|
+
# around a plain String. Needs work
|
4
|
+
class TextNode < SimpleDelegator
|
5
|
+
def inspect
|
6
|
+
__getobj__.inspect
|
7
|
+
end
|
8
|
+
|
9
|
+
def tree_walk
|
10
|
+
yield self
|
11
|
+
end
|
12
|
+
|
13
|
+
def attributes
|
14
|
+
{}.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def pp
|
18
|
+
inspect
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_a
|
22
|
+
[:text, self, Hexp::List[]]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/hexp/version.rb
CHANGED
data/spec/shared_helper.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hexp::Node::Domize do
|
4
|
+
def build_doc(&blk)
|
5
|
+
Nokogiri::HTML::Builder.new(&blk).doc
|
6
|
+
end
|
7
|
+
|
8
|
+
subject { Hexp::Node::Domize.new(hexp).call }
|
9
|
+
|
10
|
+
context 'with the same single node' do
|
11
|
+
let(:dom) { build_doc { html } }
|
12
|
+
let(:hexp) { Hexp::Node[:html] }
|
13
|
+
|
14
|
+
it { should dom_eq(dom) }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with a different single node' do
|
18
|
+
let(:dom) { build_doc { html } }
|
19
|
+
let(:hexp) { Hexp::Node[:body] }
|
20
|
+
|
21
|
+
it { should_not dom_eq(dom) }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with nested nodes' do
|
25
|
+
let(:dom) { build_doc { html { div(class: 'big') } } }
|
26
|
+
let(:hexp) { Hexp::Node[:html, [ [:div, class: 'big'] ]] }
|
27
|
+
|
28
|
+
it { should dom_eq(dom) }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with equal text nodes' do
|
32
|
+
let(:dom) { build_doc {
|
33
|
+
html do
|
34
|
+
div(class: 'big')
|
35
|
+
text "awesometown!"
|
36
|
+
end
|
37
|
+
} }
|
38
|
+
let(:hexp) {
|
39
|
+
Hexp::Node[:html, [
|
40
|
+
[:div, class: 'big'],
|
41
|
+
"awesometown!"
|
42
|
+
]
|
43
|
+
]
|
44
|
+
}
|
45
|
+
|
46
|
+
it { should dom_eq(dom) }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'with differing text nodes' do
|
50
|
+
let(:dom) { build_doc {
|
51
|
+
html do
|
52
|
+
div(class: 'big')
|
53
|
+
text "awesomevillage!"
|
54
|
+
end
|
55
|
+
} }
|
56
|
+
let(:hexp) {
|
57
|
+
Hexp::Node[:html, [
|
58
|
+
[:div, class: 'big'],
|
59
|
+
"awesometown!"
|
60
|
+
]
|
61
|
+
]
|
62
|
+
}
|
63
|
+
|
64
|
+
it { should_not dom_eq(dom) }
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hexp::Node::Normalize, '#call' do
|
4
|
+
subject { Hexp::Node[*node] }
|
5
|
+
|
6
|
+
describe 'with a single element' do
|
7
|
+
let (:node) { [:div] }
|
8
|
+
|
9
|
+
it 'should treat the first as the tag' do
|
10
|
+
subject.tag.should == :div
|
11
|
+
end
|
12
|
+
it 'should set an empty attribute list' do
|
13
|
+
subject.attributes.should == {}
|
14
|
+
end
|
15
|
+
it 'should set an empty children list' do
|
16
|
+
subject.children.should == Hexp::List[]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'with two parameters' do
|
21
|
+
let (:node) { [:div, {class: 'foo'}] }
|
22
|
+
|
23
|
+
it 'should treat the first as the tag' do
|
24
|
+
subject.tag.should == :div
|
25
|
+
end
|
26
|
+
it 'should treat the second as the attribute list, if it is a Hash' do
|
27
|
+
subject.attributes.should == {'class' => 'foo'}
|
28
|
+
end
|
29
|
+
it 'should treat the second as a list of children, if it is an Array' do
|
30
|
+
subject.children.should == Hexp::List[]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'with a single text child node' do
|
35
|
+
let(:node) { [:div, "this is text in the div"] }
|
36
|
+
|
37
|
+
it 'should set is as the single child' do
|
38
|
+
subject.children.should == Hexp::List["this is text in the div"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'with child nodes' do
|
43
|
+
let(:node) {
|
44
|
+
[:div, [
|
45
|
+
[:h1, "Big Title"],
|
46
|
+
[:p, {class: 'greeting'}, "hello world"],
|
47
|
+
"Some loose text"
|
48
|
+
]
|
49
|
+
]
|
50
|
+
}
|
51
|
+
|
52
|
+
it 'must normalize them recursively' do
|
53
|
+
subject.children.should == Hexp::List[
|
54
|
+
Hexp::Node[:h1, {}, Hexp::List["Big Title"] ],
|
55
|
+
Hexp::Node[:p, {class: 'greeting'}, Hexp::List["hello world"] ],
|
56
|
+
"Some loose text"
|
57
|
+
]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'with an object that responds to to_hexp' do
|
62
|
+
let(:hexpable) {
|
63
|
+
Class.new do
|
64
|
+
def to_hexp
|
65
|
+
Hexp::Node[:em, "I am in your hexpz"]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
}
|
69
|
+
let(:node) {
|
70
|
+
[:div, [ hexpable.new ] ]
|
71
|
+
}
|
72
|
+
|
73
|
+
it 'must expand that object' do
|
74
|
+
subject.children.should == Hexp::List[
|
75
|
+
Hexp::Node[:em, {}, Hexp::List["I am in your hexpz"] ]
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hexp::Node, 'pp' do
|
4
|
+
subject { object.pp }
|
5
|
+
|
6
|
+
context 'with no attributes or children' do
|
7
|
+
let(:object) { Hexp::Node[:p, {}] }
|
8
|
+
it { should == 'Hexp::Node[:p]'}
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'with a single child' do
|
12
|
+
let(:object) { Hexp::Node[:p, [ [:abbr, {title: 'YAGNI'}, "You ain't gonna need it"] ]] }
|
13
|
+
it { should == %q^Hexp::Node[:p, [
|
14
|
+
Hexp::Node[:abbr, {"title"=>"YAGNI"}, [
|
15
|
+
"You ain't gonna need it"]]]]^.gsub(' '*22, '')
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hexp::Node, 'to_dom' do
|
4
|
+
subject { Hexp::Node[:blink] }
|
5
|
+
|
6
|
+
it 'should delegate to Domize' do
|
7
|
+
Hexp::Node::Domize.should_receive(:new).with(subject).and_return( ->{ 'result' } )
|
8
|
+
subject.to_dom.should == 'result'
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hexp::Nokogiri::Equality do
|
4
|
+
let(:doc) { Nokogiri::HTML::Document.new }
|
5
|
+
|
6
|
+
context 'two empty documents' do
|
7
|
+
it 'should be equal' do
|
8
|
+
described_class.new(Nokogiri::HTML::Document.new, Nokogiri::HTML::Document.new).call.should be_true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'two nodes with the same attributes' do
|
13
|
+
it 'should be equal' do
|
14
|
+
node1 = Nokogiri::XML::Node.new('div', doc)
|
15
|
+
node1['class'] = 'hello'
|
16
|
+
node2 = Nokogiri::XML::Node.new('div', doc)
|
17
|
+
node2['class'] = 'hello'
|
18
|
+
|
19
|
+
described_class.new(node1, node2).call.should be_true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'one node has an attribute more' do
|
24
|
+
it 'should be equal' do
|
25
|
+
node1 = Nokogiri::XML::Node.new('div', doc)
|
26
|
+
node1['class'] = 'hello'
|
27
|
+
node2 = Nokogiri::XML::Node.new('div', doc)
|
28
|
+
node2['class'] = 'hello'
|
29
|
+
node2['id'] = 'zigzag'
|
30
|
+
|
31
|
+
described_class.new(node1, node2).call.should be_true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'two nodes with the same children' do
|
36
|
+
it 'should be equal' do
|
37
|
+
node1 = Nokogiri::XML::Node.new('div', doc)
|
38
|
+
node1 << Nokogiri::XML::Node.new('p', doc)
|
39
|
+
node2 = Nokogiri::XML::Node.new('div', doc)
|
40
|
+
node2 << Nokogiri::XML::Node.new('p', doc)
|
41
|
+
|
42
|
+
described_class.new(node1, node2).call.should be_true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'two nodes with a child of a different name' do
|
47
|
+
it 'should not be equal' do
|
48
|
+
node1 = Nokogiri::XML::Node.new('div', doc)
|
49
|
+
node1 << Nokogiri::XML::Node.new('p', doc)
|
50
|
+
node2 = Nokogiri::XML::Node.new('div', doc)
|
51
|
+
node2 << Nokogiri::XML::Node.new('em', doc)
|
52
|
+
|
53
|
+
described_class.new(node1, node2).call.should be_false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'one node has a child more than the other, otherwise identical' do
|
58
|
+
it 'should not be equal' do
|
59
|
+
node1 = Nokogiri::XML::Node.new('div', doc)
|
60
|
+
node1 << Nokogiri::XML::Node.new('p', doc)
|
61
|
+
node2 = Nokogiri::XML::Node.new('div', doc)
|
62
|
+
node2 << Nokogiri::XML::Node.new('p', doc)
|
63
|
+
node2 << Nokogiri::XML::Node.new('em', doc)
|
64
|
+
|
65
|
+
described_class.new(node1, node2).call.should be_false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.
|
4
|
+
version: 0.0.1.pre2
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-06-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ice_nine
|
@@ -43,18 +43,35 @@ files:
|
|
43
43
|
- README.md
|
44
44
|
- Rakefile
|
45
45
|
- SPEC.md
|
46
|
+
- config/devtools.yml
|
46
47
|
- config/flay.yml
|
47
48
|
- config/flog.yml
|
48
49
|
- config/mutant.yml
|
49
50
|
- config/reek.yml
|
50
51
|
- config/yardstick.yml
|
52
|
+
- examples/filter.rb
|
51
53
|
- hexp.gemspec
|
52
54
|
- lib/hexp.rb
|
53
|
-
- lib/hexp/
|
55
|
+
- lib/hexp/dom.rb
|
56
|
+
- lib/hexp/h.rb
|
57
|
+
- lib/hexp/list.rb
|
58
|
+
- lib/hexp/node.rb
|
59
|
+
- lib/hexp/node/domize.rb
|
60
|
+
- lib/hexp/node/normalize.rb
|
61
|
+
- lib/hexp/node/pp.rb
|
62
|
+
- lib/hexp/nokogiri/equality.rb
|
63
|
+
- lib/hexp/text_node.rb
|
54
64
|
- lib/hexp/version.rb
|
55
65
|
- spec/shared_helper.rb
|
56
66
|
- spec/spec_helper.rb
|
57
|
-
- spec/unit/hexp/
|
67
|
+
- spec/unit/hexp/node/domize_spec.rb
|
68
|
+
- spec/unit/hexp/node/normalize_spec.rb
|
69
|
+
- spec/unit/hexp/node/pp_spec.rb
|
70
|
+
- spec/unit/hexp/node/to_a_spec.rb
|
71
|
+
- spec/unit/hexp/node/to_dom_spec.rb
|
72
|
+
- spec/unit/hexp/node/to_hexp_spec.rb
|
73
|
+
- spec/unit/hexp/node/to_html_spec.rb
|
74
|
+
- spec/unit/hexp/nokogiri/equality_spec.rb
|
58
75
|
homepage: https://github.com/plexus/hexp
|
59
76
|
licenses: []
|
60
77
|
post_install_message:
|
@@ -69,7 +86,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
86
|
version: '0'
|
70
87
|
segments:
|
71
88
|
- 0
|
72
|
-
hash:
|
89
|
+
hash: 4219632061257453794
|
73
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
91
|
none: false
|
75
92
|
requirements:
|
@@ -85,4 +102,11 @@ summary: HTML expressions
|
|
85
102
|
test_files:
|
86
103
|
- spec/shared_helper.rb
|
87
104
|
- spec/spec_helper.rb
|
88
|
-
- spec/unit/hexp/
|
105
|
+
- spec/unit/hexp/node/domize_spec.rb
|
106
|
+
- spec/unit/hexp/node/normalize_spec.rb
|
107
|
+
- spec/unit/hexp/node/pp_spec.rb
|
108
|
+
- spec/unit/hexp/node/to_a_spec.rb
|
109
|
+
- spec/unit/hexp/node/to_dom_spec.rb
|
110
|
+
- spec/unit/hexp/node/to_hexp_spec.rb
|
111
|
+
- spec/unit/hexp/node/to_html_spec.rb
|
112
|
+
- spec/unit/hexp/nokogiri/equality_spec.rb
|
data/lib/hexp/array.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
module Hexp
|
2
|
-
class Array < ::Array
|
3
|
-
def self.[](*args)
|
4
|
-
super(*normalize(args))
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.normalize(args)
|
8
|
-
idx = 0
|
9
|
-
case args[idx]
|
10
|
-
when Symbol
|
11
|
-
tag = args[idx] ; idx+=1
|
12
|
-
attrs = case args[idx]
|
13
|
-
when Hash
|
14
|
-
idx += 1 ; args[idx-1]
|
15
|
-
else
|
16
|
-
{}
|
17
|
-
end
|
18
|
-
children = normalize_children args[idx]
|
19
|
-
[tag, attrs, children]
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.normalize_children(children)
|
24
|
-
case children
|
25
|
-
when String
|
26
|
-
[ children ]
|
27
|
-
when ::Array
|
28
|
-
children.map do |child|
|
29
|
-
case child
|
30
|
-
when String
|
31
|
-
child
|
32
|
-
when ::Array
|
33
|
-
Hexp::Array[*child]
|
34
|
-
else
|
35
|
-
raise "bad input #{child}"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
else
|
39
|
-
[]
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
H=Hexp::Array
|
4
|
-
|
5
|
-
describe Hexp::Array do
|
6
|
-
describe 'normalization' do
|
7
|
-
describe 'with a single parameter' do
|
8
|
-
it 'should return a triplet' do
|
9
|
-
H[:div].count.should == 3
|
10
|
-
end
|
11
|
-
it 'should treat the first as the tag' do
|
12
|
-
H[:div][0].should == :div
|
13
|
-
end
|
14
|
-
it 'should set an empty attribute list' do
|
15
|
-
H[:div][1].should == {}
|
16
|
-
end
|
17
|
-
it 'should set an empty children list' do
|
18
|
-
H[:div][2].should == []
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe 'with two parameters' do
|
23
|
-
it 'should treat the first as the tag' do
|
24
|
-
H[:div, {class: 'foo'}][0].should == :div
|
25
|
-
end
|
26
|
-
it 'should treat the second as the attribute list, if it is a Hash' do
|
27
|
-
H[:div, {class: 'foo'}][1].should == {class: 'foo'}
|
28
|
-
end
|
29
|
-
it 'should treat the second as a list of children, if it is an Array' do
|
30
|
-
H[:div, []][2].should == []
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
describe 'with a single text child node' do
|
35
|
-
it 'should set is as the single child' do
|
36
|
-
H[:div, "this is text in the div"][2].should == ["this is text in the div"]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe 'with child nodes' do
|
41
|
-
it 'must normalize them recursively' do
|
42
|
-
H[:div, [
|
43
|
-
[:h1, "Big Title"],
|
44
|
-
[:p, {class: 'greeting'}, "hello world"],
|
45
|
-
"Some loose text"
|
46
|
-
]
|
47
|
-
][2].should == [
|
48
|
-
[:h1, {}, ["Big Title"] ],
|
49
|
-
[:p, {class: 'greeting'}, ["hello world"] ],
|
50
|
-
"Some loose text"
|
51
|
-
]
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
describe 'with bad input' do
|
56
|
-
it 'should raise exception' do
|
57
|
-
expect { H[:p, {}, [123]] }.to raise_exception
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|