The app I'm working on makes heavy use of Rails services. My problem is I need to get the root url of the app, similar to how you would use root_url in a view, but this doesn't work in a service. Does anyone know a way to do this other than entering the url in each of my environment setting files?
Edit
I tried using Rails.application.routes.url_helpers.root_url as it suggests to do here stackoverflow.com/a/5456103/772309 but it expects you to pass the :host => ... in as a parameter. That's what Im trying to find.
Based on what I've read from the linked 'Rails services' article, the services are just plain old ruby objects. If that's the case, then you'd need to pass the root_url from the controller to the initializer of your service object. To extend the example from that article:
UsersController
class UsersController < ActionController::Base
...
private
...
def register_with_credit_card_service
CreditCardService.new({
card: params[:stripe_token],
email: params[:user][:email],
root_url: root_url
}).create_customer
end
end
CreditCardService
class CreditCardService
def initialize(params)
...
#root_url = params[:root_url]
end
end
EDIT: Alternative solution that leverages the Rails.application.config
class UsersController < ActionController::Base
before_filter :set_root_url
def set_root_url
Rails.application.config.root_url = root_url
end
end
class CreditCardService
def some_method
callback_url = "#{Rails.application.config.root_url}/my_callback"
end
end
Since you're opposed to putting it in your environment folders you could do something like below in your App controller
class ApplicationController < ActionController::Base
def default_url_options
if Rails.env.production?
{:host => "myproduction.com"}
else
{}
end
end
end
Related
I'm sure this is an obvious question but I just don't understand why this isn't working.
I'm finding that my static pages defined in the routes.rb file don't seem to have access to cookies? Is that correct? I'm trying to read the value of a cookie but the pages below seem to return a null object.
My routes.rb contains the following:
scope controller: :static do
get :about
get :terms
get :privacy
get :cookies
get :returns
get :delivery
end
For completeness here is the Static Controller:
# frozen_string_literal: true
class StaticController < ApplicationController
before_action :find_order
def index; end
def about; end
def pricing
redirect_to root_path, alert: t('.no_plans') unless Plan.without_free.exists?
end
def terms; end
def privacy; end
def cookies; end
def returns; end
def delivery; end
end
And this is how the cookie is set:
class OrdersController < ApplicationController
before_action :find_order
def add_hamper
#order ||= Order.create!(
ordernum: find_next_order_number,
user: current_user,
basket: true
)
#order.hampers << Hamper.friendly.find(params[:id])
update_order_total(#order)
if current_user&.custaddress
#order.update(custaddress: current_user.custaddress)
else
cookies[:ordernum] = #order.ordernum
end
redirect_to order_path(#order.ordernum)
# Update basket in Navbar
# Save the information as a cookie reference if they are not signed in
end
At the start of each controller I have a before_action to find an order if it exists in the DB or Cookie. For all other controllers, the find_order method is working. For the StaticController, there seems to be no access to the cookies.
Here is my find_order as defined in ApplicationController:
def find_order
#order = if current_user
Order.where(
user: current_user,
basket: true
).first
else
if cookies.dig[:ordernum]
Order.where(
ordernum: cookies[:ordernum],
basket: true
).first
end
end
end
helper_method :find_order
I've had to add the check for cookies and then if cookies[:ordernum] to stop it from failing on the static pages.
Thanks for any help with this.
PS. If anyone feels the above code could be better ... please let me know! There must be a nicer way to achieve this. It feels a little clunky.
I have a pritty simple custom Authorization but when it fails the user get redirected to what is set in active_admin.rb
config.root_to = 'dashboard#index'
and I have a redirect loop.
My only solution was to monkeypatch this line ActiveAdmin::BaseController::Authorization
with
module ActiveAdmin
class BaseController < ::InheritedResources::Base
module Authorization
def redirect_backwards_or_to_root
ActiveAdmin::Dependency.rails.redirect_back self, my_custom_authorize_failure_path
end
end
end
end
is there a prettier solution?
regards Kai
config.on_unauthorized_access = :access_denied
then in application_controller
class ApplicationController < ActionController::Base
protect_from_forgery
def access_denied(exception)
redirect_to define_your_path_here, alert: exception.message
end
end
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
I'm doing an authentication application. I have this code
class UsersController < ApplicationController
def new
#user = User.new
#title = "User Sign Up"
end
def create
#user = User.new(params[:user])
sign_in_check #user
if #user.save
#flash[:status] = true
#flash[:alert] = "You have successfully signed up!!"
#sign_in_check #user
redirect_to root_path, :flash => { :success => "Welcome to the Bakeshop"}
else
#title = "User Sign Up"
render 'new'
end
end
end
This is a simple sign-up code, and whenever I try and sign up, rails returns an error:
undefined method `sign_in_check' for #<UsersController:0x68c0a90>
but I defined a method sign_in_check in my Users_helper.rb:
module UsersHelper
def sign_in_check(user)
#some stuff to enable session
end
end
Does anyone have an idea why this is happening, and how to fix it?
The reason is your method is a helper. Helpers will be available in views with matching name by default, but not open to controllers without setting.
Two ways to fix:
Allow this helper in UsersController
class UsersController < ApplicationController
helper :user #This will expose UsersHelper module to UsersController
Instead, put this method into ApplicationController. I would prefer this due to the method's nature.
Include your UserHelper in your UserController as follows and you should be able to use any methods defined within the helper.
class UsersController < ApplicationController
include UsersHelper
...
end
This is usually put in the application controller
class ApplicationController < ActionController::Base
def sign_in_check(user)
#some stuff to enable session
end
end
Helpers are used for views. If you want to use it in both - you can do that, but that doesn't sound like what you're looking for here.
just include your helper module in your controller
class UsersController < ApplicationController
helper :user
...
end
Thanks
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