stache 0.2.2 → 0.9.0
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/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
|
+
[](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