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...
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.
On Thu, Jun 11, 2009 at 10:47 AM, Alex Chaffee <ale...@gmail.com> wrote:
> 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...
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.
> 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.
> On Thu, Jun 11, 2009 at 10:47 AM, Alex Chaffee <ale...@gmail.com> wrote:
>> 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...
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.
On Thu, Jun 11, 2009 at 1:20 PM, Alex Chaffee <ale...@gmail.com> wrote:
> 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.
> On Thu, Jun 11, 2009 at 11:13 AM, Blake Mizerany <blake.mizer...@gmail.com
> > wrote:
>> 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.
>> On Thu, Jun 11, 2009 at 10:47 AM, Alex Chaffee <ale...@gmail.com> wrote:
>>> 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...
On Thu, Jun 11, 2009 at 10:47 AM, Alex Chaffee <ale...@gmail.com> wrote: > 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...
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!
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
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.
On Jul 20, 7:10 am, Marcos Vanetta <marcosvane...@gmail.com> wrote:
> 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!
> 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.
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.
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.
> 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.
> 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
> On Jul 30, 2009, at 1:55 PM, David James wrote:
> > 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).
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.
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.
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.
> 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.
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.
> 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.
>> 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.
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.
> 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.
> 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.
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?
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.
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! :-)
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.
On Mon, Aug 3, 2009 at 9:30 AM, Rick Moynihan<rick.moyni...@gmail.com> wrote:
> 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.
> 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! :-)
> 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".
>> 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.