hom 0.1.0

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