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.
Files changed (41) hide show
  1. data/CHANGELOG.md +10 -0
  2. data/README.md +32 -7
  3. data/lib/stache.rb +1 -3
  4. data/lib/stache/asset_helper.rb +15 -22
  5. data/lib/stache/config.rb +11 -9
  6. data/lib/stache/handlebars.rb +7 -0
  7. data/lib/stache/handlebars/handler.rb +80 -0
  8. data/lib/stache/handlebars/view.rb +14 -0
  9. data/lib/stache/mustache.rb +7 -0
  10. data/lib/stache/mustache/handler.rb +70 -0
  11. data/lib/stache/mustache/view.rb +37 -0
  12. data/lib/stache/version.rb +1 -1
  13. data/spec/controllers/handlebars_controller_spec.rb +35 -0
  14. data/spec/controllers/stache_controller_spec.rb +17 -5
  15. data/spec/dummy/app/assets/stylesheets/test.css +3 -0
  16. data/spec/dummy/app/controllers/handlebars_controller.rb +18 -0
  17. data/spec/dummy/app/controllers/stache_controller.rb +8 -3
  18. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  19. data/spec/dummy/app/views/handlebars/_eaten_by_a.html.hbs +1 -0
  20. data/spec/dummy/app/views/handlebars/index.html.hbs +1 -0
  21. data/spec/dummy/app/views/handlebars/with_helpers.html.hbs +3 -0
  22. data/spec/dummy/app/views/handlebars/with_partials.html.hbs +3 -0
  23. data/spec/dummy/app/views/stache/with_asset_helpers.html.mustache +4 -0
  24. data/spec/dummy/config/application.rb +3 -0
  25. data/spec/dummy/config/initializers/stache.rb +2 -0
  26. data/spec/dummy/config/routes.rb +5 -53
  27. data/spec/dummy/lib/with_asset_helpers.rb +11 -0
  28. data/spec/spec_helper.rb +1 -1
  29. data/spec/stache/asset_helper_spec.rb +41 -39
  30. data/spec/stache/config_spec.rb +4 -6
  31. data/spec/stache/handlebars/handlebars_spec.rb +37 -0
  32. data/spec/stache/handlebars/profile_autoload.rb +6 -0
  33. data/spec/stache/handlebars/view_spec.rb +11 -0
  34. data/spec/stache/{handler_spec.rb → mustache/handler_spec.rb} +8 -8
  35. data/spec/stache/{profile_autoload.rb → mustache/profile_autoload.rb} +1 -1
  36. data/spec/stache/mustache/view_spec.rb +11 -0
  37. data/stache.gemspec +3 -5
  38. metadata +50 -22
  39. data/lib/stache/handler.rb +0 -68
  40. data/lib/stache/view.rb +0 -38
  41. 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. On the todo list is the ability to customize this helper a little more :).
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
@@ -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
- exploded = source.split("/")
8
- file = exploded.pop
9
- file = file.split(".").first
10
-
11
- base_path = Stache.template_base_path.join(*exploded)
12
- template_path = locate_template_for(base_path, file)
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, :template_extension, :shared_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,7 @@
1
+ require "stache/handlebars/handler"
2
+
3
+ module Stache
4
+ module Handlebars; end
5
+ end
6
+
7
+ ActionView::Template.register_template_handler :hbs, Stache::Handlebars::Handler
@@ -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,7 @@
1
+ require "stache/mustache/handler"
2
+
3
+ module Stache
4
+ module Mustache; end
5
+ end
6
+
7
+ ActionView::Template.register_template_handler :mustache, Stache::Mustache::Handler
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Stache
2
- VERSION = "0.2.2"
2
+ VERSION = "0.9.0"
3
3
  end