mynyml-merb_simple_views 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +0 -0
- data/LICENSE +0 -0
- data/README +158 -0
- data/Rakefile +49 -0
- data/TODO +0 -0
- data/autotest/discover.rb +2 -0
- data/autotest/rspec_simpleviews.rb +106 -0
- data/lib/merb_simple_views.rb +33 -0
- data/lib/merb_simple_views/mixin.rb +84 -0
- data/lib/merb_simple_views/template_parser.rb +93 -0
- data/spec/mixin_spec.rb +160 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/template_parser_spec.rb +52 -0
- metadata +93 -0
data/CHANGELOG
ADDED
File without changes
|
data/LICENSE
ADDED
File without changes
|
data/README
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
merb_simple_views *ALPHA*
|
2
|
+
==============================
|
3
|
+
|
4
|
+
WARNING: This plugin is a rewrite of merb-in-file-templates for merb >= 1.0 It
|
5
|
+
is very much a work in progress, and is NOT yet stable! Feel free to contribute
|
6
|
+
or fork if you are interesting though.
|
7
|
+
|
8
|
+
UPDATE: Due to the direction the merb project has taken, this plugin's
|
9
|
+
development has been put on hold indefinitly.
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
A plugin for the Merb framework that allows templates (views) to be defined in
|
14
|
+
the same file as the controller (Sinatra style). Especially useful for
|
15
|
+
--very-flat apps (making them truly flat), apps that have litle/small view
|
16
|
+
code, and for rapid prototyping.
|
17
|
+
|
18
|
+
==== Features
|
19
|
+
* Seamless integration with #render, #display
|
20
|
+
* Respects template reloading
|
21
|
+
* Very simple to use
|
22
|
+
* Flexible
|
23
|
+
|
24
|
+
==== Dependencies
|
25
|
+
* Merb > 0.9.4 (?)
|
26
|
+
* Rspec to run the specs
|
27
|
+
|
28
|
+
==== Examples
|
29
|
+
#example for --very-flat app (single file app)
|
30
|
+
|
31
|
+
#...
|
32
|
+
class Application < Merb::Controller; end
|
33
|
+
class Products < Application
|
34
|
+
def index
|
35
|
+
@products = Product.all
|
36
|
+
render
|
37
|
+
end
|
38
|
+
def show
|
39
|
+
@product = Product[params[:id]]
|
40
|
+
render
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
__END__
|
45
|
+
@@ index.html.erb
|
46
|
+
<h1>Product List</h1>
|
47
|
+
<ul>
|
48
|
+
<% for product in @products -%>
|
49
|
+
<li><%= product.name %></li>
|
50
|
+
<% end -%>
|
51
|
+
</ul>
|
52
|
+
|
53
|
+
@@ show.html.erb
|
54
|
+
<h1><%= @product.name %></h1>
|
55
|
+
|
56
|
+
In-file templates cohabit peacefully with regular external templates. So in the
|
57
|
+
above example, show.html.erb could be defined in views/products/show.html.erb
|
58
|
+
and both templates will still be picked up normally. In case of a name conflict
|
59
|
+
between an in-file and an external template, the external one will take
|
60
|
+
precedence and won't be overwritten, keeping it safe from data loss.
|
61
|
+
|
62
|
+
Template names follow the same rules as regular templates (usually
|
63
|
+
action.mime_type.templating_engine).
|
64
|
+
|
65
|
+
Layouts, stylesheets and javascript can also be placed in in-file templates.
|
66
|
+
|
67
|
+
#...
|
68
|
+
|
69
|
+
__END__
|
70
|
+
@@ layout/application.css
|
71
|
+
#...
|
72
|
+
|
73
|
+
@@ layout/products.css
|
74
|
+
#...
|
75
|
+
|
76
|
+
@@ stylesheets/application.css
|
77
|
+
#...
|
78
|
+
|
79
|
+
@@ javascripts/jquery.js
|
80
|
+
#...
|
81
|
+
|
82
|
+
==== Tweaking
|
83
|
+
In order to be fed into merb's templating system, in-file templates need to be
|
84
|
+
written to external files. This means you will see dynamically created view
|
85
|
+
files inside your Merb.dir_for(:view) directory/subdirectories.
|
86
|
+
|
87
|
+
The directory in which files are stored is chosen based on the templating
|
88
|
+
system's native mechanisms, and so merb-in-file-templates will respect any
|
89
|
+
changes to it. Therefore if you want to change where template files are stored,
|
90
|
+
you can play with Merb.push_path(:view, ...) and the controller's
|
91
|
+
#_template_location method.
|
92
|
+
|
93
|
+
Merb.push_path(:view, Merb.root / 'views')
|
94
|
+
class Application
|
95
|
+
def _template_location(context, type=nil, controller = controller_name)
|
96
|
+
"#{controller}.#{action_name}.#{type}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
This will tell Merb to look under the /views directory, for a template named
|
101
|
+
products.index.html.erb.
|
102
|
+
|
103
|
+
want even flatter?
|
104
|
+
|
105
|
+
Merb.push_path(:view, Merb.root)
|
106
|
+
class Application
|
107
|
+
def _template_location(context, type=nil, controller=controller_name)
|
108
|
+
"view.#{controller}.#{action_name}.#{type}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
will give you a template under the root dir called view.products.index.html.erb
|
113
|
+
(adding a common prefix, 'view.' in the example above, causes template files to
|
114
|
+
show up nicely grouped with ls, file managers, etc, so they look organized even
|
115
|
+
without being placed in a subdirectory).
|
116
|
+
|
117
|
+
If you mix in-file and external templates and you don't want them to be mixed
|
118
|
+
in the same directories, you can tell merb-in-file-templates to store its
|
119
|
+
templates somewhere else. This is done through the config hash:
|
120
|
+
|
121
|
+
Merb::Plugins.config[:in_file_templates] = {
|
122
|
+
:view_root => '...',
|
123
|
+
:stylesheets_root => '...',
|
124
|
+
:javascripts_root => '...',
|
125
|
+
}
|
126
|
+
|
127
|
+
For example, you could set
|
128
|
+
|
129
|
+
:view_root => Merb.root / 'tmp' / 'ift_views'
|
130
|
+
|
131
|
+
to store your files in merb-root/tmp/ift_views, or
|
132
|
+
|
133
|
+
:view_root => '/tmp'
|
134
|
+
|
135
|
+
to store files in your system's tmp directory.
|
136
|
+
|
137
|
+
Same goes for stylesheets and javascripts, but remember that those need to be
|
138
|
+
placed in a public directory, and that they need to be referenced properly from
|
139
|
+
within your html header. You can use the controller's
|
140
|
+
#ift_dir_for(:stylesheets) and #ift_dir_for(:javascripts) methods to find their
|
141
|
+
locations.
|
142
|
+
|
143
|
+
==== Rake Task
|
144
|
+
TODO
|
145
|
+
(cleanup rake task before deployment..)
|
146
|
+
|
147
|
+
==== Installation
|
148
|
+
TODO
|
149
|
+
|
150
|
+
==== Contact
|
151
|
+
If you have suggestions, comments, a patch, a git pull request, rants, doc
|
152
|
+
fixes/improvements, etc., feel free to contact me: mynyml at gmail,
|
153
|
+
irc.freenode.net #rubyonrails, #merb
|
154
|
+
|
155
|
+
Happy Hacking!
|
156
|
+
|
157
|
+
-------------------------------------------------------------------------
|
158
|
+
Copyright (c) 2008 Martin Aumont (mynyml), released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rake/gempackagetask'
|
2
|
+
require 'pathname'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
def gem
|
6
|
+
RUBY_1_9 ? 'gem19' : 'gem'
|
7
|
+
end
|
8
|
+
|
9
|
+
def all_except(paths)
|
10
|
+
Dir['**/*'] - paths.map {|path| path.strip.gsub(/^\//,'').gsub(/\/$/,'') }
|
11
|
+
end
|
12
|
+
|
13
|
+
spec = Gem::Specification.new do |s|
|
14
|
+
s.name = 'merb_simple_views'
|
15
|
+
s.version = '0.5.0'
|
16
|
+
s.summary = 'Merb plugin that allows defining templates (views, css, js)in the same file as the controller.'
|
17
|
+
s.description = 'Merb plugin that allows defining templates (views, css, js)in the same file as the controller.'
|
18
|
+
s.author = "Martin Aumont"
|
19
|
+
s.email = 'mynyml@gmail.com'
|
20
|
+
s.homepage = ''
|
21
|
+
s.has_rdoc = true
|
22
|
+
s.require_path = "lib"
|
23
|
+
s.files = all_except(%w( tmp/* log/* ))
|
24
|
+
s.add_dependency 'merb-core >= 1.0'
|
25
|
+
end
|
26
|
+
|
27
|
+
Rake::GemPackageTask.new(spec) do |p|
|
28
|
+
p.gem_spec = spec
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
desc "Remove package products"
|
33
|
+
task :clean => :clobber_package
|
34
|
+
|
35
|
+
desc "Update the gemspec for GitHub's gem server"
|
36
|
+
task :gemspec do
|
37
|
+
Pathname("#{spec.name}.gemspec").open('w') {|f| f << YAML.dump(spec) }
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Install gem"
|
41
|
+
task :install => [:clobber, :package] do
|
42
|
+
sh "#{SUDO} #{gem} install pkg/#{spec.full_name}.gem"
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Uninstall gem"
|
46
|
+
task :uninstall => :clean do
|
47
|
+
sh "#{SUDO} #{gem} uninstall -v #{spec.version} -x #{spec.name}"
|
48
|
+
end
|
49
|
+
|
data/TODO
ADDED
File without changes
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# adaptest from autotest/merb_rspec.rb
|
2
|
+
require 'autotest'
|
3
|
+
|
4
|
+
class RspecCommandError < StandardError; end
|
5
|
+
|
6
|
+
class Autotest::RspecSimpleviews < Autotest
|
7
|
+
def initialize
|
8
|
+
super
|
9
|
+
|
10
|
+
# Ignore any happenings in these directories
|
11
|
+
add_exception %r%^\./(?:doc|log|tmp|autotest|bin|\.git|\.autotest)%
|
12
|
+
|
13
|
+
# Ignore any mappings that Autotest may have already set up
|
14
|
+
clear_mappings
|
15
|
+
|
16
|
+
# run all specs if spec_helper is modified
|
17
|
+
add_mapping %r%^spec/spec_helper\.rb% do |_,_|
|
18
|
+
all_specs
|
19
|
+
end
|
20
|
+
|
21
|
+
# run all specs if main lib file is modified
|
22
|
+
add_mapping %r%^lib/merb_simple_views\.rb% do |_,_|
|
23
|
+
all_specs
|
24
|
+
end
|
25
|
+
|
26
|
+
# changing a lib file runs corresponding spec
|
27
|
+
add_mapping %r%^lib/merb_simple_views/(.*)\.rb% do |_, m|
|
28
|
+
files_matching %r%^spec/.*#{m[1]}_spec\.rb$%
|
29
|
+
end
|
30
|
+
|
31
|
+
# Changing a spec will cause it to run itself
|
32
|
+
add_mapping %r%^spec/.*_spec\.rb$% do |filename, _|
|
33
|
+
filename
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
39
|
+
|
40
|
+
def failed_results(results)
|
41
|
+
results.scan(/^\d+\)\n(?:\e\[\d*m)?(?:.*?Error in )?'([^\n]*)'(?: FAILED)?(?:\e\[\d*m)?\n(.*?)\n\n/m)
|
42
|
+
end
|
43
|
+
|
44
|
+
def handle_results(results)
|
45
|
+
@failures = failed_results(results)
|
46
|
+
@files_to_test = consolidate_failures(@failures)
|
47
|
+
@files_to_test.empty? && !$TESTING ? hook(:green) : hook(:red)
|
48
|
+
@tainted = !@files_to_test.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
def consolidate_failures(failed)
|
52
|
+
filters = Hash.new { |h,k| h[k] = [] }
|
53
|
+
failed.each do |spec, failed_trace|
|
54
|
+
if f = test_files_for(failed).find { |f| f =~ /spec\// }
|
55
|
+
filters[f] << spec
|
56
|
+
break
|
57
|
+
end
|
58
|
+
end
|
59
|
+
filters
|
60
|
+
end
|
61
|
+
|
62
|
+
def make_test_cmd(specs_to_runs)
|
63
|
+
[
|
64
|
+
ruby,
|
65
|
+
"-S",
|
66
|
+
spec_command,
|
67
|
+
add_options_if_present,
|
68
|
+
files_to_test.keys.flatten.join(' ')
|
69
|
+
].join(' ')
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_options_if_present
|
73
|
+
File.exist?("spec/spec.opts") ? "-O spec/spec.opts " : ""
|
74
|
+
end
|
75
|
+
|
76
|
+
# Finds the proper spec command to use. Precendence is set in the
|
77
|
+
# lazily-evaluated method spec_commands. Alias + Override that in
|
78
|
+
# ~/.autotest to provide a different spec command then the default
|
79
|
+
# paths provided.
|
80
|
+
def spec_command(separator=File::ALT_SEPARATOR)
|
81
|
+
unless defined?(@spec_command)
|
82
|
+
@spec_command = spec_commands.find { |cmd| File.exists?(cmd) }
|
83
|
+
|
84
|
+
raise RspecCommandError, "No spec command could be found" unless @spec_command
|
85
|
+
|
86
|
+
@spec_command.gsub!(File::SEPARATOR, separator) if separator
|
87
|
+
end
|
88
|
+
@spec_command
|
89
|
+
end
|
90
|
+
|
91
|
+
# Autotest will look for spec commands in the following
|
92
|
+
# locations, in this order:
|
93
|
+
#
|
94
|
+
# * default spec bin/loader installed in Rubygems
|
95
|
+
# * any spec command found in PATH
|
96
|
+
def spec_commands
|
97
|
+
[File.join(Config::CONFIG['bindir'], 'spec'), 'spec']
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Runs +files_matching+ for all specs
|
103
|
+
def all_specs
|
104
|
+
files_matching %r%^spec/.*_spec\.rb$%
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
if defined?(Merb::Plugins)
|
2
|
+
dir = Pathname(__FILE__).dirname / 'merb_simple_views'
|
3
|
+
|
4
|
+
# Make Merb::RenderMixin's #render and #display methods chainable
|
5
|
+
#
|
6
|
+
# ==== Notes
|
7
|
+
# This is used as a (arguably) more elegant workaround to
|
8
|
+
# alias_method_chaining them.
|
9
|
+
#
|
10
|
+
# "We consider cases of people using alias_method_chain on Merb to be a bug in
|
11
|
+
# Merb, and try to find ways to expose enough functionality so it will not be
|
12
|
+
# required."
|
13
|
+
# -- http://yehudakatz.com/2008/05/22/the-greatest-thing-since-sliced-merb/
|
14
|
+
#
|
15
|
+
# Ideally those methods would be declared chainable within the RenderMixin
|
16
|
+
# itself and this monkey patch wouldn't be needed.
|
17
|
+
Merb::RenderMixin.module_eval do
|
18
|
+
[:render, :display].each do |name|
|
19
|
+
method = instance_method(name)
|
20
|
+
self.class.chainable { send(:define_method, name, method) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require dir / 'template_parser'
|
25
|
+
require dir / 'mixin'
|
26
|
+
|
27
|
+
# config options
|
28
|
+
Merb::Plugins.config[:simple_views] = {}
|
29
|
+
|
30
|
+
Merb::BootLoader.after_app_loads do
|
31
|
+
Merb::Controller.class_eval { include SimpleViews::Mixin }
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module SimpleViews
|
2
|
+
|
3
|
+
TEMPLATES = {}
|
4
|
+
|
5
|
+
# Adds SimpleViews functionality to a Merb controller, allowing templates
|
6
|
+
# defined in the controller's file to serve as views. See README for more.
|
7
|
+
module Mixin
|
8
|
+
attr_reader :_template_parser
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@_template_parser = SimpleViews::TemplateParser.new
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
# In addition to default #render behaviour, parse simple views defined in
|
16
|
+
# controller's file and render them when requested, or render external
|
17
|
+
# templates otherwise.
|
18
|
+
#
|
19
|
+
# ==== Notes
|
20
|
+
# 1. Turned into a chainable method in merb_simple_views.rb
|
21
|
+
# 2. Uses a VirtualFile to trick template engines.
|
22
|
+
#
|
23
|
+
# ==== Parameters
|
24
|
+
# See Merb::RenderMixin#render
|
25
|
+
#
|
26
|
+
# ==== Options
|
27
|
+
# See Merb::RenderMixin#render
|
28
|
+
#
|
29
|
+
# ==== Returns
|
30
|
+
# See Merb::RenderMixin#render
|
31
|
+
#
|
32
|
+
# ==== Raises
|
33
|
+
# See Merb::RenderMixin#render
|
34
|
+
#
|
35
|
+
# ==== Alternatives
|
36
|
+
# See Merb::RenderMixin#render
|
37
|
+
#
|
38
|
+
# :api: public
|
39
|
+
def render(*args)
|
40
|
+
tpls_file = (@__caller_info__ || __caller_info__).first.first
|
41
|
+
self._template_parser.load(tpls_file).parse.each do |template_name, raw_content|
|
42
|
+
# no controller name if absolute view path
|
43
|
+
ctrl_name = template_name.match(/^\//) ? nil : self.controller_name
|
44
|
+
path = Merb.dir_for(:view) / self._template_location(template_name.gsub(/^\//,''), nil, ctrl_name)
|
45
|
+
file = VirtualFile.new(raw_content, path)
|
46
|
+
TEMPLATES[path.to_s] = file
|
47
|
+
end
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
# Wrap #display and store caller info so that #render (called internally by
|
52
|
+
# #display) knows what file to fetch template data from.
|
53
|
+
#
|
54
|
+
# ==== Notes
|
55
|
+
# Turned into a chainable method in merb_simple_views.rb
|
56
|
+
#
|
57
|
+
# ==== Parameters
|
58
|
+
# See Merb::RenderMixin#display
|
59
|
+
#
|
60
|
+
# ==== Options
|
61
|
+
# See Merb::RenderMixin#display
|
62
|
+
#
|
63
|
+
# ==== Returns
|
64
|
+
# See Merb::RenderMixin#display
|
65
|
+
#
|
66
|
+
# ==== Raises
|
67
|
+
# See Merb::RenderMixin#display
|
68
|
+
#
|
69
|
+
# ==== Alternatives
|
70
|
+
# See Merb::RenderMixin#display
|
71
|
+
#
|
72
|
+
# :api: public
|
73
|
+
def display(*args)
|
74
|
+
@__caller_info__ = __caller_info__
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
module Merb::Template
|
81
|
+
def self.load_template_io(path)
|
82
|
+
super || template_extensions.map {|ext| SimpleViews::TEMPLATES["#{path}.#{ext}"] }.compact.first
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module SimpleViews
|
2
|
+
class TemplateParser
|
3
|
+
# templates as a string, directly extracted from the file.
|
4
|
+
attr_accessor :raw_templates
|
5
|
+
alias :raw :raw_templates
|
6
|
+
alias :raw= :raw_templates=
|
7
|
+
|
8
|
+
# templates as a {'name' => 'content'} hash
|
9
|
+
attr_accessor :parsed_templates
|
10
|
+
alias :parsed :raw_templates
|
11
|
+
alias :parsed= :raw_templates=
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
self.raw_templates = ''
|
15
|
+
self.parsed_templates = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Read a file and fetch its __END__ section, if any.
|
19
|
+
#
|
20
|
+
# ==== Parameters
|
21
|
+
# file<String,Pathname>:: File to find in-file templates in
|
22
|
+
#
|
23
|
+
# ==== Returns
|
24
|
+
# self
|
25
|
+
#
|
26
|
+
# :api: public
|
27
|
+
def load(file='')
|
28
|
+
file = Pathname(file)
|
29
|
+
if file.exist?
|
30
|
+
parts = file.read.split(/^__END__$/)
|
31
|
+
if parts.size > 1
|
32
|
+
self.raw_templates = parts[1].lstrip
|
33
|
+
end
|
34
|
+
end
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
# Parse templates string and extract template names and contents.
|
39
|
+
#
|
40
|
+
# Template names must be preceded with '@@', appear alone on their line,
|
41
|
+
# and follow the same conventions regular templates do. They can also be
|
42
|
+
# disabled by simply commenting out their name.
|
43
|
+
#
|
44
|
+
# ==== Parameters
|
45
|
+
# file<String,Pathname>:: File to find in-file templates in
|
46
|
+
#
|
47
|
+
# ==== Returns
|
48
|
+
# Hash:: Parsed templates as {'name' => 'content'}
|
49
|
+
#
|
50
|
+
# ==== Examples
|
51
|
+
#
|
52
|
+
# #posts_controller.rb
|
53
|
+
# [...]
|
54
|
+
# __END__
|
55
|
+
# @@ index.html.erb
|
56
|
+
# posts
|
57
|
+
#
|
58
|
+
# @@ show.html.erb
|
59
|
+
# post01
|
60
|
+
#
|
61
|
+
# #=> {'index.html.erb' => "posts", 'show.html.erb' => "post01"}
|
62
|
+
#
|
63
|
+
# To disable a template, comment out its name:
|
64
|
+
#
|
65
|
+
# __END__
|
66
|
+
# #@@ index.html.erb
|
67
|
+
# posts
|
68
|
+
#
|
69
|
+
# #=> {}
|
70
|
+
#
|
71
|
+
# ==== Notes
|
72
|
+
# Also stores the result in self.parsed_templates
|
73
|
+
#
|
74
|
+
# :api: public
|
75
|
+
def parse(file='')
|
76
|
+
self.load(file) if file
|
77
|
+
templates, current, ignore = {}, nil, false
|
78
|
+
self.raw_templates.each_line do |line|
|
79
|
+
if matches = line.strip.match(/^\s*(\#)*\s*@@\s*(.*)/)
|
80
|
+
ignore = (matches[1] && matches[1].match(/^#/)) and next #skip commented out templates
|
81
|
+
templates[current = matches[2]] = ''
|
82
|
+
elsif ignore
|
83
|
+
next
|
84
|
+
elsif current
|
85
|
+
templates[current] << line
|
86
|
+
end
|
87
|
+
end
|
88
|
+
# remove trailing witespace off every template body and template name
|
89
|
+
self.parsed_templates = templates.each {|key,value| templates[key.chomp.gsub(/[[:blank:]]|\t/,'')] = value.rstrip }
|
90
|
+
self.parsed_templates
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/spec/mixin_spec.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "Simple Views" do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
Merb.root.should == Pathname(__FILE__).dirname.parent.expand_path.to_s
|
7
|
+
views_dir = Pathname(Merb.root / "spec/fixtures/views")
|
8
|
+
views_dir.mkdir unless views_dir.directory?
|
9
|
+
Merb.push_path(:view, views_dir, "**/*")
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
class Posts < Merb::Controller
|
14
|
+
def index() render end
|
15
|
+
def edit() render end
|
16
|
+
def quote() render end
|
17
|
+
def nohaz() render end
|
18
|
+
def random() render end
|
19
|
+
def show
|
20
|
+
obj = Object.new; def obj.to_html() '<b>x</b>' end
|
21
|
+
display(obj)
|
22
|
+
end
|
23
|
+
def new
|
24
|
+
obj = Object.new; def obj.to_html() '<b>y</b>' end
|
25
|
+
display(obj)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
class Topics < Merb::Controller
|
29
|
+
def index() render end
|
30
|
+
def quote() render end
|
31
|
+
end
|
32
|
+
# -----
|
33
|
+
@posts = Posts.new(fake_request)
|
34
|
+
@topics = Topics.new(fake_request)
|
35
|
+
# -----
|
36
|
+
clean_view_dir!
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should be injected in Merb::Controller" do
|
40
|
+
Merb::Controller.included_modules.should include(SimpleViews::Mixin)
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "parsing templates" do
|
44
|
+
|
45
|
+
it "should parse in-file templates on render" do
|
46
|
+
@posts._dispatch(:index)
|
47
|
+
@posts._template_parser.parsed_templates['index.html.erb'].should == "kittehs"
|
48
|
+
@posts.body.should == "kittehs"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should parse in-file templates on display" do
|
52
|
+
@posts._dispatch(:show)
|
53
|
+
@posts._template_parser.parsed_templates['show.html.erb'].should == "kitteh-01"
|
54
|
+
@posts.body.should == "kitteh-01"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should parse absolute paths" do
|
58
|
+
@posts._dispatch(:quote)
|
59
|
+
@posts._template_parser.parsed_templates['/posts/quote.html.erb'].should == "<i>kitteh-01</i>"
|
60
|
+
@posts.body.should == "<i>kitteh-01</i>"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should let template engines parse views" do
|
64
|
+
@posts._dispatch(:random)
|
65
|
+
@posts._template_parser.parsed_templates['/posts/random.html.erb'].should == "kitteh-<%= '07' %>"
|
66
|
+
@posts.body.should == "kitteh-07"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should respect changes to #_template_location" do
|
70
|
+
def @posts._template_location(context, type=nil, controller=controller_name)
|
71
|
+
"#{controller}.#{action_name}.#{type}"
|
72
|
+
end
|
73
|
+
@posts._dispatch(:index)
|
74
|
+
@posts.body.should == "tiny kittehs"
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "with template reloading" do
|
78
|
+
|
79
|
+
it "should reload templates when code reloading is on" do
|
80
|
+
Merb::Config[:reload_templates] = true
|
81
|
+
# -----
|
82
|
+
@posts._dispatch(:index)
|
83
|
+
@posts.body.should == "kittehs"
|
84
|
+
# -----
|
85
|
+
@posts._template_parser.stub!(:parsed_templates).and_return({'index.html.erb' => 'swapped'})
|
86
|
+
# -----
|
87
|
+
@posts._dispatch(:index)
|
88
|
+
@posts.body.should == "swapped"
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should not reload templates when code reloading is off" do
|
92
|
+
Merb::Config[:reload_templates] = false
|
93
|
+
# -----
|
94
|
+
@posts._dispatch(:index)
|
95
|
+
@posts.body.should == "kittehs"
|
96
|
+
# -----
|
97
|
+
@posts._template_parser.stub!(:parsed_templates).and_return({'index.html.erb' => 'swapped'})
|
98
|
+
# -----
|
99
|
+
@posts._dispatch(:index)
|
100
|
+
@posts.body.should == "kittehs"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "falling back to default behaviour" do
|
105
|
+
|
106
|
+
it "should delegate to regular render control flow when no in-file template exists" do
|
107
|
+
write_template('edit.html.erb', "<i>ohaie!</i>", @posts)
|
108
|
+
@posts._dispatch(:edit)
|
109
|
+
@posts.body.should == "<i>ohaie!</i>"
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should delegate to regular display control flow when no in-file template exists" do
|
113
|
+
@posts._dispatch(:new)
|
114
|
+
@posts.body.should == "<b>y</b>"
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should raise exception when template is not found" do
|
118
|
+
lambda {
|
119
|
+
@posts._dispatch(:nohaz)
|
120
|
+
}.should raise_error(Merb::ControllerExceptions::TemplateNotFound)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "with multiple controllers" do
|
125
|
+
|
126
|
+
it "should pick up templates from the same file" do
|
127
|
+
@posts._dispatch(:quote)
|
128
|
+
@posts.body.should == "<i>kitteh-01</i>"
|
129
|
+
@topics._dispatch(:quote)
|
130
|
+
@topics.body.should == "<i>ceilingcat</i>"
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should allow different controllers to use the same, relative template" do
|
134
|
+
@posts._dispatch(:index)
|
135
|
+
@posts.body.should == "kittehs"
|
136
|
+
@topics._dispatch(:index)
|
137
|
+
@topics.body.should == "kittehs"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
__END__
|
144
|
+
@@ index.html.erb
|
145
|
+
kittehs
|
146
|
+
|
147
|
+
@@ show.html.erb
|
148
|
+
kitteh-01
|
149
|
+
|
150
|
+
@@ /posts/random.html.erb
|
151
|
+
kitteh-<%= '07' %>
|
152
|
+
|
153
|
+
@@ /posts/quote.html.erb
|
154
|
+
<i>kitteh-01</i>
|
155
|
+
|
156
|
+
@@ /topics/quote.html.erb
|
157
|
+
<i>ceilingcat</i>
|
158
|
+
|
159
|
+
@@ /posts.index.html.erb
|
160
|
+
tiny kittehs
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
$:.push File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'spec'
|
5
|
+
require 'merb-core'
|
6
|
+
require 'merb_simple_views'
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'ruby-debug'
|
10
|
+
rescue LoadError, RuntimeError
|
11
|
+
# i can haz dibugz plz?
|
12
|
+
end
|
13
|
+
|
14
|
+
Merb.start :environment => 'test', :adapter => 'runner'
|
15
|
+
|
16
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
17
|
+
# Core Extensions
|
18
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
19
|
+
# This might cause errors to slip into specs, but it beats using fixtures.
|
20
|
+
class String
|
21
|
+
def unindent
|
22
|
+
indent = self.select {|line| !line.strip.empty? }.map {|line| line.index(/[^\s]/) }.compact.min
|
23
|
+
self.gsub(/^[[:blank:]]{#{indent}}/, '')
|
24
|
+
end
|
25
|
+
def unindent!
|
26
|
+
self.replace(self.unindent)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
31
|
+
# Helpers
|
32
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
33
|
+
module Helpers
|
34
|
+
def write_template(name, content, controller)
|
35
|
+
view_path = Merb.dir_for(:view) / controller.controller_name / name
|
36
|
+
view_path.dirname.mkdir
|
37
|
+
Kernel.open(view_path, 'w+') {|file| file.write(content) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def clean_view_dir!
|
41
|
+
FileUtils.rm_rf( Pathname.glob(Merb.dir_for(:view) / "*") )
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
46
|
+
# Config
|
47
|
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
48
|
+
Spec::Runner.configure do |config|
|
49
|
+
config.include Merb::Test::RequestHelper
|
50
|
+
config.include Helpers
|
51
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "Parser" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@parser = SimpleViews::TemplateParser.new.load(__FILE__)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should load in-file templates" do
|
10
|
+
@parser.raw_templates.should match(/ohaie/)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "when parsing template strings" do
|
14
|
+
|
15
|
+
it "should split templates at @@ marks" do
|
16
|
+
@parser.raw = %|
|
17
|
+
@@ index
|
18
|
+
kittehs
|
19
|
+
@@ show
|
20
|
+
kitteh1
|
21
|
+
|.unindent
|
22
|
+
@parser.parse.size.should == 2
|
23
|
+
@parser.parse['index'].strip.should == 'kittehs'
|
24
|
+
@parser.parse['show'].strip.should == 'kitteh1'
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should remove trailing witespace off every template" do
|
28
|
+
@parser.raw = %|
|
29
|
+
@@ index
|
30
|
+
|
31
|
+
kittehs \t\n
|
32
|
+
|.unindent
|
33
|
+
@parser.parse['index'].should_not match(/\t\n$/)
|
34
|
+
@parser.parse['index'].should match(/^\n/)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should not be bothered by amount of spaces or tabs between marker and template name" do
|
38
|
+
@parser.raw = %|
|
39
|
+
@@index
|
40
|
+
kittehs
|
41
|
+
@@\t\sshow
|
42
|
+
kitteh1
|
43
|
+
|.unindent
|
44
|
+
@parser.parse['index'].should_not be_nil
|
45
|
+
@parser.parse['show'].should_not be_nil
|
46
|
+
@parser.parse["\t\sshow"].should be_nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
__END__
|
52
|
+
ohaie
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mynyml-merb_simple_views
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Aumont
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-14 21:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: merb-core >= 1.0
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: Merb plugin that allows defining templates (views, css, js)in the same file as the controller.
|
26
|
+
email: mynyml@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- CHANGELOG
|
35
|
+
- log
|
36
|
+
- lib
|
37
|
+
- lib/merb_simple_views.rb
|
38
|
+
- lib/merb_simple_views
|
39
|
+
- lib/merb_simple_views/mixin.bk.rb
|
40
|
+
- lib/merb_simple_views/cache.rb
|
41
|
+
- lib/merb_simple_views/cache.bk.rb
|
42
|
+
- lib/merb_simple_views/merbtasks.rb
|
43
|
+
- lib/merb_simple_views/merbtasks.bk.rb
|
44
|
+
- lib/merb_simple_views/mixin.rb
|
45
|
+
- lib/merb_simple_views/template_parser.rb
|
46
|
+
- lib/merb_simple_views.bk.rb
|
47
|
+
- LICENSE
|
48
|
+
- README
|
49
|
+
- Rakefile
|
50
|
+
- MYTODO
|
51
|
+
- draft
|
52
|
+
- TODO
|
53
|
+
- autotest
|
54
|
+
- autotest/rspec_simpleviews.rb
|
55
|
+
- autotest/discover.rb
|
56
|
+
- autotest/rspec_simpleviews.bk.rb
|
57
|
+
- spec
|
58
|
+
- spec/spec_helper.rb
|
59
|
+
- spec/mixin_spec.bk.rb
|
60
|
+
- spec/mixin_spec.rb
|
61
|
+
- spec/fixtures
|
62
|
+
- spec/fixtures/views
|
63
|
+
- spec/fixtures/controller3.rb
|
64
|
+
- spec/spec.opts
|
65
|
+
- spec/template_parser_spec.rb
|
66
|
+
has_rdoc: true
|
67
|
+
homepage: ""
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
version:
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.2.0
|
89
|
+
signing_key:
|
90
|
+
specification_version: 2
|
91
|
+
summary: Merb plugin that allows defining templates (views, css, js)in the same file as the controller.
|
92
|
+
test_files: []
|
93
|
+
|