Tests within Gem need to test migration generator and apply migrations for tests - ruby

I'm working on a Gem that contains a migration generator and a bunch of models, classes, etc that utilize the tables created as part of the migration.
Although testing the migration generator itself is easy enough - there's loads of tutorials around for getting that done, what i'm trying to work out is how to actually run the migration on the test DB so I can later test how the gem interacts with the test data?
Since the gem doesn't have a schema.rb, I wasn't sure how to go about doing it.

This is how I run migrations in code;
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
ActiveRecord::Migration.verbose = false
#migration = Class.new(ActiveRecord::Migration) do
def change
create_table :users, :force => true do |t|
t.string :roles_mask
end
create_table :user_without_roles, :force => true do |t|
t.string :roles_mask
end
create_table :user_without_role_masks, :force => true do |t|
end
end
end
#migration.new.migrate(:up)
If you have a string containing your generated migration you could do something like this in your test setup;
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
ActiveRecord::Migration.verbose = false
# Or however you intend to grab the output of the migration generator
migration_string = ERB.new(File.read(<file name here>)).result
migration = Class.new(ActiveRecord::Migration)
migration.class_eval(migration_string)
migration.new.migrate(:up)
That should give you a migrated database using the migration you've generated.

You can do something like this:
I am going to assume you're using ActiveRecord. So in your test helper you should setup a in-memory database:
require 'active_record'
# Connection must be establised before anything else
ActiveRecord::Base.establish_connection(
:adapter => defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3',
:database => ':memory:'
)
Then invoke the rake task within your test. This would look something like:
require 'rake'
requie File.expand_path('../Rakefile', __FILE__) # you'll need to modify this path to actually point to the Rakefile
Rake::Task['db:migrate'].invoke
The rake taks invocation is untested, but that should point you in the right direction.
The other option would be to just run the command:
%x{rake db:migrate}

Related

Codeship, Sinatra, ActiveRecord - configuration

I have a sinatra app working on my computer and am trying to get the tests to run on Codeship. I've reduced it to a tiny subset to see if I can sort out the problem I am having. I would greatly appreciate another pair of eyes...
Here are the key files:
Rakefile:
require 'sinatra/activerecord/rake'
require 'rake/testtask'
require_relative "demo_app"
Rake::TestTask.new do |t|
t.pattern = "test/*_test.rb"
end
database.yml
development:
adapter: sqlite3
database: db/development.sqlite3
test:
adapter: sqlite3
database: db/test.sqlite3
production:
url: <%= ENV['DATABASE_URL'] %>
demo_app.rb:
require 'sinatra/activerecord'
require './models/event'
require './models/person'
require './models/registration'
require 'pry-byebug'
migrations:
class CreateEvents < ActiveRecord::Migration
def change
create_table :events do |t|
t.string :name
t.date :date
end
end
end
test_helper.rb:
ENV['RACK_ENV'] = "test"
ENV["SINATRA_ENV"] = "test"
require_relative '../demo_app'
require 'minitest/autorun'
require 'rack/test'
ActiveRecord::Migration.maintain_test_schema!
event_test.rb:
require_relative './test_helper.rb'
describe Event do
it "can add events" do
Event.create(name: "An Event")
Event.create(name: "Another Event")
Event.all.size.must_equal 2
end
end
So, with that context I setup Codeship. Here are the key settings there:
Setup commands:
rvm use 2.2.2
bundle install
RACK_ENV=test bundle exec rake db:migrate
Test Pipeline (1 of 1)
rake test
And now when I push to git, and codeship picks it up I get this error on line 1 of demo_app.rb which is a require to sinatra/activerecord:
rake aborted!
Database URL cannot be empty
.....
It seems that for some reason, it is not paying attention to the fact that I am running in test mode and so it should use the test clause of database.yml.
Thoughts?

Sinatra db table not created after migration

I am trying to create active record using Sinatra and Sinatra/active record, my migrations are getting migrated to the Postgres database but tables are not getting created in the database, I went through all the possible solutions on stack overflow but its of no use. I tried even deleting my migration files from db/migrate folder but still the same output. What must be the error
Gemfile
source 'https://rubygems.org'
gem "sinatra"
gem "pg" #for postgres
gem "activerecord"
gem "sinatra-activerecord"
config.ru
require "./app"
run Sinatra::Application
rakefile.rb
require "./app"
require "sinatra/activerecord/rake"
app.rb
require 'sinatra'
require 'sinatra/activerecord'
db = URI.parse('postgres://project1:project1#localhost/*****')
ActiveRecord::Base.establish_connection(
:adapter => db.scheme == 'postgres' ? 'postgresql' : db.scheme,
:host => db.host,
:username => db.user,
:password => db.password,
:database => db.path[1..-1],
:encoding => 'utf8'
)
class Note < ActiveRecord::Base
end
class CreateNotes < ActiveRecord::Migration
def up
create_table :notes do |t|
t.string :title
t.text :body
t.timestamps
end
end
def down
drop_table :notes
end
end
output of migration
user#user-Inspiron-5520:~/rails-apps/project1$ rake db:migrate
== 20150704053019 CreateNotes: migrating ======================================
== 20150704053019 CreateNotes: migrated (0.0000s) =============================
db output (psql)
\dt
List of relations
Schema | Name | Type | Owner
--------+-------------------+-------+-----------
public | schema_migrations | table | project1
(1 row)
project1=# select * from schema_migrations;
version
----------------
20150704053019
(1 row)
note: Project1 user is a super user with all privileges
EDIT
Migrations file 20150704053019_create_notes.rb
class CreateNotes < ActiveRecord::Migration
def change
end
end
I would first like to note that #limekin was the first to get the answer, commenting:
The migration you are using to create notes table is actually kept in app.rb. But the migrate task looks for migrations inside db/migrate. If the migration file you gave at the bottom is inside the right directory, then move the migration definition from app.rb to there.
I will just go into a bit more detail. The function that you are using to create the table belongs in the migration file, because a migration is what creates, changes and deletes tables, columns, and records.
So to solve your issue just move the function up in your app file into the change function in your migration file.

Turning off logging output in activerecord sqlite3 adaptor in ruby

I use the ActiveRecord-gem for accessing a sqlite3-database, all without Rails. I use it like this:
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3", :database => name_file )
ActiveRecord::Schema.define do
if ! table_exists? "db_table"
do_something
end
end
And then I get an output like
-- table_exists?("db_table")
-> 0.0048s
Which I don't want. According to different posts, a simple
ActiveRecord::Base.logger = nil
should do the trick. But this is already nil...
Those look like migration messages. This should help:
ActiveRecord::Migration.verbose = false

Generate migrations outside Rails

I'm using ActiveRecord outside Rails. I would want a program to generate the skeleton of a migration ( as well as a system to collect and maintain them ).
Can anyone make a suggestion?
Also take a look at new active_record_migrations
There is a gem to use Rails Database Migrations in non Rails projects. Its name is "standalone_migrations"
Here is a link
https://github.com/thuss/standalone-migrations
If you do not like to use rake, but still get the system part of ActiveRecord::Migration, then you can use the following to handle the ups and downs from plain ruby (without any rails):
require 'active_record'
require 'benchmark'
# Migration method, which does not uses files in db/migrate but in-memory migrations
# Based on ActiveRecord::Migrator::migrate
def migrate(migrations, target_version = nil)
direction = case
when target_version.nil?
:up
when (ActiveRecord::Migrator::current_version == target_version)
return # do nothing
when ActiveRecord::Migrator::current_version > target_version
:down
else
:up
end
ActiveRecord::Migrator.new(direction, migrations, target_version).migrate
puts "Current version: #{ActiveRecord::Migrator::current_version}"
end
# MigrationProxy deals with loading Migrations from files, we reuse it
# to create instances of the migration classes we provide
class MigrationClassProxy < ActiveRecord::MigrationProxy
def initialize(migrationClass, version)
super(migrationClass.name, version, nil, nil)
#migrationClass = migrationClass
end
def mtime
0
end
def load_migration
#migrationClass.new(name, version)
end
end
# Hash of all our migrations
migrations = {
2016_08_09_2013_00 =>
class CreateSolutionTable < ActiveRecord::Migration[5.0]
def change
create_table :solution_submissions do |t|
t.string :problem_hash, index: true
t.string :solution_hash, index: true
t.float :resemblance
t.timestamps
end
end
self # Necessary to get the class instance into the hash!
end,
2016_08_09_2014_16 =>
class CreateProductFields < ActiveRecord::Migration[5.0]
# ...
self
end
}.map { |key,value| MigrationClassProxy.new(value, key) }
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'XXX.db'
)
# Play all migrations (rake db:migrate)
migrate(migrations, migrations.last.version)
# ... or undo them (rake db:migrate VERSION=0)
migrate(migrations, 0)
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
class SolutionSubmission < ApplicationRecord
end
I have made a minimal example of how to use active record outside of Rails. Rails migrations (Standalone migrations) in non-Rails (and non Ruby) projects.
https://github.com/euclid1990/rails-migration
(Support Rails >= 5.2)
You can refer Rake file in this repo.
There is another gem called otr-activerecord. This gem provides following tasks:
rake db:create
rake db:create_migration[name]
rake db:drop
rake db:environment:set
rake db:fixtures:load
rake db:migrate
rake db:migrate:status
rake db:rollback
rake db:schema:cache:clear
rake db:schema:cache:dump
rake db:schema:dump
rake db:schema:load
rake db:seed
rake db:setup
rake db:structure:dump
rake db:structure:load
rake db:version
All you need to do is to install it and add a Rakefile with content
load 'tasks/otr-activerecord.rake'
OTR::ActiveRecord.configure_from_file! 'config/database.yml'
I prefer this gem over active_record_migrations or Standalone Migration because those two gems depend on railties, which is almost the entire Rails. For example, Nokogiri takes a long time to compile, and takes a lot of spaces.

ActiveRecord and sqlite3 : find does not accept any condition?

I have a problem I can't figure out here. I'm writing a ruby script that deals with an sqllite database.
require 'rubygems'
require 'sqlite3'
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database => "../database/my.db"
)
class KeyWord < ActiveRecord::Base
set_table_name "keywords"
end
# THIS STATEMENT WORKS (finds the first record, returns "ruby") :
KeyWord.find(1).keyval
# THOSE STATEMENTS RETURN NO RESULT :
KeyWord.find(:all, :conditions => {:keyval => "ruby"})
KeyWord.find_by_sql("SELECT * FROM keywords WHERE keyval='ruby'")
KeyWord.find_by_keyval("ruby")
This is how the table was created :
create_table :keywords do |table|
table.column :keyval, :text
end
Does anyone know where this could come from ?
Thanks,
R.
I see a couple of issues here.
I'm not sure why you're manually setting your table name.
ActiveRecord assumes that your model name is camelCased. So... AR
would, by default, search for a table called key_words. Why not just
go with that?
Pay attention to which version of active record you are
using. Passing in conditions is deprecated. You should be using the
.where syntax. So... you would need to do KeyWord.where(:keyval
=> 'ruby').first or end in .all for a collection of results.
If you are just fooling around, you can use sqlite3 in memory.
ActiveRecord::Base.establish_connection( adapter: 'sqlite3',
database: ":memory:")
Also don't forget to define your schema!
Here is full code w/ more modern syntax.
require 'rubygems'
require 'sqlite3'
require 'active_record'
ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ":memory:" )
ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define(version: 2) do
create_table :key_words do |t|
t.text :keyval
end
end
class KeyWord < ActiveRecord::Base
end
Keyword.create!(:keyval => 'ruby')
# THESE STATEMENTS WORK:
KeyWord.find(1).keyval
KeyWord.where(:keyval => 'ruby').first
KeyWord.where(:keyval => 'ruby').all
KeyWord.find_by_sql("SELECT * FROM key_words WHERE keyval='ruby'")
KeyWord.find_by_keyval("ruby")

Resources