Making Active Resource act like Active Record...

Hi there,

I've been using Active Resource pretty heavily over the last four months and have found, like many, that it's not as much like Active Record as I'd really like. So I've been improving a plugin called Hyperactive Resource which does just that. (http://github.com/ taryneast/hyperactiveresource/tree/master)

I've got it to a state where it's fairly useful, and want to start moving some of my stuff back into Active Resource itself. I spoke with Yehuda at Rails Underground and he said that this would be useful - especially the validations and callbacks.

My fork of rails is here:

I've got a lot of stuff I'd like to do to pull in my work from Hyres. Some of it may change the way that Active Resource behaves and I'd like to chat to you guys about whether it's a) wanted b) the best way to go about it.

As an example to begin with. I added Validations by using the ActiveModel::Validations module (BTB - is that a module or a component?)

I also added ,as yehuda called it, a couple of simple smoke-tests to make sure they work. They do - but with a caveat: validates_presence_of expects that an attribute will return 'nil' if it's not set... and doesn't recognise a MethodMissing as meaning "this attribute is not set yet".

ARes, as it currently stands, does not provide a MethodMissing for attributes unless they are present on the object because we currently don't know what "column-equivalents" we're likely to get back from the remote system.

That's fine for somebody grabbing remote objects from a system where they don't know the structure of the objects they're getting back... but in my experience so far - that's reasonably rare. Most of the systems that access a remote API (at least that I've worked on) run against a known API with known attributes... IMHO there should be no reason why we couldn't provide a set of known attributes/"columns" for ARes to do something useful with.

This is how HyRes works. You can manually hand it a set of columns that can then be used to feed MethodMissing and nicely spit out a nil if the attribute is not currently set on the given object.

We *could* override validates_presence_of to not explode with an Exception in this case - but that feels ugly to me... and somehow a little dangerous. IMO the HyRes method makes more sense. The known columns do not interfere with any pre-existing functionality which allows any values that come back from the remote system to be set as attributes - which means we just get an added benefit without any disruption to existing code.

This helps us with the validates_presence_of situation, because if we're running validations on it - it'll be on a column that we know about. But it also paves the way for a whole bunch of other functionality as well (eg attr_accessible/protected and some funky stuff once you start getting into Associations - but more on that later).

So my question is - are columns ok? and is anybody interested? If so - how do I go about getting this stuff back into Rails?

Cheers, Taryn

Awesome, Taryn! I suggest pairing up with Josh Peek, who's current working on Active Model for Google Summer of Code, to start merging your work in ASAP.

Best, jeremy

I'm very excited about this. Thanks for all the work Taryn!

My memory may be failing me, but doesn't ARes support (or plan to support) some lightweight standard (!= SOAP, != XMLRPC) for remote introspection of resources? Seems like that would be the means of getting column data.

-Chris

Mike Burrows [http://positiveincline.com/\] has been working along those lines. Don't know whether it would make sense to try to integrate his work into core or not.

Mike

I don’t know if this may help, but a couple of years ago I wrote a plugin in order to unify AR and ARes behaviors in one class: http://github.com/jodosha/acts-as-resource/tree/master

class Carrot

acts_as_resource

self.site = “http://api.example.com

belongs_to :bunny

valdates_presence_of :color

end

carrot = Carrot.find(23) # local find (database)

carrot = Carrot.find(23, :remote => true) # remote find (rest service)

Carrot.create(:color => “orange”) # local save

Carrot.create(:color => “orange”, :remote => true) # local save

etc… etc…

This is basically an hack, it lazily wraps two classes: an AR instance and an ARes one, and executes commands according to the :remote flag.

Cheers,

Luca

Perhaps I'm alone in my opinion, but I don't see the value of this. ActiveRecord is an ORM that interfaces with the database; ActiveResource is a convenience for manipulating a RESTful API that exposes resources as XML. My validations and callbacks and the like exist in the app server as the canonical behavior of my domain objects. I don't want clients of my API who use ActiveResource duplicating the validations in their code; that's just unnecessary duplication of behavior. If I add or remove a validation then suddenly all those clients are wrong. If validation fails now then the client receives a 400 response with an XML representation of the errors; no duplication needed.

Now, there's nothing to stop clients from adding their own logic on their end, just as a form on a web page may include JS to validate inputs before POSTing to the server. But, I don't see that as a responsibility of ActiveResource any more than Rails form helpers should include validation options that write JS.

Even if you control both ends of the equation you still end up violating DRY.

Hi Chris,

AFAICS it only does so after the object comes back from the remote system. Currently it just grabs what it can out of the returned XML (or, now json) and whatever it has is counted as an attribute.

If there's another way planned I'd love to see it. Querying the remote system, for example, certainly has its benefits.

Still - I think there wouldn't be any issue with letting ARes know what columns are *supposed* to be there on a remote system we may or may not trust. I think I pointed out - in some way it can be used a little like attr_accessible. On our own system we make sure that th set of columns are all that's sent during create/update because we use this for our user and don't want to send the unencrypted version of the password over the wire :wink:

Cheers, Taryn

Nice.

I've also been working on something like that - for a resource that has some local fields. Mine was just a "quick get it working" until I could get the HyRes stuff working, so I'm guessing mine is far more ugly. So I'd love to have a look at what you've got there :slight_smile:

Taryn

If there's another way planned I'd love to see it. Querying the remote system, for example, certainly has its benefits.

Previously we've discussed requesting:

/posts/new.xml

Which could return an 'empty object' which would tell you all the columns and their relative defaults. I'm not sure if anything came of this but it could be worth looking into.

Another option would be to just declare the attributes and their types in your ARes model declarations. I know everyone likes the idea of self-learning-self-aware XML webservices magic, but 99 times out of 100 you know the attributes of the object you're building a service against.

Either way, nice work!

Hi Adam.

Perhaps I'm alone in my opinion, but I don't see the value of this. ActiveRecord is an ORM that interfaces with the database; ActiveResource is a convenience for manipulating a RESTful API that exposes resources as XML. My validations and callbacks and the like exist in the app server as the canonical behavior of my domain objects. I don't want clients of my API who use ActiveResource duplicating the validations in their code; that's just unnecessary duplication of behavior. If I add or remove a validation then suddenly all those clients are wrong. If validation fails now then the client receives a 400 response with an XML representation of the errors; no duplication needed.

I think you may be confusing being an API producer with an API consumer. You are producing an API - ARes consumes it... a person may or may not even own the API being consumed.

We have a big project that involves having *all* of our data on a remote database and accessible via a newly-crafted RESTful API. We do, in fact, have the luxury of adding validations on the remote side of the equation - though many may not, which makes it good to have them available. You are also assuming that the API-system is written in Rails. It's written in Cold Fusion and the validations simply aren't as good in CF.

In fact - as we built th HyRes project up we realised that a lot of assumptions that have gone into ARes are based around the remote system also being Rails. Decoupling that assumption has been important as I worked on HyRes.

One of the big reasons why *we* want Validations on AREs is that relying on the remote system's validations means we have to have a network hit for every simple check to make sure the user has filled out the name-field when signing up :stuck_out_tongue: Adding client-side validations is a simple thing to do to reduce network traffic and lag.

Now, there's nothing to stop clients from adding their own logic on their end, just as a form on a web page may include JS to validate inputs before POSTing to the server. But, I don't see that as a responsibility of ActiveResource any more than Rails form helpers should include validation options that write JS.

but there's no reason we can't do it in ARes. The validations that are in Rails are just so darned ice to use - and it means we have immediate access to all the error-method helpers. So your comment is noted - but I disagree... in fact I've always had a niggling though that writing JS error-helpers might be a good idea too (even if just as a plugin) :wink:

Even if you control both ends of the equation you still end up violating DRY.

Yep - that happens sometimes when you're increasing throughput. Sad but true. But IMO it's better to do it by reusing a big code library (eg Validations) tan rewriting validations themselves by hand in JS.

Cheers, Taryn

Yay - sounds good. So I'll just email him? Taryn

> If there's another way planned I'd love to see it. > Querying the remote system, for example, certainly has its benefits.

Previously we've discussed requesting:

/posts/new.xml

Which could return an 'empty object' which would tell you all the columns and their relative defaults. I'm not sure if anything came of this but it could be worth looking into.

Interesting idea. This would probably add a network hit every time you want to show the new-page, which seems a bit like overkill. Still - there's no reason why we can't have both options available. :slight_smile:

Another option would be to just declare the attributes and their types in your ARes model declarations. I know everyone likes the idea of self-learning-self-aware XML webservices magic, but 99 times out of 100 you know the attributes of the object you're building a service against.

Yup - this was exactly the idea. In the HyperactiveResource plugin we've been using - this is done simply with the 'columns' accessor... which is then used internally just as 'columns' is used in Active Record... which makes it neat. The naming of this accessor can be anything... "fields" perhaps... but using "columns" just means that any plugins that currently work on AR's columns can Just Work with ARes... which is nice :wink:

Either way, nice work!

thanks :slight_smile:

Taryn

Further to this... have been in touch with Josh and have made my first patches which have been accepted. So, thanks for all the help so far :wink:

Taryn

> If there's another way planned I'd love to see it. > Querying the remote system, for example, certainly has its benefits.

Previously we've discussed requesting:

/posts/new.xml

Which could return an 'empty object' which would tell you all the columns and their relative defaults. I'm not sure if anything came of this but it could be worth looking into.

Interesting idea. This would probably add a network hit every time you want to show the new-page, which seems a bit like overkill. Still - there's no reason why we can't have both options available. :slight_smile:

Hit the /new resource once and cache, like introspecting db columns.

Another option would be to just declare the attributes and their types in your ARes model declarations. I know everyone likes the idea of self-learning-self-aware XML webservices magic, but 99 times out of 100 you know the attributes of the object you're building a service against.

Yup - this was exactly the idea. In the HyperactiveResource plugin we've been using - this is done simply with the 'columns' accessor... which is then used internally just as 'columns' is used in Active Record... which makes it neat. The naming of this accessor can be anything... "fields" perhaps... but using "columns" just means that any plugins that currently work on AR's columns can Just Work with ARes... which is nice :wink:

Declaring attributes in a common Active Model style would rock.

Either way, nice work!

thanks :slight_smile:

Seconded! :slight_smile:

Best, jeremy