Creating a global variable to all the controllers in Rails - ruby-on-rails

I have a base URL common to all my controllers. I want to declare this as a variable in one place, and use it in all my controllers. That will make any future updates fast and simple. Is that possible? I'm declaring it in all my controllers like this:
#baseURL = "www.url.com/something/"

In your application controller.
before_action :set_variables
def set_variables
#baseURL = "www.url.com/something/"
end
This #baseURL instance variable will be accessible in all your actions and views as you make all controllers inherit the ApplicationController.

Typically, all your controllers will inherit from ApplicationController. You may then define the variable there, making it available to its descendants.
class ApplicationController < ActionController::Base
BASE_URL = "www.url.com/something/"
end

You could define a method inside ApplicationController and use that method like a helper_method to access it from the views.
class ApplicationController < ActionController::Base
helper_method :base_url
def base_url
#base_url ||= "www.url.com/something/"
end
end
I try to avoid before_actions to setup variables.
Inside your controllers and views you will able to call the base_url method.
It's the same to include this method in the application_helper.rb.

Rails controllers inherit from ApplicationController. Try putting it there:
def baseUrl
#baseURL = "www.url.com/something/"
end

You could define a class variable in your application controller:
class ApplicationController < ActionController::Base
##baseURL = "www.url.com/something/"
def self.baseURL
##baseURL
end
end
class SomeFrontendController < ApplicationController
end
Within all your controllers now you could access ##baseURL or call the class method:
SomeFrontendController.baseURL
# => "www.url.com/something/"
But this is dirty. Better use a constant:
class ApplicationController < ActionController::Base
BASE_URL = "www.url.com/something/"
end
class SomeFrontendController < ApplicationController
end
Within all your controllers now you could access BASE_URL or:
SomeFrontendController::BASE_URL

If it is only one variable and you are sure, that you need it only within controllers scope declaring a constant in ApplicationController should be enough:
class ApplicationController < ActionController::Base
BASE_URL = "www.url.com/something/"
end
class SomeOtherController < ApplicationController
def index
#base_url = BASE_URL
end
end
However usually custom URLs (and other things like email addresses) earlier or later are needed in other parts of an application so it is useful to have a single source of truth by using gem like https://github.com/settingslogic/settingslogic and store all such variables in one place (file).

Related

How to use reusable controller methods in Rails?

I'm relatively new to Rails and am working on creating a simple user authentication system to get to grips with how Rails works.
I'm currently at the point where I'd like to create some methods that I can use in my controllers like so:
is_logged? # => true
and
current_user_id # => 6
These would be used to interact with sessions, mainly so I'm not repeating myself in the controller.
Where would I define these functions and how would I include them in a controller?
Thanks a lot in advance for any help.
Method 1
You can define these method in helper files, inside app/helpers/my_module.rb. You can create a module there, put all the methods inside of it, and then include the modules in your control to use these method.
module MyMoule
def is_logged?
...
end
end
Then in you class include the module
class MyClassController < ApplicationController
include MyModule
def my_method
#Use it like this
logged_in = MyModule.is_logged?
end
end
Method 2
If you using session related stuff you can always put them inside application_controller.rb. And since all your controller will inherit ApplicationController the methods will be available to you.
class ApplicationController < ActionController::Base
def is_logged?
...
end
end
In your other controller you can use them directly.
class MyClassController < ApplicationController
def my_method
logged_in = is_logged?
end
end

Rails 4.x before_action with params?

I am trying to make my rails code a bit nicer.
I have this:
class MyController < ApplicationController
before_action do
# #variable_defined_else_where is an object w/ accessors
#variable_defined_else_where.some_value = "string"
end
end
I would like to do this some how get to here:
class MyController < ApplicationController
variable_defined_else_where(some_value: "string")
# or
variable_defined_else_where.some_value = "string"
# or
some_method "string"
end
I looked at the rails actionview code, for "layout" which has a similar syntax-ness
class MyController < ApplicationController
layout "string"
end
However, it declares a method in the class, I need to modify a
#variable_defined_else_where
which then controls how several bits of helpers behave
module MyHelper
def do_if_that
if #variable_defined_else_where == "string"
# do so and so
end
end
end
Does anyone have any suggestions on how I can get to syntaxically happy-ness?
Since before_action executes in the instance context, your variable-in-question is an instance variable, which means it's only set on the instance of the controller (i.e., during the request lifecycle). On the other hand, layout is most likely setting a property on the controller class itself.
If your variable could be moved to the class-level without compromising thread-safety, you could make it a class attribute and set it directly like:
class MyController < ApplicationController
##my_variable = 3
def test
##my_variable # returns 3
end
end
But if you don't like how # signs look, maybe that's not better :)
Here's another option, which just wraps your before_action definition inside a class method:
module SetsSomeVariable
include ActiveSupport::Concern
module ClassMethods
def set_variable(value)
self.before_action do
#my_variable = value
end
end
end
end
# ...
class MyController < ApplicationController
include SetsSomeVariable # this could be in ApplicationController
set_variable 'string'
end

How do I initialise objects in a partial view for use by multiple controllers?

I hope this is something obvious that I've just consistently overlooked and the community can set me on the right path.
I have a news article controller, but I want to be able to use a "common" ticker list on different views. How do I initialise this "#article_list" if I'm using the partial in a few controllers? Apparently it is of the opinion that using a helper is not the solution, since helpers are just for view logic. So where do I put this initialiser that would be available to every controller as required? I shouldn't put them in application controller should I?
You can use before_filter method, i.e. something like this:
class ApplicationController < ActionController::Base
def set_article_list
#article_list = ArticleList.all # or any onther selection
end
end
class NewsArticleController < ApplicationController
before_filter :set_article_list, only: :action1
def action1
end
end
class AnotherNewsArticleController < ApplicationController
before_filter :set_article_list, only: :another_action1
def another_action1
end
end
UPDATE:
Indeed, there will be problem with a fat ApplicationController. To avoid it it's possible to use module (almost #carolclarinet describe it below):
module ArticleList
def set_article_list
#article_list = ArticleList.all # or any onther selection
end
end
class NewsArticleController < ApplicationController
include ArticleList
before_filter :set_article_list, only: :action1
def action1
end
end
class AnotherNewsArticleController < ApplicationController
include ArticleList
before_filter :set_article_list, only: :another_action1
def another_action1
end
end
And
You can create, essentially, a query object that is only responsible for returning what you need for #article_list, for example, building off of Psylone's answer:
class ArticleList
def ticker_articles
ArticleList.all # or any onther selection
end
end
This class could go in lib, app/models, app/query_objects, app/models/query_objects, wherever it makes sense for you. This is a bit outside The Rails Way so there's no convention about where these types of objects should live.
Then in whatever controller you need this, do:
#article_list = ArticleList.new.ticker_articles
For more explanation of query objects, see this codeclimate article #4. Depending on what you're doing to set #article_list, this might also be called a service object (#2) or something else entirely. No matter what you call it though, its responsibility would be to return the value you need for #article_list and that's it.

Calling a method from another controller

If I've got a method in a different controller to the one I'm writing in, and I want to call that method, is it possible, or should I consider moving that method to a helper?
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
Yet another way to do it as jimworm suggested, is to use a module for the common functionality.
# 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
Try and progressively move you methods to your models, if they don't apply to a model then a helper and if it still needs to be accessed elsewhere put in the ApplicationController
If you requirement has to Do with some DB operations, then you can write a common function (class method) inside that Model. Functions defined inside model are accessible across to all the controllers. But this solution does to apply to all cases.
I don't know any details of your problem, but maybe paths could be solution in your case (especially if its RESTful action).
http://guides.rubyonrails.org/routing.html#path-and-url-helpers

adding a method to application_controller so it is visible to all controllers and views

I want to create a method that will be available in all controllers, and views.
This method actually makes a db call to get data back, so since it won't be used all the time I want it be be lazily loaded.
class ApplicationController < ActionController::Base
def some_method
#object = customdb.call_method(....)
end
end
To make it lazily loaded do I just do?
#object ||= ....
And how do I propagate this to all controllers and view pages?
Use helper_method in ApplicationController to make some_method available in any controller or view:
class ApplicationController < ActionController::Base
helper_method :some_method
def some_method
#object ||= customdb.call_method(....)
end
end
The ||= does per request caching, not lazy loading. Lazy loading is the deferred initialization pattern.
Big scopes and controller methods in views are code smells. Best to minimize object scope and view logic.
You could use the memoize feature provided by ActiveSupport
http://api.rubyonrails.org/classes/ActiveSupport/Memoizable.html
http://ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization
So possibly something like:
class ApplicationController < ActionController::Base
def some_method
#object = customdb.call_method(....)
end
memoize :some_method
end
If you want the result to be accessible across accross requests, you have to store the results in a class variable:
class ApplicationController < ActionController::Base
helper_method :city_list
def city_list
##city_list =|| City.order(:name)
end
end
The result is lazy loaded and cached.
PS: The variable ##city_list is a class variable.
You should use a before_filter
class ApplicationController < ActionController::Base
before_filter :some_method
def some_method
#object = customdb.call_method(....)
end
end
It will be triggered in all your controllers.
Note that you can skip this before filter using skip_before_filter :some_method wherever you want. You can even prevent only some actions to be preceded by this filter.
Because this will be triggered before all decided controller actions, you won't need your line:
#object ||= ....
Since #object will be instantiated.

Resources