fumbler 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 +98 -0
- data/Rakefile +2 -0
- data/fumbler.gemspec +25 -0
- data/lib/fumbler.rb +9 -0
- data/lib/fumbler/engine.rb +16 -0
- data/lib/fumbler/filter.rb +45 -0
- data/lib/fumbler/model.rb +41 -0
- data/lib/fumbler/parser.rb +86 -0
- data/lib/fumbler/template.rb +8 -0
- data/lib/fumbler/version.rb +3 -0
- data/spec/fumbler_model_spec.rb +44 -0
- data/spec/fumbler_parser_spec.rb +51 -0
- data/spec/fumbler_templater_spec.rb +69 -0
- metadata +105 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
Fumbler
|
2
|
+
=======
|
3
|
+
|
4
|
+
This is the start of a ruby template engine based on the Tumblr syntax.
|
5
|
+
|
6
|
+
It's not production ready, and this is my first gem so please try it out and let me know if I'm doing anything wrong.
|
7
|
+
|
8
|
+
Some better examples are coming, checkout the specs for basic usage.
|
9
|
+
|
10
|
+
|
11
|
+
Overview
|
12
|
+
--------
|
13
|
+
|
14
|
+
The aim is to create a safe-evaluating template engine based on the designer friendly Tumblr syntax. It will be a lot more restrictive than ERB and even Liquid, but slightly more evaluating than Mustache.
|
15
|
+
|
16
|
+
It will enable you to give the designer limited control flow, safely evaluating contexts, nest-able blocks, and a few extensions to make things easier.
|
17
|
+
|
18
|
+
|
19
|
+
Whats Implemented
|
20
|
+
-----------------
|
21
|
+
|
22
|
+
|
23
|
+
###Model
|
24
|
+
|
25
|
+
Inherit from the Model class to create your context which the template can access, it includes a little attr_reader style helper called 'fumbleable' to make stuff available to the template so you can apply it to existing classes and not worry about giving access to db saves etc.
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
class Cage < Fumbler::Model
|
30
|
+
fumbleable :inside,:current
|
31
|
+
|
32
|
+
def initialize(c)
|
33
|
+
@current = c
|
34
|
+
end
|
35
|
+
|
36
|
+
def inside
|
37
|
+
"im trapped"
|
38
|
+
end
|
39
|
+
|
40
|
+
def current
|
41
|
+
@current
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Context < Fumbler::Model
|
46
|
+
|
47
|
+
fumbleable :you, :cage, :cages
|
48
|
+
|
49
|
+
def you
|
50
|
+
"dave"
|
51
|
+
end
|
52
|
+
|
53
|
+
def hidden
|
54
|
+
"you cant access me"
|
55
|
+
end
|
56
|
+
|
57
|
+
def cage
|
58
|
+
Cage.new(0)
|
59
|
+
end
|
60
|
+
|
61
|
+
def cages
|
62
|
+
[Cage.new(1),Cage.new(2)]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
@context = Context.new
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
###Template
|
70
|
+
|
71
|
+
This is based on [Tilt](https://github.com/rtomayko/tilt) but not everything is implemented yet.
|
72
|
+
|
73
|
+
The current syntax supports basic {tags} and {block:items}{tagsinparent}or{tagsinhere}{/block:items}, blocks can be nested and will always look to the parent for missing tags.
|
74
|
+
|
75
|
+
t = Fumbler::Template.new {"hello {you}, {block:cage}{current}{/block:cage} {block:cages}{you}{current},{/block:cages}]"}
|
76
|
+
t.render(@context)
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
TODO
|
81
|
+
----
|
82
|
+
|
83
|
+
* Attributes
|
84
|
+
* Meta-settings
|
85
|
+
* Partials
|
86
|
+
* Layout Inheritance
|
87
|
+
* File handling
|
88
|
+
* Rails Integration via Tilt
|
89
|
+
* Re-write the regex as a token scanner
|
90
|
+
* Lots More!
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
Acknowledgements
|
95
|
+
----------------
|
96
|
+
|
97
|
+
This is based on the [Temple](https://github.com/judofyr/temple) framework, taking a few tips from [Mustache](https://github.com/defunkt/mustache). The syntax aims to resemble that of [Tumblr](http://www.tumblr.com) and (Posterous)[http://www.posterous.com].
|
98
|
+
|
data/Rakefile
ADDED
data/fumbler.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fumbler/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "fumbler"
|
7
|
+
s.version = Fumbler::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Anthony Corcutt"]
|
10
|
+
s.email = ["acorcutt@me.com"]
|
11
|
+
s.homepage = "https://github.com/acorcutt/fumbler"
|
12
|
+
s.summary = %q{Tumblr style template engine for ruby}
|
13
|
+
|
14
|
+
s.rubyforge_project = "fumbler"
|
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
|
+
s.add_dependency("temple")
|
22
|
+
s.add_dependency("tilt")
|
23
|
+
|
24
|
+
s.add_development_dependency("rspec", ["~> 2.4"])
|
25
|
+
end
|
data/lib/fumbler.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'temple'
|
2
|
+
require 'fumbler'
|
3
|
+
|
4
|
+
module Fumbler
|
5
|
+
class Engine < Temple::Engine
|
6
|
+
use Fumbler::Parser
|
7
|
+
use Fumbler::Filter
|
8
|
+
|
9
|
+
#filter :EscapeHTML, :use_html_safe
|
10
|
+
filter :MultiFlattener
|
11
|
+
filter :StaticMerger
|
12
|
+
filter :DynamicInliner
|
13
|
+
|
14
|
+
generator :ArrayBuffer
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'temple'
|
2
|
+
|
3
|
+
module Fumbler
|
4
|
+
#
|
5
|
+
# We just take our array and turn it into something Temple understands
|
6
|
+
#
|
7
|
+
# We prefix any calls with fumble_ which will be defined in our context class, and any children
|
8
|
+
#
|
9
|
+
#
|
10
|
+
class Filter < Temple::Filter
|
11
|
+
def on_multi(*exps)
|
12
|
+
exps.each_with_index do |exp,i|
|
13
|
+
name = exp[1]
|
14
|
+
case exp[0].to_sym
|
15
|
+
when :dynamic
|
16
|
+
exps[i] = [:dynamic,"fumble_#{name}"]
|
17
|
+
when :block
|
18
|
+
if exp[1] == "end"
|
19
|
+
#just strip any attributes and double up end tags to match block
|
20
|
+
exps[i] = [:block,"end;end"]
|
21
|
+
else
|
22
|
+
exps[i]=[:static,"block"]
|
23
|
+
|
24
|
+
# TODO - attributes, first we need to fix the parser to grab the whole attribute including quotes, also allow fumble_*params
|
25
|
+
atr = exp[2..-1]
|
26
|
+
|
27
|
+
# TODO - handle Arrays - think we just need to add it and also end;end above? or inject :multi
|
28
|
+
exps[i]= [:block,<<-code]
|
29
|
+
c = #{name}
|
30
|
+
c = [c] unless c.is_a?(Array)
|
31
|
+
c.each do |b|
|
32
|
+
b.fumble(self) do
|
33
|
+
code
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return [:multi, *exps]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create evaluating string
|
41
|
+
def ev(s)
|
42
|
+
"#\{#{s}}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Fumbler
|
2
|
+
|
3
|
+
class Model
|
4
|
+
|
5
|
+
def self.fumbleable(*accessors)
|
6
|
+
accessors.each do |m|
|
7
|
+
class_eval <<-EOS
|
8
|
+
def fumble_#{m}
|
9
|
+
self.#{m}
|
10
|
+
end
|
11
|
+
EOS
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# What I'm trying todo is make blocks work inside blocks, and still be able to access
|
16
|
+
# the parents attributes, instance_eval lets us do something similar to javascripts with(o){}
|
17
|
+
# In this example we cant access 'you' inside the cage context how we would like to
|
18
|
+
# as its not a local, but see how 'outside' works. So we need to catch the missing_method
|
19
|
+
# and push send up to the parent, which might be a bit slow but it simplifies the block filter.
|
20
|
+
#
|
21
|
+
# @context.fumble(self){
|
22
|
+
# puts you #defined in root context class
|
23
|
+
# outside = "outside"
|
24
|
+
# cage.fumble(self){
|
25
|
+
# puts outside
|
26
|
+
# puts inside #defined in cage class
|
27
|
+
# puts you #this errors unless we catch method_missing
|
28
|
+
# }
|
29
|
+
# }
|
30
|
+
def fumble(parent,&block)
|
31
|
+
@parent = parent
|
32
|
+
instance_eval &block
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(sym, *args,&block)
|
36
|
+
if @parent
|
37
|
+
@parent.send(sym, *args,&block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'temple'
|
2
|
+
|
3
|
+
module Fumbler
|
4
|
+
#
|
5
|
+
# Handles parsing of our template string
|
6
|
+
#
|
7
|
+
# The Basics:
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# Display something:
|
11
|
+
#
|
12
|
+
# {title}
|
13
|
+
#
|
14
|
+
# Blocks:
|
15
|
+
#
|
16
|
+
# {block:item limit="10"}
|
17
|
+
# {title}
|
18
|
+
# {/block:item}
|
19
|
+
#
|
20
|
+
#
|
21
|
+
# We allow a few tag formats and special html style wrapper to hide from rich html editors
|
22
|
+
# {block:items} {{block:items}} <{block:items}> <!--{block:items}--> are all equivelant,
|
23
|
+
# The surrounding tag will always be stripped from the output.
|
24
|
+
#
|
25
|
+
# Escape things with
|
26
|
+
# {{{ <{im}> {escaped} .css{color:red} }}}
|
27
|
+
#
|
28
|
+
# TODO - make tag patterns customizable so we can do ${tag} etc.
|
29
|
+
# TODO - combine the block and tag pattern, then allow tag & block delimiters to be differerent e.g. erb style
|
30
|
+
# TODO - or switch to scanning for opening and closing tags so we can do things like {block:tag a="{" b="}"} and ignore {tag}}
|
31
|
+
|
32
|
+
class Parser
|
33
|
+
include Temple::Mixins::Options
|
34
|
+
|
35
|
+
TAG_PATTERN = /{{{(.*?)}}}|({|{{|<{|<!--{)\s*?(\/?\b.+?)\s*?(}-->|}>|}}|})/m #order of the { {{ <{ <!--{ tag groups is important
|
36
|
+
|
37
|
+
BLOCK_PATTERN = /(\/?\b\w+\b)\s*?:\s*?(\b\w+\b)\s*?(.*)/m
|
38
|
+
|
39
|
+
ATTRIBUTE_PATTERN = /\s+(\b\w+\b)\s*=\s*(?:(['"])(.*?)\2)/m #a='1' b="2"
|
40
|
+
|
41
|
+
def compile(input)
|
42
|
+
result = [:multi]
|
43
|
+
return result unless input
|
44
|
+
|
45
|
+
pos = 0
|
46
|
+
blocks = 0
|
47
|
+
input.scan(TAG_PATTERN) do |escaped,s, tag, e|
|
48
|
+
m = Regexp.last_match
|
49
|
+
text = input[pos...m.begin(0)]
|
50
|
+
pos = m.end(0)
|
51
|
+
result << [:static, text] if !text.empty?
|
52
|
+
if escaped
|
53
|
+
result << [:static, escaped]
|
54
|
+
else
|
55
|
+
m = tag.match(BLOCK_PATTERN)
|
56
|
+
if m
|
57
|
+
t = m[2]
|
58
|
+
a = []
|
59
|
+
m[3].scan(ATTRIBUTE_PATTERN) do |key,q,value|
|
60
|
+
a << [key,value]
|
61
|
+
end
|
62
|
+
case m[1]
|
63
|
+
when "block"
|
64
|
+
result << [:block,t,a]
|
65
|
+
blocks+=1
|
66
|
+
when "/block"
|
67
|
+
result << [:block,"end"]
|
68
|
+
blocks-=1
|
69
|
+
end
|
70
|
+
else
|
71
|
+
result << [:dynamic,tag]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
text = input[pos..-1]
|
76
|
+
result << [:static, text] if !text.empty?
|
77
|
+
|
78
|
+
#close any blocks left over
|
79
|
+
blocks.times do
|
80
|
+
result << [:block,"end"]
|
81
|
+
end
|
82
|
+
|
83
|
+
result
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'fumbler/model'
|
2
|
+
|
3
|
+
describe Fumbler::Model do
|
4
|
+
before(:all) do
|
5
|
+
|
6
|
+
class Context < Fumbler::Model
|
7
|
+
|
8
|
+
fumbleable :you, :cage
|
9
|
+
|
10
|
+
def you
|
11
|
+
"dave"
|
12
|
+
end
|
13
|
+
|
14
|
+
class Cage < Fumbler::Model
|
15
|
+
fumbleable :inside
|
16
|
+
|
17
|
+
def inside
|
18
|
+
"im trapped"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def cage
|
23
|
+
Cage.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should find all attributes" do
|
31
|
+
@context = Context.new
|
32
|
+
|
33
|
+
@context.fumble(self){
|
34
|
+
you.inspect
|
35
|
+
outside = "outside"
|
36
|
+
cage.fumble(self){
|
37
|
+
outside.inspect
|
38
|
+
inside.inspect
|
39
|
+
you.inspect
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'fumbler/engine'
|
2
|
+
require 'fumbler/parser'
|
3
|
+
|
4
|
+
describe Fumbler::Engine do
|
5
|
+
it "should make a new temple engine" do
|
6
|
+
Fumbler::Engine.new
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Fumbler::Parser do
|
11
|
+
it "should make a new parser" do
|
12
|
+
Fumbler::Parser.new
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should parse empty string" do
|
16
|
+
Fumbler::Parser.new.compile("").should eql([:multi])
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should parse some static text" do
|
20
|
+
Fumbler::Parser.new.compile("some text").should eql([:multi,[:static,"some text"]])
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should parse escaped tags" do
|
24
|
+
Fumbler::Parser.new.compile("hello {{{ <{im}> {escaped} .css{color:red} }}}").should eql([:multi,[:static,"hello "],[:static," <{im}> {escaped} .css{color:red} "]])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should parse a text tag" do
|
28
|
+
Fumbler::Parser.new.compile("hello {name}-{ age }").should eql([:multi,[:static,"hello "],[:dynamic,"name"],[:static,"-"],[:dynamic,"age"]])
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should parse a text tag across multi-lines" do
|
32
|
+
Fumbler::Parser.new.compile("hello {name\nhere}").should eql([:multi,[:static,"hello "],[:dynamic, "name\nhere"]])
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should parse a text tag in all tag formats" do
|
36
|
+
Fumbler::Parser.new.compile("hello {a}{{b}}<{c}><!--{d}-->").should eql([:multi,[:static,"hello "],[:dynamic,"a"],[:dynamic,"b"],[:dynamic,"c"],[:dynamic,"d"]])
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should parse a block tag" do
|
40
|
+
Fumbler::Parser.new.compile("hello {block:item}{name}{/block:item}").should eql([:multi,[:static,"hello "],[:block,"item",[]],[:dynamic,"name"],[:block,"end"]])
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should parse a spaced block tag" do
|
44
|
+
Fumbler::Parser.new.compile("hello { block : item}{name}{/block : item }").should eql([:multi,[:static,"hello "],[:block,"item",[]],[:dynamic,"name"],[:block,"end"]])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should parse a block tag with attributes" do
|
48
|
+
Fumbler::Parser.new.compile("hello {block:item a=\"1\" b='2'}{name}{/block:item}").should eql([:multi,[:static,"hello "],[:block,"item",[["a","1"],["b","2"]]],[:dynamic,"name"],[:block,"end"]])
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'fumbler/template'
|
2
|
+
require 'fumbler/model'
|
3
|
+
|
4
|
+
describe Fumbler::Template do
|
5
|
+
before(:all) do
|
6
|
+
class Context < Fumbler::Model
|
7
|
+
|
8
|
+
fumbleable :you, :cage, :cages
|
9
|
+
|
10
|
+
def you
|
11
|
+
"dave"
|
12
|
+
end
|
13
|
+
|
14
|
+
def hidden
|
15
|
+
"you cant access me"
|
16
|
+
end
|
17
|
+
|
18
|
+
class Cage < Fumbler::Model
|
19
|
+
fumbleable :inside,:current
|
20
|
+
|
21
|
+
def initialize(c)
|
22
|
+
@current = c
|
23
|
+
end
|
24
|
+
|
25
|
+
def inside
|
26
|
+
"im trapped"
|
27
|
+
end
|
28
|
+
|
29
|
+
def current
|
30
|
+
@current
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def cage
|
35
|
+
Cage.new(0)
|
36
|
+
end
|
37
|
+
|
38
|
+
def cages
|
39
|
+
[Cage.new(1),Cage.new(2)]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
@context = Context.new
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should make a new temple" do
|
47
|
+
Fumbler::Template.new {}
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should render some static text" do
|
51
|
+
Fumbler::Template.new{"hello from template"}.render.should eql("hello from template")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should render a tag" do
|
55
|
+
t = Fumbler::Template.new {"hello {you}"}
|
56
|
+
t.render(@context).should eql("hello dave")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should render a block" do
|
60
|
+
t = Fumbler::Template.new {"hello {you}, {block:cage}{you} {inside}{/block:cage}"}
|
61
|
+
t.render(@context).should eql("hello dave, dave im trapped")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should render a block a few times" do
|
65
|
+
t = Fumbler::Template.new {"hello {you}, [{block:cages}{you}{current},{/block:cages}]"}
|
66
|
+
t.render(@context).should eql("hello dave, [dave1,dave2,]")
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fumbler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Anthony Corcutt
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-03-06 00:00:00 +00:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: temple
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tilt
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0"
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rspec
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "2.4"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
description:
|
50
|
+
email:
|
51
|
+
- acorcutt@me.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- .gitignore
|
60
|
+
- Gemfile
|
61
|
+
- README.md
|
62
|
+
- Rakefile
|
63
|
+
- fumbler.gemspec
|
64
|
+
- lib/fumbler.rb
|
65
|
+
- lib/fumbler/engine.rb
|
66
|
+
- lib/fumbler/filter.rb
|
67
|
+
- lib/fumbler/model.rb
|
68
|
+
- lib/fumbler/parser.rb
|
69
|
+
- lib/fumbler/template.rb
|
70
|
+
- lib/fumbler/version.rb
|
71
|
+
- spec/fumbler_model_spec.rb
|
72
|
+
- spec/fumbler_parser_spec.rb
|
73
|
+
- spec/fumbler_templater_spec.rb
|
74
|
+
has_rdoc: true
|
75
|
+
homepage: https://github.com/acorcutt/fumbler
|
76
|
+
licenses: []
|
77
|
+
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project: fumbler
|
98
|
+
rubygems_version: 1.6.1
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Tumblr style template engine for ruby
|
102
|
+
test_files:
|
103
|
+
- spec/fumbler_model_spec.rb
|
104
|
+
- spec/fumbler_parser_spec.rb
|
105
|
+
- spec/fumbler_templater_spec.rb
|