faml 0.5.0 → 0.5.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.
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