crafty 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README.md +175 -0
- data/Rakefile +103 -0
- data/VERSION +1 -0
- data/crafty.gemspec +55 -0
- data/lib/crafty/safety.rb +34 -0
- data/lib/crafty/tools.rb +108 -0
- data/lib/crafty/toolset.rb +49 -0
- data/lib/crafty/toolsets/html4.rb +32 -0
- data/lib/crafty/toolsets/html5.rb +34 -0
- data/lib/crafty.rb +11 -0
- data/test/test_helper.rb +21 -0
- data/test/unit/html_test.rb +126 -0
- data/test/unit/tools_test.rb +239 -0
- data/test/unit/toolset_test.rb +52 -0
- metadata +74 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010-2011 Voormedia B.V.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
Crafty – handcrafted HTML in pure Ruby
|
2
|
+
======================================
|
3
|
+
|
4
|
+
Crafty allows you to easily and flexibly craft HTML output with pure Ruby
|
5
|
+
code. It is inspired by Builder and Markaby, but is simpler and more flexible.
|
6
|
+
Its goal is to provide simple helpers that allow you to build HTML markup in
|
7
|
+
any class.
|
8
|
+
|
9
|
+
Crafty can be used in builder classes that construct complex HTML markup, such
|
10
|
+
as form builders or pagination builders. Whenever you need to programmatically
|
11
|
+
construct HTML, consider including a Crafty helper module.
|
12
|
+
|
13
|
+
|
14
|
+
Features
|
15
|
+
--------
|
16
|
+
|
17
|
+
* Crafty is simple and fast.
|
18
|
+
|
19
|
+
* HTML element sets are predefined and provided as mix-ins.
|
20
|
+
|
21
|
+
* Automatic HTML output escaping, 100% compatible with Rails.
|
22
|
+
|
23
|
+
* No `instance_eval` or `method_missing` tricks, just helper methods.
|
24
|
+
|
25
|
+
|
26
|
+
Synopsis
|
27
|
+
--------
|
28
|
+
|
29
|
+
A very simple example:
|
30
|
+
|
31
|
+
require "crafty"
|
32
|
+
|
33
|
+
include Crafty::HTML::Basic
|
34
|
+
|
35
|
+
div class: "green" do
|
36
|
+
h1 "Crafty crafts HTML"
|
37
|
+
end
|
38
|
+
#=> "<div class=\"green\"><h1>Crafty crafts HTML</h1></div>"
|
39
|
+
|
40
|
+
|
41
|
+
Another example:
|
42
|
+
|
43
|
+
require "crafty"
|
44
|
+
|
45
|
+
class Widget
|
46
|
+
include Crafty::HTML::Basic
|
47
|
+
|
48
|
+
def initialize(target)
|
49
|
+
@target = target
|
50
|
+
end
|
51
|
+
|
52
|
+
def render
|
53
|
+
# The first helper method that is called will collect all HTML.
|
54
|
+
html do
|
55
|
+
head do
|
56
|
+
title "Hello"
|
57
|
+
end
|
58
|
+
body do
|
59
|
+
# Continue building in another method.
|
60
|
+
render_content
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def render_content
|
66
|
+
div class: ["main", "content"] do
|
67
|
+
p "Hello, #{@target}!"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
Widget.new("world").render
|
73
|
+
#=> "<html><head><title>Hello</title></head><body><div class=\"main content\"><p>Hello, world!</p></div></body></html>"
|
74
|
+
|
75
|
+
|
76
|
+
Helper modules
|
77
|
+
--------------
|
78
|
+
|
79
|
+
You can choose from one of the following helper modules:
|
80
|
+
|
81
|
+
* `Crafty::HTML::Basic`: A simple subset of all HTML elements, enough
|
82
|
+
for most HTML layouts.
|
83
|
+
|
84
|
+
* `Crafty::HTML::Forms`: All HTML elements that are related to forms. If you
|
85
|
+
use Rails, note that the names of some of these elements conflict with Rails
|
86
|
+
helpers (such as `label` and `input`).
|
87
|
+
|
88
|
+
* `Crafty::HTML::Semantic`: Juicy new HTML5 elements such as `header`,
|
89
|
+
`footer`, `nav`, etc.
|
90
|
+
|
91
|
+
* `Crafty::HTML::All`: All HTML5 elements that are defined in the HTML5 draft
|
92
|
+
spec. If you use Rails, note that some elements might conflict with Rails
|
93
|
+
helpers.
|
94
|
+
|
95
|
+
You can also choose to use `Crafty::HTML4::Basic`, `Crafty::HTML4::Forms` and
|
96
|
+
`Crafty::HTML4::All` instead. These modules provide helpers for HTML4/XHTML
|
97
|
+
elements. For a complete reference of which elements are included in which
|
98
|
+
module, [see the source](https://github.com/voormedia/crafty/tree/master/lib/crafty/toolsets).
|
99
|
+
|
100
|
+
|
101
|
+
Output streaming
|
102
|
+
----------------
|
103
|
+
|
104
|
+
Crafty helpers return strings by default. However, if the object you use the
|
105
|
+
helpers in responds to `<<`, Crafty will push any output directly onto the
|
106
|
+
current object. This allows you to create builder classes that can stream
|
107
|
+
output. Observe how it works with this contrived example:
|
108
|
+
|
109
|
+
class StreamingWidget < Array # Subclass Array to demonstrate '<<'
|
110
|
+
include Crafty::HTML::Basic
|
111
|
+
|
112
|
+
def render(target)
|
113
|
+
html do
|
114
|
+
head do
|
115
|
+
title %Q(Hello "#{target}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
widget = StreamingWidget.new
|
122
|
+
widget.render("world")
|
123
|
+
#=> nil
|
124
|
+
widget
|
125
|
+
#=> ["<html>", "<head>", "<title>", "Hello "world"", "</title>", "</head>", "</html>"]
|
126
|
+
|
127
|
+
|
128
|
+
Benchmarks
|
129
|
+
----------
|
130
|
+
|
131
|
+
Benchmarks do not necessarily give a complete picture of real-world
|
132
|
+
performance. Nevertheless, we wish to demonstrate that Crafty is fast enough
|
133
|
+
for daily use. These benchmarks were performed with Ruby 1.9.2.
|
134
|
+
|
135
|
+
Number of iterations = 50000
|
136
|
+
user system total real
|
137
|
+
crafty 7.630000 0.140000 7.770000 ( 7.750502)
|
138
|
+
builder 17.420000 0.110000 17.530000 ( 17.513536)
|
139
|
+
haml 17.450000 0.180000 17.630000 ( 17.600038)
|
140
|
+
erector 15.400000 0.110000 15.510000 ( 15.491134)
|
141
|
+
tagz 32.860000 0.650000 33.510000 ( 33.461828)
|
142
|
+
nokogiri 27.450000 0.210000 27.660000 ( 27.608287)
|
143
|
+
|
144
|
+
|
145
|
+
Requirements
|
146
|
+
------------
|
147
|
+
|
148
|
+
Crafty has no requirements other than Ruby (tested with 1.8.7+).
|
149
|
+
|
150
|
+
|
151
|
+
Installation
|
152
|
+
------------
|
153
|
+
|
154
|
+
Install as a gem:
|
155
|
+
|
156
|
+
gem install crafty
|
157
|
+
|
158
|
+
Or add to your project's `Gemfile`:
|
159
|
+
|
160
|
+
gem "crafty"
|
161
|
+
|
162
|
+
|
163
|
+
About Crafty
|
164
|
+
-------------
|
165
|
+
|
166
|
+
Crafty was created by Rolf Timmermans (r.timmermans *at* voormedia.com)
|
167
|
+
|
168
|
+
Copyright 2010-2011 Voormedia - [www.voormedia.com](http://www.voormedia.com/)
|
169
|
+
|
170
|
+
|
171
|
+
License
|
172
|
+
-------
|
173
|
+
|
174
|
+
Crafty is released under the MIT license.
|
175
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "jeweler"
|
3
|
+
require "rake/testtask"
|
4
|
+
|
5
|
+
Jeweler::Tasks.new do |spec|
|
6
|
+
spec.name = "crafty"
|
7
|
+
spec.summary = "Build HTML like a master craftsman."
|
8
|
+
spec.description = "Crafty provides you the tools to easily and flexibly create HTML output with pure Ruby."
|
9
|
+
|
10
|
+
spec.authors = ["Rolf Timmermans"]
|
11
|
+
spec.email = "r.timmermans@voormedia.com"
|
12
|
+
|
13
|
+
spec.files -= Dir["{benchmark,src}/**/*"]
|
14
|
+
end
|
15
|
+
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
|
18
|
+
Rake::TestTask.new do |test|
|
19
|
+
test.pattern = "test/unit/**/*_test.rb"
|
20
|
+
end
|
21
|
+
|
22
|
+
task :default => [:generate, :test]
|
23
|
+
|
24
|
+
desc "Benchmark Crafty against various other HTML builders"
|
25
|
+
task :bench do
|
26
|
+
require File.expand_path("benchmark/bench", File.dirname(__FILE__))
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Profile Crafty"
|
30
|
+
task :profile do
|
31
|
+
require File.expand_path("benchmark/profile", File.dirname(__FILE__))
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Regenerate toolsets"
|
35
|
+
task :generate do
|
36
|
+
require File.expand_path("src/elements", File.dirname(__FILE__))
|
37
|
+
|
38
|
+
def simple_format(text, len = 73, indent = 6)
|
39
|
+
sentences = [[]]
|
40
|
+
|
41
|
+
text.split.each do |word|
|
42
|
+
if (sentences.last + [word]).join(' ').length > len
|
43
|
+
sentences << [word]
|
44
|
+
else
|
45
|
+
sentences.last << word
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
sentences.map { |sentence|
|
50
|
+
"#{" " * indent}#{sentence.join(' ')}"
|
51
|
+
}.join "\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
def define(set, regular, empty)
|
55
|
+
simple_format("Toolset.define(self, %w{#{regular * " "}}" + (empty.any? ? ", %w{#{empty * " "}}" : "") + ")")
|
56
|
+
end
|
57
|
+
|
58
|
+
autoloading = [" # Generated HTML toolsets."]
|
59
|
+
Versions.each do |version|
|
60
|
+
path = "crafty/toolsets/#{version.to_s.downcase}"
|
61
|
+
file = File.open("lib/#{path}.rb", "w+")
|
62
|
+
file.puts "module Crafty"
|
63
|
+
file.puts " # This toolset has been automatically generated."
|
64
|
+
file.puts " module #{version}"
|
65
|
+
|
66
|
+
version_elements = Object.const_get(version)
|
67
|
+
Sets.each do |set|
|
68
|
+
set_elements = Object.const_get(set)
|
69
|
+
|
70
|
+
broken = set_elements - (HTML4 + HTML5)
|
71
|
+
raise "Incorrect elements in set: #{broken}" if broken.any?
|
72
|
+
|
73
|
+
all = version_elements & set_elements
|
74
|
+
|
75
|
+
file.puts " module #{set}"
|
76
|
+
file.puts define(set, all - Childless, all & Childless)
|
77
|
+
file.puts " end"
|
78
|
+
file.puts
|
79
|
+
end
|
80
|
+
|
81
|
+
file.puts " module All"
|
82
|
+
file.puts define(:All, version_elements - Childless, version_elements & Childless)
|
83
|
+
file.puts " end"
|
84
|
+
file.puts " end"
|
85
|
+
|
86
|
+
aliases = []
|
87
|
+
Aliases.each { |alt, orig| aliases << alt if orig == version }
|
88
|
+
file.puts "\n #{aliases * " = "} = #{version}" if aliases.any?
|
89
|
+
file.puts "end"
|
90
|
+
file.close
|
91
|
+
|
92
|
+
([version] + aliases).each do |mod|
|
93
|
+
autoloading << %Q( autoload #{mod.inspect}, #{path.inspect})
|
94
|
+
end
|
95
|
+
autoloading << ""
|
96
|
+
end
|
97
|
+
|
98
|
+
lib = File.read("lib/crafty.rb")
|
99
|
+
lib.gsub!(/^\s*#\s*Generated.*\nend/m, "\n" + autoloading.join("\n") + "end")
|
100
|
+
File.open "lib/crafty.rb", "w+" do |file|
|
101
|
+
file.write lib
|
102
|
+
end
|
103
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/crafty.gemspec
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{crafty}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Rolf Timmermans"]
|
12
|
+
s.date = %q{2011-04-01}
|
13
|
+
s.description = %q{Crafty provides you the tools to easily and flexibly create HTML output with pure Ruby.}
|
14
|
+
s.email = %q{r.timmermans@voormedia.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"LICENSE",
|
21
|
+
"README.md",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"crafty.gemspec",
|
25
|
+
"lib/crafty.rb",
|
26
|
+
"lib/crafty/safety.rb",
|
27
|
+
"lib/crafty/tools.rb",
|
28
|
+
"lib/crafty/toolset.rb",
|
29
|
+
"lib/crafty/toolsets/html4.rb",
|
30
|
+
"lib/crafty/toolsets/html5.rb",
|
31
|
+
"test/test_helper.rb",
|
32
|
+
"test/unit/html_test.rb",
|
33
|
+
"test/unit/tools_test.rb",
|
34
|
+
"test/unit/toolset_test.rb"
|
35
|
+
]
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubygems_version = %q{1.5.2}
|
38
|
+
s.summary = %q{Build HTML like a master craftsman.}
|
39
|
+
s.test_files = [
|
40
|
+
"test/test_helper.rb",
|
41
|
+
"test/unit/html_test.rb",
|
42
|
+
"test/unit/tools_test.rb",
|
43
|
+
"test/unit/toolset_test.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
s.specification_version = 3
|
48
|
+
|
49
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
50
|
+
else
|
51
|
+
end
|
52
|
+
else
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Object
|
2
|
+
def html_safe?
|
3
|
+
false
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
module Crafty
|
8
|
+
class SafeString < String
|
9
|
+
def html_safe?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def html_safe
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :to_s, :html_safe
|
18
|
+
alias_method :render, :html_safe
|
19
|
+
end
|
20
|
+
|
21
|
+
class SafeWrapper
|
22
|
+
def initialize(base)
|
23
|
+
@base = base
|
24
|
+
end
|
25
|
+
|
26
|
+
def <<(data)
|
27
|
+
@base << SafeString.new(data)
|
28
|
+
end
|
29
|
+
|
30
|
+
def render
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/crafty/tools.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require "crafty/safety"
|
2
|
+
|
3
|
+
module Crafty
|
4
|
+
module Tools
|
5
|
+
class << self
|
6
|
+
ESCAPE_SEQUENCE = { "&" => "&", ">" => ">", "<" => "<", '"' => """ }
|
7
|
+
|
8
|
+
def escape(content)
|
9
|
+
return content if content.html_safe?
|
10
|
+
content.gsub(/[&><"]/) { |char| ESCAPE_SEQUENCE[char] }
|
11
|
+
end
|
12
|
+
|
13
|
+
def format_attributes(attributes)
|
14
|
+
return if attributes.nil?
|
15
|
+
attributes.collect do |name, value|
|
16
|
+
value = if value.kind_of? Array
|
17
|
+
value.flatten.compact * " "
|
18
|
+
else
|
19
|
+
value.to_s
|
20
|
+
end
|
21
|
+
next if value == ""
|
22
|
+
%Q{ #{name}="#{escape(value)}"}
|
23
|
+
end.join
|
24
|
+
end
|
25
|
+
|
26
|
+
def format_parameters(parameters)
|
27
|
+
return if parameters.nil?
|
28
|
+
parameters.collect do |name|
|
29
|
+
name = name.inspect if name.kind_of? String
|
30
|
+
%Q{ #{name}}
|
31
|
+
end.join
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_stream(base)
|
35
|
+
if base.respond_to? :<<
|
36
|
+
SafeWrapper.new(base)
|
37
|
+
else
|
38
|
+
SafeString.new
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def element!(name, content = nil, attributes = nil)
|
44
|
+
build! do
|
45
|
+
if content or block_given?
|
46
|
+
@_crafted << "<#{name}#{Tools.format_attributes(attributes)}>"
|
47
|
+
if block_given?
|
48
|
+
value = yield
|
49
|
+
content = value if !@_appended or value.kind_of? String
|
50
|
+
end
|
51
|
+
content = content.to_s
|
52
|
+
@_crafted << Tools.escape(content) if content != ""
|
53
|
+
@_crafted << "</#{name}>"
|
54
|
+
else
|
55
|
+
@_crafted << "<#{name}#{Tools.format_attributes(attributes)}/>"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def comment!(content)
|
61
|
+
build! do
|
62
|
+
@_crafted << "<!-- "
|
63
|
+
@_crafted << Tools.escape(content.to_s)
|
64
|
+
@_crafted << " -->"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def instruct!(name = nil, attributes = {})
|
69
|
+
unless name
|
70
|
+
name = "xml"
|
71
|
+
attributes = { :version => "1.0", :encoding => "UTF-8" }
|
72
|
+
end
|
73
|
+
build! do
|
74
|
+
@_crafted << "<?#{name}#{Tools.format_attributes(attributes)}?>"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def declare!(name, *parameters)
|
79
|
+
build! do
|
80
|
+
@_crafted << "<!#{name}#{Tools.format_parameters(parameters)}>"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def text!(content)
|
85
|
+
build! do
|
86
|
+
@_crafted << Tools.escape(content.to_s)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
alias_method :write!, :text!
|
90
|
+
|
91
|
+
def build!
|
92
|
+
@_appended = false
|
93
|
+
if @_crafted
|
94
|
+
yield
|
95
|
+
@_appended = true
|
96
|
+
nil
|
97
|
+
else
|
98
|
+
begin
|
99
|
+
@_crafted = Tools.create_stream(self)
|
100
|
+
yield
|
101
|
+
@_crafted.render
|
102
|
+
ensure
|
103
|
+
@_crafted = nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Crafty
|
2
|
+
module Toolset
|
3
|
+
class << self
|
4
|
+
def define(mod, elements = [], empty_elements = [])
|
5
|
+
define_elements(mod, elements)
|
6
|
+
define_empty_elements(mod, empty_elements)
|
7
|
+
|
8
|
+
mod.module_eval do
|
9
|
+
include Tools
|
10
|
+
|
11
|
+
def self.append_features(mod)
|
12
|
+
redefined = mod.instance_methods & self.instance_methods(false)
|
13
|
+
if redefined.any?
|
14
|
+
dup.tap do |safe|
|
15
|
+
redefined.each do |method|
|
16
|
+
safe.send :remove_method, method
|
17
|
+
end
|
18
|
+
end.append_features(mod)
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def define_elements(mod, elements)
|
27
|
+
elements.each do |element|
|
28
|
+
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
29
|
+
def #{element}(*arguments, &block)
|
30
|
+
attributes = arguments.pop if arguments.last.kind_of? Hash
|
31
|
+
content = arguments.first || ""
|
32
|
+
element!("#{element}", content, attributes, &block)
|
33
|
+
end
|
34
|
+
RUBY
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def define_empty_elements(mod, elements)
|
39
|
+
elements.each do |element|
|
40
|
+
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
41
|
+
def #{element}(attributes = nil)
|
42
|
+
element!("#{element}", nil, attributes)
|
43
|
+
end
|
44
|
+
RUBY
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Crafty
|
2
|
+
# This toolset has been automatically generated.
|
3
|
+
module HTML4
|
4
|
+
module Basic
|
5
|
+
Toolset.define(self, %w{a blockquote body div em h1 h2 h3 h4 h5 h6 head
|
6
|
+
html li p pre small span strong table td th title tr ul}, %w{br img
|
7
|
+
link})
|
8
|
+
end
|
9
|
+
|
10
|
+
module Forms
|
11
|
+
Toolset.define(self, %w{button fieldset form label object option select
|
12
|
+
textarea}, %w{input})
|
13
|
+
end
|
14
|
+
|
15
|
+
module Semantic
|
16
|
+
Toolset.define(self, %w{abbr acronym address cite code legend menu})
|
17
|
+
end
|
18
|
+
|
19
|
+
module All
|
20
|
+
Toolset.define(self, %w{a abbr acronym address applet b bdo big
|
21
|
+
blockquote body button caption center cite code colgroup dd del dfn dir
|
22
|
+
div dl dt em fieldset font form frameset h1 h2 h3 h4 h5 h6 head html i
|
23
|
+
iframe ins kbd label legend li map menu noframes noscript object ol
|
24
|
+
optgroup option p pre q s samp script select small span strike strong
|
25
|
+
style sub sup table tbody td textarea tfoot th thead title tr tt u ul
|
26
|
+
var}, %w{area base basefont br col frame hr img input isindex link meta
|
27
|
+
param})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
XHTML = HTML4
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Crafty
|
2
|
+
# This toolset has been automatically generated.
|
3
|
+
module HTML5
|
4
|
+
module Basic
|
5
|
+
Toolset.define(self, %w{a blockquote body div em h1 h2 h3 h4 h5 h6 head
|
6
|
+
html li p pre small span strong table td th title tr ul}, %w{br img
|
7
|
+
link})
|
8
|
+
end
|
9
|
+
|
10
|
+
module Forms
|
11
|
+
Toolset.define(self, %w{button datalist fieldset form label meter object
|
12
|
+
option output progress select textarea}, %w{input keygen})
|
13
|
+
end
|
14
|
+
|
15
|
+
module Semantic
|
16
|
+
Toolset.define(self, %w{abbr address article cite code details figcaption
|
17
|
+
figure footer header legend menu nav section}, %w{command})
|
18
|
+
end
|
19
|
+
|
20
|
+
module All
|
21
|
+
Toolset.define(self, %w{a abbr address article aside audio b bdi bdo
|
22
|
+
blockquote body button canvas caption cite code colgroup datalist dd del
|
23
|
+
details dfn div dl dt em fieldset figcaption figure footer form h1 h2 h3
|
24
|
+
h4 h5 h6 head header hgroup html i iframe ins kbd label legend li map
|
25
|
+
mark menu meter nav noscript object ol optgroup option output p pre
|
26
|
+
progress q rp rt ruby s samp script section select small span strong
|
27
|
+
style sub summary sup table tbody td textarea tfoot th thead time title
|
28
|
+
tr ul var video}, %w{area base br col command embed hr img input keygen
|
29
|
+
link meta param source track wbr})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
HTML = HTML5
|
34
|
+
end
|
data/lib/crafty.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Crafty
|
2
|
+
autoload :Tools, "crafty/tools"
|
3
|
+
autoload :Toolset, "crafty/toolset"
|
4
|
+
|
5
|
+
# Generated HTML toolsets.
|
6
|
+
autoload :HTML4, "crafty/toolsets/html4"
|
7
|
+
autoload :XHTML, "crafty/toolsets/html4"
|
8
|
+
|
9
|
+
autoload :HTML5, "crafty/toolsets/html5"
|
10
|
+
autoload :HTML, "crafty/toolsets/html5"
|
11
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
require "test/unit"
|
4
|
+
require "crafty"
|
5
|
+
|
6
|
+
class Test::Unit::TestCase
|
7
|
+
class << self
|
8
|
+
# Support declarative specification of test methods.
|
9
|
+
def test(name)
|
10
|
+
define_method "test_#{name.gsub(/\s+/,'_')}".to_sym, &Proc.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
if RUBY_VERSION < "1.9"
|
16
|
+
class Hash
|
17
|
+
def each
|
18
|
+
to_a.sort_by { |k, v| k.to_s }.each &Proc.new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class HTMLBase < Test::Unit::TestCase
|
4
|
+
def test_dummy; end
|
5
|
+
|
6
|
+
def self.behaves_as_basic_html
|
7
|
+
# Simple methods =========================================================
|
8
|
+
test "div should return content with given attributes" do
|
9
|
+
assert_equal %Q{<div class="green">Hello</div>}, @object.div("Hello", :class => "green")
|
10
|
+
end
|
11
|
+
|
12
|
+
test "div should return content in block with given attributes" do
|
13
|
+
assert_equal %Q{<div class="green">Hello</div>}, @object.div(:class => "green") { "Hello" }
|
14
|
+
end
|
15
|
+
|
16
|
+
test "div should return non string content" do
|
17
|
+
assert_equal %Q{<div>1234</div>}, @object.div(1234)
|
18
|
+
end
|
19
|
+
|
20
|
+
test "div should not be self closing" do
|
21
|
+
assert_equal %Q{<div></div>}, @object.div
|
22
|
+
end
|
23
|
+
|
24
|
+
test "div should not be self closing with nil block" do
|
25
|
+
assert_equal %Q{<div></div>}, @object.div { nil }
|
26
|
+
end
|
27
|
+
|
28
|
+
test "div should return closing tag without content" do
|
29
|
+
assert_equal %Q{<div class="green"></div>}, @object.div(:class => "green")
|
30
|
+
end
|
31
|
+
|
32
|
+
test "a should return anchor with given attributes" do
|
33
|
+
assert_equal %Q{<a href="http://example.org">link</a>}, @object.a("link", :href => "http://example.org")
|
34
|
+
end
|
35
|
+
|
36
|
+
test "title should return title with content" do
|
37
|
+
assert_equal %Q{<title>Hello</title>}, @object.title("Hello")
|
38
|
+
end
|
39
|
+
|
40
|
+
test "h1 should return h1 header" do
|
41
|
+
assert_equal %Q{<h1>Title</h1>}, @object.h1("Title")
|
42
|
+
end
|
43
|
+
|
44
|
+
test "br should be self closing" do
|
45
|
+
assert_equal %Q{<br/>}, @object.br
|
46
|
+
end
|
47
|
+
|
48
|
+
test "br should not accept block content" do
|
49
|
+
assert_equal %Q{<br/>}, @object.br { "foo" }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Streaming ==============================================================
|
53
|
+
test "html should product stream of strings if object responds to arrows" do
|
54
|
+
@streaming_object.instance_eval do
|
55
|
+
html do
|
56
|
+
head do
|
57
|
+
title "Hi"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
assert_equal ["<html>", "<head>", "<title>", "Hi", "</title>", "</head>", "</html>"], @streaming_object
|
62
|
+
end
|
63
|
+
|
64
|
+
# Examples ===============================================================
|
65
|
+
test "complex nested build calls should render correctly" do
|
66
|
+
assert_equal %Q{<html>} +
|
67
|
+
%Q{<head><title>my document</title><link href="style.css" rel="stylesheet" type="text/css"/></head>} +
|
68
|
+
%Q{<body class="awesome"><div><div><table cellspacing="0">} +
|
69
|
+
%Q{<tr><th>Col 1</th><th>Col 2</th></tr>} +
|
70
|
+
%Q{<tr><td>10000</td><td>content</td></tr>} +
|
71
|
+
%Q{</table></div></div></body>} +
|
72
|
+
%Q{</html>}, @object.instance_eval {
|
73
|
+
html do
|
74
|
+
head do
|
75
|
+
title "my document"
|
76
|
+
link :href => "style.css", :rel => :stylesheet, :type => "text/css"
|
77
|
+
end
|
78
|
+
body :class => :awesome do
|
79
|
+
div {
|
80
|
+
div {
|
81
|
+
table :cellspacing => 0 do
|
82
|
+
tr {
|
83
|
+
th "Col 1"
|
84
|
+
th "Col 2"
|
85
|
+
}
|
86
|
+
tr {
|
87
|
+
td 10_000
|
88
|
+
td "content"
|
89
|
+
}
|
90
|
+
end
|
91
|
+
}
|
92
|
+
}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class HTML5Test < HTMLBase
|
101
|
+
def setup
|
102
|
+
@object = Class.new { include Crafty::HTML5::Basic }.new
|
103
|
+
@streaming_object = Class.new(Array) { include Crafty::HTML5::Basic }.new
|
104
|
+
end
|
105
|
+
|
106
|
+
behaves_as_basic_html
|
107
|
+
end
|
108
|
+
|
109
|
+
class HTML4Test < HTMLBase
|
110
|
+
def setup
|
111
|
+
@object = Class.new { include Crafty::HTML4::Basic }.new
|
112
|
+
@streaming_object = Class.new(Array) { include Crafty::HTML4::Basic }.new
|
113
|
+
end
|
114
|
+
|
115
|
+
behaves_as_basic_html
|
116
|
+
end
|
117
|
+
|
118
|
+
class HTMLAliasTest < Test::Unit::TestCase
|
119
|
+
test "html should equal html5" do
|
120
|
+
assert_equal Crafty::HTML, Crafty::HTML5
|
121
|
+
end
|
122
|
+
|
123
|
+
test "xhtml should equal html4" do
|
124
|
+
assert_equal Crafty::XHTML, Crafty::HTML4
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class ToolsTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@object = Class.new { include Crafty::Tools }.new
|
6
|
+
end
|
7
|
+
|
8
|
+
# Basic element functionality ==============================================
|
9
|
+
test "element should return element with given name" do
|
10
|
+
assert_equal %Q{<el/>}, @object.element!("el")
|
11
|
+
end
|
12
|
+
|
13
|
+
test "element should return element with given name and content" do
|
14
|
+
assert_equal %Q{<el>content</el>}, @object.element!("el", "content")
|
15
|
+
end
|
16
|
+
|
17
|
+
test "element should return element with given name and non string content" do
|
18
|
+
assert_equal %Q{<el>1234</el>}, @object.element!("el", 1234)
|
19
|
+
end
|
20
|
+
|
21
|
+
test "element should return element with given name and content in block" do
|
22
|
+
assert_equal %Q{<el>content</el>}, @object.element!("el") { "content" }
|
23
|
+
end
|
24
|
+
|
25
|
+
test "element should return element with given name and non string content in block" do
|
26
|
+
assert_equal %Q{<el>1234</el>}, @object.element!("el") { 1234 }
|
27
|
+
end
|
28
|
+
|
29
|
+
test "element should return element with given name and built content without non string block return value" do
|
30
|
+
assert_equal %Q{<el><el>content</el><el>content</el></el>}, @object.element!("el") {
|
31
|
+
2.times { @object.element!("el") { "content" } }
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
test "element should return element with given name and built content with string block return value" do
|
36
|
+
assert_equal %Q{<el><el>content</el>foo</el>}, @object.element!("el") {
|
37
|
+
@object.element!("el") { "content" }
|
38
|
+
"foo"
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
test "element should return element with given name and no content" do
|
43
|
+
assert_equal %Q{<el></el>}, @object.element!("el") { nil }
|
44
|
+
end
|
45
|
+
|
46
|
+
test "element should return element with given name and blank content" do
|
47
|
+
assert_equal %Q{<el></el>}, @object.element!("el") { "" }
|
48
|
+
end
|
49
|
+
|
50
|
+
test "element should return element with given name and attributes" do
|
51
|
+
assert_equal %Q{<el attr="val" prop="value"/>},
|
52
|
+
@object.element!("el", nil, :attr => "val", :prop => "value")
|
53
|
+
end
|
54
|
+
|
55
|
+
test "element should return element with given name and attributes and content" do
|
56
|
+
assert_equal %Q{<el attr="val" prop="value">content</el>},
|
57
|
+
@object.element!("el", "content", :attr => "val", :prop => "value")
|
58
|
+
end
|
59
|
+
|
60
|
+
test "element should return element with given name and attributes and content in block" do
|
61
|
+
assert_equal %Q{<el attr="val" prop="value">content</el>},
|
62
|
+
@object.element!("el", nil, :attr => "val", :prop => "value") { "content" }
|
63
|
+
end
|
64
|
+
|
65
|
+
test "element should return element with given name and attributes with symbol values" do
|
66
|
+
assert_equal %Q{<el attr="val"/>}, @object.element!("el", nil, :attr => :val)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Advanced functionality ===================================================
|
70
|
+
test "comment should return comment" do
|
71
|
+
assert_equal "<!-- commented -->", @object.comment!("commented")
|
72
|
+
end
|
73
|
+
|
74
|
+
test "instruct should return xml processing instruction" do
|
75
|
+
if RUBY_VERSION < "1.9"
|
76
|
+
assert_equal %Q{<?xml encoding="UTF-8" version="1.0"?>}, @object.instruct!
|
77
|
+
else
|
78
|
+
assert_equal %Q{<?xml version="1.0" encoding="UTF-8"?>}, @object.instruct!
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
test "instruct should return custom processing instruction" do
|
83
|
+
assert_equal %Q{<?foo attr="instr"?>}, @object.instruct!("foo", :attr => "instr")
|
84
|
+
end
|
85
|
+
|
86
|
+
test "declare should return declaration" do
|
87
|
+
assert_equal %Q{<!ENTITY greeting "Hello world">}, @object.declare!(:ENTITY, :greeting, "Hello world")
|
88
|
+
end
|
89
|
+
|
90
|
+
test "declare should return empty declaration if there are no parameters" do
|
91
|
+
assert_equal %Q{<!ENTITY>}, @object.declare!("ENTITY")
|
92
|
+
end
|
93
|
+
|
94
|
+
test "text should append directly to output stream" do
|
95
|
+
assert_equal %Q{foobar}, @object.text!("foobar")
|
96
|
+
end
|
97
|
+
|
98
|
+
test "write should append directly to output stream" do
|
99
|
+
assert_equal %Q{foobar}, @object.write!("foobar")
|
100
|
+
end
|
101
|
+
|
102
|
+
test "build should collect output" do
|
103
|
+
assert_equal %Q{<el/><el/>}, @object.build! {
|
104
|
+
@object.element!("el")
|
105
|
+
@object.element!("el")
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
test "attribute values as array should be joined with spaces" do
|
110
|
+
assert_equal %Q{<el attr="val1 val2 val3">content</el>},
|
111
|
+
@object.element!("el", "content", :attr => ["val1", "val2", "val3"])
|
112
|
+
end
|
113
|
+
|
114
|
+
test "attribute values as array should be flattened and compacted and joined with spaces" do
|
115
|
+
assert_equal %Q{<el attr="val1 val2 val3">content</el>},
|
116
|
+
@object.element!("el", "content", :attr => ["val1", [[nil, "val2"], "val3"]])
|
117
|
+
end
|
118
|
+
|
119
|
+
test "attribute values as empty array should be omitted" do
|
120
|
+
assert_equal %Q{<el>content</el>}, @object.element!("el", "content", :attr => [])
|
121
|
+
end
|
122
|
+
|
123
|
+
# Escaping =================================================================
|
124
|
+
test "element should return element with given name and escaped content" do
|
125
|
+
assert_equal %Q{<el>content & "info" < ></el>},
|
126
|
+
@object.element!("el") { @object.write! %Q{content & "info" < >} }
|
127
|
+
end
|
128
|
+
|
129
|
+
test "element should return element with given name and escaped attributes" do
|
130
|
+
assert_equal %Q{<el attr=""attrib"" prop="a > 1 & b < 4"/>},
|
131
|
+
@object.element!("el", nil, :attr => %Q{"attrib"}, :prop => "a > 1 & b < 4")
|
132
|
+
end
|
133
|
+
|
134
|
+
test "comment should return escaped comment" do
|
135
|
+
assert_equal "<!-- remarked & commented -->", @object.comment!("remarked & commented")
|
136
|
+
end
|
137
|
+
|
138
|
+
test "instruct should return instruction with escaped attributes" do
|
139
|
+
assert_equal %Q{<?foo comment="1 < 2"?>}, @object.instruct!("foo", :comment => "1 < 2")
|
140
|
+
end
|
141
|
+
|
142
|
+
test "write should escape output" do
|
143
|
+
assert_equal %Q{foo & bar}, @object.write!("foo & bar")
|
144
|
+
end
|
145
|
+
|
146
|
+
test "element should not escape content that has been marked as html safe" do
|
147
|
+
html = Crafty::SafeString.new("<safe></safe>")
|
148
|
+
assert_equal %Q{<el><safe></safe></el>}, @object.element!("el") { html }
|
149
|
+
end
|
150
|
+
|
151
|
+
test "element should not escape attributes that have been marked as html safe" do
|
152
|
+
html = Crafty::SafeString.new("http://example.org/?q=example&a=search")
|
153
|
+
assert_equal %Q{<el attr="http://example.org/?q=example&a=search"/>}, @object.element!("el", nil, :attr => html)
|
154
|
+
end
|
155
|
+
|
156
|
+
test "write should not escape output that has been marked as html safe" do
|
157
|
+
assert_equal %Q{foo & bar}, @object.write!(Crafty::SafeString.new("foo & bar"))
|
158
|
+
end
|
159
|
+
|
160
|
+
test "element should return html safe string" do
|
161
|
+
assert_equal true, @object.element!("el").html_safe?
|
162
|
+
end
|
163
|
+
|
164
|
+
test "element should return string that reamins html safe when calling to_s" do
|
165
|
+
assert_equal true, @object.element!("el").to_s.html_safe?
|
166
|
+
end
|
167
|
+
|
168
|
+
# Building =================================================================
|
169
|
+
test "element should be nestable" do
|
170
|
+
assert_equal %Q{<el><nested>content</nested></el>},
|
171
|
+
@object.element!("el") { @object.element!("nested") { "content" } }
|
172
|
+
end
|
173
|
+
|
174
|
+
test "element should be nestable and chainable without concatenation" do
|
175
|
+
assert_equal %Q{<el><nest>content</nest><nested>more & content</nested></el>},
|
176
|
+
@object.element!("el") {
|
177
|
+
@object.element!("nest") { "content" }
|
178
|
+
@object.element!("nested") { "more & content" }
|
179
|
+
}
|
180
|
+
end
|
181
|
+
|
182
|
+
test "element should be reset state of buffer after being called" do
|
183
|
+
assert_equal %Q{<el/><el/>}, @object.element!("el") + @object.element!("el")
|
184
|
+
end
|
185
|
+
|
186
|
+
test "element should append to object that responds to arrows" do
|
187
|
+
object = Class.new(Array) { include Crafty::Tools }.new
|
188
|
+
object.element!("el") {
|
189
|
+
object.element!("nest") { "content" }
|
190
|
+
object.element!("nested") { "more & content" }
|
191
|
+
}
|
192
|
+
assert_equal ["<el>", "<nest>", "content", "</nest>", "<nested>", "more & content", "</nested>", "</el>"], object
|
193
|
+
end
|
194
|
+
|
195
|
+
test "element should escape content if appending to object that responds to arrows" do
|
196
|
+
object = Class.new(Array) { include Crafty::Tools }.new
|
197
|
+
content = "foo & bar"
|
198
|
+
object.element!("el", content)
|
199
|
+
assert_equal ["<el>", "foo & bar", "</el>"], object
|
200
|
+
end
|
201
|
+
|
202
|
+
test "element should return nil if appending to object that responds to arrows" do
|
203
|
+
object = Class.new(Array) { include Crafty::Tools }.new
|
204
|
+
assert_nil object.element!("el")
|
205
|
+
end
|
206
|
+
|
207
|
+
test "element should append html safe strings to object that responds to arrows if html safe exists" do
|
208
|
+
begin
|
209
|
+
String.class_eval do
|
210
|
+
def html_safe
|
211
|
+
obj = dup
|
212
|
+
class << obj
|
213
|
+
def html_safe?; true end
|
214
|
+
end
|
215
|
+
obj
|
216
|
+
end
|
217
|
+
end
|
218
|
+
object = Class.new(Array) { include Crafty::Tools }.new
|
219
|
+
object.element!("el") {
|
220
|
+
object.element!("nest") { "content" }
|
221
|
+
object.element!("nested") { "more & content" }
|
222
|
+
}
|
223
|
+
assert_equal [true] * object.length, object.map(&:html_safe?)
|
224
|
+
ensure
|
225
|
+
String.class_eval do
|
226
|
+
undef_method :html_safe
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
test "element should append html safe strings to object that responds to arrows if html safe does not exist" do
|
232
|
+
object = Class.new(Array) { include Crafty::Tools }.new
|
233
|
+
object.element!("el") {
|
234
|
+
object.element!("nest") { "content" }
|
235
|
+
object.element!("nested") { "more & content" }
|
236
|
+
}
|
237
|
+
assert_equal [true] * object.length, object.map(&:html_safe?)
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class ToolsetTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@toolset = Module.new { Crafty::Toolset.define(self, %w{a b c d}) }
|
6
|
+
end
|
7
|
+
|
8
|
+
# Basic functionality ======================================================
|
9
|
+
test "toolset should include methods" do
|
10
|
+
toolset = @toolset
|
11
|
+
object = Class.new { include toolset }.new
|
12
|
+
assert_equal "<a></a>", object.a
|
13
|
+
end
|
14
|
+
|
15
|
+
test "toolset should include elements" do
|
16
|
+
toolset = @toolset
|
17
|
+
object = Class.new { include toolset }.new
|
18
|
+
assert_equal "<x/>", object.element!("x")
|
19
|
+
end
|
20
|
+
|
21
|
+
# No conflicts =============================================================
|
22
|
+
test "toolset should not override existing method" do
|
23
|
+
toolset = @toolset
|
24
|
+
object = Class.new do
|
25
|
+
def b; end
|
26
|
+
include toolset
|
27
|
+
end.new
|
28
|
+
assert_equal nil, object.b {}
|
29
|
+
end
|
30
|
+
|
31
|
+
test "toolset should not override existing method when included twice" do
|
32
|
+
toolset = @toolset
|
33
|
+
object = Class.new do
|
34
|
+
def b; end
|
35
|
+
include toolset
|
36
|
+
include toolset
|
37
|
+
end.new
|
38
|
+
assert_equal nil, object.b {}
|
39
|
+
end
|
40
|
+
|
41
|
+
test "toolset should not override existing method from ancestor" do
|
42
|
+
toolset = @toolset
|
43
|
+
conflict = Module.new do
|
44
|
+
def b; end
|
45
|
+
end
|
46
|
+
object = Class.new do
|
47
|
+
include conflict
|
48
|
+
include toolset
|
49
|
+
end.new
|
50
|
+
assert_equal nil, object.b {}
|
51
|
+
end
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: crafty
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Rolf Timmermans
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-04-01 00:00:00 +02:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: Crafty provides you the tools to easily and flexibly create HTML output with pure Ruby.
|
18
|
+
email: r.timmermans@voormedia.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- LICENSE
|
25
|
+
- README.md
|
26
|
+
files:
|
27
|
+
- LICENSE
|
28
|
+
- README.md
|
29
|
+
- Rakefile
|
30
|
+
- VERSION
|
31
|
+
- crafty.gemspec
|
32
|
+
- lib/crafty.rb
|
33
|
+
- lib/crafty/safety.rb
|
34
|
+
- lib/crafty/tools.rb
|
35
|
+
- lib/crafty/toolset.rb
|
36
|
+
- lib/crafty/toolsets/html4.rb
|
37
|
+
- lib/crafty/toolsets/html5.rb
|
38
|
+
- test/test_helper.rb
|
39
|
+
- test/unit/html_test.rb
|
40
|
+
- test/unit/tools_test.rb
|
41
|
+
- test/unit/toolset_test.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage:
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.5.2
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Build HTML like a master craftsman.
|
70
|
+
test_files:
|
71
|
+
- test/test_helper.rb
|
72
|
+
- test/unit/html_test.rb
|
73
|
+
- test/unit/tools_test.rb
|
74
|
+
- test/unit/toolset_test.rb
|