Extending model class - ruby-on-rails

I'm creating a authentication engine. It has a built in model User, which contains some basic information. I want to make it possible to extend it from outside the engine (from parent app). For example to add some has_many relations. I almost managed to do this by creating:
#host_app/app/models/my_user.rb
calss MyUser < User
has_many :comments
I thought it was it, but another problem turned out. In my engine in users show action's view I generate an additional partial which is supposed to be in host_app/app/views/shared/_partial_to_add.html.erb I also pass there the #user. This should enable me to add some additional information about the user (like the list of comment's he wrote or whatever) without touching the engine itself. But it it turns out (which is quite obvious) that this #user is almost useless because it doesn't have any new methods from the MyUser class.
Any ideas how to fix this?

Ruby has open classes. If you want to extend your engine's User model, you should be able to do this from the parent app:
User.class_eval do
has_many :comments
end
(This uses class_eval so that Rails will autoload the file defining User before evaluating this code.) Put that in a file which will load when the application does; either put it in config/initializers/, or put it lib/ and require it.

Related

Creating a rails view + controller that doesn't require a model?

I'm a bit confused on what's the proper design for a page that doesn't actually need a model.
As an example, I want to create an export page that will allow a user to export various other models into a CSV.
Obviously I'll need a view (most likely a show.html.erb file), and a controller; but making some sort of Export model wouldn't really make sense, and the same goes for creating new/edit/index views.
Is the correct way to do this just to manually create the view + controller for my Export page? It seems weird to not have a model after reading the rails documentation which is so heavily based on the MVC pattern, but I also don't see any reason why I "need" to follow the pattern for a case like this where a model just wouldn't make sense.
My guess is you are using the scaffold generator which does create a model.
If you just want to create the views and the controller type this in your terminal.
rails g controller exports
This will create the views, controller, and assets associated with the controller. Just add the routes.
resources :exports
You don't need a model. The generators and assumptions in rails generally work better if you have a model, but you don't need one. You can manually create the controllers and views, or use rails g controller exports.
You might look into form objects to provide a model in the controller - those are plain old ruby objects that provide a model without a database record.
A form object for an Export might start like this:
class Export
include ActiveModel::Model
include ActiveModel::Validations::Callbacks
end
Create a controller in your app/controllers folder like this:
class ExportsController < ApplicationController
def show
#export = ... # Your query here
end
end
Create a folder named exports inside app/views folder.
Create your show.html.erb inside the exports folder you just created.
Add manually your exports#show route like this:
resources "exports", only: [:show]
You should be good to go. Add the necessary auth and before_action methods in your ExportsController.

How to get Pundit to apply a scope to a model with a different name than the policy?

I have a DirectoryController, and -- deliberately -- no UsersController. UsersController will doubtless be added when I create the administration controls, and will have a totally difference scope & purpose.
When I call policy_scoped in the directory controller, I want to use the scope from DirectoryPolicy and not UserPolicy (which doesn't exist yet). I want to do it in a way that verify_policy_scoped recognizes, which means the obvious work around of DirectoryPolicy::Scope.new(current_user, User).resolve is not only a tad long but also actually doesn't work. (Which seems like a bug) This seems like such an obvious oversight that I'm sure there's something, somewhere, to make this work, I just don't know what.
How do I do this in Pundit?
When you do this
DirectoryPolicy::Scope.new(current_user, User).resolve
you're effectively bypassing pundit's policy_scoped? tracking.
My first thought is that if DirectoryPolicy::Scope is intended to scope your user data, maybe you need to create a Directory model as pundit expects. It could be as simple as this
class Directory < User; end
Now when you have something like this in your DirectoryController
#users = policy_scope(Directory)
pundit can properly infer you want the DirectoryPolicy::Scope, and your resolve method can treat the scope as if it were the User class because of the inheritance.
If you can't subclass User as I describe here you'll need to show some actual code from your app and provide more context as to what Directory is and what you're trying to do with it.

Using a Decorator, (rails) Could not infer a decorator for ActiveRecord::Base

I'm having trouble using a decorator. I've never used one before and I've been trying to use one with regards to something that I've been doing for breaking up some emails.
However because I've never used one before, I've been having trouble even doing very simple things with my decorator and I'm thinking there is some form of setup issue with it. I do know that everything outside of my little feature (aka the gemfile and such) are all up to date and proper.
The error I am getting is simply,
Could not infer a decorator for ActiveRecord::Base.
Now I have a controller that is almost empty, but inside it, I have the active record portion saved like so.
class Admin::ReceivedEmailsController < Admin::ApplicationController
With my view being titled,
_receive_email.html.haml
All I am doing in my view as of right now is so:
%td= received_email.decorate
My decorator
class Admin::ReceivedEmailsDecorator < Admin::ApplicationDecorator
def received_email
if can? :update, #customer
received_email.content
else
"You need to have the correct admin access to view the email"
end
end
I feel like this would have to be such an elementary thing for me to be missing, but I'm not sure what it is. Would anybody have any idea what I'm missing?
After much further research, reverse engineering further decorators and reading more documentation. I learned that a model or a helper is needed for a decorator to be properly used. Which due to my partial I did not have one specific model or helper to use.

How can I insert this subdomain filtering in an existing Rails application?

I have an existing Rails 3.2 application that is basically a simple product catalog. Now I am faced with a situation where I need to only allow interactions with specific products based on the subdomain the user is using to access the site.
The Product model belongs_to a Repository, which has the subdomain as a value.
So that a request to:
http://bobs_store.myapp.com/products
only shows products that have a repository with the name bobs_store.
Where is/are the best place/places to introduce this filtering? Is there some kind of default scope I can create at the model level to do this? My only problem with that is that I can't access the request from the model, so does this mean that I have to pass the subdomain in to every call I make to that model?
One other thing, all of the calls to the Product model are made like this:
current_user.products.<whatever>
Is it possible to modify my current_user helper method somehow to get my desired functionality? Barring that, is there something fancy I can do with routes? These are shots in the dark, but I'm hoping there is some Rails shortcut I can use that I'm not aware of.
As luri G mentioned, watch this Railscast:
221-Subdomains
Create a scope to query products of a repository
class Product < ActiveRecord::Base
belongs_to :repository
scope :for_repository,
(lambda do |repository_name|
includes(:repository).
merge(Repository.where(name: repository_name))
end)
end
After you had implemented the subdomain route handling logic as per the Railscasts, in your controller you will call the products for a repository like this:
current_user.products.for_repository(request.subdomain)
The way I've seen it done before is to have a scope on the model, e.g. Product.repository_scope.
class Product < ActiveRecord::Base
def self.repository_scope(repository)
where(repository: repository)
end
end
I'm not sure about a good workaround for not having to pass in the current repository every time.

ROR creating standalone components

In my web application I have a model User. It's quite common that you need to select some users for many different purposes related to many different models. My aim is to make this component very easy and fast to attach in a new place. E.g. if a users wants to select his friends the result of selection should be handled by User controller, but if you want to assign some users to a task this should be handled by Task controller.
Do you have any concept how to do this? Should I make another controller for selecting? How should I pass the selection to the suitable controller? Maybe by session? Do you have any other suggestions?
I think what you are looking for is a module which has common methods. If so you can do something like:
Create a module called Users and add the methods to that, and keep it inside your lib folder
Ex:
<app root>/lib
module User
def friends
<returns the given users friends>
end
end
and then you can call this module in both your controllers and models
Ex:
Class Friend
include User
end
Class FriendsController < ApplicationController
include User
end

Resources