New project with "rails new --api" -- missing ActionDispatch::Request - ruby-on-rails

I'm trying to access the request object in app/controllers/application_controller.rb. My code is:
class ApplicationController < ActionController::API
include ActionView::Layouts
include ActionController::RequestForgeryProtection
protect_from_forgery
before_action :require_login
private
def require_login
unless logged_in?
logger.log "#{request}"
end
end
def logged_in?
false
end
end
This results in the error:
comparison of String with 0 failed, highlighting the line logger.log "#{request}"
I thought it was a problem with the middleware not being loaded, so I tried to load it in config/application.rb:
config.middleware.use ActionDispatch::Request
But this results in another error:
undefined method 'call' for ActionDispatch::Request
I seem to keep having to add things back in since I used the --api option and it strips a lot of things out. But I don't know how to add back in access to the request option. Any help?

It looks like you are mis-using logger.log. In the simple example below, I have outlined three ways to approach this. If you want to use logger.log, you need to specify at minimum a severity level. That is the source of the comparison of String with 0 failed message you are receiving.
class ApplicationController < ActionController::API
before_action :log_request
def log_request
logger.log request # This doesn't work
logger.info("#{request}") # This works
logger.log(Logger::WARN,"#{request}") # This works
end
end
Valid levels are: FATAL, ERROR, WARN, INFO, DEBUG.
Reference: http://ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html#method-i-add (logger.log is an alias for logger.add)
Logger#add(severity, message = nil, progname = nil) { ... }

Related

Best way to perform error handling using modules in Rails?

I'm pretty new to Rails and back-end API developement so excuse me if I misuse a concept or so. Right now I'm attempting to refactor a large amount of conditional error handling code that is sprinkled around the code base and move towards using an explicit list of rescued exceptions that's mixed into the API controller by including it in as a module. This will allow me to attach custom, but arbitrary, codes to each exception caught, so long as we use the bang alternatives for active record methods, and the error handling code can live in one place. So far, for the error handling module, I have something like this:
# app/lib/error/error_handling.rb
module Error
module ErrorHandling
def self.included(klass)
klass.class_eval do
rescue_from ActiveRecord::RecordNotFound do |e|
respond(:record_not_found, 404, e.to_s)
end
rescue_from ActiveRecord::ActiveRecordError do |e|
respond(e.error, 422, e.to_s)
end
rescue_from ActiveController::ParameterMissing do |e|
response(:unprocessable_entitry, 422, e.to_s)
end
rescue_from ActiveModel::ValidationError do |e|
response(e.error, 422, e.to_s)
end
rescue_from CustomApiError do |e|
respond(e.error, e.status, e.message.to_s)
end
rescue_from CanCan::AccessDenied do
respond(:forbidden, 401, "current user isn't authorized for that")
end
rescue_from StandardError do |e|
respond(:standard_error, 500, e.to_s)
end
end
end
private
def respond(_error, _status, _message)
render "layouts/api/errors", status: _status
end
end
end
Where layouts/api/errors is a view built using jbuilder.
In the ApiController we have:
# app/controllers/api/api_controller.rb
module Api
class ApiController < ApplicationController
include Error::ErrorHandling
attr_reader :active_user
layout "api/application"
before_action :authenticate_by_token!
before_action :set_uuid_header
respond_to :json
protect_from_forgery with: :null_session
skip_before_action :verify_authenticity_token, if: :json_request?
private
...
end
Unfortunately this doesn't seem to work. Running tests shows that the private methods are not being loaded at all and are considered undefined!
To be more specific, here are the errors emitted:
uninitialized constant Error::ErrorHandling::ActiveController
and
undefined local variable or method `active_user' for Api::FooController
Where active_user is an attribute that is set inside of an instance variable by a method named set_active_user. Which is obviously not being called.
However the ErrorHandling module is being evaluated. How could this be? Am I namespacing incorrectly or something?
Thanks for reading.
The answer is broken down into two parts as I believe that there are two separate problems.
unitinalized constant error
The error
uninitialized constant Error::ErrorHandling::ActiveController
can be fixed by changing this
rescue_from ActiveController::ParameterMissing do |e|
response(:unprocessable_entitry, 422, e.to_s)
end
to this:
rescue_from ::ActiveController::ParameterMissing do |e|
response(:unprocessable_entitry, 422, e.to_s)
end
(adding :: in front of the ActiveController constant)
Constant lookup in ruby takes lexical nesting into account. As you reference the Constant within
module Error
module ErrorHandling
...
end
end
ruby will try to find the constant within this namespace if the constant is undefined before. Prepending :: will tell ruby to ignore the nesting on constant lookup.
undefined local method
The error
undefined local variable or method `active_user' for Api::FooController
is raised because some code is calling the instance method active_user on the class Api::FooController where it is not defined.
I know this is an old question but I was struggling with it but found the fix. To fix the issue of:
`undefined local variable or method `active_user' for Api::FooController`
You need to include extend ActiveSupport::Concern e.g.
module Error
module ErrorHandling
extend ActiveSupport::Concern
# rest of your code
end
end

How to keep my gem self-contained without having to edit any core source Rails?

I want to add a filter to the ApplicationController but I want to do it within my gem.
What I want to avoid is the following:
class ApplicationController < ActionController::Base
include MyGem
end
I do not want that. I don't want to have to include my module in the source code.
I am having issues though.
Here is the relevant code:
lib/correlation_id/controller_extension
module CorrelationId
module ControllerExtension
def self.included(klass)
klass.class_eval do
after_filter :pass_correlation_id
end
end
def pass_correlation_id
correlation_id = request.headers['Correlation-ID'] || SecureRandom.uuid
headers['Correlation-ID'] = correlation_id
end
end
end
ApplicationController.send :include, CorrelationId::ControllerExtension
lib/correlation_id.rb
require 'correlation_id/controller_extension'
module CorrelationId
end
Now, when I'm in the test/dummy directory, which is a test rails app for my gem, I try to boot up the server using rails s and I get the following error:
/correlation_id/lib/correlation_id/controller_extension.rb:17:in `<top (required)>': uninitialized constant ApplicationController (NameError)
I'm clearly having problems with referencing ApplicationController to monkey-patch it.
How would I manage this? I want my gem to be self-contained.
The following code works. What I did was prematurely create ApplicationController with the appropriate inheritance. Note, many people use the rails-api gem, so I factored in them to ensure the fact that it would work.
Also, note: You must inherit from a class because otherwise ApplicationController will be a usual class that doesn't understand what after_filter is.
module CorrelationId
module ControllerExtension
def self.included(klass)
klass.class_eval do
after_filter :pass_correlation_id
end
end
def pass_correlation_id
correlation_id = request.headers['Correlation-ID'] || SecureRandom.uuid
headers['Correlation-ID'] = correlation_id
end
def self.base_controller_inheritance
if Gem::Specification.find_all_by_name('rails-api').any?
ActionController::API
else
ActionController::Base
end
end
end
end
class ApplicationController < CorrelationId::ControllerExtension.base_controller_inheritance
include CorrelationId::ControllerExtension
end
I imagine there might be a better way to check if they are using ActionController::API and if so, please do share, but as of now, this seems like the most solid way to do it.

Rails: Undefined method `flash' for ActionDispatch::Request

I'm trying to write an Ember application in Rails 4, and have decided to go with rails-api for the api controllers, while keeping the application controller intact for a few pages that aren't part of the single-page app. To put it in more concrete terms, here are my controllers:
app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery
end
app/controllers/sample_controller.rb:
class SampleController < ApplicationController
# my methods
end
app/controllers/api/v1/api_controller.rb:
class Api::V1::ApiController < ActionController::Api
include ActionController::MimeResponds
end
app/controllers/api/v1/sample_controller.rb:
module Api::V1
class SampleController < ApiController
respond_to :json
# my methods
end
end
My application.html.slim contains the following line:
== render partial: "flash_msgs" unless flash.blank?
The inclusion of which results in the following error:
undefined method 'flash' for #< ActionDispatch::Request:0x007f99f41d8720 >
Per discussion on this thread, it seems that the culprit could be rails-api, but I'm not entirely convinced given the inheritance I've set up. Any suggestions?
Not sure but maybe you need to include the ActionDispatch::Flash middleware to support the flash. Using:
config.middleware.use ActionDispatch::Flash
The docs says:
ActionDispatch::Flash: Supports the flash mechanism in
ActionController.
I hope it helps
See: https://github.com/plataformatec/devise/issues/2775
Inside devise.rb change
config.navigational_formats = ['*/*', :html]
to:
config.navigational_formats = [:json]
or just [ ]
If you're like me and creating an API on top of an existing application, you can add this to your config/application.rb file:
config.api_only = false
well, in my case my API mode rails app I had this code in one of my controller:
protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
due to which handle_unverified_request was getting called that has this small piece of code request.flash = nil which was raising Undefined method 'flash' for ActionDispatch::Request for me.
Dealt with it by replacing protect_from_forgery with skip_before_action :verify_authenticity_token

ActionController::RoutingError (undefined method `before_filter' for Class): Error

Basically I have a UsersInitializeController Class
class UsersInitializeController < ApplicationController
before_filter :authenticate_user!
def create
render true
end
end
authenticate_user! is found in the Application Controller
class ApplicationController < ActionController::Base
# protect_from_forgery
def authenticate_user!
#current_user = User.find_by_token params[:auth_token]
if !#current_user
#current_user = User.create :token => params[:auth_token]
end
end
end
When my application starts, it sends POST request to the UsersInitializeController. Since before_filter is set, it will thus call authenticate_user! first. However the error I got says before_filter is an undefined method.
From my knowledge, before_filter exist in ActionController, and since UsersInitializeContoller < ApplicationController < ActionController, I shouldn't be getting this error. Has anyone encounter this issue before ?
Exception Stack (as requested)
Started POST "/users_initialize.json" for 127.0.0.1 at 2012-03-06 00:32:50 -0800
ActionController::RoutingError (undefined method `before_filter' for UsersInitializeController:Class):
app/controllers/users_initialize_controller.rb:3:in `<class:UsersInitializeController>'
app/controllers/users_initialize_controller.rb:1:in `<top (required)>'
Routes.rb file (as requested)
MyApplication::Application.routes.draw do
resources :users_initialize
match 'info/required_client_version' => 'info#required_client_version'
end
### Problem Solved ###
Unused Devise Gem somehow causing the complication. Removed it and done.
add the before_filter within an "included do" block:
included do
before_filter :authenticate_user!
end
Update:
just noticed you solved it already. However I was having the same troubles and the solution above solved it in my case. So, I'll leave the comment here since it may help others
Not able to reproduce, the code you posted works fine on my Rails 3.2.2 app.
Probably there is something wrong with your source file (i.e. some extra hidden bytes somewhere).
You could try a step-by-step approach to solve this:
add a new UsersController and add resources :users to routes.rb
add an index action with the following code:
def index
render :text => "Hello there"
end
When you visit http://localhost:3000 you should see the text "Hello there"
Add the before_filter and verify that the filter is executed by adding e.g. logger.warn( 'In the Filter' ) to the beginning of the filter method

Rails3 filter is being skipped in production

I have a strange behavior of Rails 3.0.10. Got this application controller:
app/controllers/application_controller.rb
require 'api/api_controller'
# rest not important
app/controllers/api/api_controller.rb
class Api::ApiController < ActionController::Base
before_filter :require_user
def require_user
#user = User.find(xxx, yyy)
end
end
and then this controller:
app/controllers/api/ac_controller.rb
class Api::AcController < Api::ApiController
before_filter :find_pool, :only => [:add_pool, :remove_pool]
def add_pool
# some logic that needs #user to be set
end
def remove_pool
# some logic that needs #user to be set
end
def find_pool
# some logic here
end
end
My problem is when I run this in production mode the require_user filter is NOT called. When I try this in the development mode, it works.
Now, I understand in development mode classes are being reloaded, but the question is why the require_user filter does NOT get called?
Edit: Please note AC controller is before API controller lexicographically.
It looks like order of required files problem or the ApiController being loaded twice. Once before AcController and one more time after AcController being loaded. This could cause, that find_pool filter will get evaluated before require_user. ApiController is alse after AcController in lex order.
The problem might be caused by require "api_controller" being present somewhere - it should be handled by Rails and does not need to be put down explicitly. So if there is such a line, removing it could help.
Typically the methods called by filters should NOT be public. Public methods in controllers are treated as actions. Have you tried making require_user a private method?

Resources