So, one of the things I wanted to add to Active Resource is the ability to have knowledge of expected 'columns'. ie a set of things that ARes knows are attributes of the given resource.
Why do I want this? First let me explain the current situation.
Right now - ARes sets attributes based on whatever comes back from the remote system when you do a fetch. This is fine and dandy and need not be changed... but what if we're creating a brand new resource and want to run local validations over it?
Say I'm implementing a flickr-image-submitting service. The flickr API is known and published. Here's an example that shows up the problem:
class FlickrPhoto << ActiveResource::Base validates_presence_of :photo end
# this works my_photo = FlickrPhoto.new(:photo => 'photo_stream_here', :title => 'eben', :tags => ['cat']) my_photo.valid? # => true my_photo.save # => true my_photo.photo # 'photo_stream_here'
# this doesn't my_photo = FlickrPhoto.new(:title => 'snooch', :tags => ['cat']) # note: no photo my_photo.photo # throws NoMethodError exception my_photo.valid? # throws NoMethodError exception my_photo.save # throws NoMethodError exception
All the latter three don't work because we're calling 'photo' which doesn't exist as a method on FlickrPhoto - even though we know in advance (due to the published API) that photo is a required column. This is pretty annoying when the validates_presence_of method should be responding to exactly this situation...
We *could* overload validates_presence_of to respond to a NoMethodException... but we'd probably also have to update errors.on and the view-methods and various other locations... when all we really want is to add something into method_missing to return 'nil' if we know it's a column... but don't have a value yet.
Active Record uses a 'columns' method that is populated by reflecting on the db table... clearly Active Resource doesn't have that luxury.
We could get all fancy and do a remote lookup on the remote system... or we could just set the columns ourselves given that we know what they should contain. eg:
class FlickrPhoto << ActiveResource::Base columns = [:photo, :title, :description, :tags] validates_presence_of :photo end
# now this does... my_photo = FlickrPhoto.new(:title => 'snooch', :tags => ['cat']) # note: no photo my_photo.photo # 'nil' my_photo.valid? # returns 'false' with errors.on(:photo) == ['can't be blank'] my_photo.save # returns 'false' with errors.on(:photo) == ['can't be blank']
The latter seems like the simplest solution and AFAICS will not interfere with how ARes currently runs.
It also has a nice side-effect of allowing plugins that assume the existence of the 'columns' method to work with ARes as well. The plugins shouldn't care that the columns come from the db-table or otherwise...
I've provided a working proposed patch (below) to implement this and would value opinion on a) the patch as is and b) any other options.
Note - this follows from discussion (and my original proposal) in the following thread: http://groups.google.com/group/rubyonrails-core/browse_thread/thread/2afb6dcb7c555eb1
Cheers, Taryn
From 1cb4d7178f716d3c865ab8c36819c0f772fbfa01 Mon Sep 17 00:00:00 2001