hom 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ HOM: A straightforward API for generating HTML
2
+ ==============================================
3
+
4
+ Motivation
5
+ ----------
6
+
7
+ HOM helps you implement HTML presentation logic in your code. Things like
8
+ navigation links, select boxes, sets of checkboxes; anything with behaviour
9
+ that is too complex for your templates.
10
+
11
+ Usage
12
+ -----
13
+
14
+ Build up an object tree using `HOM::Element` objects. The first constuctor
15
+ argument is a symbol representing the tag name. For example, here's how you'd
16
+ represent a line break element:
17
+
18
+ ```ruby
19
+ HOM::Element.new(:br)
20
+ ```
21
+
22
+ The second constructor argument represents the element attributes, and can be
23
+ nil, a single symbol/string, a hash, or an array of hashes and symbols/strings.
24
+ For example, here's how you'd represent some input elements:
25
+
26
+ ```ruby
27
+ HOM::Element.new(:input, :disabled)
28
+
29
+ HOM::Element.new(:input, {type: :text, size: 30})
30
+
31
+ HOM::Element.new(:input, [{type: :text, size: 30}, :disabled])
32
+ ```
33
+
34
+ The third constructor argument is the inner content, which can be a string,
35
+ another element object, or an array of child items. For example, here's how
36
+ you can represent elements with attributes:
37
+
38
+ ```ruby
39
+ span = HOM::Element.new(:span, nil, '')
40
+
41
+ h1 = HOM::Element.new(:h1, {}, 'hello world')
42
+
43
+ image = HOM::Element.new(:img, {src: 'image.png', width: 100, height: 30})
44
+
45
+ link = HOM::Element.new(:a, {target: :_blank, href: '/'}, image)
46
+
47
+ list = HOM::Element.new(:ul, nil, (1..3).map { |n| HOM::Element.new(:li, nil, n) })
48
+ ```
49
+
50
+ There's also a `HOM::Entity` class which you can use to represent HTML entities;
51
+ integer values for numeric entities and symbol/string values for named entities,
52
+ for example:
53
+
54
+ ```ruby
55
+ HOM::Element.new(:span, nil, HOM::Entity.new(160))
56
+
57
+ HOM::Element.new(:span, nil, HOM::Entity.new(:nbsp))
58
+ ```
59
+
60
+ Calling `#to_s` on a `HOM::Element` object will return a string containing
61
+ the generated markup. `HOM::Element` objects are safe to use directly in
62
+ Rails templates, all escaping is handled automatically.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => :spec
4
+
5
+ Rake::TestTask.new(:spec) do |t|
6
+ t.test_files = FileList['spec/*_spec.rb']
7
+ t.warning = true
8
+ end
data/hom.gemspec CHANGED
@@ -1,13 +1,13 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'hom'
3
- s.version = '0.3.0'
3
+ s.version = '0.4.0'
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.authors = ['Tim Craft']
6
6
  s.email = ['mail@timcraft.com']
7
7
  s.homepage = 'http://github.com/timcraft/hom'
8
- s.description = 'A straightforward API for generating HTML programmatically'
8
+ s.description = 'A straightforward API for generating HTML'
9
9
  s.summary = 'See description'
10
- s.files = Dir.glob('{lib,test}/**/*') + %w(README.txt hom.gemspec)
10
+ s.files = Dir.glob('{lib,spec}/**/*') + %w(Rakefile README.md hom.gemspec)
11
11
  s.add_development_dependency('activesupport', ['>= 3.0.3'])
12
12
  s.require_path = 'lib'
13
13
  end
data/lib/hom.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'cgi'
2
2
 
3
3
  module HOM
4
+ Undefined = Class.new
5
+
4
6
  class Entity
5
7
  attr_reader :value
6
8
 
@@ -8,7 +10,7 @@ module HOM
8
10
  @value = value
9
11
  end
10
12
 
11
- def html
13
+ def to_s
12
14
  numeric? ? "&\##{value};" : "&#{value};"
13
15
  end
14
16
 
@@ -19,41 +21,25 @@ module HOM
19
21
  def named?
20
22
  !numeric?
21
23
  end
24
+
25
+ def html_safe?
26
+ true
27
+ end
22
28
  end
23
29
 
24
30
  class Element
25
- def initialize(tag_name, attributes = nil, content = nil)
31
+ attr_reader :tag_name, :attributes, :content
32
+
33
+ def initialize(tag_name, attributes = nil, content = Undefined)
26
34
  @tag_name, @attributes, @content = tag_name, AttributeList.new.update(attributes), content
27
35
  end
28
36
 
29
- def html
30
- @content.nil? ? start_tag : "#{start_tag}#{encode(@content)}</#{@tag_name}>"
37
+ def content?
38
+ @content != Undefined
31
39
  end
32
40
 
33
41
  def to_s
34
- html_safe(html)
35
- end
36
-
37
- private
38
-
39
- def html_safe(string)
40
- string.respond_to?(:html_safe) ? string.html_safe : string
41
- end
42
-
43
- def encode(object)
44
- if object.is_a?(Array)
45
- object.map { |item| encode(item) }.join
46
- elsif object.respond_to?(:html)
47
- object.html
48
- elsif object.respond_to?(:html_safe?) && object.html_safe?
49
- object.to_s
50
- else
51
- HOM.escape(object)
52
- end
53
- end
54
-
55
- def start_tag
56
- "<#{@tag_name}#{@attributes.html}>"
42
+ Encoding.safe_encode(self)
57
43
  end
58
44
  end
59
45
 
@@ -62,16 +48,14 @@ module HOM
62
48
  @index = {}
63
49
  end
64
50
 
65
- def html
66
- @index.map { |name, value| attribute_html(name, value) }.join
67
- end
68
-
69
- Undefined = Class.new
70
-
71
51
  def set(name, value = Undefined)
72
52
  @index[name.to_s] = value
73
53
  end
74
54
 
55
+ def to_hash
56
+ @index
57
+ end
58
+
75
59
  def update(object)
76
60
  case object
77
61
  when NilClass
@@ -86,15 +70,51 @@ module HOM
86
70
 
87
71
  return self
88
72
  end
73
+ end
74
+
75
+ module Encoding
76
+ def self.safe_encode(object)
77
+ safe(encode(object))
78
+ end
89
79
 
90
- private
80
+ def self.encode(object)
81
+ if object.is_a?(Array)
82
+ object.map { |item| encode(item) }.join
83
+ elsif object.is_a?(Element)
84
+ 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'
91
87
 
92
- def attribute_html(name, value)
93
- value == Undefined ? " #{name}" : %( #{name}="#{HOM.escape value}")
88
+ object.html
89
+ elsif object.respond_to?(:html_safe?) && object.html_safe?
90
+ object.to_s
91
+ else
92
+ escape(object)
93
+ end
94
94
  end
95
- end
96
95
 
97
- def self.escape(object)
98
- CGI.escapeHTML(object.to_s)
96
+ def self.encode_element(object)
97
+ object.content? ? "#{start_tag(object)}#{encode(object.content)}</#{object.tag_name}>" : start_tag(object)
98
+ end
99
+
100
+ def self.start_tag(element)
101
+ "<#{element.tag_name}#{encode_attributes(element)}>"
102
+ end
103
+
104
+ def self.encode_attributes(element)
105
+ element.attributes.to_hash.map { |name, value| encode_attribute(name, value) }.join
106
+ end
107
+
108
+ def self.encode_attribute(name, value)
109
+ value == Undefined ? " #{name}" : %( #{name}="#{escape value}")
110
+ end
111
+
112
+ def self.safe(string)
113
+ string.respond_to?(:html_safe) ? string.html_safe : string
114
+ end
115
+
116
+ def self.escape(object)
117
+ CGI.escapeHTML(object.to_s)
118
+ end
99
119
  end
100
120
  end
@@ -0,0 +1,71 @@
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 &amp;&amp; b, x &gt; 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>&lt;br&gt;</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
@@ -0,0 +1,75 @@
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('&nbsp;')
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('&#160;')
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-23 00:00:00.000000000Z
12
+ date: 2012-09-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &10113880 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,8 +21,13 @@ dependencies:
21
21
  version: 3.0.3
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *10113880
25
- description: A straightforward API for generating HTML programmatically
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.3
30
+ description: A straightforward API for generating HTML
26
31
  email:
27
32
  - mail@timcraft.com
28
33
  executables: []
@@ -30,7 +35,10 @@ extensions: []
30
35
  extra_rdoc_files: []
31
36
  files:
32
37
  - lib/hom.rb
33
- - README.txt
38
+ - spec/element_spec.rb
39
+ - spec/entity_spec.rb
40
+ - Rakefile
41
+ - README.md
34
42
  - hom.gemspec
35
43
  homepage: http://github.com/timcraft/hom
36
44
  licenses: []
@@ -52,8 +60,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
60
  version: '0'
53
61
  requirements: []
54
62
  rubyforge_project:
55
- rubygems_version: 1.8.10
63
+ rubygems_version: 1.8.24
56
64
  signing_key:
57
65
  specification_version: 3
58
66
  summary: See description
59
67
  test_files: []
68
+ has_rdoc:
data/README.txt DELETED
@@ -1,50 +0,0 @@
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.