Rails 4 Override belongs_to setter - ruby-on-rails

I am having an issue overriding a setter on a belongs_to attribute. I have the following:
class Task < ActiveRecord::Base
belongs_to :parent_task, :class_name => 'Task', :foreign_key => 'parent_task_id'
def parent_task=(value)
write_attribute(:parent_task, value)
unless value == nil
#remove all groups_belonging_to if this has been made into a child task -i.e. if it has a parent
self.groups_belonging_to = []
end
self.save
end
The user model has many tasks:
class User < ActiveRecord::Base
has_many :tasks_created_by, class_name: 'Task', foreign_key: 'created_by_id', dependent: :destroy
In my testing I am creating a task like so:
#child_task = #user.tasks_created_by.create!(name: "Task to Delete", parent_task: #top_parent)
Which gives the error:
ActiveModel::MissingAttributeError: can't write unknown attribute `parent_task`
When I remove the override there is no problem, so I am definitely doing the override wrong somehow. I have used very similar override logic elsewhere but not through a relation before.

This would be better written as a callback. You can use a before_save callback to check for a parent_task and if it is set, clear groups_belonging_to:
class Task < ActiveRecord::Base
belongs_to :parent_task, class_name: 'Task', foreign_key: 'parent_task_id'
before_save :clear_groups if: :parent_task
def clear_groups
self.groups_belonging_to = []
end
end

I solved this problem using alias_method before I override the setter:
class Task < ActiveRecord::Base
belongs_to :parent_task, :class_name => 'Task', :foreign_key => 'parent_task_id'
alias_method :set_parent_task, :parent_task=
def parent_task=(value)
set_parent_task(value)
unless value == nil
#remove all groups_belonging_to if this has been made into a child task -i.e. if it has a parent
self.groups_belonging_to = []
end
self.save
end
end

Related

When creating a record, copy an attribute from a associated record

In my Rails to-do app, I have a Tasks model. Tasks can be blocked_by each other. Each task has a User. When I do taskA.blocked_by.create(name: "Task B"), I would like Task B to get the same User that Task A has.
The problem is, I can't figure out how to refer to the record that's creating the current record. I need to learn how to get taskA.user so I can automatically assign it to taskB. I'd rather not have to do that manually every time I create a blocked_by task.
I've tried setting self.user in a before_validation method.
before_validation :inherit_user, on: :create
private
def inherit_user
if self.user == nil
p "Task #{self.name} missing user"
if self.blocked_by.count > 0
self.user = self.blocked_by.first.user
p "Inheriting user from first blocked_by task #{self.blocked_by.first.name}"
end
end
end
This doesn't work because self.blocked_by is empty because the record isn't saved yet.
Rails' documentation on association class methods leads me to believe I should be able to do something like this:
has_many :blocked_by do |owner|
def create(attributes)
p owner # access properties from association owner here
attributes.push(user: owner.user)
super
end
end
When I try this I get:
NoMethodError (undefined method `owner' for #<ActiveRecord::Associations::CollectionProxy []>)
Edit: Here's my model file:
class Task < ApplicationRecord
validates :name, :presence => true
belongs_to :user
has_many :children, class_name: "Task", foreign_key: "parent_id"
belongs_to :parent, class_name: "Task", optional: true
has_ancestry
# thanks to https://medium.com/#jbmilgrom/active-record-many-to-many-self-join-table-e0992c27c1e
has_many :blocked_blocks, foreign_key: :blocker_id, class_name: "BlockingTask"
has_many :blocked_by, through: :blocked_blocks, source: :blocking, dependent: :destroy
has_many :blocker_blocks, foreign_key: :blocked_id, class_name: "BlockingTask"
has_many :blocking, through: :blocker_blocks, source: :blocker, dependent: :destroy
has_many_attached :attachments
before_validation :inherit_user, on: :create
def completed_descendants
self.descendants.where(completed: true)
end
def attachment_count
self.attachments.count
end
private
def inherit_user
if self.user == nil and self.parent
self.user = self.parent.user
end
end
end
I can inherit_user from a parent task, like so: taskA.children.create(name: "Task B"). I'd like to do the same for a blocked_by relationship.
To refer to the current record which is supposed to be created, try running before_create callback.
before_create :inherit_user
and the self.blocked_by now must have a value.

rails ams, Nested models undefined method `' for nil:NilClass

I have the following models:
class Appeal < ActiveRecord::Base
belongs_to :applicant, :autosave => true
belongs_to :appealer, :autosave => true
end
class Appealer < ActiveRecord::Base
has_many :appeals, :autosave => true
end
class Applicant < ActiveRecord::Base
has_many :appeals
end
What I want is for every appealer to hold a reference to the applicant of his last appeal
so I modified Appealer model to:
class Appealer < ActiveRecord::Base
has_many :appeals, :autosave => true
def last_applicant
return self.appeals.last.applicant
end
end
but I get the error:
undefined method `applicant' for nil:NilClass
what is strange that if I debug this (via RubyMine - Evaluate Expression) I can get the applicant.
If I try to get the last appeal:
class Appealer < ActiveRecord::Base
has_many :appeals, :autosave => true
def last_appeal
return self.appeals.last
end
end
everything works.
Im working with active-model-serializer, tried to do it also in the serializer (I actually need this value on a specific call - not allover the model) but it also didnt worked with the same errors.
the AMS code:
class AppealerTableSerializer < ActiveModel::Serializer
attributes :id, :appealer_id, :first_name, :last_name, :city
has_many :appeals, serializer: AppealMiniSerializer
def city
object.appeals.last.appealer.city
end
end
MY QUESTION:
How can I get the nested object attributes in my JSON?
What am I doing wrong?
EDIT:
My controller call:
class AppealersController < ApplicationController
def index
appealers = Appealer.all
render json: appealers, each_serializer: AppealerTableSerializer, include: 'appeal,applicant'
end
end
I've tried with and without the include, still not works
Maybe I am missing something, because this looks like you have Appealer record that doesn't have any appeals yet.
In such case, this code
def last_appeal
return self.appeals.last
end
Will return nil, which won't raise any errors. But if you call this
def last_applicant
return self.appeals.last.applicant
end
return self.appeals.last is nil and you try to call applicant method on nil object instead of Appeal object.
To fix it just add check for nil
class Appealer < ActiveRecord::Base
has_many :appeals, :autosave => true
def last_applicant
last = self.appeals.last
if last.nil?
return nil
else
return last.applicant
end
end
end

Validation to prevent parent referencing self as children

I'm looking for a solution to prevent "parents" to add his self as "children".
My Model looks like this:
class Category < ActiveRecord::Base
belongs_to :parent, :class_name => 'Category'
has_many :children, :class_name => 'Category', :foreign_key => 'parent_id'
end
Now I look for a solution to prevent things like this
parent = Category.create(name: "Parent")
Category.new(name: "Children", parent_id: parent.id).valid? # should be => false
You can add a custom validation for that.
Something like
class ParentValidator < ActiveModel::Validator
def validate(record)
if record.parent_id == record.id
record.errors[:parent_id] << 'A record\'s parent cannot be the record itself'
end
end
end
class Category
include ActiveModel::Validations
validates_with ParentValidator
end
or even simpler (if it is a one off thing)
class Category < ActiveRecord::Base
validate :parent_not_self, on: :save
def parent_not_self
if parent_id == id
errors.add(:parent_id, 'A record\'s parent cannot be the record itself')
end
end
end
Both cases will generate a validation error when you try to assign the record itself as the parent's record

ruby on rails, has_many, define class name for polymorphic relationship

This is my code for moving data from my old database:
class Old < ActiveRecord::Base
establish_connection :old_version
self.abstract_class = true
class Recipe < self
set_table_name :recipes
has_many :uploaded_files, :as => :storage
end
class UploadedFile < self
set_table_name :uploaded_files
belongs_to :storage, :polymorphic => true
end
end
When I run the following code
Old::Recipe.all.each do |recipe|
puts recipe.uploaded_files.to_sql
end
It performs this SQL
SELECT `uploaded_files`.* FROM `uploaded_files` WHERE `uploaded_files`.`storage_id` = 38 AND `uploaded_files`.`storage_type` = 'Old::Recipe'
The problem is that I get:
`storage_type` = 'Old::Recipe'
But I need:
`storage_type` = 'Recipe'
How can I change the class for a polymorphic relationship?
The doc for has_many doesn't give me an answer.
Recently I had similar problem, this is a solution that worked for me in rails 4.2:
class Recipe < self
set_table_name :recipes
has_many :old_files, -> (object) { unscope(where: :storage_type).where(storage_type: 'Recipe') }, class_name: 'UploadedFile'
end
You have to add unscope(:where) to remove condition uploaded_files.storage_type = 'Old::Recipe' from query.
The answer by santuxus above is working properly for rails 4.2+
However, for lower versions, you could try overwriting the association like so:
class Recipe
has_many :uploaded_files, conditions: { storage_type: 'Recipe' }, foreign_key: :storage_id
end
Unfortunately, for now I found only one way to do that:
class Old < ActiveRecord::Base
establish_connection :old_version
self.abstract_class = true
class Recipe < self
set_table_name :recipes
has_many :old_files,
:class_name => 'UploadedFile',
:finder_sql => Proc.new {
%Q{
SELECT uploaded_files.*
FROM uploaded_files
WHERE uploaded_files.storage_id = #{id} AND
uploaded_files.storage_type = 'Recipe'
}
}
end
class UploadedFile < self
set_table_name :uploaded_files
belongs_to :storage, :polymorphic => true
end
end
namespace :old do
task :menu => :environment do
Old::Recipe.all.each do |recipe|
puts '~' * 50
puts recipe.id
recipe.old_files.to_a.each do |file|
puts file.storage_id, file.storage_type
end
end
end
end
very very sad

Is there any way to check that has_many association exists in Rails 3.1?

For example there are some models
class Model_1 < ActiveRecord::Base
has_many :images, :as => :imageable
end
class Model_2 < ActiveRecord::Base
# doesn't have has_many association
end
...
class Image < ActiveRecord::Base
belongs_to :imageable, :polymorphic => true
end
How can I check that model has has_many association? Something like this
class ActiveRecord::Base
def self.has_many_association_exists?(:association)
...
end
end
And it can be used so
Model_1.has_many_association_exists?(:images) # true
Model_2.has_many_association_exists?(:images) # false
Thanks in advance
What about reflect_on_association?
Model_1.reflect_on_association(:images)
Or reflect_on_all_associations:
associations = Model_1.reflect_on_all_associations(:has_many)
associations.any? { |a| a.name == :images }
I found the following to be the simple way to achieve the desired result:
ModelName.method_defined?(:method_name)
Example:
Model_1.method_defined?(:images) # true
Model_2.method_defined?(:images) # false
Reference: https://stackoverflow.com/a/18066069/936494
You could probably use respond_to?
class ActiveRecord::Base
def self.has_many_association_exists?(related)
self.class.associations.respond_to?(related)
end
end
You could just have a method that tries to access a Model_1 object images inside an exception block like (roughly) :
begin
model1_obj.images
rescue
puts 'No association between model_1 and images'
end
Inside rescue, you can just return false if you like.

Resources