hom 0.4.0 → 1.3.1
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 +7 -0
- data/CHANGES.md +57 -0
- data/LICENSE.txt +4 -0
- data/README.md +87 -18
- data/hom.gemspec +11 -4
- data/lib/hom.rb +50 -4
- metadata +22 -38
- data/Rakefile +0 -8
- data/spec/element_spec.rb +0 -71
- data/spec/entity_spec.rb +0 -75
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 80265a394bf1cecc27755bfcc583dbc81cb3ea51ffcb110fae6a61ecc3d0f0cd
|
4
|
+
data.tar.gz: 9ab3f813474864c3db170c4add2780309a2470f8bc4acca7c6daa2355bd27087
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1d7d556b5749cd886a0731f39734e32cd34dde6ee8ffc0c07294034d0554c0f9bc2f6c35c51bf1f206b50bd9f03a6c01fae28bab158590f1792782f1bee341d6
|
7
|
+
data.tar.gz: 6e27e6d36d7648c968c8bec6c45a99c263e056945eacab2b4de3538220a40f5ad881e5cf30974e5561c0645044eb3ea9968cda6c2ea0e4ab148e1f1d207206c7
|
data/CHANGES.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# 1.3.1
|
2
|
+
|
3
|
+
* Added metadata to gemspec
|
4
|
+
|
5
|
+
# 1.3.0
|
6
|
+
|
7
|
+
* HOM::NodeList#join now returns a new node list
|
8
|
+
|
9
|
+
* Added plus method to HOM::Element
|
10
|
+
|
11
|
+
* Added plus method to HOM::Entity
|
12
|
+
|
13
|
+
# 1.2.0
|
14
|
+
|
15
|
+
* Added html_safe? methods to HOM::Element and HOM::NodeList for compatibility with [erubis-auto](https://github.com/timcraft/erubis-auto)
|
16
|
+
|
17
|
+
# 1.1.0
|
18
|
+
|
19
|
+
* Added HOM::NodeList class
|
20
|
+
|
21
|
+
# 1.0.0
|
22
|
+
|
23
|
+
* Ruby 1.8.7 compatibility
|
24
|
+
|
25
|
+
* Removed support for rendering arbitrary objects by calling #html
|
26
|
+
|
27
|
+
# 0.4.0
|
28
|
+
|
29
|
+
* Deprecated support for rendering arbitrary objects by calling #html
|
30
|
+
|
31
|
+
* Added notion of undefined content, changing how elements with nil content are rendered:
|
32
|
+
|
33
|
+
With an element like this: `HOM::Element.new(:h1, {}, nil)`
|
34
|
+
|
35
|
+
Pre v0.4.0 rendering: `<h1>`
|
36
|
+
|
37
|
+
Post v0.4.0 rendering: `<h1></h1>`
|
38
|
+
|
39
|
+
# 0.3.0
|
40
|
+
|
41
|
+
* Added notion of undefined attribute values, nil is now rendered as an empty value:
|
42
|
+
|
43
|
+
With an element like this: `HOM::Element.new(:input, {:value => nil})`
|
44
|
+
|
45
|
+
Pre v0.3.0 rendering: `<input value>`
|
46
|
+
|
47
|
+
Post v0.3.0 rendering: `<input value="">`
|
48
|
+
|
49
|
+
* Removed Element#lookup method
|
50
|
+
|
51
|
+
# 0.2.0
|
52
|
+
|
53
|
+
* Replaced method missing attribute access with Element#lookup method
|
54
|
+
|
55
|
+
# 0.1.0
|
56
|
+
|
57
|
+
* First version!
|
data/LICENSE.txt
ADDED
data/README.md
CHANGED
@@ -1,19 +1,37 @@
|
|
1
|
-
|
2
|
-
==============================================
|
1
|
+
# hom
|
3
2
|
|
4
|
-
|
5
|
-
|
3
|
+
[](https://badge.fury.io/rb/hom) [](https://travis-ci.org/timcraft/hom)
|
4
|
+
|
5
|
+
|
6
|
+
A straightforward API for generating HTML.
|
7
|
+
|
8
|
+
|
9
|
+
## Motivation
|
6
10
|
|
7
11
|
HOM helps you implement HTML presentation logic in your code. Things like
|
8
12
|
navigation links, select boxes, sets of checkboxes; anything with behaviour
|
9
13
|
that is too complex for your templates.
|
10
14
|
|
11
|
-
Usage
|
12
|
-
-----
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
$ gem install hom
|
19
|
+
|
20
|
+
|
21
|
+
## Quick start
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'hom'
|
25
|
+
|
26
|
+
puts HOM::Element.new(:h1, nil, 'hello world')
|
27
|
+
```
|
28
|
+
|
29
|
+
|
30
|
+
## Using HOM::Element
|
31
|
+
|
32
|
+
Create instances of HOM::Element to represent DOM elements. The first
|
33
|
+
constructor argument is a symbol representing the tag name. For example,
|
34
|
+
you can represent a line break element like this:
|
17
35
|
|
18
36
|
```ruby
|
19
37
|
HOM::Element.new(:br)
|
@@ -32,8 +50,8 @@ HOM::Element.new(:input, [{type: :text, size: 30}, :disabled])
|
|
32
50
|
```
|
33
51
|
|
34
52
|
The third constructor argument is the inner content, which can be a string,
|
35
|
-
another element object, or an array of child
|
36
|
-
you can represent elements with
|
53
|
+
another element object, or an array of child nodes. For example, here's how
|
54
|
+
you can represent various elements with inner content:
|
37
55
|
|
38
56
|
```ruby
|
39
57
|
span = HOM::Element.new(:span, nil, '')
|
@@ -47,16 +65,67 @@ link = HOM::Element.new(:a, {target: :_blank, href: '/'}, image)
|
|
47
65
|
list = HOM::Element.new(:ul, nil, (1..3).map { |n| HOM::Element.new(:li, nil, n) })
|
48
66
|
```
|
49
67
|
|
50
|
-
|
51
|
-
|
68
|
+
Calling #to_s on a HOM::Element object will return a string containing the
|
69
|
+
generated HTML markup. HOM::Element objects are safe to use directly in Rails
|
70
|
+
templates, all escaping is handled automatically.
|
71
|
+
|
72
|
+
|
73
|
+
## Using HOM::Entity
|
74
|
+
|
75
|
+
Create instances of HOM::Entity to represent HTML entities. Use an integer
|
76
|
+
argument for numeric entities and a symbol/string argument for named entities,
|
52
77
|
for example:
|
53
78
|
|
54
79
|
```ruby
|
55
|
-
HOM::
|
80
|
+
HOM::Entity.new(160)
|
81
|
+
|
82
|
+
HOM::Entity.new(:nbsp)
|
83
|
+
```
|
84
|
+
|
85
|
+
|
86
|
+
## Using HOM::NodeList
|
87
|
+
|
88
|
+
Use HOM::NodeList to group nodes together without having to wrap them in an
|
89
|
+
outer element. For example:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
HOM::NodeList.new(['This is a ', HOM::Element.new(:strong, nil, 'Contrived'), ' example'])
|
93
|
+
```
|
94
|
+
|
95
|
+
Calling #to_s on a HOM::NodeList object will return a string containing the
|
96
|
+
generated HTML markup. Calling #join will insert separator nodes, a bit like
|
97
|
+
Array#join, but returning HTML safe output.
|
98
|
+
|
99
|
+
|
100
|
+
## XSS 101
|
101
|
+
|
102
|
+
Do you have helper methods that look like this:
|
56
103
|
|
57
|
-
|
104
|
+
```ruby
|
105
|
+
def user_name(user)
|
106
|
+
"<strong>#{user.name}</strong>".html_safe
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
Bzzzt, that's a security vulnerability right there. If you're using Rails
|
111
|
+
you should have code that looks like this:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
def user_name(user)
|
115
|
+
content_tag(:strong, user.name)
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
The content_tag helper will automatically escape content not explicitly
|
120
|
+
marked as safe. HOM will do very much the same thing, the equivalent helper
|
121
|
+
method would look like this:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
def user_name(user)
|
125
|
+
HOM::Element.new(:strong, nil, user.name)
|
126
|
+
end
|
58
127
|
```
|
59
128
|
|
60
|
-
|
61
|
-
|
62
|
-
|
129
|
+
Moral of the story: building up fragments of HTML using string interpolation
|
130
|
+
and concatenation is highly error prone. Solution: use content_tag or HOM to
|
131
|
+
safely build your content, and audit your usage of html_safe.
|
data/hom.gemspec
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'hom'
|
3
|
-
s.version = '
|
3
|
+
s.version = '1.3.1'
|
4
|
+
s.license = 'LGPL-3.0'
|
4
5
|
s.platform = Gem::Platform::RUBY
|
5
6
|
s.authors = ['Tim Craft']
|
6
7
|
s.email = ['mail@timcraft.com']
|
7
|
-
s.homepage = '
|
8
|
+
s.homepage = 'https://github.com/timcraft/hom'
|
8
9
|
s.description = 'A straightforward API for generating HTML'
|
9
10
|
s.summary = 'See description'
|
10
|
-
s.files = Dir.glob('
|
11
|
-
s.
|
11
|
+
s.files = Dir.glob('lib/**/*.rb') + %w(LICENSE.txt README.md CHANGES.md hom.gemspec)
|
12
|
+
s.required_ruby_version = '>= 1.9.3'
|
12
13
|
s.require_path = 'lib'
|
14
|
+
s.metadata = {
|
15
|
+
'homepage' => 'https://github.com/timcraft/hom',
|
16
|
+
'source_code_uri' => 'https://github.com/timcraft/hom',
|
17
|
+
'bug_tracker_uri' => 'https://github.com/timcraft/hom/issues',
|
18
|
+
'changelog_uri' => 'https://github.com/timcraft/hom/blob/main/CHANGES.md'
|
19
|
+
}
|
13
20
|
end
|
data/lib/hom.rb
CHANGED
@@ -25,6 +25,10 @@ module HOM
|
|
25
25
|
def html_safe?
|
26
26
|
true
|
27
27
|
end
|
28
|
+
|
29
|
+
def +(object)
|
30
|
+
NodeList.new([self, object])
|
31
|
+
end
|
28
32
|
end
|
29
33
|
|
30
34
|
class Element
|
@@ -38,9 +42,53 @@ module HOM
|
|
38
42
|
@content != Undefined
|
39
43
|
end
|
40
44
|
|
45
|
+
def html_safe?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
Encoding.safe_encode(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
def +(object)
|
54
|
+
NodeList.new([self, object])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class NodeList
|
59
|
+
def initialize(nodes)
|
60
|
+
@nodes = Array(nodes)
|
61
|
+
end
|
62
|
+
|
63
|
+
def html_safe?
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
41
67
|
def to_s
|
42
68
|
Encoding.safe_encode(self)
|
43
69
|
end
|
70
|
+
|
71
|
+
def to_a
|
72
|
+
@nodes
|
73
|
+
end
|
74
|
+
|
75
|
+
def +(object)
|
76
|
+
self.class.new(@nodes + Array(object))
|
77
|
+
end
|
78
|
+
|
79
|
+
def join(separator)
|
80
|
+
self.class.new(intersperse(separator, @nodes))
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def intersperse(separator, array)
|
86
|
+
array.inject([]) do |tmp, item|
|
87
|
+
tmp << separator unless tmp.empty?
|
88
|
+
tmp << item
|
89
|
+
tmp
|
90
|
+
end
|
91
|
+
end
|
44
92
|
end
|
45
93
|
|
46
94
|
class AttributeList
|
@@ -80,12 +128,10 @@ module HOM
|
|
80
128
|
def self.encode(object)
|
81
129
|
if object.is_a?(Array)
|
82
130
|
object.map { |item| encode(item) }.join
|
131
|
+
elsif object.is_a?(NodeList)
|
132
|
+
object.to_a.map { |item| encode(item) }.join
|
83
133
|
elsif object.is_a?(Element)
|
84
134
|
encode_element(object)
|
85
|
-
elsif object.respond_to?(:html) # TODO: REMOVE ME
|
86
|
-
Kernel.warn '[hom] defining #html on custom objects is deprecated, map the objects to elements instead'
|
87
|
-
|
88
|
-
object.html
|
89
135
|
elsif object.respond_to?(:html_safe?) && object.html_safe?
|
90
136
|
object.to_s
|
91
137
|
else
|
metadata
CHANGED
@@ -1,32 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 1.3.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Tim Craft
|
9
|
-
autorequire:
|
8
|
+
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: activesupport
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: 3.0.3
|
22
|
-
type: :development
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 3.0.3
|
11
|
+
date: 2020-11-03 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
30
13
|
description: A straightforward API for generating HTML
|
31
14
|
email:
|
32
15
|
- mail@timcraft.com
|
@@ -34,35 +17,36 @@ executables: []
|
|
34
17
|
extensions: []
|
35
18
|
extra_rdoc_files: []
|
36
19
|
files:
|
37
|
-
-
|
38
|
-
-
|
39
|
-
- spec/entity_spec.rb
|
40
|
-
- Rakefile
|
20
|
+
- CHANGES.md
|
21
|
+
- LICENSE.txt
|
41
22
|
- README.md
|
42
23
|
- hom.gemspec
|
43
|
-
|
44
|
-
|
45
|
-
|
24
|
+
- lib/hom.rb
|
25
|
+
homepage: https://github.com/timcraft/hom
|
26
|
+
licenses:
|
27
|
+
- LGPL-3.0
|
28
|
+
metadata:
|
29
|
+
homepage: https://github.com/timcraft/hom
|
30
|
+
source_code_uri: https://github.com/timcraft/hom
|
31
|
+
bug_tracker_uri: https://github.com/timcraft/hom/issues
|
32
|
+
changelog_uri: https://github.com/timcraft/hom/blob/main/CHANGES.md
|
33
|
+
post_install_message:
|
46
34
|
rdoc_options: []
|
47
35
|
require_paths:
|
48
36
|
- lib
|
49
37
|
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
38
|
requirements:
|
52
|
-
- -
|
39
|
+
- - ">="
|
53
40
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
41
|
+
version: 1.9.3
|
55
42
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
-
none: false
|
57
43
|
requirements:
|
58
|
-
- -
|
44
|
+
- - ">="
|
59
45
|
- !ruby/object:Gem::Version
|
60
46
|
version: '0'
|
61
47
|
requirements: []
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
specification_version: 3
|
48
|
+
rubygems_version: 3.1.4
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
66
51
|
summary: See description
|
67
52
|
test_files: []
|
68
|
-
has_rdoc:
|
data/Rakefile
DELETED
data/spec/element_spec.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
require 'active_support/core_ext/string/output_safety'
|
3
|
-
|
4
|
-
require_relative '../lib/hom'
|
5
|
-
|
6
|
-
describe 'Element' do
|
7
|
-
before do
|
8
|
-
@br = HOM::Element.new(:br)
|
9
|
-
@i1 = HOM::Element.new(:input, :disabled)
|
10
|
-
@i2 = HOM::Element.new(:input, {type: :text, size: 30, value: nil})
|
11
|
-
@i3 = HOM::Element.new(:input, [{type: :text, size: 30}, :disabled])
|
12
|
-
@h1 = HOM::Element.new(:h1, nil, '')
|
13
|
-
@h2 = HOM::Element.new(:h2, nil, 'hello world')
|
14
|
-
@h3 = HOM::Element.new(:h3, nil, 'a && b, x > y')
|
15
|
-
@h4 = HOM::Element.new(:h4, nil, 1234567890)
|
16
|
-
@h5 = HOM::Element.new(:h5, nil, HOM::Element.new(:b, nil, 'how bold'))
|
17
|
-
@h6 = HOM::Element.new(:h6, nil, nil)
|
18
|
-
@ul = HOM::Element.new(:ul, nil, (1..3).map { |n| HOM::Element.new(:li, nil, n) })
|
19
|
-
end
|
20
|
-
|
21
|
-
describe 'content query method' do
|
22
|
-
it 'should return false if the content is undefined' do
|
23
|
-
@br.content?.must_equal(false)
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'should return true otherwise' do
|
27
|
-
@h1.content?.must_equal(true)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe 'to_s method' do
|
32
|
-
it 'should return a string containing the correct markup' do
|
33
|
-
@br.to_s.must_equal('<br>')
|
34
|
-
@i1.to_s.must_equal('<input disabled>')
|
35
|
-
@i2.to_s.must_equal('<input type="text" size="30" value="">')
|
36
|
-
@i3.to_s.must_equal('<input type="text" size="30" disabled>')
|
37
|
-
@h1.to_s.must_equal('<h1></h1>')
|
38
|
-
@h2.to_s.must_equal('<h2>hello world</h2>')
|
39
|
-
@h3.to_s.must_equal('<h3>a && b, x > y</h3>')
|
40
|
-
@h4.to_s.must_equal('<h4>1234567890</h4>')
|
41
|
-
@h5.to_s.must_equal('<h5><b>how bold</b></h5>')
|
42
|
-
@h6.to_s.must_equal('<h6></h6>')
|
43
|
-
@ul.to_s.must_equal('<ul><li>1</li><li>2</li><li>3</li></ul>')
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'should not encode content that has been marked as html safe' do
|
47
|
-
HOM::Element.new(:span, nil, '<br>').to_s.must_equal('<span><br></span>')
|
48
|
-
HOM::Element.new(:span, nil, '<br>'.html_safe).to_s.must_equal('<span><br></span>')
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'should return content that is marked as html safe' do
|
52
|
-
@br.to_s.html_safe?.must_equal(true)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
describe 'encoding objects with an html method' do # TODO: REMOVE ME
|
57
|
-
it 'should emit a deprecation warning' do
|
58
|
-
require 'mocha'
|
59
|
-
|
60
|
-
object = Object.new
|
61
|
-
|
62
|
-
def object.html; HOM::Element.new(:span, nil, 'html') end
|
63
|
-
|
64
|
-
div = HOM::Element.new(:div, nil, object)
|
65
|
-
|
66
|
-
Kernel.expects(:warn).with(regexp_matches(/defining #html on custom objects is deprecated/))
|
67
|
-
|
68
|
-
div.to_s.must_equal('<div><span>html</span></div>')
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
data/spec/entity_spec.rb
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
|
3
|
-
require_relative '../lib/hom'
|
4
|
-
|
5
|
-
describe 'Named entity' do
|
6
|
-
before do
|
7
|
-
@entity = HOM::Entity.new(:nbsp)
|
8
|
-
end
|
9
|
-
|
10
|
-
describe 'value method' do
|
11
|
-
it 'should return the symbol value' do
|
12
|
-
@entity.value.must_equal(:nbsp)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
describe 'numeric query method' do
|
17
|
-
it 'should return false' do
|
18
|
-
@entity.numeric?.must_equal(false)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe 'named query method' do
|
23
|
-
it 'should return true' do
|
24
|
-
@entity.named?.must_equal(true)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
describe 'to_s method' do
|
29
|
-
it 'should return the encoded html entity' do
|
30
|
-
@entity.to_s.must_equal(' ')
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
describe 'html_safe query method' do
|
35
|
-
it 'should return true' do
|
36
|
-
@entity.html_safe?.must_equal(true)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe 'Numeric entity' do
|
42
|
-
before do
|
43
|
-
@entity = HOM::Entity.new(160)
|
44
|
-
end
|
45
|
-
|
46
|
-
describe 'value method' do
|
47
|
-
it 'should return the integer value' do
|
48
|
-
@entity.value.must_equal(160)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe 'numeric query method' do
|
53
|
-
it 'should return true' do
|
54
|
-
@entity.numeric?.must_equal(true)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
describe 'named query method' do
|
59
|
-
it 'should return false' do
|
60
|
-
@entity.named?.must_equal(false)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe 'to_s method' do
|
65
|
-
it 'should return the encoded html entity' do
|
66
|
-
@entity.to_s.must_equal(' ')
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe 'html_safe query method' do
|
71
|
-
it 'should return true' do
|
72
|
-
@entity.html_safe?.must_equal(true)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|