Skip to content

Instantly share code, notes, and snippets.

@stevenharman
Created December 11, 2011 01:41
Show Gist options
  • Select an option

  • Save stevenharman/1457584 to your computer and use it in GitHub Desktop.

Select an option

Save stevenharman/1457584 to your computer and use it in GitHub Desktop.
When wrapping AR behind an intentional interface, go explicit or generic/idiomatic?
# This is a record, as in a vinyl.
class Record < ActiveRecord::Base
belongs_to :user
belongs_to :album
# explicitly stating what is needed
def from_library_for_album(collector, album)
where(user_id: collector).where(album_id: album)
end
# or, if we go more generic/idiomatic
def from_library(collector, options={})
album = options.fetch(:album) { false }
status = options.fetch(:status) { :mint }
records = where(user_id: collector)
records = records.where(album_id: album) if album
records = records.where(status: status) if status
records
end
end
class Album < ActiveRecord::Base
has_many :records
belongs_to :artist
end
class User < ActiveRecord::Base
has_many :records
end
# I prefer to keep AR as a private interface used only w/in the Model.
class Library
def initialize(collector, fetch_records=Record)
@collector = collector
@fetch_records = fetch_records
end
def fetch_records_for_album(album)
fetch_records.from_library_for_album(collector, album)
# ... or ...
fetch_records.from_library(collector, album: album)
# and what about more params?
fetch_records.from_library_for_album_in_mint_condition(collector, album)
# ... or ...
fetch_records.from_library(collector, album: album, condition: :mint)
end
end
class LibrariesController < ApplicationController
def album
library = Library.new(current_user)
@album = Album.find(params[:id])
@records = library.fetch_records_for_album(@album)
end
end
@steveklabnik
Copy link

Fucking software, it's hard, man.

Word. Datamapper is okay, but the Ruby implementation tries to pretend it's AR too much, it has the same problems.

I don't have the time and energy to write a good ORM...

@laribee
Copy link

laribee commented Dec 20, 2011

I feel like the DCI pattern could help you here... similar to your last code posting. (As we just discussed offline, but for posterity.)

Let data objects be primarily data objects. AR does an OK job with that anyway. That's the D.

The C would be your use case (I like the term activity better, but anyway). C is for context.

I are your interactions that you extend onto AR's domain/data objects at runtime inside your context.

The DCI pattern puts contextual analysis over the domain modelling desire "single source of truth," so I understand it's a different motivation but there might be more value in that POV?

@hkarthik
Copy link

There's some great discussion here, wish I could make it to CodeMash this year to partake in the booze filled continuation.

I agree AR definitely leaves a lot to be desired when it comes to separation of concerns. It's true that if you follow the idiomatic rules, things can remain quite testable purely because of the powerful stubbing capabilities in Ruby and RSpec. You don't have to wrap everything in a testable abstraction like we were all used to doing with C#.

But there are definitely things that still feel weird with AR and don't work quite as well if you try to build any abstraction layers around it. I'd consider Sequel as a possible alternative to using an ORM, although its really raw by comparison.

Or just try using MongoDB and get out of relational mapping all together. For side projects, I think its probably the best way to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment