How to get result of multiples queries in a scope? - ruby-on-rails

I have a dummy_names table which contains random first_names and last_names. In the db, where there is a first_name for an entry, the last_name is NULL and vice versa.
I'm trying to write a scope that returns a random name (a random first_name + a random last_name from that able).
What am I doing wrong here...?
scope :random_name, lambda {
fname = self.where('first_name IS NOT NULL').first
lname = self.where('last_name IS NOT NULL').first
fname.first_name.to_s + " " + lname.last_name.to_s
}

here we go
#in your initializer
module ActiveRecord
class Base
def self.random
if (c = count) != 0
find(:first, :offset =>rand(c))
end
end
end
end
#in your model
def self.random_name
"#{self.where('first_name IS NOT NULL').random.first_name} #{self.where('last_name IS NOT NULL').random.last_name}"
end

Related

Rails association scope by method with aggregate

I'm trying to retrieve association records that are dependent on their association records' attributes. Below are the (abridged) models.
class Holding
belongs_to :user
has_many :transactions
def amount
transactions.reduce(0) { |m, t| t.buy? ? m + t.amount : m - t.amount }
end
class << self
def without_empty
includes(:transactions).select { |h| h.amount.positive? }
end
end
class Transaction
belongs_to :holding
attributes :action, :amount
def buy?
action == ACTION_BUY
end
end
The problem is my without_empty method returns an array, which prevents me from using my pagination.
Is there a way to rewrite Holding#amount and Holding#without_empty to function more efficiently with ActiveRecord/SQL?
Here's what I ended up using:
def amount
transactions.sum("CASE WHEN action = '#{Transaction::ACTION_BUY}' THEN amount ELSE (amount * -1) END")END")
end
def without_empty
joins(:transactions).group(:id).having("SUM(CASE WHEN transactions.action = '#{Transaction::ACTION_BUY}' THEN transactions.amount ELSE (transactions.amount * -1) END) > 0")
end

Adding a bitwise virtual column to a model

I'm building this RoR site on an existing database. The user model on database has a column called "secret", which is a bitwise integer that holds information of the columns user has set as secret (first name, last name, etc).
Variables are to the power of two, for example: last name = 1<<1 = 2, first name = 1<<2 = 4, email == 1<<3 = 8, etc. So if user has set first name & email as secret, the column value becomes 4+8 = 12.
Now, I'm trying to find a generalized way to implement these virtual columns into a Rails model. So that, I could do (just a dummy example, the point being, i want to retrieve & store the status):
if user.secret_email?
user.secret_name_last = true
user.secret_name_first = false
end
How to implement these virtual columns neatly to a model (without modifying the existing database)? Current I've got following. It works, but it's not neat. As I've got 20 secret columns, the code looks very ugly.
SECRET_NAME_LAST = (1 << 1) # 2
attr_accessible :secret_name_last
def secret_name_last; secret & SECRET_NAME_LAST > 0 unless secret.nil?; end
def secret_name_last=(value); secret_set_value(SECRET_NAME_LAST, value); end
SECRET_NAME_FIRST = (1 << 2) # 4
attr_accessible :secret_name_first
def secret_name_first; secret & SECRET_NAME_FIRST > 0 unless secret.nil?; end
def secret_name_first=(value); secret_set_value(SECRET_NAME_FIRST, value); end
SECRET_EMAIL = (1 << 3) # 8
attr_accessible :secret_email
def secret_email; secret & SECRET_EMAIL > 0 unless secret.nil?; end
def secret_email=(value); secret_set_value(SECRET_EMAIL, value); end
***snip (17 more)***
private
def secret_set_value(item, value)
if self.secret.nil?
self.secret = 0
end
if value == "1" || value == true || value == 1
# Add item to secret column (if it doesn't exist)
if self.secret & item == 0
self.secret += item
end
else
# Remove item from secret column (if it exists)
if self.secret & item > 0
self.secret -= item
end
end
end
It would be great of I could just do something like:
as_bitwise :secret_name_first, :column=>'secret', :value=>4
as_bitwise :secret_name_last, :column=>'secret', :value=>2
Or even,
as_bitwise :secret, { :secret_name_last=>4, :secret_name_first=>2 }
EDIT
Based on Brandan's excellent answer, this is what I've got currently:
module BitwiseColumn
extend ActiveSupport::Concern
module ClassMethods
def bitwise_column(*args)
mapping = args.extract_options!
column_name = args.shift
real_column_name = args.shift
logger.debug "Initializing bitwisecolumn, column: " + column_name.to_s
mapping.each_pair do |attribute, offset|
logger.debug "\tSetting a pair: offset: " + offset.to_s + ", " + attribute.to_s
mask = 2 ** offset
class_eval %{
attr_accessible :#{column_name}_#{attribute}
def #{column_name}_#{attribute}?
#{real_column_name} & #{mask} > 0 unless #{real_column_name}.nil?
end
def #{column_name}_#{attribute}=(value)
if self.#{real_column_name}.nil?
self.#{real_column_name} = 0
end
if value == "1" || value == true || value == 1
if self.#{real_column_name} & #{mask} == 0
self.#{real_column_name} += #{mask}
end
else
if self.#{real_column_name} & #{mask} > 0
self.#{real_column_name} -= #{mask}
end
end
end
}
end
end
end
end
This allows me to use:
bitwise_column :secret, :realsecretcolumnatdatabase, :name_last=>1, :name_first=>2, :email=>3, :picture=>5, :dob=>6, :place=>12
After that, I can call User.first.secret_name_last? etc.
You can use class_eval to DRY up your code quite a bit. I'd also suggest factoring this behavior into some kind of a module separate from your User class so that you can test it thoroughly and separately from other User-specific behavior.
Like you, I tend to start these kinds of tasks with the desired API and work backwards. I started with this in my model:
class User < ActiveRecord::Base
include BitwiseColumn
bitwise_column :secret, :first_name => 1, :last_name => 2
end
The hash passed to bitwise_column maps the virtual attribute names to their mask value as an exponent. I felt like that was easier to manage than having to remember the powers of 2 myself :-)
Then I created the mixin:
module BitwiseColumn
extend ActiveSupport::Concern
module ClassMethods
def bitwise_column(*args)
mapping = args.extract_options!
column_name = args.shift
mapping.each_pair do |attribute, offset|
mask = 2 ** offset
class_eval %{
def secret_#{attribute}?
#{column_name} & #{mask} > 0 unless #{column_name}.nil?
end
def secret_#{attribute}=(value)
if self.#{column_name}.nil?
self.#{column_name} = 0
end
if value == "1" || value == true || value == 1
if self.#{column_name} & #{mask} == 0
self.#{column_name} += #{mask}
end
else
if self.#{column_name} & #{mask} > 0
self.#{column_name} -= #{mask}
end
end
end
}
end
end
end
end
This mixin creates two instance methods for each virtual attribute, one with a ? and one with a =, since that seems to be what you're after. I used your existing logic for the bitwise operations, which seems to work perfectly.

Creating sqlite dbs a la rails way, without execute()

I have a controller like this:
def download_link
#It starts a background process to handle all these things
temp_file = Tempfile.new 'temp_file'
temp_sqlite_db = SQLite3::Database.new temp_file.path
temp_sqlite_db.execute("CREATE TABLE inspection (id INTEGER NOT NULL,desc VARCHAR(255));")
inspections = Inspection.a_heavy_query_that_doesnt_worths_to_wait_so_much_for_a_reply
# Some code inserting records and creating tables, with execute() too
# more code, compressing the db and sending an email with a download link to the zip file
end
Now, I would like to know if there's a way to replace the execute() function and maybe create the tables and save records like inspection.create(something) . Thanks in advance
If anyone needs something similar, this was my implementation:
# config/initializers/sql_returner.rb
module ActiveRecord
class Base
def sql_insert
if attributes_with_quotes.empty?
connection.empty_insert_statement(self.class.table_name)
else
"INSERT INTO #{self.class.quoted_table_name} " +
"(#{quoted_column_names.join(', ')}) " +
"VALUES(#{attributes_with_quotes.values.join(', ')});"
end
end
def self.sql_create
"CREATE TABLE #{table_name} (" +
" #{ self.columns.collect{ |column|
column_sql = " #{ column.name } #{ sql_type column } "
column_sql << " PRIMARY KEY " if column.primary
column_sql << " NOT NULL " unless column.null
column_sql
}.join(', ') } );"
end
private
def self.sql_type column
case column.type
when 'datetime', 'string'
'TEXT'
else
column.type.to_s
end
end
end
end
Then, if I need to create tables and insert records, taking the same code of the question as example, I must to run:
def download_link
temp_file = Tempfile.new 'temp_file'
temp_sqlite_db = SQLite3::Database.new temp_file.path
temp_sqlite_db.execute(Inspection.sql_create)
inspections = Inspection.a_heavy_query_that_doesnt_worths_to_wait_so_much_for_a_reply
insert = ""
inspections.each{ |insp|
insert << insp.return_insert_sql
}
#.....
end
For the first method sql_insert I took as example the create method code of ActiveRecord. I know that maybe some kittens died coding this implementation, but at least for me it works.

Get multiple records with one query

User table:
name lastname
Bob Presley
Jamie Cox
Lucy Bush
Roman Cox
Find users
q = Query.new("Bob Presley, Cox, Lucy")
q.find_users => {0=>{:name=>"Bob", :lastname=>"Presley"}, 1=>{:lastname=>"Cox"}, 2=>{:name=>"Lucy"}}
Question:
I've got hash with few names and lastnames. I need to build Activerecord query to fetch all users from that hash.
If i have name and lastname I should find user with exactly the same name and lastname.
If I have only lastname or name I should find all users with this name or lastname. So when i search for :lastname => Cox it should return two users [Roman Cox,Jamie Cox]
I can do
object = []
hash = q.find_users
hash.each do |data|
#Pseudocode
# object << User.where(:name => if data[:lastname] exist, :lastname => if data[:name] exist)
end
But I think it is higly inefficient. How should I do this ?
Environment
rails: 3.0.3
ruby: 1.9.2-head
gem: meta_search https://github.com/ernie/meta_search
I'm sure this can be refactored nicely (hint!), but this code below will construct a SQL which can be used in a sub-select.
Code below does not sanitize the input values.
Note that you should sanitize the values in the h hash!
h = {0=>{:name=>"Bob", :lastname=>"Presley"}, 1=>{:lastname=>"Cox"}, 2=>{:name=>"Lucy"}}
conditions = ""
h.each_pair do |k,v|
if not conditions.empty?
conditions += " or "
end
conditions += "("
a_condition = ""
v.each_pair do |a,b|
if not a_condition.empty?
a_condition += " and "
end
a_condition += "#{a.to_s} = '#{b}'"
end
conditions += a_condition
conditions += ")"
end
conditions = "("+conditions+")"
p conditions
# => "((name = 'Bob' and lastname = 'Presley') or (lastname = 'Cox') or (name = 'Lucy'))"
# use the generated SQL conditions to find the users
#users = User.find(:all, :conditions => "(#{conditions})")

Rails: can't convert ActiveRecord::Associations::BelongsToAssociation into String

This rails project is very bare-bones, just begun, so I haven't done any weird loopholes or patching.
The model, to_s replaces school with bar if nil:
class Department < ActiveRecord::Base
belongs_to :school
def to_s
"foo" + (school || "bar")
end
end
Says the view:
can't convert ActiveRecord::Associations::BelongsToAssociation into String
about the to_s statement
but in script/console, I can take a Department d where school==nil and say
"foo" + (d.school || "bar")
and get "foobar"
The problem is when school is not nil. It is not a string, so you can't add it to "foo". Here are some options to fix it:
"foo" + (school || "bar").to_s
"foo" + (school ? school.to_s : "bar")
"foo" + (school.try(:to_s) || "bar")
"foo#{school || 'bar'}"
Try self.school

Resources