What Ruby Technique Does Rails Use to Make My Controller Methods Render Views

What Ruby technique does Rails use to make my controller methods render views?

On the controller, there is a method called render_for_text. This takes a string and sets the result as the response body. Even if you don't render as text, rendering a view file simply reads the file's contents, evaluates it, and passes it to the render_for_text method. The method then stores sets an instance variable called @performed_render to true, which tells rails that the controller has already rendered a view.

There is then a method called performed? that indicates whether or not the render action has been called. It does this by checking if @performed_render or @performed_redirect is true.

With the above information, the very first line of the render method should make sense now, and hopefully answer your question:

raise DoubleRenderError, "Can only render or redirect once per action" if performed?

multiple controllers in one view (ruby on rails MVC)

  1. Technically, this terminology is somewhat confusing. By default each method in a controller corresponds to a similarly-named view file, so it may be better to word the question as "Is it good practice to render a view that is not the default view?" And the answer is, of course, it depends. It is a technique commonly used to DRY up controller code and if there are advantages to be gained by it, in your application, I would certainly use it. In fact, the controller code generated by the default resource scaffolding in Rails uses it in the create and update methods. I think you could make the argument that anything in Rails core is, if not a best-practice, at least within the limits of sanity.

  2. With that being said, there may be opportunities for improvement. A common way to handle the same thing would be to route your create and update requests to the same controller action and use the same controller view. If that is not feasible, at least be assured that you will not need to redefine the variables. From the official documentation:

Using render with :action is a frequent source of confusion for Rails
newcomers. The specified action is used to determine which view to
render, but Rails does not run any of the code for that action in the
controller. Any instance variables that you require in the view must
be set up in the current action before calling render.

  1. You shouldn't need to do that... Not in this situation, or any other one that I can think of. Any time you see something super hackey like that in Rails is a sign that something probably isn't quite right, and that there are probably better ways to handle the same thing.

Bonus: If you need to go somewhere that already is in charge of setting itself up, and you don't need access to any of the in scope variables, a redirect is probably a better choice. That way, you don't need to re-describe your controller logic in multiple places. Here is an example from your posted code:

    # inefficient and not DRY
@user = User.find(current_user)
@microposts = @user.microposts.paginate(page: params[:page])
render 'users/show'

# does the same thing as above (in this case)
redirect_to users_path

Rails - Is it better to use class methods in controller or Views

From practical perspective, it is always a hard decision where to draw the line between view layer and controller logic. It could be questionable to fill your controller with the likes of:

# controller
@project_names = Project.names

# view
<%= @project_names.join(", ") %>

Only to use the @procject_names in the view.

But keeping that code out from views would give you later the opportunity to do this without changing the view:

# Controller is updated
@project_names = Project.names

# Show only matching projects
if params[:search]
@project_names = @project_names.select{|n| n =~ /#{params[:search]}/}
end

# view - still the same
<%= @project_names.join(", ") %>

Also, take look at the Draper gem which builds upon the
Decorator pattern and ViewModel of the MVVM pattern.

With Draper, you can keep even your controllers cleaner and have multiple decorators for same object depending on the need (e.g one for web, other for mailer) while still using same view code to render output.

Common thing to place in the Decorator is localized dates which depend on logged in user, therefore don't fit into view, but would clutter the controller with view-layer logic.

What properties and methods does an Application Controller have?

In a standard rails application, all the app's controllers are inherited from ApplicationController which inherits from ActionController::Base, which contains classes and method definitions for handling HTTP request that enters your application's codebase from the client (web-browser, bots, mobile devices, etc), extract request parameters, process data and return a response back to the client in proper format (XML, HTML, JSON, etc) as per the request.

So, from an OOP point of view, ApplicationController serves at the top of the hierarchy of all your application's controllers. Thus, most of the common filters/hooks which are required in every part of your applicaion such as: handling authentication, authorization, etc are defined here as this is the first place that the HTTP request hits on your application before delegating to the specified controller.

ApplicationController object is instantiated when there is a presence of HTTP request from a client. For example, if you have an action test on application_controller.rb with routes.rb entry get '/test' => 'application#test':

class ApplicationController < ActionController::Base

def test
puts "Instance: #{self}"
puts "Instance Methods: #{self.methods}"
end

end

You can start your local server and visit http://localhost:3000/test and check your server logs to inspect the ApplicationController object and it's methods.

For detailed inspection, I would recommend adding gem 'pry' to your Gemfile and bundling it, then using binding.pry on the ApplicationController#test action, which allows you to further inspect the object on your server on runtime.

How to move logic out of views and into controller in Rails

As it is, in the view, it's probably fine. However, you could (possibly, depending on your app) refine it a little by putting the board/topic information in the URL using nested routes. Routes that would look something like this:

/boards/4/posts
/topics/133/posts

Then your "is it from a board or a post" logic could happen in your PostsController. Again, whether or not this is 'better' is dependent on your requirements, but that's an alternative way of approaching this.

As a side note, you can slim down your conditionals a little. if @board is the same as if @board != nil (unless you have a special case for @board being false. nil and false will both evaluate as false).

Rails - Multiple Views(admin, regular) from same action(index)

From within your controllers, just render view you want to serve for user, I mean:

ProductsController
def index
# ...
render 'index'
end

Admin::ProductsController
def index
# ...
render 'products/index'
end


Related Topics



Leave a reply



Submit