how would one put everything in app/presenters under Presenters namespace?
so app/presenters/admin/article.rb containing
module Presenters
module Admin
class Article
end
end
end
may be available as Presenters::Admin::Article ?
Here's the working answer:
app/presenters/admin/article.rb
module Admin
class Article
end
end
access like: Admin::Article.new
Related
I am having trouble with Ruby class (constant) lookup within the context of a Rails engine gem.
I have a gem MyGem that is a Rails engine. It defines non-namespaced models that are expected to be overridden by the MainApp that would include the gem and namespaced modules, which are included in gem's and main_app's models to have a DRY way of defining reusable code.
Here is a sample code structure:
Two models
# in /app/models/user.rb
class User < ActiveRecord::Base
include MyGem::User::CommonExt
end
# in /app/models/comment.rb
class Comment < ActiveRecord::Base
include MyGem::Comment::CommonExt
end
Their two modules
# in /app/models/concerns/my_gem/user/common_ext.rb
module MyGem::User::CommonExt
def load_comment(id)
return Comment.find(id)
end
end
# in /app/models/concerns/my_gem/comment/common_ext.rb
module MyGem::Comment::CommonExt
def load_user(id)
return User.find(id)
end
end
Now, if I call
User.new.load_comment(1)
I get undefined method #find for MyGem::Comment::Module
I think I understand why this is happening - in the context of #load_comment definition, which is namespaced under MyGem, Comment constant lookup returns MyGem::Comment, rather than the more distant ::Comment
I would prefer not to have to prepend every model instance with ::.
Is there a file structure, model/class definition or configuration change I could use to make a call to Comment return the model Comment, not the MyGem::Comment module?
I would use inheritance instead of mixin in this case.
So in your gem/engine you could define your common class similar to this:
module MyGem
module Common
class Base < ActiveRecord::Base
# common functionality goes here
def load(record_type, id)
record_type.find(id)
end
end
end
end
Then in your main_app code:
class User < MyGem::Common::Base
...
end
Now you could do this:
User.new.load(Comment, 1)
This violates the Law of Demeter but hopefully it illustrates the point.
Doing it like this is DRY and has the added benefit that it prevents your gem from having to know about classes which are outside it's own scope.
I'm trying to create a back-end area to my application, so I've created a folder named backend and backend_controller.rb inside it. I need the folder because the backend area will have multiple folders, so it's better-separated from my others.
my routes.rb looks like:
namespace :backend do
get 'index'
end
my backend_controller.rb:
class BackendController < ApplicationController
def index
end
end
But in this mode Rails will search for my backend_controller.rb inside the controllers folder, not in controller>backend. I've tried many variations, and I get routing errors.
So what is correct way to do that? To set the path /backend to index action instead of /backend/index?
Thanks
What i've done:
based on all answers, principally the one from Cyril DD
I've created the backend_controller.rb on the app/controller folder and in the sub-folder app/controller/backend i created the static_pages_controller.rb and all files looks like this:
app/controllers/backend_controller.rb:
class BackendController < ApplicationController
end
app/controller/backend/static_pages_contter.rb:
class Backend::StaticPagesController < BackendController
def dashboard
end
end
routes.rb:
namespace :backend do
resource :static_pages, path: '', only: [] do
root to:'static_pages#dashboard'
end
this works fine, but cause i'm newbie on rails i must ask. This is a good or a conventional way to do that? to administrate the permissions which user can see on the backend i use the backend_controller.rb right? and at last wy i must use resource: instead just get ''
Answering your question
Alright, namespace :something is a shorthand for scope 'something', module: 'something', as: 'something'
Now your declaration is very ambiguous, because you don't specify a controller. Typical declarations look like (assume you have a controller controllers/backend/some_resources_controller.rb and you want to generate default routes)
namespace :backend do
resources :some_resources
end
Now what you did
namespace :backend
get 'index'
end
is really ambiguous and I'm not surprised it doesn't do what you want. Basically you just tell rails to "look inside subfolder 'backend' and define the route 'index'". oO ? Which file/controller are we even talking about ?
What is your backend_controller.rb supposed to do ? Is it some kind of Control Panel ? Dashboard ? If so you're probably gonna have a lot of non-CRUD actions, but anyways you should go for the following syntax
namespace :backend
# Below line of code will auto-generate the `index` for /backend/backend_controller
resource :backend, only: [:index], path: '' do # we need " path: '' " otherwise we'll have https://xxx/backend/backend/dashboard
# If you have non-CRUD actions, put them here !
get 'dashboard' # https://xxx/backend/dashboard
...
end
# However, this will create URLs like "https://xxx/backend/dashboard", etc.
# If you want to redirect https://xxx/backend/ to your backend_controller#index, use root
root to: 'backend#index' # https://xxx/backend/
end
Last thing as mentionned by other guys, when you namespace a file like your Backend_controller inside /backend/ subfolder, you must rename the class like (/controllers/backend/backend_controller)
class Backend::BackendController < ApplicationController
Remark : if you only have like one or two controller actions, instead of using the resource method, you can declare singular resources
namespace :backend do
root to: 'backend#dashboard'
get 'dashboard', to: 'backend#dashboard' # singular resource
end
An Example of what you may actually really want to do...
I'm not sure you are clear yourself about what you want to do. As an example, here is my architecture
Files
/controllers/application_controller.rb
/controllers/backend_controller.rb
/controllers/backend/static_pages_controller.rb
/controllers/backend/***.rb
The class /controllers/backend_controller.rb will not serve any action, but will override ApplicationController to tune it for backend access (but maybe you don't need to do so)
class BackendController < ApplicationController
# Do you need to change user_access method ? Or any other backend-wide config ?
# If so put this config here, otherwise leave empty
end
Now for every file that goes in the /backend/ subfolder, I inherit the backend_controller
class Backend::StaticPagesController < BackendController
def index
end
# Note : if your index is some kind of dashboard, instead I would declare
def dashboard
end
end
class Backend::SomeResourcesController < BackendController
...
end
Routes
namespace :backend do
root to 'static_pages#index' # https://xxxx/backend/
resource :static_pages, only: [:index], path: '' # https://xxxx/backend/index
resources :some_resources
end
If you choose the dashboard solution in your controller, write instead :
namespace :backend do
root to: static_pages#dashboard # https://xxxx/backend/
resource :static_pages, path: '', only: [] do
get 'dashboard' # https://xxxx/backend/dashboard
end
resources :some_resources
end
Then it's simply
# routes.rb
Rails.application.routes.draw do
namespace :backend, shallow: true do
resource :backend, path:''
end
end
Then in your app/controllers/backend/backend_controller.rb, it'd look like this.
class Backend::BackendController < ApplicationController
def index
end
end
When I use rake routes it shows
Prefix Verb URI Pattern Controller#Action
backend_backends GET /backend(.:format) backend/backends#index
Hope this helps.
Good Day,
I have been using the book of Agile Web Development with Ruby on Rails 4 and I have been trying to use a Model Module in the directory:
app/models/concerns/MyModule.rb
I found some documentation on how to do it for previous versions of rails using the /lib folder and some other methods but I need a simple method to call my Module inside my Model.
Module MyModule
extends ...
#
def some_method_in_MyModule
end
end
I have tried this:
app/models/users.eb
Class ...
include MyModule
a = some_method_in_MyModule
#
end
but rails keeps saying it is not correct.
How do I include a module method in a model with ruby on rails 4?
In previous versions of Rails, we needed to save files in the /lib folder, as we have the new folder app/models/concerns in Rails 4, we can directly call the method from the module in a model without any helper.
e.g:
app/models/concerns/MyModule.rb
Module MyModule
extends .. #
def some_method_in_my_module
end
end
in our model we call it as:
class ...
#
#usuario = MyModule.some_method_in_my_module
#
end
I have Question model in my application.
app/models/question.rb
class Question < ActiveRecord::Base
...
end
I'm using 'pundit' gem for authorization. There are two controllers to do some changes in questions: one for registered user, one for admin.
I'm trying to create separate policies for controllers.
app/controllers/questions_controller.rb
class QuestionsController < ApplicationController
...
end
app/policies/question_policy.rb
class QuestionPolicy < ApplicationPolicy
...
end
app/controllers/admin/questions_controller.rb
class Admin::QuestionsController < Admin::ApplicationController
...
end
app/policies/admin/question_policy.rb
class Admin::QuestionPolicy < Admin::ApplicationPolicy
...
end
When I'm trying to use 'authorize' method in Admin::QuestionsController it uses app/policies/question_policy.rb class not from admin folder.
Gem's documentation says that is should work like I described above (https://github.com/elabs/pundit#namespaced-policies).
Can somebody help me with that?
I was trying to get separated policies for the main app and the ActiveAdmin and ended up with a working solution by creating a customized PunditAdapter to be used in config/initializers/active_admin.rb
class NamespacedPunditAdapter < ActiveAdmin::PunditAdapter
def get_policy(subject, user, resource)
"ActiveAdmin::#{subject}Policy".constantize.new(user, resource)
end
def retrieve_policy(subject)
case subject
when nil then get_policy(subject, user, resource)
when Class then get_policy(subject, user, subject.new)
else
if subject.class.to_s.split('::')[0] == 'ActiveAdmin'
Pundit.policy!(user, subject)
else
get_policy(subject.class, user, subject)
end
end
end
def scope_collection(collection, _action = Auth::READ)
return collection if collection.class != Class
scope = "ActiveAdmin::#{collection}Policy::Scope".constantize
scope.new(user, collection).resolve
rescue Pundit::NotDefinedError => e
if default_policy_class && default_policy_class.const_defined?(:Scope)
default_policy_class::Scope.new(user, collection).resolve
else
raise e
end
end
end
Another option would be to use an ActiveSupport::Concern as pointed out here
I've created issue in github source code and it was closed with such explanation:
The docs refer to the currently unreleased master branch. You can use it by referring to the github source in your Gemfile.
# Gemfile
gem 'pundit', github: 'elabs/pundit'
A bundle install later your code should work.
You can switch back to a released version on Rubygems as soon as 0.3.0 is out. We're still discussing a few namespacing issues, but it will come soon.
If anyone is still looking for this functionality, I needed it as well for splitting up authorizations between ActiveAdmin and my end-user facing site. I built a Pundit compatible gem for controller-based namespaced authorizations (your policies will work), and I plan to follow any features released for pundit. It also includes an ActiveAdmin adapter.
I've notice the new features of concern in rails4, and I read the document of it in http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html.
But it seems doesn't work as I expect. Here is my code of my /models/concerns/current_user.rb.
require 'active_support/concern'
module CurrentUser
extend ActiveSupport::Concern
module ClassMethods
def accessor_current_user
attr_accessor :current_user
end
end
end
class ActiveRecord::Base
include CurrentUser
end
You probably notice that last 3 lines of my code, that's because I want all my models can call the method and I think it may be a good way to achieve that. But when i start rails server, it just cannot call the accessor_current_user method. So I am confusing about this. I really don't know the reason. Hope someone can help me. :)
Update!
Finally, i found it's maybe a good way to create a ActiveRecord::Base class in the initializers fold, and then include the CurrentUser in the class.
Try:
require 'active_support/concern'
module CurrentUser
extend ActiveSupport::Concern
included do
attr_accessor :current_user
end
module ClassMethods
end
end
class ActiveRecord::Base
include CurrentUser
end
Then you should be able to do something like this:
foo = Foo.new
foo.current_user
Where Foo is:
class Foo < ActiveRecord:Base
include CurrentUser
end
The way you are calling accessor_current_user is wrong. This way you can call ActiveRecord::Base.accessor_current_user method and it will execute the code without any error. And then you can call
ar = ActiveRecord::Base.new
ar.current_user = "foo"
ar.current_user # this will return 'foo'
But the correct way to implement the attr_accessor using ActiveSupport::Concern is
module CurrentUser
extend ActiveSupport::Concern
included do
attr_accessor :current_user
end
end
If you are writing your code in rails, then you don't need require 'active_support/concern'. Please refer http://blog.neerajk.com/articles/2014-11-26-is-active-support-concern-really-another-concern/