stache 0.2.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +10 -0
- data/README.md +32 -7
- data/lib/stache.rb +1 -3
- data/lib/stache/asset_helper.rb +15 -22
- data/lib/stache/config.rb +11 -9
- data/lib/stache/handlebars.rb +7 -0
- data/lib/stache/handlebars/handler.rb +80 -0
- data/lib/stache/handlebars/view.rb +14 -0
- data/lib/stache/mustache.rb +7 -0
- data/lib/stache/mustache/handler.rb +70 -0
- data/lib/stache/mustache/view.rb +37 -0
- data/lib/stache/version.rb +1 -1
- data/spec/controllers/handlebars_controller_spec.rb +35 -0
- data/spec/controllers/stache_controller_spec.rb +17 -5
- data/spec/dummy/app/assets/stylesheets/test.css +3 -0
- data/spec/dummy/app/controllers/handlebars_controller.rb +18 -0
- data/spec/dummy/app/controllers/stache_controller.rb +8 -3
- data/spec/dummy/app/helpers/application_helper.rb +4 -0
- data/spec/dummy/app/views/handlebars/_eaten_by_a.html.hbs +1 -0
- data/spec/dummy/app/views/handlebars/index.html.hbs +1 -0
- data/spec/dummy/app/views/handlebars/with_helpers.html.hbs +3 -0
- data/spec/dummy/app/views/handlebars/with_partials.html.hbs +3 -0
- data/spec/dummy/app/views/stache/with_asset_helpers.html.mustache +4 -0
- data/spec/dummy/config/application.rb +3 -0
- data/spec/dummy/config/initializers/stache.rb +2 -0
- data/spec/dummy/config/routes.rb +5 -53
- data/spec/dummy/lib/with_asset_helpers.rb +11 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/stache/asset_helper_spec.rb +41 -39
- data/spec/stache/config_spec.rb +4 -6
- data/spec/stache/handlebars/handlebars_spec.rb +37 -0
- data/spec/stache/handlebars/profile_autoload.rb +6 -0
- data/spec/stache/handlebars/view_spec.rb +11 -0
- data/spec/stache/{handler_spec.rb → mustache/handler_spec.rb} +8 -8
- data/spec/stache/{profile_autoload.rb → mustache/profile_autoload.rb} +1 -1
- data/spec/stache/mustache/view_spec.rb +11 -0
- data/stache.gemspec +3 -5
- metadata +50 -22
- data/lib/stache/handler.rb +0 -68
- data/lib/stache/view.rb +0 -38
- data/spec/stache/view_spec.rb +0 -9
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# 0.9.0
|
2
|
+
|
3
|
+
/!\ /!\ Breaking Changes.
|
4
|
+
|
5
|
+
* 1.0 release candidate
|
6
|
+
* Handlebars support
|
7
|
+
* uses Rails' own template resolution system to find partials.
|
8
|
+
|
9
|
+
There's some code duplication that should be crushed out before 1.0.
|
10
|
+
|
1
11
|
# 0.2.2
|
2
12
|
|
3
13
|
* Saner, consistent handling of template extensions: partials and full templates both use configured value at `Stache.template_extension`. Thanks @ajacksified!
|
data/README.md
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
# stache
|
2
2
|
|
3
|
-
A Rails 3.x compatible Mustache Template Handler, with support for partials and a couple extra niceties to make sharing the raw templates with client-side javascript a little easier.
|
3
|
+
A Rails 3.x compatible Mustache/Handlebars Template Handler, with support for partials and a couple extra niceties to make sharing the raw templates with client-side javascript a little easier. It's a one-stop shop for your facial-hair-inspired templates.
|
4
|
+
|
5
|
+
[![Build Status](https://secure.travis-ci.org/agoragames/stache.png)](http://travis-ci.org/agoragames/stache)
|
6
|
+
|
7
|
+
## Notice Of Breaking Changes
|
8
|
+
|
9
|
+
Stache 0.9.x adds handlebars support. In the process, the public API has changed *ever-so-slightly*. Specifically, you'll need to require the mustache or handlebars gems on your own, **and** you'll need to tell Stache which one (or both!) you want to use. Add `config.use :mustache` to your initializer.
|
4
10
|
|
5
11
|
## Usage
|
6
12
|
|
13
|
+
gem "mustache" # or "handlebars"
|
7
14
|
gem "stache"
|
8
15
|
|
9
16
|
Install the gem. If you want to override any of the configuration options (see `stache/config`), toss an initializer in `config/initializers` and:
|
@@ -12,23 +19,28 @@ Install the gem. If you want to override any of the configuration options (see `
|
|
12
19
|
Stache.configure do |c|
|
13
20
|
c.template_base_path = "..." # this is probably the one you'll want to change
|
14
21
|
# it defaults to app/templates
|
22
|
+
|
23
|
+
# N.B. YOU MUST TELL STACHE WHICH TO USE:
|
24
|
+
c.use :mustache
|
25
|
+
# and / or
|
26
|
+
c.use :handlebars
|
15
27
|
end
|
16
28
|
|
17
29
|
# or if the block style ain't yer thang, just:
|
18
30
|
Stache.template_base_path = File.join(Rails.root, "app", "şablon")
|
19
31
|
```
|
20
32
|
|
21
|
-
There is as of right now one provided helper, `template_include_tag`. Give it the name of a partial and it will write it raw to a script block.
|
33
|
+
There is as of right now one provided helper, `template_include_tag`. Give it the name of a partial and it will write it raw to a script block.
|
22
34
|
|
23
35
|
## A View Class of your Very Own
|
24
36
|
|
25
|
-
To facilitate easy integration, 'Stache comes packaged with a fully-functioning subclass of Mustache, called `Stache::View`. It will try to find a more appropriate view class to provide to the template renderer based on the template name, but if one cannot be found it will automatically give ya a `Stache::View` so you can have *something*.
|
37
|
+
To facilitate easy integration, 'Stache comes packaged with a fully-functioning subclass of Mustache, called `Stache::Mustache::View`. It will try to find a more appropriate view class to provide to the template renderer based on the template name, but if one cannot be found it will automatically give ya a `Stache::Mustache::View` so you can have *something*.
|
26
38
|
|
27
|
-
Needless to say, it's probably better if your custom View objects are subclasses of `Stache::View`. That way we can all be sure that the handler will render correctly.
|
39
|
+
Needless to say, it's probably better if your custom View objects are subclasses of `Stache::Mustache::View`. That way we can all be sure that the handler will render correctly.
|
28
40
|
|
29
41
|
An example by way of explanation:
|
30
42
|
|
31
|
-
With a template `app/templates/profiles/index`, Stache will look for a view named `Profiles::Index`, and, if not found, will just use the base `Stache::View`. Stache adds `app/views` to Rails' autoload paths, so here's a sample directory structure and some sample files:
|
43
|
+
With a template `app/templates/profiles/index`, Stache will look for a view named `Profiles::Index`, and, if not found, will just use the base `Stache::Mustache::View`. Stache adds `app/views` to Rails' autoload paths, so here's a sample directory structure and some sample files:
|
32
44
|
|
33
45
|
```
|
34
46
|
app/
|
@@ -43,7 +55,7 @@ app/
|
|
43
55
|
```ruby
|
44
56
|
# in profiles/index.rb
|
45
57
|
module Profiles
|
46
|
-
class Index < ::Stache::View
|
58
|
+
class Index < ::Stache::Mustache::View
|
47
59
|
def my_view_helper_method
|
48
60
|
"whoo"
|
49
61
|
end
|
@@ -56,6 +68,16 @@ end
|
|
56
68
|
<p>Here's a helper_method call: {{ my_view_helper_method }}</p>
|
57
69
|
```
|
58
70
|
|
71
|
+
### Handlebars?
|
72
|
+
|
73
|
+
Handlebars will have full access to your rails view helpers.
|
74
|
+
|
75
|
+
```
|
76
|
+
I'm a handlebars template. Look at me call a helper: {{{image_path my_image}}}
|
77
|
+
```
|
78
|
+
|
79
|
+
You can subclass `Stache::Handlebars::View` in the same way as mustache above, but there isn't as much point in doing so.
|
80
|
+
|
59
81
|
## Of Note
|
60
82
|
|
61
83
|
This is code that was ripped out of a research project. It probably has some rough edges.
|
@@ -73,15 +95,18 @@ This project builds on work done by the following people and projects:
|
|
73
95
|
* olivernn's [Poirot](https://github.com/olivernn/poirot)
|
74
96
|
* goodmike's [mustache_rails3](https://github.com/goodmike/mustache_rails3)
|
75
97
|
* nex3's [HAML](https://github.com/nex3/haml)
|
98
|
+
* cowboyd's [handlebars.rb](https://github.com/cowboyd/handlebars.rb)
|
76
99
|
|
77
100
|
So: thanks a ton to those guys.
|
78
101
|
|
79
102
|
## Contributors
|
80
103
|
|
81
104
|
* [afeld](https://github.com/afeld) provided 1.8.7 compatibility fixes.
|
82
|
-
* [subwindow](https://github.com/subwindow) provided some much needed love for Stache::View exception handling.
|
105
|
+
* [subwindow](https://github.com/subwindow) provided some much needed love for Stache::Mustache::View exception handling.
|
83
106
|
* [solotimes](https://github.com/solotimes) provided better support for non-standard encodings.
|
84
107
|
* [ajacksified](https://github.com/ajacksified) cleaned up template extension handling.
|
108
|
+
* [ayamomiji](https://github.com/ayamomiji) extended the `#template_include_tag` to pass through the full range of `#content_tag` options.
|
109
|
+
* [awestendorf](https://github.com/awestendorf) requested that `View#partial` not be so particular about leading underscores. Though I didn't use his code, his prompt lead me to investigate how to properly use Rails' internal template lookup code.
|
85
110
|
|
86
111
|
## Note on Patches/Pull Requests
|
87
112
|
|
data/lib/stache.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require "stache/version"
|
2
2
|
require "stache/config"
|
3
3
|
require "stache/util"
|
4
|
-
require "stache/handler"
|
5
4
|
require "stache/asset_helper"
|
6
5
|
|
7
6
|
if defined? ::Rails::Railtie and ::Rails::VERSION::MAJOR >= 3
|
@@ -10,8 +9,7 @@ end
|
|
10
9
|
|
11
10
|
module Stache
|
12
11
|
extend Config
|
13
|
-
|
12
|
+
|
14
13
|
end
|
15
14
|
|
16
|
-
ActionView::Template.register_template_handler(:mustache, Stache::Handler)
|
17
15
|
ActionView::Base.send :include, Stache::AssetHelper
|
data/lib/stache/asset_helper.rb
CHANGED
@@ -3,31 +3,24 @@ module Stache
|
|
3
3
|
# template_include_tag("widgets/basic_text_api_data")
|
4
4
|
# template_include_tag("shared/test_thing")
|
5
5
|
def template_include_tag(*sources)
|
6
|
+
options = sources.extract_options!
|
6
7
|
sources.collect do |source|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
if template_path
|
14
|
-
template = ::File.open(template_path, "rb" , :encoding => Rails.configuration.encoding)
|
15
|
-
content_tag(:script, template.read.html_safe, :type => "text/html", :id => "#{file.dasherize.underscore}_template")
|
16
|
-
else
|
17
|
-
raise ActionView::MissingTemplate.new(potential_paths(base_path, file), file, [base_path], false, { :handlers => [:mustache] })
|
8
|
+
template_finder = lambda do |partial|
|
9
|
+
if ActionPack::VERSION::MAJOR == 3 && ActionPack::VERSION::MINOR < 2
|
10
|
+
lookup_context.find(source, [], partial)
|
11
|
+
else # Rails 3.2 and higher
|
12
|
+
lookup_context.find(source, [], partial, [], { formats: [:html] })
|
13
|
+
end
|
18
14
|
end
|
15
|
+
|
16
|
+
template = template_finder.call(true) rescue template_finder.call(false)
|
17
|
+
template_id = source.split("/").last
|
18
|
+
|
19
|
+
options = options.reverse_merge(:type => "text/html", :id => "#{template_id.dasherize.underscore}_template")
|
20
|
+
content_tag(:script, template.source.html_safe, options)
|
21
|
+
|
19
22
|
end.join("\n").html_safe
|
20
23
|
end
|
21
|
-
|
22
|
-
def potential_paths(path, candidate_file_name)
|
23
|
-
[
|
24
|
-
path.join("_#{candidate_file_name}.#{Stache.template_extension}"),
|
25
|
-
path.join("#{candidate_file_name}.#{Stache.template_extension}")
|
26
|
-
]
|
27
|
-
end
|
28
|
-
|
29
|
-
def locate_template_for(path, candidate_file_name)
|
30
|
-
potential_paths(path, candidate_file_name).find { |file| File.file?(file.to_s) }
|
31
|
-
end
|
24
|
+
|
32
25
|
end
|
33
26
|
end
|
data/lib/stache/config.rb
CHANGED
@@ -7,29 +7,31 @@ module Stache
|
|
7
7
|
#
|
8
8
|
# Stache.configure do |config|
|
9
9
|
# config.template_base_path = Rails.root.join('app', 'views', 'shared')
|
10
|
+
#
|
11
|
+
# use :mustache # or :handlebars
|
10
12
|
# end
|
11
13
|
module Config
|
12
|
-
attr_accessor :template_base_path, :
|
13
|
-
|
14
|
+
attr_accessor :template_base_path, :shared_path
|
15
|
+
|
14
16
|
def configure
|
15
17
|
yield self
|
16
18
|
end
|
17
|
-
|
18
|
-
|
19
|
+
|
20
|
+
|
19
21
|
def template_base_path
|
20
22
|
@template_base_path ||= ::Rails.root.join('app', 'templates')
|
21
23
|
end
|
22
|
-
|
24
|
+
|
23
25
|
def template_base_path= path
|
24
26
|
@template_base_path = Pathname.new(path)
|
25
27
|
end
|
26
28
|
|
27
|
-
def template_extension
|
28
|
-
@template_extension ||= 'html.mustache'
|
29
|
-
end
|
30
|
-
|
31
29
|
def shared_path
|
32
30
|
@shared_path ||= ::Rails.root.join('app', 'templates', 'shared')
|
33
31
|
end
|
32
|
+
|
33
|
+
def use template_engine
|
34
|
+
require "stache/#{template_engine}"
|
35
|
+
end
|
34
36
|
end
|
35
37
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'stache/handlebars/view'
|
2
|
+
|
3
|
+
module Stache
|
4
|
+
module Handlebars
|
5
|
+
# From HAML, thanks a bunch, guys!
|
6
|
+
# In Rails 3.1+, template handlers don't inherit from anything. In <= 3.0, they do.
|
7
|
+
# To avoid messy logic figuring this out, we just inherit from whatever the ERB handler does.
|
8
|
+
class Handler < Stache::Util.av_template_class(:Handlers)::ERB.superclass
|
9
|
+
if Stache::Util.needs_compilable?
|
10
|
+
include Stache::Util.av_template_class(:Handlers)::Compilable
|
11
|
+
end
|
12
|
+
|
13
|
+
def compile(template)
|
14
|
+
handlebars_class = handlebars_class_from_template(template)
|
15
|
+
|
16
|
+
<<-RUBY_CODE
|
17
|
+
handlebars = ::#{handlebars_class}.new
|
18
|
+
|
19
|
+
handlebars.register_helper('helperMissing') do |name, *args|
|
20
|
+
meth, *params, options = args
|
21
|
+
if self.respond_to?(meth)
|
22
|
+
self.send(meth, *params)
|
23
|
+
else
|
24
|
+
raise "Could not find property '\#\{meth\}'"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
template = handlebars.compile('#{template.source.gsub(/'/, "\\\\'")}');
|
29
|
+
vars = {}
|
30
|
+
partial_renderer = @view_renderer.send(:_partial_renderer)
|
31
|
+
vars.merge!(@_assigns)
|
32
|
+
vars.merge!(partial_renderer.instance_variable_get('@locals') || {})
|
33
|
+
options = partial_renderer.instance_variable_get('@options')
|
34
|
+
vars.merge!(options[:context] || {}) if options
|
35
|
+
|
36
|
+
Rails.logger.info vars.inspect
|
37
|
+
|
38
|
+
handlebars.partial_missing do |name|
|
39
|
+
search_path = '#{template.virtual_path}'.split("/")[0..-2]
|
40
|
+
file = (search_path + [name]).join("/")
|
41
|
+
finder = lambda do |partial|
|
42
|
+
self.lookup_context.find(file, [], partial, [], {:formats => [:html]})
|
43
|
+
end
|
44
|
+
template = finder.call(false) rescue finder.call(true)
|
45
|
+
|
46
|
+
if template
|
47
|
+
t = handlebars.compile(template.source)
|
48
|
+
t.call(vars).html_safe
|
49
|
+
else
|
50
|
+
raise "Could not find template '\#\{file\}'"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
template.call(vars).html_safe
|
55
|
+
RUBY_CODE
|
56
|
+
end
|
57
|
+
|
58
|
+
# In Rails 3.1+, #call takes the place of #compile
|
59
|
+
def self.call(template)
|
60
|
+
new.compile(template)
|
61
|
+
end
|
62
|
+
|
63
|
+
# suss out a constant name for the given template
|
64
|
+
def handlebars_class_from_template(template)
|
65
|
+
const_name = ActiveSupport::Inflector.camelize(template.virtual_path.to_s)
|
66
|
+
begin
|
67
|
+
const_name.constantize
|
68
|
+
rescue NameError, LoadError => e
|
69
|
+
# Only rescue NameError/LoadError concerning our mustache_class
|
70
|
+
if e.message.match(/#{const_name}$/)
|
71
|
+
Stache::Handlebars::View
|
72
|
+
else
|
73
|
+
raise e
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'handlebars'
|
2
|
+
|
3
|
+
module Stache
|
4
|
+
module Handlebars
|
5
|
+
#
|
6
|
+
# A Convienent Base Class for the views. Subclass this for autoloading magic with your templates.
|
7
|
+
class View < ::Handlebars::Context
|
8
|
+
|
9
|
+
# crickets. So, this isn't as useful right now since you have to call #register_helper
|
10
|
+
# and #partial_missing on the instance, but I'm sure we'll find a use for it :).
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'stache/mustache/view'
|
2
|
+
|
3
|
+
module Stache
|
4
|
+
module Mustache
|
5
|
+
# From HAML, thanks a bunch, guys!
|
6
|
+
# In Rails 3.1+, template handlers don't inherit from anything. In <= 3.0, they do.
|
7
|
+
# To avoid messy logic figuring this out, we just inherit from whatever the ERB handler does.
|
8
|
+
class Handler < Stache::Util.av_template_class(:Handlers)::ERB.superclass
|
9
|
+
if Stache::Util.needs_compilable?
|
10
|
+
include Stache::Util.av_template_class(:Handlers)::Compilable
|
11
|
+
end
|
12
|
+
|
13
|
+
# Thanks to Mustache::Rails3 for getting us most of the way home here
|
14
|
+
def compile(template)
|
15
|
+
#
|
16
|
+
# get a custom Mustache, or the default Stache::Mustache::View
|
17
|
+
mustache_class = mustache_class_from_template(template)
|
18
|
+
|
19
|
+
# Return a string that will be eval'd in the context of the ActionView, ugly, but it works.
|
20
|
+
<<-MUSTACHE
|
21
|
+
mustache = ::#{mustache_class}.new
|
22
|
+
mustache.view = self
|
23
|
+
mustache.template = '#{template.source.gsub(/'/, "\\\\'")}'
|
24
|
+
mustache.virtual_path = '#{template.virtual_path.to_s}'
|
25
|
+
mustache[:yield] = content_for(:layout)
|
26
|
+
mustache.context.update(local_assigns)
|
27
|
+
variables = controller.instance_variable_names
|
28
|
+
variables -= %w[@template]
|
29
|
+
|
30
|
+
if controller.respond_to?(:protected_instance_variables)
|
31
|
+
variables -= controller.protected_instance_variables
|
32
|
+
end
|
33
|
+
|
34
|
+
variables.each do |name|
|
35
|
+
mustache.instance_variable_set(name, controller.instance_variable_get(name))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Declaring an +attr_reader+ for each instance variable in the
|
39
|
+
# Stache::Mustache::View subclass makes them available to your templates.
|
40
|
+
mustache.class.class_eval do
|
41
|
+
attr_reader *variables.map { |name| name.sub(/^@/, '').to_sym }
|
42
|
+
end
|
43
|
+
|
44
|
+
mustache.render.html_safe
|
45
|
+
MUSTACHE
|
46
|
+
end
|
47
|
+
|
48
|
+
# In Rails 3.1+, #call takes the place of #compile
|
49
|
+
def self.call(template)
|
50
|
+
new.compile(template)
|
51
|
+
end
|
52
|
+
|
53
|
+
# suss out a constant name for the given template
|
54
|
+
def mustache_class_from_template(template)
|
55
|
+
const_name = ActiveSupport::Inflector.camelize(template.virtual_path.to_s)
|
56
|
+
begin
|
57
|
+
const_name.constantize
|
58
|
+
rescue NameError, LoadError => e
|
59
|
+
# Only rescue NameError/LoadError concerning our mustache_class
|
60
|
+
if e.message.match(/#{const_name}$/)
|
61
|
+
Stache::Mustache::View
|
62
|
+
else
|
63
|
+
raise e
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
|
3
|
+
module Stache
|
4
|
+
module Mustache
|
5
|
+
#
|
6
|
+
# A Convienent Base Class for the views. Subclass this for autoloading magic with your templates.
|
7
|
+
class View < ::Mustache
|
8
|
+
attr_accessor :view, :template, :virtual_path
|
9
|
+
|
10
|
+
def method_missing(method, *args, &block)
|
11
|
+
view.send(method, *args, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def respond_to?(method, include_private=false)
|
15
|
+
super(method, include_private) || view.respond_to?(method, include_private)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Redefine where Stache::View templates locate their partials
|
19
|
+
def partial(name)
|
20
|
+
current_dir = Stache.template_base_path.join( self.virtual_path.split("/")[0..-2].join("/") )
|
21
|
+
lookup_context.view_paths += [current_dir]
|
22
|
+
|
23
|
+
template_finder = lambda do |partial|
|
24
|
+
if ActionPack::VERSION::MAJOR == 3 && ActionPack::VERSION::MINOR < 2
|
25
|
+
lookup_context.find(name, [], partial)
|
26
|
+
else # Rails 3.2 and higher
|
27
|
+
lookup_context.find(name, [], partial, [], { formats: [:html], handlers: [:mustache] })
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
template = template_finder.call(true) rescue template_finder.call(false)
|
32
|
+
template.source
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/stache/version.rb
CHANGED