Rack must not dictate how to create a middleware

In current realization of Rack::Builder the method :use dictates that
middlewares must be instances of some class by internal invoking :new
initializer.

Thus Rack’s Builder demands to delegate creation of middleware object
and demands existing special class for middleware. Though in most cases
middleware conforms with singleton pattern. Or just simply a callable
object.

Moreover, it is always more cleary to link already existing middleware,
created outside of builder. Is middleware worse than ordinary app in
respect of this? :slight_smile:

Unneeded delegation of creation makes unneeded coupling and unneeded
restrictions.

As Vidar H. well wrote in his post “Cohesion vs. coupling”
http://www.hokstad.com/why-coupling-is-always-bad-cohesion-vs-coupling.html

Looking at realization of Builder’s methods we can make something like
this.

first variant

module MiddleHello

this method needs only

to prevent duplicating of code in second example

def self.invoke app, env, who, whom
status, header, resp = app.call env
add_str = “”
if resp.kind_of? Rack::Response
new_resp = Rack::Response.new(add_str, status, header)
new_resp.write resp.body
return new_resp.finish
end
[status, header, (add_str + resp)]
end

def self.creator app
@app = app
self
end

def self.call env
invoke @app, env, self.method(:call), “#{self.class}: #{self}”
end

end

second variant

middle_hello = proc do |app|
proc do |env|
# just for preventing duplication of code
MiddleHello.invoke app, env, Proc, Proc
end
end

builder = Rack::Builder.new do
use …

use …

map ‘/some_path’ do
run MiddleHello.method(:creator) # :run instead of :use !
run middle_hello # :run instead of :use !
run OurApp
end

end

It works. Now I can not find better solution without changing of Builder
class.

But it looks like a hack.

First. There are no warranties that realization of Builder’s methods
will not have changed in the future.

Second. It is still needed an additional object for creating a
middleware object.

Third, and maybe more important. This breaks the semantics of Builder’s
language. And it is losing distinction in declarations of middlewares
and ordinary apps.

Of course we can remake the Builder or make a subclass from it. But can
it cause conflicts with hostings where Rack is used for administration?

Sys P. wrote:

In current realization of Rack::Builder the method :use dictates that
middlewares must be instances of some class by internal invoking :new
initializer.

A middleware module must have a “next module” to pass the call() request
onto (otherwise it wouldn’t be in the middle :slight_smile: So it makes sense to
create an instance of the middleware class, with the “next module” being
stored in an instance variable.

The complicated bit is that Builder has to create them in a backwards
order so they wrap each other.

module MiddleHello
def self.call env
invoke @app, env, self.method(:call), “#{self.class}: #{self}”
end

I see you are using a module instance variable here. This is fine except
that it limits you to only using the same middleware once.

You may have a Rack setup which bundles multiple apps, e.g. using
urlmap, all of which are using the same middleware - in which case
you’ll need multiple instances. Not doing this would severely limit the
flexibility of middleware IMO.

Of course we can remake the Builder or make a subclass from it. But can
it cause conflicts with hostings where Rack is used for administration?

No, you’re free to create your own alternative Builder implementation.
After all, all it does is return an object which responds to #call.
That’s all you need to do.

I did look at this myself once - I wanted to be able to pass extra
parameters to the middleware modules. But the problem was the deferred
initialisation of the modules, which was required to build them in the
inside-out order. I decided that it was simpler in such cases not to use
middleware, but to construct the stack explicitly:

run Foo.new(Bar.new(Baz.new))

Brian C. wrote:

A middleware module must have a “next module” to pass the call() request
onto (otherwise it wouldn’t be in the middle :slight_smile: So it makes sense to
create an instance of the middleware class, with the “next module” being
stored in an instance variable.

Yes, I have caught sight of it. :slight_smile: I just have noticed that Builder must
not dictate way of creation each chained object.

The complicated bit is that Builder has to create them in a backwards
order so they wrap each other.

Just little more complicated in realization. IMO it does not change fact
of the matter.

I see you are using a module instance variable here. This is fine except
that it limits you to only using the same middleware once.

Such is indeed I meant cases where middleware have to be a singleton. Or
when mixing is intensively used instead of classes. In other cases
surely using of classes is the best choice. Of course. But Builder must
not make any limitations for using singletons or any “classless” objects
there they are needed.

You may have a Rack setup which bundles multiple apps, e.g. using
urlmap, all of which are using the same middleware - in which case
you’ll need multiple instances. Not doing this would severely limit the
flexibility of middleware IMO.

There are some non-classic OOP paradigms, by the way intensively being
used in Ruby, as I see. Such “classless” things as mixings, singletons,
interfaces and so on. They are very useful, when the quantity of
differences in object structures is near the quantity of these objects.
On startup stages of project, when at the fist look it is very hard to
deside which classes we have to make, but it is cleary known what kind
of objects we are need. It especially relates to such muddy things as
controllers in MVC pattern.

And invoking of method :new inside Builder’s :use - it is bad
resctiction IMO.

No, you’re free to create your own alternative Builder implementation.
After all, all it does is return an object which responds to #call.
That’s all you need to do.

It is very good.

Brian C. wrote:

I did look at this myself once - I wanted to be able to pass extra
parameters to the middleware modules. But the problem was the deferred
initialisation of the modules, which was required to build them in the
inside-out order. I decided that it was simpler in such cases not to use
middleware, but to construct the stack explicitly:

run Foo.new(Bar.new(Baz.new))

It is not very hard.

module Node

def create app
@app = app
self
end

def call env, *args
str, *new_args = get_str args
status, header, resp = @app.call env, new_args
new_resp = Rack::Response.new(str, status, header)
new_resp.write resp.body
return new_resp.finish
end

end

builder = Rack::Builder.new do
use …

use …

map ‘/’ do

module MidA
  def self.get_str *args
    ["<p>MidA args: #{args}</p>\n", args, 'AAA']
  end
  extend Node
end
run MidA.method(:create)

module MidB
  def self.get_str *args
    ["<p>MidB args: #{args}</p>\n", args, 'BBB']
  end
  extend Node
end
run MidB.method(:create)

p = Proc.new do |env, *args|
  resp = Rack::Response.new
  resp.write "<p>Main args: #{args}</p>\n"
  resp.finish
end
run p

end

end

Or something else like that.

I think, blocks may be easy passed down to middleware chains. Like this.
status, header, resp = @app.call env, args {|…| …}
But I have not yet invented how to use such pervertion. :slight_smile:

status, header, resp = @app.call env, args {|…| …}
status, header, resp = @app.call(env, args){|…| …}

Sys P. wrote:

I see you are using a module instance variable here. This is fine except
that it limits you to only using the same middleware once.

Such is indeed I meant cases where middleware have to be a singleton. Or
when mixing is intensively used instead of classes. In other cases
surely using of classes is the best choice. Of course. But Builder must
not make any limitations for using singletons or any “classless” objects
there they are needed.

Why “must” it not? It makes the 99% usage case simple, where the
middleware is an object containing an instance variable, and the
constructor is passed the next module in the chain. If you want to have
a Module as middleware, then you just construct it by hand.

There are some non-classic OOP paradigms, by the way intensively being
used in Ruby, as I see. Such “classless” things as mixings, singletons,
interfaces and so on. They are very useful, when the quantity of
differences in object structures is near the quantity of these objects.
On startup stages of project, when at the fist look it is very hard to
deside which classes we have to make, but it is cleary known what kind
of objects we are need. It especially relates to such muddy things as
controllers in MVC pattern.

And invoking of method :new inside Builder’s :use - it is bad
resctiction IMO.

If this “restriction” were removed, it would make Builder much harder to
use. And that would defeat the point of Builder entirely.

With current Builder you just write:

use M1
use M2
use M3
run App

The whole point is that it encapsulates the following pattern for you:

a = App.new
b = M3.new(a)
c = M2.new(b)
d = M1.new©
run d

i.e. each request is handled by M1, then passed to M2, then passed to
M3, then passed to the App.

If this is not the pattern you want, then don’t use ‘use’ - combine the
objects in whatever other way makes sense to you.

With current Builder you just write:

use M1
use M2
use M3
run App

The whole point is that it encapsulates the following pattern for you:
[correction]

a = App
b = M3.new(a)
c = M2.new(b)
d = M1.new©
run d

Anyway, there’s nothing magic about the method ‘new’. You could write:

module Mine
def self.new(app)
@app = app
self
end

def self.call(env)

@app.call(env)
end
end

Or even this:

nextapp = nil
mymid = lambda { |env| puts “Got #{env}”; nextapp.call(env) }
class << mymid; self; end.class_eval {
define_method(:new) { |na| nextapp = na; mymid }
}

That is, you can make your code conform to the middleware API simply by
implementing a ‘new’ method which sets the next app in the chain, and
returns the object to be called.

You can make a wrapper for this pattern if you are using it frequently.

But I still assert that middleware written as a singleton is pretty
useless. The whole point of Rack is that you can have a complex
branching tree of request handling, and each node in the tree “knows”
where to forward the request next. Having a node which can only ever
forward to one place makes it impossible to plug in somewhere else. I
cannot think of any realistic example where this makes sense.

Certainly there may be middleware objects which share some underlying
singleton object (e.g. a logger), but the middleware object itself
should not be a singleton, because it’s responsible for storing the next
hop.

class LoggerMiddleware
@logger = Logger.new # this is the singleton object

def self.log(*args)
@logger.log(args.inspect)
end

def initialize(app)
@app = app
end

def call(env)
resp = @app.call(env)
self.class.log(env, resp)
resp
end
end

On Thu, Aug 27, 2009 at 11:37 AM, Sys P. [email protected] wrote:

use a
use b
use c
run d

I would suggest that you create a patch/alternative version of Builder
that
can run like this and I’m certain the Rack folks would love to take a
look
at it. There’s nothing stopping variants like use, use_xxx, and
use_instance
within the Builder DSL as long as someone takes the time to figure out
how
to deal with the technical issues (whatever they might be in the end).

That’s always the beauty of things with open source - you can create a
better mouse trap anytime you want…

John

John W Higgins wrote:

I would suggest that you create a patch/alternative version of Builder
that
can run like this and I’m certain the Rack folks would love to take a
look
at it. There’s nothing stopping variants like use, use_xxx, and
use_instance
within the Builder DSL as long as someone takes the time to figure out
how
to deal with the technical issues (whatever they might be in the end).

That’s always the beauty of things with open source - you can create a
better mouse trap anytime you want…

John

I absolutely agree with you.

Brian C. wrote:

Sys P. wrote:

But Builder must not make any limitations for using singletons or any “classless” objects there they are needed.

Why “must” it not?

Why must objects be created by Builder and not be created outside?
The answer of this questions I have done in the first post. Besause in
the
first case it will be a coupling, but in the second it will be cohesion.
You
cau decide by youself, what you like more.

It makes the 99% usage case simple, where the middleware is an object containing an instance variable, and the constructor is passed the next module in the chain.

Simplicity of usage is necessary but not sufficiently for well project
design.

If you want to have a Module as middleware, then you just construct it by hand.

What do mean saying “by hand”? Classes usually also is constructed “by
hand”, in common way. But in case of modules you always may use mixing.

If this “restriction” were removed, it would make Builder much harder to
use. And that would defeat the point of Builder entirely.

Why do you think what difficulty of usage will be certainly caused just
by
removing this restriction?

With current Builder you just write:

use M1
use M2
use M3
run App

I also want to write so.
use a
use b
use c
run d

But I want what these objects would be precreated and (kind_of?
ChainMember)
== true. Rather than they have to be some classes, which must create
chain
objects. This exactly is cohesion vs. coupling.

Anyway, there’s nothing magic about the method ‘new’. You could write:

That is, you can make your code conform to the middleware API simply by
implementing a ‘new’ method which sets the next app in the chain, and
returns the object to be called.

This is excellent technical solution based on some technical conditions.
I was waiting what you would have suggested it.

But it is the ugly hack at the semantic look.
It conflicts with the principle of least astonishment. If something has
the
:new method, then something.kind_of? Class == true.

Thus in searching of solution we get either semantic hack of Builder
(see my
1st post) or semantic hack of Ruby. Why? The answer is in bad design of
Builder.

You can make a wrapper for this pattern if you are using it frequently.

If it is needed to invent wrappers for making something more clear, it
is
better to primary make something more clear.

But I still assert that middleware written as a singleton is pretty
useless. The whole point of Rack is that you can have a complex
branching tree of request handling, and each node in the tree “knows”
where to forward the request next. Having a node which can only ever
forward to one place makes it impossible to plug in somewhere else. I
cannot think of any realistic example where this makes sense.

Certainly there may be middleware objects which share some underlying
singleton object (e.g. a logger), but the middleware object itself
should not be a singleton, because it’s responsible for storing the next
hop.

Are singletons useful or not. May I use one singleton in several nodes
of
middleware tree, or may I use several singletons only one per one node.
How
to better make middlewares, as instances, as modules, as procs, or as
some
objects else. These are all my owns desiners’s problems. And I do not
want,
what the Builder decides instead of me, how can I do it.

And IMO this is not a good solution to delegate to Builder creation of
chained
objects.

On Thu, Aug 27, 2009 at 3:25 PM, John W Higgins[email protected]
wrote:

That’s always the beauty of things with open source - you can create a
better mouse trap anytime you want…

And your ideas are much more likely to be incorporated if you take an
approach such as “what do you think about this patch” or “may I have
the temerity to suggest this patch” rather than you MUST NOT do what
you are doing."


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

Rick Denatale wrote:

And your ideas are much more likely to be incorporated if you take an
approach such as “what do you think about this patch” or “may I have
the temerity to suggest this patch” rather than you MUST NOT do what
you are doing."

I do not want to obtrude my ideas on anybody. I just have expressed my
opinion.

In difference of Builder I do not dictate to others how they should
create their middlewares. :wink:

As it have been written higher, it is advantage of open source, what one
is able not to wait for acceptation of his opinion by others.

Sys P. wrote:

Brian C. wrote:

Sys P. wrote:

But Builder must not make any limitations for using singletons or any “classless” objects there they are needed.

Why “must” it not?

Why must objects be created by Builder and not be created outside?

I never said objects must be created by Builder and not be created
outside, because it’s not true. If you think that, then you have missed
the point.

If you use rackup, which in turn reads config.ru and wraps it in a
Builder class, then you are using Builder. But in the first case, you
don’t need to use rackup at all to start your application; and in the
second case, even using Builder, you can write

x = any_combination_of_rack_modules_you_like
run x

(where ‘x’ is any object which responds to #call)

That is, you are not required to use the ‘use X’ syntax at all.

I think the advice given here is sound: write a patch, submit it to the
rack-devel mailing list, showing how it would change Builder usage and
what new things you would be able to do with it. If they don’t like it,
nothing stops you maintaining your own derived Builder class for your
own use, and/or publishing it for others to use. That is the main
benefit of Open Source.

Brian C. wrote:

I never said objects must be created by Builder and not be created
outside, because it’s not true. If you think that, then you have missed
the point.

But on my opinion that “Builder must not make any limitations”, you have
asked by yourself - word for word - Why “must” it not?

And you have added after it. - “It makes the 99% usage case simple”.

My point of view was very clear. Builder must not create objects by
itself in its :use method.

Only don’t say, that I try to dictate anything. I am not the Builder. :wink:
I just show the choice.

If you use rackup, which in turn reads config.ru and wraps it in a
Builder class, then you are using Builder. But in the first case, you
don’t need to use rackup at all to start your application; and in the
second case, even using Builder, you can write

x = any_combination_of_rack_modules_you_like
run x

(where ‘x’ is any object which responds to #call)

That is, you are not required to use the ‘use X’ syntax at all.

“Syntax” is that commands follow one by one and an operand follows an
operation.

But semantics is that ‘use X’ means just use X, where X must be some
precreated object, not its class.
And semantics of Rack::Builder in particular is that in ‘use x’ x is a
middleware, not an app in an endpoint.

And if semantics is to create something, it must be just ‘create
something’.

use a
use b
create C
create D
run x

I think the advice given here is sound: write a patch, submit it to the
rack-devel mailing list, showing how it would change Builder usage and
what new things you would be able to do with it. If they don’t like it,
nothing stops you maintaining your own derived Builder class for your
own use, and/or publishing it for others to use. That is the main
benefit of Open Source.

Also it is the main benefit of Open Source that if somebody will like my
point of view, one can by himself “write a patch, submit it to the
rack-devel mailing list, showing how it would change Builder usage and
what new things you would be able to do with it”.

The Builder is not very complex for it.

But I do not dictate it to anybody. Im not the Builder of anybody’s
mind. :wink:

Brian C. wrote:

If you use rackup, which in turn reads config.ru and wraps it in a
Builder class, then you are using Builder. But in the first case, you
don’t need to use rackup at all to start your application;

Actually it is not needed to wrap something to some class only for
wrapping something to some class.

It would be more weighty reason to use Builder.

and in the
second case, even using Builder, you can write

x = any_combination_of_rack_modules_you_like
run x

In such case I shall jast write

x = any_combination_of_rack_modules_you_like
Rack::Handler::ConcreteHandler.run x

In fact there is the one reason of using the Builder. To use already
existing middleware which already have been developed for not so clear
semantics of the Builder.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs