Rails STI Controllers - ruby-on-rails

I have a Rails site using STI with the following classes:
Pages
Homepage < Pages
LandingPage < Pages
On the front-end all requests get handled by the Pages controller. However, if the object detected is actually an instance of LandingPage, i'd like to have the action on a LandingPages controller get called. (for example, the show method in the child controller classes will pull in some specific lookups that aren't always relevant).
Any suggestions on how to best accomplish this?
Thanks

This sounds a bit like you are clouding the MVC distinction, but it should be doable.
I'd add a series of tests on the Pages model (e.g. supports_buzzbar_foo? or wiggums_itemization_controller, then override them as appropriate in the subclasses) and use these in the view to conditionally generate the appropriate links to the controller methods you want.
That way you're keeping each part (roughly) doing it's job.

Markus' solution should work. You could also keep your links in the views pointed to Pages, evaluate the incoming object and then redirect_to the appropriate controller based on the object class.
However, unless you're performing completely different actions with each type of object, then you'll wind up with duplicate code in your controllers. So you might be better off sticking with the Pages controller and just adding some methods that handle the extra lookups that are needed for that object.

Related

Do Actions in RCAV = Model in MVC? (Rails)

I'm trying to better understand the Rails workflow, specifically RCAV in relation to MVC. I know that MVC is the typical structure of a Rails app and that RCAV is the standard order of building various components of the app, however, I'm a little confused about the correlation between the two.
For example, I'm assuming the routes in RCAV are what link the models with views and controller in MVC. Is this correct?
I'm also guessing that the controller and view in RCAV are the same as the controller and view in MVC and simply represent the order in which you build them. Is this correct as well?
What I'm really stuck on is the Action part of RCAV - does this represent the Model component of MVC?
Sorry if my question doesn't make sense, just trying to get a better hang of the standard Rails workflow and the order in which various components of an app are typically built. I wish it were a little more distilled i.e. "first build Model, then Views, then Controller" but this whole separate RCAV thing is confusing me a little.
To your title question, no.
To elaborate, it's kind of confusing to map out RCAV (the sequence) against MVC (the framework), but I'll see if I can clarify. It's worth noting that the concepts themselves are not as complex as they seem, it's just confusing to explain.
RCAV
(Route-Controller-Action-View)
Routes are defined in config/routes.rb. You may think of them as the coupling between an HTTP Request and a Method (a specific type of method called an Action). On the one side it defines a path (ex. "/login") and one of the HTTP verbs (ex. "GET"), and says that whenever that type of request is made to that location, to perform the appropriate action. On the other side, an action is given in the form of the name of its controller (ex. "sessions"), and the name of the method itself (ex. "new").
Controllers receive the request according to the routes (so following the above example, if a user navigated to www.example.com/login, they would wind up in the sessions controller, which would be housed in app/controllers/sessions_controller.rb). The Controller may perform a number of functions prior to (and following) the action, in the form of callbacks. Callbacks are arguably a more advanced concept than what you need to understand right now, they are an important step (and the reason I explain RCAV as "Route >> Controller >> Action >> View", not as "Route >> Controller-Action >> View").
Actions are public methods in the Controller. Again, by our above example, you'll be hitting a method named "new" in the sessions_controller when a user navigates to www.example.com/login. Actions may perform any variety of functions, and there is often interaction with the model here (for instance, you may have a session model in app/models/sessions.rb with a method named "logout", and perhaps the first thing you do when you hit the login page is ensure the user is currently logged out by calling Session.logout. This is a poor design, but a decent example). Your action must "return" in one way or another. Assuming your action is meant to be consumed by a user directly through their browser (as opposed to as, say, a JSON or XML service), you will either render a view now, or redirect_to another action.
Views are, dare I say, the "end result" of the action. If nothing is specified, the above example will end by rendering "app/views/sessions/new.html.erb" for the user (Rails defaults to rendering the view whose path matches the controller and action, but as referenced above you could name something different). In this file you will also potentially make reference to the model associated with sessions (but this time inside of ERB tags, to be compiled into HTML).
MVC
(Model-View-Controller)
Models are classes tied, generally, to tables in a database. They contain validations for their fields, class methods, and instance methods. Say your database keeps track of sessions. Then you could potentially grab a list of sessions by calling Session.all anywhere in your application. You'd probably use an action in the Sessions controller to save this list to an instance variable (maybe #sessions), which could in turn be accessed in its appropriate view (<% #sessions.each do |s| %> ... <% end %>).
Views are essentially the web pages. They can bring along their own styles and scripts, and exist in the form of HTML files with embedded ruby. They are the end point of request made to your site (unless you're hitting a service which returns something like JSON or XML instead).
Controllers contain all the actions a user may access, often prepare variables via methods from the models, and generally drop the user into a view.
In Summation
A user accesses a route. The route drops them into a specific action within a controller. The controller (in the form of callbacks) and the action (on its own) prepare data from the model (via instance variables) to be consumed by the view. The view is then rendered into the users browser.
This is a simplification, but in the most standard cases, this is how the MVC framework (and rails, specifically) functions.

How to call controller's method

I have a functionality that doesn't relate to incoming requests (like get,post,etc).
But I want to follow MVC convention and make it like Model-Controller:
in controller part I will process income requests from another parts, decide from which model it should retrieve information, authorization, filtering and so on;
in model part I will store information, validate it, expire it and etc.
In Rails the controller's actions can be called by income request directed by routes file.
The problem for me that if I've created a controller like this:
class SomeController < ApplicationController
def some_action; end
end
How can I call method some_action from SomeController from any place in my application?
P.S. Calling SomeController.new.some_action doesn't seem right to me, because I think all controllers are parts of app object.
You can do this by instanciating the controller.
SomeController.new.some_action
However this is not really an MVC way. I don't really know what you want to use it for, but probably there is a better way. For example it can be done as in the model, as the modification of data should belong there not to a controller.
I think you should probably create a PORO which will encapsulate this functionality in some method. So that any logic dependent functionality should be within that instead of in the controller. Then you can call them in either controller.

Is it considered bad practice to write db queries in a controller rather than a model

I've read that best practice dictates fats models, skinny controllers.
Models should contain business logic such as getting a list of customers based on parameters sent from a controller.
Controllers should contain just enough logic to invoke methods within a model to return to a view.
However I see many examples and tutorials where there is logic within the controller such as a query that accesses a db to get a list of products. I was under the impression that the logic should live in a method inside a model. The controller can then invoke this method, rather than containing the actual logic to query the database.
So if I have a ProductController I might have a Index action which returns an Index View, and I would have a ProductModel which would house my logic to display certain products based on a query string(which the ProductController would pass to said model). Right?
So if I have a ProductController I might have a Index action which returns an Index View, and I would have a ProductModel which would house my logic to display certain products based on a query string(which the ProductController would pass to said model). Right?
That is correct. As per the Model-view-controller architecture:
The model manages the behavior and data of the application domain,
responds to requests for information about its state (usually from the
view), and responds to instructions to change state (usually from the
controller). In event-driven systems, the model notifies observers
(usually views) when the information changes so that they can react.
The view renders the model into a form suitable for interaction,
typically a user interface element. Multiple views can exist for a
single model for different purposes. A viewport typically has a one to
one correspondence with a display surface and knows how to render to
it.
The controller receives user input and initiates a response by making
calls on model objects. A controller accepts input from the user and
instructs the model and viewport to perform actions based on that
input.
Keep the data-related queries and operations in the model; stuff as much as you can in there in accordance with DRY (Don't Repeat Yourself). Make the functions reusable as much as possible so they can be ported into various controllers and used throughout the application as necessary.
The view should contain little - if any - logic outside of view-specific work.
Your controller functions should invoke the model functions required to retrieve and manipulate data, and should be as "thin" as possible (as you pointed out). The smaller and less involved the controller, the easier it will be to add asynchronous features that "don't reboot the application" on the front-end, making for a better user experience. (If you are concerned about this, anyway!)
The Add Controller dialog box in VS 2010 has the option of adding a scaffold with CRUD functions into the controller. This should tell you quite a bit above how the ASP.NET MVC dev. team views this debate.

Rendering Navigation in MVC3

What's the best way to render Navigation in MVC3? In my app the Controller has to decide what should be in the Navigation but as far as I know there's no way to pass a Model up to the _Layout file (where the Navigation html lives) to give it this information.
You can make the model available to your layout.
Define a base class, MyBaseModel, with the properties you want available to the layout.
Have your models subclass MyBaseModel, and ensure that you populate the properties
Have your layout specify #model MyBaseModel
Use the properties
See also this blog post where a similar problem ("we often find ourselves needing to include the same information in every page") is tackled.
Best is a relative and subjective term. I would usually go with an approach similar to druttka's answer but another option is to use RenderAction() to invoke a controller action
<div>#Html.RenderAction("action", "controller")</div>
I will offer some counterpoints to #druttka's answer. But mostly, you have to decide which trade off you want to make.
If you use a model for your view, you are now forcing 2 things:
Every view must be strongly typed
Every action must call the function to populate all levels of nav included in your view model
The first point isn't so bad, because there are very few pages in a typical non-static site which wouldn't derive from a model anyway, and its easy enough to create an empty model for those pages. The second point is much more annoying however. Each function has to instantiate it's own model, and then populate the nav properties for every level of nav provided by the model. This can be quite cumbersome, but can be fairly elegantly handled in OnActionExecuted for at least nav which is not specific to a given action.
The alternative is to add the nav to the ViewBag. This can be done whenever and does not force you to specify a model for every view, which is great for the flexibility in those cases where you do not need to specify a model. It should be noted however that the ViewBag in asp.net mvc 3 is of type dynamic, which you cannot use as a parameter in a lambda function, thus preventing you from doing something like #Html.DisplayFor(viewBag => viewBag.MainNav) in your layout, which is a real drag. You can still render partial and specify the appropriate DisplayTemplate however.

Rails Model, View, Controller, and Helper: what goes where?

In Ruby on Rails Development (or MVC in general), what quick rule should I follow as to where to put logic.
Please answer in the affirmative - With Do put this here, rather than Don't put that there.
MVC
Controller: Put code here that has to do with working out what a user wants, and deciding what to give them, working out whether they are logged in, whether they should see certain data, etc. In the end, the controller looks at requests and works out what data (Models) to show and what Views to render. If you are in doubt about whether code should go in the controller, then it probably shouldn't. Keep your controllers skinny.
View: The view should only contain the minimum code to display your data (Model), it shouldn't do lots of processing or calculating, it should be displaying data calculated (or summarized) by the Model, or generated from the Controller. If your View really needs to do processing that can't be done by the Model or Controller, put the code in a Helper. Lots of Ruby code in a View makes the pages markup hard to read.
Model: Your model should be where all your code that relates to your data (the entities that make up your site e.g. Users, Post, Accounts, Friends etc.) lives. If code needs to save, update or summarise data related to your entities, put it here. It will be re-usable across your Views and Controllers.
To add to pauliephonic's answer:
Helper: functions to make creating the view easier. For example, if you're always iterating over a list of widgets to display their price, put it into a helper (along with a partial for the actual display). Or if you have a piece of RJS that you don't want cluttering up the view, put it into a helper.
The MVC pattern is really only concerned with UI and nothing else. You shouldn't put any complex business logic in the controller as it controls the view but not the logic. The Controller should concern itself with selecting the proper view and delegate more complex stuff to the domain model (Model) or the business layer.
Domain Driven Design has a concept of Services which is a place you stick logic which needs to orchestrate a number of various types of objects which generally means logic which doesn't naturally belong on a Model class.
I generally think of the Service layer as the API of my applications. My Services layers usually map pretty closely to the requirements of the application I'm creating thus the Service layer acts as a simplification of the more complex interactions found in the lower levels of my app, i.e. you could accomplish the same goal bypassing the Service layers but you'd have to pull a lot more levers to make it work.
Note that I'm not talking about Rails here I'm talking about a general architectural style which addresses your particular problem.
Perfect explanations here already, one very simple sentence as conclusion and easy to remember:
We need SMART Models, THIN Controllers, and DUMB Views.
http://c2.com/cgi/wiki?ModelViewController
The Rails way is to have skinny controllers and fat models.
Do put stuff related to authorization/access control in the controller.
Models are all about your data. Validation, Relationships, CRUD, Business Logic
Views are about showing your data. Display and getting input only.
Controllers are about controlling what data goes from your model to your view (and which view) and from your view to your model. Controllers can also exist without models.
I like to think of the controller as a security guard/receptionist who directs you the customer(request) to the appropriate counter where you ask a teller (view) a question. The teller (view) then goes and gets the answer from a manager (model), who you never see. You the request then go back to the security guard/receptionist (controller) and wait until you are directed to go another teller (view) who tells you the answer the manager (model) told them in response to the other teller's (view) question.
Likewise if you want to tell the teller (view) something then largely the same thing happens except the second teller will tell you whether the manager accepted your information. It is also possible that the security guard/receptionist (controller) may have told you to take a hike since you were not authorized to tell the manager that information.
So to extend the metaphor, in my stereotyped and unrealistic world, tellers (views) are pretty but empty-headed and often believe anything you tell them, security guard/receptionists are minimally polite but are not very knowledgeable but they know where people should and shouldn't go and managers are really ugly and mean but know everything and can tell what is true and what isn't.
One thing that helps separate properly is avoiding the "pass local variables from controller to view" anti-pattern. Instead of this:
# app/controllers/foos_controller.rb:
class FoosController < ApplicationController
def show
#foo = Foo.find(...)
end
end
#app/views/foos/show.html.erb:
...
<%= #foo.bar %>
...
Try moving it to a getter that is available as a helper method:
# app/controllers/foos_controller.rb:
class FoosController < ApplicationController
helper_method :foo
def show
end
protected
def foo
#foo ||= Foo.find(...)
end
end
#app/views/foos/show.html.erb:
...
<%= foo.bar %>
...
This makes it easier to modify what gets put in "#foo" and how it is used. It increases separation between controller and view without making them any more complicated.
Well, it sort of depends upon what the logic has to deal with...
Often, it makes sense to push more things into your models, leaving controllers small. This ensures that this logic can easily be used from anywhere you need to access the data that your model represents. Views should contain almost no logic. So really, in general, you should strive to make it so that you Don't Repeat Yourself.
Also, a quick bit of google reveals a few more concrete examples of what goes where.
Model: validation requirements, data relationships, create methods, update methods, destroy methods, find methods (note that you should have not only the generic versions of these methods, but if there is something you are doing a lot, like finding people with red hair by last name, then you should extract that logic so that all you have to do is call the find_redH_by_name("smith") or something like that)
View: This should be all about formatting of data, not the processing of data.
Controller: This is where data processing goes. From the internet: "The controller’s purpose is to respond to the action requested by the user, take any parameters the user has set, process the data, interact with the model, and then pass the requested data, in final form, off to the view."
Hope that helps.
In simple terms, generally,
Models will have all the codes related to table(s), their simple or complex relationships (think them as sql queries involving multiple tables), manipulation of the data/variables to arrive at a result using the business logic.
Controllers will have code/pointers towards the relevant models for the job requested.
Views will accept the user input/interaction and display the resultant response.
Any major deviation from these will put unwanted strain on that part and the overall application performance may get impacted.
Testing, Testing ...
Put as much logic as possible in the model and then you will be able to test it properly. Unit tests test the data and the way it is formed by testing the model, and functional tests test the way it is routed or controlled by testing the controllers, so it follows that you can't test the integrity of the data unless it is in the model.
j

Resources