Application structure tutorials?

Hi,

now that I've done a few Rails tutorials and understand the basics of AR, migrations, templates and such, I would like to embark on my first own project. It's a rewrite, so most of the functionality has been done already (by me). But there is something that I missed in all the tutorials I've read: structure design.

Assume you have a web app that has users who can sign up, login and logout, and users can seach for, and CRUD, some sort of items which get saved in your database. Plus you have a WelcomeController that does the main page, the "about us" links, partner sites and so on.

The question is: how do I integrate all this? For example, I need some actions that are available globally. For every page request I need to find out whether the user is logged in and display his account info in the sidebar. I guess I need a :before_filter to do this, but how does this work exactly? I would also like to offer a search form on the main page without having to resort to linking to "Search:" first.

I have a class "User" and a user_controller. Does logging in and out belong into this controller or does it make sense to create another model/controller (Login) for this?

The user has_many addresses and has_many cars (for example). What's the best way of presenting this info to the user in a way that he can most easily edit all his info? Are there usability studies somewhere that weigh different presentation structures?

For what should I create partials, which page elements should commonly be cached, should I use link_to for all (even static) links? How do I include completely static data (does Rails realize whether a static file with the given path exists? Does that maybe depend on the webserver used, and the .htaccess files?)

I have a feeling I'm missing the "big picture" here ... I think I got a rough idea how parts of Rails work but I don't know how to make them work together to fit my project.

Any pointers to documentation would be great.

Thanks!

Jens

now that I've done a few Rails tutorials and understand the basics of AR, migrations, templates and such, I would like to embark on my first own project. It's a rewrite, so most of the functionality has been done already (by me). But there is something that I missed in all the tutorials I've read: structure design.

Assume you have a web app that has users who can sign up, login and logout, and users can seach for, and CRUD, some sort of items which get saved in your database. Plus you have a WelcomeController that does the main page, the "about us" links, partner sites and so on.

The question is: how do I integrate all this? For example, I need some actions that are available globally. For every page request I need to find out whether the user is logged in and display his account info in the sidebar. I guess I need a :before_filter to do this, but how does this work exactly?

In your app/controllers/application.rb, create a :before_filter that does whatever checking you need it to do, then set say '@user' to the logged in user, otherwise set it to nil. Now you have '@user' available in all of your controllers and views. Personally I don't like @user since you might have an admin that manages a @user and then you've got conflicts. Maybe @the_end_user or @the_logged_in_user or even just @the_user.

I would also like to offer a search form on the main page without having to resort to linking to "Search:" first.

Create app/views/shared/_search.rhtml with the search form "bucket" html content. Then on whatever page (or in your layout) you want that on do:

<%= render :partial => 'shared/search' %>

I have a class "User" and a user_controller. Does logging in and out belong into this controller or does it make sense to create another model/controller (Login) for this?

I'd leave it in the user controller.

http://host/user/login http://host/user/logout http://host/user/settings etc...

The user has_many addresses and has_many cars (for example). What's the best way of presenting this info to the user in a way that he can most easily edit all his info? Are there usability studies somewhere that weigh different presentation structures?

I dunno, but I imagine there's lots of info on how to do this...

For what should I create partials,

Whatever you might think you need to share b/n views. Or if you know you might be updating a part of your site with ajax, make that into a partial so it's easy to do...

which page elements should commonly be cached,

Impossible to say without knowing your usage patterns. If nothing changes, cache it. If it's highly dynamic, don't.

should I use link_to for all (even static) links?

Sure.

How do I include completely static data (does Rails realize whether a static file with the given path exists? Does that maybe depend on the webserver used, and the .htaccess files?)

What type of static data? Images, Javascript, Stylesheets all have their own helper methods. Use those. Otherwise for PDF/downloads just use link_to. For flash and embedded things, just wrap it up in the right tags yourself (or see if there's a helper which there probably is)

Any pointers to documentation would be great.

good luck!

Philip Hallstrom wrote:

Assume you have a web app that has users who can sign up, login and logout, and users can seach for, and CRUD, some sort of items which get saved in your database. Plus you have a WelcomeController that does the main page, the "about us" links, partner sites and so on.

The question is: how do I integrate all this? For example, I need some actions that are available globally. For every page request I need to find out whether the user is logged in and display his account info in the sidebar. I guess I need a :before_filter to do this, but how does this work exactly?

In your app/controllers/application.rb, create a :before_filter that does whatever checking you need it to do, then set say '@user' to the logged in user, otherwise set it to nil. Now you have '@user' available in all of your controllers and views. Personally I don't like @user since you might have an admin that manages a @user and then you've got conflicts. Maybe @the_end_user or @the_logged_in_user or even just @the_user.

Hello Philip,

thanks for your reply! I'm guessing that using your suggested method, I can define exceptions here, right? I.e. I don't need to check for a user if the user calls User#login (because only anonymous users can do that) or Welcome#about_us (because there, I don't care whether he's logged in).

How would I define exceptions? Can I say

        :before_filter check_user, :except => User#login, Welcome#about_us

? AFAIUnderstand the docs, I can only specify exceptions within the same controller.

I would also like to offer a search form on the main page without having to resort to linking to "Search:" first.

Create app/views/shared/_search.rhtml with the search form "bucket" html content. Then on whatever page (or in your layout) you want that on do:

<%= render :partial => 'shared/search' %>

OK, that looks simple. Thanks :slight_smile:

I have a class "User" and a user_controller. Does logging in and out belong into this controller or does it make sense to create another model/controller (Login) for this?

I'd leave it in the user controller.

http://host/user/login http://host/user/logout http://host/user/settings etc...

Makes sense. How about an admin user? I'm planning to also do

        :before_filter :check_admin, only => :list, :edit, ...

where

        def check_admin                 @user.is_admin         end

Does creating a seperate "admin" controller make sense? I think not, since the admin is just a special form of "user" (right)?

The user has_many addresses and has_many cars (for example). What's the best way of presenting this info to the user in a way that he can most easily edit all his info? Are there usability studies somewhere that weigh different presentation structures?

I dunno, but I imagine there's lots of info on how to do this...

I imagine too, but most of the people who have it charge for it :slight_smile: I guess you need a certain expertise to make the site "feel" good.

For what should I create partials,

Whatever you might think you need to share b/n views. Or if you know you might be updating a part of your site with ajax, make that into a partial so it's easy to do...

Ah. Good thinking. Yes, I'm planning to add a lot of AJAX later on.

which page elements should commonly be cached,

Impossible to say without knowing your usage patterns. If nothing changes, cache it. If it's highly dynamic, don't.

Rails includes a profiler, right? So I can see how long stuff takes.

In my old PHP app, populating a select list out of the DB, which was displayed on the main page, took nearly 80% of the total rendering time. It was still about 0.5s, on average, but the server load went down an order of magnitude after I started caching that HTML snippet.

should I use link_to for all (even static) links?

Sure.

Devil's advocate ... isn't that unnecessarily slower than using direct links? :wink:

Well, I guess I'll have to profile it ...

How do I include completely static data (does Rails realize whether a static file with the given path exists? Does that maybe depend on the webserver used, and the .htaccess files?)

What type of static data? Images, Javascript, Stylesheets all have their own helper methods. Use those. Otherwise for PDF/downloads just use link_to. For flash and embedded things, just wrap it up in the right tags yourself (or see if there's a helper which there probably is)

"static data" as in "About us" and "Contact us" pages, terms of service and whatever else.

Any pointers to documentation would be great.

good luck!

Thanks! :slight_smile:

Jens

Assume you have a web app that has users who can sign up, login and logout, and users can seach for, and CRUD, some sort of items which get saved in your database. Plus you have a WelcomeController that does the main page, the "about us" links, partner sites and so on.

The question is: how do I integrate all this? For example, I need some actions that are available globally. For every page request I need to find out whether the user is logged in and display his account info in the sidebar. I guess I need a :before_filter to do this, but how does this work exactly?

In your app/controllers/application.rb, create a :before_filter that does whatever checking you need it to do, then set say '@user' to the logged in user, otherwise set it to nil. Now you have '@user' available in all of your controllers and views. Personally I don't like @user since you might have an admin that manages a @user and then you've got conflicts. Maybe @the_end_user or @the_logged_in_user or even just @the_user.

Hello Philip,

thanks for your reply! I'm guessing that using your suggested method, I can define exceptions here, right? I.e. I don't need to check for a user if the user calls User#login (because only anonymous users can do that) or Welcome#about_us (because there, I don't care whether he's logged in).

How would I define exceptions? Can I say

       :before_filter check_user, :except => User#login, Welcome#about_us

? AFAIUnderstand the docs, I can only specify exceptions within the same controller.

Sure. But take a look at application.rb. It's a ApplicationController class. And if you look at your other xxxx_controller.rb files you'll see that those classes inherit from ApplicationController. So by putting it in application.rb you're putting it in all your models.

You're right though. If you had User#login and Foo#login and had your before filter look like this:

:before_filter check_user, :except => [:login, :about_us]

I don't know how it would know which is which. But I haven't checked the source to see if there's a way to specify specific controllers...

I have a class "User" and a user_controller. Does logging in and out belong into this controller or does it make sense to create another model/controller (Login) for this?

I'd leave it in the user controller.

http://host/user/login http://host/user/logout http://host/user/settings etc...

Makes sense. How about an admin user? I'm planning to also do

       :before_filter :check_admin, only => :list, :edit, ...

where

       def check_admin                @user.is_admin        end

Does creating a seperate "admin" controller make sense? I think not, since the admin is just a special form of "user" (right)?

Depends on how you have things setup. If an admin is just a user with an is_admin boolean field, then probably not. But I don't know your app :slight_smile:

Impossible to say without knowing your usage patterns. If nothing changes, cache it. If it's highly dynamic, don't.

Rails includes a profiler, right? So I can see how long stuff takes.

In my old PHP app, populating a select list out of the DB, which was displayed on the main page, took nearly 80% of the total rendering time. It was still about 0.5s, on average, but the server load went down an order of magnitude after I started caching that HTML snippet.

Yes. You can benchmark as well as just watch the output of your development.log and it will tell you how long it took doing each step.

should I use link_to for all (even static) links?

Sure.

Devil's advocate ... isn't that unnecessarily slower than using direct links? :wink:

Probably. But it's easier. And if you cache it doesn't matter. And as a bonus in 6 months when your site is massively popular and you realize you need to put all your images/stylsheets/javascript on another server to spread the load if you've used the tags you can set a config option to have it prepend the server name to all those links automatically.

How do I include completely static data (does Rails realize whether a static file with the given path exists? Does that maybe depend on the webserver used, and the .htaccess files?)

What type of static data? Images, Javascript, Stylesheets all have

their

own helper methods. Use those. Otherwise for PDF/downloads just use link_to. For flash and embedded things, just wrap it up in the right tags yourself (or see if there's a helper which there probably is)

"static data" as in "About us" and "Contact us" pages, terms of service and whatever else.

If it were me, I'd just put it in the appropriate view files and cache it.

-philip