How to access ActiveModel attributes from a method call on the class? - ruby

I need to reference user#role to define an association in a module. I've tried with a block as shown below, but that doesn't work. How does Rails implement behavior like this?
class User < ActiveRecord::Base
include Profile
has_profile { |user| { class_name: "#{user.role}::Profile" }}
end
module Profile
extend ActiveSupport::Concern
module ClassMethods
def has_profile(&block)
role = ### How to access #role ? ###
class_eval do
has_one :profile, class_name: "#{role}::Profile"
end
...

You might need to do something like this. I didn't tested and I'm just supposing you can do this kind of stuff
class User < ActiveRecord::Base
include Profile
has_profile { |user| user.role }
end
module Profile
extend ActiveSupport::Concern
included do
after_initialize :_init_profile
end
def _init_profile
role = #_role_block.call(self)
# Here we do class eval on singleton so we dont change base class
# I'm not sure if this works as it is but should be close enought
class << self; self; end.class_eval do
has_one :profile, class_name: "#{role}::Profile"
end
end
module ClassMethods
def has_profile(&block)
#_role_block = block
...

This works:
module Models
module Profile
extend ActiveSupport::Concern
included do
after_initialize :_init_profile
end
module ClassMethods
def has_profile(&block)
#profile_association_block = block
end
end
def _init_profile
block = self.class.instance_variable_get :#profile_association_block
self.class.has_one :profile, block.call(self)
end

Related

Calling a Associated Controller Method Not Working

I have 3 models of User, Role and UserRole with their respective controllers as UsersController, RolesController and UserRolesController.
I have a method in the UserRoles controller which I would want to access through the Users controller but I keep having errors as explained below.
I have tried various means of even moving the method def self.add_roles_to_user(user, role) from the UsersRoles controller into the UserRole model and call it but I keep having the same error.
I have gone through lots of similar questions and various blogs, including those on this platform such as Calling a method from controller, and others but to no good results.
class UserRole < ApplicationRecord
# many-to-many association using join table with roles and user
belongs_to :user, inverse_of: :user_roles
belongs_to :role, optional: true, inverse_of: :user_roles
end
class User < ApplicationRecord
has_many :user_roles, inverse_of: :user
has_many :roles, through: :user_roles
end
class Role < ApplicationRecord
# table associations between role and user
has_many :user_roles, inverse_of: :role
has_many :users, through: :user_roles
end
class UserRolesController < ApplicationController
def self.add_roles_to_user(user, role)
if ! user.nil?
if role.length > 0
role.each do |sel_role|
#u_role = UserRole.new
#u_role.user_id = user_id
#u_role.role_id = sel_role.role_id
#u_role.save
end
end
end
end
end
class Users::RegistrationsController < Devise::RegistrationsController
def create_user
respond_to do |format|
if #user.save
# add roles
UserRoles.add_user_roles(params[:user], params[:role])
end
end
end
end
I am calling the add_uer_to_role method in the User controller when I am adding or creating a new user.
What I have noticed is that I keep getting different errors based on how I call the method.
For example, I expect to have no errors when I call the method like; UserRoles.add_roles_to_user(params[:user], params[:role]) but it gives the error NameError (uninitialized constant Users::RegistrationsController::UserRoles):
Hoping a good samaritan will come to my aid as soon as possible. Thanks in advance
If it is a common function, you can define it in application controller and call it. Else you can define in helper.
Please verify Calling a method from another controller
You can use that function as a module and use it:
# lib/common_stuff.rb
module CommonStuff
def common_thing
# code
end
end
# app/controllers/my_controller.rb
require 'common_stuff'
class MyController < ApplicationController
include CommonStuff
# has access to common_thing
end

Modifying a Lib Model

I would like to override a Lib Model in my Models and add a relation.
What is the best way to do it ?
Example of a model in rpush lib:
https://github.com/rpush/rpush/blob/f82cc6a25861612ce118b2661f5a47bceb7ebd86/lib/rpush/client/active_record/app.rb
module Rpush
module Client
module ActiveRecord
class App < ::ActiveRecord::Base
self.table_name = 'rpush_apps'
if Rpush.attr_accessible_available?
attr_accessible :name, :environment, :certificate, :password, :connections, :auth_key, :client_id, :client_secret
end
has_many :notifications, class_name: 'Rpush::Client::ActiveRecord::Notification', dependent: :destroy
validates :name, presence: true, uniqueness: { scope: [:type, :environment] }
end
end
end
end
I would like to add a has_many relation without editing the gem
So I thought creating a models/app.rb with this would be a start:
class Rpush::Client::ActiveRecord::App
has_many :rel_group_apps
has_many :groups, :through => :rel_group_apps
end
I tried this but nothing changed. Maybe my models/app.rb is not called ?:
module Rpush
module Client
module ActiveRecord
module App
def self.included(includer)
includer.class_eval do
has_many :rel_group_apps
has_many :groups, :through => :rel_group_apps
end
end
end
end
end
end
How should I do it ? Is there a way to extend a lib model without removing the original behavior ?
Thanks !
EDIT
I Made it work but only by putting this code directly in config/initializers/rpush.rb
It wasn't working in models/app.rb
class Rpush::Client::ActiveRecord::App
has_many :rel_group_apps
has_many :groups, :through => :rel_group_apps
end
If someone has a nicer idea, I'll take it !
Extend the class with class << self
class Rpush::Client::ActiveRecord::App
class << self
[your methods here]
end
end

Using Ability class in Rails Engine

I have a engine inside the lib folder named Support. In that folder, I have a Ticket controller.
I have created an ability class in the main app and I'm trying to manage all the models for the admin role. When I call the Tickets controller, it throws the error:
NameError in Support::TicketsController#index uninitialized constant Ticket
The app/model/ability.rb file is:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.role? == :admin
can :manage , :all
end
end
end
The lib/support/tickets_controller.rb file is:
module Support
class TicketsController < Support::ApplicationController
load_and_authorize_resource
respond_to :html, :xml, :json
def index
end
end
end
If the model class is namespaced differently than the controller, you will need to specify the :class option.
module Support
class TicketsController < ApplicationController
load_and_authorize_resource :class => Support::Ticket
end
end

"undefined method" for a rails model

I am using Devise with rails and i want to add a method "getAllComments", so i write this :
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :city, :newsletter_register, :birthday, :postal_code,
:address_complement, :address, :lastname, :firstname, :civility
has_many :hotels_comments
class << self # Class methods
def getAllComments
true
end
end
end
And in my controller :
def dashboard
#user = current_user
#comments = #user.getAllComments();
end
And when i go to my url, i got
undefined method `getAllComments' for #<User:0x00000008759718>
What i am doing wrong?
Thank you
Because getAllComments is a class method and you are attempting to access it as an instance method.
You either need to access it as:
User.getAllComments
or redefine it as an instance method:
class User < ActiveRecord::Base
#...
def getAllComments
true
end
end
def dashboard
#user = current_user
#comments = #user.getAllComments
end
As I can see, you make getAllComments as class method through addition it to eigenclass. And you try to call this method from instance.
Content of class << self means class method. It could be shortened as def self.getAllComments
You should call it User.getAllComments and not #user.getAllComments
The getAllComments() method that you wrote is a class method
So the correct way to call the methods is
#comments = User.getAllComments
But if you really want to scope the getAllComments to the current user, I recommend you write an instance method
class User < ActiveRecord::Base
..
def getAllComments
// comments implementation
end
So that way you can access the getAllComments method like so:
#user = current_user
#comments = #user.getAllComments

Override a Mongoid model's setters and getters

Is there a way to override a setter or getter for a model in Mongoid? Something like:
class Project
include Mongoid::Document
field :name, :type => String
field :num_users, type: Integer, default: 0
key :name
has_and_belongs_to_many :users, class_name: "User", inverse_of: :projects
# This will not work
def name=(projectname)
#name = projectname.capitalize
end
end
where the name method can be overwritten without using virtual fields?
better use
def name=(projectname)
super(projectname.capitalize)
end
the method
self[:name] = projectname.capitalize
can be dangerous, cause overloading with it can cause endless recursion
def name=(projectname)
self[:name] = projectname.capitalize
end
I had a similar issue with needing to override the "user" setter for a belongs_to :user relationship. I came up with this solution for not only this case but for wrapping any method already defined within the same class.
class Class
def wrap_method(name, &block)
existing = self.instance_method(name)
define_method name do |*args|
instance_exec(*args, existing ? existing.bind(self) : nil, &block)
end
end
This allows you to do the following in your model class:
wrap_method :user= do |value, wrapped|
wrapped.call(value)
#additional logic here
end

Resources