Two Controllers for One Shared View in Ruby on Rails

Two controllers for one shared view in Ruby on Rails

I have a similar situation with one of my projects. All the delete views for most controllers are styled the same way, display the same confirmation boxes, and simply renders a predictable display of whatever object is being deleted.

The solution was quite simple and elegant in my opinion. Simply put, what we (the developers) did was create a new directory in app/views called shared and put shared views in there. These could be full template files or just partials.

I would suggest using a shared template (in neither categories nor photos view directories, but rather in the shared directory) and rendering it manually from the view.

e.g. have a method as such in both controllers and a file app/views/shared/photo.html.erb:

def show
@photo = Photo.first # ... or whatever here
render :template => 'shared/photo'
end

This should successfully render the shared template. It is the DRYest route and doesn't have the feeling of pollution you get when using a more-or-less empty view in each controller's view directory just to include a shared partial, as I understand your question is suggesting.

Two views, two controllers, one model, different re_direct in each controller based on view

You can get referer path and redirect action to this path:

# comments_controller.rb

def comment_create_redirect_path
return root_path if request.try(:referer).blank?
URI(request.referer).path
end

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

link_to tag used for two controllers with shared (one) view

Some notes: try avoiding #eval method in rails, and params variable outside of controller code, so:

= link_to "New #{env.camelize}", send(:"new_#{env}_path"), class: 'btn btn-primary btn-xs', data: ajax_modal_data("form")

and call of controller to render:

 render :index, locals: { env: safe_controller_name }

def safe_controller_name
controllers = %w(templates estimates)
if ! controllers.include? params[:controller]
raise
end
params[:controller].singularize
end

two controllers in one view in Rails

What I'm trying to do, is to put on the same view, the users informations and the microposts form

I think you are trying to create a new micropost inside the user show view. If so, you can include the below:

<%= form_tag(controller: :microposts, action: :create) do %>
...
<% end %>

When you submit the form, it will call the action create in the microposts controller.

How to use two controllers with one model in Rails

The answer to this question is related to your last question. I've updated my answer there, but in a nutshell, the problem is not with respond_with (which as @jiri-pospisil points out you don't really need) but with your form generated by simple_form_for. The action url in that form defaults to /orders because @purchase is an instance of the class Order.

To fix that problem, specify the url in the form:

= simple_form_for @purchase, :as => :purchase, :url => purchases_path(@purchase) do |f|
= f.error_notification
= f.input :name
= f.button :submit

You'll then find that you have another problem: after the new order (purchase) is created, respond_with will redirect to the show action of OrdersController. To fix that, you can use the location option:

def create
@purchase = Order.new(params[:purchase])
if @purchase.save
respond_with(@purchase, :location => purchases_path(@purchase))
...

As you can probably tell at this point, using two controllers for a single model this way becomes somewhat convoluted, so you might want to consider namespaces instead.

Rails: Methods shared by multiple controllers

As you are using Rails 4 (This approach should work in newer versions of Rails as well), the recommended way of sharing code among your controllers is to use Controller Concerns. Controller Concerns are modules that can be mixed into controllers to share code between them. So, you should put the common helper methods inside the controller concern and include the concern module in all of your controllers where you need to use the helper method.

In your case, as you want to share method3 between two controllers, you should put it in a concern. See this tutorial to know how to create concern and share codes/methods among controllers.

Here are some codes to help you get going:

Define you controller concern:

# app/controllers/concerns/your_controller_concern.rb
module YourControllerConcern
extend ActiveSupport::Concern

included do
helper_method :method3
end

def method3
# method code here
end
end

Then, include the concern in your controllers:

class CartsController < ApplicationController
include YourControllerConcern
# rest of the controller codes
end

class OrdersController < ApplicationController
include YourControllerConcern
# rest of the controller codes
end

Now, you should be able to use method3 in both controllers.

Rails: Combining Multiple Controller Actions with their own Views into a Single View

As I understand it - you want to reuse both the views and the code that populates @post_array and @request_array in your home view. I see these as two different concerns with different solutions:

  • For reusing the views you can use partials. By having partials for the posts feed and the request feed you could reuse the view in the home view.
  • And for reusing the way you populate the @post_array you could try and place that logic in somewhere where your HomeController has access to, ideally your Post model (i.e. Post.feed(current_user, location)) or a service object. Another, hacky way to do this is by placing these methods in the ApplicationController, making them available in all controllers.

How to make two controllers share one controller action?

I would first have Reminder and Invoice extend a shared superclass or mix in a module to both of them with their shared functionality. I prefer the latter since I prefer delegations and mixins to inheritance.

Secondly, you should create a module to mix into your controllers with a method like this:

def download_helper(your_superclass_instance)
if @your_superclass_instance
respond_to do |format|
format.pdf { |pdf| render_pdf("attachment") }
end
#all the rest where you replace @reminder or @invoice with @superclass_instance
end

With the module containing download_helper mixed into your controllers, you would then simply call it like this:

def download
@invoice = Invoice.find_by(:download_code => params[:id])
download_helper(@invoice)
end

And obviously do the same with RemindersController.

I am pretty sure there is some clever metaprogramming you can do to make even that first find_by call part of download_helper so you can simply pass in the Invoice or Reminder class itself. But I would have to research how to do that, and I think that's bordering on overengineering by being too clever by half and making things less readable in the process.



Related Topics



Leave a reply



Submit