hammer_builder 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>'