Can I change SQLite column name with DataMapper? - ruby

Brand new to DataMapper and wondering if I can use DataMapper.auto_updgrade! to change a column name of an existing column in a SQLite database?
If I have the following in a song.rb
require 'date'
require 'dm-core'
require 'dm-migrations'
DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/development.db")
class Song
include DataMapper::Resource
property :id, Serial
property :title, String
property :music_by, String
property :lryics_by, String
property :lyrics, Text
property :length, Integer
property :released_on, Date
def released_on=date
super Date.strptime(date, '%m/%d/%Y')
end
end
DataMapper.finalize
After a Song.auto_migrate!
2.0.0-p598 :004 > Song.new
=> #<Song #id=nil #title=nil #music_by=nil #lyrics_by=nil #lyrics=nil #length=nil #released_on=nil>
Is it possible to change the
property :lryics_by, String
to
property :words_by, String
and have the database column name change, but keep any existing data?
I've tried with Song.auto_upgrade! and it adds an empty new column and leaves the original column and data in place. On the other hand, my Song.new object looks right.
2.0.0-p598 :004 > Song.new
=> #<Song #id=nil #title=nil #music_by=nil #words_by=nil #lyrics=nil #length=nil #released_on=nil>
It seems like I need a migration in the way that ActiveRecord (I've played around a little with that ORM) handles migrations. Or I would need to change the column name with SQL or an app or the Firefox SQLlite plugin.
UPDATE:
I'm wondering now if this is more a SQLite thing than a DataMapper thing. When I went to delete a column in Firefox's SQLite Manager plugin I got this message:
This is a potentially dangerous operation. SQLite does not support statements that can alter a column in a table. Here, we attempt to reconstruct the new CREATE SQL statement by looking at the pragma table_info which does not contain complete information about the structure of the existing table.
Do you still want to proceed?

dm-migrations can do this but not for SQLite, mostly because SQLite doesn't support renaming columns, as it has a limited ALTER TABLE implementation (http://www.sqlite.org/lang_altertable.html)
There's a rename_column migration but as you can see from the dm-migrations TableModifier class (https://github.com/datamapper/dm-migrations/blob/master/lib/dm-migrations/sql/table_modifier.rb), it's not available for SQLite:
def rename_column(name, new_name, opts = {})
# raise NotImplemented for SQLite3
#statements << #adapter.rename_column_type_statement(table_name, name, new_name)
end

Related

How to implement Application Record in ruby without rails

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

Ruby class expects class variable but only in Rake task

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.

ActiveRecord is showing wrong column defaults

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

DataObject::Integrity error in DataMapper

I wrote an app where I plan to save user data via DataMapper and sqlite3, and inventory items (several of them) using GDBM. When I run this with irb, User.new and inventory methods works fine however DataMapper says that my user model is not valid and does not save it. I worked with DataMapper previously and never encountered such an error, in fact I used the same user model (with password salt and hash) without any errors in a sinatra-datamapper app.
To be clear, I intend to save both the username and 'the path to the gdbm database file' as a string in datamapper. The gdbm creates its own db file and all methods work fine without complaints and all data is persistent. Since I am not trying to save the gdbm object but only a string in datamapper, I think there should be no problem of validation.
DataMapper::Model.raise_on_save_failure = true does not give a specific description of the error but save! method gives the following error:
DataObjects::IntegrityError: users.name may not be NULL (code: 19, sql state: , query: INSERT INTO "users" DEFAULT VALUES, uri: sqlite3:/home/barerd/game_inventory/users.db?scheme=sqlite3&user=...... but name attribute is not empty as I checked in irb.
Maybe the error originates from sqlite but I have no knowledge to test that. Can someone guide me how to inspect this error?
require 'gdbm'
require 'data_mapper'
require 'dm-validations'
DataMapper.setup :default, 'sqlite://' + Dir.pwd + '/users.db'
class User
attr_reader :name, :inventory_db
include DataMapper::Resource
property id, Serial
property :name, String, :required => true, :unique => true, :messages => { :presence =>...... etc }
property :inventory_db, String
def initialize name
#name = name
#inventory_db = Dir.pwd + '/#{name}_inventory.db'
end
def inventory
GDBM.new #inventory_db, 0644, GDBM::WRCREAT
end
..several methods related to inventory..
end
DataMapper.finalize.auto_migrate!
Googling further, I found out that
there is a guard around defining property getter in dkubb/dm-core
via this link. After removing getter methods from the model, everything worked fine.

does a sequel models generator exists?

I'm looking for a ruby class that could generate the sequel model file for Ramaze after reading the definition of the table in a mySQL database.
For example, I would like to type :
ruby mySuperGenerator.rb "mytable"
And the result shold be the file "mytable.rb" in "model" directory, containing :
class Mytable < Sequel::Model(:mytable)
# All plugins I've defined somewhere before lauching the generator
plugin :validation_helpers
plugin :json_serializer
one_to_many :othertable
many_to_one :othertable2
def validate
# Generating this if there are some not null attributes in this table
validates_presence [:fieldthatshoulnotbenull1, :fieldthatshoulnotbenull2]
errors.add(:fieldthatshoulnotbenull1, 'The field fieldthatshoulnotbenull1 should not be null.') if self.fieldthatshoulnotbenull1.nil?
end
def before_create
# All the default values found for each table attributes
self.creation_time ||= Time.now
end
def before_destroy
# referential integrity
self.othertable_dataset.destroy unless self.othertable.nil?
end
end
Does someone knows if such a generator exists ?
Well...
I finally wrote my script.
see https://github.com/Pilooz/sequel_model_generator Have look and fork !

Resources