hammer_builder 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +1 -4
- data/lib/hammer_builder/abstract.rb +35 -11
- data/lib/hammer_builder/abstract/abstract_double_tag.rb +67 -71
- data/lib/hammer_builder/abstract/abstract_single_tag.rb +1 -1
- data/lib/hammer_builder/abstract/abstract_tag.rb +45 -39
- data/lib/hammer_builder/doc.rb +100 -104
- data/lib/hammer_builder/dynamic_classes.rb +11 -4
- data/lib/hammer_builder/formatted.rb +6 -6
- data/lib/hammer_builder/strings_injector.rb +43 -0
- data/spec/hammer_builder_spec.rb +22 -15
- metadata +58 -47
- data/lib/hammer_builder/strings.rb +0 -29
data/README.md
CHANGED
@@ -5,12 +5,9 @@ Fast Ruby xhtml5 renderer
|
|
5
5
|
## Links
|
6
6
|
|
7
7
|
* **Presentation**: <http://hammer.pitr.ch/hammer_builder/presentation/presentation.html>
|
8
|
-
* Gemcutter: <https://rubygems.org/gems/hammer_builder>
|
9
8
|
* Github: <https://github.com/ruby-hammer/hammer-builder>
|
10
|
-
* Yardoc: <http://rubydoc.info/
|
9
|
+
* Yardoc: <http://rubydoc.info/gems/hammer_builder/frames>
|
11
10
|
* Issues: <https://github.com/ruby-hammer/hammer-builder/issues>
|
12
|
-
* Changelog: <http://hammer.pitr.ch/hammer-builder/file.CHANGELOG.html>
|
13
|
-
* Gem: [https://rubygems.org/gems/hammer_builder](https://rubygems.org/gems/hammer_builder)
|
14
11
|
* Blog: <http://hammer.pitr.ch/>
|
15
12
|
|
16
13
|
## Syntax
|
@@ -3,7 +3,7 @@ require 'active_support/core_ext/class/attribute'
|
|
3
3
|
require 'active_support/core_ext/string/inflections'
|
4
4
|
|
5
5
|
require 'hammer_builder/dynamic_classes'
|
6
|
-
require 'hammer_builder/
|
6
|
+
require 'hammer_builder/strings_injector'
|
7
7
|
require 'hammer_builder/data'
|
8
8
|
require 'hammer_builder/data/html5'
|
9
9
|
require "hammer_builder/pool"
|
@@ -19,6 +19,27 @@ module HammerBuilder
|
|
19
19
|
require "hammer_builder/abstract/abstract_single_tag"
|
20
20
|
require "hammer_builder/abstract/abstract_double_tag"
|
21
21
|
|
22
|
+
def self.strings_injector
|
23
|
+
@strings_injector ||= StringsInjector.new do
|
24
|
+
add :lt, '<'
|
25
|
+
add :gt, '>'
|
26
|
+
add :slash_lt, '</'
|
27
|
+
add :slash_gt, ' />'
|
28
|
+
add :dash, '-'
|
29
|
+
add :underscore, '_'
|
30
|
+
add :space, ' '
|
31
|
+
add :spaces, Array.new(300) { |i| (' ' * i).freeze }
|
32
|
+
add :newline, "\n"
|
33
|
+
add :quote, '"'
|
34
|
+
add :eql, '='
|
35
|
+
add :eql_quote, self[:eql] + self[:quote]
|
36
|
+
add :comment_start, '<!--'
|
37
|
+
add :comment_end, '-->'
|
38
|
+
add :cdata_start, '<![CDATA['
|
39
|
+
add :cdata_end, ']]>'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
22
43
|
# << faster then +
|
23
44
|
# yield faster then block.call
|
24
45
|
# accessing ivar and constant is faster then accesing hash or cvar
|
@@ -58,6 +79,9 @@ module HammerBuilder
|
|
58
79
|
@_output = ""
|
59
80
|
@_stack = []
|
60
81
|
@_current = nil
|
82
|
+
|
83
|
+
self.class.strings_injector.inject_to self
|
84
|
+
|
61
85
|
# tag classes initialization
|
62
86
|
tags.each do |klass|
|
63
87
|
instance_variable_set(:"@_#{klass}", self.class.dynamic_classes[klass.camelize.to_sym].new(self))
|
@@ -79,13 +103,13 @@ module HammerBuilder
|
|
79
103
|
# inserts +comment+
|
80
104
|
def comment(comment)
|
81
105
|
flush
|
82
|
-
@_output <<
|
106
|
+
@_output << @_str_comment_start << comment.to_s << @_str_comment_end
|
83
107
|
end
|
84
108
|
|
85
109
|
# insersts CDATA with +content+
|
86
110
|
def cdata(content)
|
87
111
|
flush
|
88
|
-
@_output <<
|
112
|
+
@_output << @_str_cdata_start << content.to_s << @_str_cdata_end
|
89
113
|
end
|
90
114
|
|
91
115
|
# renders html5 doc type
|
@@ -120,7 +144,7 @@ module HammerBuilder
|
|
120
144
|
# @example
|
121
145
|
# HammerBuilder::Formatted.new.go_in('asd') do |string|
|
122
146
|
# div string
|
123
|
-
# end.to_html
|
147
|
+
# end.to_html #=> "<div>asd</div>"
|
124
148
|
#
|
125
149
|
def go_in(*variables, &block)
|
126
150
|
instance_exec *variables, &block
|
@@ -186,13 +210,13 @@ module HammerBuilder
|
|
186
210
|
def join(collection, glue = nil, &it)
|
187
211
|
# TODO as helper? two block method call #join(collection, &item).with(&glue)
|
188
212
|
glue_block = case glue
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
213
|
+
when String
|
214
|
+
lambda { text glue }
|
215
|
+
when Proc
|
216
|
+
glue
|
217
|
+
else
|
218
|
+
lambda { }
|
219
|
+
end
|
196
220
|
|
197
221
|
collection.each_with_index do |obj, i|
|
198
222
|
glue_block.call() if i > 0
|
@@ -5,55 +5,53 @@ module HammerBuilder
|
|
5
5
|
def_class :AbstractDoubleTag, :AbstractTag do ###import
|
6
6
|
nil
|
7
7
|
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def initialize(builder)
|
14
|
-
super
|
15
|
-
@content = nil
|
16
|
-
end
|
8
|
+
# @api private
|
9
|
+
def initialize(builder)
|
10
|
+
super
|
11
|
+
@content = nil
|
12
|
+
end
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
else
|
26
|
-
self.content(args[0]) if args[0]
|
27
|
-
self.__send__($3 == '!' ? :id : :class, $2, &block)
|
28
|
-
end
|
14
|
+
# allows data-* attributes and id, classes by method_missing
|
15
|
+
def method_missing(method, *args, &block)
|
16
|
+
method = method.to_s
|
17
|
+
if method =~ METHOD_MISSING_REGEXP
|
18
|
+
if $1
|
19
|
+
self.rclass.add_attributes Data::Attribute.new(method.to_sym, :string)
|
20
|
+
self.send method, *args, &block
|
29
21
|
else
|
30
|
-
|
22
|
+
attributes(if args.last.is_a?(Hash)
|
23
|
+
args.pop
|
24
|
+
end)
|
25
|
+
content args.first
|
26
|
+
self.__send__($3 == '!' ? :id : :class, $2.gsub(@_str_underscore, @_str_dash), &block)
|
31
27
|
end
|
28
|
+
else
|
29
|
+
super(method, *args, &block)
|
32
30
|
end
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
33
|
+
# @api private
|
34
|
+
def open(*args, &block)
|
35
|
+
attributes = if args.last.is_a?(Hash)
|
36
|
+
args.pop
|
37
|
+
end
|
38
|
+
content args[0]
|
39
|
+
super attributes
|
40
|
+
@stack << @tag_name
|
41
|
+
if block
|
42
|
+
with &block
|
43
|
+
else
|
44
|
+
self
|
47
45
|
end
|
48
|
-
|
46
|
+
end
|
49
47
|
|
50
48
|
# @api private
|
51
49
|
# closes the tag
|
52
50
|
def flush
|
53
51
|
flush_classes
|
54
|
-
@output <<
|
52
|
+
@output << @_str_gt
|
55
53
|
@output << CGI.escapeHTML(@content) if @content
|
56
|
-
@output <<
|
54
|
+
@output << @_str_slash_lt << @stack.pop << @_str_gt
|
57
55
|
@content = nil
|
58
56
|
end
|
59
57
|
|
@@ -76,7 +74,7 @@ module HammerBuilder
|
|
76
74
|
# end # => <div id="id">content</div>
|
77
75
|
def with
|
78
76
|
flush_classes
|
79
|
-
@output <<
|
77
|
+
@output << @_str_gt
|
80
78
|
@content = nil
|
81
79
|
@builder.current = nil
|
82
80
|
yield
|
@@ -84,37 +82,35 @@ module HammerBuilder
|
|
84
82
|
# @output << EscapeUtils.escape_html(content)
|
85
83
|
#end
|
86
84
|
@builder.flush
|
87
|
-
@output <<
|
85
|
+
@output << @_str_slash_lt << @stack.pop << @_str_gt
|
88
86
|
nil
|
89
87
|
end
|
90
88
|
|
91
89
|
alias_method :w, :with
|
92
90
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
91
|
+
def mimic(obj, &block)
|
92
|
+
super(obj, &nil)
|
93
|
+
return with(&block) if block
|
94
|
+
self
|
95
|
+
end
|
99
96
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
97
|
+
def data(hash, &block)
|
98
|
+
super(hash, &nil)
|
99
|
+
return with(&block) if block
|
100
|
+
self
|
101
|
+
end
|
105
102
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
103
|
+
def attribute(name, value, &block)
|
104
|
+
super(name, value, &nil)
|
105
|
+
return with(&block) if block
|
106
|
+
self
|
107
|
+
end
|
111
108
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
RUBY
|
109
|
+
def attributes(attrs, &block)
|
110
|
+
super(attrs, &nil)
|
111
|
+
return with(&block) if block
|
112
|
+
self
|
113
|
+
end
|
118
114
|
|
119
115
|
protected
|
120
116
|
|
@@ -125,20 +121,20 @@ module HammerBuilder
|
|
125
121
|
|
126
122
|
if instance_methods.include?(attribute.name)
|
127
123
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
124
|
+
def #{name}(*args, &block)
|
125
|
+
super(*args, &nil)
|
126
|
+
return with(&block) if block
|
127
|
+
self
|
128
|
+
end
|
133
129
|
RUBY
|
134
130
|
else
|
135
131
|
content_rendering = attribute_content_rendering(attribute)
|
136
132
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
133
|
+
def #{name}(content#{' = true' if attribute.type == :boolean}, &block)
|
134
|
+
#{content_rendering}
|
135
|
+
return with(&block) if block
|
136
|
+
self
|
137
|
+
end
|
142
138
|
RUBY
|
143
139
|
end
|
144
140
|
end
|
@@ -3,6 +3,10 @@ module HammerBuilder
|
|
3
3
|
dynamic_classes do
|
4
4
|
def_class :AbstractTag do ###import
|
5
5
|
|
6
|
+
def self.strings_injector
|
7
|
+
dynamic_class_base.strings_injector
|
8
|
+
end
|
9
|
+
|
6
10
|
class_attribute :_attributes, :instance_writer => false, :instance_reader => false
|
7
11
|
self._attributes = []
|
8
12
|
|
@@ -58,11 +62,13 @@ module HammerBuilder
|
|
58
62
|
name = attribute.name.to_s
|
59
63
|
case attribute.type
|
60
64
|
when :string
|
61
|
-
|
62
|
-
"@output <<
|
65
|
+
strings_injector.add "attr_#{name}", " #{name.gsub('_', '-')}=#{strings_injector[:quote]}"
|
66
|
+
"@output << @_str_attr_#{name} << CGI.escapeHTML(content.to_s) << @_str_quote"
|
63
67
|
when :boolean
|
64
|
-
|
65
|
-
|
68
|
+
strings_injector.add(
|
69
|
+
"attr_#{name}",
|
70
|
+
" #{name.gsub('_', '-')}=#{strings_injector[:quote]}#{name}#{strings_injector[:quote]}")
|
71
|
+
"@output << @_str_attr_#{name} if content"
|
66
72
|
end
|
67
73
|
end
|
68
74
|
|
@@ -87,11 +93,13 @@ module HammerBuilder
|
|
87
93
|
@stack = builder.instance_eval { @_stack }
|
88
94
|
@classes = []
|
89
95
|
@tag_name = self.rclass.tag_name
|
96
|
+
|
97
|
+
self.rclass.strings_injector.inject_to self
|
90
98
|
end
|
91
99
|
|
92
100
|
# @api private
|
93
101
|
def open(attributes = nil)
|
94
|
-
@output <<
|
102
|
+
@output << @_str_lt << @tag_name
|
95
103
|
@builder.current = self
|
96
104
|
attributes(attributes)
|
97
105
|
default
|
@@ -103,7 +111,7 @@ module HammerBuilder
|
|
103
111
|
# @param [#to_s] value
|
104
112
|
def attribute(name, value)
|
105
113
|
return __send__(name, value) if respond_to?(name)
|
106
|
-
@output <<
|
114
|
+
@output << @_str_space << name.to_s << @_str_eql_quote << CGI.escapeHTML(value.to_s) << @_str_quote
|
107
115
|
self
|
108
116
|
end
|
109
117
|
|
@@ -131,28 +139,27 @@ module HammerBuilder
|
|
131
139
|
data_attribute = /^data_([a-z_]+)$/
|
132
140
|
METHOD_MISSING_REGEXP = /#{data_attribute}|#{id_class}/ unless defined? METHOD_MISSING_REGEXP
|
133
141
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
if
|
139
|
-
|
140
|
-
|
141
|
-
self.send method, *args
|
142
|
-
else
|
143
|
-
self.__send__($3 == '!' ? :id : :class, $2)
|
144
|
-
end
|
142
|
+
# allows data-* attributes and id, classes by method_missing
|
143
|
+
def method_missing(method, *args, &block)
|
144
|
+
method = method.to_s
|
145
|
+
if method =~ METHOD_MISSING_REGEXP
|
146
|
+
if $1
|
147
|
+
self.rclass.add_attributes Data::Attribute.new(method, :string)
|
148
|
+
self.send method, *args
|
145
149
|
else
|
146
|
-
|
150
|
+
self.__send__($3 == '!' ? :id : :class, $2.gsub(@_str_underscore, @_str_dash))
|
151
|
+
self.attributes args.first
|
147
152
|
end
|
153
|
+
else
|
154
|
+
super(method, *args, &block)
|
148
155
|
end
|
156
|
+
end
|
149
157
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
RUBY
|
158
|
+
#def respond_to?(symbol, include_private = false)
|
159
|
+
# symbol.to_s =~ METHOD_MISSING_REGEXP || super(symbol, include_private)
|
160
|
+
#end
|
154
161
|
|
155
|
-
|
162
|
+
strings_injector.add "attr_class", " class=#{strings_injector[:quote]}"
|
156
163
|
# adds classes to the tag by joining +classes+ with ' ' and skipping non-true classes
|
157
164
|
# @param [Array<#to_s>] classes
|
158
165
|
# @example
|
@@ -162,14 +169,13 @@ module HammerBuilder
|
|
162
169
|
self
|
163
170
|
end
|
164
171
|
|
165
|
-
|
172
|
+
strings_injector.add "attr_id", " id=#{strings_injector[:quote]}"
|
166
173
|
# adds id to the tag by joining +values+ with '_'
|
167
174
|
# @param [Array<#to_s>] values
|
168
175
|
# @example
|
169
|
-
# id('user', 12) #=> id="
|
176
|
+
# id('user', 12) #=> id="user-15"
|
170
177
|
def id(*values)
|
171
|
-
@output <<
|
172
|
-
Strings::QUOTE
|
178
|
+
@output << @_str_attr_id << CGI.escapeHTML(values.select { |v| v }.join(@_str_dash)) << @_str_quote
|
173
179
|
self
|
174
180
|
end
|
175
181
|
|
@@ -182,19 +188,19 @@ module HammerBuilder
|
|
182
188
|
# div[AUser.new].with { text 'a' } # => <div id="a_user_1" class="a_user">a</div>
|
183
189
|
def mimic(obj)
|
184
190
|
klass = if obj.class.respond_to? :hammer_builder_ref
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
191
|
+
obj.class.hammer_builder_ref
|
192
|
+
else
|
193
|
+
ActiveSupport::Inflector.underscore(obj.class.to_s).tr('/', '-')
|
194
|
+
end
|
189
195
|
|
190
196
|
id = case
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
197
|
+
when obj.respond_to?(:hammer_builder_ref)
|
198
|
+
obj.hammer_builder_ref
|
199
|
+
when obj.respond_to?(:id)
|
200
|
+
[klass, obj.id]
|
201
|
+
else
|
202
|
+
[klass, obj.object_id]
|
203
|
+
end
|
198
204
|
#noinspection RubyArgCount
|
199
205
|
self.class(klass).id(id)
|
200
206
|
end
|
@@ -222,7 +228,7 @@ module HammerBuilder
|
|
222
228
|
# @api private
|
223
229
|
def flush_classes
|
224
230
|
unless @classes.empty?
|
225
|
-
@output <<
|
231
|
+
@output << @_str_attr_class << CGI.escapeHTML(@classes.join(@_str_space)) << @_str_quote
|
226
232
|
@classes.clear
|
227
233
|
end
|
228
234
|
end
|