stache 0.2.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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