Sequel model over two joined tables - ruby

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.

Related

ActiveRecord mapping to table name with schema name as prefix

Has anyone experienced this issue on mapping table in ActiveRecord when table name need a schema name as a prefix (oracle)?
Gemfile
gem 'activerecord', '4.2.4'
gem 'activerecord-oracle_enhanced-adapter', '1.6.7'
....
db_helper.rb
class Student < ActiveRecord::Base
self.abstract_class = true
self.table_name_prefix = 'OPT_ABC.'
self.table_name = 'STUDENT'
def self.list_student
puts Student.take(1) #testing
end
end
The actual table name looks like:
SELECT * FROM OPT_ABC.STUDENT;
I am able to connect to the database instance, but when the code gets line:
puts Student.take(1) # SELECT * FROM STUDENT LIMIT 1
I get the following error:
ActiveRecord::StatementInvalid:
table or view does not exist: SELECT "STUDENT".* FROM "STUDENT"
I am looking for solution on how to handle 'OPT_ABC." table prefix. Please share your solution.
It looks like the problem is that you're trying to use both self.table_name_prefix= and self.table_name= together when you should be using one OR the other.
First let's consider how both self.table_name_prefix= and self.table_name= work.
self.table_name_prefix=
According to the documentation, self.table_name_prefix= works by prepending the passed in value to the table name that ActiveRecord automatically generates based off of the name of the class.
So, if the class name is Student and you do self.table_name_prefix = 'OPT_ABC.', your table name will be OPT_ABC.STUDENTS. Note that the generated table name is plural (and ends in an s).
self.table_name=
According to the documentation, self.table_name= sets the table name explicitly. This means that it completely overrides the table name to the value that you pass in.
So, if you do self.table_name = 'OPT_ABC.STUDENT' your table name will be OPT_ABC.STUDENT.
So, given that, to set the table name to OPT_ABC.STUDENT, you should be able to simply pass the value into self.table_name like this:
class Student < ActiveRecord::Base
self.abstract_class = true
self.table_name = 'OPT_ABC.STUDENT'
def self.list_student
puts Student.take(1) #testing
end
end

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)

Securely pass ID param in URL rails

I have a link like this: http://www.somesite.com/s/bkucoj?i=#{#client.id}.
How do I make sure, that client's id will be passed securely (hashed/encrypted), and not just naked number?
So the goal is to get something like:
http://www.somesite.com/s/bkucoj?i=f1nSbd3bH34ghfAh12lcvzD
instead of
http://www.somesite.com/s/bkucoj?i=12.
How can I achieve it?
And what's more, I would also like to ensure, that on the other end the client ID is gotten correctly
Thank you!
Probably the best way to do this would be to add an extra column (non-null, unique) to your User model, which is randomized upon the User creation.
before_create do
self.uuid = SecureRandom.uuid
end
Then you can use uuid to identify the user instead of id.
Naturally you will need to modify all your existing user when adding this column.
Implementation details
Your migration needs to have 3 parts. Firstly you need to add a uniq, nullable column uuid to users table. Then, you need to loop over your existing customers and populate this column. After it you can make the column not-nullable. It would most likely look like this:
class Blah000000000 < ActiveRecord::Migration
class User < ActiveRecord::Base
before_save { self.uuid ||= SecureRandom.uuid }
end
def up
add_column :users, :uuid, :string, unique: true
User.all.each &:save!
change_column :user, :uuid, string, unique: true, null: false
end
def down
remove_column :users, :uuid
end
end
You could add an extra parameter which is the HMAC or digital signature of the id. The recipient can verify that id has not changed.

ORM one model - multiply tables

Have a database with multiple tables. All tables have the same structure. I am writing a small web application to work with this database on Ruby / Sinatra. I want to simplify the work with tables using ORM - Active Record or DataMapper (preferred). The manuals for the use of a single model for multiple tables offer something like:
class Table
include DataMapper::Resource
property id, Serial
property item, String
end
class TableA < Table
self.table_name = 'table_a'
end
class TableB < Table
self.table_name = 'table_b'
end
How this can be done for several dozen tables without copypaste?
If possible, the decision should be the possibility to add / remove the table without changing the code / settings and restart the application.
Something like:
# Model declaration
DataMapper.finalize
itemA = Table.new (use_table: 'table_a')
itemB = Table.new (use_table: 'table_b')
One of the ways to achive this is using eval.
class Table
include DataMapper::Resource
property id, Serial
property item, String
end
table_names = {'TableA' => 'table_a', 'TableB' => 'table_b'}
table_names.each do |klass_name, table_name|
eval <<DYNAMIC
class #{klass_name} < Table
self.table_name = '#{table_name}'
end
DYNAMIC
end

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

Resources