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
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
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.
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.
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})")
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