curly-templates 0.1.1 → 0.2.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 +8 -0
- data/README.md +55 -1
- data/Rakefile +0 -8
- data/curly-templates.gemspec +10 -4
- data/lib/curly.rb +15 -1
- data/lib/curly/presenter.rb +49 -1
- data/lib/curly/railtie.rb +2 -1
- data/lib/curly/template_handler.rb +18 -0
- data/lib/generators/curly/controller/controller_generator.rb +30 -0
- data/lib/generators/curly/controller/templates/presenter.rb.erb +10 -0
- data/lib/generators/curly/controller/templates/view.html.curly.erb +2 -0
- data/spec/curly_spec.rb +10 -4
- data/spec/generators/controller_generator_spec.rb +35 -0
- data/spec/presenter_spec.rb +0 -2
- data/spec/spec_helper.rb +1 -0
- data/spec/template_handler_spec.rb +0 -3
- metadata +43 -5
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -29,7 +29,61 @@ 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', '~> 0.1.0'
|
32
|
+
gem 'curly-templates', '~> 0.1.0'
|
33
|
+
```
|
34
|
+
|
35
|
+
|
36
|
+
How to use Curly
|
37
|
+
----------------
|
38
|
+
|
39
|
+
In order to use Curly for a view or partial, use the suffix `.curly` instead of
|
40
|
+
`.erb`, e.g. `app/views/posts/_comment.html.curly`. Curly will look for a
|
41
|
+
corresponding presenter class named `Posts::CommentPresenter`. By convention,
|
42
|
+
these are placed in `app/presenters/`, so in this case the presenter would
|
43
|
+
reside in `app/presenters/posts/comment_presenter.rb`. Note that presenters
|
44
|
+
for partials are not prepended with an underscore.
|
45
|
+
|
46
|
+
Add some HTML to the partial template along with some Curly variables:
|
47
|
+
|
48
|
+
```html
|
49
|
+
<!-- app/views/posts/_comment.html.curly -->
|
50
|
+
<div class="comment">
|
51
|
+
<p>
|
52
|
+
{{author_link}} posted {{time_ago}} ago.
|
53
|
+
</p>
|
54
|
+
|
55
|
+
{{body}}
|
56
|
+
</div>
|
57
|
+
```
|
58
|
+
|
59
|
+
The presenter will be responsible for filling in the variables. Add the necessary
|
60
|
+
Ruby code to the presenter:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
# app/presenters/posts/comment_presenter.rb
|
64
|
+
class Posts::CommentPresenter < Curly::Presenter
|
65
|
+
presents :comment
|
66
|
+
|
67
|
+
def body
|
68
|
+
BlueCloth.new(@comment.body).to_html
|
69
|
+
end
|
70
|
+
|
71
|
+
def author_link
|
72
|
+
link_to(@comment.author.name, @comment.author, rel: "author")
|
73
|
+
end
|
74
|
+
|
75
|
+
def time_ago
|
76
|
+
time_ago_in_words(@comment.created_at)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
The partial can now be rendered like any other, e.g. by calling
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
render 'comment', comment: comment
|
85
|
+
render comment
|
86
|
+
render collection: post.comments
|
33
87
|
```
|
34
88
|
|
35
89
|
|
data/Rakefile
CHANGED
@@ -52,14 +52,6 @@ task :default => :spec
|
|
52
52
|
require 'rspec/core/rake_task'
|
53
53
|
RSpec::Core::RakeTask.new(:spec)
|
54
54
|
|
55
|
-
require 'rdoc/task'
|
56
|
-
Rake::RDocTask.new do |rdoc|
|
57
|
-
rdoc.rdoc_dir = 'rdoc'
|
58
|
-
rdoc.title = "#{name} #{version}"
|
59
|
-
rdoc.rdoc_files.include('README*')
|
60
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
61
|
-
end
|
62
|
-
|
63
55
|
desc "Open an irb session preloaded with this library"
|
64
56
|
task :console do
|
65
57
|
sh "irb -rubygems -r ./lib/#{name}.rb"
|
data/curly-templates.gemspec
CHANGED
@@ -4,15 +4,15 @@ 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.2.0'
|
8
|
+
s.date = '2013-02-07'
|
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
12
|
|
13
13
|
s.authors = ["Daniel Schierbeck"]
|
14
|
-
s.email = '
|
15
|
-
s.homepage = '
|
14
|
+
s.email = 'daniel.schierbeck@gmail.com.com'
|
15
|
+
s.homepage = 'https://github.com/dasch/curly'
|
16
16
|
|
17
17
|
s.require_paths = %w[lib]
|
18
18
|
|
@@ -20,8 +20,10 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.add_dependency("actionpack", "~> 3.2.11")
|
22
22
|
|
23
|
+
s.add_development_dependency("railties", "~> 3.2.11")
|
23
24
|
s.add_development_dependency("rake")
|
24
25
|
s.add_development_dependency("rspec", "~> 2.12.0")
|
26
|
+
s.add_development_dependency("genspec")
|
25
27
|
|
26
28
|
# = MANIFEST =
|
27
29
|
s.files = %w[
|
@@ -34,7 +36,11 @@ Gem::Specification.new do |s|
|
|
34
36
|
lib/curly/presenter.rb
|
35
37
|
lib/curly/railtie.rb
|
36
38
|
lib/curly/template_handler.rb
|
39
|
+
lib/generators/curly/controller/controller_generator.rb
|
40
|
+
lib/generators/curly/controller/templates/presenter.rb.erb
|
41
|
+
lib/generators/curly/controller/templates/view.html.curly.erb
|
37
42
|
spec/curly_spec.rb
|
43
|
+
spec/generators/controller_generator_spec.rb
|
38
44
|
spec/presenter_spec.rb
|
39
45
|
spec/spec_helper.rb
|
40
46
|
spec/template_handler_spec.rb
|
data/lib/curly.rb
CHANGED
@@ -26,7 +26,7 @@
|
|
26
26
|
# See Curly::Presenter for more information on presenters.
|
27
27
|
#
|
28
28
|
module Curly
|
29
|
-
VERSION = "0.
|
29
|
+
VERSION = "0.2.0"
|
30
30
|
|
31
31
|
REFERENCE_REGEX = %r(\{\{(\w+)\}\})
|
32
32
|
|
@@ -35,6 +35,11 @@ module Curly
|
|
35
35
|
|
36
36
|
class << self
|
37
37
|
|
38
|
+
# Compiles a Curly template to Ruby code.
|
39
|
+
#
|
40
|
+
# template - The template String that should be compiled.
|
41
|
+
#
|
42
|
+
# Returns a String containing the Ruby code.
|
38
43
|
def compile(template)
|
39
44
|
source = template.inspect
|
40
45
|
source.gsub!(REFERENCE_REGEX) { compile_reference($1) }
|
@@ -42,6 +47,13 @@ module Curly
|
|
42
47
|
source
|
43
48
|
end
|
44
49
|
|
50
|
+
# Whether the Curly template is valid. This includes whether all
|
51
|
+
# references are available on the presenter class.
|
52
|
+
#
|
53
|
+
# template - The template String that should be validated.
|
54
|
+
# presenter_class - The presenter Class.
|
55
|
+
#
|
56
|
+
# Returns true if the template is valid, false otherwise.
|
45
57
|
def valid?(template, presenter_class)
|
46
58
|
references = extract_references(template)
|
47
59
|
methods = presenter_class.available_methods.map(&:to_s)
|
@@ -68,4 +80,6 @@ module Curly
|
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
83
|
+
require 'curly/presenter'
|
84
|
+
require 'curly/template_handler'
|
71
85
|
require 'curly/railtie' if defined?(Rails)
|
data/lib/curly/presenter.rb
CHANGED
@@ -12,7 +12,7 @@ module Curly
|
|
12
12
|
#
|
13
13
|
# A presenter is always instantiated with a context to which it delegates
|
14
14
|
# unknown messages, usually an instance of ActionView::Base provided by
|
15
|
-
# Rails. See Curly::
|
15
|
+
# Rails. See Curly::TemplateHandler for a typical use.
|
16
16
|
#
|
17
17
|
# Examples
|
18
18
|
#
|
@@ -36,6 +36,7 @@ module Curly
|
|
36
36
|
# presenter.author #=> "Jackie Chan"
|
37
37
|
#
|
38
38
|
class Presenter
|
39
|
+
|
39
40
|
# Initializes the presenter with the given context and options.
|
40
41
|
#
|
41
42
|
# context - An ActionView::Base context.
|
@@ -48,18 +49,62 @@ module Curly
|
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
52
|
+
# The key that should be used to cache the view.
|
53
|
+
#
|
54
|
+
# Unless `#cache_key` returns nil, the result of rendering the template
|
55
|
+
# that the presenter supports will be cached. The return value will be
|
56
|
+
# part of the final cache key, along with a digest of the template itself.
|
57
|
+
#
|
58
|
+
# Any object can be used as a cache key, so long as it
|
59
|
+
#
|
60
|
+
# - is a String,
|
61
|
+
# - responds to #cache_key itself, or
|
62
|
+
# - is an Array of a Hash whose items themselves fit either of these
|
63
|
+
# criteria.
|
64
|
+
#
|
65
|
+
# Returns the cache key Object or nil if no caching should be performed.
|
51
66
|
def cache_key
|
52
67
|
nil
|
53
68
|
end
|
54
69
|
|
70
|
+
# The duration that the view should be cached for. Only relevant if
|
71
|
+
# `#cache_key` returns a non nil value.
|
72
|
+
#
|
73
|
+
# If nil, the view will not have an expiration time set.
|
74
|
+
#
|
75
|
+
# Examples
|
76
|
+
#
|
77
|
+
# def cache_duration
|
78
|
+
# 10.minutes
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# Returns the Fixnum duration of the cache item, in seconds, or nil if no
|
82
|
+
# duration should be set.
|
55
83
|
def cache_duration
|
56
84
|
nil
|
57
85
|
end
|
58
86
|
|
87
|
+
# Whether a method is available to templates rendered with the presenter.
|
88
|
+
#
|
89
|
+
# Templates can reference "variables", which are simply methods defined on
|
90
|
+
# the presenter. By default, only public instance methods can be
|
91
|
+
# referenced, and any method defined on Curly::Presenter itself cannot be
|
92
|
+
# referenced. This means that methods such as `#cache_key` and #inspect are
|
93
|
+
# not available. This is done for safety purposes.
|
94
|
+
#
|
95
|
+
# This policy can be changed by overriding this method in your presenters.
|
96
|
+
#
|
97
|
+
# method - The Symbol name of the method.
|
98
|
+
#
|
99
|
+
# Returns true if the method can be referenced by a template,
|
100
|
+
# false otherwise.
|
59
101
|
def method_available?(method)
|
60
102
|
self.class.available_methods.include?(method)
|
61
103
|
end
|
62
104
|
|
105
|
+
# A list of methods available to templates rendered with the presenter.
|
106
|
+
#
|
107
|
+
# Returns an Array of Symbol method names.
|
63
108
|
def self.available_methods
|
64
109
|
public_instance_methods - Curly::Presenter.public_instance_methods
|
65
110
|
end
|
@@ -73,6 +118,9 @@ module Curly
|
|
73
118
|
self.presented_names += args
|
74
119
|
end
|
75
120
|
|
121
|
+
# Delegates private method calls to the current view context.
|
122
|
+
#
|
123
|
+
# The view context, an instance of ActionView::Base, is set by Rails.
|
76
124
|
def method_missing(method, *args, &block)
|
77
125
|
@_context.public_send(method, *args, &block)
|
78
126
|
end
|
data/lib/curly/railtie.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Curly
|
2
2
|
class Railtie < Rails::Railtie
|
3
|
+
config.app_generators.template_engine :curly
|
4
|
+
|
3
5
|
initializer 'curly.initialize_template_handler' do
|
4
|
-
require 'curly/template_handler'
|
5
6
|
ActionView::Template.register_template_handler :curly, Curly::TemplateHandler
|
6
7
|
end
|
7
8
|
end
|
@@ -3,10 +3,28 @@ require 'action_view'
|
|
3
3
|
require 'curly'
|
4
4
|
|
5
5
|
class Curly::TemplateHandler
|
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.
|
6
17
|
def self.presenter_name_for_path(path)
|
7
18
|
"#{path}_presenter".camelize
|
8
19
|
end
|
9
20
|
|
21
|
+
# Handles a Curly template, compiling it to Ruby code. The code will be
|
22
|
+
# evaluated in the context of an ActionView::Base instance, having access
|
23
|
+
# to a number of variables.
|
24
|
+
#
|
25
|
+
# template - The ActionView::Template template that should be compiled.
|
26
|
+
#
|
27
|
+
# Returns a String containing the Ruby code representing the template.
|
10
28
|
def self.call(template)
|
11
29
|
presenter_class = presenter_name_for_path(template.virtual_path)
|
12
30
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/named_base'
|
3
|
+
|
4
|
+
module Curly
|
5
|
+
module Generators
|
6
|
+
class ControllerGenerator < Rails::Generators::NamedBase
|
7
|
+
source_root File.expand_path("../templates", __FILE__)
|
8
|
+
|
9
|
+
argument :actions, type: :array, default: [], banner: "action action"
|
10
|
+
|
11
|
+
def create_view_files
|
12
|
+
base_views_path = File.join("app/views", class_path, file_name)
|
13
|
+
base_presenters_path = File.join("app/presenters", class_path, file_name)
|
14
|
+
|
15
|
+
empty_directory base_views_path
|
16
|
+
empty_directory base_presenters_path
|
17
|
+
|
18
|
+
actions.each do |action|
|
19
|
+
@view_path = File.join(base_views_path, "#{action}.html.curly")
|
20
|
+
@presenter_path = File.join(base_presenters_path, "#{action}_presenter.rb")
|
21
|
+
@action = action
|
22
|
+
@presenter_name = "#{class_name}::#{action.capitalize}Presenter"
|
23
|
+
|
24
|
+
template "view.html.curly.erb", @view_path
|
25
|
+
template "presenter.rb.erb", @presenter_path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class <%= @presenter_name %> < Curly::Presenter
|
2
|
+
# If you need to assign variables to the presenter, you can use the
|
3
|
+
# `presents` method.
|
4
|
+
#
|
5
|
+
# presents :foo, :bar
|
6
|
+
#
|
7
|
+
# Any public method defined in a presenter class will be available
|
8
|
+
# to the Curly template as a variable. Consider making these methods
|
9
|
+
# idempotent.
|
10
|
+
end
|
data/spec/curly_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'active_support/core_ext/string/output_safety'
|
3
2
|
require 'curly'
|
4
3
|
|
5
4
|
describe Curly do
|
@@ -38,7 +37,6 @@ describe Curly do
|
|
38
37
|
end
|
39
38
|
|
40
39
|
let(:presenter) { presenter_class.new }
|
41
|
-
let(:context) { double("context", presenter: presenter) }
|
42
40
|
|
43
41
|
it "compiles Curly templates to Ruby code" do
|
44
42
|
evaluate("{{foo}}").should == "FOO"
|
@@ -85,8 +83,16 @@ describe Curly do
|
|
85
83
|
end
|
86
84
|
end
|
87
85
|
|
88
|
-
def evaluate(template)
|
86
|
+
def evaluate(template, &block)
|
89
87
|
code = Curly.compile(template)
|
90
|
-
context
|
88
|
+
context = double("context", presenter: presenter)
|
89
|
+
|
90
|
+
context.instance_eval(<<-RUBY)
|
91
|
+
def self.render
|
92
|
+
#{code}
|
93
|
+
end
|
94
|
+
RUBY
|
95
|
+
|
96
|
+
context.render(&block)
|
91
97
|
end
|
92
98
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'genspec'
|
3
|
+
require 'generators/curly/controller/controller_generator'
|
4
|
+
|
5
|
+
describe Curly::Generators::ControllerGenerator do
|
6
|
+
with_args "animals/cows", "foo"
|
7
|
+
|
8
|
+
it "generates a Curly template for each action" do
|
9
|
+
subject.should generate("app/views/animals/cows/foo.html.curly") {|content|
|
10
|
+
expected_content = "<h1>Animals::Cows#foo</h1>\n" +
|
11
|
+
"<p>Find me in app/views/animals/cows/foo.html.curly</p>\n"
|
12
|
+
|
13
|
+
content.should == expected_content
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "generates a Curly presenter for each action" do
|
18
|
+
subject.should generate("app/presenters/animals/cows/foo_presenter.rb") {|content|
|
19
|
+
expected_content = (<<-RUBY).gsub(/^\s{8}/, "")
|
20
|
+
class Animals::Cows::FooPresenter < Curly::Presenter
|
21
|
+
# If you need to assign variables to the presenter, you can use the
|
22
|
+
# `presents` method.
|
23
|
+
#
|
24
|
+
# presents :foo, :bar
|
25
|
+
#
|
26
|
+
# Any public method defined in a presenter class will be available
|
27
|
+
# to the Curly template as a variable. Consider making these methods
|
28
|
+
# idempotent.
|
29
|
+
end
|
30
|
+
RUBY
|
31
|
+
|
32
|
+
content.should == expected_content
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
data/spec/presenter_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
require 'active_support/all'
|
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.2.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-02-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 3.2.11
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: railties
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 3.2.11
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 3.2.11
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: rake
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,8 +75,24 @@ dependencies:
|
|
59
75
|
- - ~>
|
60
76
|
- !ruby/object:Gem::Version
|
61
77
|
version: 2.12.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: genspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
62
94
|
description: A view layer for your Rails apps that separates structure and logic.
|
63
|
-
email:
|
95
|
+
email: daniel.schierbeck@gmail.com.com
|
64
96
|
executables: []
|
65
97
|
extensions: []
|
66
98
|
extra_rdoc_files: []
|
@@ -74,11 +106,15 @@ files:
|
|
74
106
|
- lib/curly/presenter.rb
|
75
107
|
- lib/curly/railtie.rb
|
76
108
|
- lib/curly/template_handler.rb
|
109
|
+
- lib/generators/curly/controller/controller_generator.rb
|
110
|
+
- lib/generators/curly/controller/templates/presenter.rb.erb
|
111
|
+
- lib/generators/curly/controller/templates/view.html.curly.erb
|
77
112
|
- spec/curly_spec.rb
|
113
|
+
- spec/generators/controller_generator_spec.rb
|
78
114
|
- spec/presenter_spec.rb
|
79
115
|
- spec/spec_helper.rb
|
80
116
|
- spec/template_handler_spec.rb
|
81
|
-
homepage:
|
117
|
+
homepage: https://github.com/dasch/curly
|
82
118
|
licenses: []
|
83
119
|
post_install_message:
|
84
120
|
rdoc_options:
|
@@ -93,7 +129,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
129
|
version: '0'
|
94
130
|
segments:
|
95
131
|
- 0
|
96
|
-
hash: -
|
132
|
+
hash: -1725225944803924182
|
97
133
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
134
|
none: false
|
99
135
|
requirements:
|
@@ -108,5 +144,7 @@ specification_version: 2
|
|
108
144
|
summary: Free your views!
|
109
145
|
test_files:
|
110
146
|
- spec/curly_spec.rb
|
147
|
+
- spec/generators/controller_generator_spec.rb
|
111
148
|
- spec/presenter_spec.rb
|
112
149
|
- spec/template_handler_spec.rb
|
150
|
+
has_rdoc:
|