This isn’t going to be a rant about ORM, more of a statement of my own problem I’m trying to solve. With the internal work sales application that I develop, I have the problem of not being able to specify business logic in one place. It seems to be a general rule with ORM systems (at least with my very small experience with Rails), that business logic all goes in the ORM layer.
class PurchaseOrder < ActiveRecord::Base include Shared::TracksSerialNumberExtension order_lines :received validates_presence_of :supplier, :despatch_by validates_inclusion_of :ongoing, :in => [true, false] validates_date :created_at owned_by :supplier add_filter :ongoing tracks_serial_numbers :items sendable :supplier attr_accessor :force_create # This is if the user wants to create a new # order, even though an ongoing order may # exist. validate_on_create :check_for_ongoing validate_on_update :ongoing_set_on_create_only def initialize( params = nil ) super self.created_at ||= Time.now.to_date self.ongoing ||= false self.despatch_by ||= 'Most Economical' self.force_create ||= false end ...
This is pretty typical in Rails, and after all the model object needs to know these sorts of things so it knows how it’s all supposed to fit together. That’s fine. The problem is when you do what I like to do, and actually use the database as, well, a database.
It’s the job of the database to enforce data integrity, this includes things like making sure invalid data doesn’t get in. So those “validates_” bits, they also need to go in the database, not only that, but if you want to have a nice interface that shouldn’t piss off your users, you need those validates in your UI code as well.
So, the problem should be apparent, three separate layers need to follow the same rules, clearly a violation of DRY. That’s the problem, each layer needs to know the business rules and to do their bit to follow those rules. The UI needs to give the user feedback as quickly as possible about any errors, the application layer needs to know how everything fits together, and the database needs to make sure that the data is correct completely independently from the other layers.
So, we need a way to have business logic in a way that all three layers can use, in one place to make updates easy and as error free as possible. ORM doesn’t do that, I want something that does. I want to be able to define my business logic in one place, and to have it taken care of across all three layers.
Of course it has to be flexible enough that you can override anything you need to, otherwise you end up stuck in a framework like Rails and ActiveRecord and it becomes a pain to do anything outside of that.
I’ve been reading lately, and by lately I mean my very long back log of Reddit. That there seems to be a few noises being made about ORM, and I thought I better just write a little note to myself in the form of this blog post.
Except for the case where you’re writing a small, quick, throwaway program, in which case you should then admit that in most cases no such thing exists and you should be writing it properly. ORM is just a bad idea, ORM is just a case of programmers wanting to fit something into an easier way for them to handle databases. Isn’t that something that we should be doing anyway? We do it all the time. Yes, but, and you knew a but was coming. The correct abstractions should be applied, and showhorning an object model onto something that is relational data is going to be a problem.
Learn SQL, fuck it, pick a database and learn to use that. Use triggers, don’t use the database as a dumbstore!
Obviously, this post is all opinion and no substance, but I’m going to see about coming back and fixing it later on, I have to work.
I’ve previously blogged about my migration from Windows to OSX. A problem I have found, is that OSX is not really the best environment for how I prefer to organise my music collection. It seems that the software just isn’t quite right, however, first, an explanation of my setup.
To keep my music safe, and to save on my desktop hard drive space, all my music is copied from my CDs to my storage server (which runs Solaris). This is because Solaris has ZFS, which is an absolutely brilliant filesystem. Nothing else offered is as easy, nor as mature as the ZFS filesystem. For example, I had recently needed to upgrade my storage.
Normally something like this would involve backing the data somewhere else, installing the new drives, and copying the data back again. This approach has always been a little problematic, especially with larger and larger harddrives. However, with ZFS upgrading harddrives becomes an easy and safe operation.
Since I was more interested in keeping my data secure, I decided to go with a four drive mirrored zpool: Two 1GB drives, and two 500GB drives, for a total of 1.5TB. Such a configuration is not really about maximising drive space, since you only get half the actual space of the drives. However, it allows me to survive two drive failures (as long as both drives that fail aren’t the paired ones), and also means that if I want to upgrade the space, I need only replace two drives.
So, I needed to upgrade my storage, so how did I do this with ZFS? Basically I bought two 2GB drives, and first replaced one of the 500GB drives and booted. Then all I had to do was run the ‘zpool replace’ command (with the correct arguments of course), wait for the drive to re-silver, shutdown, replace the other 500GB hard drive, again ‘zpool replace’.
Once the re-silver there has finished, it’s a matter of ‘zpool autoexpand=on storage’ and I now have 3TB of storage space. The advantage of a mirrored ZFS config like this, means it’s much easier to expand in the future. No mucking about copying data from one place to the other (while also worrying that it may get corrupted in the transfer).
First off, I use dbPowerAmp for ripping my CDs, since this is a very nice ripper, I don’t plan on changing it any time soon. So I will still have to boot into Windows to use it.
I organise my music files in a particular way, just because I find it easier to keep track of them. There is never one true way, but this is how I do it:
Music/Artist/Album/Disc <disc>/<track> - <track name>.m4a
Music that is only on one disc skips creating a “Disc 1″ directory.
Now this isn’t all done by hand, what I actually do (ensuring that anything I rip has the correct tag information) is use the best player on Windows (actually the best player on any operating system) Foobar. With the file operations module installed (an option when you install it), you can write a little bit of code so it will correctly move/copy files based off the tag information.
The one I currently use is:
$if(%tracknumber%,$if2($trim(%album artist%),Unknown Artist)/$if(%album%,$ifgreater(%totaldiscs%,1,$trim(%album%/Disc %discnumber%),$trim(%album%)),Unknown Album)/$trim(%tracknumber% - %title%),%album artist%/%title%)
However, I still need to refine it.
Next, I need to make sure that I have album art, and soundcheck data for the tracks. Now iTunes will calculate soundcheck data for tracks, however it’s all based on a per-track basis, which really sounds horrible if you’re listening to a continuous mix album. What you really need, is album level replaygain.
Again, foobar to the rescue, the replaygain functions of foobar will allow you to calculate the album replaygain. Unfortunately, iTunes doesn’t support replaygain, so I have to use mp3tag to convert the album replaygain data into iTunes soundcheck format.
This is further complicated by the fact that soundcheck data is stored differently if it’s a MP3 file, or an AAC/ALAC file. Luckly you just need to create an action, and add the two following rules:
Format value: COMMENT ITUNNORM
Format value: ITUNNORM