Tag Archives: ORM

DataMapper defaults to socket; causes error

Just started migrating a Ruby project I’m working on to DataMapper and I found an interesting error.


DataMapper.setup(:default, ENV['DATABASE_URL'])

Where my DATABASE_URL was set as an environment variable to something along the lines of the following:


DATABASE_URL=mysql://username:password@localhost:3306/database_name

The key point to note is that I’m trying to point it to TCP port 3306 on my local machine. When I’d trying to load up a model object and hit the database, I got the following error:


DataObjects::SQLError: Can't connect to local MySQL server through socket '/opt/local/var/run/mysql5/mysqld.sock' (2)

Full stack trace:

DataObjects::SQLError: Can't connect to local MySQL server through socket '/opt/local/var/run/mysql5/mysqld.sock' (2)
	/Library/Ruby/Gems/1.8/gems/data_objects-0.10.2/lib/data_objects/connection.rb:76:in `initialize'
	/Library/Ruby/Gems/1.8/gems/data_objects-0.10.2/lib/data_objects/connection.rb:76:in `send'
	/Library/Ruby/Gems/1.8/gems/data_objects-0.10.2/lib/data_objects/connection.rb:76:in `__new'
	/Library/Ruby/Gems/1.8/gems/data_objects-0.10.2/lib/data_objects/pooling.rb:177:in `new'
	/Library/Ruby/Gems/1.8/gems/data_objects-0.10.2/lib/data_objects/pooling.rb:172:in `synchronize'
	/Library/Ruby/Gems/1.8/gems/data_objects-0.10.2/lib/data_objects/pooling.rb:172:in `new'
	/Library/Ruby/Gems/1.8/gems/data_objects-0.10.2/lib/data_objects/pooling.rb:119:in `new'
	/Library/Ruby/Gems/1.8/gems/data_objects-0.10.2/lib/data_objects/connection.rb:65:in `new'
	/Library/Ruby/Gems/1.8/gems/dm-do-adapter-1.0.2/lib/dm-do-adapter/adapter.rb:235:in `open_connection'
	/Library/Ruby/Gems/1.8/gems/dm-do-adapter-1.0.2/lib/dm-do-adapter/adapter.rb:260:in `with_connection'
	/Library/Ruby/Gems/1.8/gems/dm-do-adapter-1.0.2/lib/dm-do-adapter/adapter.rb:138:in `read'
	/Library/Ruby/Gems/1.8/gems/dm-core-1.0.2/lib/dm-core/repository.rb:162:in `read'
	/Library/Ruby/Gems/1.8/gems/dm-core-1.0.2/lib/dm-core/model.rb:379:in `first'
	./routes/services.rb:11:in `GET /v1/:choice_id'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:1032:in `call'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:1032:in `compile!'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:620:in `instance_eval'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:620:in `route_eval'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:604:in `route!'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:656:in `process_route'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:653:in `catch'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:653:in `process_route'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:603:in `route!'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:602:in `each'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:602:in `route!'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:741:in `dispatch!'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:530:in `call!'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:706:in `instance_eval'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:706:in `invoke'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:706:in `catch'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:706:in `invoke'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:530:in `call!'
	/Library/Ruby/Gems/1.8/gems/sinatra-1.1.0/lib/sinatra/base.rb:516:in `call'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/showexceptions.rb:24:in `call'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/lint.rb:48:in `_call'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/lint.rb:36:in `call'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/showexceptions.rb:24:in `call'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/commonlogger.rb:18:in `call'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/content_length.rb:13:in `call'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/chunked.rb:15:in `call'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/handler/mongrel.rb:67:in `process'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:159:in `process_client'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:in `each'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:158:in `process_client'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:285:in `run'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:285:in `initialize'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:285:in `new'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:285:in `run'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:268:in `initialize'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:268:in `new'
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb:268:in `run'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/handler/mongrel.rb:38:in `run'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/server.rb:213:in `start'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/lib/rack/server.rb:100:in `start'
	/Library/Ruby/Gems/1.8/gems/rack-1.2.1/bin/rackup:4
	/usr/bin/rackup:19:in `load'
	/usr/bin/rackup:19

Despite the fact that I was telling it to go after a TCP port, it was attempting to connect to MySQL via a socket (the reason it was getting the error is it was looking for the socket at /opt/local/var/run/mysql5/mysqld.sock and mine was configured /tmp/mysql.sock — but that’s not the point, it shouldn’t have been looking there to begin with

Through trial and error I found that it appears that DataMapper automatically falls back to socket based communication with MySQL when you specify localhost. I was able to correct the error by specifying an IP address instead:


DATABASE_URL=mysql://username:password@127.0.0.1:3306/database_name

Mapping Enums to custom strings in NHibernate

Many times when working with a legacy relation model and a newly developed C# object model that sits on top of the relational model, you may need to map enum values to/from arbitrary strings in the database using NHibernate.

For example, suppose you have a a work order object that three possible states: request, approved, denied. To represent this, you might use the following enum:

enum WorkRequestState
{
    Request,
    Approved,
    Denied
};

But let’s assume that the legacy relational model has represented these values as strings: REQ, APR, DEN. You can’t change the values for the relational model, and you certainly don’t want to use these values in your enum because they are not as readable as the values shown above.

By default, NHibernate will automatically convert you enum to a string value that matches the name of the enum values if the table field a string, or it will convert to the integer value for the enum options if the table field is an integer.

To do the above custom mapping we must implement a custom NHibernate type.

First, create a new class that inherits NHibernate.Type.EnumStringType. In your constructor, pass the type of the enum you are handling, and the maximum number of characters that the enum values will be to the base class constructor:

class WorkRequestStateEnumStringType : NHibernate.Type.EnumStringType
{
    public WorkRequestStateEnumStringType()
        : base( typeof(WorkRequestState), 3)
    {
    }
    ...
}

Next, override the GetValue(...) method to map the enum value to the equivalent string.

public override object GetValue(object enm)
{
	if( null == enm )
		return String.Empty;

	switch( (WorkRequestState)enm )
	{
		case WorkRequestState.Request	: return "REQ";
		case WorkRequestState.Approved	: return "APR";
		case WorkRequestState.Denied	: return "DEN";
		default : throw new ArgumentException("Invalid WorkRequestState.");
	}			
}

Override the GetInstance(...) method to map a string value to the equivalent enum value:

public override object GetInstance(object code)
{
	code = code.ToUpper();

	if( "REQ".Equals(code) )
		return WorkRequestState.Request;
	else if( "APR".Equals(code) )
		return WorkRequestState.Approved;
	else if( "DEN".Equals(code) )
		return WorkRequestState.Denied;

	throw new ArgumentException(
		"Cannot convert code '" + code + "' to WorkRequestState.");
}

Finally, in the NHibernate mapping file, specify the property’s type as the derived EnumStringType class.

<class name="WorkRequest" table="work_request_table">
  <property
    name="Status"
    column="status_field"
    type="my.namespace. WorkRequestStateEnumStringType, MyAssembly" />
  ...
<class>

Note that when specifying this type, you must use the assembly-qualified name because the NHibernate code doing the mapping is in a separate assembly from the type you created.

This information has been derived from Jeremy Miller’s original post here.