How do I dynamically create a named_route in rails? - ruby-on-rails

I currently have this in my ApplicationController
def account_path
eval "#{current_user.type.downcase}_account_path"
end
I use it for redirects, etc. But I also want to use it in the view (for link_to's etc). Is this is a legit case to share code between the controller and view to keep it DRY even though it breaks MVC?

Yes, I'd say that's a legitimate re-use. The helper_method call is there for a reason so:
helper_method :account_path
will make this available to your views too.
If you prefer not to use eval you could do:
def account_path
self.send("#{current_user.type.downcase}_account_path")
end
as the _path method is interpreted as a method on the controller.

Related

Add helper to rails controller instance only

I have some helpers that are defined on runtime that are specific for a single call, e.g. a single instance of a controller (the next call could have different helper methods). Is there a robust way to add a helper method to an instance of a controller and it's view only, without adding the helper to other instances and views of this controller?
To define a helper for ALL instances, you could use the .helper_method method, e.g.
class Article < ApplicationController
helper_method :my_helper
def my_helper
# do something
end
end
I digged around in the source code, and found the (fairly private looking) #_helpers method which returns a module that contains all helpers for this instance. I could now use some meta programming to define my methods on this module
def index
_helpers.define_singleton_method(:my_helper) do
# do something
end
end
But I don't like this approach because I'm using a clearly private intended method that could easily change in the future (see the leading _).
If I only needed the helper inside the controller instance only, I could just call #define_singleton_method on the instance directly, but this doesn't make it available to the view.
So I'm looking for an official "Rails way" to define a helper for a single instance of a controller and it's view, like Rails provides with it's class method .helper_method.
I'm not sure if there is an official Rails way of doing this.
You could create an anonymous module and extend from that. Since this solution uses pure Ruby, you'll have to extend both the controller and view.
before_action :set_helpers, only: :index
def index
# ...
end
private
def set_helpers
#helpers = Module.new do |mod|
define_method(:my_helper) do
# do something
end
end
extend(#helpers)
end
<% extend(#helpers) %>

Controller variables are not recognizable in some views

How to pass variables between the application controller and different views in Ruby on rails ? Is there any global scope which helps us to keep track of any variable created in the controller and use it in any view code in the same application ?
To use a method from the application controller in a view, mark it as an helper method.
Something like this
class ApplicationController < ActionController::Base
def current_user
#current_user ||= User.find(session[:user_id])
end
helper_method :current_user
end
Now, the current_user method is accessible in all views and it can be used to get the #current_user instance variable.
Simply Write your code in application helper and it will be accessible through out any view in application..
please read http://guides.rubyonrails.org/action_controller_overview.html (Especially part 3)
This is not the best option though, I recommend reading about exposing you variables in controllers.
https://github.com/hashrocket/decent_exposure
or
https://github.com/netguru/decent_decoration
are created just for that and I recommend them.
A variable declared like so:
#current_user
for example will be available in any views as long as the method that creates that variable is called in the controller for that view.
You can read more about ruby variables here http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/variables.html

using helper_method AbstractController::Helpers::ClassMethods in a model

In my application controller I have the following code:
helper_method :current_user
def current_user
#current_user ||= User.find(session[:user]) if session[:user]
end
I would like to use the "current_user" method in a model. According to the rails API the helper method can be accessed at "AbstractController::Helpers::ClassMethods".
See link:
http://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html
When I add this to my model I get a method not found error:
include AbstractController::Helpers::ClassMethods
Am I missing something in how to include these helper methods?
Thanks!
Short answer: you can't and you shouldn't.
The model doesn't have (and should not have) any visibility of the view and the controller. The clear separation is one of the key principle of the MVC pattern.
If you want a method in your model to have access to the current user, then pass the user when invoking the method.
For instance, assuming you want to pass the user on the Post creation, define a custom method
class Post
def do_something_with_user(user)
# ...
end
end
and call it from the controller
def action
Post.find(...).do_something_with_user(current_user)
end
There are possible workarounds, such as storing the current user into the current thread or in a global variable, but this is gonna break the rules (and you should not break the rules).

How to ensure an object is always available in a view?

I have some code in my application layout view that requires an object to exist in order to work. The object exists on some controllers, but not all.
At first I thought I could use the after_filter on the application_controller to ensure that the object exists. But this didn't work because the after_filter is only applied after the view is rendered and it is apparently an anti-pattern as well.
What is the best way to ensure that the object always exists, without unnecessarily creating the object on controllers that already create the object.
Say your variable is #foo.
application_helper.rb
def foo
#foo ||= generate_foo()
end
then from any view, you just call your object by foo and not #foo.
This might have it's own pitfalls as well but personally, I will create a method in my application_controller to return the instance. To make it available in the views, just pass the method to helper_method. helper_method is used to share methods between views, helpers and controllers.
I mostly do this with authentication. So you I will have something like;
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
helper_method :current_user # make current_user available in the views

What do helper and helper_method do?

helper_method is straightforward: it makes some or all of the controller's methods available to the view.
What is helper? Is it the other way around, i.e., it imports helper methods into a file or a module? (Maybe the name helper and helper_method are alike. They may rather instead be share_methods_with_view and import_methods_from_view)
reference
The method helper_method is to explicitly share some methods defined in the controller to make them available for the view. This is used for any method that you need to access from both controllers and helpers/views (standard helper methods are not available in controllers). e.g. common use case:
#application_controller.rb
def current_user
#current_user ||= User.find_by_id!(session[:user_id])
end
helper_method :current_user
the helper method on the other hand, is for importing an entire helper to the views provided by the controller (and it's inherited controllers). What this means is doing
# application_controller.rb
helper :all
For Rails > 3.1
# application.rb
config.action_controller.include_all_helpers = true
# This is the default anyway, but worth knowing how to turn it off
makes all helper modules available to all views (at least for all controllers inheriting from application_controller.
# home_controller.rb
helper UserHelper
makes the UserHelper methods available to views for actions of the home controller. This is equivalent to doing:
# HomeHelper
include UserHelper
A Helper method is used to perform a particular repetitive task common across multiple classes. This keeps us from repeating the same piece of code in different classes again and again.
Here's an example to simplify the above definition:
Here is a code, where you would have something like this in the view:
<% if #user && #user.email.present? %>
<%= #user.email %>
<% end %>
We can clean it up a little bit and put it into a helper:
module SiteHelper
def user_email(user)
user.email if user && user.email.present?
end
end
And then in the view code, you call the helper method and pass it to the user as an argument.
<%= user_email(#user) %>
This extraction makes the view code easier to read especially if you choose your helper method names wisely.
So I hope this clears things up a little for you.
Source for the quotation
Source for the code

Resources