faml 0.2.0
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 +7 -0
- data/.gitignore +16 -0
- data/.gitmodules +3 -0
- data/.rspec +2 -0
- data/.travis.yml +27 -0
- data/Appraisals +26 -0
- data/CHANGELOG.md +47 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +115 -0
- data/Rakefile +28 -0
- data/benchmark/attribute_builder.haml +5 -0
- data/benchmark/rendering.rb +35 -0
- data/bin/faml +4 -0
- data/ext/attribute_builder/attribute_builder.c +261 -0
- data/ext/attribute_builder/extconf.rb +3 -0
- data/faml.gemspec +38 -0
- data/gemfiles/rails_4.0.gemfile +9 -0
- data/gemfiles/rails_4.1.gemfile +9 -0
- data/gemfiles/rails_4.2.gemfile +9 -0
- data/gemfiles/rails_edge.gemfile +10 -0
- data/haml_spec_test.rb +22 -0
- data/lib/faml.rb +10 -0
- data/lib/faml/ast.rb +112 -0
- data/lib/faml/cli.rb +38 -0
- data/lib/faml/compiler.rb +374 -0
- data/lib/faml/element_parser.rb +235 -0
- data/lib/faml/engine.rb +34 -0
- data/lib/faml/filter_compilers.rb +41 -0
- data/lib/faml/filter_compilers/base.rb +43 -0
- data/lib/faml/filter_compilers/cdata.rb +15 -0
- data/lib/faml/filter_compilers/coffee.rb +16 -0
- data/lib/faml/filter_compilers/css.rb +16 -0
- data/lib/faml/filter_compilers/escaped.rb +23 -0
- data/lib/faml/filter_compilers/javascript.rb +16 -0
- data/lib/faml/filter_compilers/markdown.rb +18 -0
- data/lib/faml/filter_compilers/plain.rb +15 -0
- data/lib/faml/filter_compilers/preserve.rb +26 -0
- data/lib/faml/filter_compilers/ruby.rb +17 -0
- data/lib/faml/filter_compilers/sass.rb +15 -0
- data/lib/faml/filter_compilers/scss.rb +16 -0
- data/lib/faml/filter_compilers/tilt_base.rb +34 -0
- data/lib/faml/filter_parser.rb +54 -0
- data/lib/faml/html.rb +58 -0
- data/lib/faml/indent_tracker.rb +84 -0
- data/lib/faml/line_parser.rb +66 -0
- data/lib/faml/newline.rb +30 -0
- data/lib/faml/parser.rb +211 -0
- data/lib/faml/parser_utils.rb +17 -0
- data/lib/faml/rails_handler.rb +10 -0
- data/lib/faml/railtie.rb +9 -0
- data/lib/faml/ruby_multiline.rb +23 -0
- data/lib/faml/script_parser.rb +84 -0
- data/lib/faml/static_hash_parser.rb +113 -0
- data/lib/faml/syntax_error.rb +10 -0
- data/lib/faml/text_compiler.rb +69 -0
- data/lib/faml/tilt.rb +17 -0
- data/lib/faml/version.rb +3 -0
- data/spec/compiler_newline_spec.rb +162 -0
- data/spec/rails/Rakefile +6 -0
- data/spec/rails/app/assets/images/.keep +0 -0
- data/spec/rails/app/assets/javascripts/application.js +13 -0
- data/spec/rails/app/assets/stylesheets/application.css +15 -0
- data/spec/rails/app/controllers/application_controller.rb +5 -0
- data/spec/rails/app/controllers/books_controller.rb +8 -0
- data/spec/rails/app/controllers/concerns/.keep +0 -0
- data/spec/rails/app/helpers/application_helper.rb +2 -0
- data/spec/rails/app/mailers/.keep +0 -0
- data/spec/rails/app/models/.keep +0 -0
- data/spec/rails/app/models/book.rb +9 -0
- data/spec/rails/app/models/concerns/.keep +0 -0
- data/spec/rails/app/views/books/hello.html.haml +2 -0
- data/spec/rails/app/views/books/with_capture.html.haml +4 -0
- data/spec/rails/app/views/books/with_variables.html.haml +4 -0
- data/spec/rails/app/views/layouts/application.html.haml +9 -0
- data/spec/rails/bin/bundle +3 -0
- data/spec/rails/bin/rails +4 -0
- data/spec/rails/bin/rake +4 -0
- data/spec/rails/bin/setup +29 -0
- data/spec/rails/config.ru +4 -0
- data/spec/rails/config/application.rb +12 -0
- data/spec/rails/config/boot.rb +3 -0
- data/spec/rails/config/database.yml +25 -0
- data/spec/rails/config/environment.rb +5 -0
- data/spec/rails/config/environments/development.rb +41 -0
- data/spec/rails/config/environments/production.rb +79 -0
- data/spec/rails/config/environments/test.rb +42 -0
- data/spec/rails/config/initializers/assets.rb +11 -0
- data/spec/rails/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails/config/initializers/cookies_serializer.rb +3 -0
- data/spec/rails/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails/config/initializers/inflections.rb +16 -0
- data/spec/rails/config/initializers/mime_types.rb +4 -0
- data/spec/rails/config/initializers/secret_key_base.rb +6 -0
- data/spec/rails/config/initializers/session_store.rb +3 -0
- data/spec/rails/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails/config/locales/en.yml +23 -0
- data/spec/rails/config/routes.rb +7 -0
- data/spec/rails/config/secrets.yml +22 -0
- data/spec/rails/db/seeds.rb +7 -0
- data/spec/rails/lib/assets/.keep +0 -0
- data/spec/rails/lib/tasks/.keep +0 -0
- data/spec/rails/log/.keep +0 -0
- data/spec/rails/public/404.html +67 -0
- data/spec/rails/public/422.html +67 -0
- data/spec/rails/public/500.html +66 -0
- data/spec/rails/public/favicon.ico +0 -0
- data/spec/rails/public/robots.txt +5 -0
- data/spec/rails/spec/requests/faml_spec.rb +41 -0
- data/spec/rails/vendor/assets/javascripts/.keep +0 -0
- data/spec/rails/vendor/assets/stylesheets/.keep +0 -0
- data/spec/rails_helper.rb +4 -0
- data/spec/render/attribute_spec.rb +241 -0
- data/spec/render/comment_spec.rb +61 -0
- data/spec/render/doctype_spec.rb +57 -0
- data/spec/render/element_spec.rb +136 -0
- data/spec/render/filters/cdata_spec.rb +12 -0
- data/spec/render/filters/coffee_spec.rb +25 -0
- data/spec/render/filters/css_spec.rb +45 -0
- data/spec/render/filters/escaped_spec.rb +14 -0
- data/spec/render/filters/javascript_spec.rb +44 -0
- data/spec/render/filters/markdown_spec.rb +19 -0
- data/spec/render/filters/plain_spec.rb +24 -0
- data/spec/render/filters/preserve_spec.rb +24 -0
- data/spec/render/filters/ruby_spec.rb +13 -0
- data/spec/render/filters/sass_spec.rb +28 -0
- data/spec/render/filters/scss_spec.rb +32 -0
- data/spec/render/filters_spec.rb +11 -0
- data/spec/render/haml_comment_spec.rb +24 -0
- data/spec/render/multiline_spec.rb +39 -0
- data/spec/render/newline_spec.rb +83 -0
- data/spec/render/plain_spec.rb +20 -0
- data/spec/render/preserve_spec.rb +8 -0
- data/spec/render/sanitize_spec.rb +36 -0
- data/spec/render/script_spec.rb +81 -0
- data/spec/render/silent_script_spec.rb +97 -0
- data/spec/render/unescape_spec.rb +45 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/tilt_spec.rb +33 -0
- metadata +489 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: f3b0f1b792e063159d89d05f763e294c63d45348
|
|
4
|
+
data.tar.gz: 7aa644aea3462479bd94e0def8c174c7e560e8bc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 71db1678c99dd7e46e7d64ea14e8336879a69da2beb115524715f265e730402c617c63fc2bde58061b07190cadffd20730bacc9ad17ecefca2352a94f881e8c8
|
|
7
|
+
data.tar.gz: 63150b32c393d4b75a10681f944f98c0fa86a08f2eedc1a90151e6feb74bd09e454d02616d268e0a00918872be127a1acfc9c460dc2bf1a7858ad785ff71f2e4
|
data/.gitignore
ADDED
data/.gitmodules
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
language: ruby
|
|
2
|
+
sudo: false
|
|
3
|
+
rvm:
|
|
4
|
+
- 2.0.0
|
|
5
|
+
- 2.1
|
|
6
|
+
- 2.2
|
|
7
|
+
- ruby-head
|
|
8
|
+
gemfile:
|
|
9
|
+
- gemfiles/rails_4.0.gemfile
|
|
10
|
+
- gemfiles/rails_4.1.gemfile
|
|
11
|
+
- gemfiles/rails_4.2.gemfile
|
|
12
|
+
- gemfiles/rails_edge.gemfile
|
|
13
|
+
after_script:
|
|
14
|
+
- bundle exec rake benchmark:rendering
|
|
15
|
+
matrix:
|
|
16
|
+
allow_failures:
|
|
17
|
+
- rvm: ruby-head
|
|
18
|
+
- gemfile: gemfiles/rails_edge.gemfile
|
|
19
|
+
# https://github.com/rspec/rspec-rails/pull/1264
|
|
20
|
+
- rvm: 2.2
|
|
21
|
+
gemfile: gemfiles/rails_4.0.gemfile
|
|
22
|
+
exclude:
|
|
23
|
+
# Rails 5 requires to run on Ruby 2.2.0 or newer.
|
|
24
|
+
- rvm: 2.0.0
|
|
25
|
+
gemfile: gemfiles/rails_edge.gemfile
|
|
26
|
+
- rvm: 2.1
|
|
27
|
+
gemfile: gemfiles/rails_edge.gemfile
|
data/Appraisals
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
appraise 'rails-4.0' do
|
|
2
|
+
gem 'rails', '~> 4.0.0'
|
|
3
|
+
gem 'rspec-rails'
|
|
4
|
+
gem 'sqlite3'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
appraise 'rails-4.1' do
|
|
8
|
+
gem 'rails', '~> 4.1.0'
|
|
9
|
+
gem 'rspec-rails'
|
|
10
|
+
gem 'sqlite3'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
appraise 'rails-4.2' do
|
|
14
|
+
gem 'rails', '~> 4.2.0'
|
|
15
|
+
gem 'rspec-rails'
|
|
16
|
+
gem 'sqlite3'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
appraise 'rails-edge' do
|
|
20
|
+
gem 'rails', git: 'https://github.com/rails/rails'
|
|
21
|
+
gem 'arel', git: 'https://github.com/rails/arel'
|
|
22
|
+
gem 'rspec-rails'
|
|
23
|
+
gem 'sqlite3'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# vim: set ft=ruby:
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
## 0.2.0 (2015-03-19)
|
|
2
|
+
- Rename from fast_haml to faml
|
|
3
|
+
- Allow .faml extension
|
|
4
|
+
|
|
5
|
+
## 0.1.10 (2015-03-19)
|
|
6
|
+
- Fix ruby filter to not generate newlines
|
|
7
|
+
- Support markdown filter
|
|
8
|
+
|
|
9
|
+
## 0.1.9 (2015-03-18)
|
|
10
|
+
- Refactor script parser (internal)
|
|
11
|
+
- Fix newline generation with filters
|
|
12
|
+
- Support sass, scss and coffee filter
|
|
13
|
+
|
|
14
|
+
## 0.1.8 (2015-03-17)
|
|
15
|
+
- Fix whitespace removal (`<` and `>`) behavior
|
|
16
|
+
- Internally, new instructions `mknl` and `rmnl` are added.
|
|
17
|
+
|
|
18
|
+
## 0.1.7 (2015-03-16)
|
|
19
|
+
- Fix attribute rendering with falsey values
|
|
20
|
+
- https://github.com/eagletmt/faml/pull/11
|
|
21
|
+
|
|
22
|
+
## 0.1.6 (2015-03-11)
|
|
23
|
+
- Fix render error with comment-only script
|
|
24
|
+
- https://github.com/eagletmt/faml/issues/6
|
|
25
|
+
- Fix parsing error at multiline attributes
|
|
26
|
+
- https://github.com/eagletmt/faml/issues/7
|
|
27
|
+
|
|
28
|
+
## 0.1.5 (2015-03-01)
|
|
29
|
+
- Fix minor newline generation bug with Haml comment
|
|
30
|
+
|
|
31
|
+
## 0.1.4 (2015-02-28)
|
|
32
|
+
- Fix newline generation around empty lines
|
|
33
|
+
- Internal: introduce Ast::Empty and remove LineCounter
|
|
34
|
+
|
|
35
|
+
## 0.1.3 (2015-02-27)
|
|
36
|
+
- Fix internal compiler error when `>` is used
|
|
37
|
+
- Fix newline generation at Ast::Element case
|
|
38
|
+
|
|
39
|
+
## 0.1.2 (2015-02-24)
|
|
40
|
+
- Keep newlines for better backtrace (#4)
|
|
41
|
+
|
|
42
|
+
## 0.1.1 (2015-02-23)
|
|
43
|
+
- Fix attribute parsing with `%span {foo}` or `%span (foo)` cases.
|
|
44
|
+
- Fix comparison with statically-compilable class attributes like `%span.foo{class: bar}` .
|
|
45
|
+
|
|
46
|
+
## 0.1.0 (2015-02-23)
|
|
47
|
+
- Initial release
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2015 Kohei Suzuki
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Faml
|
|
2
|
+
[](http://badge.fury.io/rb/faml)
|
|
3
|
+
[](https://travis-ci.org/eagletmt/faml)
|
|
4
|
+
[](https://coveralls.io/r/eagletmt/faml)
|
|
5
|
+
[](https://codeclimate.com/github/eagletmt/faml)
|
|
6
|
+
|
|
7
|
+
Faster implementation of Haml template language.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add this line to your application's Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'faml'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
And then execute:
|
|
18
|
+
|
|
19
|
+
$ bundle
|
|
20
|
+
|
|
21
|
+
Or install it yourself as:
|
|
22
|
+
|
|
23
|
+
$ gem install faml
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Just replace your `gem 'haml'` with `gem 'faml'` .
|
|
28
|
+
|
|
29
|
+
## Incompatibilities
|
|
30
|
+
There are several incompatibilities.
|
|
31
|
+
|
|
32
|
+
### Hash attributes
|
|
33
|
+
Hash attributes are only supported to "data" attributes.
|
|
34
|
+
|
|
35
|
+
With original haml, `%span{foo: {bar: 'baz'}}` is rendered as `<span foo-bar='baz'></span>` .
|
|
36
|
+
With faml, it's rendered as `<span foo='{:bar=>"baz"}'></span>` .
|
|
37
|
+
|
|
38
|
+
Only "data" attributes are converted to hyphenated attributes.
|
|
39
|
+
|
|
40
|
+
### HTML-escape by default
|
|
41
|
+
Even with non-Rails project, all string are HTML-escaped.
|
|
42
|
+
|
|
43
|
+
### "ugly" mode only
|
|
44
|
+
Only "ugly" mode in original haml is supported.
|
|
45
|
+
|
|
46
|
+
```haml
|
|
47
|
+
%div
|
|
48
|
+
%div hello
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
is always rendered as
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<div>
|
|
55
|
+
<div>hello</div>
|
|
56
|
+
</div>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
It's equivalent to haml's "ugly" mode.
|
|
60
|
+
|
|
61
|
+
### Others
|
|
62
|
+
If you find other incompatibility, please report it to me :-p.
|
|
63
|
+
|
|
64
|
+
## Why faml is faster?
|
|
65
|
+
### Temple backend
|
|
66
|
+
I use [temple](https://github.com/judofyr/temple) to achieve faster template rendering.
|
|
67
|
+
It's used by [slim](https://github.com/slim-template/slim) template language & engine which is known as fast.
|
|
68
|
+
|
|
69
|
+
1. Faml::Parser converts source language (Haml template) to own AST (Faml::Ast) .
|
|
70
|
+
- You can see the Faml::Ast by running `faml parse template.haml` .
|
|
71
|
+
2. Faml::Compiler compiles Faml::Ast into Temple AST.
|
|
72
|
+
- You can see the Temple AST by running `faml temple template.haml` .
|
|
73
|
+
3. Temple compiles its AST into Ruby code.
|
|
74
|
+
- You can see the Ruby code by running `faml compile template.haml` .
|
|
75
|
+
- During this process, several optimizations are performed such as Temple::Filters::MultiFlattener and Temple::Filters::StaticMerger.
|
|
76
|
+
|
|
77
|
+
### Attribute optimization
|
|
78
|
+
Although Haml allows arbitrary Ruby hash in the attribute syntax, most attributes are written in hash literal form.
|
|
79
|
+
All keys are string or symbol literals (i.e., not dynamic values) in typical case.
|
|
80
|
+
|
|
81
|
+
```haml
|
|
82
|
+
%div{class: some_helper_method(current_user)}
|
|
83
|
+
%a{href: foo_path(@item)}= @item.name
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
There is an optimization chance if we could know the value is String.
|
|
87
|
+
I introduced incompatibility to expand the chance: all attribute values are converted to String by `#to_s` except for `id`, `class` and `data` .
|
|
88
|
+
This will enable us to avoid runtime expensive hash merging and rendering.
|
|
89
|
+
The runtime hash merging is implemented by C extension in faml.
|
|
90
|
+
|
|
91
|
+
Internally, attributes are categolized into three types.
|
|
92
|
+
|
|
93
|
+
1. Static attributes
|
|
94
|
+
- Both the key and the value are literal.
|
|
95
|
+
- Compiled into string literals.
|
|
96
|
+
- Fastest.
|
|
97
|
+
- e.g. `%input{checked: false}`
|
|
98
|
+
2. Dynamic attributes
|
|
99
|
+
- The key is literal but the value isn't.
|
|
100
|
+
- The key is compiled into string literal. The value is interpolated at run-time.
|
|
101
|
+
- Relatively fast.
|
|
102
|
+
- e.g. `%input{checked: helper_method(@record)}`
|
|
103
|
+
3. Ruby attributes
|
|
104
|
+
- Both the key and the value are non-literal expression.
|
|
105
|
+
- The attributes are stringified at run-time.
|
|
106
|
+
- Slow.
|
|
107
|
+
- e.g. `%input{helper_method(@record)}`
|
|
108
|
+
|
|
109
|
+
## Contributing
|
|
110
|
+
|
|
111
|
+
1. Fork it ( https://github.com/eagletmt/faml/fork )
|
|
112
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
113
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
114
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
115
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'bundler/gem_tasks'
|
|
2
|
+
|
|
3
|
+
task :default => [:compile, :spec]
|
|
4
|
+
|
|
5
|
+
require 'rake/extensiontask'
|
|
6
|
+
Rake::ExtensionTask.new('attribute_builder') do |ext|
|
|
7
|
+
ext.lib_dir = 'lib/faml'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require 'rspec/core/rake_task'
|
|
11
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
12
|
+
|
|
13
|
+
namespace :benchmark do
|
|
14
|
+
task :rendering => ['benchmark:rendering:haml', 'benchmark:rendering:attributes']
|
|
15
|
+
namespace :rendering do
|
|
16
|
+
desc "Run benchmark with Haml's standard template"
|
|
17
|
+
task :haml do
|
|
18
|
+
haml_gem = Gem::Specification.find_by_name('haml')
|
|
19
|
+
standard_haml_path = File.join(haml_gem.gem_dir, 'test', 'templates', 'standard.haml')
|
|
20
|
+
sh 'ruby', 'benchmark/rendering.rb', standard_haml_path
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "Run benchmark for attribute builder"
|
|
24
|
+
task :attributes do
|
|
25
|
+
sh 'ruby', 'benchmark/rendering.rb', File.join('benchmark/attribute_builder.haml')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'benchmark/ips'
|
|
3
|
+
require 'haml'
|
|
4
|
+
require 'faml'
|
|
5
|
+
require 'escape_utils/html/haml'
|
|
6
|
+
|
|
7
|
+
template = ARGV[0]
|
|
8
|
+
unless template
|
|
9
|
+
$stderr.puts "Usage: #{$0} template.haml"
|
|
10
|
+
exit 1
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Benchmark.ips do |x|
|
|
14
|
+
obj = Object.new
|
|
15
|
+
|
|
16
|
+
Haml::Engine.new(File.read(template), ugly: true, escape_html: true).def_method(obj, :haml)
|
|
17
|
+
code_array = Faml::Engine.new.call(File.read(template))
|
|
18
|
+
obj.instance_eval("def faml_array; #{code_array}; end")
|
|
19
|
+
code_string = Faml::Engine.new(generator: Temple::Generators::RailsOutputBuffer).call(File.read(template))
|
|
20
|
+
obj.instance_eval("def faml_string; #{code_string}; end")
|
|
21
|
+
|
|
22
|
+
x.report('Haml') do
|
|
23
|
+
obj.haml
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
x.report('Faml (Array)') do
|
|
27
|
+
obj.faml_array
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
x.report('Faml (String)') do
|
|
31
|
+
obj.faml_string
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
x.compare!
|
|
35
|
+
end
|
data/bin/faml
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <ruby/version.h>
|
|
3
|
+
|
|
4
|
+
#if (RUBY_API_VERSION_MAJOR > 2) || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 1)
|
|
5
|
+
/* define nothing */
|
|
6
|
+
#else
|
|
7
|
+
# define RARRAY_AREF(a, i) RARRAY_PTR(a)[i]
|
|
8
|
+
# define rb_ary_new_capa rb_ary_new2
|
|
9
|
+
#endif
|
|
10
|
+
|
|
11
|
+
VALUE rb_mAttributeBuilder;
|
|
12
|
+
static ID id_keys, id_sort_bang, id_merge_bang, id_temple, id_utils, id_escape_html, id_gsub, id_to_s;
|
|
13
|
+
|
|
14
|
+
static void
|
|
15
|
+
concat_array_attribute(VALUE attributes, VALUE hash, VALUE key)
|
|
16
|
+
{
|
|
17
|
+
VALUE v;
|
|
18
|
+
|
|
19
|
+
Check_Type(hash, T_HASH);
|
|
20
|
+
v = rb_hash_delete(hash, key);
|
|
21
|
+
if (!NIL_P(v)) {
|
|
22
|
+
VALUE ary;
|
|
23
|
+
|
|
24
|
+
v = rb_Array(v);
|
|
25
|
+
ary = rb_hash_lookup(attributes, key);
|
|
26
|
+
Check_Type(ary, T_ARRAY);
|
|
27
|
+
rb_ary_concat(ary, v);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static int
|
|
32
|
+
stringify_keys_i(VALUE key, VALUE value, VALUE arg)
|
|
33
|
+
{
|
|
34
|
+
key = rb_funcall(key, id_to_s, 0);
|
|
35
|
+
rb_hash_aset(arg, key, value);
|
|
36
|
+
return ST_CONTINUE;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static VALUE
|
|
40
|
+
stringify_keys(VALUE hash)
|
|
41
|
+
{
|
|
42
|
+
VALUE h = rb_hash_new();
|
|
43
|
+
rb_hash_foreach(hash, stringify_keys_i, h);
|
|
44
|
+
return h;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
struct normalize_data_i2_arg {
|
|
48
|
+
VALUE key, normalized;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
static int
|
|
52
|
+
normalize_data_i2(VALUE key, VALUE value, VALUE ptr)
|
|
53
|
+
{
|
|
54
|
+
struct normalize_data_i2_arg *arg = (struct normalize_data_i2_arg *)ptr;
|
|
55
|
+
VALUE k = rb_funcall(arg->key, id_to_s, 0);
|
|
56
|
+
|
|
57
|
+
k = rb_funcall(k, id_gsub, 2, rb_str_new_cstr("_"), rb_str_new_cstr("-"));
|
|
58
|
+
rb_str_cat(k, "-", 1);
|
|
59
|
+
rb_str_append(k, key);
|
|
60
|
+
rb_hash_aset(arg->normalized, k, value);
|
|
61
|
+
return ST_CONTINUE;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static VALUE normalize_data(VALUE data);
|
|
65
|
+
|
|
66
|
+
static int
|
|
67
|
+
normalize_data_i(VALUE key, VALUE value, VALUE normalized)
|
|
68
|
+
{
|
|
69
|
+
if (RB_TYPE_P(value, T_HASH)) {
|
|
70
|
+
struct normalize_data_i2_arg arg;
|
|
71
|
+
arg.key = key;
|
|
72
|
+
arg.normalized = normalized;
|
|
73
|
+
rb_hash_foreach(normalize_data(value), normalize_data_i2, (VALUE)(&arg));
|
|
74
|
+
} else {
|
|
75
|
+
key = rb_funcall(key, id_to_s, 0);
|
|
76
|
+
key = rb_funcall(key, id_gsub, 2, rb_str_new_cstr("_"), rb_str_new_cstr("-"));
|
|
77
|
+
rb_hash_aset(normalized, key, value);
|
|
78
|
+
}
|
|
79
|
+
return ST_CONTINUE;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static VALUE
|
|
83
|
+
normalize_data(VALUE data)
|
|
84
|
+
{
|
|
85
|
+
VALUE normalized;
|
|
86
|
+
|
|
87
|
+
Check_Type(data, T_HASH);
|
|
88
|
+
normalized = rb_hash_new();
|
|
89
|
+
rb_hash_foreach(data, normalize_data_i, normalized);
|
|
90
|
+
return normalized;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static void
|
|
94
|
+
normalize(VALUE hash)
|
|
95
|
+
{
|
|
96
|
+
VALUE keys = rb_funcall(hash, id_keys, 0);
|
|
97
|
+
const long len = RARRAY_LEN(keys);
|
|
98
|
+
long i;
|
|
99
|
+
for (i = 0; i < len; i++) {
|
|
100
|
+
VALUE key = RARRAY_AREF(keys, i);
|
|
101
|
+
const char *key_cstr = StringValueCStr(key);
|
|
102
|
+
VALUE value = rb_hash_lookup(hash, key);
|
|
103
|
+
if (RB_TYPE_P(value, T_HASH) && strcmp(key_cstr, "data") == 0) {
|
|
104
|
+
VALUE data, data_keys;
|
|
105
|
+
long data_len, j;
|
|
106
|
+
|
|
107
|
+
rb_hash_delete(hash, key);
|
|
108
|
+
data = normalize_data(value);
|
|
109
|
+
data_keys = rb_funcall(data, id_keys, 0);
|
|
110
|
+
rb_funcall(data_keys, id_sort_bang, 0);
|
|
111
|
+
data_len = RARRAY_LEN(data_keys);
|
|
112
|
+
for (j = 0; j < data_len; j++) {
|
|
113
|
+
VALUE data_key = RARRAY_AREF(data_keys, j);
|
|
114
|
+
VALUE k = rb_str_buf_new(5 + RSTRING_LEN(data_key));
|
|
115
|
+
rb_str_buf_cat(k, "data-", 5);
|
|
116
|
+
rb_str_buf_append(k, data_key);
|
|
117
|
+
rb_hash_aset(hash, k, rb_hash_lookup(data, data_key));
|
|
118
|
+
}
|
|
119
|
+
} else if (!(RB_TYPE_P(value, T_TRUE) || RB_TYPE_P(value, T_FALSE) || NIL_P(value))) {
|
|
120
|
+
rb_hash_aset(hash, key, rb_funcall(value, id_to_s, 0));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static void
|
|
126
|
+
merge(VALUE attributes, int argc, VALUE *argv)
|
|
127
|
+
{
|
|
128
|
+
int i;
|
|
129
|
+
|
|
130
|
+
for (i = 0; i < argc; i++) {
|
|
131
|
+
VALUE h;
|
|
132
|
+
|
|
133
|
+
Check_Type(argv[i], T_HASH);
|
|
134
|
+
h = stringify_keys(argv[i]);
|
|
135
|
+
concat_array_attribute(attributes, h, rb_str_new_cstr("class"));
|
|
136
|
+
concat_array_attribute(attributes, h, rb_str_new_cstr("id"));
|
|
137
|
+
normalize(h);
|
|
138
|
+
rb_funcall(attributes, id_merge_bang, 1, h);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static VALUE
|
|
143
|
+
put_attribute(VALUE attr_quote, VALUE key, VALUE value)
|
|
144
|
+
{
|
|
145
|
+
VALUE utils_class, str;
|
|
146
|
+
long len;
|
|
147
|
+
|
|
148
|
+
value = rb_funcall(value, id_to_s, 0);
|
|
149
|
+
utils_class = rb_const_get(rb_const_get(rb_cObject, id_temple), id_utils);
|
|
150
|
+
value = rb_funcall(utils_class, id_escape_html, 1, value);
|
|
151
|
+
|
|
152
|
+
len = 2 + 2*RSTRING_LEN(attr_quote) + RSTRING_LEN(key) + RSTRING_LEN(value);
|
|
153
|
+
str = rb_str_buf_new(len);
|
|
154
|
+
rb_str_buf_cat(str, " ", 1);
|
|
155
|
+
rb_str_buf_append(str, key);
|
|
156
|
+
rb_str_buf_cat(str, "=", 1);
|
|
157
|
+
rb_str_buf_append(str, attr_quote);
|
|
158
|
+
rb_str_buf_append(str, value);
|
|
159
|
+
rb_str_buf_append(str, attr_quote);
|
|
160
|
+
return str;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static VALUE
|
|
164
|
+
build_attribute(VALUE attr_quote, VALUE key, VALUE value)
|
|
165
|
+
{
|
|
166
|
+
const char *key_cstr = StringValueCStr(key);
|
|
167
|
+
if (strcmp(key_cstr, "class") == 0) {
|
|
168
|
+
long len;
|
|
169
|
+
|
|
170
|
+
Check_Type(value, T_ARRAY);
|
|
171
|
+
len = RARRAY_LEN(value);
|
|
172
|
+
if (len == 0) {
|
|
173
|
+
return rb_str_new_cstr("");
|
|
174
|
+
} else {
|
|
175
|
+
long i;
|
|
176
|
+
VALUE ary = rb_ary_new_capa(len);
|
|
177
|
+
for (i = 0; i < len; i++) {
|
|
178
|
+
VALUE v = RARRAY_AREF(value, i);
|
|
179
|
+
rb_ary_push(ary, rb_funcall(v, id_to_s, 0));
|
|
180
|
+
}
|
|
181
|
+
rb_funcall(ary, id_sort_bang, 0);
|
|
182
|
+
return put_attribute(attr_quote, key, rb_ary_join(ary, rb_str_new_cstr(" ")));
|
|
183
|
+
}
|
|
184
|
+
} else if (strcmp(key_cstr, "id") == 0) {
|
|
185
|
+
long len = RARRAY_LEN(value);
|
|
186
|
+
|
|
187
|
+
Check_Type(value, T_ARRAY);
|
|
188
|
+
len = RARRAY_LEN(value);
|
|
189
|
+
if (len == 0) {
|
|
190
|
+
return rb_str_new_cstr("");
|
|
191
|
+
} else {
|
|
192
|
+
long i;
|
|
193
|
+
VALUE ary = rb_ary_new_capa(len);
|
|
194
|
+
for (i = 0; i < len; i++) {
|
|
195
|
+
VALUE v = RARRAY_AREF(value, i);
|
|
196
|
+
rb_ary_push(ary, rb_funcall(v, id_to_s, 0));
|
|
197
|
+
}
|
|
198
|
+
return put_attribute(attr_quote, key, rb_ary_join(ary, rb_str_new_cstr("_")));
|
|
199
|
+
}
|
|
200
|
+
} else if (RB_TYPE_P(value, T_TRUE)) {
|
|
201
|
+
VALUE attr = rb_str_buf_new(1 + RSTRING_LEN(key));
|
|
202
|
+
rb_str_buf_cat(attr, " ", 1);
|
|
203
|
+
rb_str_buf_append(attr, key);
|
|
204
|
+
return attr;
|
|
205
|
+
} else if (RB_TYPE_P(value, T_FALSE) || NIL_P(value)) {
|
|
206
|
+
return Qnil;
|
|
207
|
+
} else {
|
|
208
|
+
return put_attribute(attr_quote, key, value);
|
|
209
|
+
}
|
|
210
|
+
return value;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
static VALUE
|
|
214
|
+
m_build(int argc, VALUE *argv, VALUE self)
|
|
215
|
+
{
|
|
216
|
+
VALUE attr_quote, attributes, keys, buf;
|
|
217
|
+
long len, i;
|
|
218
|
+
|
|
219
|
+
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
|
|
220
|
+
attr_quote = argv[0];
|
|
221
|
+
attributes = rb_hash_new();
|
|
222
|
+
rb_hash_aset(attributes, rb_str_new_cstr("id"), rb_ary_new());
|
|
223
|
+
rb_hash_aset(attributes, rb_str_new_cstr("class"), rb_ary_new());
|
|
224
|
+
merge(attributes, argc-1, argv+1);
|
|
225
|
+
|
|
226
|
+
keys = rb_funcall(attributes, id_keys, 0);
|
|
227
|
+
rb_funcall(keys, id_sort_bang, 0);
|
|
228
|
+
len = RARRAY_LEN(keys);
|
|
229
|
+
buf = rb_ary_new_capa(len);
|
|
230
|
+
for (i = 0; i < len; i++) {
|
|
231
|
+
VALUE k = RARRAY_AREF(keys, i);
|
|
232
|
+
rb_ary_push(buf, build_attribute(attr_quote, k, rb_hash_lookup(attributes, k)));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return rb_ary_join(buf, Qnil);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
static VALUE
|
|
239
|
+
m_normalize_data(VALUE self, VALUE data)
|
|
240
|
+
{
|
|
241
|
+
return normalize_data(data);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
void
|
|
245
|
+
Init_attribute_builder(void)
|
|
246
|
+
{
|
|
247
|
+
VALUE mFaml = rb_define_module("Faml");
|
|
248
|
+
rb_mAttributeBuilder = rb_define_module_under(mFaml, "AttributeBuilder");
|
|
249
|
+
rb_define_singleton_method(rb_mAttributeBuilder, "build", RUBY_METHOD_FUNC(m_build), -1);
|
|
250
|
+
rb_define_singleton_method(rb_mAttributeBuilder, "normalize_data", RUBY_METHOD_FUNC(m_normalize_data), 1);
|
|
251
|
+
|
|
252
|
+
id_keys = rb_intern("keys");
|
|
253
|
+
id_sort_bang = rb_intern("sort!");
|
|
254
|
+
id_merge_bang = rb_intern("merge!");
|
|
255
|
+
id_temple = rb_intern("Temple");
|
|
256
|
+
id_utils = rb_intern("Utils");
|
|
257
|
+
id_escape_html = rb_intern("escape_html");
|
|
258
|
+
id_gsub = rb_intern("gsub");
|
|
259
|
+
id_to_s = rb_intern("to_s");
|
|
260
|
+
rb_require("temple");
|
|
261
|
+
}
|