I'm working on a rails app where the associations between data change with time. I've created a model for the associations:
create_table :accounts_numbers do |t|
t.integer :number_id
t.integer :account_id
t.date :start_date
t.date :end_date
And, so far, I have a simple model
class Account < ActiveRecord::Base
has_and_belongs_to_many :numbers, :through => accounts_numbers
end
But instead of
#account.numbers
I need something like
#account.numbers_at(Date.new(2010,2,3))
I thought I could use :conditions, but I wouldn't haven't seen a way to tell has_and_belongs_to_many to create a parameterized field. I've also looked into named_scope, but that only seems to return accounts, not numbers.
More importantly, this pattern is going to cover many relationships in my code, so would there be a way to coin a time_dependent_has_and_belongs_to_many for use all over?
After much more searching, I finally found out what to do; In the /lib dir of my project, I created a module, TimeDependent:
module TimeDependent
def at(date)
find(:all, :conditions=>["start_date <= ? AND ? < end_date"], date, date)
end
end
So, my model becomes
require "TimeDependent"
class Account < ActiveRecord::Base
has_many :accounts_numbers
has_many :numbers, :through => :accounts_numbers, :extend => TimeDependent
end
Which allows me to do exactly what I want:
#numbers = #account.numbers.at(Date.new(2010,2,3));
Couldn't this be done by writing a function for Object?
class Object
def numbers_at(time)
start = time.to_date
end = time.advance(:days => 1).to_date
AccountNumber.join(:accounts, :numbers).where("time BETWEEN(?, ?)", start, end)
end
end
Related
I am looking to create a table called ignorables with ignorable_type and ignorable_id where I can store IDs of different types of objects I'd like to ignore in the UI.
How can I leverage ActiveRecord's polymorphic associations to achieve this?
I have to say that I don't 100% get your question but this would be my take
1) you would need to create the table. I am sure you know how to do this, but this is how
rails g migration CreateIgnores
#db/migrate/20180106072916_create_ignorables.rb
class CreateIgnors < ActiveRecord::Migration[5.0]
def change
create_table :ignors do |t|
t.integer :ignorable_id
t.string :ignorable_type
...
end
add_index :ignors, [:ignorable_type, :ignorable_id]
end
end
2) now you can create your models
class Ignore < ApplicationRecord
belongs_to :ignorable, polymorphic: true
end
3) what belongs to the ignorable
class Other < ApplicationRecord
has_many :ignored, as: :ignorable
end
4) now last but not list you want to do do something
class Other < ApplicationRecord
has_many :ignored, as: :ignorable
validate :belongs_to_ignored
...
private
def belongs_to_ignored
if ignored
false
end
end
end
I hope that this can lead you in the right direction
i'm trying to learn ruby and trying to work with business rules and console through active record methods.
Here's the problem i'm facing right now, assume the following scenario:
I have 3 models:
Class User < ApplicationRecord
has_many :animals
Class Animal < ApplicationRecord
belongs_to :user
belongs_to :tipo
Class Tipo < ApplicationRecord
has_many :animals
respective migration:
User
t.string :name
t.string :doc
t.date :birth_day
Animal
t.string :name
t.decimal :mensal_cost
add_index :animals, :user_id
add_index :animals, :user_id
Tipo
t.string :tipo_animal
I want to make a validation which get the the user_id and sum the mensal_cost
of all his animals, so if this cost is higher than 1000 then he cant have more animals.
So i think i must to get the user id and sum his respectively animals.mensal_cost array
Ok now u're contextualized, ill set my code below.
PS. it's inside animal model:
#Want to get the total of a single user mensal cost
def total
user_id.each do |mensal_cost|
mensal_cost.inject(&:+)
end
#Now the custom validation itself
validate :check_mtotal
def check_mtotal
if user_id && total > 1000
errors.add(:user_id, message: 'cant add more animals')
end
end
Well, first problem is that my total isn't returning anything, so i really don't know how make it proceed to get the ammount of mensal_cost of a single user.
second... i need the first problem solve to test the second :(.
anyone can help with this?
Thank you very much for your attention
Well i figured out the solution and it's below:
User model
#Returns the total mensal cost
def max_mensal
animals.sum(:mensal_cost)
end
Animal model
#Validates the mensal ammount
validate :check_form_mammount
def check_for_mammount
if user_id && user.max_mensal > (value here)
errors.add(:mensal_cost, message: 'msg here')
end
I have two models:
Routes and Activity
I have a Many-To-Many relationship between them through a migration that looks like:
class ActivitiesRoutes < ActiveRecord::Migration
def up
create_table :activities_routes, :id => false do |t|
t.integer :route_id
t.integer :activity_id
end
end
end
In a rest service i get the data for a route and I get multiple activities, my models look like this:
class Route < ActiveRecord::Base
attr_accessible :activities_attributes
has_and_belongs_to_many :activities
accepts_nested_attributes_for :activities
end
and:
class Activity < ActiveRecord::Base
attr_accessible :activitytext, :iconid
has_and_belongs_to_many :routes
end
On my app controller I want to make something like :
ruta=Route.create({
#other data for the model
})
ruta.activities_attributes = #activitiesarray #Array made with the Activities received
But I get an error:
undefined method `activities_attributes' for #<Route:0x2bccf08>
If i left it like :
ruta.activities_attributes << #activitiesarray
I get:
undefined method `with_indifferent_access' for #<Activity:0x6af7400>
Does anyone know ho can I make that possible?
Thank you :)
You can't do this
ruta.activities_attributes << #activitiesarray
because accepts_nested_attributes_for only provides a *_attributes= method so the following should work
ruta.activities_attributes = #activitiesarray
I have three models that look something like this:
class Bucket < ActiveRecord::Base
has_many :entries
end
class Entry < ActiveRecord::Base
belongs_to :submission
belongs_to :bucket
end
class Submission < ActiveRecord::Base
has_many :entries
belongs_to :user
end
class User < ActiveRecord::Base
has_many :submissions
end
When I retrieve a collection of entries doing something like:
#entries = Entry.find(:all,
:conditions => ['entries.bucket_id = ?', #bucket],
:include => :submission)
The performance is pretty quick although I get a large number of extra queries because the view uses the Submission.user object. However, if I add the user to the :include statement, the performance becomes terrible and it takes over a minute to return a total of 50 entries and submissions spread across 5 users. When I run the associated SQL commands, they complete in well under a second - the SQL query performance is the same from each set of queries.
#entries = Entry.find(:all,
:conditions => ['entries.bucket_id = ?', #bucket],
:include => {:submission => :user})
Why would this second command have such terrible performance compared to the first?
it's because you have a double join in second statement. So the number of result is bigger.
More bigger the result is much slow it's.
I'm not sure if the performance will be much better, but try:
#bucket.entries.find(:all, :include => {:submission => :user})
I'm really only familiar with MySQL, but if your database supports them, indexes will help performance significantly. I usually do this in my migrations like:
def self.up
create_table :entries do |t|
t.references :submission
t.references :bucket
# other fields...
end
add_index :entries, :submission_id
add_index :entries, :bucket_id
end
This ended up being a problem with the serialization/deserialization of the user model in the entire object graph. By caching relevant data on the Entry and Submission models we were able to avoid the lookup to User and saved a considerable amount of time.
I'm currently writing some intranet web application where people could submit to admins requests for adding different resources. The example requests would be:
installing programs, in this case user will select which program he wants installed
increasing quota, in this case user will just enter the amount of disk space he needs or maybe he will select the predefined quantities - 1GB, 10GB etc...
create new email alias, in this case user will just type the alias.
...
I was thinking about having just one model UserRequests with the reference to the sender and
two optional attributes one would be reference_id that would refefrence to other tables (for
example the Program that he wants installed) and another would be used for free type fields
like email alias or quota.
So my problem is that based on the type of the request the model should contain either:
reference to other table
integer data
string data
Based on the type of the request the given action should be taken - probably email alias
could be added from rails but the application on users computer will be installed by hand.
Does anyone had similar problem? Do you think using polymorphism for this kind of stuff is a good idea? Do you have any suggestions on how to organize data in the tables?
Single Table Inheritance! This way you can have each type of request have custom validations, while still having every request live in the same table.
class CreateUserRequests < ActiveRecord::Migration
def self.up
create_table :user_requests do |t|
t.string :string_data, :type
t.integer :user_id, :integer_data
t.timestamps
end
end
def self.down
drop_table :user_requests
end
end
class UserRequest < ActiveRecord::Base
belongs_to :user
end
class EmailAliasRequest < UserRequest
validates_presence_of :string_data
validates_format_of :string_data, :with => EMAIL_REGEX
end
class ProgramInstallRequest < UserRequest
belongs_to :program, :class_name => "Program", :foreign_key => "integer_data"
validates_presence_of :integer_data
end
class QuotaIncreaseRequest < UserRequest
validates_presence_of :string_data
validates_inclusion_of :string_data, :in => %w( 1GB 5GB 10GB 15GB )
end
And of course, alias your string_data and integer_data to email or whatnot to make your other code have a little more meaning. Let the model be the little black box that hides it all away.
I would use polymorphic associations, which let a model belong to more than one other model using a single association. Something like this:
class AdminRequest < ActiveRecord::Base
belongs_to :user
belongs_to :requestable, :polymorphic => true
end
class EmailAlias < ActiveRecord::Base
has_many :admin_requests, :as => :requestable
end
class ProgramInstall < ActiveRecord::Base
has_many :admin_requests, :as => :requestable
end
class QuotaIncrease < ActiveRecord::Base
has_many :admin_requests, :as => :requestable
end
As ever, Ryan Bates has an excellent Railscast on the subject.