Relationship urls

320 views
Skip to first unread message

Peter Ehrlich

unread,
Feb 7, 2012, 2:33:19 PM2/7/12
to spi...@googlegroups.com
Normally, a REST Post model has an endpoint of '/posts'.

When a post is associated with a channel, this endpoint would become '/channel/id/posts'.

How can this be done?  I would expect both of these to work, but neither does:

class window.Post extends Spine.Model

  @configure 'Post',
      'body'

  @extend Spine.Model.Ajax

  @belongsTo 'channel', 'Goal'

  # instance method is not used when available
  url: (path)->
    @channel.url('post', path)


# parameter is not read when given
post.save({url: 'abcde'})

Thanks!
--Peter

Alex MacCaw

unread,
Feb 7, 2012, 3:01:39 PM2/7/12
to spi...@googlegroups.com
Override url - make it a function that checks to see if this.channel_id exists and creates an appropriate url.
--
Alex MacCaw

+12147175129
@maccman

http://alexmaccaw.co.uk

Peter Ehrlich

unread,
Feb 7, 2012, 3:28:19 PM2/7/12
to spi...@googlegroups.com
So I'm not sure what you mean-- whether on a class or instance level, so I tried this:

@url: (path)->
    console.log 'url class', this, arguments

  url: (path)->
    console.log 'url instance', this, arguments
    @channel().url('post', path)


@url wasn't called, but used literally:

This doesn't make any sense to me, because it looks like the inner function calls url on the instantiated resource:

getURL: (object) ->
    object and object.url?() or object.url

Peter Ehrlich

unread,
Feb 7, 2012, 3:29:57 PM2/7/12
to spi...@googlegroups.com
running 1.0.5 off of github

Alex MacCaw

unread,
Feb 7, 2012, 3:57:59 PM2/7/12
to spi...@googlegroups.com
Well, it'll be on an instance level - otherwise 'this.channel_id' won't be present. If it's a function, it should be invoked.

On Tue, Feb 7, 2012 at 12:29 PM, Peter Ehrlich <peter.i...@gmail.com> wrote:
running 1.0.5 off of github

Peter Ehrlich

unread,
Feb 7, 2012, 4:03:42 PM2/7/12
to spi...@googlegroups.com
Right. That was my first thought:

Spine.Ajax.getURL(Post.first())
> "/goals/17/post/"

And yet, when create action is called, the POST request goes to /posts

Peter Ehrlich

unread,
Feb 7, 2012, 4:08:35 PM2/7/12
to spi...@googlegroups.com
A little hunting shows the problem code:

 Singleton.prototype.create = function(params, options) {
      var _this = this;
      return this.queue(function() {
console.log('inside ajax create', Ajax.getURL(_this.model));
        return _this.ajax(params, {
          type: 'POST',
          data: JSON.stringify(_this.record),
          url: Ajax.getURL(_this.model)

One possible solution would be to try the record first, and then the model

Peter Ehrlich

unread,
Feb 7, 2012, 4:17:08 PM2/7/12
to spi...@googlegroups.com
Forgive my js/cs inexperience, but as far as I can see, there's no way to patch this for my own use, without going in and mucking around the spine source itself, right?


Does hem do anything specific for spine besides making sure that required files get included?  I may just remove those lines so that I can use the coffeescript in rails without being forced to mess around with hem watch

BARTCC

unread,
Feb 7, 2012, 4:27:41 PM2/7/12
to spi...@googlegroups.com
Sorry to intrude this, but wasn't this globally accesible a couple of weeks ago.
Big parts of my app builds on this accessibility
> -- Singleton is inaccessible

Vincent De Snerck

unread,
Feb 9, 2012, 2:53:03 PM2/9/12
to spi...@googlegroups.com
Peter,

Spine has 2 different url methods. One on the model and one on the records (instances). This is necessary because when creating a record the url differs from all the other actions. 

The problem however is that spine creates this difference by passing along only the model (with it's own unique url function) to the Ajax.getUrl function. This makes it so that we'd never be able to acces an instance variable that is needed for our nested urls. Simply providing the record here would cause all sorts of issues.

So I removed the url method on the model and the records and I created 2 different methods both on the records. One that gets called when a create is made (resourceUrl) and one that gets called for all the rest (recordUrl).

So now you just have to override the resourceUrl instance method on your model and you are set. In your case this would be:

class App.Post extends Spine.Model
  @configure 'Post', 'channel_id', 'title'
  @extend Spine.Model.Ajax

  resourceUrl: ->
    "channels/#{@channel_id}/posts"

  @hasOne 'channel', 'App.Channel'

In this case the recordUrl will be the resourceUrl + the the record id. But you can override the recordUrl as well if you're doing something fancy.

My fork is at https://github.com/SpoBo/spine but I haven't compiled the CS into JS yet. Not sure if there are any bugs though but it seems to work properly for me and the existing spec still passes.

Eleo

unread,
Feb 27, 2012, 10:15:24 PM2/27/12
to spi...@googlegroups.com
I find it kind of annoying that Spine doesn't allow me to do this natively.

However, you can send params to your url with the fetch() method.  e.g. Post.fetch({ data: 'foo=bar' }) will append those params to the url.  (I also tried sending data as an object itself but for some reason it isn't working.  Maybe an oversight by me or a bug?  For example { data: { foo: 'bar' } } doesn't work.

It could be argued that your API should not use nested routes.  However, it's probably more convenient to work with existing routes.

Personally, I've changed up my approach.  Rather than use nested routes I will just go based on the resource name.

I'm using Padrino, which gives me the option to mount multiple apps within a single project.  I'm not sure if Rails allows you to do this, or how easily.  I've created a separate app just for the API.  Instead of doing for example board/1/posts, I'll do api.domain.com/posts?board_id=1.  I'll extract any duplicative functionality into a global helper if it pops up.

Maybe this seems silly, but I think there's some validity to it.  For one, most of my routes that serve HTML are purposely contrived to look pretty to the end user, and sometimes obfuscate what resource they represent.  It's probably just not a good idea to use those same routes to serve JSON as part of a RESTful API.  Secondly, there are sometimes some subtleties in the way JSON is served versus HTML; so it kind of makes sense to separate the handling of both.

Still, I think Spine could be better with its URLs, and I might not have even considered this route had I not been backed into this corner.  Although I think my approach to the problem is still sound, and may even be better design for my project.
Reply all
Reply to author
Forward
0 new messages