Rails Controller - Inherit a Custom Action - ruby-on-rails

I have DRY'd up the controller code using inheritance.
class MastersController < ApplicationController
def index
...
...
end
...
...
end
class ItemsController < MastersController
end
Now I have added a custom action to the MastersController
class MastersController < ApplicationController
def index
...
...
end
...
...
def clone
...
...
end
end
I have added the routes for item
resources :items do
get :clone
end
Now when I try to access myapp.dev/items/1/clone, I get the error
AbstractController::ActionNotFound at /items/1/clone
The action 'clone' could not be found for ItemsController
If I add the 'clone' action in ItemsController the error goes away.
class ItemsController < MastersController
def clone
...
...
end
end
How do I abstract a custom action in Rails Controller?

You can use it like this
def clone
super
end
in your child Controller

It is not possible to use clone as an action which is inherited from a parent controller. This is true of ApplicationController in addition to your MastersController.
Rename your action to copy and you will be able to define it in MastersController and route correctly to ItemsController#copy.
Please note, I cannot tell you why this is. There is a Ruby method clone which is defined on the Object class. I suspect this is the reason - maybe someone with more knowledge could explain.

Related

How to change index method in my controller to be more DRY

I'm a Rails beginner and I learn that I always must try to be more DRY.
I'm have a comment system associated to my content model, and I load my comment with ajax on page scroll.
In my view I have:
%section.article-comments{'data-url' => content_comments_path(#content)}
and in my routes.rb file I have the route
resources :contents, only: :index do
resources :comments, only: :index
end
My comment controller of course is
def index
#content = Content.find(params[:content_id])
#comments = #content.comments
render ...
end
Now I want to add comments also to videos and gallery.
So I need to add a route for every resource and I need a gallery_index and a video_index.
Content, video and gallery index method in comment controlelr are repeated, and I cannot understand how can I be more DRY.
All your controllers presumably inherit from ApplicationController:
class CommentsController < ApplicationController
If you find yourself with a lot of repetition in any of the controller methods you could define it in ApplicationController instead, with maybe some specific processing in each controller.
For example:
class ApplicationController < ActionController::Base
def index
...some common processing...
specific_index_processing
end
private
def specific_index_processing
# empty method; will be overridden by each controller as required
end
end
class CommentsController < ApplicationController
private
def specific_index_processing
...specific procesing for the comments index method...
end
end
And of course, if one of your controllers needs to be completely different from this common approach you can always just override the entire index method.

How to execute an action from another controller without leaving the current controller

class UsersController < ApplicationController
def create
# call the action do_something from ImagesController
# continue in the normal flow
end
end
class ImagesController < ApplicationController
def do_something
...
end
end
I want to call the action do_something in the ImagesController from UsersController but after it is executed I want to continue in the normal flow of the create action, few questions:
Is it a bad practice?
How can I do that? Do I have to create an instance of the ImagesController and then call the action or is there another way?
You could technically create an instance of the other controller and call methods on that, but it is tedious, error prone and highly not recommended.
If that function is common to both controllers, you should probably have it in ApplicationController or another superclass controller of your creation.
class ApplicationController < ActionController::Base
def common_to_all_controllers
# some code
end
end
class SuperController < ApplicationController
def common_to_some_controllers
# some other code
end
end
class MyController < SuperController
# has access to common_to_all_controllers and common_to_some_controllers
end
class MyOtherController < ApplicationController
# has access to common_to_all_controllers only
end
another method is
# lib/common_stuff.rb
module CommonStuff
def common_thing
# code
end
end
# app/controllers/my_controller.rb
require 'common_stuff'
class MyController < ApplicationController
include CommonStuff
# has access to common_thing
end
source - Calling a method from another controller
this is possible actually. And not that hard.
That said. It is bad practice. Very bad practice.
What you want to do is extract the logic you do in the controller you want to invoke into a service object or move it into a model. Alternativly you could also make your first controller inherit from the one you are trying to invoke.
So, how to call a controller?
TheController.new.dispatch(:index, request)

How and where to define common code that will be invoked from various controllers - Rails v3

I need to define some common piece of code that can be invoked from different controllers(not view). Is there a way to do that in Rails v3?
I have defined the code in ApplicationHelper and tried to invoke it using
#template.<helper_method>
and
ActionController::Base.helpers.<helper_method>
But it does not work?
Code that you will invoke in controllers can be defined as protected methods in their parent, ApplicationController.
app/controllers/application_controller.rb:
class ApplicationController
# ...
protected
def work_some_magic(param)
# work magic here
end
end
app/controllers/users_controller.rb:
class UsersController < ApplicationController
# ...
def show
#user = User.find(params[:id])
#magic_result = work_some_magic(#user)
end
end

calling methods dynamically inside a controller

I have the following scenario
I want to add methods dynamically to a controller. All my method names are in a table . Please refer the following example
-table (method_names)-
1 - Walk
2 - Speek
3 - Run
and I have a controller
class UsersController < ApplicationController
def index
end
end
Inside this index action i want to call my methods dynamically. Those methods were actually implemented else ware.
I have another controller like
class ActionImplementController < ApplicationController
def walk
puts "I'm walking"
end
def speek
puts "I'm sppeking"
end
def run
puts "I'm running"
end
end
** I have done something like below and its working
class UsersController < ApplicationController
def index
a = eval("ActionImplementController.new.run")
end
end
But my question is , is this the right way or is there anyother way to do this
Thanks in advance
cheers
sameera
While the first answer works, i would prefer something like this
module ImplementsActions
def run
...
end
def walk
..
end
def ...
end
and then in your controller write
class UsersController < ActionController::Base
include ImplementsActions
# now you can just use run/speek/walk
def index
run
end
end
Much cleaner because the code can be shared, but it is defined where you need it.
I think it's generally best to avoid the use of eval. If you can, I would make all your methods class methods and then run them like so:
def index
ActionImplementController.send :run
# ActionImplementController.new.send(:run) works if you can't use class methods
end

Rails ActionController Execute Same Code for Every Action

To the rails experts out there I was wondering where/how you would execute the same code for every action in your web application? If you can point me to an article or provide a short code snippet I would greatly appreciate it.
Thanks in advance to anyone who can help.
Use a filter in your ApplicationController to run the code for every action in your application. All your controllers descend from ApplicationController, so putting the filter there will ensure the filter gets run.
class ApplicationController
before_filter :verify_security_token
def verify_security_token; puts "Run"; end;
end
It sounds to me like you're talking about filters.
class MyController < ActionController::Base
before_filter :execute_this_for_every_action
def index
#foo = #bar
end
def new
#foo = #bar.to_s
end
def execute_this_for_every_action
#bar = :baz
end
end
You can put the filter on the ApplicationController, too, if you want every controller to run it.
before_filter if you want the code to be executed "before" each action.
If you want the action to be declared each time you use it, you can put it in ApplicationController and call the method in any controller.
Another approach is to use helpers like:
module PersonHelper
def eat
{.. some code ..}
end
end
And in your controller:
class MyController < ActionController::Base
include PersonHelper
def index
eat
end
end

Resources