fu 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Puts the Fu in Mustache](http://2.bp.blogspot.com/-_i2s2gzRwgw/TZCLNfnXg4I/AAAAAAAAAEg/_fIOfF6cUxw/s1600/the-face-of-fu-manchu-original.jpg)
|
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
|