Adding non-self rails method to Class - ruby-on-rails

I want a method within
class User < ActiveRecord::Base
def global_user_id
User.find_by_username("Global_User").id
end
end
which returns the current global user ID. I want it to be run using User.global_user_id rather than something like User.new.global_user_id
How would I do this?
I'm needing the user ID in other models and right now its stuck in class resources which I dont think is the best spot.

The key is the self. in the name, it means that this function is a static one tied to the class and not to an instance of it.
class User < ActiveRecord::Base
def self.global_user_id
find_by_username("Global_User").id
end
end

Related

superclass mismatch for class User - inheriting from ActiveRecord::Base

I am trying to figure out my superclass mismatch error. All the posts I've read about this describe the problem as being that User is defined twice as a class in my application.
In my case, it isn't defined twice. I have a services folder and within that I have a user folder (for user service classes). In that user folder, I have a file called organisation_mapper_service.rb, with:
class User < ActiveRecord::Base
class OrganisationMapperService
def self.call(user: u)
new(user: user).call
end
def initialize(user: u)
self.user = user
end
def call
if matching_organisation.present?
# user.organisation_request.new(organisation_id: matching_organisation.id)
# user.update_attributes!(organisation_id: matching_organisation.id)
else
#SystemMailer.unmatched_organisation(user: user).deliver_now
end
end
private
attr_accessor :user
def matching_organisation
User::OrganisationMapperService.new(user).matching_organisation
end
end
end
Separate to that, I have my user model which defines user as:
class User < ApplicationRecord
I thought it should be fine to define the service class in the way I have because it inherits from ActiveRecord::Base rather than ApplicationRecord.
Can anyone see what I've done wrong here? Where else could I look for a second definition of User?
TAKING SERGIO'S SUGGESTION
I change the user organisation mapper service to open as follows:
class User::OrganisationMapperService < ActiveRecord::Base
But that then gives an error with my Users::OrgRequestsController which has new defined as follows:
def new
#all_organisations = Organisation.select(:title, :id).map { |org| [org.title, org.id] }
#org_request = OrgRequest.new#form(OrganisationRequest::Create)
matched_organisation = User::OrganisationMapperService.new(current_user).matching_organisation
#org_request.organisation_id = matched_organisation.try(:id)
end
the error message then says:
PG::UndefinedTable at /users/4/org_requests/new
ERROR: relation "user_organisation_mapper_services" does not exist
LINE 8: WHERE a.attrelid = '"user_organisation_mapper...
**TAKING SERGIO'S SUGGESTION (exactly) **
I change my service class to:
class User::OrganisationMapperService
But then I get an error that says:
wrong number of arguments (given 1, expected 0)
That error highlights this line of my service class:
def initialize(user: u)
self.user = user
end
I don't know what to do about that because I clearly have a user if there is an inheritance from user.
Even once you solve all your other issues, you actually have an infinite recursion going on.
User::OrganisationMapperService.call(user: User.first)
Is equivalent to calling:
User::OrganisationMapperService.new(user: User.first).call
Which internally calls matching_organisation, so is sort of equivalent to:
User::OrganisationMapperService.new(user: User.first).matching_organisation
Meanwhile, matching_organisation calls
User::OrganisationMapperService.new(user).matching_organisation
It's just going to go round and round in circles.
The only reason it doesn't is because of the wrong number of arguments (given 1, expected 0) error. This is because it should be User::OrganisationMapperService.new(user: user) rather than User::OrganisationMapperService.new(user) in your matching_organisation method.
Update in response to comment:
From what I understand, the User::OrganisationMapperService is a service class that does the job of finding some Organisation and then performing some sort of work.
The User::OrganisationMapperService#matching_organisation method should actually contain the code that returns the matching organisation for the given user. The implementation will completely depend on how you have structured your database, but I'll give a couple of examples to put you on the right track or give you ideas.
First, Your organisations table may have a user_id column. In this case you could do a simple query on the Organisation model and perform a search using the user's id:
class User::OrganisationMapperService
def matching_organisation
# find the organisation and cache the result
#matching_organisation ||= ::Organisation.where(user_id: user).first
end
end
Alternatively, you may have some sort of join table where there may be multiple Users at an Organisation (just for this example let us call this table 'employments'):
class Employment < ApplicationRecord
belongs_to :user
belongs_to :organisation
end
We can add scopes (this is a must read) to the Organisation model to assist with the query:
class Organisation < ApplicationRecord
has_many :employments
has_many :users, through: :employments
scope :for_user, ->(user) {
# return organisations belonging to this user
joins(:users).merge( Employment.where(user_id: user) )
}
end
Then finally, the OrganisationMapperService#matching_organisation method becomes:
class User::OrganisationMapperService
def matching_organisation
# find the organisation and cache the result
#matching_organisation ||= ::Organisation.for_user(user).first
end
end
You are defining User class with two separate parent classes. Don't do that.
It should be
class User::OrganisationMapperService
This way, your existing User class will be loaded and used, rather than a new one created.
I thought it should be fine to define the service class in the way I have because it inherits from ActiveRecord::Base rather than ApplicationRecord.
The service class in your example doesn't inherit from anything.

What method will return ActiveRecord owner object from class method?

Let's say I have users which have items:
class User < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :user
end
Now I want to add a class method to item for a customized record creation:
def self.create_personalized
create description: "#{user.name}' item"
end
But of course, since this is a class method, user is undefined. But if I call it using user.items.create_personalized, then there is an associated user via the relationship. I know that Item.create_personalized is aware of the user because this works:
def self.create_personalized
item = create
item.update_attribute :description, "#{item.user.name}'s item"
end
But clearly that's not the best way to access the owner object. What is the correct method?
If you do
Item.scope_attributes
then you'll get a hash of any attributes from the current scope that apply to item. These are the ones that would be extracted from the scope if you called create. These are the DB level attributes so you'll get a user id rather than the user itself.
The best option I've found is current_scope.proxy_association.owner. It's still a bit indirect, but it returns the exact object I'm looking for.
def self.create_personalized
user = current_scope.proxy_association.owner
create description: "#{user.name}'s item"
end

How do I extract this so it can be reused in other parts of my app?

I have this method in my book model but now I realize I need this in a category model as well:
def proper_user?(logged_in_user)
return false unless logged_in_user.is_a? User
user == logged_in_user
end
I now have this method duplicated in the books model and the category model. Both category and books has belongs_to :user and both have the user_id:integer in the table as well. I simply want to extract this somewhere where so I can its DRY.
i tried to put the method in application_controller.rb but it says undefined method `proper_user?' for #
Thanks
Jeff
I think you'd want to be able to call this method like this:
book.proper_user?(current_user)
So it would work best to define it in each model rather then in User. This is best done by mixing in a module with the method:
module UserMethods
def proper_user?(logged_in_user)
# ... etc ...
end
end
and including it in each model:
class Book < AR::Base
include UserMethods
class Category < AR::Base
include UserMethods
The module can go in a source file in config/initializers, or you can put it elsewhere and change config.autoload_paths in config/environment.rb to point to the location.
Since it is related to the User model, why not put it there?

How do I use the 'map' method in an ActiveRecord class method?

Not sure on my Ruby syntax here.
I want to define a method that I can call like this: client.invoices.average_turnaround. So my average_turnaround method needs to work with a collection of ActiveRecord objects.
Here's my code thus far:
class Invoice < ActiveRecord::Base
...
def self.average_turnaround
return self.map(&:turnaround).inject(:+) / self.count
end
end
So I'm trying to find the sum of the turnaround times for each invoice, then divide it by the total number of invoices.
Ruby is complaining that there is no map method defined for Class. I was expecting self to be an Array.
How do I write a method that works on a collection of Invoices and uses the map function? Where am I going wrong?
If you want to use map within the class method as opposed to through an association extension. For example if it would be useful to call Invoice.average_turnaround directly or Invoice.where(x: y).average_turnaround. Place all. in front of map.
class Invoice < ActiveRecord::Base
...
def self.average_turnaround
all.map(&:turnaround).inject(:+) / all.count
end
end
Use average_turnaround using any collection.
You defined a class method, which is called on the class itself. What you need is an association extension. The method should be defined on your client model like this:
class Client < ActiveRecord::Base
has_many :invoices do
def average_turnaround
return map(&:turnaround).inject(:+) / count
end
end

Where to put model "utility" functions in Ruby on Rails

I have a rails app with several models.
I have a function that I want to access from several models.
What's the best place to put this code and how can I make it accessible from the models that need to get at it?
My understanding is that helpers are just for views. Is this correct?
It seems wrong to create a plug-in and put it in the vendor folder - this is my code and integral to my app. Is this correct?
Thanks.
The simplest solution would be to create a module under lib and mix this into the models that need it, for instance, in lib/fooable.rb:
module Fooable
def do_foo
end
end
And then in your various models:
class MyModel < ActiveRecord::Base
include Fooable
end
No need to require fooable.rb, the Rails autoloading mechanism will find it for you as long as it's named using correct conventions.
In order to lessen the repetition of code, you could also create a main class which would include that module and the simply inherit from it from every model you'd like to share the behaviour.
Something like:
module Fooable
def do_foo
end
end
class ParentModel < ActiveRecord::Base
include Fooable
end
class Product < ParentModel end
class User < ParentModel end
class Item < ActiveRecord::Base end
Thus, in that example, both Product and User would share the do_foo functionality and Item would not.

Resources