I'm looking to write some unit tests that will let me build/test non-stubbed CRUD functions like I can in Rails but I want to use a minimal number of gems (test unit&active record).
Anyone know of any resources that might help?
Thanks
Don't know of any resources specifically about this. To get active record working in a simple test you would just need to set up the connection details and your model classes assuming you have an existing database to work with that matches the active record conventions. Don't know what stage you are at but if you just need a simple example:
require 'rubygems'
require 'active_record'
require 'test/unit'
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'db/my.db'
)
# Assuming a table like:
# CREATE TABLE people (id INTEGER PRIMARY KEY, name VARCHAR(100) NOT NULL, age INTEGER NOT NULL);
class Person < ActiveRecord::Base
end
class TestActiveRecord < Test::Unit::TestCase
def setup
#bob = Person.create(:name => 'Bob', :age => 95)
end
def teardown
#bob.destroy
end
def test_find_bob
bob = Person.find_by_name('Bob')
assert_not_nil(bob)
assert_equal(95, bob.age)
end
end
There are no other gems involved here other than those that active record itself depends on.
You'll have to work out what configuration settings you need depending on the type of database adapter you are using. If your database schema doesn't conform to the active record conventions then you will also have to specify some mappings in your model classes.
Related
I've been looking at this repository
https://github.com/stungeye/ActiveRecord-without-Rails to understand how can I implement activerecord without rails.I got some problems. At first I got this error when I tried to run this class:
require 'active_record'
ActiveRecord::Base.establish_connection(adapter: 'mysql2', database: 'rbuserroom')
# Can override table name and primary key
class User < ActiveRecord::Base
self.primary_key = 'user_id'
def initialize(id, email)
#user_id = id
#user_email = email
#user_room
end
def create()
self.save
end
# accessor get and set method
attr_accessor :user_room
attr_reader :user_id, :user_email
end
usr = User.new(1, "user#user")
usr.create()
but I got this error:
Traceback (most recent call last):
1: from -:25:in `<main>'
/home/felipe/.rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/inheritance.rb:52:in `new': wrong number of arguments (given 2, expected 0..1) (ArgumentError)
it seems that active record doesn't accept the parameters in the creation of the class, in fact after that i noticed that the classes in this example don't contain anything inside, how would active record define the columns of the tables?
i'm used to java jpa and springboot that i have to define all the attributes of the class.
besides i don't know if the active record is really working.
I just want that when I create a new user with my user class, the information persists in the database as an insert, or that it updates when I make a change to my object attribute value.
With ActiveRecord you don't need to specify the column names. It detects them from the DB.
You can just write:
require 'active_record'
ActiveRecord::Base.establish_connection(adapter: 'mysql2', database: 'rbuserroom')
class User < ActiveRecord::Base
self.primary_key = 'user_id'
end
usr = User.create(user_id: 1, user_email: "user#user")
You can read more about creating models in the docs. Especially in 3 Creating Active Record Models
My environment: Ruby 1.9.2p290, Rails 3.0.9 and RubyGem 1.8.8
unfortunately I have an issue when come across multiple database.
The situation is this: I have two model connect with two different database and also establishing association between each other.
database connection specifying in each model, look likes
class Visit < ActiveRecord::Base
self.establish_connection "lab"
belongs_to :patient
end
class Patient < ActiveRecord::Base
self.establish_connection "main"
has_many :visits
end
I got an error when meet following scenario
#visits = Visit.joins(:patient)
Errors: Mysql2::Error: Table 'lab.patients' doesn't exist: SELECT visits.* FROM visits INNER JOIN patients ON patients.id IS NULL
Here 'patients' table is in 'main' database and 'visits' table in 'lab' database
I doubt when executing the code, that Rails is considering 'patients' table is part of 'lab' database [which holds 'visits' table].
Well, I don't know if this is the most elegant solution, but I did get this to work by defining self.table_name_prefix to explicitly return the database name.
class Visit < ActiveRecord::Base
def self.table_name_prefix
renv = ENV['RAILS_ENV'] || ENV['RACK_ENV']
(renv.empty? ? "lab." : "lab_#{renv}.")
end
self.establish_connection "lab"
belongs_to :patient
end
class Patient < ActiveRecord::Base
def self.table_name_prefix
renv = ENV['RAILS_ENV'] || ENV['RACK_ENV']
(renv.empty? ? "main." : "main_#{renv}.")
end
self.establish_connection "main"
has_many :visits
end
I'm still working through all the details when it comes to specifying the join conditions, but I hope this helps.
Might be cleaner to do something like this:
def self.table_name_prefix
"#{Rails.configuration.database_configuration["#{Rails.env}"]['database']}."
end
That will pull the appropriate database name from your database.yml file
Or even
def self.table_name_prefix
self.connection.current_database+'.'
end
Is your 2nd database on another machine? You can always do as suggested in this other question:
MySQL -- Joins Between Databases On Different Servers Using Python?
I'd use the self.table_name_prefix as proposed by others, but you can define it a little more cleanly like this:
self.table_name_prefix "#{Rails.configuration.database_configuration["#{Rails.env}"]['database']}."
alternatively you could also use this:
self.table_name_prefix "#{connection.current_database}."
You have to keep in mind that the latter will execute a query SELECT DATABASE() as db the first time that class is loaded.
I am trying to seed my database with some dummy data, so I have this seed file:
db/seeds.rb
require 'require_all'
require_all 'lib'
Author.create(name: "Mark Twain")
My Ruby model and relevant methods:
lib/author.rb
class Author
attr_accessor :name, :id
def initialize(name, id=nil)
#name = name
#id = id
end
def self.make_object_from_row(row)
# [1, "Mark Twain"]
Author.new(row[1], row[0])
end
def self.create(name)
author = Author.new(name)
author.save
end
def save
if self.id.nil? # doesn't exist in the database yet
sql = <<-SQL
INSERT INTO authors (name)
VALUES (?)
SQL
DB.execute(sql, self.name)
sql = "SELECT last_insert_rowid()"
self.id = DB.execute(sql)[0][0]
else # just update the row in the db
sql = <<-SQL
UPDATE authors SET (name) = ? WHERE id = ?
SQL
DB.execute(sql, self.name, self.id)
end
end
Rakefile
require_relative './config/environment'
desc "Set up database"
task :db_setup do
author_sql = <<-SQL
CREATE TABLE IF NOT EXISTS authors(
id integer PRIMARY KEY,
name varchar(255)
);
SQL
DB.execute(author_sql)
end
desc "Seed database"
task :db_seed do
ruby "db/seeds.rb"
end
config/environment.rb
require 'bundler/setup'
Bundler.require
# setting up the database connection (old way)
DB = SQLite3::Database.new("db/development.db")
require_relative '../lib/author.rb'
When I run the rake task for db_seed I get the error . lib/author.rb:26:in save': uninitialized constant Author::DB (NameError)
The db_setup Rake task works fine. Also if I go into a pry from my console, I can instantiate a new Author without a problem (and it writes to the database). If I run the seed file from my command line, I get the same error.
I see that it's looking for an attribute DB on the Author class, but I don't see why, or why it's inconsistent in that I can create an Author from the command line but not from the Rake task--if the variable were undefined that shouldn't make a difference, correct?
(I'm also aware that using ActiveRecord would be much easier, but I'm not looking to use it right now)
When you see errors like "uninitialized constant" popping up and you're sure you've defined that constant in a file somewhere, make sure you're loading that code in before the method with the error runs.
It looks like in this case config/environment wasn't loaded before DB was referenced, so it can't complete.
Due to how Ruby searches for constants it's presented as Author::DB because the code was running inside of the Author namespace and that's where searches start.
I would like to remove column from the table by using Active record. Please find the below snippet
require "active_record"
require 'sqlite3'
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => 'test_one')
class Account < ActiveRecord::Base
table_name = "AccountBean"
primary_key = "process_id"
remove_column "xxx" // I need this type of method to remove column "xxx" from accounts table
end
Is there any class method in ActiveRecord which satisfy this requirement ?
I guess ActiveRecord presumes that changes to structure shall be done using migrations.
If you really need to, you could use the same methods rails uses in migrations to e.g. remove a column - like here
I don't recommend this :)
ActiveRecord::Base.connection.remove_column("persons", "first_name")
Within a class that would look something like:
class Account < ActiveRecord::Base
table_name = "AccountBean"
primary_key = "process_id"
connection.remove_column(table_name, "xxx")
end
This is my first time using ActiveRecord in a non-rails application, and I'm running into a problem. ActiveRecord is able to figure out the columns I have in my sqlite3 database, but it can't figure out the default column values for some reason.
Here is the sql description for the table in question.
-- Describe ACCOUNTS
CREATE TABLE "accounts" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL DEFAULT (0),
"username" TEXT NOT NULL,
"password_hash" BLOB NOT NULL,
"creation_time" INTEGER NOT NULL DEFAULT(strftime('%s', 'now')),
"expiration_time" INTEGER NOT NULL DEFAULT(strftime('%s', 'now') + 2592000)
)
I used the following code for loading my database file.
require 'active_record'
require './config.rb'
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => DB_FILE
)
class Account < ActiveRecord::Base
end
When I look at the column defaults for the Account table with a REPL, this is what I get:
[10] pry(main)> Account.column_defaults
=> {"id"=>0,
"username"=>nil,
"password_hash"=>nil,
"creation_time"=>0,
"expiration_time"=>0}
I worked with ActiveRecord for a rails app before and it was smart enough to figure out the default values. For some reason, it can't figure them out now.
Am I doing something wrong here? I read that I can manually specify the default value with default :id => bla, but shouldn't ActiveRecord be able to figure out the defaults?
Update: I think I figured out a workaround. The hash returned by Account.column_defaults is writeable, and changing those elements seems to work fine.
Try doing this:
class Account < ActiveRecord::Base
after_initialize :default_values
private
def default_values
self.username ||= "default value"
#etc...
end
end