hom 0.1.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.
- data/README.txt +50 -0
- data/hom.gemspec +13 -0
- data/lib/hom/attribute.rb +9 -0
- data/lib/hom/attribute_list.rb +36 -0
- data/lib/hom/element.rb +51 -0
- data/lib/hom/entity.rb +21 -0
- data/lib/hom.rb +6 -0
- data/test/hom_attribute_list.rb +53 -0
- data/test/hom_element.rb +46 -0
- data/test/hom_entity.rb +33 -0
- metadata +68 -0
data/README.txt
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
A straightforward API for generating HTML programmatically.
|
2
|
+
|
3
|
+
Install via rubygems (no dependencies):
|
4
|
+
|
5
|
+
gem install hom
|
6
|
+
|
7
|
+
Either require it, or add it to your Rails Gemfile:
|
8
|
+
|
9
|
+
require 'hom'
|
10
|
+
|
11
|
+
gem 'hom'
|
12
|
+
|
13
|
+
Generate HTML using HOM::Element objects. The first constuctor argument is
|
14
|
+
a symbol representing the tag name, for example:
|
15
|
+
|
16
|
+
HOM::Element.new(:br)
|
17
|
+
|
18
|
+
The second constructor argument represents the element attributes, and can be
|
19
|
+
nil, a single symbol/string, a hash, or an array of hashes and symbols/strings.
|
20
|
+
For example:
|
21
|
+
|
22
|
+
HOM::Element.new(:input, :disabled)
|
23
|
+
|
24
|
+
HOM::Element.new(:input, {type: :text, size: 30})
|
25
|
+
|
26
|
+
HOM::Element.new(:input, [{type: :text, size: 30}, :disabled])
|
27
|
+
|
28
|
+
The third constructor argument is the inner content, which can be a string,
|
29
|
+
another element object, or an array of child items. For example:
|
30
|
+
|
31
|
+
span = HOM::Element.new(:span, nil, '')
|
32
|
+
|
33
|
+
h1 = HOM::Element.new(:h1, {}, 'hello world')
|
34
|
+
|
35
|
+
image = HOM::Element.new(:img, {src: 'image.png', width: 100, height: 30})
|
36
|
+
|
37
|
+
link = HOM::Element.new(:a, {target: :_blank, href: '/'}, image)
|
38
|
+
|
39
|
+
list = HOM::Element.new(:ul, nil, (1..3).map { |n| HOM::Element.new(:li, nil, n) })
|
40
|
+
|
41
|
+
Use HOM::Entity objects for HTML entities; integer values for numeric entities
|
42
|
+
and symbol/string values for named entities, for example:
|
43
|
+
|
44
|
+
HOM::Element.new(:span, nil, HOM::Entity.new(160))
|
45
|
+
|
46
|
+
HOM::Element.new(:span, nil, HOM::Entity.new(:nbsp))
|
47
|
+
|
48
|
+
Call #to_s or #html on a HOM::Element object to return a string containing
|
49
|
+
the generated HTML. HOM::Element objects are safe to use directly in Rails
|
50
|
+
templates, all escaping is handled automatically.
|
data/hom.gemspec
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'hom'
|
3
|
+
s.version = '0.1.0'
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.authors = ['Tim Craft']
|
6
|
+
s.email = ['mail@timcraft.com']
|
7
|
+
s.homepage = 'http://github.com/timcraft/hom'
|
8
|
+
s.description = 'A straightforward API for generating HTML programmatically'
|
9
|
+
s.summary = 'See description'
|
10
|
+
s.files = Dir.glob('{lib,test}/**/*') + %w(README.txt hom.gemspec)
|
11
|
+
s.add_development_dependency('activesupport', ['>= 3.0.3'])
|
12
|
+
s.require_path = 'lib'
|
13
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module HOM
|
2
|
+
class AttributeList < Array
|
3
|
+
def html
|
4
|
+
map(&:html).join
|
5
|
+
end
|
6
|
+
|
7
|
+
def lookup(name)
|
8
|
+
detect { |object| object.name.to_s == name.to_s }
|
9
|
+
end
|
10
|
+
|
11
|
+
def set(name, value = nil)
|
12
|
+
attribute = lookup(name)
|
13
|
+
|
14
|
+
if attribute.nil?
|
15
|
+
self << Attribute.new(name, value)
|
16
|
+
else
|
17
|
+
attribute.value = value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(object)
|
22
|
+
case object
|
23
|
+
when NilClass
|
24
|
+
:pass
|
25
|
+
when Hash
|
26
|
+
object.each { |k, v| set(k, v) }
|
27
|
+
when Array
|
28
|
+
object.each { |item| update(item) }
|
29
|
+
else
|
30
|
+
set(object)
|
31
|
+
end
|
32
|
+
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/hom/element.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module HOM
|
4
|
+
class Element
|
5
|
+
attr_reader :tag_name, :attributes
|
6
|
+
|
7
|
+
def initialize(tag_name, attributes = nil, content = nil)
|
8
|
+
@tag_name, @attributes, @content = tag_name, AttributeList.new.update(attributes), content
|
9
|
+
end
|
10
|
+
|
11
|
+
def html
|
12
|
+
start_tag = "<#{@tag_name}#{@attributes.html}>"
|
13
|
+
|
14
|
+
@content.nil? ? start_tag : "#{start_tag}#{escape(@content)}</#{@tag_name}>"
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
html_safe(html)
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(name, *args, &block)
|
22
|
+
if args.empty? && block.nil?
|
23
|
+
attribute = attributes.lookup(name)
|
24
|
+
|
25
|
+
unless attribute.nil? || attribute.value.nil?
|
26
|
+
return attribute.value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
super name, *args, &block
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def html_safe(string)
|
36
|
+
string.respond_to?(:html_safe) ? string.html_safe : string
|
37
|
+
end
|
38
|
+
|
39
|
+
def escape(object)
|
40
|
+
if object.is_a?(Array)
|
41
|
+
object.map { |item| escape(item) }.join
|
42
|
+
elsif object.respond_to?(:html)
|
43
|
+
object.html
|
44
|
+
elsif object.respond_to?(:html_safe?) && object.html_safe?
|
45
|
+
object.to_s
|
46
|
+
else
|
47
|
+
CGI.escapeHTML(object.to_s)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/hom/entity.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module HOM
|
2
|
+
class Entity
|
3
|
+
attr_reader :value
|
4
|
+
|
5
|
+
def initialize(value)
|
6
|
+
@value = value
|
7
|
+
end
|
8
|
+
|
9
|
+
def html
|
10
|
+
numeric? ? "&\##{value};" : "&#{value};"
|
11
|
+
end
|
12
|
+
|
13
|
+
def numeric?
|
14
|
+
Numeric === @value
|
15
|
+
end
|
16
|
+
|
17
|
+
def named?
|
18
|
+
!numeric?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/hom.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
|
3
|
+
$:.unshift 'lib'
|
4
|
+
|
5
|
+
require 'hom'
|
6
|
+
|
7
|
+
class HOM::AttributeList::TestCase < MiniTest::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@list = HOM::AttributeList.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_update_with_nil
|
13
|
+
@list.update(nil)
|
14
|
+
|
15
|
+
assert_equal 0, @list.length
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_update_with_symbol
|
19
|
+
@list.update(:disabled)
|
20
|
+
|
21
|
+
assert_equal 1, @list.length
|
22
|
+
assert_equal :disabled, @list[0].name
|
23
|
+
assert_equal nil, @list[0].value
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_update_with_hash
|
27
|
+
@list.update({type: :text, size: 30})
|
28
|
+
|
29
|
+
assert_equal 2, @list.length
|
30
|
+
assert_equal :type, @list[0].name
|
31
|
+
assert_equal :text, @list[0].value
|
32
|
+
assert_equal :size, @list[1].name
|
33
|
+
assert_equal 30, @list[1].value
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_update_with_array
|
37
|
+
@list.update([{type: :text}, :disabled])
|
38
|
+
|
39
|
+
assert_equal 2, @list.length
|
40
|
+
assert_equal :type, @list[0].name
|
41
|
+
assert_equal :text, @list[0].value
|
42
|
+
assert_equal :disabled, @list[1].name
|
43
|
+
assert_equal nil, @list[1].value
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_update_with_duplicate_symbols
|
47
|
+
@list.update([:disabled, :disabled])
|
48
|
+
|
49
|
+
assert_equal 1, @list.length
|
50
|
+
assert_equal :disabled, @list[0].name
|
51
|
+
assert_equal nil, @list[0].value
|
52
|
+
end
|
53
|
+
end
|
data/test/hom_element.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'active_support/core_ext/string/output_safety'
|
3
|
+
|
4
|
+
$:.unshift 'lib'
|
5
|
+
|
6
|
+
require 'hom'
|
7
|
+
|
8
|
+
class HOM::Element::TestCase < MiniTest::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
@br = HOM::Element.new(:br)
|
11
|
+
@i1 = HOM::Element.new(:input, :disabled)
|
12
|
+
@i2 = HOM::Element.new(:input, {type: :text, size: 30})
|
13
|
+
@i3 = HOM::Element.new(:input, [{type: :text, size: 30}, :disabled])
|
14
|
+
@h1 = HOM::Element.new(:h1, nil, '')
|
15
|
+
@h2 = HOM::Element.new(:h2, nil, 'hello world')
|
16
|
+
@h3 = HOM::Element.new(:h3, nil, 'a && b, x > y')
|
17
|
+
@h4 = HOM::Element.new(:h4, nil, 1234567890)
|
18
|
+
@h5 = HOM::Element.new(:h5, nil, HOM::Element.new(:b, nil, 'how bold'))
|
19
|
+
@ul = HOM::Element.new(:ul, nil, (1..3).map { |n| HOM::Element.new(:li, nil, n) })
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_stringification
|
23
|
+
assert_equal '<br>', @br.to_s
|
24
|
+
assert_equal '<input disabled>', @i1.to_s
|
25
|
+
assert_equal '<input type="text" size="30">', @i2.to_s
|
26
|
+
assert_equal '<input type="text" size="30" disabled>', @i3.to_s
|
27
|
+
assert_equal '<h1></h1>', @h1.to_s
|
28
|
+
assert_equal '<h2>hello world</h2>', @h2.to_s
|
29
|
+
assert_equal '<h3>a && b, x > y</h3>', @h3.to_s
|
30
|
+
assert_equal '<h4>1234567890</h4>', @h4.to_s
|
31
|
+
assert_equal '<h5><b>how bold</b></h5>', @h5.to_s
|
32
|
+
assert_equal '<ul><li>1</li><li>2</li><li>3</li></ul>', @ul.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_output_safety
|
36
|
+
assert @br.to_s.html_safe?
|
37
|
+
|
38
|
+
assert '<span><br></span>', HOM::Element.new(:span, nil, '<br>'.html_safe).to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_attribute_access
|
42
|
+
assert_equal :text, @i3.type
|
43
|
+
assert_equal 30, @i3.size
|
44
|
+
assert_raises(NoMethodError) { @i3.disabled }
|
45
|
+
end
|
46
|
+
end
|
data/test/hom_entity.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
|
3
|
+
$:.unshift 'lib'
|
4
|
+
|
5
|
+
require 'hom'
|
6
|
+
|
7
|
+
class HOM::Entity::TestCase < MiniTest::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@e160 = HOM::Entity.new(160)
|
10
|
+
|
11
|
+
@nbsp = HOM::Entity.new(:nbsp)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_value_method
|
15
|
+
assert_equal 160, @e160.value
|
16
|
+
assert_equal :nbsp, @nbsp.value
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_numeric_query_method
|
20
|
+
assert_equal true, @e160.numeric?
|
21
|
+
assert_equal false, @nbsp.numeric?
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_named_query_method
|
25
|
+
assert_equal false, @e160.named?
|
26
|
+
assert_equal true, @nbsp.named?
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_html_method
|
30
|
+
assert_equal ' ', @e160.html
|
31
|
+
assert_equal ' ', @nbsp.html
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hom
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tim Craft
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-08-19 00:00:00.000000000 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
requirement: &3706480 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.0.3
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *3706480
|
26
|
+
description: A straightforward API for generating HTML programmatically
|
27
|
+
email:
|
28
|
+
- mail@timcraft.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/hom/attribute.rb
|
34
|
+
- lib/hom/attribute_list.rb
|
35
|
+
- lib/hom/element.rb
|
36
|
+
- lib/hom/entity.rb
|
37
|
+
- lib/hom.rb
|
38
|
+
- test/hom_attribute_list.rb
|
39
|
+
- test/hom_element.rb
|
40
|
+
- test/hom_entity.rb
|
41
|
+
- README.txt
|
42
|
+
- hom.gemspec
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/timcraft/hom
|
45
|
+
licenses: []
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.6.2
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: See description
|
68
|
+
test_files: []
|