Cancancan in a engine - ruby-on-rails

I want to use the rails gem cancancan in an engine (https://github.com/CanCanCommunity/cancancan). So I added it to my gemspec file like this:
s.add_dependency "cancancan"
In the engines dummy app and in a test app I load my engine and I'm getting always errors about undefined methods for every cancancan method.
undefined local variable or method `load_and_authorize_resource'
undefined local variable or method `can?'
[...]
This is my ability.rb file in the main app:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :manage, :all
end
end
I tried several solutions for using cancancan in engines but I found no solution for my engine. Here are some examples:
http://mx.kelsin.net/2011/08/10/using-cancan-in-a-engine-and-your-app/
https://github.com/CanCanCommunity/cancancan/wiki/Authorization-for-Namespaced-Controllers
https://github.com/CanCanCommunity/cancancan/wiki/Admin-Namespace
Where is my fault? Is one of the posted links really the solution or are they wrong/outdated? Is there a good tutorial to use cancancan in an engine? Thanks!

I could solve my problem by updating Cancancan from 1.9.2 to actual 1.10.1 and compare my engine with the one from this post: https://github.com/CanCanCommunity/cancancan/issues/151#issuecomment-69487040

you have load_and_authorize_resource in your WelcomeController, it's trying to load the Welcome model which doesn't exist. Instead, trying using authorize_resource class: false, so
class WelcomeController < ApplicationController
authorize_resource class: false
# rest of your code
end

Related

The reason why it's possible to use helper method like current_user, authenticate_user!, and so on without including any module

The title is my question.
devise provide us many useful methods like current_user, authenticate_user!, and so on. I want to know why is it possible to use them without including any module like below.
class ApplicationController < ActionController::Base
before_action :authenticate_user!
end
Those method's definition is here
Somebody please help me!
The answer is devise included its helper methods into ActionController on behalf of you when Rails on_load
# devise/rails.rb
module Devise
class Engine < ::Rails::Engine
# ...
initializer "devise.url_helpers" do
Devise.include_helpers(Devise::Controllers)
end
# ...
end
# devise.rb
# ...
def self.include_helpers(scope)
ActiveSupport.on_load(:action_controller) do
include scope::Helpers if defined?(scope::Helpers)
include scope::UrlHelpers
end
ActiveSupport.on_load(:action_view) do
include scope::UrlHelpers
end
end
# ...
I saw many 3rd gems using on_load to include their methods (or themselves) into Rails core, maybe it's a typical way to do that (allows Rails to lazily load a lot of components and thus making the app boot faster). If you install some gems and you could use their methods on your model/controller/view then those gems did the same thing devise did above.
About those methods current_user, authenticate_user! ... they are dynamic methods devise will generate when it did include scope::Helpers into Rails (see code above and the link).
So one very good thing about rails is the fact that you get a lot of things for free out of the box. One of these things at the top level is autoloading.
So in the case of Devise. When you install Devise and run the generate command, you get a devise.rb file inside of your config/initializers/ folder. This file is always autoloaded by rails on server startup and reload. That's how all these methods are able to be use these devise methods without importing anything.
Read more at rails docs

How can I extend my controller from installed Spree gem's controller?

I have spree gem installed successfully. I don't need spree_frontend. Here is the Gemfile
gem 'spree_core', '4.2.0.rc2'
gem 'spree_backend', '4.2.0.rc2'
gem 'spree_sample', '4.2.0.rc2'
gem 'spree_cmd', '4.2.0.rc2'
gem 'spree_auth_devise', '~> 4.2'
So I want to extend my ApplicationController from Spree's BaseController. Here is the code:
class ApplicationController < Spree::BaseController
include Spree::Core::ControllerHelpers::Order
end
But I get following errors:
uninitialized constant Spree::BaseController (NameError)
How can I extend my controller from installed Spree gem's controller?
The problem you're running into is that Spree::BaseController already inherits from ApplicationController; see https://github.com/spree/spree/blob/master/core/app/controllers/spree/base_controller.rb. This is to allow your ApplicationController to define things like current_user and similar basic functions before Spree sees it.
Declaring them the other way around as well creates a circular dependency, and the class loading fails as a result. Without changing Spree itself, the only fix is to do something else.
Instead, to have your controllers use Spree::BaseController as a superclass, first define ApplicationController in the more usual fashion e.g.:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# ...
end
then invent a new abstract controller, for your own use, that inherits from Spree, e.g. let's name it StoreBaseController:
# app/controllers/store_base_controller.rb
class StoreBaseController < Spree::BaseController
include Spree::Core::ControllerHelpers::Order
# ...
end
This StoreBaseController can now be used in place of ApplicationController when defining more specific controllers. It works because it doesn't create a loop in the inheritance tree, which now looks like this:
Note: if you're also using the rails generator command to produce controllers or scaffolds from templates, be aware that the generator has ApplicationController hard-coded in the templates, so you'll need to amend them once created.
Is there any reason why you need to extend strictly ApplicationController?
I advise you alternative approach to create a new Base controller class, and then inherit all the children from it and leave ApplicationController to basic rails
app/controller/my_base_controller.rb
class MyBaseController < Spree::BaseController
def foo
# ...
end
end
app/controller/my_resources_controller.rb
class MyResourcesController < MyBaseController
def bar
# ...
end
end
As the errors states, Spree::BaseController is not defined within your app - it is defined in the spree-core gem. If you re-create the filepath to the base controller locally, that is app/controllers/spree/, and copy and paste the code from the controller into a local base_controller.rb, you can edit it and add custom functionality.
Note that it will still inherit from the ApplicationController, but you can place any of the code you wanted to put in the ApplicationController into here and have your classes inherit from Spree::BaseContoller and the effect will be the same.
hmmm, I tried what you want to do but I succeeded (?)
class PagesController < Spree::BaseController
include Spree::Core::ControllerHelpers::Order
end
in the console
2.6.5 :006 > pp PagesController.ancestors
[PagesController,
Spree::Core::ControllerHelpers::Order,
#<Module:0x00007fca27610410>,
Spree::BaseController,
Spree::Core::ControllerHelpers::CurrencyHelpers,
Spree::Core::ControllerHelpers::StrongParameters,
...
I'm using
ruby 2.6.5
rails 6.0.3.4
run bundle update after adding the your spree's gems in the Gemfile
So I think its the requiring or auto-loading problem
what's your rails version? 6? spree >= 4.1 should use rails >= 6
Does Spree::BaseController exist in rails console?
Is Bundler.require(*Rails.groups) in config/application.rb?
Does the gems included in the right group of the Gemfile? ex: spree gems are in :production group.
Does it have config.load_defaults 6.0 in config/application.rb?

How to include respond_to when you don't inherit from ApplicationController in Rails 4?

I have an API controller in a Rails 4.1.2 app that does not inherit from Application controller. I'm trying to include the respond_to method and get a method undefined error....So then I required actionpack at the top like below:
require 'action_pack'
class Api::V1::UsersController < Api::ApiController
version 1
doorkeeper_for :all
respond_to :json
def show
respond_with current_user.as_json(except: :password_digest)
end
end
and I still get
ActionController::RoutingError (undefined method `respond_to' for Api::V1::UsersController:Class):
But the respond_to method is part of the MODULE ActionController::MimeResponds::ClassMethods which can be found under the action_pack folder in a subdirectory if I open up the actionpack gem source code.
EDIT:
I should also mention Api::ApiController has a parent RocketPants::Base since I'm using the rocketpants api gem ... The gem's author states on his README: "RocketPants only includes the bare minimum to make apis. In the near future, it may be modified to work with ActionController::Base for the purposes of better compatibility with other gems."
I'm particularly interested in how to get access to the action_pack methods/libraries (if it's possible) in a standalone fashion, the way you can when you include activesupport.
Simply make your Api::ApiController (or its parent if there is one) inherit from ActionController::Base
Good thing you mention you are using RocketPants! A quick look at their github page confirm that it is already derivated from ActionController::Base. That renders my previous answer incorrect.
So it seems you don't need to call respond_to and that instead of respond_with you should use expose, but well it's a blink guess. You should follow their documentation
Also check your rails version. In Rails 4.2 and up, the respond_to syntax has been moved into the
responders gem.
Gemfile:
gem 'responders'
Console:
bundle install
rails g responders:install
I saw this error ActionController::RoutingError: undefined method 'respond_to' while I was using rails-api gem. After switching ActionController::API to ActionController::Base my error was resolved. This also fixed active_model_serializers failing to wrap render :json automatically with the corresponding serializers to my models. I may log this as an issue with the gem.
You have to include (at least) the ActionController::RespondWith module.

Creating gem and trying to reference ApplicationController

I'm creating a gem to create an around_filter on my ApplicationController in all projects that the gem is in. This is my first gem and I'm having a few concerns.
I included rails as a dependency in the gemspec file, but how would I reference anything in rails?
Would I do something like require 'rails' at the top of my file, then do something like
Rails::Path::To::ApplicationController? I understand I could do something like:
Module MyGem
def self.included(klass)
klass.class_eval do
around_filter do |controller, action|
#insert code here
end
end
end
end
Then, in my Rails app I would include it in my Rails ApplicationController, but I don't want to have to include it. Of course, I might have to do some additional config for even that to work, but that is the gist of my crux. How would I reference ApplicationController in my gem? Do I just assume I have Rails since it is a dependency now and I should be able to freely call upon it in my ruby files?

Can't access model from inside ActionController method added by a Rails engine

I am developing a Rails engine to be packaged as a gem. In my engine's main module file, I have:
module Auditor
require 'engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
require 'application_controller'
end
module ActionController
module Auditor
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def is_audited
include ActionController::Auditor::InstanceMethods
before_filter :audit_request
end
end
module InstanceMethods
def audit_request
a = AuditorLog.new
a.save!
end
end
end
end
ActionController::Base.send(:include, ActionController::Auditor)
where AuditorLog is a model also provided by the engine. (My intent is to have "is_audited" added to the controllers in an application using this engine which will cause audit logging of the details of the request.)
The problem I have is that when this code gets called from an application where the engine is being used, the AuditorLog model isn't accessible. It looks like Ruby thinks it should be a class in ActionController:
NameError (uninitialized constant
ActionController::Auditor::InstanceMethods::AuditorLog)
rather than a model from my engine.
Can anyone point me in the right direction? This is my first time creating an engine and attempting to package it as a gem; I've searched for examples of this and haven't had much luck. My approach to adding this capability to the ActionController class was based on what mobile_fu does, so please let me know if I'm going about this all wrong.
Use ::AuditorLog to access the ActiveRecord class (unless you have it in a module or namespace, in which case you'll need to include the module name).

Resources