curly-templates 0.2.1 → 0.3.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/Gemfile +1 -1
- data/README.md +74 -4
- data/curly-templates.gemspec +5 -4
- data/lib/curly.rb +1 -1
- data/lib/curly/presenter.rb +85 -9
- data/lib/curly/template_handler.rb +9 -16
- data/spec/presenter_spec.rb +55 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/template_handler_spec.rb +14 -0
- metadata +8 -7
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -29,7 +29,7 @@ Installing Curly is as simple as running `gem install curly-templates`. If you'r
|
|
29
29
|
using Bundler to manage your dependencies, add this to your Gemfile
|
30
30
|
|
31
31
|
```ruby
|
32
|
-
gem 'curly-templates'
|
32
|
+
gem 'curly-templates'
|
33
33
|
```
|
34
34
|
|
35
35
|
|
@@ -144,11 +144,24 @@ end
|
|
144
144
|
Caching
|
145
145
|
-------
|
146
146
|
|
147
|
+
Caching is handled at two levels in Curly – statically and dynamically. Static caching
|
148
|
+
concerns changes to your code and templates introduced by deploys. If you do not wish
|
149
|
+
to clear your entire cache every time you deploy, you need a way to indicate that some
|
150
|
+
view, helper, or other piece of logic has changed.
|
151
|
+
|
152
|
+
Dynamic caching concerns changes that happen on the fly, usually made by your users in
|
153
|
+
the running system. You wish to cache a view or a partial and have it expire whenever
|
154
|
+
some data is updated – usually whenever a specific record is changed.
|
155
|
+
|
156
|
+
|
157
|
+
### Dynamic Caching
|
158
|
+
|
147
159
|
Because of the way logic is contained in presenters, caching entire views or partials
|
148
|
-
becomes exceedingly straightforward. Simply define a
|
149
|
-
a non-nil object, and the return value will be used to
|
160
|
+
by the data they present becomes exceedingly straightforward. Simply define a
|
161
|
+
`#cache_key` method that returns a non-nil object, and the return value will be used to
|
162
|
+
cache the template.
|
150
163
|
|
151
|
-
Whereas in ERB
|
164
|
+
Whereas in ERB you would include the `cache` call in the template itself:
|
152
165
|
|
153
166
|
```erb
|
154
167
|
<% cache([@post, signed_in?]) do %>
|
@@ -182,6 +195,63 @@ end
|
|
182
195
|
```
|
183
196
|
|
184
197
|
|
198
|
+
### Static Caching
|
199
|
+
|
200
|
+
Static caching will only be enabled for presenters that define a non-nil `#cache_key`
|
201
|
+
method (see "Dynamic Caching.")
|
202
|
+
|
203
|
+
In order to make a deploy expire the cache for a specific view, set the version of the
|
204
|
+
view to something new, usually by incrementing by one:
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
class Posts::ShowPresenter < Curly::Presenter
|
208
|
+
version 3
|
209
|
+
|
210
|
+
def cache_key
|
211
|
+
# Some objects
|
212
|
+
end
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
This will change the cache keys for all instances of that view, effectively expiring
|
217
|
+
the old cache entries.
|
218
|
+
|
219
|
+
This works well for views, or for partials that are rendered in views that themselves
|
220
|
+
are not cached. If the partial is nested within a view that _is_ cached, however, the
|
221
|
+
outer cache will not be expired. The solution is to register that the inner partial
|
222
|
+
is a dependency of the outer one such that Curly can automatically deduce that the
|
223
|
+
outer partial cache should be expired:
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
class Posts::ShowPresenter < Curly::Presenter
|
227
|
+
version 3
|
228
|
+
depends_on 'posts/comment'
|
229
|
+
|
230
|
+
def cache_key
|
231
|
+
# Some objects
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
class Posts::CommentPresenter < Curly::Presenter
|
236
|
+
version 4
|
237
|
+
depends_on 'posts/comment'
|
238
|
+
|
239
|
+
def cache_key
|
240
|
+
# Some objects
|
241
|
+
end
|
242
|
+
end
|
243
|
+
```
|
244
|
+
|
245
|
+
Now, if the version of `Posts::CommentPresenter` is bumped, the cache keys for both
|
246
|
+
presenters would change. You can register any number of view paths with `depends_on`.
|
247
|
+
|
248
|
+
|
249
|
+
Thanks
|
250
|
+
------
|
251
|
+
|
252
|
+
Thanks to [Zendesk](http://zendesk.com/) for sponsoring the work on Curly.
|
253
|
+
|
254
|
+
|
185
255
|
Copyright and License
|
186
256
|
---------------------
|
187
257
|
|
data/curly-templates.gemspec
CHANGED
@@ -4,15 +4,16 @@ 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.
|
8
|
-
s.date = '2013-
|
7
|
+
s.version = '0.3.0'
|
8
|
+
s.date = '2013-04-18'
|
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."
|
12
|
+
s.license = "apache2"
|
12
13
|
|
13
14
|
s.authors = ["Daniel Schierbeck"]
|
14
|
-
s.email = 'daniel.schierbeck@gmail.com
|
15
|
-
s.homepage = 'https://github.com/
|
15
|
+
s.email = 'daniel.schierbeck@gmail.com'
|
16
|
+
s.homepage = 'https://github.com/zendesk/curly'
|
16
17
|
|
17
18
|
s.require_paths = %w[lib]
|
18
19
|
|
data/lib/curly.rb
CHANGED
data/lib/curly/presenter.rb
CHANGED
@@ -102,11 +102,91 @@ module Curly
|
|
102
102
|
self.class.available_methods.include?(method)
|
103
103
|
end
|
104
104
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
105
|
+
class << self
|
106
|
+
|
107
|
+
# The name of the presenter class for a given view path.
|
108
|
+
#
|
109
|
+
# path - The String path of a view.
|
110
|
+
#
|
111
|
+
# Examples
|
112
|
+
#
|
113
|
+
# Curly::TemplateHandler.presenter_name_for_path("foo/bar")
|
114
|
+
# #=> "Foo::BarPresenter"
|
115
|
+
#
|
116
|
+
# Returns the String name of the matching presenter class.
|
117
|
+
def presenter_name_for_path(path)
|
118
|
+
"#{path}_presenter".camelize
|
119
|
+
end
|
120
|
+
|
121
|
+
def presenter_for_path(path)
|
122
|
+
presenter_name_for_path(path).constantize
|
123
|
+
end
|
124
|
+
|
125
|
+
# A list of methods available to templates rendered with the presenter.
|
126
|
+
#
|
127
|
+
# Returns an Array of Symbol method names.
|
128
|
+
def available_methods
|
129
|
+
public_instance_methods - Curly::Presenter.public_instance_methods
|
130
|
+
end
|
131
|
+
|
132
|
+
# The set of view paths that the presenter depends on.
|
133
|
+
#
|
134
|
+
# Example
|
135
|
+
#
|
136
|
+
# class Posts::ShowPresenter < Curly::Presenter
|
137
|
+
# version 2
|
138
|
+
# depends_on 'posts/comment', 'posts/comment_form'
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# Posts::ShowPresenter.dependencies
|
142
|
+
# #=> ['posts/comment', 'posts/comment_form']
|
143
|
+
#
|
144
|
+
# Returns a Set of String view paths.
|
145
|
+
def dependencies
|
146
|
+
@dependencies ||= Set.new
|
147
|
+
end
|
148
|
+
|
149
|
+
# Indicate that the presenter depends a list of other views.
|
150
|
+
#
|
151
|
+
# deps - A list of String view paths that the presenter depends on.
|
152
|
+
#
|
153
|
+
# Returns nothing.
|
154
|
+
def depends_on(*deps)
|
155
|
+
dependencies.merge(deps)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Get or set the version of the presenter.
|
159
|
+
#
|
160
|
+
# version - The Integer version that should be set. If nil, no version
|
161
|
+
# is set.
|
162
|
+
#
|
163
|
+
# Returns the current Integer version of the presenter.
|
164
|
+
def version(version = nil)
|
165
|
+
@version = version if version.present?
|
166
|
+
@version || 0
|
167
|
+
end
|
168
|
+
|
169
|
+
# The cache key for the presenter class. Includes all dependencies as well.
|
170
|
+
#
|
171
|
+
# Returns a String cache key.
|
172
|
+
def cache_key
|
173
|
+
@cache_key ||= compute_cache_key
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def compute_cache_key
|
179
|
+
dependency_cache_keys = dependencies.map do |path|
|
180
|
+
presenter = presenter_for_path(path)
|
181
|
+
presenter.cache_key
|
182
|
+
end
|
183
|
+
|
184
|
+
[name, version, dependency_cache_keys].flatten.join("/")
|
185
|
+
end
|
186
|
+
|
187
|
+
def presents(*args)
|
188
|
+
self.presented_names += args
|
189
|
+
end
|
110
190
|
end
|
111
191
|
|
112
192
|
private
|
@@ -114,10 +194,6 @@ module Curly
|
|
114
194
|
class_attribute :presented_names
|
115
195
|
self.presented_names = [].freeze
|
116
196
|
|
117
|
-
def self.presents(*args)
|
118
|
-
self.presented_names += args
|
119
|
-
end
|
120
|
-
|
121
197
|
# Delegates private method calls to the current view context.
|
122
198
|
#
|
123
199
|
# The view context, an instance of ActionView::Base, is set by Rails.
|
@@ -4,20 +4,6 @@ require 'curly'
|
|
4
4
|
|
5
5
|
class Curly::TemplateHandler
|
6
6
|
|
7
|
-
# The name of the presenter class for a given view path.
|
8
|
-
#
|
9
|
-
# path - The String path of a view.
|
10
|
-
#
|
11
|
-
# Examples
|
12
|
-
#
|
13
|
-
# Curly::TemplateHandler.presenter_name_for_path("foo/bar")
|
14
|
-
# #=> "Foo::BarPresenter"
|
15
|
-
#
|
16
|
-
# Returns the String name of the matching presenter class.
|
17
|
-
def self.presenter_name_for_path(path)
|
18
|
-
"#{path}_presenter".camelize
|
19
|
-
end
|
20
|
-
|
21
7
|
# Handles a Curly template, compiling it to Ruby code. The code will be
|
22
8
|
# evaluated in the context of an ActionView::Base instance, having access
|
23
9
|
# to a number of variables.
|
@@ -26,7 +12,8 @@ class Curly::TemplateHandler
|
|
26
12
|
#
|
27
13
|
# Returns a String containing the Ruby code representing the template.
|
28
14
|
def self.call(template)
|
29
|
-
|
15
|
+
path = template.virtual_path
|
16
|
+
presenter_class = Curly::Presenter.presenter_name_for_path(path)
|
30
17
|
|
31
18
|
source = Curly.compile(template.source)
|
32
19
|
template_digest = Digest::MD5.hexdigest(template.source)
|
@@ -52,11 +39,17 @@ class Curly::TemplateHandler
|
|
52
39
|
|
53
40
|
template_digest = #{template_digest.inspect}
|
54
41
|
|
42
|
+
if #{presenter_class}.respond_to?(:cache_key)
|
43
|
+
presenter_key = #{presenter_class}.cache_key
|
44
|
+
else
|
45
|
+
presenter_key = nil
|
46
|
+
end
|
47
|
+
|
55
48
|
options = {
|
56
49
|
expires_in: presenter.cache_duration
|
57
50
|
}
|
58
51
|
|
59
|
-
cache([template_digest, key], options) do
|
52
|
+
cache([template_digest, key, presenter_key].compact, options) do
|
60
53
|
safe_concat(view_function.call)
|
61
54
|
end
|
62
55
|
|
data/spec/presenter_spec.rb
CHANGED
@@ -24,4 +24,59 @@ describe Curly::Presenter do
|
|
24
24
|
presenter.midget.should == "Meek Harolson"
|
25
25
|
presenter.clown.should == "Bubbles"
|
26
26
|
end
|
27
|
+
|
28
|
+
describe ".presenter_for_path" do
|
29
|
+
it "returns the presenter class for the given path" do
|
30
|
+
presenter = double("presenter")
|
31
|
+
stub_const("Foo::BarPresenter", presenter)
|
32
|
+
|
33
|
+
Curly::Presenter.presenter_for_path("foo/bar").should == presenter
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".version" do
|
38
|
+
it "sets the version of the presenter" do
|
39
|
+
presenter1 = Class.new(Curly::Presenter) do
|
40
|
+
version 42
|
41
|
+
end
|
42
|
+
|
43
|
+
presenter2 = Class.new(Curly::Presenter) do
|
44
|
+
version 1337
|
45
|
+
end
|
46
|
+
|
47
|
+
presenter1.version.should == 42
|
48
|
+
presenter2.version.should == 1337
|
49
|
+
end
|
50
|
+
|
51
|
+
it "returns 0 if no version has been set" do
|
52
|
+
presenter = Class.new(Curly::Presenter)
|
53
|
+
presenter.version.should == 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe ".cache_key" do
|
58
|
+
it "includes the presenter's class name and version" do
|
59
|
+
presenter = Class.new(Curly::Presenter) { version 42 }
|
60
|
+
stub_const("Foo::BarPresenter", presenter)
|
61
|
+
|
62
|
+
Foo::BarPresenter.cache_key.should == "Foo::BarPresenter/42"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "includes the cache keys of presenters in the dependency list" do
|
66
|
+
presenter = Class.new(Curly::Presenter) do
|
67
|
+
version 42
|
68
|
+
depends_on 'foo/bum'
|
69
|
+
end
|
70
|
+
|
71
|
+
dependency = Class.new(Curly::Presenter) do
|
72
|
+
version 1337
|
73
|
+
end
|
74
|
+
|
75
|
+
stub_const("Foo::BarPresenter", presenter)
|
76
|
+
stub_const("Foo::BumPresenter", dependency)
|
77
|
+
|
78
|
+
cache_key = Foo::BarPresenter.cache_key
|
79
|
+
cache_key.should == "Foo::BarPresenter/42/Foo::BumPresenter/1337"
|
80
|
+
end
|
81
|
+
end
|
27
82
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -133,6 +133,20 @@ describe Curly::TemplateHandler do
|
|
133
133
|
output.should == "FOOBAR"
|
134
134
|
end
|
135
135
|
|
136
|
+
it "adds the presenter class' cache key to the instance's cache key" do
|
137
|
+
# Make sure caching is enabled
|
138
|
+
context.assigns[:cache_key] = "x"
|
139
|
+
|
140
|
+
presenter_class.stub(:cache_key) { "foo" }
|
141
|
+
|
142
|
+
output.should == "BAR"
|
143
|
+
|
144
|
+
presenter_class.stub(:cache_key) { "bar" }
|
145
|
+
|
146
|
+
context.stub(:bar) { "FOOBAR" }
|
147
|
+
output.should == "FOOBAR"
|
148
|
+
end
|
149
|
+
|
136
150
|
it "expires the cache keys after #cache_duration" do
|
137
151
|
context.assigns[:cache_key] = "x"
|
138
152
|
context.assigns[:cache_duration] = 42
|
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.
|
4
|
+
version: 0.3.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-
|
12
|
+
date: 2013-04-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -104,7 +104,7 @@ dependencies:
|
|
104
104
|
- !ruby/object:Gem::Version
|
105
105
|
version: '0'
|
106
106
|
description: A view layer for your Rails apps that separates structure and logic.
|
107
|
-
email: daniel.schierbeck@gmail.com
|
107
|
+
email: daniel.schierbeck@gmail.com
|
108
108
|
executables: []
|
109
109
|
extensions: []
|
110
110
|
extra_rdoc_files: []
|
@@ -126,8 +126,9 @@ files:
|
|
126
126
|
- spec/presenter_spec.rb
|
127
127
|
- spec/spec_helper.rb
|
128
128
|
- spec/template_handler_spec.rb
|
129
|
-
homepage: https://github.com/
|
130
|
-
licenses:
|
129
|
+
homepage: https://github.com/zendesk/curly
|
130
|
+
licenses:
|
131
|
+
- apache2
|
131
132
|
post_install_message:
|
132
133
|
rdoc_options:
|
133
134
|
- --charset=UTF-8
|
@@ -141,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
141
142
|
version: '0'
|
142
143
|
segments:
|
143
144
|
- 0
|
144
|
-
hash:
|
145
|
+
hash: 1050384954956597031
|
145
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
147
|
none: false
|
147
148
|
requirements:
|
@@ -150,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
150
151
|
version: '0'
|
151
152
|
requirements: []
|
152
153
|
rubyforge_project:
|
153
|
-
rubygems_version: 1.8.
|
154
|
+
rubygems_version: 1.8.25
|
154
155
|
signing_key:
|
155
156
|
specification_version: 2
|
156
157
|
summary: Free your views!
|