faml 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c7f71b5e360cafa8cef276e06dbc8c8b0c2214bb
4
- data.tar.gz: b94247f91e3508b3261686ad678eb539cf535ea5
3
+ metadata.gz: 467a84dc8e9b891942ce31fc121c273080fcc72a
4
+ data.tar.gz: 9523d713adcd1dc6daf5dd384cf74fc26e5be883
5
5
  SHA512:
6
- metadata.gz: 2aa2f8a5e0e40444fa7c87d1f864fe0ba713b7f2a76ddcc722635233a43a43a8b20ad480f599636a2f85f84a0018ba0adf912f0b2399f537b8c95ae0af26552c
7
- data.tar.gz: e11276dff335fe35d1c95753539ea45be0500e09ff030a616d53b587b6cc5570869cc40ad0247f33c8deaefd0c764e9a8c1a424ae522faf87f50712e1a62087a
6
+ metadata.gz: a2f64b49c6b59166fc97eab6078a4a0e529ed9c0b9fa45257891606c0c49582b100d08fd64e38e5ef0bf9c2aa689e79888db4381ddc5ce119268163f6c647c9f
7
+ data.tar.gz: 9ddc8674331446ae2459eeeb0360f924d383f88b2cbdef065a47a765f420d3ae9e6ebee590ca241db3e3ae02a958903082818eb38700d7872ed5c792bd894d42
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.5.1 (2015-11-18)
2
+ - Fix id ordering of object reference
3
+ - https://github.com/eagletmt/faml/issues/33
4
+
1
5
  ## 0.5.0 (2015-11-17)
2
6
  - Support object reference syntax
3
7
  - https://github.com/eagletmt/faml/pull/32
@@ -165,19 +165,28 @@ normalize(VALUE hash)
165
165
  }
166
166
 
167
167
  static void
168
- merge(VALUE attributes, int argc, VALUE *argv)
168
+ merge_one(VALUE attributes, VALUE arg)
169
+ {
170
+ VALUE h;
171
+
172
+ Check_Type(arg, T_HASH);
173
+ h = stringify_keys(arg);
174
+ concat_array_attribute(attributes, h, rb_const_get(rb_mAttributeBuilder, id_class));
175
+ concat_array_attribute(attributes, h, rb_const_get(rb_mAttributeBuilder, id_id));
176
+ normalize(h);
177
+ rb_funcall(attributes, id_merge_bang, 1, h);
178
+ }
179
+
180
+ static void
181
+ merge(VALUE attributes, VALUE object_ref, int argc, VALUE *argv)
169
182
  {
170
183
  int i;
171
184
 
172
185
  for (i = 0; i < argc; i++) {
173
- VALUE h;
174
-
175
- Check_Type(argv[i], T_HASH);
176
- h = stringify_keys(argv[i]);
177
- concat_array_attribute(attributes, h, rb_const_get(rb_mAttributeBuilder, id_class));
178
- concat_array_attribute(attributes, h, rb_const_get(rb_mAttributeBuilder, id_id));
179
- normalize(h);
180
- rb_funcall(attributes, id_merge_bang, 1, h);
186
+ merge_one(attributes, argv[i]);
187
+ }
188
+ if (!NIL_P(object_ref)) {
189
+ merge_one(attributes, object_ref);
181
190
  }
182
191
  }
183
192
 
@@ -253,17 +262,18 @@ build_attribute(VALUE buf, VALUE attr_quote, int is_html, VALUE key, VALUE value
253
262
  static VALUE
254
263
  m_build(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
255
264
  {
256
- VALUE attr_quote, attributes, keys, buf;
265
+ VALUE attr_quote, object_ref, attributes, keys, buf;
257
266
  int is_html;
258
267
  long len, i;
259
268
 
260
- rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
269
+ rb_check_arity(argc, 3, UNLIMITED_ARGUMENTS);
261
270
  attr_quote = argv[0];
262
271
  is_html = RTEST(argv[1]);
272
+ object_ref = argv[2];
263
273
  attributes = rb_hash_new();
264
274
  rb_hash_aset(attributes, rb_const_get(rb_mAttributeBuilder, id_id), rb_ary_new());
265
275
  rb_hash_aset(attributes, rb_const_get(rb_mAttributeBuilder, id_class), rb_ary_new());
266
- merge(attributes, argc-2, argv+2);
276
+ merge(attributes, object_ref, argc-3, argv+3);
267
277
 
268
278
  keys = rb_funcall(attributes, id_keys, 0);
269
279
  rb_funcall(keys, id_sort_bang, 0);
@@ -1,7 +1,7 @@
1
1
  # Incompatibilities
2
2
  ## Versions
3
3
  - Haml 4.0.7
4
- - Faml 0.5.0
4
+ - Faml 0.5.1
5
5
  - Hamlit 1.7.2
6
6
 
7
7
  ## Table of contents
@@ -513,3 +513,21 @@
513
513
 
514
514
  ```
515
515
 
516
+ # [./spec/render/attribute_spec.rb:227](../../../spec/render/attribute_spec.rb#L227)
517
+ ## Input
518
+ ```haml
519
+ %span#baz[Faml::TestStruct.new(123)]{id: "foo"} hello
520
+ ```
521
+
522
+ ## Faml, Haml
523
+ ```html
524
+ <span class='faml_test_struct' id='baz_foo_faml_test_struct_123'>hello</span>
525
+
526
+ ```
527
+
528
+ ## Hamlit
529
+ ```html
530
+ <span id='baz'>[Faml::TestStruct.new(123)]{id: "foo"} hello</span>
531
+
532
+ ```
533
+
@@ -0,0 +1,98 @@
1
+ require_relative 'attribute_optimizer'
2
+ require_relative 'object_ref'
3
+
4
+ module Faml
5
+ class AttributeCompiler
6
+ def compile(ast)
7
+ if !ast.object_ref && ast.attributes.empty?
8
+ return compile_static_id_and_class(ast.static_id, ast.static_class)
9
+ end
10
+
11
+ unless ast.object_ref
12
+ attrs = try_optimize_attributes(ast.attributes, ast.static_id, ast.static_class)
13
+ if attrs
14
+ line_count = ast.attributes.count("\n")
15
+ return [:multi, [:html, :attrs, *attrs]].concat([[:newline]] * line_count)
16
+ end
17
+ end
18
+
19
+ compile_slow_attributes(ast.attributes, ast.static_id, ast.static_class, ast.object_ref)
20
+ end
21
+
22
+ private
23
+
24
+ def compile_static_id_and_class(static_id, static_class)
25
+ [:html, :attrs].tap do |html_attrs|
26
+ unless static_class.empty?
27
+ html_attrs << [:haml, :attr, 'class', [:static, static_class]]
28
+ end
29
+ unless static_id.empty?
30
+ html_attrs << [:haml, :attr, 'id', [:static, static_id]]
31
+ end
32
+ end
33
+ end
34
+
35
+ def try_optimize_attributes(text, static_id, static_class)
36
+ static_attributes, dynamic_attributes = AttributeOptimizer.new.try_optimize(text, static_id, static_class)
37
+ if static_attributes
38
+ (static_attributes.keys + dynamic_attributes.keys).sort.flat_map do |k|
39
+ if static_attributes.key?(k)
40
+ compile_static_attribute(k, static_attributes[k])
41
+ else
42
+ compile_dynamic_attribute(k, dynamic_attributes[k])
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def compile_static_attribute(key, value)
49
+ if value.is_a?(Hash) && key == 'data'
50
+ data = AttributeBuilder.normalize_data(value)
51
+ data.keys.sort.map do |k|
52
+ compile_static_simple_attribute("data-#{k}", data[k])
53
+ end
54
+ else
55
+ [compile_static_simple_attribute(key, value)]
56
+ end
57
+ end
58
+
59
+ def compile_static_simple_attribute(key, value)
60
+ case
61
+ when value == true
62
+ [:haml, :attr, key, [:multi]]
63
+ when value == false || value.nil?
64
+ [:multi]
65
+ else
66
+ [:haml, :attr, key, [:static, Temple::Utils.escape_html(value)]]
67
+ end
68
+ end
69
+
70
+ def compile_dynamic_attribute(key, value)
71
+ [[:haml, :attr, key, [:dvalue, value]]]
72
+ end
73
+
74
+ def compile_slow_attributes(text, static_id, static_class, object_ref)
75
+ h = {}
76
+ unless static_class.empty?
77
+ h[:class] = static_class.split(/ +/)
78
+ end
79
+ unless static_id.empty?
80
+ h[:id] = static_id
81
+ end
82
+
83
+ codes = []
84
+ if object_ref
85
+ codes << "::Faml::ObjectRef.render(#{object_ref})"
86
+ else
87
+ codes << 'nil'
88
+ end
89
+ unless h.empty?
90
+ codes << h.inspect
91
+ end
92
+ unless text.empty?
93
+ codes << text
94
+ end
95
+ [:haml, :attrs, codes.join(', ')]
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,100 @@
1
+ require_relative 'error'
2
+ require_relative 'ruby_syntax_checker'
3
+ require_relative 'static_hash_parser'
4
+
5
+ module Faml
6
+ class AttributeOptimizer
7
+ def try_optimize(text, static_id, static_class)
8
+ parser = StaticHashParser.new
9
+ unless parser.parse("{#{text}}")
10
+ assert_valid_ruby_code!(text)
11
+ return [nil, nil]
12
+ end
13
+
14
+ static_attributes, dynamic_attributes = build_optimized_attributes(parser, static_id, static_class)
15
+ if optimizable?(text, static_attributes, dynamic_attributes)
16
+ [static_attributes, dynamic_attributes]
17
+ else
18
+ [nil, nil]
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def assert_valid_ruby_code!(text)
25
+ RubySyntaxChecker.new("call(#{text})", '(faml)').parse
26
+ true
27
+ rescue RubySyntaxChecker::Error
28
+ raise UnparsableRubyCode.new("Unparsable Ruby code is given to attributes: #{text}", nil)
29
+ end
30
+
31
+ def build_optimized_attributes(parser, static_id, static_class)
32
+ static_attributes = build_optimized_static_attributes(parser, static_id, static_class)
33
+ dynamic_attributes = build_optimized_dynamic_attributes(parser, static_attributes)
34
+ if dynamic_attributes
35
+ [static_attributes, dynamic_attributes]
36
+ else
37
+ [nil, nil]
38
+ end
39
+ end
40
+
41
+ def build_optimized_static_attributes(parser, static_id, static_class)
42
+ static_attributes = {}
43
+ parser.static_attributes.each do |k, v|
44
+ static_attributes[k.to_s] = v
45
+ end
46
+
47
+ class_list = Array(static_attributes.delete('class')).select { |v| v }.flat_map { |c| c.to_s.split(/ +/) }
48
+ unless static_class.empty?
49
+ class_list.concat(static_class.split(/ +/))
50
+ end
51
+ unless class_list.empty?
52
+ static_attributes['class'] = class_list.uniq.sort.join(' ')
53
+ end
54
+
55
+ id_list = Array(static_attributes.delete('id')).select { |v| v }
56
+ unless static_id.empty?
57
+ id_list = [static_id].concat(id_list)
58
+ end
59
+ unless id_list.empty?
60
+ static_attributes['id'] = id_list.join('_')
61
+ end
62
+
63
+ static_attributes
64
+ end
65
+
66
+ def build_optimized_dynamic_attributes(parser, static_attributes)
67
+ dynamic_attributes = {}
68
+ parser.dynamic_attributes.each do |k, v|
69
+ k = k.to_s
70
+ if static_attributes.key?(k)
71
+ if StaticHashParser::SPECIAL_ATTRIBUTES.include?(k)
72
+ # XXX: Quit optimization
73
+ return nil
74
+ end
75
+ end
76
+ dynamic_attributes[k] = v
77
+ end
78
+ dynamic_attributes
79
+ end
80
+
81
+ def optimizable?(text, static_attributes, dynamic_attributes)
82
+ if static_attributes.nil?
83
+ return false
84
+ end
85
+
86
+ if dynamic_attributes.key?('data')
87
+ # XXX: Quit optimization...
88
+ return false
89
+ end
90
+
91
+ if text.include?("\n") && !dynamic_attributes.empty?
92
+ # XXX: Quit optimization to keep newlines
93
+ # https://github.com/eagletmt/faml/issues/18
94
+ return false
95
+ end
96
+
97
+ true
98
+ end
99
+ end
100
+ end
data/lib/faml/compiler.rb CHANGED
@@ -1,20 +1,15 @@
1
1
  require 'ripper'
2
2
  require 'temple'
3
3
  require 'haml_parser/ast'
4
+ require_relative 'attribute_compiler'
4
5
  require_relative 'error'
5
6
  require_relative 'filter_compilers'
6
7
  require_relative 'helpers'
7
- require_relative 'object_ref'
8
8
  require_relative 'rails_helpers'
9
- require_relative 'ruby_syntax_checker'
10
- require_relative 'static_hash_parser'
11
9
  require_relative 'text_compiler'
12
10
 
13
11
  module Faml
14
12
  class Compiler < Temple::Parser
15
- class UnparsableRubyCode < Error
16
- end
17
-
18
13
  DEFAULT_AUTO_CLOSE_TAGS = %w[
19
14
  area base basefont br col command embed frame hr img input isindex keygen
20
15
  link menuitem meta param source track wbr
@@ -182,7 +177,7 @@ module Faml
182
177
  :haml, :tag,
183
178
  ast.tag_name,
184
179
  self_closing?(ast),
185
- compile_attributes(ast.attributes, ast.static_id, ast.static_class, ast.object_ref),
180
+ AttributeCompiler.new.compile(ast),
186
181
  ]
187
182
 
188
183
  if ast.oneline_child
@@ -224,168 +219,6 @@ module Faml
224
219
  ast.nuke_inner_whitespace || options[:preserve].include?(ast.tag_name)
225
220
  end
226
221
 
227
- def compile_attributes(text, static_id, static_class, object_ref)
228
- if !object_ref && text.empty?
229
- return compile_static_id_and_class(static_id, static_class)
230
- end
231
-
232
- unless object_ref
233
- attrs = try_optimize_attributes(text, static_id, static_class)
234
- if attrs
235
- line_count = text.count("\n")
236
- return [:multi, [:html, :attrs, *attrs]].concat([[:newline]] * line_count)
237
- end
238
- end
239
-
240
- # Slow version
241
-
242
- h = {}
243
- unless static_class.empty?
244
- h[:class] = static_class.split(/ +/)
245
- end
246
- unless static_id.empty?
247
- h[:id] = static_id
248
- end
249
-
250
- codes = []
251
- unless h.empty?
252
- codes << h.inspect
253
- end
254
- if object_ref
255
- codes << "::Faml::ObjectRef.render(#{object_ref})"
256
- end
257
- unless text.empty?
258
- codes << text
259
- end
260
- [:haml, :attrs, codes.join(', ')]
261
- end
262
-
263
- def compile_static_id_and_class(static_id, static_class)
264
- [:html, :attrs].tap do |html_attrs|
265
- unless static_class.empty?
266
- html_attrs << [:haml, :attr, 'class', [:static, static_class]]
267
- end
268
- unless static_id.empty?
269
- html_attrs << [:haml, :attr, 'id', [:static, static_id]]
270
- end
271
- end
272
- end
273
-
274
- def try_optimize_attributes(text, static_id, static_class)
275
- parser = StaticHashParser.new
276
- unless parser.parse("{#{text}}")
277
- assert_valid_ruby_code!(text)
278
- return nil
279
- end
280
-
281
- static_attributes, dynamic_attributes = build_optimized_attributes(parser, static_id, static_class)
282
- if static_attributes.nil?
283
- return nil
284
- end
285
-
286
- if dynamic_attributes.key?('data')
287
- # XXX: Quit optimization...
288
- return nil
289
- end
290
-
291
- if text.include?("\n") && !dynamic_attributes.empty?
292
- # XXX: Quit optimization to keep newlines
293
- # https://github.com/eagletmt/faml/issues/18
294
- return nil
295
- end
296
-
297
- (static_attributes.keys + dynamic_attributes.keys).sort.flat_map do |k|
298
- if static_attributes.key?(k)
299
- compile_static_attribute(k, static_attributes[k])
300
- else
301
- compile_dynamic_attribute(k, dynamic_attributes[k])
302
- end
303
- end
304
- end
305
-
306
- def assert_valid_ruby_code!(text)
307
- RubySyntaxChecker.new("call(#{text})", '(faml)').parse
308
- true
309
- rescue RubySyntaxChecker::Error
310
- raise UnparsableRubyCode.new("Unparsable Ruby code is given to attributes: #{text}", nil)
311
- end
312
-
313
- def build_optimized_attributes(parser, static_id, static_class)
314
- static_attributes = build_optimized_static_attributes(parser, static_id, static_class)
315
- dynamic_attributes = build_optimized_dynamic_attributes(parser, static_attributes)
316
- if dynamic_attributes
317
- [static_attributes, dynamic_attributes]
318
- else
319
- [nil, nil]
320
- end
321
- end
322
-
323
- def build_optimized_static_attributes(parser, static_id, static_class)
324
- static_attributes = {}
325
- parser.static_attributes.each do |k, v|
326
- static_attributes[k.to_s] = v
327
- end
328
-
329
- class_list = Array(static_attributes.delete('class')).select { |v| v }.flat_map { |c| c.to_s.split(/ +/) }
330
- unless static_class.empty?
331
- class_list.concat(static_class.split(/ +/))
332
- end
333
- unless class_list.empty?
334
- static_attributes['class'] = class_list.uniq.sort.join(' ')
335
- end
336
-
337
- id_list = Array(static_attributes.delete('id')).select { |v| v }
338
- unless static_id.empty?
339
- id_list = [static_id].concat(id_list)
340
- end
341
- unless id_list.empty?
342
- static_attributes['id'] = id_list.join('_')
343
- end
344
-
345
- static_attributes
346
- end
347
-
348
- def build_optimized_dynamic_attributes(parser, static_attributes)
349
- dynamic_attributes = {}
350
- parser.dynamic_attributes.each do |k, v|
351
- k = k.to_s
352
- if static_attributes.key?(k)
353
- if StaticHashParser::SPECIAL_ATTRIBUTES.include?(k)
354
- # XXX: Quit optimization
355
- return nil
356
- end
357
- end
358
- dynamic_attributes[k] = v
359
- end
360
- dynamic_attributes
361
- end
362
-
363
- def compile_static_attribute(key, value)
364
- if value.is_a?(Hash) && key == 'data'
365
- data = AttributeBuilder.normalize_data(value)
366
- data.keys.sort.map do |k|
367
- compile_static_simple_attribute("data-#{k}", data[k])
368
- end
369
- else
370
- [compile_static_simple_attribute(key, value)]
371
- end
372
- end
373
-
374
- def compile_static_simple_attribute(key, value)
375
- case
376
- when value == true
377
- [:haml, :attr, key, [:multi]]
378
- when value == false || value.nil?
379
- [:multi]
380
- else
381
- [:haml, :attr, key, [:static, Temple::Utils.escape_html(value)]]
382
- end
383
- end
384
-
385
- def compile_dynamic_attribute(key, value)
386
- [[:haml, :attr, key, [:dvalue, value]]]
387
- end
388
-
389
222
  def compile_script(ast)
390
223
  sym = unique_name
391
224
  temple = [:multi]
data/lib/faml/error.rb CHANGED
@@ -7,4 +7,7 @@ module Faml
7
7
  @lineno = lineno
8
8
  end
9
9
  end
10
+
11
+ class UnparsableRubyCode < Error
12
+ end
10
13
  end
data/lib/faml/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Faml
2
- VERSION = '0.5.0'
2
+ VERSION = '0.5.1'
3
3
  end
@@ -84,7 +84,7 @@ RSpec.describe 'Faml with Rails', type: :request do
84
84
  end
85
85
  end
86
86
 
87
- describe Faml::Compiler::UnparsableRubyCode do
87
+ describe Faml::UnparsableRubyCode do
88
88
  it 'has proper backtrace' do
89
89
  expect { get '/books/unparsable' }.to raise_error { |e|
90
90
  expect(e.backtrace[0]).to end_with('app/views/books/unparsable.html.haml:2')
@@ -102,7 +102,7 @@ HAML
102
102
  end
103
103
 
104
104
  it 'raises error when unparsable Ruby code is given' do
105
- expect { render_string('%span{x ==== 2}') }.to raise_error(Faml::Compiler::UnparsableRubyCode)
105
+ expect { render_string('%span{x ==== 2}') }.to raise_error(Faml::UnparsableRubyCode)
106
106
  end
107
107
 
108
108
  context 'with xhtml format' do
@@ -223,5 +223,9 @@ HAML
223
223
  it 'renders id and class attribute with haml_object_ref' do
224
224
  expect(render_string('%span[Faml::TestRefStruct.new(123)] hello')).to eq("<span class='faml_test' id='faml_test_123'>hello</span>\n")
225
225
  end
226
+
227
+ it 'renders id in correct order' do
228
+ expect(render_string('%span#baz[Faml::TestStruct.new(123)]{id: "foo"} hello')).to eq("<span class='faml_test_struct' id='baz_foo_faml_test_struct_123'>hello</span>\n")
229
+ end
226
230
  end
227
231
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohei Suzuki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-17 00:00:00.000000000 Z
11
+ date: 2015-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: escape_utils
@@ -350,6 +350,8 @@ files:
350
350
  - incompatibilities/spec/render/silent_script_spec.md
351
351
  - incompatibilities/spec/render/unescape_spec.md
352
352
  - lib/faml.rb
353
+ - lib/faml/attribute_compiler.rb
354
+ - lib/faml/attribute_optimizer.rb
353
355
  - lib/faml/cli.rb
354
356
  - lib/faml/compiler.rb
355
357
  - lib/faml/engine.rb