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.
@@ -3,6 +3,10 @@ module HammerBuilder
3
3
  class AbstractTag
4
4
 
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,11 @@ module HammerBuilder
58
62
  name = attribute.name.to_s
59
63
  case attribute.type
60
64
  when :string
61
- Strings.add "attr_#{name}", " #{name.gsub('_', '-')}=\""
62
- "@output << Strings::ATTR_#{name.upcase} << CGI.escapeHTML(content.to_s) << Strings::QUOTE"
65
+ strings_injector.add "attr_#{name}", " #{name.gsub('_', '-')}=#{@_str_quote}"
66
+ "@output << @_str_attr_#{name} << CGI.escapeHTML(content.to_s) << @_str_quote"
63
67
  when :boolean
64
- Strings.add "attr_#{name}", " #{name.gsub('_', '-')}=\"#{name}\""
65
- "@output << Strings::ATTR_#{name.upcase} if content"
68
+ strings_injector.add "attr_#{name}", " #{name.gsub('_', '-')}=#{@_str_quote}#{name}#{@_str_quote}"
69
+ "@output << @_str_attr_#{name} if content"
66
70
  end
67
71
  end
68
72
 
@@ -87,11 +91,13 @@ module HammerBuilder
87
91
  @stack = builder.instance_eval { @_stack }
88
92
  @classes = []
89
93
  @tag_name = self.rclass.tag_name
94
+
95
+ self.rclass.strings_injector.inject_to self
90
96
  end
91
97
 
92
98
  # @api private
93
99
  def open(attributes = nil)
94
- @output << Strings::LT << @tag_name
100
+ @output << @_str_lt << @tag_name
95
101
  @builder.current = self
96
102
  attributes(attributes)
97
103
  default
@@ -103,7 +109,7 @@ module HammerBuilder
103
109
  # @param [#to_s] value
104
110
  def attribute(name, value)
105
111
  return __send__(name, value) if respond_to?(name)
106
- @output << Strings::SPACE << name.to_s << Strings::EQL_QUOTE << CGI.escapeHTML(value.to_s) << Strings::QUOTE
112
+ @output << @_str_space << name.to_s << @_str_eql_quote << CGI.escapeHTML(value.to_s) << @_str_quote
107
113
  self
108
114
  end
109
115
 
@@ -131,28 +137,26 @@ module HammerBuilder
131
137
  data_attribute = /^data_([a-z_]+)$/
132
138
  METHOD_MISSING_REGEXP = /#{data_attribute}|#{id_class}/ unless defined? METHOD_MISSING_REGEXP
133
139
 
134
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
135
- # allows data-* attributes and id, classes by method_missing
136
- def method_missing(method, *args, &block)
137
- method = method.to_s
138
- if method =~ METHOD_MISSING_REGEXP
139
- if $1
140
- self.rclass.add_attributes Data::Attribute.new(method, :string)
141
- self.send method, *args
142
- else
143
- self.__send__($3 == '!' ? :id : :class, $2)
144
- end
145
- else
146
- super(method, *args, &block)
147
- end
140
+ # allows data-* attributes and id, classes by method_missing
141
+ def method_missing(method, *args, &block)
142
+ method = method.to_s
143
+ if method =~ METHOD_MISSING_REGEXP
144
+ if $1
145
+ self.rclass.add_attributes Data::Attribute.new(method, :string)
146
+ self.send method, *args
147
+ else
148
+ self.__send__($3 == '!' ? :id : :class, $2)
148
149
  end
150
+ else
151
+ super(method, *args, &block)
152
+ end
153
+ end
149
154
 
150
- #def respond_to?(symbol, include_private = false)
151
- # symbol.to_s =~ METHOD_MISSING_REGEXP || super(symbol, include_private)
152
- #end
153
- RUBY
155
+ #def respond_to?(symbol, include_private = false)
156
+ # symbol.to_s =~ METHOD_MISSING_REGEXP || super(symbol, include_private)
157
+ #end
154
158
 
155
- Strings.add "attr_class", " class=\""
159
+ strings_injector.add "attr_class", " class=#{strings_injector[:quote]}"
156
160
  # adds classes to the tag by joining +classes+ with ' ' and skipping non-true classes
157
161
  # @param [Array<#to_s>] classes
158
162
  # @example
@@ -162,14 +166,13 @@ module HammerBuilder
162
166
  self
163
167
  end
164
168
 
165
- Strings.add "attr_id", " id=\""
169
+ strings_injector.add "attr_id", " id=#{strings_injector[:quote]}"
166
170
  # adds id to the tag by joining +values+ with '_'
167
171
  # @param [Array<#to_s>] values
168
172
  # @example
169
173
  # id('user', 12) #=> id="user_15"
170
174
  def id(*values)
171
- @output << Strings::ATTR_ID << CGI.escapeHTML(values.select { |v| v }.join(Strings::UNDERSCORE)) <<
172
- Strings::QUOTE
175
+ @output << @_str_attr_id << CGI.escapeHTML(values.select { |v| v }.join(@_str_underscore)) << @_str_quote
173
176
  self
174
177
  end
175
178
 
@@ -222,7 +225,7 @@ module HammerBuilder
222
225
  # @api private
223
226
  def flush_classes
224
227
  unless @classes.empty?
225
- @output << Strings::ATTR_CLASS << CGI.escapeHTML(@classes.join(Strings::SPACE)) << Strings::QUOTE
228
+ @output << @_str_attr_class << CGI.escapeHTML(@classes.join(@_str_space)) << @_str_quote
226
229
  @classes.clear
227
230
  end
228
231
  end
@@ -235,7 +238,7 @@ module HammerBuilder
235
238
  # closes the tag
236
239
  def flush
237
240
  flush_classes
238
- @output << Strings::SLASH_GT
241
+ @output << @_str_slash_gt
239
242
  nil
240
243
  end
241
244
  end
@@ -243,55 +246,50 @@ module HammerBuilder
243
246
 
244
247
  nil
245
248
 
246
- # defined by class_eval because there is a error cased by super
247
- # super from singleton method that is defined to multiple classes is not supported;
248
- # this will be fixed in 1.9.3 or later (NotImplementedError)
249
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
250
- # @api private
251
- def initialize(builder)
252
- super
253
- @content = nil
254
- end
249
+ # @api private
250
+ def initialize(builder)
251
+ super
252
+ @content = nil
253
+ end
255
254
 
256
- # allows data-* attributes and id, classes by method_missing
257
- def method_missing(method, *args, &block)
258
- method = method.to_s
259
- if method =~ METHOD_MISSING_REGEXP
260
- if $1
261
- self.rclass.add_attributes Data::Attribute.new(method.to_sym, :string)
262
- self.send method, *args, &block
263
- else
264
- self.content(args[0]) if args[0]
265
- self.__send__($3 == '!' ? :id : :class, $2, &block)
266
- end
267
- else
268
- super(method, *args, &block)
269
- end
255
+ # allows data-* attributes and id, classes by method_missing
256
+ def method_missing(method, *args, &block)
257
+ method = method.to_s
258
+ if method =~ METHOD_MISSING_REGEXP
259
+ if $1
260
+ self.rclass.add_attributes Data::Attribute.new(method.to_sym, :string)
261
+ self.send method, *args, &block
262
+ else
263
+ self.content(args[0]) if args[0]
264
+ self.__send__($3 == '!' ? :id : :class, $2, &block)
270
265
  end
266
+ else
267
+ super(method, *args, &block)
268
+ end
269
+ end
271
270
 
272
- # @api private
273
- def open(*args, &block)
274
- attributes = if args.last.is_a?(Hash)
275
- args.pop
276
- end
277
- content args[0]
278
- super attributes
279
- @stack << @tag_name
280
- if block
281
- with &block
282
- else
283
- self
284
- end
285
- end
286
- RUBY
271
+ # @api private
272
+ def open(*args, &block)
273
+ attributes = if args.last.is_a?(Hash)
274
+ args.pop
275
+ end
276
+ content args[0]
277
+ super attributes
278
+ @stack << @tag_name
279
+ if block
280
+ with &block
281
+ else
282
+ self
283
+ end
284
+ end
287
285
 
288
286
  # @api private
289
287
  # closes the tag
290
288
  def flush
291
289
  flush_classes
292
- @output << Strings::GT
290
+ @output << @_str_gt
293
291
  @output << CGI.escapeHTML(@content) if @content
294
- @output << Strings::SLASH_LT << @stack.pop << Strings::GT
292
+ @output << @_str_slash_lt << @stack.pop << @_str_gt
295
293
  @content = nil
296
294
  end
297
295
 
@@ -314,7 +312,7 @@ module HammerBuilder
314
312
  # end # => <div id="id">content</div>
315
313
  def with
316
314
  flush_classes
317
- @output << Strings::GT
315
+ @output << @_str_gt
318
316
  @content = nil
319
317
  @builder.current = nil
320
318
  yield
@@ -322,37 +320,35 @@ module HammerBuilder
322
320
  # @output << EscapeUtils.escape_html(content)
323
321
  #end
324
322
  @builder.flush
325
- @output << Strings::SLASH_LT << @stack.pop << Strings::GT
323
+ @output << @_str_slash_lt << @stack.pop << @_str_gt
326
324
  nil
327
325
  end
328
326
 
329
327
  alias_method :w, :with
330
328
 
331
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
332
- def mimic(obj, &block)
333
- super(obj, &nil)
334
- return with(&block) if block
335
- self
336
- end
329
+ def mimic(obj, &block)
330
+ super(obj, &nil)
331
+ return with(&block) if block
332
+ self
333
+ end
337
334
 
338
- def data(hash, &block)
339
- super(hash, &nil)
340
- return with(&block) if block
341
- self
342
- end
335
+ def data(hash, &block)
336
+ super(hash, &nil)
337
+ return with(&block) if block
338
+ self
339
+ end
343
340
 
344
- def attribute(name, value, &block)
345
- super(name, value, &nil)
346
- return with(&block) if block
347
- self
348
- end
341
+ def attribute(name, value, &block)
342
+ super(name, value, &nil)
343
+ return with(&block) if block
344
+ self
345
+ end
349
346
 
350
- def attributes(attrs, &block)
351
- super(attrs, &nil)
352
- return with(&block) if block
353
- self
354
- end
355
- RUBY
347
+ def attributes(attrs, &block)
348
+ super(attrs, &nil)
349
+ return with(&block) if block
350
+ self
351
+ end
356
352
 
357
353
  protected
358
354
 
@@ -363,20 +359,20 @@ module HammerBuilder
363
359
 
364
360
  if instance_methods.include?(attribute.name)
365
361
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
366
- def #{name}(*args, &block)
367
- super(*args, &nil)
368
- return with(&block) if block
369
- self
370
- end
362
+ def #{name}(*args, &block)
363
+ super(*args, &nil)
364
+ return with(&block) if block
365
+ self
366
+ end
371
367
  RUBY
372
368
  else
373
369
  content_rendering = attribute_content_rendering(attribute)
374
370
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
375
- def #{name}(content#{' = true' if attribute.type == :boolean}, &block)
376
- #{content_rendering}
377
- return with(&block) if block
378
- self
379
- end
371
+ def #{name}(content#{' = true' if attribute.type == :boolean}, &block)
372
+ #{content_rendering}
373
+ return with(&block) if block
374
+ self
375
+ end
380
376
  RUBY
381
377
  end
382
378
  end
@@ -137,12 +137,19 @@ module HammerBuilder
137
137
  DescribableClass
138
138
  end
139
139
 
140
- klass = Class.new(superclass, &klass_definition.definition)
141
- klass._description = "#{base}.dc[:#{klass_definition.name}]"
140
+ set_up_klass = lambda do |klass, description, block|
141
+ klass._description = description
142
+ klass.instance_variable_set :@dynamic_class_base, base
143
+ klass.singleton_class.send :attr_reader, :dynamic_class_base
144
+ klass.class_eval &block
145
+ end
146
+
147
+ klass = Class.new(superclass)
148
+ set_up_klass.call klass, "#{base}.dc[:#{klass_definition.name}]", klass_definition.definition
142
149
 
143
150
  class_extensions(name).each do |klass_extension|
144
- klass = Class.new(klass, &klass_extension.definition)
145
- klass._description = "#{base}.dc[:#{klass_extension.name}]"
151
+ klass = Class.new klass
152
+ set_up_klass.call klass, "#{base}.dc[:#{klass_extension.name}]", klass_extension.definition
146
153
  end
147
154
 
148
155
  @classes[name] = klass
@@ -8,7 +8,7 @@ module HammerBuilder
8
8
  dynamic_classes do
9
9
  extend_class :AbstractTag do
10
10
  def open(attributes = nil)
11
- @output << Strings::NEWLINE << Strings::SPACES.fetch(@stack.size, Strings::SPACE) << Strings::LT << @tag_name
11
+ @output << @_str_newline << @_str_spaces.fetch(@stack.size, @_str_space) << @_str_lt << @tag_name
12
12
  @builder.current = self
13
13
  attributes(attributes)
14
14
  default
@@ -19,7 +19,7 @@ module HammerBuilder
19
19
  extend_class :AbstractDoubleTag do
20
20
  def with
21
21
  flush_classes
22
- @output << Strings::GT
22
+ @output << @_str_gt
23
23
  @content = nil
24
24
  @builder.current = nil
25
25
  yield
@@ -27,8 +27,8 @@ module HammerBuilder
27
27
  # @output << EscapeUtils.escape_html(content, false)
28
28
  #end
29
29
  @builder.flush
30
- @output << Strings::NEWLINE << Strings::SPACES.fetch(@stack.size-1, Strings::SPACE) << Strings::SLASH_LT <<
31
- @stack.pop << Strings::GT
30
+ @output << @_str_newline << @_str_spaces.fetch(@stack.size-1, @_str_space) << @_str_slash_lt <<
31
+ @stack.pop << @_str_gt
32
32
  nil
33
33
  end
34
34
  end
@@ -36,8 +36,8 @@ module HammerBuilder
36
36
 
37
37
  def comment(comment)
38
38
  flush
39
- @_output << Strings::NEWLINE << Strings::SPACES.fetch(@_stack.size, Strings::SPACE) << Strings::COMMENT_START <<
40
- comment.to_s << Strings::COMMENT_END
39
+ @_output << @_str_newline << @_str_spaces.fetch(@_stack.size, @_str_space) << @_str_comment_start <<
40
+ comment.to_s << @_str_comment_end
41
41
  end
42
42
  end
43
43
  end
@@ -0,0 +1,43 @@
1
+ module HammerBuilder
2
+ class StringsInjector
3
+
4
+ attr_reader :strings, :objects_to_update
5
+
6
+ def initialize(&block)
7
+ @strings = Hash.new {|hash, key| raise ArgumentError "missing key #{key}" }
8
+ @objects_to_update = []
9
+ instance_eval &block
10
+ end
11
+
12
+ def [](name)
13
+ strings[name]
14
+ end
15
+
16
+ def add(name, value)
17
+ name = name.to_sym
18
+ raise "string #{name} is already set to #{value}" if strings.has_key?(name) && self[name] != value
19
+ replace name, value
20
+ end
21
+
22
+ def replace(name, value)
23
+ name = name.to_sym
24
+ strings[name] = value
25
+ update_objects name
26
+ end
27
+
28
+ def inject_to(obj)
29
+ @objects_to_update << obj
30
+ strings.keys.each { |name| update_object obj, name }
31
+ end
32
+
33
+ private
34
+
35
+ def update_objects(name)
36
+ objects_to_update.each { |obj| update_object obj, name }
37
+ end
38
+
39
+ def update_object(obj, name)
40
+ obj.instance_variable_set(:"@_str_#{name}", self[name])
41
+ end
42
+ end
43
+ end
@@ -119,31 +119,38 @@ describe HammerBuilder do
119
119
 
120
120
  it 'should render #id' do
121
121
  quick_render { div.id :an_id }.should == '<div id="an_id"></div>'
122
- quick_render { div.an_id! }.should == '<div id="an_id"></div>'
123
- quick_render { div.an_id! { text 'content' } }.should == '<div id="an_id">content</div>'
124
- quick_render { div.an_id! 'content' }.should == '<div id="an_id">content</div>'
125
- quick_render { div.an_id! }.should == '<div id="an_id"></div>'
122
+ quick_render { div.an_id! }.should == '<div id="an-id"></div>'
123
+ quick_render { div.an_id! { text 'content' } }.should == '<div id="an-id">content</div>'
124
+ quick_render { div.an_id! 'content' }.should == '<div id="an-id">content</div>'
125
+ quick_render { div.an_id! }.should == '<div id="an-id"></div>'
126
126
  quick_render { div :id => 12 }.should == '<div id="12"></div>'
127
127
  quick_render { div 'asd', :id => 12 }.should == '<div id="12">asd</div>'
128
128
  quick_render { hr.id 'an_id' }.should == '<hr id="an_id" />'
129
- quick_render { hr.an_id! }.should == '<hr id="an_id" />'
129
+ quick_render { hr.an_id! }.should == '<hr id="an-id" />'
130
130
  quick_render { hr :id => 'an_id' }.should == '<hr id="an_id" />'
131
131
 
132
- quick_render { hr.id 'an', 'id', nil, false }.should == '<hr id="an_id" />'
133
- quick_render { div.id 'an', 'id', nil, false }.should == '<div id="an_id"></div>'
132
+ quick_render { hr.id 'an', 'id', nil, false }.should == '<hr id="an-id" />'
133
+ quick_render { div.id 'an', 'id', nil, false }.should == '<div id="an-id"></div>'
134
+
135
+ quick_render { div.an_id! :class => 'big' }.should == '<div id="an-id" class="big"></div>'
136
+ quick_render { div.an_id!(:class => 'big') { text 'content' } }.should ==
137
+ '<div id="an-id" class="big">content</div>'
138
+ quick_render { div.an_id! 'content', :class => 'big' }.should == '<div id="an-id" class="big">content</div>'
139
+ quick_render { div.an_id! 'content' }.should == '<div id="an-id">content</div>'
140
+ quick_render { hr.an_id! :class => 'big' }.should == '<hr id="an-id" class="big" />'
134
141
  end
135
142
 
136
143
  it 'should render #class' do
137
144
  #noinspection RubyArgCount
138
145
  quick_render { div.class 'an_class' }.should == '<div class="an_class"></div>'
139
- quick_render { div.an_class }.should == '<div class="an_class"></div>'
146
+ quick_render { div.an_class }.should == '<div class="an-class"></div>'
140
147
  quick_render { div :class => 'an_class' }.should == '<div class="an_class"></div>'
141
148
  #noinspection RubyArgCount
142
149
  quick_render { hr.class 'an_class' }.should == '<hr class="an_class" />'
143
- quick_render { hr.an_class }.should == '<hr class="an_class" />'
150
+ quick_render { hr.an_class }.should == '<hr class="an-class" />'
144
151
  quick_render { hr :class => 'an_class' }.should == '<hr class="an_class" />'
145
152
 
146
- quick_render { div.an_class.another_class }.should == '<div class="an_class another_class"></div>'
153
+ quick_render { div.an_class.another_class }.should == '<div class="an-class another-class"></div>'
147
154
  #noinspection RubyArgCount
148
155
  quick_render { div.class 'an_class', 'another_class' }.should == '<div class="an_class another_class"></div>'
149
156
  quick_render { div :class => ['an_class', 'another_class'] }.should == '<div class="an_class another_class"></div>'
@@ -159,7 +166,7 @@ describe HammerBuilder do
159
166
 
160
167
  it '#[]' do
161
168
  obj = Object.new
162
- quick_render { div[obj] }.should == %Q(<div id="object_#{obj.object_id}" class="object"></div>)
169
+ quick_render { div[obj] }.should == %Q(<div id="object-#{obj.object_id}" class="object"></div>)
163
170
 
164
171
  class AnObject
165
172
  def self.hammer_builder_ref
@@ -178,11 +185,11 @@ describe HammerBuilder do
178
185
  "an_id";
179
186
  end
180
187
  end)
181
- quick_render { div[obj] }.should == %Q(<div id="object_an_id" class="object"></div>)
182
- quick_render { div.mimic(obj) { text 'a' } }.should == %Q(<div id="object_an_id" class="object">a</div>)
188
+ quick_render { div[obj] }.should == %Q(<div id="object-an_id" class="object"></div>)
189
+ quick_render { div.mimic(obj) { text 'a' } }.should == %Q(<div id="object-an_id" class="object">a</div>)
183
190
  end
184
191
 
185
- it "#data-.*" do
192
+ it "#data-secret" do
186
193
  quick_render { div('a').data_secret("I won't tell.") }.should == '<div data-secret="I won\'t tell.">a</div>'
187
194
  end
188
195
 
@@ -240,7 +247,7 @@ describe HammerBuilder do
240
247
  comment 'asd'
241
248
  end
242
249
  end.should == '<!DOCTYPE html>' + "\n" +
243
- '<html xmlns="http://www.w3.org/1999/xhtml"><head><title id="an_id">a_title</title><meta charset="utf-8" />'+
250
+ '<html xmlns="http://www.w3.org/1999/xhtml"><head><title id="an-id">a_title</title><meta charset="utf-8" />'+
244
251
  '</head><body id="content">asd<div style="" class="left">asd<hr /></div><br /><div class="left"><hr />'+
245
252
  '<script type="text/javascript">asd</script><script type="text/javascript"><![CDATA[asd]]></script>'+
246
253
  '</div><!--asd--></body><!--asd--></html>'