Active Record with Delegate and Conditions

Active Record with Delegate and conditions

No, you can't, but you can pass the :allow_nil => true option to return nil if the master is nil.

class User < ActiveRecord::Base
delegate :company, :to => :master, :allow_nil => true

# ...
end

user.master = nil
user.company
# => nil

user.master = <#User ...>
user.company
# => ...

Otherwise, you need to write your own custom method instead using the delegate macro for more complex options.

class User < ActiveRecord::Base
# ...

def company
master.company if has_master?
end

end

How to conditionally delegate Active Records in Ruby

Yes.

in addition to other objects, functions can be delegated to other functions.

Ex:

class Item
has_one :thing
has_one :other_thing

delegate :foo,
:bar
to: :correct_thing

def correct_thing
[conditional] ? thing : other_thing
end
end

delegate in rails active record

All those methods are a part of an ActiveRecord::Relation. YourModel.all returns a YourModel::ActiveRecord_Relation (which is an ActiveRecord::Relation despite the different name). So, that delegation works a shorthand to do YourModel.all.find without writing .all all the time.

Rails: Calling or delegate instance method from another model

do it in a callback function...

inside your model:

after_save :update_order_status

def update_order_status
if # relevant condition
# do stuff
end
end

you're calling a class method but defining it as an instance method. in order to call:

Reservation.update_status

update_status needs to be defined as:

def self.update_status

but when you're calling a class method...you're not going to have access to self, so you won't be able to say

self.status_id # or whatever it was

that is, you won't be able to call this:

Reservation.update_status_completed # calling as class method

in one model and do this in another:

self.reservation_status_id = 5 # inside that class method trying to use instance 

because you don't have an instance there to get the status_id from. To fix this, pass the value through an argument to the class method so you're not trying to rely on an object you don't have access to.

EDIT:
Can't tell exactly which reservation you're trying to update but to update all of them:

# order.rb
def update_reservations_status
if self.order_status_id == 2
self.reservations.each do |reservation|
reservation.update_status_completed
end
end
end

# reservation.rb
def update_status_completed
self.reservation_status_id = 5
self.save
end

Now the self in update status completed is referring to an instance...bc we're calling last in update_reservations_status.

Active Record Query for subset conditions

To get ClinicianProfiles that match at least one insurance:

@profiles = ClinicianProfile.joins(:insurances)
.where(insurances: { id: @patient.insurances })
.group('clinician_profiles.id')
.having("count(*) = 1")

You can then fetch the users by joining clinician_profiles:

@users = User.joins(:clinician_profile)
.where(
market: @user.university.market,
clinician_profile: { id: @profiles }
)

Rails - Delegate method to last record in has_many relation

I think you can do that if you create a method to return the last 'bs' object:

def last_bs
self.bs.last
end

then delegate:

delegate :my_method, to: :last_bs

UPDATE: This can also be achieved by:

delegate :my_method, to: "bs.last", allow_nil: true

Delegating method to has_many association ignores preloading

Explanation

The reason why all_have_title? delegation doesn't work properly in your example is that your are delegating the method to blogs association, but yet defining it as a Blog class method, which are different entities and thus receivers.

At this point everybody following would be asking a question why there is no NoMethodError exception raised when calling user.all_have_title? in the second example provided by OP. The reason behind this is elaborated in the ActiveRecord::Associations::CollectionProxy documentation (which is the resulting object class of the user.blogs call), which rephrasing due to our example namings states:

that the association proxy in user.blogs has the object in user as @owner, the collection of his blogs as @target, and the @reflection object represents a :has_many macro.

This class delegates unknown methods to @target via method_missing.

So the order of things that are happening is as follows:

  1. delegate defines all_have_title? instance method in has_many scope in User model on initialization;
  2. when called on user all_have_title? method is delegated to the has_many association;
  3. as there is no such method defined there it is delegated to Blog class all_have_title? method via method_missing;
  4. all method is called on Blog with current_scope which holds user_id condition (scoped_attributes at this point is holding {"user_id"=>1} value), so there is no information about preloading, because basically what is happening is:

    Blog.where(user_id: 1)

    for each user separately, which is the key difference in comparison with the preloading that was performed before, which queries associated records by multiple values using in, but the one performed here queries a single record with = (this is the reason why the query itself is not even cached between these two calls).

Solution

To both encapsulate the method explicitly and mark it as a relation-based (between User and Blog) you should define and describe it's logic in the has_many association scope:

class User
delegate :all_have_title?, to: :blogs, prefix: false, allow_nil: false

has_many :blogs do
def all_have_title?
all? { |blog| blog.title.present? }
end
end
end

Thus the calling you do should result in the following 2 queries only:

user = User.includes(:blogs).first
=> #<User:0x00007f9ace1067e0
User Load (0.8ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
Blog Load (1.4ms) SELECT `blogs`.* FROM `blogs` WHERE `blogs`.`user_id` IN (1)
user.all_have_title?
=> true

this way User doesn't implicitly operate with Blog's attributes and you don't lose you preloaded data. If you don't want association methods operating with title attribute directly (block in the all method ), you can define an instance method in Blog model and define all the logic there:

class Blog
def has_title?
title.present?
end
end

Rails delegate method with more than one association

As a personal opinion, @user.blog_hash_tag_tag_name is horrible to look at.

That said, wanting to define both delegates at the user level is also a violation of LoD because you are using knowledge of the inner workings of blog (the fact that it belongs to a hash_tag) from the user class.

If you want to use delegates, you should add to class User

delegate :hash_tag_tag_name, to: :blog, prefix: true, allow_nil: true

and to class Blog

delegate :tag_name, to: :hash_tag, prefix: true, allow_nil: true

Building Active Record Conditions in an array - private method 'scan' called error

Try this:

Rails 2.3

class Site < ActiveRecord::Base

def self.build_conditions(ids, name=nil, state=nil)
cond = []
cond << send(:sanitize_sql_array, ["id NOT IN (?)", ids]) unless ids.empty?
cond << send(:sanitize_sql_array, ["name = ? ", name]) unless name
cond << send(:sanitize_sql_array, ["state = ? ", state]) unless state
cond.join(" and ")
end
end

Now somewhere in your controller:

Site.all(:conditions => Site.build_conditions([1,2])) 
Site.all(:conditions => Site.build_conditions(nil, "ABC"))

Rails 3

class Site < ActiveRecord::Base          
def self.exclude_ids_by_name_and_state(ids, name=nil, state=nil)
result = scoped
result = result.where("id NOT IN (?)", ids) if ids.present?
result = result.where(:name => name) if name.present?
result = result.where(:state => state) if state.present?
result
end
end

Now somewhere in your controller:

Site.exclude_ids_by_name_and_state([1,2])).all 
Site.exclude_ids_by_name_and_state(nil, "ABC").all


Related Topics



Leave a reply



Submit