Access to request object from within tags (answer?)


#1

Well, I spoke 5 minutes too soon. I tried a few more things and found
out how to get it to work - now this may be completely wrong on a
number of levels but this got me the request within snippets that
previously had a nil request.

Does anyone know any ramifications of doing this? I haven’t yet
checked if it will break caching… we’ll see about that.

I notated the lines I changed or added with a <----

In page_context.rb

def initialize(page, request)
super()

@page = page
globals.request = request <----
globals.page = @page

… …

define_tag ‘snippet’ do |tag|
if name = tag.attr[‘name’]
if snippet = Snippet.find_by_name(name.strip)
page = tag.locals.page
page.behavior.request = tag.globals.request <----
page.behavior.render_snippet(snippet)
else
raise TagError.new(‘snippet not found’)
end
else
raise TagError.new(“snippet' tag must containname’ attribute”)
end
end

behavior.rb

def lazy_initialize_parser_and_context
unless @context and @parser
@context = PageContext.new(@page, @request) <----
self.class.additional_tag_definition_blocks.each { |block|
instance_eval &block }
add_tags_from_parent_to_context
@parser = Radius::Parser.new(@context, :tag_prefix => ‘r’)
end
end


#2

Kaleb,

Interesting technique. I don’t think you have to worry about it
breaking
caching. If your page/behavior is very dynamic, I would just make it
uncached. Interestingly enough, in mental you have complete access to
the
request and response variables because your tag definitions are defined
inside the Page model rather than in PageContext.

Sean


#3

I’m quite unfamiliar with Mental and only recently started working
with Radiant - I’ll look into Mental to see if it meets my needs.
Thanks!


#4

Kaleb Walton wrote:

I’m quite unfamiliar with Mental and only recently started working
with Radiant - I’ll look into Mental to see if it meets my needs.
Thanks!

Hi I’m fighting this too on Mental.
My problem is that included pages (via r:find) have no access to the
request.
This is because in app/models/page.rb Page stores the request into
instance
variables, but the find tag will construct a new page - and since nobody
will call
process(request, response) on that, it will have these instance
variables left on nil.

I tried to fix this in an extension, I “reopened” the Page class and
clobbered the request= and request definitions with a new implementation
wich stores into a class level variable.
This is almost ok, except that the process() call is not very nice, and
accesses
the @request directly and not via the attr_writer function, so I had to
clobber
that too.

This works if I edit page.rb directly, but when I try to “reopen” the
class,
and rewrite a few functions mysterious failures pop up - like no layout
being
rendered, broken admin pages… etc.

Please someone smarter than me: What is wrong with this:
in vendor/extensions/example/app/models/page.rb

require “app/models/page”
class Page < ActiveRecord::Base
@@request = nil
def request= rq
@@request = rq
end
def request
@@request
end
def process(request, response)
self.request = request
self.response = response
if layout
content_type = layout.content_type.to_s.strip
@response.headers[‘Content-Type’] = content_type unless
content_type.empty?
end
headers.each { |k,v| @response.headers[k] = v }
@response.body = render
self.request = nil
self.response = nil
end
end

I do require this file from the example_extension.rb so it does run

My guess is that some magic must be happening inside Page with the
subclasses
which prevent me from successfully patch it from an extension.


#5

Hi I’m fighting this too on Mental.
My problem is that included pages (via r:find) have no access to the
request.
This is because in app/models/page.rb Page stores the request into
instance
variables, but the find tag will construct a new page - and
since nobody
will call
process(request, response) on that, it will have these instance
variables left on nil.

Do you need access to the request from the page objects or from the
tags? If it’s just the tags:

class PageContext
alias __old_initialize initialize

def initialize(page)
__old_initialize(page)
globals.request = page.request
end
end

If you need it set on the page object for one of your tags to work, just
do the setting in that tag.

Dan.


#6

Storing the request at a class level is a very bad idea. At least I
think it is…

Isn’t it possible that the same class could be used for multiple
requests?

It sounds to me like any solution would have to involve directly
populating the request on the found pages within the tag definitions for
r:find and r:children.


#7

Daniel!

Thanks for trying to help.

I must admit my ruby-kungfu is weak.

Your suggestion on letting the tag know about the request seems to be
right,
but when I did what you suggested:

require “app/models/page_context”
class PageContext
alias initialize_before_reformhaz initialize

def initialize(page)
initialize_before_reformhaz(page)
globals.request = page.request
end
end

in my reformhaz_extansion.rb

I got

SystemStackError in SiteController#show_page
stack level too deep
vendor/extensions/reformhaz/reformhaz_extension.rb:29:in
initialize_before_reformhaz' vendor/extensions/reformhaz/reformhaz_extension.rb:29:ininitialize_before_reformhaz’
vendor/extensions/reformhaz/reformhaz_extension.rb:29:in initialize' app/models/page.rb:226:inlazy_initialize_parser_and_context’
app/models/page.rb:126:in render' app/models/page.rb:121:inprocess’

Since I have a similar experience with reopening and redefining methods
in the Page class, albeit that explodes in a different way, I just
boldly edited PageContext directly to have-a-look.

The exact situation is : root page <r:find>s the products page and
renders
it’s body. The outcome is the same: the tags in product can’t read the
request
to determine the offset and length. It’s only okay if I redirect to
/products
and the first page that gets dug up from the db is my ProductPage.
I guess I would have to tweak r:find too to pass down the info to the
found one.
(the way Kaleb did with the snippet up front)

The other way is to hack Page to store the request in a class variable.
This isn’t bad -methinks- since we have the luxury of having our whole
process to answer a lonely request. Another request gets it’s own
interpreter.

You’re right about that I might just need my own controller to handle
the
search-paging request, and then feed the params to the tags, but so far
I can
only do this for the directly requested page.

My only true headache is: I just can’t reliably redefine methods in Page
and
PageContext. On SiteController on the other hand it does work. Why?

Daniel S. wrote:

Do you need access to the request from the page objects or from the
tags? If it’s just the tags:

class PageContext
alias __old_initialize initialize

def initialize(page)
__old_initialize(page)
globals.request = page.request
end
end

If you need it set on the page object for one of your tags to work, just
do the setting in that tag.

Dan.


#8

in my reformhaz_extansion.rb

I got

SystemStackError in SiteController#show_page
stack level too deep
vendor/extensions/reformhaz/reformhaz_extension.rb:29:in
`initialize_before_reformhaz’

The problem is the aliasing, which is happening each time your extension
is loading (so on the second load, it re-aliased your new method to
initialize_before_reformaz, so the your method ended up calling itself).

Try:

class PageContent
unless method_defined? :__initialize_before_reformhaz
alias initialize_before_reformhaz initialize

def initialize(page)
  __initialize_before_reformhaz(page)
  globals.request = page.request
end

end
end

Dan.