Call child's method or cast parent to child in Rails - ruby-on-rails

I have some STI structure like following:
class Assignment < ActiveRecord::Base
has_many :criteria, :class_name => "Criterion", :order => :position
end
class Criterion < ActiveRecord::Base
# Represents a criterion used to mark an assignment that
# being the super class for all different criterias
set_table_name "criteria" # set table name correctly
belongs_to :assignment
validates_associated :assignment, :message => 'association is not strong with an assignment'
validates_presence_of :assignment_id
validates_numericality_of :assignment_id, :only_integer => true, :greater_than => 0, :message => "can only be whole number greater than 0"
validates_uniqueness_of :criterion_name, :scope => :assignment_id, :message => 'is already taken'
validates_presence_of :criterion_name,:assignment_id
end
class FlexibleCriterion < Criterion
has_one :flexible_criterion_attribute, :class_name => "FlexibleCriterionAttribute"
accepts_nested_attributes_for :flexible_criterion_attribute
default_scope :include => :flexible_criterion_attribute
end
class FlexibleCriterionAttribute < ActiveRecord::Base
belongs_to :criterion, :class_name => "FlexibleCriterion"
validates_presence_of :max
validates_numericality_of :max, :message => "must be a number greater than 0.0", :greater_than => 0.0
DEFAULT_MAX = 1
end
Alright, I have uploaded my current working codes. So basically the problem is:
When I use a method like criterion = assignment.criteria.find_or_create_by_criterion_name("AAA"), I will get an object of criterion. But I want cast this object to flexiblecriterion so that I can assign the value of criterion.flexible_criterion_attribute
Thx in advance!

If assignment.criteria.find_or_create_by_criterion_name("AAA") returns a record in which type column has value FlexibleCriterion, it will automatically cast the object to FlexibleCriterion.
If you want to ensure that only FlexibleCriterion is returned, modify your finder like this: assignment.criteria.find_or_create_by_criterion_name_and_type("AAA", 'FlexibleCriterion')

Related

How to validate_presence_of with condition in a nested attributes?

I have model Question, Answer and AnswerDetail.
Answer:
class Answer < ActiveRecord::Base
has_many :answer_details, :dependent => :destroy
accepts_nested_attributes_for :answer_details, :allow_destroy => true
validates_associated :answer_details
end
AnswerDetail:
class AnswerDetail < ActiveRecord::Base
belongs_to :answer
belongs_to :question
validates_presence_of :answer_field, :if => lambda {isrequired == true}, :message => "This is required field"
end
The isrequired field is from the model Question.
Question:
class Question < ActiveRecord::Base
has_one :answer_detail
end
The AnswerDetal model has the question_id and answer_id field on it. I want to filter the answer_field if the isrequire field from Question model is equal to true? How I will do this? How to access the has_one association's attribute inside model?
I've done this before, although I've not got the code handy right now:
Nested
From what I remember, you can actually put the validation in the nested model:
#app/models/question.rb
class Question < ActiveRecord::Base
validates :answer_field, presence: true, if: lambda {isrequired == true}
end
I highly recommend using the new validates syntax
--
inverse_of
I'm sure I had to use inverse_of somewhere in the code I had (it's locked in a private GitHub repo sorry).
inverse_of basically includes the associated model in your current model. Much like how you've found the effectiveness of self.question.isrequired:
#app/models/answer_detail.rb
class AnswerDetail < ActiveRecord::Base
belongs_to :question, inverse_of: :answer_detail
validates :answer_field, presence: true, if: lambda { question.isrequired == true }
end
#app/models/question.rb
class Question < ActiveRecord::Base
has_one :answer_detail, inverse_of: :question
end
However, good news for you:
This is not the answer, this is how I would troubleshoot
Inspect in:
validates_presence_of :answer_field, :if => lambda {isrequired == true}, :message => "This is required field"
What variables you have available, you can do this with pry-rails gem. Like this:
validates_presence_of :answer_field, :if => lambda {binding.pry; isrequired == true}, :message => "This is required field"
When trying to save a question you will get a interactive console(in the server terminal window) where you can print values like self or do this:
validates_presence_of :answer_field, :if => lambda {|record| binding.pry; isrequired == true}, :message => "This is required field"
And print record.
Then you can try finding where Question is stored with: self.question or record.question.
I tried using self.question.isrequired and it is working. But maybe you have other nice suggestion to do the error validation. Thank you.

Rails 4 ActiveRecord::Relation ArgumentError just doesn't make sense

This is a rather simple Rails 4 situation. Model Intranet has_many activities. Activities exists with sufficient records for several intranets. Current_intranet.activities.size returns 69 records. However, whenever I try to access any of the records, I receive "output error: <ArgumentError: wrong number of arguments (1 for 0)" even though I'm not passing any arguments. All of the following fail with that error.
Current_intranet.activities.first
Activity.where(intranet_id: 1).first
Activity.where(:intranet_id => 1).first
Activity.where{intranet_id.eq 1}.first
All of the above with [0] instead of first
There is no problem with any other models
I'd appreciate any suggestions. Thanks in advance.
There are no defined scopes. The code is:
class Activity < ActiveRecord::Base
acts_as_capitalized
# activities created during intranet creation.
DEFAULT_ACTIVITIES = { 'Document Preparation' => 'general income', 'No Charge' => 'general income'}
# static activity names that can not be deleted.
STATIC_ACTIVITY_NAMES = [ 'Document Preparation','No Charge']
before_destroy :check_if_used
before_destroy :cannot_delete_static_activities
belongs_to :created_by_user, :class_name => "User", :foreign_key => "created_by"
belongs_to :updated_by_user, :class_name => "User", :foreign_key => "updated_by"
belongs_to :intranet
belongs_to :chart_of_account
has_many :activity_rates, :dependent => :destroy
has_many :event_type
has_many :billings do
def find_by_name(name)
(find :all, :conditions => ["activity.name = ?",name])
end
end
has_many :document_templates, :foreign_key => "billing_activity_id"
validates_presence_of :intranet_id, :name, :chart_of_account_id
validates_uniqueness_of :name, :scope => :intranet_id

Rails Conditional Validation

So I have two models here:
class Screen < ActiveRecord::Base
belongs_to :user
validates :screen_size, :numericality =>{:less_than_or_equal_to =>100,:greater_than_or_equal_to => 0},:if => "user.access==1"
class User < ActiveRecord::Base
has_many :screens
attr_accessible :access
But this code doesn't work, cause no matter what value the user.access is, it will still execute the validation. What am I doing wrong here?
Thank you
change:
:if => "user.access==1"
with:
:if => lambda { |screen| screen.user.try(:access) ==1 }
Because:
you need to pass a function to evaluate condition on the fly
if your screen doesn't have any user, a mere screen.user.access would throw an exception
You passed in a string to the :if executable proc/function parameter. When this is a string, it tries to find a function with that name. What you actually want is an anonymous function here using a lambda.
class Screen < ActiveRecord::Base
belongs_to :user
validates :screen_size, :numericality => {:less_than_or_equal_to =>100, :greater_than_or_equal_to => 0}, :if => lambda {|s| s.user.access == 1 }
end
class User < ActiveRecord::Base
has_many :screens
attr_accessible :access
end

Polymorphic model question in rails - does it maintain the relationship for you?

Let's say you have a SiteUpdate and a Comment model, and you want to make Comment polymorphic. You make comment hold a "commentable_id" and "commentable_type"...
Here's our comment model:
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
validates_presence_of :content
validates_presence_of :commentable
end
Here is our SiteUpdate:
class SiteUpdate < ActiveRecord::Base
belongs_to :author, :class_name => 'User', :foreign_key => 'author_id'
has_many :comments, :as => :commentable
validates_presence_of :subject
validates_length_of :subject, :maximum => 80
validates_presence_of :intro
validates_length_of :intro, :maximum => 200
validates_presence_of :text
validates_presence_of :author
scope :sorted, order("site_updates.created_at desc")
end
Does Rails link the commentable to the site_update instance, or do I have to do that manually?
#site_update.comments << Factory.build(:comment, :commentable_id => nil)
#site_update.save
This fails -> it complains that the comment.commentable_id should not be blank (I set this validation in the Comment model).
So do I do this manually, or do I set this up incorrectly?
Or do I just not validate it at all?
I'm making an assumption that your #site_update object is a new object. If so...
There's a somewhat annoying thing about rails associations. You can't really add them before the record is saved.
What's happening is, you have a new site update object without an ID. You build a new comment object for that site update, so it sets the commentable_type to "SiteUpdate", however, there's no ID yet, so it sets the commentable_id to nil. You save, and it bubbles out to save associated objects, but it doesn't set the comment commentable_id to the SiteUpdate ID, because it doesn't exist.
So if you change it around to be :
#site_update.save
#site_update.comments << Factory.build(:comment, :commentable_id => nil)
#site_update.comments.map { |c| c.save }
it should work.
If it's not a new record...it should work as is.

accepts_nested_attributes_for validation

I'm using Rails 2.3.8 and accepts_nested_attributes_for.
I have a simple category object which uses awesome_nested_set to allow nested categories.
For each category I would like a unique field called code. This would be unique for the category per level. Meaning parent categories will all have unique codes and sub categories will be unique within their own parent category.
EG:
code name
1 cat1
1 sub cat 1
2 cat2
1 sub cat 1
2 sub cat 2
3 cat3
1 sub1
This works without the validation process but when I try and use something like:
validates_uniqueness_of :code, :scope => :parent_id
That will not work because the parent has not been saved yet.
Here is my model:
class Category < ActiveRecord::Base
acts_as_nested_set
accepts_nested_attributes_for :children, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
default_scope :order => "lft"
validates_presence_of :code, :name, :is_child
validates_uniqueness_of :code, :scope => :parent_id
end
I have thought of a different way to do this and it's very close to working, problem is I cannot check for uniqueness between child categories.
In this second example I've embedded a hidden field in the form called 'is_child' to flag if the item is a sub category or not. Here is my example of this model:
class Category < ActiveRecord::Base
acts_as_nested_set
accepts_nested_attributes_for :children, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
default_scope :order => "lft"
validates_presence_of :code, :name, :is_child
#validates_uniqueness_of :code, :scope => :parent_id
validate :has_unique_code
attr_accessor :is_child
private
def has_unique_code
if self.is_child == "1"
# Check here if the code has already been taken this will only work for
# updating existing children.
else
# Check code relating to other parents
result = Category.find_by_code(self.code, :conditions => { :parent_id => nil})
if result.nil?
true
else
errors.add("code", "Duplicate found")
false
end
end
end
end
This is very close. If there was a way to detect duplicate codes in the reject_if syntax under accepts_nested_attributes_for then I would be there. This all seems over complicated though and would like suggestions to make it easier. We would like to keep adding categories and sub categories in the one form as it speeds up data entry.
Update:
Maybe I should be using build or before_save.
Instead of
validates_uniqueness_of :code, :scope => :parent_id
Try
validates_uniqueness_of :code, :scope => :parent
Besides that you'll need to set in the Category class:
has_many :children, :inverse_of => :category # or whatever name the relation is called in Child
The use of inverse_of will make the children variable parent be set before saving, and there is a chance it will work.

Resources