How to use hstore on Heroku - ruby-on-rails-3

As per https://postgres.heroku.com/blog/past/2012/4/26/heroku_postgres_development_plan/ I did "heroku addons:add heroku-postgresql:dev". But when I do
class CreateUsers < ActiveRecord::Migration
def up
create_table :users do |t|
execute "CREATE EXTENSION hstore"
t.hstore :access
end
end
def down
drop_table :users
execute "DROP EXTENSION hstore"
end
end
end
and then "heroku run rake db:migrate" I get this error:
PG::Error: ERROR: syntax error at or near "EXTENSION"
LINE 1: CREATE EXTENSION hstore ^
: CREATE EXTENSION hstore

finally got it to work. turns out i needed to "promote" the database using heroku pg:promote as per https://devcenter.heroku.com/articles/heroku-postgres-dev-plan

I think you want to split out your migrations, one to add hstore, the other to use it;
class SetupHStore < ActiveRecord::Migration
def self.up
execute "CREATE EXTENSION hstore"
end
def self.down
execute "DROP EXTENSION hstore"
end
end
to enable the extension, and the your Users migration will just add any fields and then use hstore on which ever column you want.

Related

Migration to add enum column in rails3 and enumerated_attribute

I need a migration to add column of type enum in rails 3. I will be using enumerated_attribute gem.
I generated a migration to add the column:
rails generate migration addUsage_reports_accessToClientParam usage_reports_access:enum
Now I need to set up the values for the enum and set the default value. Here is the generated migration:
class AddUsageReportsAccessToClientParam < ActiveRecord::Migration
def self.up
add_column :client_params, :usage_reports_access, :enum
end
def self.down
remove_column :client_params, :usage_reports_access
end
end
Thanks
I found a solution. This works:
add_column :client_params, :usage_reports_access, "ENUM('value1','value2', 'value3') DEFAULT 'value1'"

Convert Rails migration to raw SQL

How can I convert this migration to raw sql? or Can I convert?
class AddUnsubscribeTokenToUsers < ActiveRecord::Migration
def self.up
add_column :users, :unsubscribe_token, :string, :unique => true
User.all.each do |user|
user.unsubscribe_token = ActiveSupport::SecureRandom.hex(18)
end
end
def self.down
remove_column :users, :unsubscribe_token
end
end
AFAIK you can't convert a single migration into SQL, but you can have ActiveRecord output your schema in SQL instead of Ruby.
# in config/application.rb
config.active_record.schema_format = :sql
This will give you SQL output in your db/schema instead of the Ruby DSL. But neither format will include the snippet of code setting the user token.
Also, it's considered a Bad Idea to include DB modifying code like that in your migration. For example what happens if you get rid of or rename the model, when that migration runs it will fail. At least wrap it in a check for the model. or a begin/rescue/end
if defined? User
User.all.each do |user|
user.unsubscribe_token = ActiveSupport::SecureRandom.hex(18)
end
end
or
begin
User.all.each do |user|
user.unsubscribe_token = ActiveSupport::SecureRandom.hex(18)
end
rescue
end
And lastly, that snippet is not going to do what you intended since your not saving the model after setting the token, either use update_attributes or call user.save
I stumbled upon a very nice article, describing how this can be achieved via a custom rake task.

How to add a Hash object to an ActiveRecord class? Tried but migration fails

I want my ActiveRecord class User to contain options (a bunch of string key-values), so I wrote:
rails generate migration AddOptionsToUser options:Hash
It generated:
class AddOptionsToUser < ActiveRecord::Migration
def self.up
add_column :users, :options, :Hash
end
def self.down
remove_column :users, :options
end
end
I also added this line to my class User:
serialize :options, Hash
But the migration fails:
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Hash' at line 1: ALTER TABLE `users` ADD `options` Hash
I am new to Rails, what is the usual way to store a bunch of string key-values in an ActiveRecord class?
Rails serializes things in to a (YAML) string. So in your database, the type should be string (or text).
class AddOptionsToUser < ActiveRecord::Migration
def self.up
add_column :assessments, :options, :string
end
def self.down
remove_column :assessments, :options
end
end
To have ruby object as an attribute of the ActiveRecord model you should use serialize method inside your class for that attribute link

Adding a column to an existing table in a Rails migration

I have a Users model which needs an :email column (I forgot to add that column during the initial scaffold).
I opened the migration file and added t.string :email, did rake db:migrate, and got a NoMethodError. Then I added the line
add_column :users, :email, :string
again rake db:migrate, again NoMethodError. Am I missing a step here?
Edit: here's the migration file.
class CreateUsers < ActiveRecord::Migration
def self.up
add_column :users, :email, :string
create_table :users do |t|
t.string :username
t.string :email
t.string :crypted_password
t.string :password_salt
t.string :persistence_token
t.timestamps
end
end
def self.down
drop_table :users
end
end
If you have already run your original migration (before editing it), then you need to generate a new migration (rails generate migration add_email_to_users email:string will do the trick).
It will create a migration file containing line:
add_column :users, email, string
Then do a rake db:migrate and it'll run the new migration, creating the new column.
If you have not yet run the original migration you can just edit it, like you're trying to do. Your migration code is almost perfect: you just need to remove the add_column line completely (that code is trying to add a column to a table, before the table has been created, and your table creation code has already been updated to include a t.string :email anyway).
Use this command on the terminal:
rails generate migration add_fieldname_to_tablename fieldname:string
and
rake db:migrate
to run this migration
Sometimes rails generate migration add_email_to_users email:string produces a migration like this
class AddEmailToUsers < ActiveRecord::Migration[5.0]
def change
end
end
In that case you have to manually an add_column to change:
class AddEmailToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :email, :string
end
end
And then run rake db:migrate
You can also do
rake db:rollback
if you have not added any data to the tables.Then edit the migration file by adding the email column to it and then call
rake db:migrate
This will work if you have rails 3.1 onwards installed in your system.
Much simpler way of doing it is change let the change in migration file be as it is.
use
$rake db:migrate:redo
This will roll back the last migration and migrate it again.
To add a column I just had to follow these steps :
rails generate migration add_fieldname_to_tablename fieldname:string
Alternative
rails generate migration addFieldnameToTablename
Once the migration is generated, then edit the migration and define all the attributes you want that column added to have.
Note: Table names in Rails are always plural (to match DB conventions). Example using one of the steps mentioned previously-
rails generate migration addEmailToUsers
rake db:migrate
Or
You can change the schema in from db/schema.rb, Add the columns you want in the SQL query.
Run this command: rake db:schema:load
Warning/Note
Bear in mind that, running rake db:schema:load automatically wipes all data in your tables.
You can also add column to a specific position using before column or after column like:
rails generate migration add_dob_to_customer dob:date
The migration file will generate the following code except after: :email. you need to add after: :email or before: :email
class AddDobToCustomer < ActiveRecord::Migration[5.2]
def change
add_column :customers, :dob, :date, after: :email
end
end
You also can use special change_table method in the migration for adding new columns:
change_table(:users) do |t|
t.column :email, :string
end
When I've done this, rather than fiddling the original migration, I create a new one with just the add column in the up section and a drop column in the down section.
You can change the original and rerun it if you migrate down between, but in this case I think that's made a migration that won't work properly.
As currently posted, you're adding the column and then creating the table.
If you change the order it might work. Or, as you're modifying an existing migration, just add it to the create table instead of doing a separate add column.
You can also do this ..
rails g migration add_column_to_users email:string
then rake db:migrate
also add :email attribute in your user controller ;
for more detail check out http://guides.rubyonrails.org/active_record_migrations.html
You can also force to table columns in table using force: true, if you table is already exist.
example:
ActiveRecord::Schema.define(version: 20080906171750) do
create_table "authors", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
You could rollback the last migration by
rake db:rollback STEP=1
or rollback this specific migration by
rake db:migrate:down VERSION=<YYYYMMDDHHMMSS>
and edit the file, then run rake db:mirgate again.

Rails Postgres functional indexes

How I should enter my multicolum indexes which contain functions into schema.rb ?
for example this DOESN'T work:
add_index "temporary_events", ["templateinfoid", "campaign", "date(gw_out_time)", "messagetype"], :name => "temporary_events_campaign_tinfoid_date_messagetype"
rake db:test:load
rake aborted!
PGError: ERROR: column "date(gw_out_time)" does not exist
: CREATE INDEX "temporary_events_campaign_tinfoid_date_messagetype" ON "temporary_events" ("templateinfoid", "campaign", "date(gw_out_time", "messagetype")
The built-in ActiveRecord method for creating indexes (add_index) does not support functions or any other more advanced features. Instead you can use execute to create the index with SQL:
execute <<-SQL
CREATE INDEX temporary_events_campaign_tinfoid_date_messagetype
ON temporary_events(templateinfoid, campaign, date(gw_out_time), messagetype);
SQL
Note that the use of execute in migrations can be problematic if you are not using the SQL schema format (config.active_record.schema_format = :sql). For more information, search for schema_format.
I was able to get functional indexes out of Rails (3.1.3) migrations by removing a couple guard-rails!
# lib/functional_indexes.rb
module ActiveRecord
module ConnectionAdapters
module SchemaStatements
#disable quoting of index columns to allow functional indexes (e.g lower(full_name) )
def quoted_columns_for_index(column_names, options = {})
column_names
end
def index_name_for_remove(table_name, options = {})
index_name = index_name(table_name, options)
# disable this error check -- it can't see functional indexes
#unless index_name_exists?(table_name, index_name, true)
# raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
#end
index_name
end
end
end
end
I had to make my own index names, though:
class AddLowerCaseIndexes < ActiveRecord::Migration
def up
add_index :people, 'lower(full_name)', :name => "index_people_on_lower_full_name"
add_index :people, 'lower(company)', :name => "index_people_on_lower_company"
end
def down
remove_index :people, :name => "index_people_on_lower_full_name"
remove_index :people, :name => "index_people_on_lower_company"
end
end
(You probably don't need quotes around your index column names unless you are doing something insane like putting spaces or weird characters in them.)
(You are probably fine with postgres error messages when attempting to rollback non-existent indexes.)
If you are using pg_power gem(https://github.com/TMXCredit/pg_power),
you can do it in the following way:
add_index(:comments, 'dmetaphone(author)', :using => 'gist')