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 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,9 @@
1
+ require 'cgi'
2
+
3
+ module HOM
4
+ class Attribute < Struct.new(:name, :value)
5
+ def html
6
+ value.nil? ? " #{name}" : %( #{name}="#{CGI.escapeHTML(value.to_s)}")
7
+ end
8
+ end
9
+ 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
@@ -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,6 @@
1
+ module HOM
2
+ autoload :Attribute, 'hom/attribute'
3
+ autoload :AttributeList, 'hom/attribute_list'
4
+ autoload :Element, 'hom/element'
5
+ autoload :Entity, 'hom/entity'
6
+ end
@@ -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
@@ -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 &amp;&amp; b, x &gt; 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
@@ -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 '&#160;', @e160.html
31
+ assert_equal '&nbsp;', @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: []