Datamapper and transactions :(
Posted by Nucc
I have a new project idea, and I decided to create it by Merb and Datamapper. I’ve two model, Authentication and Profile model. The problem, DM doesn’t have transaction controlling, but it’s very necessary…
-
class Authentication < DataMapper::Base
-
property :email, :string
-
property :password, :string
-
-
belongs_to :profile
-
end
-
-
class Profile < DataMapper::Base
-
property :firstname, :string
-
property :lastname, :string
-
property :phone, :string
-
end
So we have an Authentications table, which contains email and password pairs and a pointer to a Profiles DB element. Let’s see a problem with Datamapper. There are no transaction controlling yet.
Put the case that, come a new visitor, and try to register. Add an email, password, and his profile. We create a Profile object with his params, and save it, because in Auth object we have to set saved object for profile pointer. The password not too secure, so Auth throw invalid exception, we can’t save it. There is a profile without auth. Okay, if Auth throw invalid message, delete profile. Is it solution? No.. If only you use this db, it may be a solution. But if sy create a pointer for your new profile, then database won’t delete your profile object because a DB element is pointing to it.
Okay, if we check validation before each model’s save method, it solves some problem, but good only for validation problems. Let’s see, how ActiveRecord solution. AR has transaction controlling. Every (the most) DB has transaction controlling, because it’s base. AR is capable throw Exception, when a save, or destroy method gets an error (DM only returns true or false). We do a transaction block, and if sg throw exception, we call a ROLLBACK, or if everything okay, we call COMMIT. There’s a schema:
-
def save!
-
raise "blablabla Save problem" unless save
-
true
-
end
-
-
def self.transaction(name, &block)
-
begin
-
send(START TRANSACTION)
-
yield
-
send("COMMIT")
-
rescue Exception=>e
-
send("ROLLBACK")
-
raise e
-
end
-
end
It’s only an idea, but what happens, when one transaction block contains another? If we do a commit inside another transaction, and the other transaction do a Rollback, then delete committed rows?
Merb - First steps
Posted by Nucc
I’ve read a lot of Merb, so I decided to try it. First it seems to be very poor framework, and it’s true. It’s only a skeleton unlike rails. First I tried
script/generate model User name:string email:string
and I got a pure User class without Datamapper, ActiveRecord, or other ORM. After googling I found the solution, it need to set use_orm :datamapper in dependencies.rb. For datamapper install use this:
sudo gem install datamapper sudo gem install merb_datamapper sudo gem install do_mysql
It’s okay, we generate again our model file.
script/generate model User name:string email:string
The result:
-
class User < DataMapper::Base
-
property :name, :string
-
property :email, :string
-
end
Okay, it’s nice, but how can we migrate with the database? We have a config/database.sample.yml file like in rails, edit this. It’s very nice, in Rails we have to set username, pass, database for all environment, here we can use inheritance.
:development: &defaults :adapter: mysql :database: merb_test :username: root :password: :host: localhost :test: <<: *defaults #:database: sample_test: production: <<: *defaults #:database: sample_production
Okay, it’s nice, try to migrate. … I’ve need some time, to solve this very simple problem. In rails, we have rake db:migrate, but in merb it’s not, or not ready yet. Okay, no problem, I found this solution:
In config/merb_init.rb insert this:
-
DataMapper::Persistence.auto_migrate!
It will migrating our models with database before merb server start. Let’s look an example. If we want to add a column to our User table, we only need to set another property tag, like:
-
class User < DataMapper::Base
-
property :name, :string
-
property :email, :string
-
property :message, :string
-
end
After a restart Users table looks:
desc users; +---------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(50) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | | message | varchar(50) | YES | | NULL | | +---------+-------------+------+-----+---------+----------------+
Nice. But! I’ve some problems. How can we manage, when new columns have an initialize value? How can we rename row? It’s very good for smaller projects, but what we can do, if we have an already exists database with a lot of data? It’s not trivial for me?
I shouldn’t remove migrating, but change it. I forget the numbers before the migration name, and use creation date for order them. Every migration file should has a creation date, and migration script use it for order them. It’s only an idea.
Another problem in Merb, controllers don’t get Controller suffix in class name. It seems to comfortable, but what happens, if we have a User model, and we want to use an User controller to manage our model. It’s problem. Need to use different namespaces for models and controllers, or use suffix or prefix in controllers like rails.