curly-templates 0.6.1 → 0.7.0

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