Topic: Design Suggestions

Since this is about application design, and not a specific question about any one piece of code, I felt it belonged here. Feel free to move it if this is the inappropriate place.

I am a Rails beginner, and my design-fu is weak (well, weak is a strong word...I know exactly what I want, so I guess my Rails-fu is weak).

I've watched Ryan's excellent Railscasts on administration, but it only slightly touches on what I want to do.

I have about 6 models. The administrator needs to do full CRUD on all of them. The next level down would be a regional manager that needs to do SOME CRUD on a subset (the team managers under him, plus the users under these team managers). The next level down would be team managers. They would need to do even less CRUD on their team members. The last level would be the team members. They would be able to CRUD some of their own record, but that's it.

Each team member is responsible for several zip codes. They need to be able to see their zip codes, and several businesses that are in those zip codes, but not edit anything. The team managers would need to see all of their team members, and re-assign zip codes throughout their team. They would also need to edit their team members records. Same for the regional managers, but they need to edit the team managers as well.

Here is where I start getting very confused. Each model has list, show, add, destroy and _form. Add them all up, and that's 30 views. That seems a bit, well, excessive. Is there any way to make the views dynamic, so there's only 5 views instead of 30?

I know it's semi-verboten to do application logic in the views, but then how do I make fields un-editable and downright hide other fields for certain roles?

I normally try to figure stuff out for myself, but I've been wrestling with these questions for 2 days now. Luckily, I'm not working with time constraints, and if I never finish it, no one's the wiser. But, I'd really like to finish it.

By the way, I'm experienced with database design, so my db is set up nicely, and I did figure out relationships in my models (my views work properly with foreign key fields rendering nicely). I also have Agile Web Development with Rails(2nd Ed.), Ruby for Rails, and the Rails Cookbook, but none of them give me a clear idea of how to proceed.

Sorry for the length, feel free to beat me up for details.

Re: Design Suggestions

mikeyv wrote:

I have about 6 models. The administrator needs to do full CRUD on all of them. The next level down would be a regional manager that needs to do SOME CRUD on a subset (the team managers under him, plus the users under these team managers). The next level down would be team managers. They would need to do even less CRUD on their team members. The last level would be the team members. They would be able to CRUD some of their own record, but that's it.

I recommend splitting up the controllers based on the model/resource they manage, not based on how they are accessed. I find it best to not worry about who has access to what at the beginning.

mikeyv wrote:

Each team member is responsible for several zip codes. They need to be able to see their zip codes, and several businesses that are in those zip codes, but not edit anything. The team managers would need to see all of their team members, and re-assign zip codes throughout their team. They would also need to edit their team members records. Same for the regional managers, but they need to edit the team managers as well.

It sounds like the interface may be slightly different between each role. Still I dont recommend thinking about the roles as much at the beginning. Intead write down the behavior each one needs and implement each behavior separately. Then you can add the restrictions of who has access to what actions/views later.

mikeyv wrote:

Here is where I start getting very confused. Each model has list, show, add, destroy and _form. Add them all up, and that's 30 views. That seems a bit, well, excessive. Is there any way to make the views dynamic, so there's only 5 views instead of 30?

30 views really isn't anything. I've some apps with over 100 views. It's not necessarily the number of views which makes it unmanageable, it's the amount of duplication between them. You should be merciless to duplication and remove it using partials, layouts, helper methods, etc.

Sure you could try to merge all of this into 5 views which are smart and dynamic, but then you end up with views which are hundreds of lines long and filled with "if/else" conditions. This seems much more difficult to manage.

mikeyv wrote:

I know it's semi-verboten to do application logic in the views, but then how do I make fields un-editable and downright hide other fields for certain roles?

You often can't move every bit of logic out of the view, but you should move stuff into models whenever possible. For example, here's the wrong and right way to do it:

# wrong
<%= link_to 'edit', edit_item_path if current_user.admin? || item.owner == current_user %>

# right
<%= link_to 'edit', edit_item_path if current_user.can_edit_item? item %>


Putting permission logic in the view is bad if it is at all complex. It should be moved into the model.

Railscasts - Free Ruby on Rails Screencasts

Re: Design Suggestions

Thank you ryanb, I will put your suggestions into practice. Especially this:

[code ruby]<%= link_to 'edit', edit_item_path if current_user.can_edit_item? item %>[/code]

I didn't even consider that. I was about to do it the wrong way tongue

Re: Design Suggestions

mikeyv wrote:

I know it's semi-verboten to do application logic in the views, but then how do I make fields un-editable and downright hide other fields for certain roles?

Just a clarification. You are absolutely correct that application logic does not belong in the views, but that is often misunderstood to mean that no logic is allowed in the views. If you really think about this, that would mean that all views would be static! So much for web apps. smile

So what kind of logic is allowed in views? View logic! Which is exactly the kind of logic you are using when you say "hide this" or "make that un-editable." That said, you should make sure that your rhtml templates should look as much like pure html as possible. First move all logic in the view that you can to your models. Whatever can't be moved to the models, try to move to the controller. Whatever can't be moved to the controller, keep in the view, but try to refactor in into short, readable, self-documenting helpers. You'll be left with very readable views, fat models, skinny controllers, and re-usable helpers. It might make you cry a little.

Re: Design Suggestions

Thank you, fabio, those are great tips. I'm still trying to figure out "where stuff goes" and how to make it all "talk" to each other, one of the hardest parts, IMHO.

Re: Design Suggestions

You are right, that is the hardest part. As a rule, models should be fat (should contain as much of the logic of the application as possible), and controllers should be skinny (just a few small methods). One resource I find invaluable is "The Rails Way" (http://www.therailsway.com) blog by Jamis Buck and Koz (both core rails contributors). Every single one of their posts is rife with code that works, but should be refactored (things aren't in the right place). I've learned more from them than pretty much any other rails resource, except maybe AWDwR. I recommend that any rails programmer read through each one of their posts (its a fairly new blog).

Re: Design Suggestions

Ooooh! Great site! Thanks, fabio

Re: Design Suggestions

This is a tangential post, but here goes: Ryanb - referring to your "wrong/right" of 26548 - I agree with you 100% but have just gotten bummed out by ActiveScaffold's change (from AjaxScaffold's <em>perfect</em> design) of putting column specs into the Controller instead of the Model.

Business logic goes into the model, into the model, into the model.  Any thoughts pro/con?

Re: Design Suggestions

brianb wrote:

I know it's semi-verboten to do application logic in the views, but then how do I make fields un-editable and downright hide other fields for certain roles?
You often can't move every bit of logic out of the view, but you should move stuff into models whenever possible. For example, here's the wrong and right way to do it:

# wrong
<%= link_to 'edit', edit_item_path if current_user.admin? || item.owner == current_user %>

# right
<%= link_to 'edit', edit_item_path if current_user.can_edit_item? item %>


Putting permission logic in the view is bad if it is at all complex. It should be moved into the model.

Hi, a newbie here smile

How can I create that 'can_edit_item?' method ?

Re: Design Suggestions

class User < ActiveRecord::Base
  has_many :items

  def can_edit_item?(item)
    self.role == "Admin" || item.owner == self
  end
end

Re: Design Suggestions

Thanks fabio.

That .owner is a custom field or its a generic field that works with relationships ?

I have a database called 'users' and another one called 'posts'.

posts has a field called: 'user_id'

Re: Design Suggestions

the owner attribute is just from the association and might look like this:

class Item < ActiveRecord::Base
  belongs_to :owner, :class_name => 'User', :foreign_key => 'user_id'
end

In your case, I would do:

class Post < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :posts

  def can_edit_post?(post)
    self.admin? || post.user == self
  end

  def admin?
    self.role == 'admin'
  end
end