I have several controllers that set an instance variable, as follows:
before_action :set_max_steam_pressure
.....
def set_max_steam_pressure
# and then about a dozen lines of code concluding with
#max_steam_pressure = Valve.where(id: socket_id).first.pressure
end
This code is repeated in about a dozen controllers.
Is it possible to do this through a helper method, as part of the before_action, without having to repeat the same code in all the controllers? Benefits: less code, and if I have to change something in the future, I would only do it in one place.
You can use "controller concern", for example:
app/controllers/concerns/steam_pressure_setup.rb
module SteamPressureSetup
extend ActiveSupport::Concern
included do
before_action: set_max_stream_pressure
end
def set_max_stream_pressure
# ...
end
end
Then include it in your controllers which need it.
app/controllers/my_controller.rb
class MyController < ApplicationController
include SteamPressureSetup
# ...
end
Ref: https://api.rubyonrails.org/classes/ActiveSupport/Concern.html
Related
Current I have three controllers that uses the exact same authorize_journey method (located in the controllers). Each controller also calls the exact same before_filter :authorize_journey. What is the best way to reduce this kind of redundancy through best-practice?
Also, how can I, if possible, stick to the fat-model-skinny-controller practice?
If the authorize_journey methods are all identical, then you can move a copy to '~/app/controllers/application_controller.rb' and remove it from all the individual controllers.
The before_filters can all remain as they are.
Of course, if the methods are not identical, you may require some refactoring to generalize it further. If you post the code, we can comment further.
You can always use it in the application_controller.rb.
Move the authorize_journey method to application_controller.
Say if you have 4 controllers and you need the before_filter in 3 controller. Then you can define the before_filter :authorize_journey in the the application_controller and use:
skip_before_filter :authorize_journey
in the 4th controller where you don't want the before_filter
I would suggest adding the method to a concern and include them in the controllers that requires the authorize_journey before_filter.
Code for concern will be:
# controllers/concerns/authorize_journery.rb
module AuthorizeJourney
extend ActiveSupport::Concern
included do
before_filter :authorize_journey
end
protected
def authorize_journey
# Code goes here
end
end
Now in the controllers, include the AuthorizeJourney module.
# controllers/examples_controller.rb
class ExamplesController < ApplicationController
include AuthorizeJourney
# Code goes here
end
I know I will get answers that I shouldn't do this, but due to specific way to solve problem I am facing, I will have to use session in my /lib/example.rb file. (or at least I think I will have to use it)
I am calling an action, which will first run (seudo code):
module ApplicationHelper
def funcion(value)
MyClass.use_this(value)
end
end
And then I will use it in my lib/example.rb
module MyClass
# include SessionsHelper # this is not working
def self.use_this(value)
# I want to be able to use session here. What I need to do that in order to make it work.
session[:my_value] = value
end
end
What should I do in order to use session inside MyClass (I can pass variable to MyClass.use_this(value,session), but I wouldn't want to do that
Edit:
What I want to achieve with this session thing is that I would like to preserve a value during multiple requests. I am making a call to the web application multiple times, and I want to preserve some value on the next call. I am calling the app via API and I shouldn't use database to save values. So I have left with sessions, or text files, or even maybe cookies to make this happen - to preserve the same value on multiple calls.
Why not include the module in your controller, and then call the use_this function directly from there?
module MyClass #should probably rename this anyway
def use_this(value)
session[:my_value] = value
end
end
class SomeController < ApplicationController
include MyClass
def some_action
...
use_this(the_value)
...
end
end
In order to use session inside MyClass may be you could use instance variable #session:
module MyClass
extend SessionsHelper
def self.use_this(value)
#session[:my_value] = value
end
end
module SessionsHelper
def some_method
#session = ...
end
end
self.include(module) method makes the instance methods (and instance variables) of the included module into instance methods of the including module.
Edit: include SessionsHelper changed to extend SessionsHelper
self.extend(module) -- methods of receiver become class methods of that class and instance variables will work between this methods.
Suppose I have a function trim_string(string) that I want to use throughout my Rails app, in both a model and a controller. If I put it in application helper, it gets into the controller. But application helper isn't required from within models typically. So where do you put common code that you'd want to use in both models and controllers?
In answer to the specific question "where do you put common code that you'd want to use in both models and controllers?":
Put it in the lib folder. Files in the lib folder will be loaded and modules therein will be available.
In more detail, using the specific example in the question:
# lib/my_utilities.rb
module MyUtilities
def trim_string(string)
do_something
end
end
Then in controller or model where you want this:
# models/foo.rb
require 'my_utilities'
class Foo < ActiveRecord::Base
include MyUtilities
def foo(a_string)
trim_string(a_string)
do_more_stuff
end
end
# controllers/foos_controller.rb
require 'my_utilities'
class FoosController < ApplicationController
include MyUtilities
def show
#foo = Foo.find(params[:id])
#foo_name = trim_string(#foo.name)
end
end
It looks like you want to have a method on the String class to "trim" itself better than a trim_string function, right? can't you use the strip method? http://www.ruby-doc.org/core-2.1.0/String.html#method-i-strip
You can add new methods to the string class on an initializer, check this In Rails, how to add a new method to String class?
class String
def trim
do_something_and_return_that
end
def trim!
do_something_on_itself
end
end
That way you can do:
s = ' with spaces '
another_s = s.trim #trim and save to another
s.trim! #trim itself
but check the String class, it looks like you already have what you need there
I have an extension to ActiveRecord's class ActionController:
module MyExtension
def my_method
"default implementation"
end
end
ActionController::Base.class_eval { include MyExtension }
All my controllers inherit from ApplicationController, which inherits from ActionController::Base. I want to add to and override the methods in MyExtension in the inherited class
class MyController < ApplicationController
module MyExtension
def my_method
"overridden implementation"
end
def my_new_method
"added method implementation"
end
end
end
I then want to look up the methods defined in MyExtension for a specific Controller class dynamically
controller = Kernel.const_get("MyController")
methods_in_my_extension = Kernel.const_get("MyController::MyExtension").public_instance_methods
Obviously, this does not work, since there are no MyController::MyExtension constant defined.
In a more general sense, what I want to achieve is:
Group methods to implement default behavior for classes inheriting from ActionController::Base
Be able to override and add to those methods in the class definition
Dynamically get a list of the grouped methods for a specific class
Edit: In answer to Deefour about why I want to do this:
I am creating a CLI menu to wrap around the controllers. It takes input from the user to call controller methods. The menu collects the methods directly from the controllers and thereby defines valid commands/input.
What I want is to define a new level of encapsulation, a new access specifier, a subset of the public methods, which is accessible to the CLI menu.
I could achieve this by just creating a prefix for all menu-available methods, but I am also trying to get a grip on the inner workings on ruby.
I figured it out:
The ActionControllerExtension:
module MyExtension
module MyDefaultImplementations
def my_method
"default implementation"
end
end
def get_selected_methods
default = MyDefaultImplementations.public_instance_methods
added = Kernel.const_get(self.class.name).const_get("MyImplementation").public_instance_methods
return (default + added).uniq
end
end
ActionController::Base.class_eval { include MyExtension }
The Controller:
class MyController < ApplicationController
module MyImplementation
def my_method
"overridden implementation"
end
def my_new_method
"added method implementation"
end
end
include MyImplementation
end
That seems to be working for now. If anyone has any alternate/better approaches I would love to see them. Also if there are any major caveats or conflicts with convention, let me know.
If I want to have functions to be called inside controllers, where should I put them?
if you want it to be local to a controller then all you need to do is to add it to the controller you wish to use.
private
def myfunction
function code.....
end
to all controllers you can put it inside the application controller, because all controlers are sub classed.
ApplicationController
protected
def myfunction
function code.....
end
If you want access in your views then you can create a helper
ApplicationHelper
def myfunction
function code...
end
#jonnii, for example, I want to call a function that returns a generated unique code.
If your generated code is going to be used only on your controllers, put the function inside a controller, as protected function (the easiest way would be putting it inside ApplicationController).
If you need to call the function on the views, then put it on a helper, like ddayan says.
If you also need to invoke the function from models, then the simplest way to do it is by putting a module inside the /lib/ directory.
# /lib/my_module.rb
module MyModule
def generate_code
1
end
end
You will also need to include it with an initializer:
#/config/initializers/my_module.rb
require 'my_module'
From that moment on, you can use the function like this:
MyModule::generate_code
If you are doing this very often, consider creating a gem.
class YourController < ActionController::Base
def your_action
your_function
end
private
def your_function
end
end
Also look at before_filter and after_filter, they're often useful in such kind of things
The ApplicationController is here for that, since every Controller inherited from it.