hamlet 0.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.
- data/.gitignore +1 -0
- data/Gemfile +25 -0
- data/LICENSE +21 -0
- data/README.md +52 -0
- data/Rakefile +57 -0
- data/bin/hamletrb +7 -0
- data/extra/slim-mode.el +409 -0
- data/extra/test.hamlet +50 -0
- data/hamlet.gemspec +36 -0
- data/lib/hamlet.rb +7 -0
- data/lib/hamlet/engine.rb +3 -0
- data/lib/hamlet/parser.rb +147 -0
- data/lib/hamlet/template.rb +19 -0
- data/test/rails/Rakefile +7 -0
- data/test/rails/app/controllers/application_controller.rb +3 -0
- data/test/rails/app/controllers/parents_controller.rb +84 -0
- data/test/rails/app/controllers/slim_controller.rb +32 -0
- data/test/rails/app/helpers/application_helper.rb +2 -0
- data/test/rails/app/models/child.rb +3 -0
- data/test/rails/app/models/parent.rb +4 -0
- data/test/rails/app/views/layouts/application.html.slim +10 -0
- data/test/rails/app/views/parents/_form.html.slim +7 -0
- data/test/rails/app/views/parents/edit.html.slim +1 -0
- data/test/rails/app/views/parents/new.html.slim +1 -0
- data/test/rails/app/views/parents/show.html.slim +5 -0
- data/test/rails/app/views/slim/_partial.html.slim +1 -0
- data/test/rails/app/views/slim/content_for.html.slim +7 -0
- data/test/rails/app/views/slim/erb.html.erb +1 -0
- data/test/rails/app/views/slim/integers.html.slim +1 -0
- data/test/rails/app/views/slim/nil.html.slim +1 -0
- data/test/rails/app/views/slim/no_layout.html.slim +1 -0
- data/test/rails/app/views/slim/normal.html.slim +1 -0
- data/test/rails/app/views/slim/partial.html.slim +2 -0
- data/test/rails/app/views/slim/variables.html.slim +1 -0
- data/test/rails/config.ru +4 -0
- data/test/rails/config/application.rb +45 -0
- data/test/rails/config/boot.rb +10 -0
- data/test/rails/config/database.yml +4 -0
- data/test/rails/config/environment.rb +5 -0
- data/test/rails/config/environments/development.rb +26 -0
- data/test/rails/config/environments/production.rb +49 -0
- data/test/rails/config/environments/test.rb +35 -0
- data/test/rails/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails/config/initializers/inflections.rb +10 -0
- data/test/rails/config/initializers/mime_types.rb +5 -0
- data/test/rails/config/initializers/secret_token.rb +7 -0
- data/test/rails/config/initializers/session_store.rb +8 -0
- data/test/rails/config/locales/en.yml +5 -0
- data/test/rails/config/routes.rb +60 -0
- data/test/rails/db/migrate/20101220223037_parents_and_children.rb +17 -0
- data/test/rails/script/rails +6 -0
- data/test/rails/test/helper.rb +10 -0
- data/test/rails/test/test_slim.rb +77 -0
- data/test/slim/helper.rb +200 -0
- data/test/slim/test_chain_manipulation.rb +42 -0
- data/test/slim/test_code_blocks.rb +68 -0
- data/test/slim/test_code_escaping.rb +53 -0
- data/test/slim/test_code_evaluation.rb +281 -0
- data/test/slim/test_code_output.rb +159 -0
- data/test/slim/test_code_structure.rb +95 -0
- data/test/slim/test_embedded_engines.rb +141 -0
- data/test/slim/test_html_escaping.rb +40 -0
- data/test/slim/test_html_structure.rb +438 -0
- data/test/slim/test_parser_errors.rb +107 -0
- data/test/slim/test_pretty.rb +75 -0
- data/test/slim/test_ruby_errors.rb +187 -0
- data/test/slim/test_sections.rb +90 -0
- data/test/slim/test_slim_template.rb +118 -0
- data/test/slim/test_text_interpolation.rb +78 -0
- data/test/slim/test_wrapper.rb +39 -0
- metadata +230 -0
data/extra/test.hamlet
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
<doctype html
|
2
|
+
|
3
|
+
<html
|
4
|
+
<head
|
5
|
+
<title Slim Test
|
6
|
+
<meta name="keywords" content="slim, syntax"
|
7
|
+
|
8
|
+
<javascript:
|
9
|
+
$(function() {
|
10
|
+
alert('Hello World');
|
11
|
+
});
|
12
|
+
|
13
|
+
<haml:
|
14
|
+
#someid.someclass{:this => 'test'} Content in haml
|
15
|
+
|
16
|
+
<erb:
|
17
|
+
<%= some_method(@request) %>
|
18
|
+
|
19
|
+
<body
|
20
|
+
/ comment block
|
21
|
+
with multiple lines
|
22
|
+
This is another line
|
23
|
+
<h1 = @page_title
|
24
|
+
<p#notice.message
|
25
|
+
| Welcome to the the syntax test.
|
26
|
+
This file is to exercise the various markup.
|
27
|
+
This is another line
|
28
|
+
- unless @users.empty?
|
29
|
+
<table
|
30
|
+
- for user in users do
|
31
|
+
<tr
|
32
|
+
<td.user id=some_ruby('ere', @rme) data-test="some text #{with @ruby}" = @post.name
|
33
|
+
- else
|
34
|
+
<p There are no users.
|
35
|
+
|
36
|
+
/ Single comment line
|
37
|
+
#content Hello #{@user.name}! Welcome to the test page!
|
38
|
+
Try out Slim!
|
39
|
+
|
40
|
+
= function_with_many_parameters(:a, @variable, :option => 1)
|
41
|
+
|
42
|
+
<p.text
|
43
|
+
' Another text block
|
44
|
+
with multiple lines
|
45
|
+
|
46
|
+
= link_to('Test', @site)
|
47
|
+
|
48
|
+
.text#footer
|
49
|
+
' Footer text block
|
50
|
+
with multiple lines
|
data/hamlet.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#require File.dirname(__FILE__) + '/lib/hamlet/version'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'hamlet'
|
7
|
+
s.version = 0.1
|
8
|
+
s.date = Date.today.to_s
|
9
|
+
s.authors = ['Greg Weber']
|
10
|
+
s.email = ['greg@gregweber.info']
|
11
|
+
s.summary = 'Hamlet is a DRY HTML template language.'
|
12
|
+
s.description = 'Hamlet is a template language whose goal is reduce HTML syntax to the essential parts.'
|
13
|
+
s.homepage = 'http://github.com/gregwebs/hamlet.rb'
|
14
|
+
s.extra_rdoc_files = %w(README.md)
|
15
|
+
s.rdoc_options = %w(--charset=UTF-8)
|
16
|
+
s.rubyforge_project = s.name
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = %w(lib)
|
21
|
+
|
22
|
+
s.add_dependency('slim', ['~> 1.0'])
|
23
|
+
|
24
|
+
s.add_development_dependency('rake', ['>= 0.8.7'])
|
25
|
+
s.add_development_dependency('sass', ['>= 3.1.0'])
|
26
|
+
s.add_development_dependency('minitest', ['>= 0'])
|
27
|
+
s.add_development_dependency('kramdown', ['>= 0'])
|
28
|
+
s.add_development_dependency('yard', ['>= 0'])
|
29
|
+
s.add_development_dependency('creole', ['>= 0'])
|
30
|
+
s.add_development_dependency('builder', ['>= 0'])
|
31
|
+
s.add_development_dependency('pry', ['>= 0'])
|
32
|
+
|
33
|
+
unless defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
34
|
+
s.add_development_dependency('rcov', ['>= 0'])
|
35
|
+
end
|
36
|
+
end
|
data/lib/hamlet.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'slim/parser'
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
module Hamlet
|
5
|
+
class Parser < Slim::Parser
|
6
|
+
private
|
7
|
+
|
8
|
+
def parse_line_indicators
|
9
|
+
case @line
|
10
|
+
when /\A\//
|
11
|
+
# Found a comment block.
|
12
|
+
if @line =~ %r{\A/!( ?)(.*)\Z}
|
13
|
+
# HTML comment
|
14
|
+
block = [:multi]
|
15
|
+
@stacks.last << [:html, :comment, block]
|
16
|
+
@stacks << block
|
17
|
+
@stacks.last << [:slim, :interpolate, $2] unless $2.empty?
|
18
|
+
parse_text_block($2.empty? ? nil : @indents.last + $1.size + 2)
|
19
|
+
elsif @line =~ %r{\A/\[\s*(.*?)\s*\]\s*\Z}
|
20
|
+
# HTML conditional comment
|
21
|
+
block = [:multi]
|
22
|
+
@stacks.last << [:slim, :condcomment, $1, block]
|
23
|
+
@stacks << block
|
24
|
+
else
|
25
|
+
# Slim comment
|
26
|
+
parse_comment_block
|
27
|
+
end
|
28
|
+
when /\A-/
|
29
|
+
# Found a code block.
|
30
|
+
# We expect the line to be broken or the next line to be indented.
|
31
|
+
block = [:multi]
|
32
|
+
@line.slice!(0)
|
33
|
+
@stacks.last << [:slim, :control, parse_broken_line, block]
|
34
|
+
@stacks << block
|
35
|
+
when /\A=/
|
36
|
+
# Found an output block.
|
37
|
+
# We expect the line to be broken or the next line to be indented.
|
38
|
+
@line =~ /\A=(=?)('?)/
|
39
|
+
@line = $'
|
40
|
+
block = [:multi]
|
41
|
+
@stacks.last << [:slim, :output, $1.empty?, parse_broken_line, block]
|
42
|
+
@stacks.last << [:static, ' '] unless $2.empty?
|
43
|
+
@stacks << block
|
44
|
+
when /\A<(\w+):\s*\Z/
|
45
|
+
# Embedded template detected. It is treated as block.
|
46
|
+
block = [:multi]
|
47
|
+
@stacks.last << [:newline] << [:slim, :embedded, $1, block]
|
48
|
+
@stacks << block
|
49
|
+
parse_text_block
|
50
|
+
return # Don't append newline, this has already been done before
|
51
|
+
when /\Adoctype\s+/i
|
52
|
+
# Found doctype declaration
|
53
|
+
@stacks.last << [:html, :doctype, $'.strip]
|
54
|
+
when /\A<([#\.]|\w[:\w-]*)/
|
55
|
+
# Found a HTML tag.
|
56
|
+
parse_tag($1)
|
57
|
+
when /\A(> *)?(.*)?\Z/
|
58
|
+
# Found a text block.
|
59
|
+
trailing_ws = !$1
|
60
|
+
@stacks.last << [:slim, :interpolate, $2] unless $2.empty?
|
61
|
+
parse_text_block($2.empty? ? nil : @indents.last + $1.to_s.size)
|
62
|
+
@stacks.last << [:static, ' '] if trailing_ws
|
63
|
+
else
|
64
|
+
syntax_error! 'Unknown line indicator'
|
65
|
+
end
|
66
|
+
@stacks.last << [:newline]
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_comment_block
|
70
|
+
while !@lines.empty? && (@lines.first =~ /\A\s*\Z/ || get_indent(@lines.first) > @indents.last)
|
71
|
+
next_line
|
72
|
+
@stacks.last << [:newline]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_text_block(text_indent = nil)
|
77
|
+
empty_lines = 0
|
78
|
+
until @lines.empty?
|
79
|
+
if @lines.first =~ /\A\s*\Z/
|
80
|
+
next_line
|
81
|
+
@stacks.last << [:newline]
|
82
|
+
empty_lines += 1 if text_indent
|
83
|
+
else
|
84
|
+
indent = get_indent(@lines.first)
|
85
|
+
break if indent <= @indents.last
|
86
|
+
|
87
|
+
if empty_lines > 0
|
88
|
+
@stacks.last << [:slim, :interpolate, "\n" * empty_lines]
|
89
|
+
empty_lines = 0
|
90
|
+
end
|
91
|
+
|
92
|
+
next_line
|
93
|
+
|
94
|
+
# The text block lines must be at least indented
|
95
|
+
# as deep as the first line.
|
96
|
+
if text_indent && indent < text_indent
|
97
|
+
@line.lstrip!
|
98
|
+
syntax_error!('Unexpected text indentation')
|
99
|
+
end
|
100
|
+
|
101
|
+
@line.slice!(0, text_indent || indent)
|
102
|
+
@stacks.last << [:slim, :interpolate, (text_indent ? "\n" : '') + @line] << [:newline]
|
103
|
+
|
104
|
+
# The indentation of first line of the text block
|
105
|
+
# determines the text base indentation.
|
106
|
+
text_indent ||= indent
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse_tag(tag)
|
112
|
+
@line.slice!(0,1) # get rid of leading '<'
|
113
|
+
if tag == '#' || tag == '.'
|
114
|
+
tag = options[:default_tag]
|
115
|
+
else
|
116
|
+
@line.slice!(0, tag.size)
|
117
|
+
end
|
118
|
+
|
119
|
+
tag = [:html, :tag, tag, parse_attributes]
|
120
|
+
@stacks.last << tag
|
121
|
+
|
122
|
+
case @line
|
123
|
+
when /\A\s*>?=(=?)('?)/
|
124
|
+
# Handle output code
|
125
|
+
block = [:multi]
|
126
|
+
@line = $'
|
127
|
+
content = [:slim, :output, $1 != '=', parse_broken_line, block]
|
128
|
+
tag << content
|
129
|
+
@stacks.last << [:static, ' '] unless $2.empty?
|
130
|
+
@stacks << block
|
131
|
+
when /\A\s*\//
|
132
|
+
# Closed tag. Do nothing
|
133
|
+
when /\A\s*>?\s*\Z/
|
134
|
+
# Empty content
|
135
|
+
content = [:multi]
|
136
|
+
tag << content
|
137
|
+
@stacks << content
|
138
|
+
when /\A( ?)>?(.*)\Z/
|
139
|
+
# Text content
|
140
|
+
content = [:multi, [:slim, :interpolate, $2]]
|
141
|
+
tag << content
|
142
|
+
@stacks << content
|
143
|
+
parse_text_block(@orig_line.size - @line.size + $1.size)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Hamlet
|
2
|
+
# Tilt template implementation for Hamlet
|
3
|
+
# @api public
|
4
|
+
Template = Temple::Templates::Tilt(Hamlet::Engine, :register_as => :hamlet)
|
5
|
+
|
6
|
+
if Object.const_defined?(:Rails)
|
7
|
+
# Rails template implementation for Hamlet
|
8
|
+
# @api public
|
9
|
+
RailsTemplate = Temple::Templates::Rails(Hamlet::Engine,
|
10
|
+
:register_as => :hamlet,
|
11
|
+
# Use rails-specific generator. This is necessary
|
12
|
+
# to support block capturing and streaming.
|
13
|
+
:generator => Temple::Generators::RailsOutputBuffer,
|
14
|
+
# Disable the internal hamlet capturing.
|
15
|
+
# Rails takes care of the capturing by itself.
|
16
|
+
:disable_capture => true,
|
17
|
+
:streaming => Object.const_defined?(:Fiber))
|
18
|
+
end
|
19
|
+
end
|
data/test/rails/Rakefile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
2
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
3
|
+
|
4
|
+
require File.expand_path('../config/application', __FILE__)
|
5
|
+
require 'rake'
|
6
|
+
|
7
|
+
Dummy::Application.load_tasks
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class ParentsController < ApplicationController
|
2
|
+
# GET /parents
|
3
|
+
# GET /parents.xml
|
4
|
+
def index
|
5
|
+
@parents = Parent.all
|
6
|
+
|
7
|
+
respond_to do |format|
|
8
|
+
format.html # index.html.erb
|
9
|
+
format.xml { render :xml => @parents }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# GET /parents/1
|
14
|
+
# GET /parents/1.xml
|
15
|
+
def show
|
16
|
+
@parent = Parent.find(params[:id])
|
17
|
+
|
18
|
+
respond_to do |format|
|
19
|
+
format.html # show.html.erb
|
20
|
+
format.xml { render :xml => @parent }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# GET /parents/new
|
25
|
+
# GET /parents/new.xml
|
26
|
+
def new
|
27
|
+
@parent = Parent.new
|
28
|
+
@parent.children.build
|
29
|
+
|
30
|
+
respond_to do |format|
|
31
|
+
format.html # new.html.erb
|
32
|
+
format.xml { render :xml => @parent }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# GET /parents/1/edit
|
37
|
+
def edit
|
38
|
+
@parent = Parent.find(params[:id])
|
39
|
+
end
|
40
|
+
|
41
|
+
# POST /parents
|
42
|
+
# POST /parents.xml
|
43
|
+
def create
|
44
|
+
@parent = Parent.new(params[:parent])
|
45
|
+
|
46
|
+
respond_to do |format|
|
47
|
+
if @parent.save
|
48
|
+
format.html { redirect_to(@parent, :notice => 'Parent was successfully created.') }
|
49
|
+
format.xml { render :xml => @parent, :status => :created, :location => @parent }
|
50
|
+
else
|
51
|
+
format.html { render :action => "new" }
|
52
|
+
format.xml { render :xml => @parent.errors, :status => :unprocessable_entity }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# PUT /parents/1
|
58
|
+
# PUT /parents/1.xml
|
59
|
+
def update
|
60
|
+
@parent = Parent.find(params[:id])
|
61
|
+
|
62
|
+
respond_to do |format|
|
63
|
+
if @parent.update_attributes(params[:parent])
|
64
|
+
format.html { redirect_to(@parent, :notice => 'Parent was successfully updated.') }
|
65
|
+
format.xml { head :ok }
|
66
|
+
else
|
67
|
+
format.html { render :action => "edit" }
|
68
|
+
format.xml { render :xml => @parent.errors, :status => :unprocessable_entity }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# DELETE /parents/1
|
74
|
+
# DELETE /parents/1.xml
|
75
|
+
def destroy
|
76
|
+
@parent = Parent.find(params[:id])
|
77
|
+
@parent.destroy
|
78
|
+
|
79
|
+
respond_to do |format|
|
80
|
+
format.html { redirect_to(parents_url) }
|
81
|
+
format.xml { head :ok }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class SlimController < ApplicationController
|
2
|
+
def normal
|
3
|
+
end
|
4
|
+
|
5
|
+
def no_layout
|
6
|
+
render :layout => false
|
7
|
+
end
|
8
|
+
|
9
|
+
def variables
|
10
|
+
@hello = "Hello Slim with variables!"
|
11
|
+
end
|
12
|
+
|
13
|
+
def partial
|
14
|
+
end
|
15
|
+
|
16
|
+
def streaming
|
17
|
+
@hello = "Hello Streaming!"
|
18
|
+
render :content_for, :stream => true
|
19
|
+
end
|
20
|
+
|
21
|
+
def integers
|
22
|
+
@integer = 1337
|
23
|
+
end
|
24
|
+
|
25
|
+
def nil
|
26
|
+
@nil = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def content_for
|
30
|
+
@hello = "Hello Slim!"
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
= render "form"
|
@@ -0,0 +1 @@
|
|
1
|
+
= render "form"
|