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…

  1. class Authentication < DataMapper::Base
  2.   property :email, :string
  3.   property :password, :string
  4.  
  5.   belongs_to :profile
  6. end
  7.  
  8. class Profile < DataMapper::Base
  9.   property :firstname, :string
  10.   property :lastname, :string
  11.   property :phone, :string
  12. 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:

  1. def save!
  2.   raise "blablabla Save problem" unless save
  3.   true
  4. end
  5.  
  6. def self.transaction(name, &amp;block)
  7.   begin
  8.     send(START TRANSACTION)
  9.     yield
  10.     send("COMMIT")
  11.   rescue Exception=>e
  12.     send("ROLLBACK")
  13.     raise e
  14.   end
  15. 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:

  1. class User < DataMapper::Base
  2.   property :name, :string
  3.   property :email, :string
  4. 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:

  1. 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:

  1. class User < DataMapper::Base
  2.   property :name, :string
  3.   property :email, :string
  4.   property :message, :string
  5. 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.