Convert Rails migration to raw SQL - 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.

Related

RoR: how can I add columns to my database on my live heroku app?

I am trying to add :price, :location, and :product to the columns for my microposts table. I have already done a bunch of other migrations and I have heard that rolling back all of migrations and redoing them is error prone. So I guess the other option is the schema file? I have heard that the schema file is just to be read and not edited. I have been looking at http://guides.rubyonrails.org/migrations.html but can't find the right info. They briefly talk about change_table which I think could be useful but it doesn't go into depth. Is this what I am looking for?
Just create a new standalone migration:
rails g migration add_price_location_and_product_to_microposts
It will create a file in the db/migrate folder, edit it:
def change
add_column :microposts, :price, :float # dont forget to change the type to the columns
add_column :microposts, :location, :string
add_column :microposts, :product, :integer
end
(You can define the change method, instead of up and down because add_column is a reversible command.)
And then, run rake db:migrate

How to use hstore on Heroku

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.

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

Using factory_girl_rails with Rspec on namespaced models

I have a web service that serves Ads to several different clients. The structure of the Ad varies between clients, and therefore, I am using namespaces for my models and controllers by the client name to differentiate between Ads. From the high level, it looks like this:
'app/models/client1/ad.rb'
class Client1::Ad < ActiveRecord::Base
attr_accessible :title, :description
end
'app/models/client2/ad.rb'
class Client2::Ad < ActiveRecord::Base
attr_accessible :title, :description, :source
end
In reality, these models are more complex and have associations, but that is not the point.
I am writing some unit tests using rspec-rails 2.4.0 and factory_girl_rails 1.0.1, and all of my factories work great. However, I am not able to define factories for the namespaced models. I've tried something like:
Factory.define :client1_ad, :class => Client1::Ad do |ad|
ad.title "software tester"
ad.description "Immediate opening"
end
and
Factory.define :client2_ad, :class => Client2::Ad do |ad|
ad.title "software tester"
ad.description "Immediate opening"
ad.source "feed"
end
It didn't do the job. I looked around, but every single example that I saw was using non-namespaced models. Anyone have any ideas? Any input is greatly appreciated.
I have a minimal working example here, maybe you could use it to pinpoint where your problem is. The comment you left on dmarkow's answer suggests to me that you have an error someplace else.
app/models/bar/foo.rb
class Bar::Foo < ActiveRecord::Base
end
*db/migrate/20110614204536_foo.rb*
class Foo < ActiveRecord::Migration
def self.up
create_table :foos do |t|
t.string :name
end
end
def self.down
drop_table :foos
end
end
spec/factories.rb
Factory.define :foo, :class => Bar::Foo do |f|
f.name 'Foooo'
end
*spec/models/foo_spec.rb*
require 'spec_helper'
describe Bar::Foo do
it 'does foo' do
foo = Factory(:foo)
foo.name.should == 'Foooo'
end
end
Running the test:
$ rake db:migrate
$ rake db:test:prepare
$ rspec spec/models/foo_spec.rb
.
Finished in 0.00977 seconds
1 example, 0 failures
Hope it helps.
I think maybe FactoryGirl changes since this answer was posted. I did to make it work
Factory.define do
factory :foo, :class => Bar::Foo do |f|
f.name 'Foooo'
end
end
With the current latest version of FactoryGirl (4.5.0), this is the syntax:
FactoryGirl.define do
factory :client1_ad, class: Client1::Ad do |f|
f.title "software tester"
f.description "Immediate opening"
end
end
Notice that client1_ad can be whatever name you want coz we already force identifying its class name.
I found this question looking into a related issue with FactoryGirl and after a little reading of the source I figured out I could solve my problem by renaming my factories.
When I had model classes that were namespaced inside modules, eg: Admin::User I should have been defining my factories like this:
factory :'admin/user', class: Admin::User do
#...
end
Rather than:
factory :admin_user, class: Admin::User do
#...
end
Maybe this little tidbit of info might help someone someday. The specifics of my issue was that I was trying to use build(described_class) to build instances from factories in my RSpec specs and this works just fine with un-namespaced classes but not with classes inside modules. The reason is that internally when looking up factories FactoryGirl will use ActiveSupport's underscore helper to normalise the factory name.
Have you tried passing the actual class, rather than a string with the class name:
Factory.define :client1_ad, :class => Client1::Ad do |ad|

Default value not populating in migration with Rails and Postgresql

I am currently trying to run this migration:
class AddDroppedProjectsCountToUser < ActiveRecord::Migration
def self.up
add_column :users, :dropped_projects, :integer, {:default=>0, :required=>true}
end
def self.down
remove_column :users, :dropped_projects
end
end
The column is added correctly, but none of the old records are populated with 0. They are nil. I have tried using default=>'0' as well, to no avail. Any idea why this might be happening? (Rails 3.0.3)
Edited to add: When I create a new user it works fine, and everything looks correct. It's just the old users that still have nil for that value in the table.
What happens if you say:
def self.up
add_column :users, :dropped_projects, :integer, :null => false, :default => 0
end
instead? Without the :null=>false you're still allowing NULL in dropped_projects so there's no reason for PostgreSQL to make them 0. Also, I don't think :required is a valid option for add_column; since the options are just a simple Hash and add_column only looks for options it knows about, your stray :required option is silently ignored.
you could do this:
(taken from http://apidock.com/rails/ActiveRecord/Migration)
Using a model after changing its table
Sometimes you’ll want to add a column in a migration and populate it
immediately after. In that case, you’ll need to make a call to
Base#reset_column_information in order to ensure that the model has
the latest column data from after the new column was added. Example:
class AddPeopleSalary < ActiveRecord::Migration
def up
add_column :people, :salary, :integer
Person.reset_column_information
Person.all.each do |p|
p.update_column :salary, SalaryCalculator.compute(p)
end
end
end
I believe this is due to the fact that you are changing the old migration, instead of creating a new.
In this case, the solution is to check the schema file (schema.rb). It does not change automatically, and add
t.integer "dropped_projects", default: 0, null: false