The following universal_methods.rb file is placed in the controllers directory to contain methods to share across controllers.
class UniversalMethods
def initialise_municipal(user_params)
user_params = params[:user]
municipal_via_id = user_params[:municipal_id]
[...]
end
end
The UserController method is instantiated and contains a method calling the above:
require_dependency 'universal_methods'
class UsersController < ApplicationController
def admin_create
set_user_login_name
user_params = params[:user]
initialise_municipal(user_params)
[...]
end
I have tested that the application accesses the file (by using just require it complains of not finding if). However, the method is definitely not accessed:
NoMethodError: undefined method initialise_municipal' for #UsersController:...`
What is wrong in this syntax / setup ?
To strictly fix your code you need UniversalMethods.new.initialise_municipal(user_params) instead of just initialise_municipal(user_params) and there's no need to require the file because everything in /controllers is autoloaded.
Another approach for this is with Concerns. Create a folder app/controllers/concerns and put universal_methods.rb there.
For Rails 6
# app/controllers/concerns/universal_methods.rb
module UniversalMethods
extend ActiveSupport::Concern
included do
def initialise_municipal(user_params)
user_params = params[:user]
municipal_via_id = user_params[:municipal_id]
end
end
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController
include UniversalMethods
def admin_create
set_user_login_name
user_params = params[:user]
initialise_municipal(user_params)
[...]
end
end
Related
I am trying to use a service class inside of my controller. I cannot figure out how to reference other classes in rails. Here is my users_controller, located under app/controllers/api/v1:
class Api::V1::UsersController < ApplicationController
respond_to :json
skip_before_filter :verify_authenticity_token
before_filter :initialize_user_service
def create
#service.create
render json: CustomResponse.new(
{
user: #service.user,
}
)
end
def initialize_user_service
#service = Services::UserService.new(user_params)
end
def user_params
params.permit(:email, :password, :password_confirmation, :first_name, :last_name)
end
end
I am attempting to reference this users_service.rb class located in app/services/users_service.rb:
class Services::UserService
attr_reader :session, :user
def initialize(params)
# #user_id = params[:id]
#params = params
end
def create
user = User.create(#params)
if user.save
return user
else
false
end
end
end
But I get the following error in the console:
NameError (uninitialized constant Api::V1::UsersController::Services):
Does anyone know what I can do to access this class? I cannot find a solution. I imagine it may have something to do with my config/application.rb file, but I am not sure what.
I think, problem is with your filename: app/services/users_service.rb. Since your class name is UserService, it should rather be: app/services/user_service.rb (user_service instead of users_service).
UserService will expect to load the class from a file called as user_service.rb in autoload path.
Define your service as following:
Filename: app/services/user_service.rb.
Definition:
module Services
class UserService
attr_reader :session, :user
def initialize(params)
# #user_id = params[:id]
#params = params
end
def create
user = User.create(#params)
if user.save
return user
else
false
end
end
end
end
Services::UserService.new(user_params) should work as intended.
Should be ::Services::UserService.new(user_params), Ruby assume you declare this class in the Api::V1 module, :: means the root module. Or you can declare your service like this:
class Api::V1::Services::UserService
end
I think it is because of defined namespace. It will work by simply putting your service class directly in service directory is enough. Check for ActiveSupport::Dependencies.autoload_paths for a basic idea of which folders are included. Even if you are using namespace then you need to update for the autoload path with it.
something like below
config.autoload_paths += %W( #{config.root}/app/your_xyz_path )
Defining service like this is enough. There's no need namespace for this:
class UserService
end
# using
#service = UserService.new(user_params)
No need to define namespace, just write it with your service name, like following
class UserService
attr_reader :session, :user
def initialize(params)
# #user_id = params[:id]
#params = params
end
def create
user = User.create(#params)
if user.save
return user
else
false
end
end
end
And call it using UserService.new(params)
And for production mode define it in application.rb file to autoload the service like following
config.autoload_paths += Dir[Rails.root.join('app', 'services')]
The code under app directory is autoloaded and all the directories too. So there should be no namespace in your service class.
class UserService
end
and this should work in any controller or model/class like
user_service = UserService.new
Is it okay to call a private method of a parent class's subclass from a module which is included in the parent class especially when it concerns ApplicationController, Controllers and lib modules in Rails?
Consider if required to change the controller name the method name to reflect the model name(to Article) change.
I feel this is really bad coding and wanted to know what community thinks about this
Example from a Rails Application:
/lib/some_module.rb
module SomeModule
include SomeModuleResource
def filtering_method
calling_method
end
def calling_method
fetch_object
end
end
/lib/some_module_resource.rb
module SomeModuleResource
def fetch_object
note
end
end
/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include SomeModule
before_action :filtering_method
end
/app/controllers/notes_controller.rb
class NotesController < ApplicationController
def show
end
private
def note
#note ||= Note.find(param[:id]))
end
end
I'm of the opinion that this is not necessary bad, although when you expect a certain interface (methods, variables, etc.) from the class that includes the module I would add the following:
module SomeModuleResource
def fetch_object
note
end
private
def note
raise NotImplementedError
end
end
This way, when #note is called without implementing it (because you forgot it was needed or whatever) a NotImplementedError is raised.
Another option is to work around it and create a more general solution. For example, if all controllers behave the same way you described above you can do the following:
module SomeModuleResource
def fetch_object
note
end
private
def note
klass = params[:controller].classify.constantize
instance = klass.find(params[:id])
var_name = "##{klass.underscore}"
instance_variable_set(var_name, instance) unless instance_variable_get(var_name)
end
end
You could also create a class helper method like before_action so that you can pass your own implementation.
module SomeModule
include SomeModuleResource
def self.included(base)
base.extend(ClassMethods)
end
def filtering_method
calling_method
end
def calling_method
fetch_object
end
module ClassMethods
def custom_before_action(&block)
define_method(:note, &block)
private :note
before_action :filtering_method
end
end
end
Now you can use custom_before_filter { #note ||= Note.find(params[:id]) } in every controller (after including).
The above is just to present you with ideas. I'm sure you could find better solution to the problem, but this hopefully points you in the right direction.
See: Alternatives to abstract classes in Ruby?. Or search for abstract classes in Ruby and you'll find more on this subject.
I have some helpers and private methods in a controller, and I want to have the same helpers and private methods in another controller. So I moved that code to module and tried to include the module in the second controller. But I can't seem to do it, because it says undefined method helper method for the DashboardHelper. Is there anyway to accomplish what I am trying to do?
Here is the code
module DashboardHelper
def get_date(log)
end
def get_working_hours(log)
end
helper_method :get_date, :get_working_hours
private
def employee_params
end
def identify_employee
end
def check_is_arrived
end
def calculate_time_percentage
end
end
class AccountController < ApplicationController
include DashboardHelper
end
hello gates you have to include extend ActiveSupport::Concern in your concern .
This should not be in your helper folder instead pull it somewhere in you concern folder
the end file may look like
module DashboardHelper
extend ActiveSupport::Concern
module ClassMethods
def get_date(log)
end
def get_working_hours(log)
end
helper_method :get_date, :get_working_hours
private
def employee_params
end
def identify_employee
end
def check_is_arrived
end
def calculate_time_percentage
end
end
end
I am looking to write certain methods for processing strings, and other tasks that take place in numerous of my controllers. I know its bad practice to include helpers in your controller, so I was just wondering, where is the best place to put application wide methods used in controllers?
I realize some of you will say to put them in models, but you have to realize that not all my controllers have an associated model. Any and all input would be appreciated.
I tend to put them into helpers. The fact that they are included in views
automatically haven't been a problem for me. You can also place them into
something like app/concerns/ or lib/
I don't like cluttering ApplicationController with private methods
because this often becomes a mess.
Example:
module AuthenticationHelper
def current_user
#current_user # ||= ...
end
def authenticate!
redirect_to new_session_url unless current_user.signed_in?
end
end
module MobileSubdomain
def self.included(controller)
controller.before_filter :set_mobile_format
end
def set_mobile_format
request.format = :mobile if request.subdomain == "m"
end
end
class ApplicationController
include AuthenticationHelper
include MobileSubdomain
end
If you need to use a method in the application scope then I would suggest that you keep those methods inside the application controller and in order to use them inside views.. declare those as helper methods.
For example,
class ApplicationController < ActionController::Base
helper_method :current_user, :some_method
def current_user
#user ||= User.find_by_id(session[:user_id])
end
def some_method
end
end
I would suggest to put them in lib folder. So for example I have:
lib/utils/string_utils
module StringUtils
def foo
...
end
end
class BarController < ActionController::Base
include StringUtils
end
This demonstrates good methodology called Fat model, Thin controller, in this case we are using Mixins instead of Models to separate logic but idea is same. You want your controllers as simple as possible.
It all depends on your needs. I will provide here 2 examples. Both of them are just a custom libraries, located under lib directory.
First example - "custom string processing"
# lib/filters.rb
module Filters
# Converts value to canonical view
def self.phone(value)
# remove all non-digits
clean_value = value.gsub(/\D/, '')
country_codes = configus.phone.country_codes
area_code = configus.phone.defaults.area_code
case clean_value.length
when 7
"#{area_code}#{clean_value}"
when 11
# remove country code only if phone starts with the allowed country code
if country_codes.include?(clean_value[0].to_i)
clean_value[1..-1]
else
clean_value
end
else clean_value
end
end
# usage
# app/api/phones_controller.rb
class Api::PhonesController < Api::ApplicationController
def exists
if params[:q]
clean_value = Filters.phone(params[:q])
...
end
end
end
Second example - helper for flash messages
# lib/flash_helper.rb
module FlashHelper
def flash_translate(key, options = {})
scope = [:flash, :controllers]
scope += params[:controller].split('/')
scope << params[:action]
t(key, {:scope => scope}.merge(options))
end
end
# app/application_controller.rb
class ApplicationController < ActionController::Base
include FlashHelper
end
# usage
# app/your_controller.rb
class YourController < ApplicationController
def create
#object = Object.new(params[:object])
if #object.save
flash[:success] = flash_translate(:success)
...
end
end
end
Note: do not forget to add lib dir to the autoload paths. In config/application.rb add/modify this line config.autoload_paths += %W(#{config.root}/lib).
So for me the answer is lib directory.
Starting from Rails 4 there is a dedicated folder for it app/controllers/concerns. So you can create a module there and then include it in a specific controller(s) or in ApplicationController if you need it available throughout all your controllers.
In case those methods are used in numerous controllers, I would define them in application_controller.rb. Every controller will inherits from it and will be capable to use any method defined there
Here's the code:
class SyncController < ApplicationController
def get_sync
#passed_ids = params[:ids].split(',')
#users = #passed_ids.collect{|id| User.find(id)}
#add the current user to the list
#users << current_user
#recommendations = get_recommendations(#users)
end
end
module SyncHelper
def get_recommendations(users)
users
end
end
I'm getting a can't find get_recommendations method error...
Your SyncHelper module needs to be included into your SyncController class. You can either add the line
include SyncHelper
in your class definition, or, if SyncHelper lives in the expected app/helpers file, you can use the rails helper method.