From blair at orcaware.com Fri Jun 2 23:05:41 2006 From: blair at orcaware.com (Blair Zajac) Date: Fri, 2 Jun 2006 20:05:41 -0700 Subject: [Restful-rails-general] New routes implementation Message-ID: <997F15D0-4806-4371-9AD8-956F60DC920A@orcaware.com> Fyi, there's a new routes implementation in Rails trunk. Here's the comment: "New routes implementation. Simpler, faster, easier to understand. The published API for config/routes.rb is unchanged, but nearly everything else is different, so expect breakage in plugins and libs that try to fiddle with routes." http://dev.rubyonrails.org/changeset/4394 When somebody gets some spare cycles, we'll have to look at this. From my quick test, this does break something. Regards, Blair -- Blair Zajac, Ph.D. Subversion training, consulting and support http://www.orcaware.com/svn/ From blair at orcaware.com Tue Jun 6 19:44:34 2006 From: blair at orcaware.com (Blair Zajac) Date: Tue, 06 Jun 2006 16:44:34 -0700 Subject: [Restful-rails-general] New routes implementation In-Reply-To: <997F15D0-4806-4371-9AD8-956F60DC920A@orcaware.com> References: <997F15D0-4806-4371-9AD8-956F60DC920A@orcaware.com> Message-ID: <44861362.4030901@orcaware.com> Blair Zajac wrote: > Fyi, there's a new routes implementation in Rails trunk. Here's the > comment: > "New routes implementation. Simpler, faster, easier to understand. The > published API for config/routes.rb is unchanged, but nearly everything > else is different, so expect breakage in plugins and libs that try to > fiddle with routes." > > http://dev.rubyonrails.org/changeset/4394 > > When somebody gets some spare cycles, we'll have to look at this. From > my quick test, this does break something. > Regards, > Blair There is a problem with the new routes implementation that breaks restful_rails. I have a ticket and a patch for RoR at: http://dev.rubyonrails.org/ticket/5314 If it doesn't get applied, then it won't be hard to work around it. Regards, Blair -- Blair Zajac, Ph.D. Subversion training, consulting and support http://www.orcaware.com/svn/ From blair at orcaware.com Wed Jun 7 12:58:58 2006 From: blair at orcaware.com (Blair Zajac) Date: Wed, 07 Jun 2006 09:58:58 -0700 Subject: [Restful-rails-general] New routes implementation In-Reply-To: <44861362.4030901@orcaware.com> References: <997F15D0-4806-4371-9AD8-956F60DC920A@orcaware.com> <44861362.4030901@orcaware.com> Message-ID: <448705D2.3090505@orcaware.com> Blair Zajac wrote: > Blair Zajac wrote: > >> Fyi, there's a new routes implementation in Rails trunk. Here's the >> comment: >> "New routes implementation. Simpler, faster, easier to understand. >> The published API for config/routes.rb is unchanged, but nearly >> everything else is different, so expect breakage in plugins and libs >> that try to fiddle with routes." >> >> http://dev.rubyonrails.org/changeset/4394 >> >> When somebody gets some spare cycles, we'll have to look at this. >> From my quick test, this does break something. >> Regards, >> Blair > > > There is a problem with the new routes implementation that breaks > restful_rails. > > I have a ticket and a patch for RoR at: > > http://dev.rubyonrails.org/ticket/5314 > > If it doesn't get applied, then it won't be hard to work around it. > > Regards, > Blair > And the patch has been applied: http://dev.rubyonrails.org/changeset/4444 Blair From blair at orcaware.com Thu Jun 15 21:04:01 2006 From: blair at orcaware.com (Blair Zajac) Date: Thu, 15 Jun 2006 18:04:01 -0700 Subject: [Restful-rails-general] Collections that are too large Message-ID: <44920381.4000702@orcaware.com> What's the best way of dealing with collections you don't want people to hit, but you still want URLs for entities to work? For example, say if http://localhost:3000/people returns 10,000 people and you want requests for that collection to be rejected. Say there's a related class, Companies, that :has_many people, so you can still have URLs like this that work: http://localhost:3000/people/123 Say that the XML returned from the companies URL contains URLs for people: http://localhost:3000/companies/456 would return Currently, I have the following code commented out in my PersonController to remind myself and fellow developers that this is a conscious choice. # resource :collection do |r| # end However, doing this prevents people from doing an OPTIONS request, which like it should be allowed. So should the request just return an HTTP::Status::BAD_REQUEST instead? Another thing is that doing an OPTIONS request against the collection always returns GET and HEAD as allowable actions, which I would like to remove. Could we change restful rails code to require developers to do resource :collection do |r| r.get do ... end end instead of leaving GET and HEAD as defaults, as they are in resource.rb. Thanks, Blair -- Blair Zajac, Ph.D. Subversion training, consulting and support http://www.orcaware.com/svn/ From dan.kubb at autopilotmarketing.com Tue Jun 20 22:02:33 2006 From: dan.kubb at autopilotmarketing.com (Dan Kubb) Date: Tue, 20 Jun 2006 19:02:33 -0700 Subject: [Restful-rails-general] Collections that are too large In-Reply-To: <44920381.4000702@orcaware.com> References: <44920381.4000702@orcaware.com> Message-ID: <7AE07D7E-9631-4D66-8BCD-8991E4638AD8@autopilotmarketing.com> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Blair, > What's the best way of dealing with collections you don't want > people to hit, > but you still want URLs for entities to work? ... snip ... > Currently, I have the following code commented out in my > PersonController to > remind myself and fellow developers that this is a conscious choice. > > # resource :collection do |r| > # end > > However, doing this prevents people from doing an OPTIONS request, > which like it > should be allowed. You could do something like this: resource :collection do |r| r.allowed_methods -= [ :get, :head ] end That will still allow OPTIONS to be performed, but it should only return that the OPTIONS method is allowed in the HTTP Allow header. I do something like this with a few collections that I only allow a POST to. > Could we change restful rails code to require developers to do > > resource :collection do |r| > r.get do > ... > end > end > > instead of leaving GET and HEAD as defaults, as they are in > resource.rb. I would say no to this. The default behavior in RESTful Rails handles the common cases, with the example above handling the edge cases. Rails' own default is to support GET/HEAD on any action, and I don't want to stray too far from that since I think its pretty reasonable. - -- Thanks, Dan __________________________________________________________________ Dan Kubb Autopilot Marketing Inc. Email: dan.kubb at autopilotmarketing.com Phone: 1 (604) 820-0212 Web: http://autopilotmarketing.com/ vCard: http://autopilotmarketing.com/~dan.kubb/vcard __________________________________________________________________ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2.2 (Darwin) iD8DBQFEmKi54DfZD7OEWk0RAstaAJ9d3WD+ZX8ahqVLeK2sR1H7WGPicgCcCUjO NohNTdPDervcDx8dPe6L9fI= =7HZa -----END PGP SIGNATURE----- From blair at orcaware.com Wed Jun 21 14:22:44 2006 From: blair at orcaware.com (Blair Zajac) Date: Wed, 21 Jun 2006 11:22:44 -0700 Subject: [Restful-rails-general] Collections that are too large In-Reply-To: <7AE07D7E-9631-4D66-8BCD-8991E4638AD8@autopilotmarketing.com> References: <44920381.4000702@orcaware.com> <7AE07D7E-9631-4D66-8BCD-8991E4638AD8@autopilotmarketing.com> Message-ID: <44998E74.6000402@orcaware.com> Dan Kubb wrote: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Hi Blair, > >> What's the best way of dealing with collections you don't want people >> to hit, >> but you still want URLs for entities to work? > > ... snip ... > >> Currently, I have the following code commented out in my >> PersonController to >> remind myself and fellow developers that this is a conscious choice. >> >> # resource :collection do |r| >> # end >> >> However, doing this prevents people from doing an OPTIONS request, >> which like it >> should be allowed. > > You could do something like this: > > resource :collection do |r| > r.allowed_methods -= [ :get, :head ] > end > > That will still allow OPTIONS to be performed, but it should only return > that the OPTIONS method is allowed in the HTTP Allow header. > > I do something like this with a few collections that I only allow a POST > to. Thanks Dan. That's a much nicer solution, since I ended up enabling the collection but having code like this: resource :collection do |r| r.get do render :status => HTTP::Status::BAD_REQUEST, :text => 'Too many properties exist to request them all' end which isn't as nice as returning a HTTP::Status::METHOD_NOT_ALLOWED which is done by default when you remove :get and :head from r.allowed_methods. > >> Could we change restful rails code to require developers to do >> >> resource :collection do |r| >> r.get do >> ... >> end >> end >> >> instead of leaving GET and HEAD as defaults, as they are in resource.rb. > > I would say no to this. The default behavior in RESTful Rails handles the > common cases, with the example above handling the edge cases. Rails' own > default is to support GET/HEAD on any action, and I don't want to stray > too far from that since I think its pretty reasonable. Sounds good. Regards, Blair -- Blair Zajac, Ph.D. Subversion training, consulting and support http://www.orcaware.com/svn/ From blair at orcaware.com Fri Jun 23 19:34:45 2006 From: blair at orcaware.com (Blair Zajac) Date: Fri, 23 Jun 2006 16:34:45 -0700 Subject: [Restful-rails-general] Status codes for failed updates and deletes Message-ID: <449C7A95.3070709@orcaware.com> I'm setting up a RESTful rails app against an Oracle database using integrity constraints. What are the appropriate codes to return in the following cases. I've put my best guesses here: 1) Delete against an id that was already deleted? 'Not Found' => 404 'Gone' => 410 2) Delete against an ID that has a foreign key constraint so it cannot be deleted, say deleting a PostType when a Post belongs_to it? 'Forbidden' => 403 'Precondition Failed' => 412 'Expectation Failed' => 417 'Locked' => 423 'Failed Dependency' => 424 3) I have modified the routes in the app to match URLs like /posts/123/1151105375 and /posts/123/1151105375 In the second case, this sets a :timestamp param, containing the Unix epoch timestamp of when the record was last modified. You could do a similar thing with the lock_version. So clients that have an older version of the resource, when they go to edit it, will fail. So if the client updates an edit URL when an old timestamp or version, 'Conflict' => 409, I got this from http://code.google.com/apis/gdata/protocol.html#Optimistic-concurrency Regards, Blair -- Blair Zajac, Ph.D. Subversion training, consulting and support http://www.orcaware.com/svn/ From blair at orcaware.com Fri Jun 23 19:47:10 2006 From: blair at orcaware.com (Blair Zajac) Date: Fri, 23 Jun 2006 16:47:10 -0700 Subject: [Restful-rails-general] Status codes for failed updates and deletes In-Reply-To: <449C7A95.3070709@orcaware.com> References: <449C7A95.3070709@orcaware.com> Message-ID: <449C7D7E.3040406@orcaware.com> Blair Zajac wrote: > > 3) I have modified the routes in the app to match URLs like > > /posts/123/1151105375 Make that /posts/123 > and > > /posts/123/1151105375 > > In the second case, this sets a :timestamp param, containing the Unix > epoch timestamp of when the record was last modified. You could do a > similar thing with the lock_version. So clients that have an older > version of the resource, when they go to edit it, will fail. > > So if the client updates an edit URL when an old timestamp or version, > > 'Conflict' => 409, > > I got this from > > http://code.google.com/apis/gdata/protocol.html#Optimistic-concurrency One more thing. I'm planning on using the edit URL to delete, so a DELETE to /posts/123 will always fail, but a DELETE to /posts/123/1151105375 may succeed. If the timestamp is out of date that the client has, what would be a good return value? Still use Conflict and 409? Regards, Blair -- Blair Zajac, Ph.D. Subversion training, consulting and support http://www.orcaware.com/svn/ From dan.kubb at autopilotmarketing.com Mon Jun 26 18:12:28 2006 From: dan.kubb at autopilotmarketing.com (Dan Kubb) Date: Mon, 26 Jun 2006 15:12:28 -0700 Subject: [Restful-rails-general] ActiveResource: access REST apps through ActiveRecord-like API Message-ID: -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi All I'm interested if anyone's heard about the new class DHH adding to Rails called ActiveResource? Here's a bit more information: http://www.technorati.com/search/ActiveResource The idea is that you can wrap up calls to a remote REST resource in an interface similar to ActiveRecord. Here's the sample code DHH provides: Person = ActiveResource::Struct.new do |p| p.uri "http://www.example.com/people" p.credentials :name => "dhh", :password => "secret" end #Issues GET http://www.example.com/people/1 matz = Person.find(1) matz.name # => "Matz" I think this is a pretty powerful idea. A hope of mine is that ActiveResource is ported to other languages too, primarily in Javascript. This would allow allow some pretty slick AJAX and REST apps. My question to the list is: If ActiveResource requires a specific URL layout, should we adapt RESTful Rails' connect_resource method to generate compatible URLs? The URL structure that ActiveResource will probably use is: GET /people - view a collection of people GET /people;new - view a form allowing a person to be added POST /people - add a person GET /people/1 - view details of a person PUT /people/1 - change details of a person DELETE /people/1 - remove a person GET /people/1;edit - view a form allowing a person to be changed The only differences with this and what RESTful Rails does is that we use "GET /people/new" instead of "GET /people;new" and we don't have any distinction between editing and viewing a resource. I personally have no problem changing to this, since I use url_for to generate my internal application URLs, and I have no external links to my "new" resource. I think it would also be easy (for compatibility reasons) to route "GET /people/1;edit" and "GET /people/1" to the same resource and method handler. What does everyone think? - -- Thanks, Dan __________________________________________________________________ Dan Kubb Autopilot Marketing Inc. Email: dan.kubb at autopilotmarketing.com Phone: 1 (604) 820-0212 Web: http://autopilotmarketing.com/ vCard: http://autopilotmarketing.com/~dan.kubb/vcard __________________________________________________________________ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2.2 (Darwin) iD8DBQFEoFvN4DfZD7OEWk0RAuuqAJ9mKvFBc73N85t0iiKv6OmU2jLBiACfSs7c DJHclLEDgLdjRQab7uSyYAw= =/Pd5 -----END PGP SIGNATURE----- From dan.kubb at autopilotmarketing.com Mon Jun 26 19:40:01 2006 From: dan.kubb at autopilotmarketing.com (Dan Kubb) Date: Mon, 26 Jun 2006 16:40:01 -0700 Subject: [Restful-rails-general] Status codes for failed updates and deletes In-Reply-To: <449C7A95.3070709@orcaware.com> References: <449C7A95.3070709@orcaware.com> Message-ID: <81A9A1A0-103B-4944-B92A-4824580488AB@autopilotmarketing.com> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Blair, > What are the appropriate codes to return in the following cases. > I've put my > best guesses here: > > 1) Delete against an id that was already deleted? 410 Gone. This status means that the URI was explicitly removed. > 2) Delete against an ID that has a foreign key constraint so it > cannot be > deleted, say deleting a PostType when a Post belongs_to it? > > 'Forbidden' => 403 > 'Precondition Failed' => 412 > 'Expectation Failed' => 417 > 'Locked' => 423 > 'Failed Dependency' => 424 403 Forbidden is for when a person has authenticated properly, but they do not have permission to perform a method on a resource. Also I think from your list the following are out: 412 Precondition Failed - this is for refusing to handle HTTP conditional requests 417 Expectation Failed - this is in relation to the Expect header 423 Locked - the resource has been locked by the LOCK method 424 Failed Dependency - some methods can result in multiple commands to execute. If one fails this is returned. I'd use 409 Conflict. Based on RFC 2616 it means that according to the current state of the resource the method cannot be performed at this time. The response should explain enough for the client to resolve the issue on its own. > 3) I have modified the routes in the app to match URLs like > > /posts/123/1151105375 > > and > > /posts/123/1151105375 > > In the second case, this sets a :timestamp param, containing the > Unix epoch > timestamp of when the record was last modified. You could do a > similar thing > with the lock_version. So clients that have an older version of > the resource, > when they go to edit it, will fail. > > So if the client updates an edit URL when an old timestamp or version, > > 'Conflict' => 409, > > I got this from > > http://code.google.com/apis/gdata/protocol.html#Optimistic-concurrency What about using HTTP's own optimistic locking system? The appropriate response when a client is submitting a request to an out-of-date resource is to return 412 Precondition Failed (except if the request is a GET/HEAD, in which case you return 304 Not Modified). - -- Thanks, Dan __________________________________________________________________ Dan Kubb Autopilot Marketing Inc. Email: dan.kubb at autopilotmarketing.com Phone: 1 (604) 820-0212 Web: http://autopilotmarketing.com/ vCard: http://autopilotmarketing.com/~dan.kubb/vcard __________________________________________________________________ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2.2 (Darwin) iD8DBQFEoHBR4DfZD7OEWk0RAhMCAJ98A7GhDRksNELm9uwfICgin1v/yQCgio1k olkKtjxBa8IyWo0T3V8Nej8= =dTAM -----END PGP SIGNATURE----- From blair at orcaware.com Mon Jun 26 19:56:59 2006 From: blair at orcaware.com (Blair Zajac) Date: Mon, 26 Jun 2006 16:56:59 -0700 Subject: [Restful-rails-general] ActiveResource: access REST apps through ActiveRecord-like API In-Reply-To: References: Message-ID: <44A0744B.7040307@orcaware.com> Dan Kubb wrote: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Hi All > > I'm interested if anyone's heard about the new class DHH adding > to Rails called ActiveResource? Here's a bit more information: > > http://www.technorati.com/search/ActiveResource > > The idea is that you can wrap up calls to a remote REST resource > in an interface similar to ActiveRecord. Here's the sample code > DHH provides: > > Person = ActiveResource::Struct.new do |p| > p.uri "http://www.example.com/people" > p.credentials :name => "dhh", :password => "secret" > end > > #Issues GET http://www.example.com/people/1 > matz = Person.find(1) > matz.name # => "Matz" > > I think this is a pretty powerful idea. A hope of mine is that > ActiveResource is ported to other languages too, primarily > in Javascript. This would allow allow some pretty slick > AJAX and REST apps. I may write this in Python. The easy part is what he's shown. The hard part is doing relationships. To preserve DRY, you don't want to re-specify the associations between Person and other classes. In my app, I have a /schema URL that returns the schema of the application from a client point of view. It looks like I'll be dynamically passing the has_many, :belongs_to and other associations through to the client. > My question to the list is: If ActiveResource requires a specific > URL layout, should we adapt RESTful Rails' connect_resource method > to generate compatible URLs? > > The URL structure that ActiveResource will probably use is: > > GET /people - view a collection of people > GET /people;new - view a form allowing a person to be added > POST /people - add a person > GET /people/1 - view details of a person > PUT /people/1 - change details of a person > DELETE /people/1 - remove a person > GET /people/1;edit - view a form allowing a person to be changed > > The only differences with this and what RESTful Rails does > is that we use "GET /people/new" instead of "GET /people;new" and > we don't have any distinction between editing and viewing a resource. > > I personally have no problem changing to this, since I use url_for > to generate my internal application URLs, and I have no external links > to my "new" resource. I think it would also be easy (for compatibility > reasons) to route "GET /people/1;edit" and "GET /people/1" to the same > resource and method handler. > > What does everyone think? Sounds good. I'm ok with any incompatible changes you would like to make, as my app is in an intranet and I'm also writing the client piece. I don't know how many other people have rolled out public code. The REST API is being used by a desktop application and I don't care about having the /people;new and /people/1;edit forms as nobody is going to be looking at them, so if we could make those optional in the routes, I would appreciate that. Regards, Blair -- Blair Zajac, Ph.D. Subversion training, consulting and support http://www.orcaware.com/svn/ From blair at orcaware.com Mon Jun 26 20:05:02 2006 From: blair at orcaware.com (Blair Zajac) Date: Mon, 26 Jun 2006 17:05:02 -0700 Subject: [Restful-rails-general] Status codes for failed updates and deletes In-Reply-To: <81A9A1A0-103B-4944-B92A-4824580488AB@autopilotmarketing.com> References: <449C7A95.3070709@orcaware.com> <81A9A1A0-103B-4944-B92A-4824580488AB@autopilotmarketing.com> Message-ID: <44A0762E.8010707@orcaware.com> Dan Kubb wrote: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Hi Blair, > >> What are the appropriate codes to return in the following cases. >> I've put my >> best guesses here: >> >> 1) Delete against an id that was already deleted? > > 410 Gone. This status means that the URI was explicitly removed. Since the row in the DB will be deleted, there's no way to know if a URL once existed and now no longer exists or if it never existed. So you would use 410 Gone for both cases, even if somebody tries to GET /people/123456789? >> 2) Delete against an ID that has a foreign key constraint so it >> cannot be >> deleted, say deleting a PostType when a Post belongs_to it? >> >> 'Forbidden' => 403 >> 'Precondition Failed' => 412 >> 'Expectation Failed' => 417 >> 'Locked' => 423 >> 'Failed Dependency' => 424 > > 403 Forbidden is for when a person has authenticated properly, but > they do not have permission to perform a method on a resource. > > Also I think from your list the following are out: > > 412 Precondition Failed - this is for refusing to handle HTTP > conditional requests > 417 Expectation Failed - this is in relation to the Expect header > 423 Locked - the resource has been locked by the LOCK > method > 424 Failed Dependency - some methods can result in multiple > commands to execute. If one fails this is returned. > > I'd use 409 Conflict. Based on RFC 2616 it means that according to > the current > state of the resource the method cannot be performed at this time. > The response > should explain enough for the client to resolve the issue on its own. Thanks. > >> 3) I have modified the routes in the app to match URLs like >> >> /posts/123/1151105375 >> >> and >> >> /posts/123/1151105375 >> >> In the second case, this sets a :timestamp param, containing the >> Unix epoch >> timestamp of when the record was last modified. You could do a >> similar thing >> with the lock_version. So clients that have an older version of >> the resource, >> when they go to edit it, will fail. >> >> So if the client updates an edit URL when an old timestamp or version, >> >> 'Conflict' => 409, >> >> I got this from >> >> http://code.google.com/apis/gdata/protocol.html#Optimistic-concurrency > > What about using HTTP's own optimistic locking system? The appropriate > response when a client is submitting a request to an out-of-date > resource is to return 412 Precondition Failed (except if the request > is a GET/HEAD, in which case you return 304 Not Modified). If I do that, then does that require the use of any additional HTTP headers, such as If-Match with the ETag header? And then I would presumably drop the timestamp in the URL? Thanks, Blair -- Blair Zajac, Ph.D. Subversion training, consulting and support http://www.orcaware.com/svn/ From dan.kubb at autopilotmarketing.com Mon Jun 26 21:02:17 2006 From: dan.kubb at autopilotmarketing.com (Dan Kubb) Date: Mon, 26 Jun 2006 18:02:17 -0700 Subject: [Restful-rails-general] Status codes for failed updates and deletes In-Reply-To: <44A0762E.8010707@orcaware.com> References: <449C7A95.3070709@orcaware.com> <81A9A1A0-103B-4944-B92A-4824580488AB@autopilotmarketing.com> <44A0762E.8010707@orcaware.com> Message-ID: <8BF4C1C2-0211-4093-A51C-EF79CE682FEB@autopilotmarketing.com> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Blair, >>> 1) Delete against an id that was already deleted? >> >> 410 Gone. This status means that the URI was explicitly removed. > > Since the row in the DB will be deleted, there's no way to know if > a URL once existed and now no longer exists or if it never existed. > > So you would use 410 Gone for both cases, even if somebody tries to > GET /people/123456789? That's a good question. I would use 410 Gone *only if I knew* the underlying data had been deleted. Otherwise I'd use 404 Not Found. With most designs, there's no way to know if something was explicitly deleted. I've heard of hacks to determine this from other data, like seeing if the requested id is less than MAX(id), an approach which I personally think is a bad idea. If you were using something like acts_as_paranoid, then you could tell the difference between a deleted record and a record that just doesn't exist. The downside to acts_as_paranoid is that you can't use foreign key constraints to make sure "linked to" records aren't deleted. >> What about using HTTP's own optimistic locking system? The >> appropriate >> response when a client is submitting a request to an out-of-date >> resource is to return 412 Precondition Failed (except if the request >> is a GET/HEAD, in which case you return 304 Not Modified). > > If I do that, then does that require the use of any additional HTTP > headers, such as If-Match with the ETag header? And then I would > presumably drop the timestamp in the URL? Yes, it does require usage of HTTP headers, and you'll be able to drop the timestamp from the URL. In the response you'd return an ETag and Last-Modified header, which will automatically be done for you if all your models are assigned to the conditions object, like this: conditions << @model_instance Remember to do this at the top of the resource code, before your first method handler block. For future requests from the client you'd take the ETag you originally got and send it back in the If-Match header. The Last-Modified response header should also be sent in the If-Unmodified-Since request header. Provided that the conditions object knows about all the models it will return a 412 Precondition Failed response if the client has a "stale" copy of the resource. - -- Thanks, Dan __________________________________________________________________ Dan Kubb Autopilot Marketing Inc. Email: dan.kubb at autopilotmarketing.com Phone: 1 (604) 820-0212 Web: http://autopilotmarketing.com/ vCard: http://autopilotmarketing.com/~dan.kubb/vcard __________________________________________________________________ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2.2 (Darwin) iD8DBQFEoIOZ4DfZD7OEWk0RAvHhAJ9eF4ztMbNGSGHR2ZRGw3li3Zf8nACfZyru DFHFU53dIZQ5VbB4jATrF7U= =Xo5f -----END PGP SIGNATURE----- From pic at superfluo.org Tue Jun 27 02:32:18 2006 From: pic at superfluo.org (Nicola Piccinini) Date: Tue, 27 Jun 2006 08:32:18 +0200 Subject: [Restful-rails-general] ActiveResource: access REST apps through ActiveRecord-like API In-Reply-To: <44A0744B.7040307@orcaware.com> References: <44A0744B.7040307@orcaware.com> Message-ID: <44A0D0F2.9040008@superfluo.org> >> [...] >>I think this is a pretty powerful idea. A hope of mine is that >>ActiveResource is ported to other languages too, primarily >>in Javascript. This would allow allow some pretty slick >>AJAX and REST apps. > I'm doing (very slowly) exactly something like it. Actually it doesn't follow much ActiveRecord but is a simple wrapper around a REST resource, for example: var p = new PC.Rest('http://www.example.com/people'); // no credentials management yet p.collection(function(data){}); // gets data == {a: [{'name': 'Matz'}, {'name': 'David'}]} p.byId(1, function(matz){}) // gets mats == {'id': 1, 'name': 'Matz'} p.doPost({'name': 'Ermete'}); p.doPut({'id': 3, 'name': 'Ermete', 'age': 27}); However, now that I've seen ActiveResource, I could rethink about it ... > [...] > > The easy part is what he's shown. The hard part is doing relationships. To > preserve DRY, you don't want to re-specify the associations between Person and > other classes. yes, this would be the tough part. I think I'm not going to address it :-D > [...] > >>My question to the list is: If ActiveResource requires a specific >>URL layout, should we adapt RESTful Rails' connect_resource method >>to generate compatible URLs? I think that compatibility is a very good thing, especially in this case that it doesn't cost much. > [...] > > Sounds good. I'm ok with any incompatible changes you would like to make, as my > app is in an intranet and I'm also writing the client piece. I don't know how > many other people have rolled out public code. no problem with back incompatible changes, my app is still in early stage. > The REST API is being used by a desktop application and I don't care about > having the /people;new and /people/1;edit forms as nobody is going to be looking > at them, so if we could make those optional in the routes, I would appreciate that. +1 Best Regards, Nicola From pic at superfluo.org Tue Jun 27 02:33:04 2006 From: pic at superfluo.org (Nicola Piccinini) Date: Tue, 27 Jun 2006 08:33:04 +0200 Subject: [Restful-rails-general] Status codes for failed updates and deletes In-Reply-To: <449C7A95.3070709@orcaware.com> References: <449C7A95.3070709@orcaware.com> Message-ID: <44A0D120.6030105@superfluo.org> Hi, > [...] > 2) Delete against an ID that has a foreign key constraint so it cannot be > deleted, say deleting a PostType when a Post belongs_to it? > > [...] and what about a PUT request without data? 'Not Acceptable' => 406 ? Thanks -- Nicola Piccinini -- http://superfluo.org From dan.kubb at autopilotmarketing.com Tue Jun 27 02:51:38 2006 From: dan.kubb at autopilotmarketing.com (Dan Kubb) Date: Mon, 26 Jun 2006 23:51:38 -0700 Subject: [Restful-rails-general] Status codes for failed updates and deletes In-Reply-To: <44A0D120.6030105@superfluo.org> References: <449C7A95.3070709@orcaware.com> <44A0D120.6030105@superfluo.org> Message-ID: -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Nicola, > and what about a PUT request without data? > 'Not Acceptable' => 406 ? 406 Not Acceptable is for when the server cannot return the response body in any of the formats specified in the request's Accept header. This can happen if the client's Accept header is set to just text/html, but the server can only output the text/xml format, for example. I actually am not sure what the best status code would be to return for a PUT request with no data. I can think of two options: - 422 Unprocessable Entity - 400 Bad Request I usually use 422 Unprocessable Entity when I get a request that contains bad or missing data, BUT the request body is otherwise syntactically valid. Not sure if this is the best match in your case since you didn't get ANY request body at all. If you look over the official status codes in RFC 2616 or 2518 and you can't find anything that matches, then there's always the fall-back 400 Bad Request. This just says that the client sent a request that is bad in some unspecified way. I only use this as a last resort though because it doesn't provide very much information to help the client resolve the problem on their own. - -- Thanks, Dan __________________________________________________________________ Dan Kubb Autopilot Marketing Inc. Email: dan.kubb at autopilotmarketing.com Phone: 1 (604) 820-0212 Web: http://autopilotmarketing.com/ vCard: http://autopilotmarketing.com/~dan.kubb/vcard __________________________________________________________________ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2.2 (Darwin) iD8DBQFEoNV64DfZD7OEWk0RAvDVAJ95ZPsbBs4Ia0g3UUk1w/C11nNclgCgo0Aj wxhzAuJWS2Qp6Xj3bM0w5Eg= =2rTp -----END PGP SIGNATURE----- From blair at orcaware.com Tue Jun 27 12:28:51 2006 From: blair at orcaware.com (Blair Zajac) Date: Tue, 27 Jun 2006 09:28:51 -0700 Subject: [Restful-rails-general] Status codes for failed updates and deletes In-Reply-To: <8BF4C1C2-0211-4093-A51C-EF79CE682FEB@autopilotmarketing.com> References: <449C7A95.3070709@orcaware.com> <81A9A1A0-103B-4944-B92A-4824580488AB@autopilotmarketing.com> <44A0762E.8010707@orcaware.com> <8BF4C1C2-0211-4093-A51C-EF79CE682FEB@autopilotmarketing.com> Message-ID: <44A15CC3.3080206@orcaware.com> Dan Kubb wrote: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Hi Blair, > >>>> 1) Delete against an id that was already deleted? >>> >>> 410 Gone. This status means that the URI was explicitly removed. >> >> Since the row in the DB will be deleted, there's no way to know if a >> URL once existed and now no longer exists or if it never existed. >> >> So you would use 410 Gone for both cases, even if somebody tries to >> GET /people/123456789? > > That's a good question. I would use 410 Gone *only if I knew* the > underlying data had been deleted. Otherwise I'd use 404 Not Found. > > With most designs, there's no way to know if something was explicitly > deleted. I've heard of hacks to determine this from other data, like > seeing if the requested id is less than MAX(id), an approach which I > personally think is a bad idea. > > If you were using something like acts_as_paranoid, then you could tell > the difference between a deleted record and a record that just doesn't > exist. The downside to acts_as_paranoid is that you can't use foreign key > constraints to make sure "linked to" records aren't deleted. Yeah, I'm not going to those lengths. I'll just use the 404 in either case. > >>> What about using HTTP's own optimistic locking system? The appropriate >>> response when a client is submitting a request to an out-of-date >>> resource is to return 412 Precondition Failed (except if the request >>> is a GET/HEAD, in which case you return 304 Not Modified). >> >> If I do that, then does that require the use of any additional HTTP >> headers, such as If-Match with the ETag header? And then I would >> presumably drop the timestamp in the URL? > > Yes, it does require usage of HTTP headers, and you'll be able to > drop the timestamp from the URL. > > In the response you'd return an ETag and Last-Modified header, > which will automatically be done for you if all your models are > assigned to the conditions object, like this: > > conditions << @model_instance Yes, I do that now. > Remember to do this at the top of the resource code, before your > first method handler block. > > For future requests from the client you'd take the ETag you > originally got and send it back in the If-Match header. The > Last-Modified response header should also be sent in the > If-Unmodified-Since request header. > > Provided that the conditions object knows about all the models > it will return a 412 Precondition Failed response if the client > has a "stale" copy of the resource. All this presumes a behaving client that PUTs to the resource URL with the If-Match header. If you have a client that PUTs without the If-Match, then it could still overwrite data? So I would have to check in the controller if the ETag and If-Match http headers are set, otherwise rejecting the request? Also, using the ETag gets a little tricky, since in my application, I have controllers that return a resource and sometimes the resource's relationships as a performance optimization. For example, I have GET /node/123 will also return the node's properties, which is a has_many relationship. So the conditions looks like conditions << @node conditions << @node.properties so with the returned XML, it looks like ... other elements for node's fields ... other elements for property's fields ... other elements for property's fields This is nice since the client can cache all the XML. Regards, Blair -- Blair Zajac, Ph.D. Subversion training, consulting and support http://www.orcaware.com/svn/ From pic at superfluo.org Wed Jun 28 02:02:59 2006 From: pic at superfluo.org (Nicola Piccinini) Date: Wed, 28 Jun 2006 08:02:59 +0200 Subject: [Restful-rails-general] Accept header and cache In-Reply-To: References: <449C7A95.3070709@orcaware.com> <44A0D120.6030105@superfluo.org> Message-ID: <44A21B93.2070902@superfluo.org> > 406 Not Acceptable is for when the server cannot return the response > body in any of the formats specified in the request's Accept header. > [...] apropos of Accept header: suppose you are serving different content depending on this header, let's say xml and json. Further suppose to make two consecutive request for the same resource ( /resources/5 ), the first with an Accept header for xml content, the second with Accept header for json content. It happens that the second request gets a 304 response status and hence the client reuses the previous content. The problem is that the previous content is an xml document, while it needs a json document. The simple workaround is to add a "format" parameter to the link, so the two requests become /resources/5?format=xml and /resources/5?format=json and the second one doesn't get a 304 response. This follows new conventions in edge rails ( http://www.ryandaigle.com/articles/2006/06/01/whats-new-in-edge-rails-parameter-driven-response-type-determination ) but I'm not sure it's a good thing. What do you think about? Thanks -- Nicola Piccinini -- http://superfluo.org From dan.kubb at autopilotmarketing.com Wed Jun 28 05:15:26 2006 From: dan.kubb at autopilotmarketing.com (Dan Kubb) Date: Wed, 28 Jun 2006 02:15:26 -0700 Subject: [Restful-rails-general] Accept header and cache In-Reply-To: <44A21B93.2070902@superfluo.org> References: <449C7A95.3070709@orcaware.com> <44A0D120.6030105@superfluo.org> <44A21B93.2070902@superfluo.org> Message-ID: -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Nicola, >> 406 Not Acceptable is for when the server cannot return the response >> body in any of the formats specified in the request's Accept header. >> [...] > > apropos of Accept header: > suppose you are serving different content depending on this header, > let's say xml and json. > Further suppose to make two consecutive request for the same > resource ( > /resources/5 ), the first with an Accept header for xml content, the > second with Accept header for json content. > It happens that the second request gets a 304 response status and > hence > the client reuses the previous content. The problem is that the > previous > content is an xml document, while it needs a json document. I know that this type of thing happens more often than not, but if you see a client behave in this manner it is most certainly broken. In the original response the Vary header should be used to indicate that the server used the Accept header to do content negotiation to generate the response. On future requests, if the client notices that its sending a different Accept header it should NOT attempt to send the earlier response's ETag and Last-Modified header as the If-Match and If-Unmodified-Since header respectively. Its only when a request matches a previous request *exactly* (as far as the Vary headers go) that it should attempt to do a conditional GET request. > The simple workaround is to add a "format" parameter to the link, > so the > two requests become /resources/5?format=xml and /resources/5? > format=json > and the second one doesn't get a 304 response. > > This follows new conventions in edge rails ( > http://www.ryandaigle.com/articles/2006/06/01/whats-new-in-edge- > rails-parameter-driven-response-type-determination > ) > but I'm not sure it's a good thing. What do you think about? I'm not entirely sure its a good thing either -- not because the developers are doing this, but because there is such bad support for content negotiation within clients that forces developers to do this sort of thing. I can't imagine a developer preferring this approach to content negotiation if it were supported by major browsers. Currently, without resorting to a hack like this, there's no easy way for me to tell my client what format I'd like to have the data in. If I go to a collection URI my browser is most likely going to get back HTML. Maybe I want the data in CSV format so I can export it to an offline program. I actually am about to run into this same problem this week. There might be hope though. With HTML there is a way to create an anchor that includes the Content-Type AND the URI like this: A List of all People (CSV) I don't know if any browsers actually respect this enough to send the proper Accept headers. I would doubt there are yet. As far as bots, AJAX clients and special purpose user agents, I don't think this sort of hack is necessary. Actually I would highly discourage it as a way of interfacing with REST services. However I do understand why its necessary as a stop-gap to get current browsers to work with content negotiation ... I think its ok as long as effort goes into changing the browsers enough so we can drop this at some point in the future. For my own uses, how I'd probably not use a query string for the format and would instead just have extensions like the following: /people /people.csv /people.xml It should be possible to set this up as a route like this: connect '/:controller.:format', :action => 'collection' ... which should allow /people or /people.csv, but I've not tested this myself yet. Yeah I know this is bad from a Cool URI's point of view, but at least people understand what I mean when I give URIs like the above. Even when building RESTful systems sometimes its important to be pragmatic and accept that browsers may need hacks to allow them to work with those systems. My hope is that with increased awareness of REST and HTTP that browsers will be improved and the hacks can be tossed aside. - -- Thanks, Dan __________________________________________________________________ Dan Kubb Autopilot Marketing Inc. Email: dan.kubb at autopilotmarketing.com Phone: 1 (604) 820-0212 Web: http://autopilotmarketing.com/ vCard: http://autopilotmarketing.com/~dan.kubb/vcard __________________________________________________________________ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2.2 (Darwin) iD8DBQFEokiu4DfZD7OEWk0RArtEAJ4znpdGFB/ykcsNsREmbbgnXXDmMQCcCxD4 HYCwtLFNHbAYHemYUCohhcI= =Jnzd -----END PGP SIGNATURE-----