Disable default scope for associations in module / Concern - ruby-on-rails

I have a concern where I want to have unscoped association, But could not able to get it. In one of my instance method , I have a logic like that:
Assume I want to use only one parent (belongs_to)
parent_class_name = self.class.reflect_on_all_associations(:belongs_to).map(&:name).last
parent = nil
if parent_class_name.present?
parent_class_const = parent_class_name.to_s.camelize.constantize
parent_class_const.send(:unscoped) do
parent = self.send(parent_class_name)
end
end
The parent association has always the default scope, which should not be case. Similarly I have logic for has_many association, but that also does not work.
Looking forward for help.
P.S I don't want to use any gem

First, you can get the class constant directly:
parent_class = self.class.reflect_on_all_associations(:belongs_to).last.klass
Then you can call unscoped on the class directly:
parent_class.unscoped do
# do the unscoped stuff here
end

Related

Check if scope is called on class or instance (scoped)

Say I have a scope:
class Post
belongs_to: :user
scope(:visible_for, ->(user = default_user) { where("<some SQL>") })
end
Is there a way to check inside the scope whether it has been called 1. scoped or 2. unscoped?
some_user.posts.visible_for
Post.visible_for
Reason I want this is scoped automatically includes WHERE posts.user_id = <user_id> which optimizes the query, and I want only optimized query to be legal.
I just found you can call scope_attributes inside the scope, problem solved.

Rails scope check serialize column is present

I am learning the scope of rails
if QuestionSet has a column called questions_list and its format is serialize.
Like this
class QuestionSet < ActiveRecord::Base
serialize :questions_list
end
Then I have a method called is_order, and it is simple.
The only use is to check whether questions_list is present or not
Like this
def is_order
self.questions_list.present?
end
Can I write it into a scope? Or in this case, it is not a suitable scope scenario
scope is a wrong choice here, becuase it's always returns an Active Record and doesn't bound to any instance object, scope it's about collection.
Scoping allows you to specify commonly-used queries which can be
referenced as method calls on the association objects or models.
If your goal is to have a scope that will return QuestionSet records that have a questions_list present, you should be able to define a scope like this:
scope :with_questions_list, -> { where.not(questions_list: nil) }
And then you can do:
QuestionSet.with_questions_list # This is the same as QuestionSet.all.with_questions_list
If the goal is instead to build a method that will return true or false for a single object, then you are doing it correctly, but I'll suggest two changes: (1) You don't need to reference self (as that is implied in the context) and (2) you should use the Ruby convention of putting a question mark at the end of your method.
def is_order?
questions_list.present?
end

Specify currently grabbed records within Model class method

I have a class method where I want to modify the records that are currently grabbed by an ActiveRecord::Relation object. But I don't know how to refer to the current scope in a class method. self does not do it.
Example:
class User < ActiveRecord::Base
...
def self.modify_those_records
#thought implicitly #to_a would be called on currently grabbed records but doesn't work
temp_users_to_a = to_a
...
end
end
I would use it like this:
User.some_scope.modify_those_records
So User.some_scope would return to me an ActiveRecord::Relation that contains a bunch of User records. I then want to modify those records within that class method and then return them.
Problem is: I don't know how to explicitly refer to "that group of records" within a class method.
You can use current_scope:
def self.modify_those_records
current_scope.each do |user|
user.do_something!
end
end
If you want to order Users based on their admin rights, you would be better to use ActiveRecord:
scope :order_admins_first, order('CASE WHEN is_admin = true THEN 0 ELSE 1 END, id')
User.some_scope.order_admins_first
This code implies that you have a boolean column is_admin on the users table.
I would argue that a combination of a scope with each and an instance method is easier to understand than a class method. And as a bonus it is easier to test, because you can test all steps in isolation:
Therefore instead of User.some_scope.modify_those_records I would do something like:
User.some_scope.each(&:modify)
and implement a instance method:
# in user.rb
def modify
# whatever needs to be done
end
If you only want to modify the order of the records - better way is to add a sort field (if you do not have it already) to the model and sort by that.
User.some_scope.order(name: :asc).order(is_admin: :desc)

How to get the scoped attributes when creating a new object through a has_many association

When creating objects through a has_many association like User.first.books.create!(...), the new book gets the user_id automatically from the association.
Is there any way to get that user_id if I call my own create method? i.e. User.first.books.own_create_method
def self.own_create_method
# how to get the user object?
end
Thanks!
To define User.first.books.own_create_method you would use:
def self.own_create_method
book = build
# something custom you want to do with book
book.save
end
self. allows you to define class methods in Ruby.
Digging into ActiveRecord new method, I found that you can call scope_attributes and you'll get a hash with all the attributes that are scoped.
def self.own_create_method
attributes = scope_attributes
# attributes["user_id"] would be the user_id that is scoped by
...
end
Not sure if this is a good practice though...

Assigning rails association using strings

Say I have two models and one belongs to another. Now normaly you would assign an object to the association when populating the fields. Does rails allow overriding the set method so that the association assignment can be customised?
E.g
class Person
# something about shirts
end
class Shirt
belongs_to :person
def person=(p)
self.person = Person.find_or_create_by_name(p)
end
end
And then use something like so auto bind the association but using a string to do the searching and binding automatically. Is this possible?
s = Shirt.new
s.person = "Test Person"
Thanks
ROR Guides cover the association extension you need.
UPDATE:
Actually, overriding setter is not that bad, once you understand what you're doing. But you have to be careful, since it can cause infinite loop (as in your example). So if you're using Rails 3.2, you have to use super, in other case you have to use alias_method_chain.

Resources