crafty 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/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
|