reactive-mvc 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/Manifest +34 -0
- data/README +30 -0
- data/Rakefile +5 -0
- data/lib/reactive-mvc.rb +26 -0
- data/lib/reactive-mvc/controller.rb +16 -0
- data/lib/reactive-mvc/controller/base.rb +405 -0
- data/lib/reactive-mvc/controller/filters.rb +767 -0
- data/lib/reactive-mvc/controller/flash.rb +161 -0
- data/lib/reactive-mvc/controller/helpers.rb +203 -0
- data/lib/reactive-mvc/controller/layout.rb +285 -0
- data/lib/reactive-mvc/controller/output.rb +262 -0
- data/lib/reactive-mvc/controller/rescue.rb +208 -0
- data/lib/reactive-mvc/dispatcher.rb +133 -0
- data/lib/reactive-mvc/view.rb +18 -0
- data/lib/reactive-mvc/view/base.rb +388 -0
- data/lib/reactive-mvc/view/helpers.rb +38 -0
- data/lib/reactive-mvc/view/partials.rb +207 -0
- data/lib/reactive-mvc/view/paths.rb +125 -0
- data/lib/reactive-mvc/view/renderable.rb +98 -0
- data/lib/reactive-mvc/view/renderable_partial.rb +49 -0
- data/lib/reactive-mvc/view/template.rb +110 -0
- data/lib/reactive-mvc/view/template_error.rb +9 -0
- data/lib/reactive-mvc/view/template_handler.rb +9 -0
- data/lib/reactive-mvc/view/template_handlers.rb +43 -0
- data/lib/reactive-mvc/view/template_handlers/builder.rb +15 -0
- data/lib/reactive-mvc/view/template_handlers/erb.rb +20 -0
- data/lib/reactive-mvc/view/template_handlers/ruby_code.rb +9 -0
- data/reactive_app_generators/mvc/USAGE +10 -0
- data/reactive_app_generators/mvc/mvc_generator.rb +48 -0
- data/reactive_app_generators/mvc/templates/application_controller.rb +2 -0
- data/reactive_generators/controller/USAGE +7 -0
- data/reactive_generators/controller/controller_generator.rb +24 -0
- data/reactive_generators/controller/templates/controller.rb +2 -0
- metadata +113 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
module Reactive
|
2
|
+
module Mvc
|
3
|
+
|
4
|
+
class Dispatcher < Reactive::Dispatcher::Base
|
5
|
+
class << self
|
6
|
+
def candidate?(request)
|
7
|
+
request.params[:dispatcher] == :mvc
|
8
|
+
end
|
9
|
+
def instance_for(request)
|
10
|
+
@@singleton ||= new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def dispatch(request)
|
15
|
+
self.request = request
|
16
|
+
controller = recognize(request).new
|
17
|
+
self.response = Response.new
|
18
|
+
|
19
|
+
log_processing
|
20
|
+
controller.process(request, response)
|
21
|
+
|
22
|
+
handle_response
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def recognize(request)
|
28
|
+
if request.params[:controller].nil? && request.params[:asset]
|
29
|
+
AssetsController
|
30
|
+
else
|
31
|
+
if request.params[:controller].nil? && request.params[:model]
|
32
|
+
model_to_params(request.params)
|
33
|
+
end
|
34
|
+
"#{request.params[:controller].camelize}Controller".constantize
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def model_to_params(params)
|
39
|
+
record = params.delete(:model)
|
40
|
+
name =record.class.to_s
|
41
|
+
params[:controller] = name.demodulize.underscore.pluralize
|
42
|
+
# determine the action
|
43
|
+
params[:action] = if params[:action].to_s == 'save'
|
44
|
+
record.new_record? ? :create : :update
|
45
|
+
elsif params[:action]
|
46
|
+
params[:action]
|
47
|
+
else
|
48
|
+
if record.supra.new_record? then :create
|
49
|
+
elsif record.supra.changed? then :update
|
50
|
+
elsif record.supra.marked_for_destruction? then :destroy
|
51
|
+
else :show
|
52
|
+
end
|
53
|
+
end
|
54
|
+
# setup params according to the action
|
55
|
+
case params[:action]
|
56
|
+
when :create then params.merge!(name.underscore => model_to_hash(record))
|
57
|
+
when :update then params.merge!(:id => record.id, name.underscore => model_to_hash(record))
|
58
|
+
when :destroy then params.merge!(:id => record.id)
|
59
|
+
when :show then params.merge!(:id => record.id)
|
60
|
+
else raise ArgumentError, "unknown action #{params[:action]}"
|
61
|
+
end
|
62
|
+
params
|
63
|
+
end
|
64
|
+
|
65
|
+
def model_to_hash(record)
|
66
|
+
hash = record.class.meta.columns.inject({}) {|memo, attr| memo.merge!(attr.name => record.send(attr.name))}
|
67
|
+
hash[:id] = record.id unless record.new_record?
|
68
|
+
hash.merge!(has_manys_to_hash(record))
|
69
|
+
hash.merge!(has_ones_to_hash(record))
|
70
|
+
hash.merge!(belong_tos_to_hash(record))
|
71
|
+
end
|
72
|
+
|
73
|
+
def has_manys_to_hash(record)
|
74
|
+
record.class.meta.has_manys.inject({}) do |memo, assoc_meta|
|
75
|
+
modified = record.send(assoc_meta.name).collect {|assoc| has_any_to_attributes(assoc)}.compact
|
76
|
+
modified.empty? ? memo : memo.merge!("#{assoc_meta.name}_attributes".to_sym => modified)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def has_ones_to_hash(record)
|
81
|
+
record.class.meta.has_ones.inject({}) do |memo, assoc_meta|
|
82
|
+
assoc = record.send(assoc_meta.name)
|
83
|
+
attrs = assoc && has_any_to_attributes(assoc)
|
84
|
+
attrs ? memo.merge!("#{assoc_meta.name}_attributes".to_sym => attrs) : memo
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def belong_tos_to_hash(record)
|
89
|
+
record.class.meta.belong_tos.inject({}) do |memo, assoc_meta|
|
90
|
+
assoc = record.send(assoc_meta.name)
|
91
|
+
if assoc && (attrs = has_any_to_attributes(assoc))
|
92
|
+
memo.merge!("#{assoc_meta.name}_attributes".to_sym => attrs)
|
93
|
+
else
|
94
|
+
memo.merge!("#{assoc_meta.name}_id".to_sym => assoc && assoc.id)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def has_any_to_attributes(assoc)
|
100
|
+
# changed? will catch modified records as well as new records. It even furthers to not catch new records that have not yet been filled, that's great.
|
101
|
+
if assoc.changed?
|
102
|
+
model_to_hash(assoc)
|
103
|
+
elsif assoc.marked_for_destruction?
|
104
|
+
{:_delete => true, :id => assoc.id}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
class AssetsController
|
111
|
+
def process(request, response)
|
112
|
+
# Assets do not need any handling
|
113
|
+
response.handler_name = false
|
114
|
+
|
115
|
+
name = request.params[:asset]
|
116
|
+
glob_path = if request.params[:kind] == :ui
|
117
|
+
# construct a path like: {client/}name{_size}.*
|
118
|
+
client, size, path = request.params[:client], request.params[:size], ""
|
119
|
+
path << "{#{client}/,}" if client
|
120
|
+
path << name
|
121
|
+
path << "{_#{size},}" if size
|
122
|
+
path << ".*"
|
123
|
+
path
|
124
|
+
else
|
125
|
+
name
|
126
|
+
end
|
127
|
+
response.body = Reactive.file_for(:assets, glob_path) or raise Reactive::Dispatcher::AssetNotFound, "Can't find '#{glob_path}' in #{Reactive.dirs_for(:assets).join(', ')}"
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'reactive-mvc/view/template_handlers'
|
2
|
+
require 'reactive-mvc/view/renderable'
|
3
|
+
require 'reactive-mvc/view/renderable_partial'
|
4
|
+
|
5
|
+
require 'reactive-mvc/view/template'
|
6
|
+
#require 'view/inline_template'
|
7
|
+
require 'reactive-mvc/view/paths'
|
8
|
+
|
9
|
+
require 'reactive-mvc/view/base'
|
10
|
+
require 'reactive-mvc/view/partials'
|
11
|
+
require 'reactive-mvc/view/template_error'
|
12
|
+
|
13
|
+
require 'reactive-mvc/view/helpers'
|
14
|
+
|
15
|
+
Reactive::Mvc::View::Base.class_eval do
|
16
|
+
include Reactive::Mvc::View::Partials
|
17
|
+
include Reactive::Mvc::View::Helpers
|
18
|
+
end
|
@@ -0,0 +1,388 @@
|
|
1
|
+
module Reactive::Mvc
|
2
|
+
module View #:nodoc:
|
3
|
+
class Error < Reactive::Error #:nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
class MissingTemplate < Error #:nodoc:
|
7
|
+
def initialize(paths, path, template_format = nil)
|
8
|
+
full_template_path = path.include?('.') ? path : "#{path}.*"
|
9
|
+
display_paths = paths.join(':')
|
10
|
+
template_type = (path =~ /layouts/i) ? 'layout' : 'template'
|
11
|
+
super("Missing #{template_type} #{full_template_path} in view path #{display_paths}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
|
16
|
+
# (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used.
|
17
|
+
# If the template file has a <tt>.rjs</tt> extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
|
18
|
+
#
|
19
|
+
# = ERb
|
20
|
+
#
|
21
|
+
# You trigger ERb by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
|
22
|
+
# following loop for names:
|
23
|
+
#
|
24
|
+
# <b>Names of all the people</b>
|
25
|
+
# <% for person in @people %>
|
26
|
+
# Name: <%= person.name %><br/>
|
27
|
+
# <% end %>
|
28
|
+
#
|
29
|
+
# The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
|
30
|
+
# is not just a usage suggestion. Regular output functions like print or puts won't work with ERb templates. So this would be wrong:
|
31
|
+
#
|
32
|
+
# Hi, Mr. <% puts "Frodo" %>
|
33
|
+
#
|
34
|
+
# If you absolutely must write from within a function, you can use the TextHelper#concat.
|
35
|
+
#
|
36
|
+
# <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
|
37
|
+
#
|
38
|
+
# == Using sub templates
|
39
|
+
#
|
40
|
+
# Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
|
41
|
+
# classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
|
42
|
+
#
|
43
|
+
# <%= render "shared/header" %>
|
44
|
+
# Something really specific and terrific
|
45
|
+
# <%= render "shared/footer" %>
|
46
|
+
#
|
47
|
+
# As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
|
48
|
+
# result of the rendering. The output embedding writes it to the current template.
|
49
|
+
#
|
50
|
+
# But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
|
51
|
+
# variables defined using the regular embedding tags. Like this:
|
52
|
+
#
|
53
|
+
# <% @page_title = "A Wonderful Hello" %>
|
54
|
+
# <%= render "shared/header" %>
|
55
|
+
#
|
56
|
+
# Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
|
57
|
+
#
|
58
|
+
# <title><%= @page_title %></title>
|
59
|
+
#
|
60
|
+
# == Passing local variables to sub templates
|
61
|
+
#
|
62
|
+
# You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
|
63
|
+
#
|
64
|
+
# <%= render "shared/header", { :headline => "Welcome", :person => person } %>
|
65
|
+
#
|
66
|
+
# These can now be accessed in <tt>shared/header</tt> with:
|
67
|
+
#
|
68
|
+
# Headline: <%= headline %>
|
69
|
+
# First name: <%= person.first_name %>
|
70
|
+
#
|
71
|
+
# If you need to find out whether a certain local variable has been assigned a value in a particular render call,
|
72
|
+
# you need to use the following pattern:
|
73
|
+
#
|
74
|
+
# <% if local_assigns.has_key? :headline %>
|
75
|
+
# Headline: <%= headline %>
|
76
|
+
# <% end %>
|
77
|
+
#
|
78
|
+
# Testing using <tt>defined? headline</tt> will not work. This is an implementation restriction.
|
79
|
+
#
|
80
|
+
# == Template caching
|
81
|
+
#
|
82
|
+
# By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will
|
83
|
+
# check the file's modification time and recompile it.
|
84
|
+
#
|
85
|
+
# == Builder
|
86
|
+
#
|
87
|
+
# Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An XmlMarkup object
|
88
|
+
# named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
|
89
|
+
#
|
90
|
+
# Here are some basic examples:
|
91
|
+
#
|
92
|
+
# xml.em("emphasized") # => <em>emphasized</em>
|
93
|
+
# xml.em { xml.b("emph & bold") } # => <em><b>emph & bold</b></em>
|
94
|
+
# xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
|
95
|
+
# xml.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\>
|
96
|
+
# # NOTE: order of attributes is not specified.
|
97
|
+
#
|
98
|
+
# Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
|
99
|
+
#
|
100
|
+
# xml.div {
|
101
|
+
# xml.h1(@person.name)
|
102
|
+
# xml.p(@person.bio)
|
103
|
+
# }
|
104
|
+
#
|
105
|
+
# would produce something like:
|
106
|
+
#
|
107
|
+
# <div>
|
108
|
+
# <h1>David Heinemeier Hansson</h1>
|
109
|
+
# <p>A product of Danish Design during the Winter of '79...</p>
|
110
|
+
# </div>
|
111
|
+
#
|
112
|
+
# A full-length RSS example actually used on Basecamp:
|
113
|
+
#
|
114
|
+
# xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
|
115
|
+
# xml.channel do
|
116
|
+
# xml.title(@feed_title)
|
117
|
+
# xml.link(@url)
|
118
|
+
# xml.description "Basecamp: Recent items"
|
119
|
+
# xml.language "en-us"
|
120
|
+
# xml.ttl "40"
|
121
|
+
#
|
122
|
+
# for item in @recent_items
|
123
|
+
# xml.item do
|
124
|
+
# xml.title(item_title(item))
|
125
|
+
# xml.description(item_description(item)) if item_description(item)
|
126
|
+
# xml.pubDate(item_pubDate(item))
|
127
|
+
# xml.guid(@person.firm.account.url + @recent_items.url(item))
|
128
|
+
# xml.link(@person.firm.account.url + @recent_items.url(item))
|
129
|
+
#
|
130
|
+
# xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
|
131
|
+
# end
|
132
|
+
# end
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# More builder documentation can be found at http://builder.rubyforge.org.
|
137
|
+
#
|
138
|
+
# == JavaScriptGenerator
|
139
|
+
#
|
140
|
+
# JavaScriptGenerator templates end in <tt>.rjs</tt>. Unlike conventional templates which are used to
|
141
|
+
# render the results of an action, these templates generate instructions on how to modify an already rendered page. This makes it easy to
|
142
|
+
# modify multiple elements on your page in one declarative Ajax response. Actions with these templates are called in the background with Ajax
|
143
|
+
# and make updates to the page where the request originated from.
|
144
|
+
#
|
145
|
+
# An instance of the JavaScriptGenerator object named +page+ is automatically made available to your template, which is implicitly wrapped in an ActionView::Helpers::PrototypeHelper#update_page block.
|
146
|
+
#
|
147
|
+
# When an <tt>.rjs</tt> action is called with +link_to_remote+, the generated JavaScript is automatically evaluated. Example:
|
148
|
+
#
|
149
|
+
# link_to_remote :url => {:action => 'delete'}
|
150
|
+
#
|
151
|
+
# The subsequently rendered <tt>delete.rjs</tt> might look like:
|
152
|
+
#
|
153
|
+
# page.replace_html 'sidebar', :partial => 'sidebar'
|
154
|
+
# page.remove "person-#{@person.id}"
|
155
|
+
# page.visual_effect :highlight, 'user-list'
|
156
|
+
#
|
157
|
+
# This refreshes the sidebar, removes a person element and highlights the user list.
|
158
|
+
#
|
159
|
+
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
|
160
|
+
class Base
|
161
|
+
extend ActiveSupport::Memoizable
|
162
|
+
|
163
|
+
attr_accessor :base_path, :assigns, :template_extension
|
164
|
+
attr_accessor :controller
|
165
|
+
|
166
|
+
attr_writer :template_format
|
167
|
+
|
168
|
+
attr_accessor :output_buffer
|
169
|
+
|
170
|
+
cattr_accessor :logger
|
171
|
+
|
172
|
+
class << self
|
173
|
+
delegate :erb_trim_mode=, :to => 'Reactive::Mvc::View::TemplateHandlers::ERB'
|
174
|
+
|
175
|
+
def handle_request(request)
|
176
|
+
Dispatcher.dispatch(request)
|
177
|
+
end
|
178
|
+
|
179
|
+
def register_format_handler(format, handler)
|
180
|
+
@@format_handlers[format.to_sym] = handler
|
181
|
+
end
|
182
|
+
|
183
|
+
def format_handler_for(format)
|
184
|
+
@@format_handlers[format.to_sym]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# TODO: format_handlers should be local to each descendant! not shared amongs all the hierarchy (because same format may be handled by different handlers in children classes)
|
189
|
+
@@format_handlers = {}
|
190
|
+
|
191
|
+
# Templates that are exempt from layouts
|
192
|
+
@@exempt_from_layout = Set.new([/\.rjs$/])
|
193
|
+
|
194
|
+
# Don't render layouts for templates with the given extensions.
|
195
|
+
def self.exempt_from_layout(*extensions)
|
196
|
+
regexps = extensions.collect do |extension|
|
197
|
+
extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
|
198
|
+
end
|
199
|
+
@@exempt_from_layout.merge(regexps)
|
200
|
+
end
|
201
|
+
|
202
|
+
# A warning will be displayed whenever an action results in a cache miss on your view paths.
|
203
|
+
@@warn_cache_misses = false
|
204
|
+
cattr_accessor :warn_cache_misses
|
205
|
+
|
206
|
+
#attr_internal :request # will be filled by the magics: _copy_ivars_from_controller
|
207
|
+
|
208
|
+
delegate :template, :params, :request, :response,
|
209
|
+
:flash, :logger, :action_name, :controller_name, :to => :controller
|
210
|
+
|
211
|
+
module CompiledTemplates #:nodoc:
|
212
|
+
# holds compiled template code
|
213
|
+
end
|
214
|
+
include CompiledTemplates
|
215
|
+
|
216
|
+
def self.process_view_paths(value)
|
217
|
+
Reactive::Mvc::View::PathSet.new(Array(value))
|
218
|
+
end
|
219
|
+
|
220
|
+
attr_reader :helpers
|
221
|
+
|
222
|
+
class ProxyModule < Module
|
223
|
+
def initialize(receiver)
|
224
|
+
@receiver = receiver
|
225
|
+
end
|
226
|
+
|
227
|
+
def include(*args)
|
228
|
+
super(*args)
|
229
|
+
@receiver.extend(*args)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def initialize(view_paths = [], controller = nil)#:nodoc:
|
234
|
+
@assigns = {}
|
235
|
+
@assigns_added = nil
|
236
|
+
@_render_stack = []
|
237
|
+
@controller = controller
|
238
|
+
@helpers = ProxyModule.new(self)
|
239
|
+
self.view_paths = view_paths
|
240
|
+
end
|
241
|
+
|
242
|
+
attr_reader :view_paths
|
243
|
+
|
244
|
+
def view_paths=(paths)
|
245
|
+
@view_paths = self.class.process_view_paths(paths)
|
246
|
+
end
|
247
|
+
|
248
|
+
# Renders the template present at <tt>template_path</tt> (relative to the view_paths array).
|
249
|
+
# The hash in <tt>local_assigns</tt> is made available as local variables.
|
250
|
+
def render(options = {}, local_assigns = {}, &block) #:nodoc:
|
251
|
+
local_assigns ||= {}
|
252
|
+
|
253
|
+
if options.is_a?(String)
|
254
|
+
render(:file => options, :locals => local_assigns)
|
255
|
+
elsif options.is_a?(Hash)
|
256
|
+
options = options.reverse_merge(:locals => {})
|
257
|
+
if options[:layout]
|
258
|
+
_render_with_layout(options, local_assigns, &block)
|
259
|
+
elsif options[:file]
|
260
|
+
_pick_template(options[:file]).render_template(self, options[:locals])
|
261
|
+
elsif options[:partial]
|
262
|
+
render_partial(options)
|
263
|
+
# elsif options[:inline]
|
264
|
+
# InlineTemplate.new(options[:inline], options[:type]).render(self, options[:locals])
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# The format to be used when choosing between multiple templates with
|
270
|
+
# the same name but differing formats. See +Request#template_format+
|
271
|
+
# for more details.
|
272
|
+
def template_format
|
273
|
+
if defined? @template_format
|
274
|
+
@template_format
|
275
|
+
elsif controller && controller.respond_to?(:request)
|
276
|
+
@template_format = controller.request.format
|
277
|
+
else
|
278
|
+
@template_format = default_template_format
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def default_template_format
|
283
|
+
:rb
|
284
|
+
end
|
285
|
+
|
286
|
+
# Access the current template being rendered.
|
287
|
+
# Returns a ActionView::Template object.
|
288
|
+
def template
|
289
|
+
@_render_stack.last
|
290
|
+
end
|
291
|
+
|
292
|
+
def template_exists?(template_path)
|
293
|
+
_pick_template(template_path) ? true : false
|
294
|
+
rescue MissingTemplate
|
295
|
+
false
|
296
|
+
end
|
297
|
+
|
298
|
+
private
|
299
|
+
# Evaluates the local assigns and controller ivars, pushes them to the view.
|
300
|
+
def _evaluate_assigns_and_ivars #:nodoc:
|
301
|
+
unless @assigns_added
|
302
|
+
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
|
303
|
+
_copy_ivars_from_controller
|
304
|
+
@assigns_added = true
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def _copy_ivars_from_controller #:nodoc:
|
309
|
+
if @controller
|
310
|
+
variables = @controller.instance_variable_names
|
311
|
+
variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
|
312
|
+
variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def _pick_template(template_path)
|
317
|
+
return template_path if template_path.respond_to?(:render)
|
318
|
+
|
319
|
+
path = template_path.sub(/^\//, '')
|
320
|
+
if m = path.match(/(.*)\.(\w+)$/)
|
321
|
+
template_file_name, template_file_extension = m[1], m[2]
|
322
|
+
else
|
323
|
+
template_file_name = path
|
324
|
+
end
|
325
|
+
|
326
|
+
# OPTIMIZE: Checks to lookup template in view path
|
327
|
+
if template = self.view_paths["#{template_file_name}.#{template_format}"]
|
328
|
+
template
|
329
|
+
elsif template = self.view_paths[template_file_name]
|
330
|
+
template
|
331
|
+
elsif (first_render = @_render_stack.first) && first_render.respond_to?(:format_and_extension) &&
|
332
|
+
(template = self.view_paths["#{template_file_name}.#{first_render.format_and_extension}"])
|
333
|
+
template
|
334
|
+
elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"]
|
335
|
+
@template_format = :html
|
336
|
+
template
|
337
|
+
else
|
338
|
+
template = Template.new(template_path, view_paths)
|
339
|
+
|
340
|
+
if self.class.warn_cache_misses && logger
|
341
|
+
logger.debug "[PERFORMANCE] Rendering a template that was " +
|
342
|
+
"not found in view path. Templates outside the view path are " +
|
343
|
+
"not cached and result in expensive disk operations. Move this " +
|
344
|
+
"file into #{view_paths.join(':')} or add the folder to your " +
|
345
|
+
"view path list"
|
346
|
+
end
|
347
|
+
|
348
|
+
template
|
349
|
+
end
|
350
|
+
end
|
351
|
+
memoize :_pick_template
|
352
|
+
|
353
|
+
def _exempt_from_layout?(template_path) #:nodoc:
|
354
|
+
template = _pick_template(template_path).to_s
|
355
|
+
@@exempt_from_layout.any? { |ext| template =~ ext }
|
356
|
+
rescue MissingTemplate
|
357
|
+
return false
|
358
|
+
end
|
359
|
+
|
360
|
+
def _render_with_layout(options, local_assigns, &block) #:nodoc:
|
361
|
+
partial_layout = options.delete(:layout)
|
362
|
+
|
363
|
+
if block_given?
|
364
|
+
begin
|
365
|
+
@_proc_for_layout = block
|
366
|
+
concat(render(options.merge(:partial => partial_layout)))
|
367
|
+
ensure
|
368
|
+
@_proc_for_layout = nil
|
369
|
+
end
|
370
|
+
else
|
371
|
+
begin
|
372
|
+
original_content_for_layout = @content_for_layout if defined?(@content_for_layout)
|
373
|
+
@content_for_layout = render(options)
|
374
|
+
|
375
|
+
if (options[:inline] || options[:file] || options[:text])
|
376
|
+
@cached_content_for_layout = @content_for_layout
|
377
|
+
render(:file => partial_layout, :locals => local_assigns)
|
378
|
+
else
|
379
|
+
render(options.merge(:partial => partial_layout))
|
380
|
+
end
|
381
|
+
ensure
|
382
|
+
@content_for_layout = original_content_for_layout
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|