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.

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.

Antonio M. 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.

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
has_many (ActiveRecord::Associations::ClassMethods) - APIdock)

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.

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.

On Tue, Aug 19, 2014 at 1:25 PM, Antonio M. [email protected]
wrote:

Anyways, I tried the order I think you are suggesting:

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

with no luck.

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 S. ------------------------ [email protected]

twitter: @hassan

On Tue, Aug 19, 2014 at 2:43 PM, Antonio M. [email protected]
wrote:

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?

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:


Hassan S. ------------------------ [email protected]

twitter: @hassan

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.