fu 0.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 +4 -0
- data/Gemfile +4 -0
- data/README.md +53 -0
- data/Rakefile +1 -0
- data/fu.gemspec +29 -0
- data/lib/fu.rb +12 -0
- data/lib/fu/error.rb +20 -0
- data/lib/fu/mustache.rb +62 -0
- data/lib/fu/parser.rb +90 -0
- data/lib/fu/tilt.rb +31 -0
- data/lib/fu/version.rb +3 -0
- data/spec/fixtures/views/list.fu +3 -0
- data/spec/fu_spec.rb +96 -0
- data/spec/fu_tilt_spec.rb +24 -0
- data/spec/spec_helper.rb +2 -0
- metadata +120 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+

|
2
|
+
|
3
|
+
Fu
|
4
|
+
==
|
5
|
+
|
6
|
+
Fu combines the logic–less portability of Mustache with the terse utility of Haml. This is what it looks like:
|
7
|
+
|
8
|
+
%ul
|
9
|
+
{{#children}}
|
10
|
+
%li {{name}}
|
11
|
+
|
12
|
+
Then in the (Sinatra) app:
|
13
|
+
|
14
|
+
get "/list" do
|
15
|
+
fu :list, :locals => {:children => [{:name => "Arne"}, {:name => "Bjarne"}]}
|
16
|
+
end
|
17
|
+
|
18
|
+
And you get:
|
19
|
+
|
20
|
+
<ul><li>Arne</li><li>Bjarne</li></ul>
|
21
|
+
|
22
|
+
A contrived example using all aspects of the syntax:
|
23
|
+
|
24
|
+
%h1 Hello, {{user_name}}
|
25
|
+
%p.text
|
26
|
+
This is a paragraph of
|
27
|
+
text.
|
28
|
+
%ul.friend_list(data-attribute1="some data", data-attribute2="{{some_mustache_data}}")
|
29
|
+
{{#friends}}
|
30
|
+
%li
|
31
|
+
{{>friend_partial}}
|
32
|
+
{{^friends}}
|
33
|
+
%p.error
|
34
|
+
You, unfortunately, have no friends.
|
35
|
+
|
36
|
+
Usage
|
37
|
+
=====
|
38
|
+
|
39
|
+
Direct:
|
40
|
+
|
41
|
+
Fu.to_mustache("%p Hello {{mustache}}")
|
42
|
+
|
43
|
+
With Sinatra and Tilt:
|
44
|
+
|
45
|
+
require 'fu/tilt'
|
46
|
+
|
47
|
+
Stick your fu-templates in your views-folder with the extension `.fu`.
|
48
|
+
|
49
|
+
Then, in your app:
|
50
|
+
|
51
|
+
get "/some_action" do
|
52
|
+
fu :some_template, :locals => {...}
|
53
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/fu.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fu/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "fu"
|
7
|
+
s.version = Fu::VERSION
|
8
|
+
s.authors = ["Simen Svale Skogsrud"]
|
9
|
+
s.email = ["simen@bengler.no"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Fu template engine}
|
12
|
+
s.description = %q{Fu combines the logic–less portability of Mustache with the terse utility of Haml.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "fu"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
s.add_development_dependency "sinatra"
|
24
|
+
s.add_development_dependency "sinatra"
|
25
|
+
s.add_development_dependency "rack-test"
|
26
|
+
s.add_development_dependency "mustache"
|
27
|
+
|
28
|
+
# s.add_runtime_dependency "rest-client"
|
29
|
+
end
|
data/lib/fu.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# Fu combines the logic–less portability of Mustache with the terse utility of Haml.
|
2
|
+
|
3
|
+
require "fu/version"
|
4
|
+
require "fu/error"
|
5
|
+
require "fu/parser"
|
6
|
+
require "fu/mustache"
|
7
|
+
|
8
|
+
module Fu
|
9
|
+
def self.to_mustache(fu)
|
10
|
+
Fu::Mustache.new(Fu::Parser.new(fu).root).mustache
|
11
|
+
end
|
12
|
+
end
|
data/lib/fu/error.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Fu
|
2
|
+
# An exception raised by Fu code.
|
3
|
+
class Error < StandardError
|
4
|
+
# The line of the template on which the error occurred.
|
5
|
+
#
|
6
|
+
# @return [Fixnum]
|
7
|
+
attr_reader :position
|
8
|
+
|
9
|
+
# @param message [String] The error message
|
10
|
+
# @param line [Fixnum] See \{#line}
|
11
|
+
def initialize(message = nil, position = nil)
|
12
|
+
super(message)
|
13
|
+
@position = position
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# SyntaxError is the type of exception raised when Fu encounters an
|
18
|
+
# ill-formatted document.
|
19
|
+
class SyntaxError < Fu::Error; end
|
20
|
+
end
|
data/lib/fu/mustache.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Renders Mustache templates from a Fu parse-tree
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Fu
|
6
|
+
class Mustache
|
7
|
+
attr_reader :mustache
|
8
|
+
SELF_CLOSING_TAGS = %w(meta img link br hr input area param col base)
|
9
|
+
BLOCK_ACTIONS = %w(# ^) # <- Mustache actions that take a block
|
10
|
+
NO_SPACE_CHARS = /[{}<>]/ # <- Characters that do not need to be separated by a space
|
11
|
+
# when joining elements (e.g. "<p>!</p>", not "<p> ! </p>")
|
12
|
+
|
13
|
+
def initialize(root)
|
14
|
+
@mustache = flatten(render_children(root))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Flatten the tag_tree inserting spaces only where they have to be.
|
20
|
+
def flatten(tag_tree)
|
21
|
+
tag_tree.flatten.inject("") do |result, element|
|
22
|
+
if result[-1] =~ NO_SPACE_CHARS || element[0] =~ NO_SPACE_CHARS
|
23
|
+
"#{result}#{element}"
|
24
|
+
else
|
25
|
+
"#{result} #{element}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns a tag-tree of nested arrays reflecting the structure of the
|
31
|
+
# document. E.g. ["<p>",["<em>", "Italicized text", "</em>"],"</p>"]
|
32
|
+
def render_children(node)
|
33
|
+
node.children.map { |child| self.send("render_#{child.type}", child) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def render_text(node)
|
37
|
+
[CGI.escapeHTML(node.text), render_children(node)].compact
|
38
|
+
end
|
39
|
+
|
40
|
+
def render_mustache(node)
|
41
|
+
/^\s*(?<action>[#>^]?)\s*(?<identifier>.*)\s*$/ =~ node.statement
|
42
|
+
if BLOCK_ACTIONS.include?(action)
|
43
|
+
["{{#{action}#{identifier}}}", render_children(node), "{{/#{identifier}}}"]
|
44
|
+
else
|
45
|
+
["{{#{node.statement}}}", render_children(node)]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def render_element(node)
|
50
|
+
tag = (node.tag || 'div').downcase
|
51
|
+
attributes = (node.attributes||{}).dup
|
52
|
+
attributes[:class] = [attributes[:class], node.css_classes].flatten.compact.join(' ')
|
53
|
+
attributes[:id] = node.dom_id
|
54
|
+
attribute_string = attributes.select{|k,v| v && !v.empty?}.map{|k,v| "#{k}=\"#{v}\""}.map{|s| " #{s}"}.join
|
55
|
+
if SELF_CLOSING_TAGS.include?(tag) && node.children.empty?
|
56
|
+
["<#{tag}#{attribute_string}/>"]
|
57
|
+
else
|
58
|
+
["<#{tag}#{attribute_string}>", render_children(node), "</#{tag}>"]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/fu/parser.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Parses Fu templates
|
2
|
+
|
3
|
+
require 'strscan'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module Fu
|
7
|
+
class Parser
|
8
|
+
attr_reader :root
|
9
|
+
|
10
|
+
def initialize(text)
|
11
|
+
@root = OpenStruct.new(:type => :root, :children => [])
|
12
|
+
scanner = StringScanner.new(text.gsub(/\t/, ' '))
|
13
|
+
parse_children(@root, scanner)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def parse_children(parent, scanner, parent_indent = -1)
|
19
|
+
indent = (scanner.check(/\ +/) || '').size
|
20
|
+
while indent > parent_indent && !scanner.eos? do
|
21
|
+
node = parse_line(parent, scanner)
|
22
|
+
parse_children(node, scanner, indent)
|
23
|
+
indent = (scanner.check(/\ +/) || '').size
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_line(parent, scanner)
|
28
|
+
scanner.scan(/[^\S\n]*/) # Consume any leading spaces
|
29
|
+
node = OpenStruct.new(:parent => parent, :children => [])
|
30
|
+
parent.children << node
|
31
|
+
# If present, the line must open with tag or script
|
32
|
+
if element_statement = scanner.scan(/\%[a-zA-Z0-9\-_]+/) # e.g. '%video'
|
33
|
+
node.type = :element
|
34
|
+
node.tag = element_statement[1..-1]
|
35
|
+
elsif mustache_statement = scanner.scan(/\{\{[^\S\n]*[#\^][^\S\n]*[a-zA-Z0-9_]+[^\S\n]*\}\}/) # e.g. = {{#comments}}
|
36
|
+
node.type = :mustache
|
37
|
+
node.statement = mustache_statement.scan(/[#\^]\s*[a-zA-Z0-9_]+/).flatten.first
|
38
|
+
end
|
39
|
+
|
40
|
+
# Classes and id's may be added, e.g. #my_special_header_id.alert.featured
|
41
|
+
while scan = scanner.scan(/[\.\#][a-zA-Z0-9\-_]+/) do
|
42
|
+
unless node.type.nil? || node.type == :element
|
43
|
+
raise SyntaxError.new("Can only attach id's or classes to elements", scanner.pos)
|
44
|
+
end
|
45
|
+
node.type = :element
|
46
|
+
node.tag ||= 'div'
|
47
|
+
case scan[0]
|
48
|
+
when '.' then (node.css_classes ||= []) << scan[1..-1]
|
49
|
+
when '#' then node.dom_id = scan[1..-1]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Attributes-section, e.g. (hidden=true, data-bananas="one, two, five")
|
54
|
+
if node.type == :element && scan = scanner.scan(/[^\S\n]*\(/) # Match opening '('
|
55
|
+
node.attributes ||= {}
|
56
|
+
begin
|
57
|
+
scanner.scan(/\s*/) # Ditch whitespace
|
58
|
+
key = scanner.scan(/[a-zA-Z0-9\-_]+/)
|
59
|
+
value = nil
|
60
|
+
raise SyntaxError.new("Expected '='", scanner.pos) unless scanner.scan(/\s*\=\s*/)
|
61
|
+
if quote = scanner.scan(/['"]/)
|
62
|
+
value = scanner.scan(/[^\n]*?(?<!\\)#{'\\'+quote}/).chomp(quote) # Consume anything until a matching, unescaped quote
|
63
|
+
else
|
64
|
+
value = scanner.scan(/[a-zA-Z0-9\-_]+/) # Simple values need not be quoted
|
65
|
+
end
|
66
|
+
node.attributes[key] = value
|
67
|
+
raise SyntaxError.new("Expected attribute value", scanner.pos) unless value
|
68
|
+
scanner.scan(/\s*,/) # Discard whitespace and optional commas
|
69
|
+
end until scanner.scan(/\s*\)/) # Match ending ')'
|
70
|
+
end
|
71
|
+
|
72
|
+
# Any plaintext?
|
73
|
+
scan = scanner.scan(/[^\n]+/)
|
74
|
+
unless scan.nil? || scan.strip.empty?
|
75
|
+
if node.type # Is this a trailing child like e.g.: %h1.title This is a trailing child
|
76
|
+
node.children << OpenStruct.new(
|
77
|
+
:parent => node, :children => [],
|
78
|
+
:type => :text, :text => scan.strip
|
79
|
+
)
|
80
|
+
else # This very node is teh text!
|
81
|
+
node.type = :text
|
82
|
+
node.text = scan.strip
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
scanner.scan(/\n/) # Consume end of line
|
87
|
+
node
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/fu/tilt.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Makes Fu available through Tilt, also contains a utility
|
2
|
+
# function that will be added to Sinatra if Sinatra is
|
3
|
+
# defined.
|
4
|
+
|
5
|
+
require 'tilt'
|
6
|
+
require 'mustache'
|
7
|
+
|
8
|
+
module Tilt
|
9
|
+
class FuTemplate < Template
|
10
|
+
self.default_mime_type = "text/html"
|
11
|
+
def initialize_engine
|
12
|
+
return if defined? ::Fu
|
13
|
+
require_template_library 'fu'
|
14
|
+
end
|
15
|
+
|
16
|
+
def prepare; end
|
17
|
+
|
18
|
+
def evaluate(scope, locals, &block)
|
19
|
+
Mustache.render(Fu.to_mustache(data), locals.merge(scope.is_a?(Hash) ? scope : {}).merge({:yield => block.nil? ? '' : block.call}))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
register FuTemplate, 'fu'
|
23
|
+
end
|
24
|
+
|
25
|
+
if defined?(Sinatra)
|
26
|
+
module Sinatra::Templates
|
27
|
+
def fu(template, options={}, locals={})
|
28
|
+
render :fu, template, options, locals
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/fu/version.rb
ADDED
data/spec/fu_spec.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe Fu::Mustache do
|
3
|
+
it "can build a div from a single class" do
|
4
|
+
Fu.to_mustache(".klass").should eq '<div class="klass"></div>'
|
5
|
+
end
|
6
|
+
|
7
|
+
it "can add multiple classes by appending" do
|
8
|
+
Fu.to_mustache(".klass.odd").should eq '<div class="klass odd"></div>'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "allows tag type to be specified on the line" do
|
12
|
+
Fu.to_mustache("%video").should eq "<video></video>"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "knows that some tags are self closing" do
|
16
|
+
Fu.to_mustache("%br").should eq "<br/>"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can specify a tag name, classes and a dom-id just by piling on statements" do
|
20
|
+
result = Fu.to_mustache("%tag.klass1.klass2#identifier")
|
21
|
+
result.should =~ /^\<tag\ /
|
22
|
+
result.should =~ /class\=\"klass1 klass2\"/
|
23
|
+
result.should =~ /id\=\"identifier\"/
|
24
|
+
end
|
25
|
+
|
26
|
+
it "handles inline text children" do
|
27
|
+
Fu.to_mustache("%h1 This is a title").should eq "<h1>This is a title</h1>"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "allows the designer to provide arbitrary attributes" do
|
31
|
+
result = Fu.to_mustache <<-END
|
32
|
+
%tag (a=1, b=2, data-c =
|
33
|
+
"Dette er en stor verdi")
|
34
|
+
END
|
35
|
+
result.should eq '<tag a="1" b="2" data-c="Dette er en stor verdi"></tag>'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "inserts children as sub nodes and concatenates sibling nodes" do
|
39
|
+
result = Fu.to_mustache <<-END
|
40
|
+
%section
|
41
|
+
%p
|
42
|
+
This is
|
43
|
+
some text
|
44
|
+
for this paragraph.
|
45
|
+
This is added too
|
46
|
+
But this is outside
|
47
|
+
END
|
48
|
+
result.should eq "<section><p>This is some text for this paragraph. This is added too</p>But this is outside</section>"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "handles complex hierarchies with attributes and cdata and multiple siblings at the root level" do
|
52
|
+
result = Fu.to_mustache <<-END
|
53
|
+
%section
|
54
|
+
%h1.header This is a header
|
55
|
+
%p(data-bananas="healthy but radioactive")
|
56
|
+
This is body
|
57
|
+
%details
|
58
|
+
%ul.big_list
|
59
|
+
%li Item 1
|
60
|
+
%li Item 2
|
61
|
+
%li Item 3
|
62
|
+
%details.secondary
|
63
|
+
%p
|
64
|
+
Some details
|
65
|
+
%section.number2
|
66
|
+
Other stuff
|
67
|
+
END
|
68
|
+
result.should eq '<section><h1 class="header">This is a header</h1><p data-bananas="healthy but radioactive">This is body<details><ul class="big_list"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul></details><details class="secondary"><p>Some details</p></details></p></section><section class="number2">Other stuff</section>'
|
69
|
+
end
|
70
|
+
|
71
|
+
it "handles mustache sections" do
|
72
|
+
result = Fu.to_mustache <<-END
|
73
|
+
{{#children}}
|
74
|
+
{{name}} and {{address}}
|
75
|
+
END
|
76
|
+
result.should eq "{{#children}}{{name}} and {{address}}{{/children}}"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "handles mustache inverted sections" do
|
80
|
+
result = Fu.to_mustache <<-END
|
81
|
+
{{^children}}
|
82
|
+
{{name}} and {{address}}
|
83
|
+
END
|
84
|
+
result.should eq "{{^children}}{{name}} and {{address}}{{/children}}"
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
it "handles mustache in attributes" do
|
89
|
+
Fu.to_mustache('%p(data-bingo="{{bingo}}")').should eq '<p data-bingo="{{bingo}}"></p>'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "handles escaped quote characters in attribute values" do
|
93
|
+
Fu.to_mustache('%p(data-quoted="\\"")').should eq '<p data-quoted="\""></p>'
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sinatra'
|
3
|
+
require 'rack/test'
|
4
|
+
require 'fu/tilt'
|
5
|
+
|
6
|
+
class FuApp < Sinatra::Base
|
7
|
+
set :root, File.dirname(__FILE__)+"/fixtures"
|
8
|
+
get "/list" do
|
9
|
+
fu :list, :locals => {:children => [{:name => "Arne"}, {:name => "Bjarne"}]}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "API v1 posts" do
|
14
|
+
include Rack::Test::Methods
|
15
|
+
|
16
|
+
def app
|
17
|
+
FuApp
|
18
|
+
end
|
19
|
+
|
20
|
+
it "'s alive" do
|
21
|
+
get "/list"
|
22
|
+
last_response.body.should eq "<ul><li>Arne</li><li>Bjarne</li></ul>"
|
23
|
+
end
|
24
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Simen Svale Skogsrud
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-12 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70166767622680 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70166767622680
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: sinatra
|
27
|
+
requirement: &70166767645740 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70166767645740
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: sinatra
|
38
|
+
requirement: &70166767651400 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70166767651400
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rack-test
|
49
|
+
requirement: &70166768253840 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70166768253840
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: mustache
|
60
|
+
requirement: &70166771869320 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70166771869320
|
69
|
+
description: Fu combines the logic–less portability of Mustache with the terse utility
|
70
|
+
of Haml.
|
71
|
+
email:
|
72
|
+
- simen@bengler.no
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- fu.gemspec
|
82
|
+
- lib/fu.rb
|
83
|
+
- lib/fu/error.rb
|
84
|
+
- lib/fu/mustache.rb
|
85
|
+
- lib/fu/parser.rb
|
86
|
+
- lib/fu/tilt.rb
|
87
|
+
- lib/fu/version.rb
|
88
|
+
- spec/fixtures/views/list.fu
|
89
|
+
- spec/fu_spec.rb
|
90
|
+
- spec/fu_tilt_spec.rb
|
91
|
+
- spec/spec_helper.rb
|
92
|
+
homepage: ''
|
93
|
+
licenses: []
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubyforge_project: fu
|
112
|
+
rubygems_version: 1.8.10
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: Fu template engine
|
116
|
+
test_files:
|
117
|
+
- spec/fixtures/views/list.fu
|
118
|
+
- spec/fu_spec.rb
|
119
|
+
- spec/fu_tilt_spec.rb
|
120
|
+
- spec/spec_helper.rb
|