curly-templates 0.6.1 → 0.7.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.
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
4
4
  s.rubygems_version = '1.3.5'
5
5
 
6
6
  s.name = 'curly-templates'
7
- s.version = '0.6.1'
8
- s.date = '2013-04-29'
7
+ s.version = '0.7.0'
8
+ s.date = '2013-05-03'
9
9
 
10
10
  s.summary = "Free your views!"
11
11
  s.description = "A view layer for your Rails apps that separates structure and logic."
@@ -34,14 +34,16 @@ Gem::Specification.new do |s|
34
34
  curly-templates.gemspec
35
35
  lib/curly-templates.rb
36
36
  lib/curly.rb
37
+ lib/curly/compiler.rb
37
38
  lib/curly/dependency_tracker.rb
39
+ lib/curly/invalid_reference.rb
38
40
  lib/curly/presenter.rb
39
41
  lib/curly/railtie.rb
40
42
  lib/curly/template_handler.rb
41
43
  lib/generators/curly/controller/controller_generator.rb
42
44
  lib/generators/curly/controller/templates/presenter.rb.erb
43
45
  lib/generators/curly/controller/templates/view.html.curly.erb
44
- spec/curly_spec.rb
46
+ spec/compiler_spec.rb
45
47
  spec/generators/controller_generator_spec.rb
46
48
  spec/presenter_spec.rb
47
49
  spec/spec_helper.rb
@@ -26,82 +26,30 @@
26
26
  # See Curly::Presenter for more information on presenters.
27
27
  #
28
28
  module Curly
29
- VERSION = "0.6.1"
30
-
31
- REFERENCE_REGEX = %r(\{\{([\w\.]+)\}\})
32
-
33
- class InvalidReference < StandardError
34
- attr_reader :reference
35
-
36
- def initialize(reference)
37
- @reference = reference
38
- end
39
-
40
- def message
41
- "invalid reference `{{#{reference}}}'"
42
- end
29
+ VERSION = "0.7.0"
30
+
31
+ # Compiles a Curly template to Ruby code.
32
+ #
33
+ # template - The template String that should be compiled.
34
+ #
35
+ # Returns a String containing the Ruby code.
36
+ def self.compile(template, presenter_class)
37
+ Compiler.compile(template, presenter_class)
43
38
  end
44
39
 
45
- class << self
46
-
47
- # Compiles a Curly template to Ruby code.
48
- #
49
- # template - The template String that should be compiled.
50
- #
51
- # Returns a String containing the Ruby code.
52
- def compile(template, presenter_class)
53
- source = template.inspect
54
- source.gsub!(REFERENCE_REGEX) { compile_reference($1, presenter_class) }
55
-
56
- source
57
- end
58
-
59
- # Whether the Curly template is valid. This includes whether all
60
- # references are available on the presenter class.
61
- #
62
- # template - The template String that should be validated.
63
- # presenter_class - The presenter Class.
64
- #
65
- # Returns true if the template is valid, false otherwise.
66
- def valid?(template, presenter_class)
67
- references = extract_references(template)
68
- methods = presenter_class.available_methods.map(&:to_s)
69
- references & methods == references
70
- end
71
-
72
- private
73
-
74
- def compile_reference(reference, presenter_class)
75
- method, argument = reference.split(".", 2)
76
-
77
- unless presenter_class.method_available?(method.to_sym)
78
- raise Curly::InvalidReference.new(method.to_sym)
79
- end
80
-
81
- if presenter_class.instance_method(method).arity == 1
82
- # The method accepts a single argument -- pass it in.
83
- code = <<-RUBY
84
- presenter.#{method}(#{argument.inspect}) {|*args| yield(*args) }
85
- RUBY
86
- else
87
- code = <<-RUBY
88
- presenter.#{method} {|*args| yield(*args) }
89
- RUBY
90
- end
91
-
92
- %(\#{
93
- result = #{code}
94
- ERB::Util.html_escape(result)
95
- })
96
- end
97
-
98
- def extract_references(template)
99
- template.scan(REFERENCE_REGEX).flatten
100
- end
101
-
40
+ # Whether the Curly template is valid. This includes whether all
41
+ # references are available on the presenter class.
42
+ #
43
+ # template - The template String that should be validated.
44
+ # presenter_class - The presenter Class.
45
+ #
46
+ # Returns true if the template is valid, false otherwise.
47
+ def self.valid?(template, presenter_class)
48
+ Compiler.valid?(template, presenter_class)
102
49
  end
103
50
  end
104
51
 
52
+ require 'curly/compiler'
105
53
  require 'curly/presenter'
106
54
  require 'curly/template_handler'
107
55
  require 'curly/railtie' if defined?(Rails)
@@ -0,0 +1,60 @@
1
+ require 'curly/invalid_reference'
2
+
3
+ module Curly
4
+ class Compiler
5
+ REFERENCE_REGEX = %r(\{\{([\w\.]+)\}\})
6
+
7
+ class << self
8
+
9
+ # Compiles a Curly template to Ruby code.
10
+ #
11
+ # template - The template String that should be compiled.
12
+ #
13
+ # Returns a String containing the Ruby code.
14
+ def compile(template, presenter_class)
15
+ source = template.inspect
16
+ source.gsub!(REFERENCE_REGEX) { compile_reference($1, presenter_class) }
17
+
18
+ source
19
+ end
20
+
21
+ # Whether the Curly template is valid. This includes whether all
22
+ # references are available on the presenter class.
23
+ #
24
+ # template - The template String that should be validated.
25
+ # presenter_class - The presenter Class.
26
+ #
27
+ # Returns true if the template is valid, false otherwise.
28
+ def valid?(template, presenter_class)
29
+ compile(template, presenter_class)
30
+
31
+ true
32
+ rescue InvalidReference
33
+ false
34
+ end
35
+
36
+ private
37
+
38
+ def compile_reference(reference, presenter_class)
39
+ method, argument = reference.split(".", 2)
40
+
41
+ unless presenter_class.method_available?(method.to_sym)
42
+ raise Curly::InvalidReference.new(method.to_sym)
43
+ end
44
+
45
+ if presenter_class.instance_method(method).arity == 1
46
+ # The method accepts a single argument -- pass it in.
47
+ code = <<-RUBY
48
+ presenter.#{method}(#{argument.inspect}) {|*args| yield(*args) }
49
+ RUBY
50
+ else
51
+ code = <<-RUBY
52
+ presenter.#{method} {|*args| yield(*args) }
53
+ RUBY
54
+ end
55
+
56
+ '#{ERB::Util.html_escape(%s)}' % code.strip
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,13 @@
1
+ module Curly
2
+ class InvalidReference < StandardError
3
+ attr_reader :reference
4
+
5
+ def initialize(reference)
6
+ @reference = reference
7
+ end
8
+
9
+ def message
10
+ "invalid reference `{{#{reference}}}'"
11
+ end
12
+ end
13
+ end
@@ -3,60 +3,75 @@ require 'action_view'
3
3
  require 'curly'
4
4
 
5
5
  class Curly::TemplateHandler
6
+ class << self
6
7
 
7
- # Handles a Curly template, compiling it to Ruby code. The code will be
8
- # evaluated in the context of an ActionView::Base instance, having access
9
- # to a number of variables.
10
- #
11
- # template - The ActionView::Template template that should be compiled.
12
- #
13
- # Returns a String containing the Ruby code representing the template.
14
- def self.call(template)
15
- path = template.virtual_path
16
- presenter_class = Curly::Presenter.presenter_for_path(path)
17
-
18
- source = Curly.compile(template.source, presenter_class)
19
- template_digest = Digest::MD5.hexdigest(template.source)
20
-
21
- # Template is empty, so there's no need to initialize a presenter.
22
- return %("") if template.source.empty?
23
-
24
- <<-RUBY
25
- if local_assigns.empty?
26
- options = assigns
27
- else
28
- options = local_assigns
8
+ # Handles a Curly template, compiling it to Ruby code. The code will be
9
+ # evaluated in the context of an ActionView::Base instance, having access
10
+ # to a number of variables.
11
+ #
12
+ # template - The ActionView::Template template that should be compiled.
13
+ #
14
+ # Returns a String containing the Ruby code representing the template.
15
+ def call(template)
16
+ instrument(template) do
17
+ compile(template)
18
+ end
29
19
  end
30
20
 
31
- presenter = #{presenter_class}.new(self, options.with_indifferent_access)
21
+ private
32
22
 
33
- view_function = lambda do
34
- #{source}
35
- end
23
+ def compile(template)
24
+ # Template is empty, so there's no need to initialize a presenter.
25
+ return %("") if template.source.empty?
36
26
 
37
- if key = presenter.cache_key
38
- @output_buffer = ActiveSupport::SafeBuffer.new
27
+ path = template.virtual_path
28
+ presenter_class = Curly::Presenter.presenter_for_path(path)
39
29
 
40
- template_digest = #{template_digest.inspect}
30
+ source = Curly.compile(template.source, presenter_class)
31
+ template_digest = Digest::MD5.hexdigest(template.source)
41
32
 
42
- if #{presenter_class}.respond_to?(:cache_key)
43
- presenter_key = #{presenter_class}.cache_key
33
+ <<-RUBY
34
+ if local_assigns.empty?
35
+ options = assigns
44
36
  else
45
- presenter_key = nil
37
+ options = local_assigns
46
38
  end
47
39
 
48
- options = {
49
- expires_in: presenter.cache_duration
50
- }
40
+ presenter = #{presenter_class}.new(self, options.with_indifferent_access)
51
41
 
52
- cache([template_digest, key, presenter_key].compact, options) do
53
- safe_concat(view_function.call)
42
+ view_function = lambda do
43
+ #{source}
54
44
  end
55
45
 
56
- @output_buffer
57
- else
58
- view_function.call.html_safe
46
+ if key = presenter.cache_key
47
+ @output_buffer = ActiveSupport::SafeBuffer.new
48
+
49
+ template_digest = #{template_digest.inspect}
50
+
51
+ if #{presenter_class}.respond_to?(:cache_key)
52
+ presenter_key = #{presenter_class}.cache_key
53
+ else
54
+ presenter_key = nil
55
+ end
56
+
57
+ options = {
58
+ expires_in: presenter.cache_duration
59
+ }
60
+
61
+ cache([template_digest, key, presenter_key].compact, options) do
62
+ safe_concat(view_function.call)
63
+ end
64
+
65
+ @output_buffer
66
+ else
67
+ view_function.call.html_safe
68
+ end
69
+ RUBY
70
+ end
71
+
72
+ def instrument(template, &block)
73
+ payload = { path: template.virtual_path }
74
+ ActiveSupport::Notifications.instrument("compile.curly", payload, &block)
59
75
  end
60
- RUBY
61
76
  end
62
77
  end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe Curly::Compiler do
4
+ let :presenter_class do
5
+ Class.new do
6
+ def foo
7
+ "FOO"
8
+ end
9
+
10
+ def high_yield
11
+ "#{yield}, motherfucker!"
12
+ end
13
+
14
+ def yield_value
15
+ "#{yield :foo}, please?"
16
+ end
17
+
18
+ def unicorns
19
+ "UNICORN"
20
+ end
21
+
22
+ def dirty
23
+ nil
24
+ end
25
+
26
+ def parameterized(value)
27
+ value
28
+ end
29
+
30
+ def self.method_available?(method)
31
+ [:foo, :parameterized, :high_yield, :yield_value, :dirty].include?(method)
32
+ end
33
+
34
+ def self.available_methods
35
+ public_instance_methods
36
+ end
37
+
38
+ private
39
+
40
+ def method_missing(*args)
41
+ "BAR"
42
+ end
43
+ end
44
+ end
45
+
46
+ let(:presenter) { presenter_class.new }
47
+
48
+ describe ".compile" do
49
+ it "compiles Curly templates to Ruby code" do
50
+ evaluate("{{foo}}").should == "FOO"
51
+ end
52
+
53
+ it "passes on an optional reference parameter to the presenter method" do
54
+ evaluate("{{parameterized.foo.bar}}").should == "foo.bar"
55
+ end
56
+
57
+ it "passes an empty string to methods that take a parameter when none is provided" do
58
+ evaluate("{{parameterized}}").should == ""
59
+ end
60
+
61
+ it "makes sure only public methods are called on the presenter object" do
62
+ expect { evaluate("{{bar}}") }.to raise_exception(Curly::InvalidReference)
63
+ end
64
+
65
+ it "includes the invalid reference when failing to compile" do
66
+ begin
67
+ evaluate("{{bar}}")
68
+ fail
69
+ rescue Curly::InvalidReference => e
70
+ e.reference.should == :bar
71
+ end
72
+ end
73
+
74
+ it "propagates yields to the caller" do
75
+ evaluate("{{high_yield}}") { "$$$" }.should == "$$$, motherfucker!"
76
+ end
77
+
78
+ it "sends along arguments passed to yield" do
79
+ evaluate("{{yield_value}}") {|v| v.upcase }.should == "FOO, please?"
80
+ end
81
+
82
+ it "escapes non HTML safe strings returned from the presenter" do
83
+ presenter.stub(:dirty) { "<p>dirty</p>" }
84
+ evaluate("{{dirty}}").should == "&lt;p&gt;dirty&lt;/p&gt;"
85
+ end
86
+
87
+ it "does not escape HTML safe strings returned from the presenter" do
88
+ presenter.stub(:dirty) { "<p>dirty</p>".html_safe }
89
+ evaluate("{{dirty}}").should == "<p>dirty</p>"
90
+ end
91
+ end
92
+
93
+ describe ".valid?" do
94
+ it "returns true if only available methods are referenced" do
95
+ validate("Hello, {{foo}}!").should == true
96
+ end
97
+
98
+ it "returns false if a missing method is referenced" do
99
+ validate("Hello, {{i_am_missing}}").should == false
100
+ end
101
+
102
+ it "returns false if an unavailable method is referenced" do
103
+ presenter_class.stub(:available_methods) { [:foo] }
104
+ validate("Hello, {{inspect}}").should == false
105
+ end
106
+
107
+ def validate(template)
108
+ Curly.valid?(template, presenter_class)
109
+ end
110
+ end
111
+
112
+ def evaluate(template, &block)
113
+ code = Curly::Compiler.compile(template, presenter_class)
114
+ context = double("context", presenter: presenter)
115
+
116
+ context.instance_eval(<<-RUBY)
117
+ def self.render
118
+ #{code}
119
+ end
120
+ RUBY
121
+
122
+ context.render(&block)
123
+ end
124
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: curly-templates
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-29 00:00:00.000000000 Z
12
+ date: 2013-05-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -115,14 +115,16 @@ files:
115
115
  - curly-templates.gemspec
116
116
  - lib/curly-templates.rb
117
117
  - lib/curly.rb
118
+ - lib/curly/compiler.rb
118
119
  - lib/curly/dependency_tracker.rb
120
+ - lib/curly/invalid_reference.rb
119
121
  - lib/curly/presenter.rb
120
122
  - lib/curly/railtie.rb
121
123
  - lib/curly/template_handler.rb
122
124
  - lib/generators/curly/controller/controller_generator.rb
123
125
  - lib/generators/curly/controller/templates/presenter.rb.erb
124
126
  - lib/generators/curly/controller/templates/view.html.curly.erb
125
- - spec/curly_spec.rb
127
+ - spec/compiler_spec.rb
126
128
  - spec/generators/controller_generator_spec.rb
127
129
  - spec/presenter_spec.rb
128
130
  - spec/spec_helper.rb
@@ -143,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
143
145
  version: '0'
144
146
  segments:
145
147
  - 0
146
- hash: 3185475346795496979
148
+ hash: -1185334728510665354
147
149
  required_rubygems_version: !ruby/object:Gem::Requirement
148
150
  none: false
149
151
  requirements:
@@ -157,7 +159,7 @@ signing_key:
157
159
  specification_version: 2
158
160
  summary: Free your views!
159
161
  test_files:
160
- - spec/curly_spec.rb
162
+ - spec/compiler_spec.rb
161
163
  - spec/generators/controller_generator_spec.rb
162
164
  - spec/presenter_spec.rb
163
165
  - spec/template_handler_spec.rb
@@ -1,123 +0,0 @@
1
- require 'spec_helper'
2
- require 'curly'
3
-
4
- describe Curly do
5
- let :presenter_class do
6
- Class.new do
7
- def foo
8
- "FOO"
9
- end
10
-
11
- def high_yield
12
- "#{yield}, motherfucker!"
13
- end
14
-
15
- def yield_value
16
- "#{yield :foo}, please?"
17
- end
18
-
19
- def unicorns
20
- "UNICORN"
21
- end
22
-
23
- def dirty
24
- nil
25
- end
26
-
27
- def parameterized(value)
28
- value
29
- end
30
-
31
- def self.method_available?(method)
32
- [:foo, :parameterized, :high_yield, :yield_value, :dirty].include?(method)
33
- end
34
-
35
- def self.available_methods
36
- public_instance_methods
37
- end
38
-
39
- private
40
-
41
- def method_missing(*args)
42
- "BAR"
43
- end
44
- end
45
- end
46
-
47
- let(:presenter) { presenter_class.new }
48
-
49
- it "compiles Curly templates to Ruby code" do
50
- evaluate("{{foo}}").should == "FOO"
51
- end
52
-
53
- it "passes on an optional reference parameter to the presenter method" do
54
- evaluate("{{parameterized.foo.bar}}").should == "foo.bar"
55
- end
56
-
57
- it "passes an empty string to methods that take a parameter when none is provided" do
58
- evaluate("{{parameterized}}").should == ""
59
- end
60
-
61
- it "makes sure only public methods are called on the presenter object" do
62
- expect { evaluate("{{bar}}") }.to raise_exception(Curly::InvalidReference)
63
- end
64
-
65
- it "includes the invalid reference when failing to compile" do
66
- begin
67
- evaluate("{{bar}}")
68
- fail
69
- rescue Curly::InvalidReference => e
70
- e.reference.should == :bar
71
- end
72
- end
73
-
74
- it "propagates yields to the caller" do
75
- evaluate("{{high_yield}}") { "$$$" }.should == "$$$, motherfucker!"
76
- end
77
-
78
- it "sends along arguments passed to yield" do
79
- evaluate("{{yield_value}}") {|v| v.upcase }.should == "FOO, please?"
80
- end
81
-
82
- it "escapes non HTML safe strings returned from the presenter" do
83
- presenter.stub(:dirty) { "<p>dirty</p>" }
84
- evaluate("{{dirty}}").should == "&lt;p&gt;dirty&lt;/p&gt;"
85
- end
86
-
87
- it "does not escape HTML safe strings returned from the presenter" do
88
- presenter.stub(:dirty) { "<p>dirty</p>".html_safe }
89
- evaluate("{{dirty}}").should == "<p>dirty</p>"
90
- end
91
-
92
- describe ".valid?" do
93
- it "returns true if only available methods are referenced" do
94
- validate("Hello, {{foo}}!").should == true
95
- end
96
-
97
- it "returns false if a missing method is referenced" do
98
- validate("Hello, {{i_am_missing}}").should == false
99
- end
100
-
101
- it "returns false if an unavailable method is referenced" do
102
- presenter_class.stub(:available_methods) { [:foo] }
103
- validate("Hello, {{inspect}}").should == false
104
- end
105
-
106
- def validate(template)
107
- Curly.valid?(template, presenter_class)
108
- end
109
- end
110
-
111
- def evaluate(template, &block)
112
- code = Curly.compile(template, presenter_class)
113
- context = double("context", presenter: presenter)
114
-
115
- context.instance_eval(<<-RUBY)
116
- def self.render
117
- #{code}
118
- end
119
- RUBY
120
-
121
- context.render(&block)
122
- end
123
- end