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
Related
I want the users to be able to write down their skills as hashtags in a text_field. I already can store a string and split it up like (btw: a user has one account)
<% #user.account.hashtag.split('#').reject { |c| c.empty? }.each do |d| %>
<p><%= d %></p>
<% end %>
But that is not elegant as it's processed in the view right now and since its just one string which is displayed as an array, I cant iterate. What I want to achieve is explained in this video.
A user should write down his skills in one field, the string should be split up at every '#' symbol and stored in a field which should belong to the user, so I can do something like url.com/user/hashtag/xyz while xyz is the hashtag.
The video tutorial is made well, but it does not work for rails 5+ since find_by is not available anymore; also I don't want to create some new tables, because later I want to do the same with other models than account. Later I want to add a autocomplete function in a search field via gems like select2. That is why it might help to add another table for the tags? :S
thanks in advance!
so there are lots of things in this short question.
The first thing that I would do would be to create a hashtag table
class CreateHashtags < ActiveRecord::Migration[5.0]
def change
create_table :hashtags do |t|
t.string :hashtag
t.references :hashtagsable, polymorphic: true
t.timestamps null: false
end
end
end
This line is critical
t.references :hashtagsable, polymorphic: true
This will create 2 new field
:hashtagsable_type => :string, # This reference the model of the assiciation
:hashtagsable_id => :integer, # This reference the id of the assiciation
This new model should look like
# app/models/hashtag.rb
class Hashtag < ApplicationRecord
belongs_to :hashtagsable, polymorphic: true
end
Now your user model you should add this line
class User < ApplicationRecord
has_many :hashtags, as: :hashtagsable # User.last.hashtags
end
class Account < ApplicationRecord
has_many :hashtags, as: :hashtagsable # Account.last.hashtags
end
in your view should look like
<% #user.account.hashtags.each do |hashtag| %>
<p><%= hashtags.hashtag %> </p>
<% end %>
I hope that this helps and set you in the right path
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
Asking this question re: a student project I'm working on and I have tinkered for too long without being able to come up with a solution.
I have a class called Game, a game has many quotes. If I create a game #game = Game.create and then associate it with characters #game.characters = [#character1, #character2]. Because of my associations, I have access to all of the quotes of both characters with #game.quotes (hundreds of objects returned).
I'd like to be able to grab a sample of 10 of the quotes, something like #game.ten_quotes (an array of objects) will return a random sample of #game.quotes. I also want #game.ten_quotes to be saved to the database.
My first thought is that I need a new attribute for Game in the migration:
class CreateGames < ActiveRecord::Migration[5.1]
def change
create_table :games do |t|
t.text :state, default: "[]"
t.boolean :completed, default: false
t.something :ten_quotes
# what would this look like if I'm saving an array of objects?
t.timestamps
end
end
end
In my rails controller below I was able to generate the ten quotes but I feel that I'm working in the wrong direction:
class Game < ApplicationRecord
has_many :game_logs
has_many :characters, through: :game_logs
has_many :quotes, through: :characters
def generate_quotes
if self.ten_quotes == []
x = quotes.shuffle.sample(10)
self.ten_quotes = x
else
return false
end
end
end
How can I get a sample of quotes, associate that sample with a game instance and then save the game instance to the database one time with no chance to overwrite in the future? Do I need a new model?
Thanks in advance if you'd like to assist. Otherwise, have a great day!
class AddTenQuotesIdsColumnToGames < ActiveRecord::Migration[5.1]
def change
add_column :games, :ten_quotes_ids, :text
end
end
class Game < ApplicationRecord
has_many :game_logs
has_many :characters, through: :game_logs
has_many :quotes, through: :characters
serialize :ten_quotes_ids, Array
def ten_quotes
if ten_quotes_ids.length == 10
quotes.where(id: ten_quotes_ids)
else
ten_quotes = quotes.order('RANDOM()').limit(10) # for MySQL use `quotes.order('RAND()').limit(10)`
update ten_quotes_ids: ten_quotes.map(&:id)
ten_quotes
end
end
end
Add a column of type text to your Game model called ten_quotes_ids. In your model, serialize the ten_quotes_ids column as an Array. This lets you store arrays of Ruby objects to the database. You could store instances of quotes, but they will not be kept in sync with your database if there are changes, so better to just store the ids so you can fetch the current records on demand.
In your ten_quotes method you're checking if ten_quotes_ids has 10 elements, and if so querying the Game's quotes based on those ids and returning them, or else selecting a random set of 10 quotes belonging to the Game from the database, updating the ten_quotes_ids attribute, and returning the ten quotes.
edit 2
I removed my first idea, based your comment, as your information "a quote should belong to many samples" and "ten quotes were for a game that happened a week ago", my idea creating new model for samples with this relation as follow
class Game < ApplicationRecord
# -> quotes -> samples
has_many :quotes, :dependent => :destroy
accepts_nested_attributes_for :quotes, :allow_destroy => :true
has_many :samples, through: :quotes
end
class Quote < ApplicationRecord
belongs_to :game
has_many :samples, :dependent => :destroy
accepts_nested_attributes_for :samples, :allow_destroy => :true
end
class Sample < ApplicationRecord
belongs_to :quote
scope :just_last_week, lambda { where('created_at >= ?', 1.week.ago)}
end
I add accepts_nested_attributes_for and allow_destroy to make easier you create child record from parent
here is link in case you want to know more
from game controller
# if you want get samples and
# for specific game
#game = Games.find(params[:id])
#samples = #game.samples.limit(10) # this will get 10 samples from samples
#samples = #game.samples.just_last_week.limit(10) # this will get 10 samples created last week
# for samples that created last week no matter what game is
#samples = Sample.all.just_last_week
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
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.