Sequel migration - only add column if table does not have it yet - ruby

I am trying to create a Sequel migration that goes through a list of tables, and try to add a specific column if the table does not of that column yet.
For example:
Sequel.migration do
change do
table_list = [:table1, :table2, :table3]
table_list.each do |t|
if t does not have :specific_column yet
alter_table(t) do
add_column :sepcific_column, type: String
end
end
end
end
end
Is is possible to tell if the column already exist in a table, so I can do stuff accordingly?

Yes, it is possible. The method Dataset#columns returns a list of columns. This result can be used to be checked with include?
Full example:
Sequel.migration do
change do
table_list = [:table1, :table2, :table3]
table_list.each do |t|
if ! self[t].columns.include?(:specific_column)
alter_table(t) do
add_column :specific_column, type: String
end
end
end
end
end
or
Sequel.migration do
change do
table_list = [:table1, :table2, :table3]
table_list.each do |t|
alter_table(t) do
add_column :specific_column, type: String
end unless self[t].columns.include?(:specific_column)
end
end
end
end

Related

Delete from table without primary key in Sequel

I am trying to delete a row I just added in a test (minitest) in its teardown method & it fails with the message that the table doesn't have a primary key. Following is its migration:
Sequel.migration do
up do
create_table :hobbies_users do
Integer :user_id
Integer :hobby_id
unique [:user_id, :hobby_id]
end
end
down do
drop_table :hobbies_users
end
end
How can I do it? Is it bad practice not to have a primary key?
Test code, where I found the problem:
require_relative '../../test_helper.rb'
require 'api/models/users_hobbies'
class UsersHobbiesTest < Minitest::Test
def setup
#uh = UsersHobbies
.new(user_id: User.all.sample.id, hobby_id: Hobby.last.id * 10)
.save
end
def test_accessors
assert_kind_of User, #uh.user
end
def teardown
#uh.destroy
end
end
For the time being you can define primary key in model:
self.primary_key = :some_column
But I find it necessary to have primary keys in every table, because lately I faced a situation, when I was having an association table without a primary key, and I needed to destroy the association by deleting the db entry, which was impossible due to absence of primary key (in my case it was MySQL).
Yo can do it this way
#record_to_erase = DB["DELETE FROM hobbies_users"]
#record_to_erase.delete

Sequel : DRY between schema migration and model validate method

I'm wondering if I miss a way to avoid repeat validation code in my Sequel::Model#validate subclass method since I've already put all constraints into my migration file.
Here's a simple example of what I'm talking about :
Sequel.migration do
change do
create_table :users do
primary_key :id
String :name, :null => false, :unique => true
end
end
end
class User < Sequel::Model
def validate
super
validates_presence :name
validates_unique :name
validates_type String :name
end
end
It seems very painful and errors prone to have to repeat all the constraints in the validate method. Did I miss something or there's no other way to do that ?
Any advice will be appreciated, thanks
Sequel has some nice plugins and extensions.
Sequel::Model.plugin(:auto_validations)
Sequel::Model.plugin(:constraint_validations)
and
DB.extension(:constraint_validations)
auto_validations
The auto_validations plugin automatically sets up three types of
validations for your model columns:
type validations for all columns
not_null validations on NOT NULL columns (optionally, presence
validations)
unique validations on columns or sets of columns with unique indexes
See http://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/AutoValidations.html
constraint_validations
The constraint_validations extension is designed to easily create
database constraints inside create_table and alter_table blocks. It
also adds relevant metadata about the constraints to a separate table,
which the constraint_validations model plugin uses to setup automatic
validations.
See http://sequel.jeremyevans.net/rdoc-plugins/files/lib/sequel/extensions/constraint_validations_rb.html
and
http://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/ConstraintValidations.html
Your example would look like this
Sequel::Model.plugin(:auto_validations)
Sequel::Model.plugin(:constraint_validations)
Sequel.migration do
up do
extension(:constraint_validations)
create_table :users do
primary_key :id
String :name, :null => false, :unique => true
validate do
presence :name,
name: :presence_name
end
end
end
down do
extension(:constraint_validations)
drop_table(:users)
end
end
class User < Sequel::Model
end
I think, it's normal. Don't worry.

Wrong STI column is used during migration

In a new migration I tried to add a column and populate its values:
def up
add_column :topics, :status, :string
Topic.reset_column_information
Topic.find_each do |t|
if t.ended?
t.status = 'ended'
end
end
Topic.reset_column_information
end
I am not sure why, but after the column is added, during the Topic.find_each the following exception were raised:
The single-table inheritance mechanism failed to locate the subclass: 'other'.
My Topic model did have the following declared:
self.inheritance_column = 'class_name'
So I am not sure why it still try to look up the STI subclass using the type column.
I found that once I removed reset_column_information, the problem is gone.
add_column :topics, :status, :string
# Topic.reset_column_information
Topic.find_each do |t| ...
reset_column_information probably has a bug at removing the STI column setting.

Adding a Hash to an ActiveRecord Hash

I read How to add a Hash object to an ActiveRecord class? Tried but migration fails and followed the format there.
I tried:
class AddTestResponsesToSurveys < ActiveRecord::Migration
def change
add_column :surveys, :responses, :hash
end
end
When I run rake db:migrate, I get an error in my schema.rb file that says:
# Could not dump table "surveys" because of following StandardError
# Unknown type 'hash' for column 'responses'
What am I doing wrong?
generate migration with column type text
class AddTestResponsesToSurveys < ActiveRecord::Migration
def change
add_column :surveys, :responses, :text
end
end
And in your Survey model, add this
serialize :responses, Hash

How can I create a field using the id from the same row in ActiveRecord Ruby

-----UPDATE-----
Well, seems that the problem was in last.id. When database is created works OK, but when not fails. Now the question is different: How can I create a field using the id from the same row?
--------ORIGINAL------
I'm working with active record in pure ruby (without Rails), and I'm literally getting crazy with this.
This is my code
class Enviroment < ActiveRecord::Base
#self.table_name = 'enviroments'
self.connection.create_table(:enviroments, :force=>true) do |t|
t.column :name, :string, :default=>'env-'+ (last.id-1).to_s
t.column :ssh, :string, :default=>nil
end
end
and here the error:
ActiveRecord::StatementInvalid: Could not find table 'enviroments'
from /usr/lib/ruby/gems/1.8/gems/activerecord-3.2.3/lib/active_record/connection_adapters/sqlite_adapter.rb:465:in `table_structure'
if I useself.table_name = 'enviroments' still not working. I've updated the gems and neither.
I'm newbie with ruby and databases, but I can't understand this problem, I think this same code worked in the past :S
Your code to create the table (very odd to have that in the model by the way) is calling last.id, and of course to call last the table must already exist.
Because you're passing :force => true to create_table you'll actually destroy the table if it already exists.
You could probably make your code work if you stashed the value of last.id in a local variable before the call to create_table but I don't understand why you are creating tables like this.
Finally, this was my solution:
class Enviroment < ActiveRecord::Base
after_create :create_default
private
def create_default
if name == nil
s = 'env-' + self.id.to_s
self.name = s
self.save
end
end
end
class CreateSchema < ActiveRecord::Migration
create_table(:enviroments, :force=>true) do |t|
t.column :name, :string, :default=>nil
t.column :ssh, :string, :default=>nil
end

Resources