SyntaxError on has_many association with block when trying to order

Hi,

in Ruby on Rails 4, let's say a parent has many children. Then I wanted to reference only the persisted records in an active record association, and followed this link's accepted answer (ruby on rails - How do you reference only the persisted records in an active record association - Stack Overflow). This works good:

class Parent < ActiveRecord::Base   has_many :children, dependent: :destroy do     def persisted       collect { |a| a if a.persisted? }     end   end

Now, I want to order the associated records:

has_many :children, dependent: :destroy, -> { order 'id asc' } do

but this raises an error:

SyntaxError in ParentsController#index

...trunk/app/models/parent.rb:3: syntax error, unexpected keyword_do_block, expecting => ...trunk/app/models/parent.rb:49: syntax error, unexpected keyword_end, expecting end-of-input

However, this does work:

has_many :children, -> { order 'id asc' } do

I can't even find documentation on how to use the do_block on an association. I'd like to know why it doesn't work what I am trying to do. Any help appreciated.

Antonio Moreno wrote in post #1155440:

Now, I want to order the associated records:

has_many :children, dependent: :destroy, -> { order 'id asc' } do

but this raises an error:

SyntaxError in ParentsController#index

has_many :children, dependent: :destroy, -> { order 'id asc' } do

This line looks wrong to me. You have a lambda arrow (-> with it's block { ... } and immediately trying to open another block with do. Written a slightly different way it might look something like:

has_many :children, dependent: :destroy, -> do order 'id asc' end do

Here's an example that maybe shows what you're trying to do:

has_many :tracks, -> { order "position" }, dependent: :destroy

In this case it appear to me the lambda function is in the "scope" argument with options following and no block.

Robert,

thanks for your answer.

has_many :children, dependent: :destroy, -> { order 'id asc' } do

This line looks wrong to me. You have a lambda arrow (-> with it's block { ... } and immediately trying to open another block with do. Written a slightly different way it might look something like:

has_many :children, dependent: :destroy, -> do order 'id asc' end do

It's interesting, because this line:

has_many :children, -> { order 'id asc' } do

works fine (except at destroying associated records, of course), so it seems it's not a problem with two blocks, right?

Here's an example that maybe shows what you're trying to do:

has_many :tracks, -> { order "position" }, dependent: :destroy

In this case it appear to me the lambda function is in the "scope" argument with options following and no block.

I don't understand what you mean with this, did you miss the "do" at the end of the line. Anyways, I tried the order I think you are suggesting:

has_many :children, -> { order 'id asc' }, dependent: :destroy do

with no luck. So it seems that I have to choose between having 2 out of the 3 things I need.

In general if you have a “do” you need an “end”, although sometimes ruby interprets the end of your block for you. (but generally it is good practice to always end a “do” with an “end”)

Your discrepancy (and confusion) has to do with the way has_many is receiving arguments.

In Rails 4, the second parameter is scope (in rails 3, the second parameter was options)

has_many :children, dependent: :destroy, → { order ‘id asc’ } do

you have your arity reversed — the block you are trying to pass in should come first, followed by the options (this is documented here http://apidock.com/rails/v4.0.2/ActiveRecord/Associations/ClassMethods/has_many)

I don’t know why would need or want a “do” statement at the end of that line or anywhere in that line – it is incorrect, just remove it.

Jason,

thanks for your answer. The link you provided was useful.

In general if you have a "do" you need an "end",

It does have an end, I left it out of the reply because it is in the first post. The complete statement I'd like to have would be:

  has_many :children, -> { order 'id asc' }, dependent: :destroy do     def persisted       collect { |a| a if a.persisted? }     end   end

What I'm trying to do is briefly mentioned in the page you referenced (has_many (ActiveRecord::Associations::ClassMethods) - APIdock) but there is no link to further documentation or examples. I just would need to add the scope to the example, from:

has_many :children, :dependent => :destroy do   def at(time)     proxy_owner.children.find_with_deleted :all, :conditions => [       "created_at <= :time AND (deleted_at > :time OR deleted_at IS NULL)", { :time => time }     ]     end   end

to

has_many :children, -> { order "posted_on" }, :dependent => :destroy do

  (rest of the code)

but this is not covered in THAiSi's note. I think this is very useful, and should be easier to find documentation.

Regards.

I believe this (explicitly wrapping the options hash) should work with your extension:

  has_many :children, -> { order 'id asc' }, { dependent: :destroy } do     def persisted       collect { |a| a if a.persisted? }     end   end

Look at the 'Association extensions' section on the has_many doc page.

Hassan,

I believe this (explicitly wrapping the options hash) should work with your extension:

  has_many :children, -> { order 'id asc' }, { dependent: :destroy } do

thanks! That did it!

Is it too much to ask why the options hash has to be wrapped?

Regards.

Sorry, I'm not a parser internals guy :slight_smile:

But when I run into a wtf? syntax error I try to imagine how the line could be ambiguous to evaluate, and make it as explicit as possible.

Sometimes that's all it takes :slight_smile:

Is it too much to ask why the options hash has to be wrapped?

I think it might be related to that option hashes can optionally be "unwrapped" when are the last argument, but must be wrapped otherwise:

I consider this solved.

Thanks again for your assistance, all of you.