Manually manage join table association in activeRecord - ruby

I have three tables, server, domain, site, I'm trying to make it so it works like this:
You create a site, then a primary domain and aliases and assign the primary domain to a site (by selecting a site via drop down box within domain page).
Then in the servers, you select a site and it auto assigns all domains for that site (including aliases) to that server.
The Domains table is also used as a through table for both the site and server tables
However at the moment, when you select a site and then save it it auto-generates the relation rather then using the one's that are already there.
Also for some reason activeRecord won't let me assign the domains to the server either, no matter how I try it:
server.domains = domains
server.domains_id = domains.map(:&id)
server[:domains_id] = domains.map(:&id)
server.assign_attribute(:domains, domains)
server.assign_attribute(:domains_id, domains.map(:&id)
domains_id on the server remains nil despite not erroring (it saves successfully, just domains_id is nil)
Ultimately, what I would like to know is how to manually manage the join table and also why won't rails let me assign values to the attribute?
server.rb:
class Server < ApplicationRecord
before_save :set_domains#, if: :sites_id_changed?
has_many :domains, dependent: :nullify
has_many :sites, through: :domains
has_many :projects, through: :sites
has_many :clients, through: :projects
def set_domains
domains = get_domains Site.where(id: self.site_ids).all
domains += get_domains domains,:domains
domains.each do |domain|
domain.server = self
end
self.domains = domains
end
private
def get_domains(objects,meth=:domain)
objects.first.blank? ? [] : objects.map(&meth).flatten
end
end
domain.rb:
class Domain < ApplicationRecord
alias_attribute :aliases, :domains
alias_attribute :alias_of, :domain
has_many :domains, dependent: :nullify
belongs_to :domain, optional: true
belongs_to :site, optional: true
belongs_to :server, optional: true
def alias?
!self.alias_of.blank?
end
accepts_nested_attributes_for :domains, allow_destroy: true
end
site.rb:
class Site < ApplicationRecord
enum environment: %i{development staging production}
before_save :set_server
belongs_to :project, optional: true
belongs_to :client, optional: true
has_one :domain, dependent: :nullify
has_many :servers, through: :domain
def set_server
self.server = self.domain.server
end
end

Related

How should I use attributes on the "through" object in a has_many: through: ActiveRecord relation?

class Reservation << ApplicationRecord
has_many :charges_reservations, :dependent => :destroy
has_many :charges, :through => :charges_reservations
monetize :price_cents
def associate_charge(charge, portion)
# looking for help here
end
def owing
price - amount_paid
end
def amount_paid
# looking for help here
end
end
class ChargesReservation << ApplicationRecord
belongs_to :charge
belongs_to :reservation
monetize :portion_cents
end
class Charge << ApplicationRecord
has_many :charges_reservations
has_many :reservations, :through => :charges_reservations
monetize :amount_cents
validates :state, inclusion: {in: %w(failed successful pending)}
def successful?
state == "successful"
end
end
What I want to know is, how to access the portion attribute on
ChargesReservation both when associating the charges with the reservations,
and when asking whether the reservation is fully paid. Both Charge and
Reservation are created at different points in the user flow.
So my question is twofold:
what's the best way to create the association once I have a charge?
how do I get the sum of a reservation's portions of all successful charges
associated with it, as a model method. I know, roughly, how to achieve this
in SQL (I'm rusty, but I'd get there) but I'm stumped in ActiveRecord.

Joint table with optional relation

I have three models:
Company;
Investor;
Broker.
in real world each investor can invest in many companies, but in some he/she can invest with broker assistance.
I made it through joint table.
def change
create_join_table :companies, :investors do |t|
t.references :broker, index: true, optional: true
end
end
class Company < ApplicationRecord
has_and_belongs_to_many :investors
end
class Investor < ApplicationRecord
has_and_belongs_to_many :companies
end
class Broker < < ApplicationRecord
has_many :companies
end
How should I configure my models/migrations to have next information:
Company.first.investors.first.broker
broker is not belongs to investor, in each company/investor pair can be different broker.
Use has_many through associations and add the broker on the join model. After adding the proper migrations your models will look like this:
class Company < ApplicationRecord
has_many :company_investors
has_many :investors, through: :company_investors
end
class Investor < ApplicationRecord
has_many :company_investors
has_many :companies, through: :company_investors
end
class Broker < ApplicationRecord
has_many :company_investors
has_many :companies, through: :company_investors
end
class CompanyInvestor < ApplicationRecord
belongs_to :broker
belongs_to :investor
belongs_to :company
end
With this models, you can add Investor to Company associations with or without Broker, and also discriminate them. I also recommend naming the join model (which in my code I named CompanyInvestor) a more significant name, like Investment

Activerecord relation not nullifying dependent despite being asked to do so

I'm getting the following error:
PG::ForeignKeyViolation: ERROR: update or delete on table "sites" violates foreign key constraint "fk_rails_b1cb5ea385" on table "domains" DETAIL: Key (id)=(1) is still referenced from table "domains". : DELETE FROM "sites" WHERE "sites"."id" = $1
This is because there is a Domain record that has a reference to the site being deleted, I know this because manually removing the site_id causes the error to go away (of course this is not the way to do this, this was done for checking purposes only).
However as can be seen in the model:
class Site < ApplicationRecord
enum environment: %i{development staging production}
belongs_to :project
belongs_to :client
has_one :domain, dependent: :nullify
has_many :servers, through: :domain
end
I am indeed asking active record to nullify the domain ( though I am considering outright destroying it, that isn't relevant to this issue).
This association is also used in Server:
class Server < ApplicationRecord
before_save :set_domains, if: :sites_id_changed?
has_many :domains, dependent: :nullify
has_many :sites, through: :domains
def clients
self.sites.map(&:client).flatten
end
def set_domains
domains = get_domains Site.where(id: self.site_ids).all
domains += get_domains domains,:domains
self.domains = domainsprimary_domains
end
private
def get_domains(object,meth=:domain)
objects.first.blank? ? [] : objects.map(&meth).flatten
end
end
and Domain:
class Domain < ApplicationRecord
alias_attribute :aliases, :domains
alias_attribute :alias_of, :domain
has_many :domains, dependent: :nullify
belongs_to :domain, optional: true
belongs_to :site, optional: true
belongs_to :server, optional: true
def alias?
!self.alias_of.blank?
end
accepts_nested_attributes_for :domains, allow_destroy: true
end
Why despite being asked to so do is active record not nullifying the reference to site table in the domain table despite being asked (seemingly at least) to do so?
I have run into something similar in the past. You are correct that this is a situation where you are running into a database-level foreign key constraint.
If you're using PG, for example, here are some helpful docs with more information on the constraint in general.
As far as solving your actual issue, the SO response that helped me overcome this error and get things working again I found here.
Let me know if that works/helps! :)

Trouble with cascading in Active Record models

I have these active record models in my application
class Artist < ActiveRecord::Base
has_many :albums, :dependent => :delete_all, autosave: true
end
class Album < ActiveRecord::Base
attr_accessor :album_artist
has_many :tracks, :dependent => :delete_all, autosave: true
has_many :covers, :dependent => :delete_all, autosave: true
belongs_to :artist
end
class Track < ActiveRecord::Base
belongs_to :album
end
class Cover < ActiveRecord::Base
belongs_to :album
end
I'm trying, in my application, when i delete an Artist, his albums and, in consequence, the tracks and covers of his albums, get all deleted, in a cascade reaction.
The way it's implemented today, when i delete the Artist, only the Album is deleted too, leaving orphan records in my database.
Am i doin' anything wrong?
Instead of :dependent => :delete_all you need to configure:
:dependent => :destroy_all
Because delete will just delete all its associated objects directly from the database without calling their destroy method (what break cascading).
You might want to read 4.1.2.4 in http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference

Comparing two instantances of the same activerecord object returns false if attr_accessible is present Ruby

Using Ruby 1.9.3 and ActiveRecord 3.2.6.
I'm having an issue when trying to compare an ActiveRecord Object that has attr_accessible :property set on it, that is contained in an Array of associated Objects using include?(object).
These are my 2 ActiveRecord models, Account and Role.
Account:
class Account < ActiveRecord::Base
# Associations
#
has_many :role_assignments, :dependent => :destroy
has_many :roles, :through => :role_assignments
end
Role:
class Role < ActiveRecord::Base
attr_accessible :title
# Associations
#
has_many :role_assignments, :dependent => :destroy
has_many :accounts, :through => :role_assignments
end
If I then create a couple of Roles (say "Admin" and "Editor") and assign the "Admin" one to an Account, I would assume this would work:
role = Role.find_by_title("Admin")
account = Account.first # => The Account we assigned the "Admin" role to
account.roles.include?(role) # => Should be true but returns false
But this actually returns false!
If I remove the 'attr_accessible :title' from the Role model and repeat the above then it does return true.
So I guess my question is... why would attr_accessible cause this particular issue? or is it a case that I have to do my check to see if role does exist in account.roles a different way?
You could try that
account.role_ids.include?(role.id)

Resources