In the form the user has to choose a month and a year. I'd like to make sure that the date is not in the future based on the month. So let's say the current date is 07/01/2016 in which case the user should be able to choose 07/2016 but shouldn't be able to choose 08/2016.
The validation seems to be working, but doesn't look well. Is there an easier way
achieve this?
validate :founded_in_the_past
def founded_in_the_past
if founded && ((founded.month > Date.current.month && founded.year == Date.current.year)
&& founded.year > Date.current.year)
errors.add :base, "Founding date must be in the past."
end
end
Instead of checking with month and year why you are not comparing date directly by creating date object using month and year.
validate :founded_in_the_past
def founded_in_the_past
if founded && (Date.new(founded.year, founded.month, 1) > Date.current)
errors.add :base, "Founding date must be in the past."
end
end
In your code if you select year as 2017 and month as 5 - it will allow to select this date
founded && ((founded.month > Date.current.month &&
founded.year == Date.current.year) &&
founded.year > Date.current.year)
founded = true
founded.month > Date.current.month = false
founded.year == Date.current.year = false
founded.year > Date.current.year = true
If we map this in your condition you condition will look like
true && ((false && false) && true)
This will return false and it will allow user select future date
You can cut out one conditional by using >= eg
if founded && (founded.month > Date.current.month && founded.year >= Date.current.year)
Related
User.rb
# Attributes
# (..)
# birthdate (string)
# format "mm/yyyy"
def age
dob = self.birthdate.to_date
now = Time.now.utc.to_date
now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
end
In the console:
irb(main):002:0> current_user.age
=> 7
I'd be able to do the following:
age_range = "25-65"
User.where(:age => between age_range)
I'm stuck at the point how to get the value from age (class method) into the where call
First of all: use a date as type for birthdate in the database.
Then you can just use:
User.where(birthdate: 65.years.ago..25.years.ago)
If you can't change the birthdate type convert it using SQL (example with PostrgeSQL):
User.where('to_date(birthdate, 'MM/YYYY') between ? and ?', 65.years.ago, 25.years.ago)
But you may still have to correct it since you don't have the exact day and only the month.
With PostgreSQL you can use that Rails scope
scope :for_age_range, -> min, max {
where("date_part('year', age(birthdate)) >= ? AND date_part('year', age(birthdate)) <= ?", min, max)
}
User.for_age_range(18, 24)
Hi I have a method that check overlapping for new and updated objects.
Method:
def timeline
if start_at_changed? || end_at_changed? || person_id_changed?
if (person.vacations.where('start_at <= ?', start_at).count > 0 &&
person.vacations.where('end_at >= ?', end_at).count > 0) ||
(person.vacations.where('start_at <= ?', start_at).count > 0 &&
person.vacations.where('end_at <= ?', end_at).count > 0 &&
person.vacations.where('end_at >= ?', start_at).count > 0) ||
(person.vacations.where('start_at >= ?', start_at).count > 0 &&
person.vacations.where('start_at <= ?', end_at).count > 0 &&
person.vacations.where('end_at >= ?', end_at).count > 0)
errors.add(:base, 'You have a vacation during this period.')
end
end
end
My issue is while I try edit some vacation. If I change e.g. start_at validation checks also this object and return error. Example: start_at 2016-06-27, end_at: 2016-06-30. I try change start_at to 2016-06-26. Validation returns 'You have a vacation during this period.', because it check period 2016-06-27 - 2016-06-30. How to exclude checking object that I try update, only other?
You just need to exclude the current vacation id on you queries. Something like:
def timeline
if start_at_changed? || end_at_changed? || person_id_changed?
if (person.vacations.where('start_at <= ? and vacations.id != ?', start_at, id).count > 0 &&
person.vacations.where('end_at >= ? and vacations.id != ?', end_at, id).count > 0) ||
...
end
end
end
Also you can use the not condition. More info here and here.
Just to point, you're executing a lot of queries here. This can be very expensive in production. My suggestion is to merge all this queries in just one query, so besides the speed improvements, the readability of your code will be really better.
You code will look like this:
def timeline
if start_at_changed? || end_at_changed? || person_id_changed?
overlapping_vacations = person.vacations.where('vacations.id != ?', id)
overlapping_vacations = overlapping_vacations.where('((start_at <= :start_at AND end_at >= :end_at) OR
(start_at <= :start_at AND end_at <= :end_at AND end_at >= :start_at) OR
(start_at >= :start_at AND start_at <= :end_at AND end_at >= :end_at))',
start_at: start_at, end_at: end_at)
if overlapping_vacations.exists?
errors.add(:base, 'You have a vacation during this period.')
end
end
end
I have a method on one of my models like the following:
def current?
self.parent.start_date <= Time.zone.now.to_date && self.parent.end_date >= Time.zone.now.to_date && !self.archived
end
I'd like to create a simple filter in ActiveAdmin based on the result of this method (a simple select filter called Current with options Yes, No, and Any) but I can't seem to figure out how.
What's the best approach for filtering on a model method rather than one of its attributes?
The following class method in the ActiveAdmin.rb would return all the records according to the conditions e.g. ActiveAdmin.current("Yes") or ActiveAdmin.current("No").
def self.current(inp = "Yes") # default inp is "Yes"
d = Time.zone.now.to_date
return case inp
# when inp == "Yes" it would return all the records with archived == false (won't return archived == nil)
# AND parents with start_date <= d and end_date >= d
when "Yes"
where(archived: false ).
joins(:parent).
where("parents.start_date <= ? AND parents.end_date >= ?",d,d)
# when inp == "No" it would return all the records with archived == true (won't return archived == nil)
# AND parents with start_date > d OR end_date < d
when "No"
where(archived: true ).
joins(:parent).
where("parents.start_date > ? OR parents.end_date < ?",d,d)
# when inp = "Any" return all records
when "Any"
scoped
# return all records if inp does not match any of the above options
else
scoped
end
end
var postsidebar = from post in postrepository.GetAllPosts()
join pstmt in postrepository.GetAllPostMetas()
on post.int_PostId equals pstmt.int_PostId
where (post.int_PostTypeId == 4
&& post.int_PostStatusId == 2
&& post.int_OrganizationId == layoutrep.GetSidebarDetailById(SidebarDetailsId).int_OrganizationId)
&& (pstmt.vcr_MetaKey.Contains(filter) && pstmt.vcr_MetaValue.Contains("true")
&& (System.DateTime.Now >=
Convert.ToDateTime(pstmt.Post.PostMetas.FirstOrDefault(m =>
m.vcr_MetaKey == "Publish Date").vcr_MetaValue)))
select post;
how can i check for empty in this part in Date(it is giving error)
&& (System.DateTime.Now >= Convert.ToDateTime(pstmt.Post.PostMetas.FirstOrDefault(m =>
m.vcr_MetaKey == "Publish Date").vcr_MetaValue)))
You could try eliminated the possibility of an empty value first and then try your cast afterward.
&& pstmt.Post.PostMetas.FirstOrDefault(m =>
m.vcr_MetaKey == "Publish Date"
&& !string.IsNullOrEmpty(m.vcr_MetaValue))
&& (System.DateTime.Now >=
Convert.ToDateTime(pstmt.Post.PostMetas.FirstOrDefault(m =>
m.vcr_MetaKey == "Publish Date").vcr_MetaValue)))
Try this:
// declare the action for re-use
Func<PostMeta,bool> action = m => m.vcr_MetaKey == "Publish Date";
// then test for Any() before comparing anything
&& (pstmt.Post.PostMetas.Any(action) && System.DateTime.Now >= Convert.ToDateTime(pstmt.Post.PostMetas.First(action).vcr_MetaValue)))
I asked a question earlier which elicited some great responses.
Here's the earlier question
On the back of some advice given there, I've tried moving the following controller logic
if params[:concept][:consulted_legal] == 0 && params[:concept][:consulted_marketing] == 1
#concept.attributes = {:status => 'Awaiting Compliance Approval'}
elsif params[:concept][:consulted_marketing] == 0 && params[:concept][:consulted_legal] == 1
#concept.attributes = {:status => 'Awaiting Marketing Approval'}
elsif params[:concept][:consulted_marketing] == 0 && params[:concept][:consulted_legal] == 0
#concept.attributes = {:status => 'Awaiting Marketing & Legal Approval'}
else
#concept.attributes = {:status => 'Pending Approval'}
end
into the model, as so:
def set_status
if status.blank?
if (consulted_legal == true) && (consulted_marketing == true)
status = "Pending Approval"
elsif (consulted_legal == true) && (consulted_marketing == false)
status = "Awaiting Marketing Approval"
elsif (consulted_legal == false) && (consulted_marketing == true)
status = "Awaiting Legal Approval"
elsif (consulted_legal == false) && (consulted_marketing == false)
status = "Awaiting Marketing & Legal Approval"
end
end
true # Needs to return true for the update to go through
end
I am calling that from a before_save callback.
As a default, both the consulted_legal and consulted_marketing attributes are set to false and not null, which is why I am testing for == false or true here, instead of asking
if consulted_legal?
for instance.
However, this logic doesn't seem to be working. If I inspect the object, status is not being set to anything, ever. Can anyone spot why this might be happening? Have I got how attributes are accessed wrong in models, for instance?
TIA
Instead of status = try self.status =. I've found that I needed to use self. to change a model's attribute within the model.
It's also much better to have errors.empty? at the end instead of true, so if you ever use errors.add_to_base in the future, your set_status method is ready to abort a save.
Edit:
You may also want to check out acts_as_state_machine. It looks like a plugin for exactly what you're doing.
Are you setting the parameters from user input?
If they're not defined as boolean database columns, then you'll be assigning a string to them, which will never be equal to true.