Separating actions into multiple files

1,241 views
Skip to first unread message

Alex Chaffee

unread,
Jun 11, 2009, 1:47:10 PM6/11/09
to sina...@googlegroups.com
My app has gotten big enough that I want to split out my actions (aka controllers, aka handlers, aka routes -- but I like "actions" since it maps to the HTML FORM term and because it's a destination, not a route) into multiple files. Rails does this by default with the whole one-controller-per-model convention, but I find that too constraining: sometimes I'd like to group together actions that relate to different models, like putting all the Sign In + Sign Up actions together (spanning User and Session); sometimes I'd like to split apart actions dealing with a single model into further subgroupings; and sometimes I just want a bunch of actions in a single app.rb file like Sinatra status quo.

The only way I've found to do this within Sinatra is by reopening the main app's class, like so:

app.rb:
class Vegas < Sinatra::Default
  set :app_file, __FILE__
  set :root, File.dirname(__FILE__)

  load 'configure.rb'

  load 'actions/login.rb'
...
  get '/' do
    MainPage.new(current_user).to_s  # render the Erector page
  end
end

actions/login.rb:
class Vegas < Sinatra::Default

  post '/login' do
    user = User.authenticate(params["name"], params["password"])
    ...
  end
end

This is unsatisfying for a few reasons:

* The order-dependency of Sinatra routes means I have to use 'load' rather than 'require' (and I'm actually still a little fuzzy on how this works).

* Since everything extends from my personalized application class it's irritating to reuse common modules across applications; I have to change the "class" line in each. So that means plugins that define actions are impossible.

Anyone have any better ideas? It'd be nice if I could mix in a module, say, but that doesn't work since the Sinatra DSL isn't available when I'm in the module's definition...

Thanks -

 - Alex

---
Alex Chaffee - al...@stinky.com - http://alexch.github.com
Stalk me: http://friendfeed.com/alexch | http://twitter.com/alexch | http://alexch.tumblr.com

Blake Mizerany

unread,
Jun 11, 2009, 2:13:20 PM6/11/09
to sina...@googlegroups.com
Alex,

I'd like to make one quick note.  Sinatra::Default is now deprecated; using Sinatra::Base or Sinatra::Application will keep enable to upgrade gracefully.




-Blake
--
Blake Mizerany
blake.m...@gmail.com

Alex Chaffee

unread,
Jun 11, 2009, 2:20:36 PM6/11/09
to sina...@googlegroups.com
Good to know, and I'm glad you're simplifying the class tree. (I was going to say "pruning the bush" but apparently that metaphor is now NSFW.)

So does that mean that the recommendation is not to extend Sinatra::Application with your own app class, but just to reopen it and add your own methods? That would fix at least one of the problems I mentioned.

Tanner Burson

unread,
Jun 11, 2009, 2:28:10 PM6/11/09
to sina...@googlegroups.com
The recommendation when you want to do sub-class style applications, is to inherit from Sinatra::Base.  If you're doing sublcass style apps, make sure to require 'sinatra/base' instead of 'sinatra'. 

Sinatra::Application only comes into play with 'classic' style applications, that use the top level methods.

--Tanner
--
===Tanner Burson===
tanner...@gmail.com
http://www.tannerburson.com

Alex Chaffee

unread,
Jul 19, 2009, 2:25:40 PM7/19/09
to sina...@googlegroups.com
So, back to the original question. Any suggestions on how to split an application with many routes/actions into multiple files?
On Thu, Jun 11, 2009 at 10:47 AM, Alex Chaffee <ale...@gmail.com> wrote:

Marcos Vanetta

unread,
Jul 19, 2009, 5:10:41 PM7/19/09
to sina...@googlegroups.com
Hi, meybe if you handle some routes in an extension.
check out the extensions tutorial [1]. There are is an example in where the routes are in a different file!

enjoy
malev

[1] http://www.sinatrarb.com/extensions.html

Will

unread,
Jul 29, 2009, 11:39:56 PM7/29/09
to sinatrarb
Hi all ... !!


I'm interested in the same thing myself. So I'm quite interested in
this thread. I had a few 'fails' as well as a couple of 'wins' but I
havent' got a satisfying *feel* yet.

I show you the structure I have so far -- Suggestions for better ways
to achieve the same result are welcome. I feel like I know what I
need to do (want to do) but not got enough Rack/Sinatra/Ruby
background. Hopefully we can get something to work between us.

I am being a little experimental so bear with me, the objectives are
quit simple so far. I want a stable foundation for different kinds of
apps that I might want to do. :-) Objectives for the small
prototype.

1. Keep a RESTful structure see [2]
2. Using just the 4 x RESful CRUD Verbs per object (POST GET PUT
DELETE)
3. I want to separate by Nouns (REST objects) by file and folder
-- That give
object decomposition.
4. They should be able to share helper code and templates, etc.

Example:

Application/
|
+--- home/ .. .. .. .. .. .. Basic web site code (e.g.
root page, about, etc.)
|
+--- things/ .. .. .. .. .. .. Individual noun (objects)
are stored under this tree
| |
| +--- person/ .. .. .. Person object type
methods.
| | |
| | +--- stuff/ .. .. .. More specific
stuff for this person.
| |
| +--- address .. .. .. Address object type
methods.
|
+--- public .. .. .. .. .. .. Static pages images, etc.
:

There would be more, common things that every app. uses, etc. like
Data, Model, Controller (or logic). At present the notion is for REST-
services, that feed a web site. A further step would be accessing
Nouns from controllers or views as well as just exposing an interface.

So far so good.

I took some time looking at the Sinatra::Base idea, I think that makes
a lot of sense for things to be modular rather than the flat look of
the traditional app. I'm not too sure what I've got is effective, it
works for now.

module Components
class HelloBase < Sinatra::Base
# add settings and stuff like that here

class HelloApp < HelloBase
# I put helpers in here so they are common
end #module

require 'home'
require 'things/person/POST'
require 'things/person/GET'
require 'things/person/stuff'/POST'
require 'things/person/stuff'/GET'
require 'things/address/POST'
require 'things/address/GET'


La la la ... In each of those little verb files (e.g. GET.rb)

module Components
class HelloApp < HelloBase
#-- add person GET pattern here and other code I may need

So far, it seems that I had do extend my Application class "HelloApp"
to add my separate functions. I thought that the idea of a
"Components" namespace was ultimately a good way to go.

What I'd have preferred is something like ...

module Components
class Person < HelloApp
#-- add person GET pattern here and other code I may need

Or even better ...

module Components
class Person < HelloBase
#-- add person GET pattern here and other code I may need

And keep anything I want shared like the helpers in the base class
("HelloBase"). Or something like that.

I reckon that Normally one file is enough for all POST, GET, PUT and
DELETE because they are simple enough bits of code (most of the
time). At first I thought I'd like to leave it granular -- See how it
works out.


For now, I don't seem to have 'visibility' to the helpers unless the
modules are part of the "Components::HelloApp" class. I can probably
say the same if I used the HelloBase. I haven't gone into the
visibility implications yet because I'm not sure this configuration
really give me what I'm looking for.

Comments welcome.

Cheers,
Will

----
> [1] http://www.sinatrarb.com/extensions.html
[2] Common REST mistakes
http://www.prescod.net/rest/mistakes
[2] Common REST mistakes
http://www.prescod.net/rest/mistakes

will

unread,
Jul 30, 2009, 5:51:00 AM7/30/09
to sinatrarb
Please forgive about the "ascii graphs" ... my display shows them as
very messy.

How do you makd your 'ascii diagrams" line-up?

w.

David James

unread,
Jul 30, 2009, 4:55:12 PM7/30/09
to sinatrarb
This is a timely thread.

To take an example from a project I'm working on... I want several
helper functions called 'validate_params'. They would be different
for each 'grouping' of actions' (roughly speaking, a controller).

How would I do this? Currently I see only one way: create subclasses
of Sinatra::Base. (If there are other ways, please let know, but I'm
skeptical, because in Sinatra::Application all helpers are global to
the application).

Here's the bummer. On my system, creating a class that inherits from
Sinatra::Base costs 13 MB. I like the idea of running a small sinatra
app at 13 MB. I don't like the idea of running an app at 13 MB x 5 or
x 10.

At the end of the day, I don't really need all the functionality of a
sub-app -- all I want is name-spacing. I would imagine others are in
the same boat.

Thanks,
David

Christopher Schneider

unread,
Jul 30, 2009, 5:08:24 PM7/30/09
to sina...@googlegroups.com
I'd do something like this:

LoginValidator.validate(params) => true

ImageValidator.validate(params) => false
ImageValidator.errors_for(params) => [whatever, you, want, to return]

But you're right, there's no way that I can think of to namespace
helpers the way you want. But the trivial workaround is to have them
be separate classes with specific roles.

Chris

will

unread,
Jul 31, 2009, 8:28:19 AM7/31/09
to sinatrarb
Hi gang ...

I'm still reading -- And looking. How many of you have looked at
Needle?

* http://needle.rubyforge.org

There's an example with 'authenticator'

* http://needle.rubyforge.org/chapter-3.html#s2

It also seems to offer a namespace thing.

It would be interesting if there's anyone ono the list who has put
these two together?

I'm still wrapping my head around it, and I like it; it or something
like it looks like it can be very useful.

Thanks,
Will


On Jul 31, 7:08 am, Christopher Schneider <ch...@christopher-

Alex Chaffee

unread,
Jul 31, 2009, 9:16:56 AM7/31/09
to sina...@googlegroups.com
Update: I went ahead and used the "extension" technique suggested by
Marcos. It works OK but is a little verbose for my taste. I'll post a
sample of what I have to a gist right now and I'll integrate it into
Vegas when I get some time.

http://gist.github.com/159216

This lets me split actions into related chunks and put them in their
own namespace (a module) and subdirectory, which is basically all I
was asking for.

Nate W

unread,
Jul 31, 2009, 12:22:05 PM7/31/09
to sinatrarb
Nice pattern, I agree it's a bit verbose. Although really no more
verbose than Rails/Merb - they just have generators so it's easy to
ignore the "module LoginHelper" and "class LoginController" that would
be in a Rails app

On Jul 31, 6:16 am, Alex Chaffee <ale...@gmail.com> wrote:
> Update: I went ahead and used the "extension" technique suggested by
> Marcos. It works OK but is a little verbose for my taste. I'll post a
> sample of what I have to a gist right now and I'll integrate it into
> Vegas when I get some time.
>
> http://gist.github.com/159216
>
> This lets me split actions into related chunks and put them in their
> own namespace (a module) and subdirectory, which is basically all I
> was asking for.
>
> ---
> Alex Chaffee - a...@stinky.com -http://alexch.github.com

Christopher Schneider

unread,
Jul 31, 2009, 12:30:33 PM7/31/09
to sina...@googlegroups.com
The extension method in that gist is exactly what I used recently on a
mid sized project, and it worked great for keeping things organized,
while still feeling much lighter weight than a Rails approach.

It's far too heavy for the one or two page apps, but it's perfect for
the 20-30 page apps.

Chris

David James

unread,
Aug 2, 2009, 1:24:15 AM8/2/09
to sinatrarb
Can you share your 'register' code? I'm curious -- why not just use
'include Helpers' in the action and 'def self.included(other_module)'
in the Helpers module?

On Jul 31, 9:16 am, Alex Chaffee <ale...@gmail.com> wrote:
> Update: I went ahead and used the "extension" technique suggested by
> Marcos. It works OK but is a little verbose for my taste. I'll post a
> sample of what I have to a gist right now and I'll integrate it into
> Vegas when I get some time.
>
> http://gist.github.com/159216
>
> This lets me split actions into related chunks and put them in their
> own namespace (a module) and subdirectory, which is basically all I
> was asking for.
>
> ---
> Alex Chaffee - a...@stinky.com -http://alexch.github.com

David James

unread,
Aug 2, 2009, 2:15:24 AM8/2/09
to sinatrarb
Never mind -- I should have read http://www.sinatrarb.com/extensions.html
first! :)

Rick Moynihan

unread,
Aug 3, 2009, 9:04:35 AM8/3/09
to sina...@googlegroups.com
2009/7/30 David James <dja...@sunlightfoundation.com>:

We're also beginning to find ourselves in this position. My original
idea was to split our application into a number of small sinatra apps
and tie them together with rackup URL maps. However, it seems that
Rack's url-mapping features are more cumberosome than sinatra's path
DSL, and given the point you've raised about the costs of inheriting
from Sinatra::Base, I'm beginning to wonder what advantages there are
to splitting a monolithic app into multiple apps, rather than adopting
the modular approach?

What is it that a modular app gives you over a registered module? Is
it simply the ability to have different options set, such as views,
sessions, logging etc?

R.

Rick Moynihan

unread,
Aug 3, 2009, 9:30:57 AM8/3/09
to sinatrarb
On Jul 31, 2:16 pm, Alex Chaffee <ale...@gmail.com> wrote:
> Update: I went ahead and used the "extension" technique suggested by
> Marcos. It works OK but is a little verbose for my taste. I'll post a
> sample of what I have to a gist right now and I'll integrate it into
> Vegas when I get some time.
>
> http://gist.github.com/159216
>

A question... The Sinatra extensions page ( http://www.sinatrarb.com/extensions.html
) indicates that when creating a module you should place it in the
Sinatra namespace and explicitly call register. Yet your module
doesn't appear to do this. Is this deliberate or not?

I'm not entirely clear why the pattern below is important... Is it to
force classic apps to register modules without an explicit call to
register?

module Sinatra
module MyModule
#... stuff here
end
register MyModule
end

Should modules be registered within the Sinatra namespace as above?
Or should they be defined as in Alex's gist, i.e. in their own
namespace with no call to register. Either way some clarification of
these issues on the extensions page would be great! :-)

R.

Thomas Sullivan

unread,
Aug 3, 2009, 9:33:56 AM8/3/09
to sina...@googlegroups.com
I'm still grasping the best method between having a straight program
or a "modular" one that requires sinatra/base - most of my skeleton
apps have been small enough, but lately I've wanted to toss them up
into Heroku and think the sinatra/base method is best.

A bit of a digression here, just saying that the idea of having a
"best method" may work for some - but the "simplest" method may be the
one we want in the docs.

Alex Chaffee

unread,
Aug 3, 2009, 11:40:47 AM8/3/09
to sina...@googlegroups.com
> A question... The Sinatra extensions page ( http://www.sinatrarb.com/extensions.html
> ) indicates that when creating a module you should place it in the
> Sinatra namespace and explicitly call register.  Yet your module
> doesn't appear to do this.  Is this deliberate or not?

It's because it's not really an extension, but an integral part of my
app. (It refers to my own domain objects like User and Views::Login.)
So I'm kind of misusing the feature... or, let's say, innovating! I
think that namespace rule was more of a suggestion anyway, right?

And you can't see it there but I do explicitly call register in my
app.rb file via "register Actions::Login".

Cheers -

- A

Rick Moynihan

unread,
Aug 5, 2009, 6:28:46 AM8/5/09
to sina...@googlegroups.com
2009/8/3 Alex Chaffee <ale...@gmail.com>:

>
>> A question... The Sinatra extensions page ( http://www.sinatrarb.com/extensions.html
>> ) indicates that when creating a module you should place it in the
>> Sinatra namespace and explicitly call register.  Yet your module
>> doesn't appear to do this.  Is this deliberate or not?
>
> It's because it's not really an extension, but an integral part of my
> app. (It refers to my own domain objects like User and Views::Login.)
> So I'm kind of misusing the feature... or, let's say, innovating! I
> think that namespace rule was more of a suggestion anyway, right?

Yes, I see that. I guess my question was more as to what the
differences are between this form:

module Sinatra
module MyExtension
# stuff here...

self.registered(app)
# stuff...
end

end

register MyExtension
end

And this form:

module MyExtension
self.registered(app)
# stuff...
end
end

i.e. is there a difference between defining an extension inside the
Sinatra module (and calling register from within it), and defining it
outside of the Sinatra namespace and not explicitly calling
register??? My guess is that the later approach won't register
automagically for classic apps. Is this true?? The page at
http://www.sinatrarb.com/extensions.html doesn't seem to explain this,
but appears to favour the former approach.

Thanks again,

R.

Reply all
Reply to author
Forward
0 new messages