How to make a database appear partitioned by some columns with Active::Record - ruby

Suppose a column client_id is ubiquitous through out our database, and for a given session or request, we will be 'in the context' of a client the whole time.
Is there a way to simulate having each client's data stored in a separate database, while keeping them in the same table for simpler database-management ? What I want is similar to a default scope, but since it will change for every request, it cant' be fixed at load-time.
# invoices table data
# -------------------
# id client_id amount
# 1 1 100.00
# 2 2 100.00
with_client( Client.find(1) ) do
Invoices.all # finds only invoice 1
Invoices.find(2) # finds nothing or raises
end
How can I do this with ActiveRecord, or at what points could I surgically alter AR to affect this behavior ?
Extra points: Id like to prevent the updating of this client_id column - it should be fixed at create-time

class Client < ActiveRecord::Base
has_one :invoice, :dependent => :destroy
end
class Invoice < ActiveRecord::Base
belongs_to :client
end
In controller
#client= Client.find(1)
#client.invoice #finds id =1 client_id =1 amount=100.0

There is a presentation here that discusses multi-tenanted applications. It has some interesting ideas to utilise database schemas, particularly for postgres.

Related

You can have_one if you're true

In my website (written with sinatra) I am trying to set up a database. I have 2 tables, here referred to as Table1 and Table2.
models.rb
class Table1 < ActiveRecord::Base
Table1.where(bool:true) has_one :table2 # PSUDO-CODE
# So that every record where bool:true has the relationship
# but every record where bool:false or bool:nil doesn't
end
class Table2 < ActiveRecord::Base
belongs_to :table1
end
I am trying to find a way to make the section labeled PSUDO-CODE into actual code. How can I do that?
You can't do this directly: a class either has a relationship or it doesn't (although of course there may be no associated record)
You can set conditions on an association, but to the best of my knowledge you can only really set conditions on the associated collection (i.e. table 2 in this case)
You can however override the generated method, so for example
class Table1 < ActiveRecord::Base
has_one :table2
def table2(*args)
bool ? super : nil
end
end
This works with current versions of activerecord - not how far back this is supported (older version defined the association methods directly on the class so you couldn't call super)

Sequel model over two joined tables

I have a legacy PostgreSQL database, which has a single model split into two tables, with one-to-one mapping between them.
CREATE TABLE auth_user (
id SERIAL,
username VARCHAR(30),
email VARCHAR(75),
password VARCHAR(64),
first_name VARCHAR(75),
last_name VARCHAR(75)
)
CREATE TABLE user_profile (
user_id INTEGER REFERENCES auth_User.id,
phone VARCHAR(32)
)
Unfortunately, I'm unable to change database structure.
I want to use this as a single Sequel model. Retreiving data from database works as expected:
class User < Sequel::Model
end
# Variant 1: using LEFT JOIN
#User.set_dataset DB[:auth_user].left_join(:user_profile, :user_id => :id)
# Variant 2: using two FROM tables
User.set_dataset DB[:auth_user, :user_profile]\
.where(:auth_user__id => :user_profile__user_id)
user = User[:username => "root"] # This works.
However, saving the model fails:
user.set :first_name => "John"
user.save # This fails.
If I use first variant of the dataset (with left_join) I get a "Need multiple FROM tables if updating/deleting a dataset with JOINs" error. If I use second variant, it still fails: "PG::Error: ERROR: column "phone" of relation "auth_user" does not exist LINE 1: ..."email" = 'nobody#example.org', "password" = '!', "phone"..."
Is there a way I could make Sequel seamlessly issue two UPDATE statements? (Same question holds for INSERTs, too).
You can have a Sequel model that uses a joined dataset, but there's no easy way to save such a model.
Personally, I would use a many_to_one relationship, nested attributes, and hooks for what you want:
class UserProfile < Sequel::Model(:user_profile)
end
class User < Sequel::Model(:auth_user)
many_to_one :user_profile, :key=>:id, :primary_key=>:user_id
plugin :nested_attributes
nested_attributes :user_profile
def phone
user_profile.phone
end
def phone=(v)
user_profile.phone = v
end
def user_profile
if s = super
s
else
self.user_profile_attributes = {}
super
end
end
def before_destroy
user_profile.destroy
super
end
def before_create
user_profile
super
end
def after_update
super
user_profile.save
end
end
I haven't tested that, but something like it should work. If you have problems with it, you should probably post on the sequel-talk Google Group.

How can I guarantee before_destroy callback order in autosave records?

I'm implementing an audit-trail-esque system in my rails3 app; basically, I want to keep track of exactly when my users make any changes.
Currently facing an issue where I have two models - one child to the other. Pseudocode follows:
class Audit;end #this is a straightforward data-storing model, trust me!
class Parent
has_many :children, :dependent => :destroy
before_destroy :audit_destroy
def audit_destroy
Audit.create(:metadata => "some identifying data")
end
end
class Child
belongs_to :parent
before_destroy :audit_destroy
def audit_destroy
Audit.create(:metadata => "some identifying data")
end
end
On calling
Parent.destroy
I'm expecting two new Audit records to be created with created_at timestamps that are ordered relative to the parent records. Said slightly differently: the parent record's Audit is created before the child record's Audit.
This doesn't, however, seem to be guaranteed, as I haven't explicitly said anything about the order of creation of the audit records. While it seems to usually hold true, I have confirmed that sometimes the order of the creation of the Audit records is inverted.
Is there some magic in the depths of ActiveRecord surrounding the ordering of before_destroy callbacks creating new records and autosave?
Thanks,
Isaac

Rails 3 - Database query on Models User and Role in a many to many condition

I have two entities. User and Role. I am using Devise and CanCan.
They are in a many to many relationship.
User has a lot of roles.
One of the roles is "Administrator". I verify if my user is an administrator using:
if (user.role? :administrator) .... #this is already implemented and working
I have to validate that never exists more than 2 administrator in the same department on the system. For that purpose I created a custom validate method:
class User < ActiveRecord::Base
validate :maximum_numbers_of_admins if self.role? :administrator
belongs_to :department
def maximum_numbers_of_admins
#Some code here
end
In that method I should count the number of Users that have role administrator (without counting myself).
I don't know how to set the :conditions of my find method to get this number.
This is the specification of the Role class:
# == Schema Information
#
# Table name: roles
#
# id :integer not null, primary key
# name :string(255)
# created_at :datetime
# updated_at :datetime
#
There is a many to many relationship between users and Roles. (Table roles_users)
Any help with that?
Thanks
It could be something along this lines:
def maximum_numbers_of_admins
if Role.find(:conditions => ['name = ?', 'Administrator']).users.count < 2
return true
else
return false
end
end

Attributes passed to .build() dont show up in the query

Hope your all enjoying your hollydays.
Ive run into a pretty funny problem when trying to insert rows into a really really simple database table.
The basic idea is pretty simple. The user selects one/multiple users in a multiselect which are supposed to be added to a group.
This piece of code will insert a row into the user_group_relationships table, but the users id always
#group = Group.find(params[:group_id])
params[:newMember][:users].each do |uid|
# For debugging purposes.
puts 'Uid:'+uid
#rel = #group.user_group_relationships.build( :user_id => uid.to_i )
#rel.save
end
The user id always gets inserted as null even though it is clearly there. You can see the uid in this example is 5, so it should work.
Uid:5
...
SQL (0.3ms) INSERT INTO
"user_group_relationships"
("created_at", "group_id",
"updated_at", "user_id") VALUES
('2010-12-27 14:03:24.331303', 2,
'2010-12-27 14:03:24.331303', NULL)
Any ideas why this fails?
Looks like user_id is not attr_accessible in the UserGroupRelationship model.
You might also want to check this it may be relevant.
I think #zabba's answer is probably the one you need to look for but i would suggest a couple of extra things here.
Your "Group" and "User" models are connected to each other thru the "UserGroup" model it seems. You would have relationship
class Group < ActiveRecord::Base
has_many :user_group_relationships
has_many :users, :through => :user_group_relationships
end
class UserGroupRelationship < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
In your controller
# Find the group
#group = Group.find(params[:group_id])
# For each user id, find the user and add the user_group_relationship
params[:newMember][:users].each{|u| #group.users << User.find(u) }
Read up Rails Documentation on associations and the methods generated automatically for you when associations are defined. More often than not, working with association is easier than you might think! I discover new APIs in Rails constantly! :) Good Luck!

Resources