Rack::Request subclasses and memoization

70 views
Skip to first unread message

Ryan Tomayko

unread,
Dec 27, 2008, 3:22:18 AM12/27/08
to rack-...@googlegroups.com
On Fri, Dec 26, 2008 at 8:55 AM, Michael Fellinger
<m.fel...@gmail.com> wrote:
> I'm using a subclass of Rack::Request for the things you mention
> here...

This is off the original topic but have you considered how to deal
with Rack::Request
subclasses now that Rack::Request.new does memoization? I suppose
we'll need to define
RequestSubclass.new to have Object.new semantics again. Has anyone
else ran into this?

I was thinking it might be better to only perform memoization when
self == Rack::Request:

class Request
def self.new(env)
if self == Rack::Request
env["rack.request"] ||= super
else
super
end
end
end

Thoughts?

Ryan

Yehuda Katz

unread,
Dec 27, 2008, 3:31:31 AM12/27/08
to rack-...@googlegroups.com
Is there a specific reason to ever *not* do memoization? It seems to me that subclasses should have identical semantics unless specifically overridden.

-- Yehuda
--
Yehuda Katz
Developer | Engine Yard
(ph) 718.877.1325

Christian Neukirchen

unread,
Dec 27, 2008, 6:10:10 AM12/27/08
to rack-...@googlegroups.com
"Ryan Tomayko" <r...@tomayko.com> writes:

> On Fri, Dec 26, 2008 at 8:55 AM, Michael Fellinger
> <m.fel...@gmail.com> wrote:
>> I'm using a subclass of Rack::Request for the things you mention
>> here...
>
> This is off the original topic but have you considered how to deal
> with Rack::Request
> subclasses now that Rack::Request.new does memoization? I suppose
> we'll need to define
> RequestSubclass.new to have Object.new semantics again. Has anyone
> else ran into this?

I'd prefer if people didn't subclass Rack::Request, but aggregated it...

--
Christian Neukirchen <chneuk...@gmail.com> http://chneukirchen.org

Yehuda Katz

unread,
Dec 27, 2008, 6:17:24 AM12/27/08
to rack-...@googlegroups.com
I think the need to subclass Rack::Request would be dramatically reduced if we made Rack::Request itself much more robust. Specifically, if we merged in the useful utilities from Merb and Rails, I suspect both would just use Rack::Request, and I suspect all other smaller frameworks that already use rack would use it as well. Then, when you use "request" inside of any Ruby web framework, it means the same thing.

Huge win from where I'm sitting!

-- Yehuda

Ryan Tomayko

unread,
Dec 27, 2008, 8:52:27 AM12/27/08
to rack-...@googlegroups.com
On Sat, Dec 27, 2008 at 3:10 AM, Christian Neukirchen
<chneuk...@gmail.com> wrote:
>
> "Ryan Tomayko" <r...@tomayko.com> writes:
>
>> On Fri, Dec 26, 2008 at 8:55 AM, Michael Fellinger
>> <m.fel...@gmail.com> wrote:
>>> I'm using a subclass of Rack::Request for the things you mention
>>> here...
>>
>> This is off the original topic but have you considered how to deal
>> with Rack::Request
>> subclasses now that Rack::Request.new does memoization? I suppose
>> we'll need to define
>> RequestSubclass.new to have Object.new semantics again. Has anyone
>> else ran into this?
>
> I'd prefer if people didn't subclass Rack::Request, but aggregated it...

You mean have everyone open / add methods directly to Rack::Request? That's the
approach we took with Sinatra initially but it led to some weirdness and I've
since erred on the side of subclassing in most cases.

For instance, at one point we implemented the equivalent of
Rack::MethodOverride directly in Rack::Request#request_method. This caused
the request body to be read and parsed into params the first time
#request_method
was called, which broke an assumption in some other piece of Rack code. The
backtrace was jarring because you could see where Sinatra had injected itself
into core rack logic that it had no need or intention of mucking with.

Rack::Cache has some interesting examples in this area as well. The Request
and Response objects have a very shared-cache oriented view of themselves. For
instance, the Response#max_age method returns the value of the s-maxage
cache-control directive before falling back on the max-age directive.
IMO, having
a subclass has some utility in these cases.

It makes sense to try to get as much shared convenience code in Rack core as
possible but I think subclassing also has its place here. It should be supported
without too much syntactic vinegar, IMO.

Thanks,
Ryan

Christian Neukirchen

unread,
Dec 27, 2008, 1:51:08 PM12/27/08
to rack-...@googlegroups.com
"Ryan Tomayko" <r...@tomayko.com> writes:

> You mean have everyone open / add methods directly to Rack::Request?

No, I mean has-a over is-a. Are there that many reasons to extend
Rack::Request?

Yehuda Katz

unread,
Dec 27, 2008, 2:12:05 PM12/27/08
to rack-...@googlegroups.com
Right now Rack::Request is a pretty simple object.

The router in Merb, for instance, allows you to use any method on the Request object (ideally the Rack::Request object), for routing.

Specifically:

match("/foo", :ssl? => true)
match("/foo", :subdomains => "foo")

We explicitly allow people to extend the Request object to add new ways to match to the router. This feature is very commonly used. If using aggregation, how would you envision the memoization working?

Merb::Request.new(Rack::Request.new(env)) in each piece of middleware?

-- Yehuda

Ryan Tomayko

unread,
Dec 27, 2008, 11:52:03 PM12/27/08
to rack-...@googlegroups.com
Sorry to keep this going. I'm running a day behind on mailing lists :/

On Sat, Dec 27, 2008 at 12:31 AM, Yehuda Katz <wyc...@gmail.com> wrote:
> Is there a specific reason to ever *not* do memoization? It seems to me that
> subclasses should have identical semantics unless specifically overridden.

If I'm reading the code correctly, the current implementation of
Rack::Request.new makes it hard/weird to use a subclass of Rack::Request once
any other Rack::Request instance has been memoized into an env:

class FooMiddleware < Struct.new(:app)
def call(env)
request = Rack::Request.new(env) # first time. memoized.
...
end
end

class BarMiddleware < Struct.new(:app)
class MySpecialRequest < Rack::Request
end

def call(env)
request = MySpecialRequest.new(env) # looked up in env.
fail "this request isn't special!" if request.class != MySpecialRequest
end
end

use FooMiddleware
use BarMiddleware
run lambda { |env| [200, {}, ['Hello World']] }

With FooMiddleware in place, BarMiddleware gets an instance of
Rack::Request. Without FooMiddleware, BarMiddleware gets an instance of
MySpecialRequest... naughty, IMO.

One workaround is to override :new in subclasses and memoize to a different
key and/or have the subclass return to Object.new semantics:

class MySpecialRequest < Rack::Request
def self.new(env)
env['bar.myspecialrequest'] ||= # this is probably useless
begin
inst = allocate
inst.send :intialize, env
inst
end
end
end

Ewww.

I can think of other approaches, like passing an empty hash to super and then
merging in the env provided, but it sure makes subclasses jump through hoops
to create an actual new instance when one is needed. A better option at this
point is to simply not use a subclass of Rack::Request and try to place the
logic elsewhere. Which may be fine, I just wanted to see if anyone else was
having to consider these issues now that new returns memoized instances without
regard to the class "new" was called on.

But now that I think about it, I'm not sure I see a huge benefit to memoizing
here anyway. Has anyone actually profiled this and found it to be a hot spot?
Rack::Request#initialize set a single instance variable to reference the env
object. A memoized lookup saves a handful of Class.allocate and
instance variable
set invocations per request. They're both basically constant time with
the memoized
version using a bit less space. It seems rather insignificant to me. What did
we actually accomplish here?

Thanks,
Ryan

Scytrin dai Kinthra

unread,
Dec 28, 2008, 12:03:57 AM12/28/08
to rack-...@googlegroups.com
Creating a new hash would be a no go, due to the expectation a change
in env to be reflected for the entire stack. Also, eww memory bloat.

The memoizing is primarily a bonus to the web frameworks crowd which
may create a new Request on each phase of handling. At least that's
where I recall the majority of the pro argument came.

Honestly, the idea of a mixin with a #robust_params makes the most
sense to me. Memoizing the superduper params hash in a different
location in the env for later retrieval similar to the way params
does.

--
stadik.net

Ryan Tomayko

unread,
Dec 28, 2008, 12:17:31 AM12/28/08
to rack-...@googlegroups.com
I think we may have crossed theads here. I was hoping to revisit the request
memoization stuff we were talking about the other day. I haven't even had a
chance to get through Josh's params thread :(

Ryan

Ryan Tomayko

unread,
Dec 28, 2008, 12:46:03 AM12/28/08
to rack-...@googlegroups.com
On Sat, Dec 27, 2008 at 8:52 PM, Ryan Tomayko <r...@tomayko.com> wrote:
> But now that I think about it, I'm not sure I see a huge benefit to memoizing
> here anyway. Has anyone actually profiled this and found it to be a hot spot?
> Rack::Request#initialize set a single instance variable to reference the env
> object. A memoized lookup saves a handful of Class.allocate and
> instance variable set invocations per request. They're both basically constant
> time with the memoized version using a bit less space. It seems rather
> insignificant to me. What did we actually accomplish here?

Here's a quick benchmark of memoized vs. normal object creation (1M iterations):

http://gist.github.com/40380

Rehearsal --------------------------------------------
memoized 0.860000 0.000000 0.860000 ( 0.874840)
normal 1.700000 0.020000 1.720000 ( 1.715816)
----------------------------------- total: 2.580000sec

user system total real
memoized 0.870000 0.000000 0.870000 ( 0.881126)
normal 1.700000 0.020000 1.720000 ( 1.728066)

I don't want to be a PITA here but Rack has a really excellent track record when
it comes to keeping the codebase simple and straightforward. I'm not against
micro-optimizations like this but I'd very much like to see what we're buying
for the added complexity. In this case, I'd say it's really very
premature (are we
seriously looking for fraction of a usec / request speedups at this point?),
though it obviously has some benefit over a large number of requests.

Thanks,
Ryan

Yehuda Katz

unread,
Dec 28, 2008, 2:24:42 AM12/28/08
to rack-...@googlegroups.com
Ryan,

The win comes from being able to lazy-load things like parameter parsing. The alternative is a middleware that does the parsing every time, which isn't ideal since it can be expensive. I laid out the full case for this solution in my first post on this topic. In short, it's decidely not a micro-optimization :P

-- Yehuda

Ryan Tomayko

unread,
Dec 28, 2008, 3:17:12 AM12/28/08
to Rack Development
On Dec 27, 11:24 pm, "Yehuda Katz" <wyc...@gmail.com> wrote:
> The win comes from being able to lazy-load things like parameter parsing.
> The alternative is a middleware that does the parsing every time, which
> isn't ideal since it can be expensive. I laid out the full case for this
> solution in my first post on this topic. In short, it's decidely not a
> micro-optimization :P

Rack::Request has always lazy loaded and memoized things like
parameter parsing
into the env hash without eager middleware or hacks. I don't see how
memoizing
the Request instance buys you anything new here other than the time/
space win.

I've re-read the original thread and I still don't get it. It's not
that big
of a deal so I'm fine with just dropping it but I'm pretty confused :(

Thanks,
Ryan

Matt Todd

unread,
Dec 28, 2008, 3:26:02 AM12/28/08
to rack-...@googlegroups.com
Perhaps we can expand the test to be something like this:

def Rack::Request.new(env)
  unless env["rack.request"] and env["rack.request"].is_a?(self)
    env["rack.request"] = super(env)
  end
  env["rack.request"]
end

Matt


--
Matt Todd
Highgroove Studios
www.highgroove.com
cell: 404-314-2612
blog: maraby.org

Scout - Web Monitoring and Reporting Software
www.scoutapp.com

Matt Todd

unread,
Dec 28, 2008, 3:34:14 AM12/28/08
to rack-...@googlegroups.com
To expand on my last message, as long as the specification for Rack::Request is clear about its behavior (which it should be), any subclass should either not break the implementation in general (pass the tests for Rack::Request) or knowingly break it and document this different behavior.

Does Rack::Lint sufficient test Rack::Request for subclasses to be able to depend on it?

Matt

Scytrin dai Kinthra

unread,
Dec 28, 2008, 3:39:27 AM12/28/08
to rack-...@googlegroups.com
Totally my bad, crossing the streams. It feels much like the same
thread, or at least concept to me. Adding a feature in favor of
frameworks.

--
stadik.net

Matt Todd

unread,
Dec 28, 2008, 3:51:24 AM12/28/08
to rack-...@googlegroups.com
No one to blame ^_^ they're all valid concerns, I just thought I'd make it a little easier to integrate the tickets into the Rack community workflow and also clarify discussion a bit.

Cheers,

Matt

Scytrin dai Kinthra

unread,
Dec 28, 2008, 3:55:29 AM12/28/08
to rack-...@googlegroups.com
Since Rack::Request memoizes itself into the request env, why can't
you simply capitalize on that fact during initialization of the
Merb::Request? And override the methods of Merb::Request that don't
align with that of Rack::Request. Something like,

class Merb::Request
def initialize env
@rr = Rack::Request.new(env)
end
def method_missing m, *a, &b
return @rr.__send__ m, *a, &b if @rr respond_to? m
super
end
def params
# lol, more thread leakage.
end
end

--
stadik.net

Matt Todd

unread,
Dec 28, 2008, 4:09:27 AM12/28/08
to rack-...@googlegroups.com
Makes sense, though I don't think that's necessary... just subclass ^_^ and with the new test for the class being equal, I think this becomes a moot point.

Thoughts?

Matt

Christian Neukirchen

unread,
Dec 28, 2008, 7:45:39 AM12/28/08
to rack-...@googlegroups.com
"Yehuda Katz" <wyc...@gmail.com> writes:

> Merb::Request.new(Rack::Request.new(env)) in each piece of middleware?

Merb::Request.new(env), and it can instantiate Rack::Request if it needs to?

Christian Neukirchen

unread,
Dec 28, 2008, 7:48:00 AM12/28/08
to rack-...@googlegroups.com
Ryan Tomayko <r...@tomayko.com> writes:

> On Dec 27, 11:24 pm, "Yehuda Katz" <wyc...@gmail.com> wrote:
>> The win comes from being able to lazy-load things like parameter parsing.
>> The alternative is a middleware that does the parsing every time, which
>> isn't ideal since it can be expensive. I laid out the full case for this
>> solution in my first post on this topic. In short, it's decidely not a
>> micro-optimization :P
>
> Rack::Request has always lazy loaded and memoized things like
> parameter parsing
> into the env hash without eager middleware or hacks. I don't see how
> memoizing
> the Request instance buys you anything new here other than the time/
> space win.

We can now encapsulate these memoized values in the Request object
itself, as instance variables.

Ezra Zygmuntowicz

unread,
Dec 28, 2008, 2:40:59 PM12/28/08
to rack-...@googlegroups.com

The idea is that you may have a middleware that instantiates a rack
request and maybe uses request.params triggering the params parsing.
Now in your later middlewares you want to use rack request.params
again without triggering parsing again.

It is not just to save on the request initialization as that is not
worth memoizing. It is about saving the request between middlewares so
you don't have to reparse params and other memoized instance vars in
the request object.

Does that make more sense?

Cheers-
-Ezra


Ryan Tomayko

unread,
Dec 28, 2008, 3:42:00 PM12/28/08
to rack-...@googlegroups.com

Well, yeah, but this has never actually been an issue AFAIK. The parsed
params and other intensive actions have always been memoized into the
env by the Request class the first time they're accessed. Other instances
of Request use the same parsed params Hash and do not perform the same
processing because they share the env. That was one of the major reasons
the environ was modifiable in WSGI and it seemed to be put to good use in
Rack as well.

Christian explained that the reason for this is so that we can memoize to
instance variables in Request instead of memoizing to the env hash. I'd
prefer memoizing to the env, personally, since that keeps all state in a
single place and allows different request instances to provide different
views or processing models on top of the env. I saw this as a feature, not
a flaw. In fact, when we were spec'ing WSGI, a simple dictionary was chosen
for storing request state precisely because it's a simple and dumb data
structure with behavior that's well defined and unlikely to be modified.
Each component along the request pipeline could add features by wrapping
the env in some higher level object (like Rack::Request), _but_ that code
applied only to the specific component.

With these changes I fear we're moving to a situation where an
instance of Rack::Request replaces the env Hash as "the place where
request state is stored." I prefer the current design because I think
it's much more simple, increases separation between components, and
makes it easier for components to easily customize the Request
implementation by subclassing (as opposed to delegating to a Request
instance). And there's very little downside from a performance perspective
that I'm aware of. I'm worried that we're forcing the env behind an
object because we're under the assumption that the env, being a simple
Hash, was under-designed or not well thought through. Nothing could be
further from the truth, IMO.

With all that being said, I'm really sorry to keep slogging away at
this. I do think there's some merit in memoizing the request for performance
and being able to use instance variables for some types of state may
be useful (although I can't think of a case where I'd use an ivar over
the env). I just want to make sure we're doing it for the right reasons
and not because we're under bad assumptions about the current design and
capabilities.

Thanks,
Ryan

Josh Peek

unread,
Dec 28, 2008, 5:06:17 PM12/28/08
to Rack Development
I'm on Ryans side here.

Memoizing the Request object is actually a step backwards and makes it
harder to implement extensions. The "env" hash should be the request
state, not some other special request object. I'd rather see specs for
conventions like "rack.forms.vars". It would really suck if you needed
to create a Rack::Request object anytime you wanted to do anything
interesting with the env hash.

Ezra Zygmuntowicz

unread,
Dec 28, 2008, 5:22:27 PM12/28/08
to rack-...@googlegroups.com

Yeah I can see this way as the way to go. We can wrap the semantics
we want while still storing the state in the env hash no problem. It
does seem cleaner that way


-Ezra

Ezra Zygmuntowicz
e...@engineyard.com

Christian Neukirchen

unread,
Dec 28, 2008, 6:41:19 PM12/28/08
to rack-...@googlegroups.com
Josh Peek <jo...@joshpeek.com> writes:

> I'm on Ryans side here.
>
> Memoizing the Request object is actually a step backwards and makes it
> harder to implement extensions. The "env" hash should be the request
> state, not some other special request object. I'd rather see specs for
> conventions like "rack.forms.vars".

I'm against that, here's why: this would make people write things like

env["rack.forms.vars"] || Rack::Request.new(env).params

which breaks encapsulation and leaks implementation detail.

> It would really suck if you needed
> to create a Rack::Request object anytime you wanted to do anything
> interesting with the env hash.

No need to, but when you want to parse parameters with Rack::Request,
you need an Rack::Request instance of course. What we are talking
about is how to make Rack::Request not parse the parameters twice,
which is just an implementation detail.

Ryan Tomayko

unread,
Dec 29, 2008, 3:19:46 AM12/29/08
to rack-...@googlegroups.com
On Sun, Dec 28, 2008 at 3:41 PM, Christian Neukirchen
<chneuk...@gmail.com> wrote:
>
> Josh Peek <jo...@joshpeek.com> writes:
>
>> I'm on Ryans side here.
>>
>> Memoizing the Request object is actually a step backwards and makes it
>> harder to implement extensions. The "env" hash should be the request
>> state, not some other special request object. I'd rather see specs for
>> conventions like "rack.forms.vars".
>
> I'm against that, here's why: this would make people write things like
>
> env["rack.forms.vars"] || Rack::Request.new(env).params
>
> which breaks encapsulation and leaks implementation detail.

Why would you first check for "rack.form.vars"? Wouldn't you just:

Rack::Request.new(env).params

>> It would really suck if you needed
>> to create a Rack::Request object anytime you wanted to do anything
>> interesting with the env hash.
>
> No need to, but when you want to parse parameters with Rack::Request,
> you need an Rack::Request instance of course. What we are talking
> about is how to make Rack::Request not parse the parameters twice,
> which is just an implementation detail.

Right. And Rack::Request never parses parameters twice today, right? The
only difference I can see with memoizing to instance variables is that you
end up parsing params for each instance of Rack::Request instead of once
per env instance.

Thanks,
Ryan

Yehuda Katz

unread,
Dec 29, 2008, 3:42:17 AM12/29/08
to rack-...@googlegroups.com
I would like, as a basic principle, to avoid creating objects and performing operations where not necessary. I'm concerned that arguments in the form of "but the overhead of FOO is small" are a slippery slope and if we have a nice way to avoid extra allocations or processing, we should do that.

-- Yehuda

Ryan Tomayko

unread,
Dec 29, 2008, 4:40:13 AM12/29/08
to rack-...@googlegroups.com
On Mon, Dec 29, 2008 at 12:42 AM, Yehuda Katz <wyc...@gmail.com> wrote:
> I would like, as a basic principle, to avoid creating objects and performing
> operations where not necessary. I'm concerned that arguments in the form of
> "but the overhead of FOO is small" are a slippery slope and if we have a
> nice way to avoid extra allocations or processing, we should do that.

Me too. All other things (e.g., simplicitly, correctness, completeness,
consistency) being considered, of course. Is there something specific
you're referring to here?

Per the original thread, you seem to be under the impression that Rack::Request
did not, or was not capable of, memoizing lengthy operations like param parsing,
which is simply not true. We've always avoided extra allocations and processing
there. The original issue was a non-issue. I've tried to make a case that the
existing model of memoizing to env has significant benefits over memoizing to
instance variables on Request with equivalent performance characteristics.

I've also already said that memoizing Request instances on Request.new in order
to reduce the number of allocations is fine (if extremely premature).

So where exactly was an "but the overhead of FOO is small" argument made? I'd
very much like to retract or defend it.

Thanks,
Ryan

Yehuda Katz

unread,
Dec 29, 2008, 5:02:13 AM12/29/08
to rack-...@googlegroups.com
There are two issues:

1) not having to do param parsing multiple times. This is handled by

class Request
  def parse
    @env["rack.request.params"] ||= ...
  end
end

2) not having to allocate a new Request object for each middleware step. This is handled by memoization. I understand that it might seem pretty silly, but I've been trying to reduce the number of objects allocated in each request, and it looks like Rails/Merb will be moving a lot of small, atomic operations into middleware, so I'd like to avoid making 20-30 Request objects per request. It may be premature... but I'm not sure what the loss is.

-- Yehuda

Ryan Tomayko

unread,
Dec 29, 2008, 5:23:14 AM12/29/08
to rack-...@googlegroups.com
On Mon, Dec 29, 2008 at 2:02 AM, Yehuda Katz <wyc...@gmail.com> wrote:
> There are two issues:
> 1) not having to do param parsing multiple times. This is handled by
> class Request
> def parse
> @env["rack.request.params"] ||= ...
> end
> end
> 2) not having to allocate a new Request object for each middleware step.
> This is handled by memoization. I understand that it might seem pretty
> silly, but I've been trying to reduce the number of objects allocated in
> each request, and it looks like Rails/Merb will be moving a lot of small,
> atomic operations into middleware, so I'd like to avoid making 20-30 Request
> objects per request. It may be premature... but I'm not sure what the loss
> is.

Then we're in agreement. Perfect.

As a side note, if you really want to find lots of unnecessary allocations and
extra processing, take a look at Rack::Response and consider that most of that
stuff will typically happen multiple times per request :)

Thanks,
Ryan

Matt Todd

unread,
Dec 29, 2008, 6:14:22 AM12/29/08
to rack-...@googlegroups.com
One note on concerns of Rack::Request being necessary to replace the env hash: Rack::Request references the hash as the definitive source of its information, so there's definitely no reason not to use this except for, I hope, Rack::Request will have a superior implementation (eventually) of whatever is needed.

Perhaps the real issue here is why Merb::Request et al require differentiation from Rack::Request.

Matt

Yehuda Katz

unread,
Dec 29, 2008, 6:23:09 AM12/29/08
to rack-...@googlegroups.com
The goal is absolutely to unify *::Request under Rack::Request. That's why the robust_params discussion happened.

-- Yehuda

Yehuda Katz

unread,
Dec 29, 2008, 6:24:24 AM12/29/08
to rack-...@googlegroups.com
Sounds good to me. I'm going to be spending a non-trivial amount of time optimizing and I didn't want to start with another uphill optimization problem before I even got started :P

-- Yehuda

Joshua Peek

unread,
Dec 29, 2008, 11:54:38 AM12/29/08
to rack-...@googlegroups.com
It does sound premature to me when you look at the *real* use cases.

The only I've need to create a Rack::Request object in middleware is
to get the parameters. Most of the time you can get the other things
like the request method simply with env["REQUEST_METHOD"]. The real
issue is that it is hard to get the parameters without making a
request object. Why not make it a utility?

# Returns params and also caches it in rack.form.hash etc
Rack::Utils.extract_params!(env)

A light weight utility like this would be more useful in middleware situations.

Joshua Peek

unread,
Dec 29, 2008, 11:58:43 AM12/29/08
to rack-...@googlegroups.com
BTW, I am in favor of reverting eefbed89 since it is making
subclassing more difficult.

--
Joshua Peek

Yehuda Katz

unread,
Dec 29, 2008, 1:47:28 PM12/29/08
to rack-...@googlegroups.com
Just FYI, WebObj, which is a rack-utils like library for wsgi, uses this technique.

-- Yehuda

Matt Todd

unread,
Dec 29, 2008, 2:54:36 PM12/29/08
to rack-...@googlegroups.com
I've offered a patch that takes care of the subclassing problems... it is on Lighthouse (http://rack.lighthouseapp.com/)

Matt

Yehuda Katz

unread,
Dec 29, 2008, 9:17:33 PM12/29/08
to rack-...@googlegroups.com
An acceptable solution would be:

module Rack::Parsers
  def self.params(env)
    env["rack.request.params"] ||= begin
      # logic here
    end
  end
end

Then middleware could use Rack::Parser.params, and Rack::Request#params could trigger that parsing.

How does that sound?

-- Yehuda

Ryan Tomayko

unread,
Dec 29, 2008, 9:58:37 PM12/29/08
to rack-...@googlegroups.com
On 12/29/08 6:17 PM, Yehuda Katz wrote:
> An acceptable solution would be:
>
> module Rack::Parsers
> def self.params(env)
> env["rack.request.params"] ||= begin
> # logic here
> end
> end
> end
>
> Then middleware could use Rack::Parser.params, and Rack::Request#params
> could trigger that parsing.
>
> How does that sound?

+1

Ryan

Christian Neukirchen

unread,
Dec 30, 2008, 6:07:29 AM12/30/08
to rack-...@googlegroups.com
Ryan Tomayko <r...@tomayko.com> writes:

+1

but I'd make it class methods of Rack::Request.

Rack::Request.params(env) sounds sensible to me.

> Ryan

Ryan Tomayko

unread,
Dec 30, 2008, 6:41:28 AM12/30/08
to rack-...@googlegroups.com
On 12/30/08 3:07 AM, Christian Neukirchen wrote:
> Ryan Tomayko <r...@tomayko.com> writes:
>
>> On 12/29/08 6:17 PM, Yehuda Katz wrote:
>>> An acceptable solution would be:
>>>
>>> module Rack::Parsers
>>> def self.params(env)
>>> env["rack.request.params"] ||= begin
>>> # logic here
>>> end
>>> end
>>> end
>>>
>>> Then middleware could use Rack::Parser.params, and Rack::Request#params
>>> could trigger that parsing.
>>>
>>> How does that sound?
>> +1
>
> +1
>
> but I'd make it class methods of Rack::Request.
>
> Rack::Request.params(env) sounds sensible to me.

Oh, yeah. No doubt. I read "Rack::Parsers" as "Rack::Request" for some
reason.

Ryan

Christian Neukirchen

unread,
Dec 30, 2008, 8:14:40 AM12/30/08
to rack-...@googlegroups.com
"Yehuda Katz" <wyc...@gmail.com> writes:

> An acceptable solution would be:
>
> module Rack::Parsers
> def self.params(env)
> env["rack.request.params"] ||= begin
> # logic here
> end
> end
> end
>
> Then middleware could use Rack::Parser.params, and Rack::Request#params could
> trigger that parsing.
>
> How does that sound?

These methods use caching at the moment:

GET
POST
cookies

What else should become class methods?

Joshua Peek

unread,
Dec 30, 2008, 11:04:25 AM12/30/08
to rack-...@googlegroups.com
On Tue, Dec 30, 2008 at 7:14 AM, Christian Neukirchen
<chneuk...@gmail.com> wrote:
> These methods use caching at the moment:
>
> GET
> POST
> cookies

+1 to those too.

We may want to consider the names a bit closer. Since they are
modifying the env, a bang would be appropriate.

Rack::Request.parse_params!(env)


--
Joshua Peek

Matt Todd

unread,
Dec 30, 2008, 11:30:39 AM12/30/08
to rack-...@googlegroups.com
I do like this approach to handling these params...

My concerns are... are we thus ignoring the subclass memoization problems, and are we going to see the same problems with subclasses trying to memoize the parsed params later on?

What are the stories for overwriting the memoized params?

Matt

Joshua Peek

unread,
Dec 30, 2008, 12:24:05 PM12/30/08
to rack-...@googlegroups.com
On Tue, Dec 30, 2008 at 10:30 AM, Matt Todd <chio...@gmail.com> wrote:
> I do like this approach to handling these params...
> My concerns are... are we thus ignoring the subclass memoization problems,
> and are we going to see the same problems with subclasses trying to memoize
> the parsed params later on?
> What are the stories for overwriting the memoized params?
> Matt

I think we still need to revert eefbed89c.

class ActionController::Request < Rack::Request
def params
@env["action_controller.request.params"] = ... # Special params parsing
end
end


--
Joshua Peek

Matt Todd

unread,
Dec 30, 2008, 12:37:21 PM12/30/08
to rack-...@googlegroups.com
I've made an amendment to eefbed89c in order to handle subclassing...

8acbf2b6f875f20893083b451d34ac72912b7a71

Do you think it still requires removal with this change?

Matt

Yehuda Katz

unread,
Dec 30, 2008, 12:47:37 PM12/30/08
to rack-...@googlegroups.com
Seems fine to me :)

-- Yehuda

Christian Neukirchen

unread,
Dec 30, 2008, 2:18:16 PM12/30/08
to rack-...@googlegroups.com
"Matt Todd" <chio...@gmail.com> writes:

> I've made an amendment to eefbed89c in order to handle subclassing...
>
> 8acbf2b6f875f20893083b451d34ac72912b7a71
>
> Do you think it still requires removal with this change?

IMO we can keep it with this change, but both is not strictly needed anymore.

Reply all
Reply to author
Forward
0 new messages