convert named_scope for rails 3 - ruby-on-rails

I have the following code wich throws an error with Rails 3 :
module Auditions
module Search
def self.included(base)
base.extend ClassMethods
...
base.named_scope :not_rated_by, lambda { |user_id| {
:conditions => ['NOT EXISTS (SELECT * FROM audition_tags bar WHERE bar.user_id = ? AND bar.audition_id = auditions.id)', user_id]
}}
...
end
end
end
When called the following error appears :
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '2053809816 AND audition_tags.audition_id = auditions.id)) ORDER BY auditions.cr' at line 1: SELECT `auditions`.* FROM `auditions` WHERE (NOT EXISTS (SELECT * FROM audition_tags WHERE audition_tags.user_id = '#<ActiveRecord::Relation:0x104ee3c20>',2053809816 AND audition_tags.audition_id = auditions.id)) ORDER BY auditions.created_at DESC LIMIT 0, 10
As you can see it's like user_id is now a table containing an ActiveRelation object and well ... my user_id
I don't understand what's going on ... do you have an idea ?
Thanks
update : here's the complete SQL output :
SELECT `auditions`.* FROM `auditions` WHERE (NOT EXISTS (SELECT * FROM audition_tags as bar WHERE bar.user_id = '#<ActiveRecord::Relation:0x104aa6c48>',2053809816 AND bar.audition_id = auditions.id)) ORDER BY auditions.created_at DESC LIMIT 0, 10
update2 : here's the code calling my scope https://gist.github.com/ddddfeddf70264a81289
update3 : as you can see in my gist the chained scopes are called with a "call" :
scopes.inject(self) {|m,v| m.scopes[v[0]].call(m, v[1]) }
If I log the user_id sent it's a FixNum not an Array :
scopes.inject(self) do |m,v|
logger.info ">>> SCOPE : #{v[0].inspect} | ARG : #{v[1].inspect} | ARG CLASS : #{v[1].class.inspect} <<<"
m.scopes[v[0]].call(m, v[1])
end
So the problem is more precise now : I send a Fixnum as argument to my named_scope but got on output an Array of type : [ActiveRecord::Relation, FixNum]
update4 : I finally find the solution (but not sure why ???)
I replaced "call" by "send" and it works ...
results = scopes.inject(self) do |combined_scope, scope|
combined_scope.send(scope[0], scope[1])
end
Thanks all for your help !

Try this:
scope :not_rated_by, lambda { |user_id| {
where('NOT EXISTS (SELECT * FROM audition_tags bar WHERE bar.user_id = ? AND bar.audition_id = auditions.id)', user_id)
}
And by the way, could it be that there is a comma missing here, something should go bewtween audition_tags and bar?
FROM audition_tags bar
And also based on your error I think you are passing in a user object - not a ID, remember in Rails 3 everything is a ActiveRecord Relation so you have to call first or all on the relation first to get a result.
Example:
user = User.where('conditions').first
Auditions.not_rated_by(user.id)

Related

What is the result of this query written in ruby in rails helper?

I am new in rails and I have started working on a project which has a model named 'Study', and here I am unable to know the result of this query written in rails helper.
def available_list_of(entities, exclude: nil)
case entities
when :studies then Study.where('id NOT IN (?)', exclude.nil? ? [-1] : exclude.pluck(:study_id) + [-1]).list
end
end
Its some pretty dense that code that can be expanded to:
def available_list_of(entities, exclude: nil)
# why use a case statement if there is only one option?
if entities == :studies
if exclude.nil?
Study.where('id NOT IN (?)', '-1')
else
Study.where('id NOT IN (?)', (exclude.pluck(:study_id) + [-1]))
end
end
end
What the actual purpose of the code is is beyond me though as it could simply be done with a scope:
class Study < ApplicationRecord
def self.excluding(excluded)
excluded.nil? ? self : self.where.not(id: excluded)
end
end
And also why would your typical auto incrementing id column ever contain -1?
When your expression works you can use the to_sql method, check the documentation here this method is from ActiveRecord::Relation
As an example:
Incident.where(reference: "PATAPAM").to_sql
=> "SELECT `incidents`.* FROM `incidents` WHERE `incidents`.`reference` = 'PATAPAM'"
I have myself found its solution.
It says that get all the records from Study model, where id is not equal to -1 if 'exclude' is 'nil' else get the 'study_id' from the exclude model and append -1 with study_id. And finally list function is a model function which will add name and id into the list

dynamic query for different values rails 4 activerecord

Here is the query I am trying in my controller
query = []
if id
query = "category_id: #{id}"
end
#posts = Post.where(query)
But throwing error as ERROR: syntax error at or near ":"
Why this is not working any other way to do it
if id
query << {sub_category_id: id}
end
if test
query << {test_id: test}
end
#posts = Post.where(query)
Is there any way of doing like this
Change query to a hash instead of string:
if id
query = { category_id: id }
end
#posts = Post.where(query)
The reason query = "category_id: #{id}" did not work is because the supplied string is literally used in the query generated by ActiveRecord, i.e. your select query will have category_id: 1 (assuming id is 1) in the where clause. And this is not a valid SQL syntax.
Please read on how you can use strings in conditions following this link. Thanks to #RustyToms for suggesting the link.
Update: ( Add extra conditions to the query hash )
if id
query[:sub_category_id] = id
end
if test
query[:test_id] = test
end
#posts = Post.where(query)
Another way to do this:
#posts = Post.scoped
#posts = #posts.where(category_id: id) if id
(in case you're playing codegolf)
Edit: (this is definitely a side note that isn't at all relevant)
Your original solution relies on one of my least favorite features of Ruby. Consider the following code:
if false
a = 4
end
puts a
I would expect the puts a to fail with a NameError (undefined local variable "a"), but no! The Ruby parser hits a = and then initalizes its value to nil. So, despite the fact that there is no way for the innards of that if statement to run, it still impacts the other code.

Need to convert a Boolean from Postgres (== String) to a Ruby Boolean

I'm using Postgres with Rails. There's a query with a subselect which returns a boolean, but Postgres always returns a String like 't' or 'f'. But in the generated JSON I need a real boolean.
This is my query:
SELECT
*,
EXISTS (
SELECT TRUE
FROM measurement
JOIN experiment ON measurement.experiment_id = experiment.id
JOIN location ON experiment.location_id = location.id
WHERE location.map_id = map.id
LIMIT 1
) AS measurements_exist
FROM "map"
It doesn't matter whether I use THEN true or THEN 1 or THEN 'true', I will always get a string. So my JSON response will always look like that:
[
{"id":8, ..., "measurements_exist":"f"},
{"id":9, ..., "measurements_exist":"t"}
]
But it should(!) look like that:
[
{"id":8, ..., "measurements_exist":false},
{"id":9, ..., "measurements_exist":true}
]
Is there any way to get this working right?
Thank you!
THE SOLUTION:
Just give the corresponding model (here: Map) an attribute accessor, which uses value_as_boolean to convert the value. So every time the controller tries to access the value, it uses the attribute accessor method automatically.
The controller code:
class MapsController < ApplicationController
def index
select = ["*"]
select.push(measurements_exist) # This will just insert the string returned by the 'measurements_exist' method
maps = Map.select(select) # Results in 'SELECT *, EXISTS (...) AS measurements_exist FROM "map"'
render json: maps
end
private
def measurements_exist
"EXISTS (
SELECT TRUE
FROM measurement
JOIN experiment ON measurement.experiment_id = experiment.id
JOIN location ON experiment.location_id = location.id
WHERE location.map_id = map.id
LIMIT 1
) AS measurements_exist"
end
end
The model code:
class Map < ActiveRecord::Base
def measurements_exist
ActiveRecord::ConnectionAdapters::Column.value_to_boolean(self[:measurements_exist])
end
end
Resulting JSON:
[
{"id":7, ..., "measurements_exist":false},
{"id":6, ..., "measurements_exist":true}
]
ActiveRecord has a method called ActiveRecord::ConnectionAdapters::Column.value_to_boolean it uses internally to convert any true-like value to a Ruby true value.
You can use it in your code.
When you query model from Rails its boolean fields converted to true/false automatically because DB adapter can determine type of field from schema. If you select custom boolean field from db - adapter doesn't know anything about it, so it returned string 't' or 'f' and you need to convert it manually.
One of the ways to get expected boolean value:
Create the view with provided SQL-query on the DBMS side (e.g. see CREATE VIEW ... statement for PostgreSQL). Views fields have types so boolean fields will be converted in your app automatically. Suppose its called map_with_measurements.
Create model MapWithMeasurement and place it in models/map_with_measurement.rb.
class MapWithMeasurement < ActiveRecord::Base; end
Use MapWithMeasurement.find_all.
You can use wannabe_bool gem.
https://github.com/prodis/wannabe_bool
This gem implements a #to_b method for String, Integer, Symbol and NilClass classes.
class Map < ActiveRecord::Base
def measurements_exist
self[:measurements_exist].to_b
end
end
Here is another solution:
boolean = (value_from_postgres =~ /^t$/i) == 0
converts a value_from_postgres of 't' to boolean true or 'f' to boolean false
$irb
2.2.1 :001 > value_from_postgres = 't'
=> "t"
2.2.1 :002 > (value_from_postgres =~ /^t$/i) == 0
=> true
2.2.1 :003 > value_from_postgres = 'f'
=> "f"
2.2.1 :004 > (value_from_postgres =~ /^t$/i) == 0
=> false
The regular expresion in this line could be modified to match /^true$/i if you are expecting a string "true" or "false". This is more flexible than using a ternary or a gem, because you can write it to convert a match to any regex to a boolean true.
Using a ternary it looks like:
boolean = value_from_postgres.eql?('t') ? true : false

NoMethodError When Using Database Column Aliases in Rails

Undefined method errors when I try to use a column alias for an aggregate (PostgreSQL)
Inside my model:
class B2bLoginAttempt < ActiveRecord::Base
set_table_name "b2b_logins"
end
Inside my controller:
#client_ip = request.env['REMOTE_ADDR']
#sql = "select count(id) as failed_logins FROM b2b_logins WHERE ip_address = '"+#client_ip+"'"
f = B2bLoginAttempt.find_by_sql(#sql)
failed_attempts = f.failed_logins.to_s
f.destroy
Then I see: undefined method `failed_logins' for #<Array:0x104d08478>
The error occurs because find_by_sql returns an array, so you need to write f.first.failed_logins.to_s instead of f.failed_logins.to_s.
Check find_by_sql doc here.
Not sure I am completely following your logic correctly, but it seems like you may be better off with this:
f = B2bLoginAttempt.where("ip_address = ?", #client_ip)
f.map(&:destroy)
You can get the actual count with f.count
Did I miss something?

how to write a ActiveRecord query in the controller using a db column

I am doing the following:
#group_coach = GroupCoach.find("groups_count < '9'" )
I have a groups_count column in my db that is being updated by a counter_cache => true method in the Group model.
I know this isn't right. Because the error it spits out: Couldn't find GroupCoach with ID=groups_count < '9'
I have reviewed the Rails Guides:
client = Client.find(10)
Client.where("orders_count = '2'")
The second option does run in the localhost but isn't actually returning a GroupCoach... It just returns groupcoach...
What is the proper syntax for this?
Have you tried:
#group_coach = GroupCoach.where("groups_count < 9")
To clarify: #group_coach will be a collection of records. So you can't call #group_coach.name because all that will do is give you "GroupCoach" (the name of the class). Instead, you would need to iterate over the items:
#group_coach.each do |group_coach|
puts group_coach.name
end

Resources