Updating multiple RESTful records in one request

I’d be interested in seeing how DHH intends to implement this in
ActiveResource … if it’s still being developed. I checked today and
ActiveResource hasn’t been touched in a very, very long time.

It seems logical to expect methods to be able to act upon more than
one object … in a batch. I cringe at the thought of iterating over
every object in a collection just to do a batch update or create. I’d
love to be able to upload an XML file to a RESTful URL and have it
know whether it’s a single object or a collection of objects. It
seems to be that it would be as simple as determining whether the root
entity was singular or plural:

If I want to work with a single entity, I’d hit
http://www.myapplication.com/people/
with…



and if I want to work with more than one person, I’d hit the same URL
with…









I’m trying to let a couple of applications talk together using REST
interfaces but, without this kind of functionality, it’s proving to be
very difficult.

Am I thinking about this incorrectly or does something like this seem
possible?

Thanks,
Chris

On 3/5/07, Chris G. [email protected] wrote:

I’d be interested in seeing how DHH intends to implement this in
ActiveResource … if it’s still being developed. I checked today and
ActiveResource hasn’t been touched in a very, very long time.

That’s odd; where’d you check? There’s regular, though not high-volume,
activity.

It seems logical to expect methods to be able to act upon more than

one object … in a batch. I cringe at the thought of iterating over
every object in a collection just to do a batch update or create. I’d
love to be able to upload an XML file to a RESTful URL and have it
know whether it’s a single object or a collection of objects. It
seems to be that it would be as simple as determining whether the root
entity was singular or plural:

There’s nothing stopping you, but it’s not baked in yet. XML params are
just read in as nested hashes, after all.

Try it, see what works best, extract the solution and post a patch!

jeremy

I used to be able to destroy multiple records in my restful controller
by using the following:

in my view:

<%= form_tag product_path, :method => :delete %>
<% for product in @products %>

<%= product.title %>
<%= check_box_tag (‘id[]’, product.id) %>

<% end %>
<%= end_form_tag %>

and in my products_controller:

def destroy
begin
# params[:id] may either be a single id, or an array of id’s
@products = Product.find(params[:id])
rescue
logger.warn(“ERROR: #{$!}”)
end

if @products and Product.destroy(@products)
  flash[:notice] = "Product successfully deleted"
else
  flash[:warning] = "An error was encountered while attempting to

delete the product(s)"
end
end

however, after upgrading to the most recent edge rails (6207 as of
yesterday), this no longer works… I get an error

“product_url failed to generate from {:controller=>“products”,
:action=>“show”} - you may have ambiguous routes, or you may need to
supply additional parameters for this route.”

I solved this by using the following:

in routes.rb:

map.resources :products, :collection => {
:delete => :delete
}

in my view:

<%= form_tag delete_products_path, :method => :delete %>
blah blah…
<%= end_form_tag %>

and then I use the same code in my controller, however, I alias the
delete method to destroy by using the following in my controller:

alias delete destroy

so am I bastardizing RESTful standards? I’m sure I am, but I can’t
think of a better way around this. If anyone else has any ideas,
please let us know! Also, why was I previously able to do this using
the standard restful routes (ie multiple id’s could be passed to an
action), but this no longer seems to be the case?

Mike

Yeah, I forgot to mention that I’m gonna start working on this and see
what I can come up with. I’m just a little surprised that it wasn’t
baked-in from the beginning. :wink:

On 3/5/07, Chris G. [email protected] wrote:

Yeah, I forgot to mention that I’m gonna start working on this and see
what I can come up with. I’m just a little surprised that it wasn’t
baked-in from the beginning. :wink:

Great! It’s not baked in because nobody ordered that entree yet.

“if it’s still being developed. I checked today and ActiveResource
hasn’t
been touched in a very, very long time”

Still curious where you checked :wink:
jeremy

I forgot to mention…

After searching around a bit, I stumbled upon some code that would
probably be a great base for what we’ve been talking about. It straps
a from_xml method onto ActiveRecord::Base and has the logic to
determine whether the root is the singular or plural version of the
current class. I’m not sure how resilient the code is when it comes
to nested associations and such but it’s worth a look.

The code is here: http://riftor.g615.co.uk/content.php?view=50&type=1

-Chris

I don’t know what I was looking at but, since checking the official
Trac (thought I was there before), I’m finding myself wrong on this
one. Thanks for keeping me honest! :wink:

Hi Chris,

On 5-Mar-07, at 9:39 PM, Chris G. wrote:

seems to be that it would be as simple as determining whether the root
entity was singular or plural:

This is pretty much the exact opposite direction that I instinctively
head in.

I cringe at the thought of littering my controller methods with case/
if statements to cover the scenarios of getting “no id”, “one id”,
“many ids”. It seems like a step backwards.

In my projects I completely sidestep the issues you guys have been
having (i.e. calling POST, PUT, DELETE for a batch) by treating the
“subset” that I want to operate on as something at a different
RESTful URL.

So rather than overloading the idea expressed by /comments with both
“the collection of comments” and “a batch-subset of comments” I would
have both /comments and /comments/batch - they are, after all, two
different things.

Admittedly, I haven’t looked at ActiveResource in a while and I
haven’t (yet) had to build anything beyond a toy ARes client.
However, I don’t see anything missing on the server side when doing
this in-browser.

Regards,
Trevor

Hey Chris,

comments inline:

On 7-Mar-07, at 1:10 AM, Chris G. wrote:

(for singular items):

I agree, reconstitution as a params hash is desirable. Careful about
that “(for singular items)” thing - it’s not quite true, you can have
multiple params and even though a param is a key/value pair, there’s
nothing saying a ‘value’ can’t be composite.

I’m pointing this out because I think you’re getting hung up on
detecting when a given param is a single object or a list of
objects. See below:

taking ActiveRecord into account), would seem to be fairly easy:

(excuse the pseudo-code, it’s late and I don’t want to go api hunting)
def create
@articles = ( if params[:articles] exists, we process as a
collection of articles … if not, we process as a singular article )

end

Blech. :slight_smile:

Even those who lines look ugly and you haven’t yet included results
checking.

The conditional code may not even need to be in the controller.
Perhaps the heavy-lifting would be offloaded to the appropriate class
methods (e.g. Article.new()) in ActiveRecord.

Article.new returns one Article so no, that’s not the place.

If you want to create a bunch of articles in one go, that’s something
like Article.create_zero_or_more_from_this_hash.

But that method (when you give it an accurate name) looks pretty
smelly and my personal preference is for classes to be really good at
doing as little as possible.

So I’d create an ArticleBatch class that was really good at
maintaining a list of Articles and would forward create/update/delete
requests to each object it’s referencing.

And suddenly the whole need to detect a singular article vs many
articles goes away - it’s not articles, it’s one article_batch.

batch = ArticleBatch.new(params[:article_batch])
batch.can_save_all?
batch.errors
batch.save_all
batch.destroy_all
batch.each_article
batch.to_xml

So, if you review my previous mail - you have /articles and /articles/
batch (or /articles_batch - it doesn’t matter).

And you have controller methods (maybe in a different _controller.rb
file, maybe not) that do one thing really well.

In the end it’s a matter of taste and smell. I’ve seen people
complain about having to create extra classes for their RESTful nouns
and I’ve seen people opine that REST breaks down a bit in more
complicated systems because you’re trying to shoe-horn the concept
(implying that noun classes will litter your project).

Hooey! More classes (that are really good at one thing) will set
you free. :slight_smile:

Give it a try, you may even end up saying “actually, I need an
ArticleBatch and an ArticleFactory because handling both new and
existing records leads to way too many conditionals”.

HTH,
Trevor

Yup, I like what you’ve got here, too.

Thanks for bringing up the ‘singular item’ point, too. I was aware of
what you’re talking about but it’s good for others who may be
following along.

I like your abstraction of the batch functionalities into a dedicated
Batch class. It makes sense, removes the need for conditionals strewn
throughout controller methods and is probably a lot easier to create
tests for.

I’m going to start working on the general case Batch class and see if
we can’t get something working.

Thanks for the thoughts!

-Chris

Trevor,

I can see the logic from both sides. Conditionals do suck and, to
avoid them and really encapsulate the behavior, I think the code would
need to be within ActiveRecord itself (maybe not core ::Base but
perhaps in a plugin that modifies AR). I’ve not had the time to study
all of the pertinent files and process flows but I think it can be
done without changing controller code too much.

Before I go any further, I may be totally wrong about how Rails is
doing some things behind the scenes because I’ve not had the time to
thoroughly study the behind-the-scenes code. So, if I get something
wrong, just let me know. :wink:

Given a RESTful URL, it seems logical to be able to POST/PUT
collections just like we do singular items. Currently, if an XML file
reaches an action (e.g. create or update), it’s processed and
reconstituted as the ‘params’ hash (see: param_parsers in base.rb and
parsing logic within cgi_methods.rb – both part of
ActionController). The XML data is then accessible via
params[:xml_root][:some_key] (note: prior to Rails 1.1, the XML root
was not part of the params hash). Anyways, this is great because,
whether our params hash was built from form parameters or a posted XML
file, our controllers can act on the params hash the same exact way
(for singular items):

(code taken from the AWDwR book)

POST /articles

POST /articles.xml

def create
@article = Article.new(params[:article])

end

So, we’re just passing the params hash to the model for processing.
The conditional logic to support collections, at this point (not
taking ActiveRecord into account), would seem to be fairly easy:

(excuse the pseudo-code, it’s late and I don’t want to go api hunting)
def create
@articles = ( if params[:articles] exists, we process as a
collection of articles … if not, we process as a singular article )

end

The conditional code may not even need to be in the controller.
Perhaps the heavy-lifting would be offloaded to the appropriate class
methods (e.g. Article.new()) in ActiveRecord.

I don’t know … I may be thinking too simply but it seems like this
would be possible. From there, the model would be responsible for
determining whether it was passed a singular item or a collection.

Anyways, I do want to look at all of the Rails code that’s involved in
this stuff and then try to take a whack at it. I’m too tired to think
right now, so I’m going to cut this shorter than I had expected
(sorry).

-Chris