Sorry in advance for this incredibly simple question, but what is the best way to set a variable while also checking a condition. For example, I have:
#friends = []
#user.facebook_friends.each do |key,value|
if test = Authorization.includes(:user).find_by_uid(key) != nil
#friends << {"name" => test.user.name, "facebook_image_url" => test.user.facebook_image_url}
end
end
I am trying to pull in the user records when I pull in the authorization record, so as to minimize my database queries. I know that I can't write
test = Authorization.includes(:user).find_by_uid(key) != nil
in order to set the test variable. What is the best way to write this code so that it is functional?
You just need parens:
(test = Authorization.includes(:user).find_by_uid(key)) != nil
Also here is a more rails way to do it:
unless (test = Authorization.includes(:user).find_by_uid(key)).nil?
#friends << {"name" => test.user.name, "facebook_image_url" => test.user.facebook_image_url}
end
Related
I'm pretty new to ruby & ruby on rails and I have a little question :
I want to set a boolean to true if the value of the entry is higher than X and another boolean to true if this value if lower than Y.
I don't really know where to do the code for this or what's the best way to do it.
To be clear, I have a form(made with a scaffold) where I ask a value and depending on this value one of the 2 boolean might be set to true.
Thanks for your help!
you can put this on your controller. I would assume that the form is on controller#create where you would have:
boolean = true if #model.value > x
boolean2 = true if #model.value < y
then save it on db with #model.save
Assuming those booleans are attributes of the same model you have value on, it seems you could do this in before_save callback:
class SomeModel < ActiveRecord::Base
# Your X & Y with some example values
X_VALUE = 5
Y_VALUE = 10
before_save do
self.boolean1 = self.value > X_VALUE
self.boolean2 = self.value < Y_VALUE
true # callback needs to return true, otherwise save will fail
end
end
With above implementation even if you update the value, the proper boolean values will change as well. I hope this helps, as it's made on some assumptions. If not let me know, we'll figure something out.
I'm coding up a search query where the user can search for Items by creator, title, or description, or any combination of the above.
So in my search controller logic I grab the params thusly:
creator = params['creator']
title = params['title']
description = params['description']
# todo: do some input validation here
results = nil
cr = User.roughly_named(creator).first
What I am doing now is:
q = []
q << "creator_id IS #{cr.id}" if cr
q << "title LIKE '%#{title}%'" if title != ''
q << "description LIKE '%#{description}%'" if title != ''
results = Item.where(q.join(' AND ')
but surely there is a better way. I am open to suggestions.
How about using scopes :
res = Item.scoped
res = res.where(["creator_id is ?", cr.id]) if cr
res = res.where(["title like ?", "%#{title}%"]) unless title.empty?
res = res.where(["description like ?", "%#{description}%"]) unless description.empty?
When doing Item.scoped, you basically do lazy loading. Iterating over res will actually execute the query. This is handy when chaining optional where clauses.
PS: prefer the ? syntax to prevent SQL injections.
You could simply achieve this with regular where clause
Or try dynamic finders? If that does not help either, you could use method_missing to create dynamic method call. It's explained elsewhere
I hit a situation today where a field in our legacy db that should never be empty... was empty.
I am using NHibernate 3.2 against this database and the queries that are affected are written in QueryOver.
My current query is this
return Session
.QueryOver<FacilityGroup>()
.Where(fg => fg.Owner.Id == Token.OwnerId &&
fg.UserName == Token.UserName)
.OrderBy(fg => fg.Code).Asc
.TransformUsing(Transformers.DistinctRootEntity);
I want it to be this:
return Session
.QueryOver<FacilityGroup>()
.Where(fg => fg.Owner.Id == Token.OwnerId &&
fg.UserName == Token.UserName &&
!string.IsNullOrEmpty(fg.Code))
.OrderBy(fg => fg.Code).Asc
.TransformUsing(Transformers.DistinctRootEntity);
When I try this I get an exception "Unrecognised method call: System.String:Boolean IsNullOrEmpty(System.String)"
So NHibernate can't translate string.IsNullOrEmpty. Fair enough. However when I try this
return Session
.QueryOver<FacilityGroup>()
.Where(fg => fg.Owner.Id == Token.OwnerId &&
fg.UserName == Token.UserName &&
!(fg.Code == null || fg.Code.Trim() == "" ))
.OrderBy(fg => fg.Code).Asc
.TransformUsing(Transformers.DistinctRootEntity);
I get an InvalidOperationException "variable 'fg' of type 'Domain.Entities.FacilityGroup' referenced from scope '', but it is not defined"
Any thoughts?
Ok... I guess I asked this question too soon. I figured out a way around this.
What I was able to do was invoke the 'trim' function from hql via a SQL Function Projection. I ended up writing it as IQueryOver Extention method to keep it flexible. I will post it here in case anyone needs it.
public static class QueriesExtentions
{
public static IQueryOver<E, F> WhereStringIsNotNullOrEmpty<E, F>(this IQueryOver<E, F> query, Expression<Func<E, object>> propExpression)
{
var prop = Projections.Property(propExpression);
var criteria = Restrictions.Or(Restrictions.IsNull(prop), Restrictions.Eq(Projections.SqlFunction("trim", NHibernateUtil.String, prop), ""));
return query.Where(Restrictions.Not(criteria));
}
}
and here it is in use
return Session
.QueryOver<FacilityGroup>()
.Where(fg => fg.Owner.Id == Token.OwnerId && fg.UserName == Token.UserName )
.WhereStringIsNotNullOrEmpty(fg => fg.Code)
.OrderBy(fg => fg.Code).Asc
.TransformUsing(Transformers.DistinctRootEntity);
I have the following method to check if a user has admin access.
def has_admin_access?(user)
user.present? && gym_users.where(:role_id => [3, 4]).where(['user_id = ? AND date_ended IS NULL', user.id]).any?
end
The problem comes when I want to call this multiple times on a page. How can I do this so that I set a private value and only make the database call the first time?
You can just store the result in a hash, and if you look up the same user again return the result from the hash. Like this:
def has_admin_access?(user)
#admin_hash ||= {}
if (!#admin_hash.include?(user))
#admin_hash[user] = user.present? && gym_users.where(:role_id => [3, 4]).where(['user_id = ? AND date_ended IS NULL', user.id]).any?
end
#admin_hash[user]
end
Try:
extend ActiveSupport::Memoizable
memoize :has_admin_access?
more information
Trying to construct a query such that I have multiple statement specifying joins, each with a where message chained onto them. When the query is run, I get all the joins, but only the where from my first call. Here's the method body that's doing the query:
observations_joins = Observation.joins(:obs_session => :project).where(:obs_sessions=>{:project_id=>self.project.id})
descriptor_hash = descriptor_where_hash if tag_descriptors && tag_descriptors.size > 0
puts "The descriptor_hash: #{descriptor_hash}"
observations = observations_joins.joins(:obs_descriptors).where("#{descriptor_hash['query_string']}", descriptor_hash['match_values']) if tag_descriptors && tag_descriptors.size > 0
arel = observations.arel
puts "The arel sql should be: #{arel.to_sql}"
observations
I have another method that gets called from inside the second joins statement, that iterates over the potential match values and generates the string and the values used; body here:
match_values = []
query_string = "obs_descriptors.tag_name = ?"
tag_descriptors.each_index do |index|
query_string = query_string + " #{tag_descriptors.fetch(index).qualifier_key} obs_descriptors.tag_name = ?" if index != 0
match_values << tag_descriptors.fetch(index).tag_name
end
{:match_values=>match_values, :query_string=>query_string}
So the sql getting generated looks like:
SELECT `observations`.* FROM `observations` INNER JOIN `obs_sessions` ON `obs_sessions`.`id` = `observations`.`obs_session_id` INNER JOIN `projects` ON `projects`.`id` = `obs_sessions`.`project_id` INNER JOIN `obs_descriptors` ON `obs_descriptors`.`observation_id` = `observations`.`id` WHERE (`obs_sessions`.`project_id` = 1)
and doesn't include the second set of where conditions. I also print the hash, just to make sure I'm not losing my mind and there are values in there, and there indeed are.
So, what am I missing to make this go as I'd expect it to?
Answering my own question here. The most elegant, concise way I found to get this working was to drop down to arel directly. Also, there were some issues with the original code posted, but even still, I needed to use arel to get properly grouped conditions. For context, I've got an object that, based on it's related data, needs to dynamically construct a semi advanced query, so I wanted to do things like checking for the existence of certain related data, and if present, then tack on the additional joins and wheres. Here's the final versions of the relevant methods:
def find_observations
observations = Observation.select('distinct observations.*').includes(:obs_session).includes(:judgements).includes(:concepts).includes(:obs_descriptors)
observations = observations.joins(:obs_session => :project).where(:obs_sessions=>{:project_id=>self.project.id})
if tag_descriptors && tag_descriptors.size > 0
observations = observations.where(descriptor_predicate)
end
if session_descriptors && session_descriptors.size > 0
observations = observations.where(session_predicate)
end
if user_descriptors && user_descriptors.size > 0
observations = observations.where(user_predicate)
end
#puts "observations sql is: #{observations.to_sql}"
observations.all
end
The above method optionally calls the remaining methods, which return the arel used in the where calls when chaining the AR object while building up the eventual query. Notice the disctinct; I'd had a version of this using arel entirely, that appeared to be working, but was in fact returning duplicates. I found references to using group(some_attribute) to fake things, but that turned out to cause problems down the chain, so to speak. So I fell back to using ActiveRelation to specify the distinct, joins and includes, and arel for the rest.
The next one was the part that was originally giving me lots of trouble; there are a variable number of possibilities, and each one could be either an AND or OR condition, and needed to be grouped separately so as not to mess up the rest of the generated where clause.
def descriptor_predicate
od = Arel::Table.new :obs_descriptors
predicate = nil
self.tag_descriptors.each_index do |index|
descriptor = self.tag_descriptors.fetch(index)
qual_key = descriptor.qualifier_key
tag_name = descriptor.tag_name
if index == 0
predicate = od[:descriptor].eq(tag_name)
else
if qual_key == "OR"
predicate = predicate.or(od[:descriptor].eq(tag_name))
else
predicate = predicate.and(od[:descriptor].eq(tag_name))
end
end
end
predicate
end
And finally the other predicate methods for the potential joined entity values:
def session_predicate
o = Arel::Table.new :observations
predicate = nil
self.session_descriptors.each_index do |index|
obs = self.session_descriptors.fetch(index)
if index == 0
predicate = o[:obs_session_id].eq(obs.entity_id)
else
predicate = predicate.or(o[:obs_session_id].eq(obs.entity_id))
end
end
predicate
end
def user_predicate
o = Arel::Table.new :observations
predicate = nil
self.user_descriptors.each_index do |index|
obs = self.user_descriptors.fetch(index)
if index == 0
predicate = o[:contributor_id].eq(obs.entity_id)
else
predicate = predicate.or(o[:contributor_id].eq(obs.entity_id))
end
end
predicate
end
def descriptor_where_string(included_where_statements)
tag_descriptors.each_index do |index|
qual_key = tag_descriptors.fetch(index).qualifier_key
tag_name = tag_descriptors.fetch(index).tag_name
if index == 0
query_string = "obs_descriptors.descriptor = #{tag_name}"
else
if qual_key == "OR"
query_string = query_string + " #{qual_key} obs_descriptors.descriptor = #{tag_name} AND #{included_where_statements} "
else
query_string = query_string + " #{qual_key} obs_descriptors.descriptor = ?"
end
end
end
query_string
end
Ultimately, I found the best solution involved leveraging both ActiveRelation chaining for providing the distinct and includes, and using arel directly for the conditions on the related values. Hope this helps somebody at some point.