Forum: Ruby on Rails SyntaxError on has_many association with block when trying to order

97619dadec2be401e5601cdb881b09d5?d=identicon&s=25 Antonio Moreno (amoreno333)
on 2014-08-18 21:26
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
(http://stackoverflow.com/questions/19842765/how-do...).
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.
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2014-08-18 23:19
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.
97619dadec2be401e5601cdb881b09d5?d=identicon&s=25 Antonio Moreno (amoreno333)
on 2014-08-19 22:25
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.
Dfc7587fd73f2efa19d6f1f9611b70ba?d=identicon&s=25 Jason Fb (jasonfb)
on 2014-08-19 22:48
(Received via mailing list)
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/Assoc...)

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.
97619dadec2be401e5601cdb881b09d5?d=identicon&s=25 Antonio Moreno (amoreno333)
on 2014-08-19 23:20
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
(http://apidock.com/rails/ActiveRecord/Associations...)
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.
Bee69cfed999cd13e3bff73d472a39ee?d=identicon&s=25 Hassan Schroeder (Guest)
on 2014-08-19 23:26
(Received via mailing list)
On Tue, Aug 19, 2014 at 1:25 PM, Antonio Moreno <lists@ruby-forum.com>
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 Schroeder ------------------------ hassan.schroeder@gmail.com
http://about.me/hassanschroeder
twitter: @hassan
97619dadec2be401e5601cdb881b09d5?d=identicon&s=25 Antonio Moreno (amoreno333)
on 2014-08-19 23:43
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.
Bee69cfed999cd13e3bff73d472a39ee?d=identicon&s=25 Hassan Schroeder (Guest)
on 2014-08-20 00:12
(Received via mailing list)
On Tue, Aug 19, 2014 at 2:43 PM, Antonio Moreno <lists@ruby-forum.com>
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 :-)

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 :-)

--
Hassan Schroeder ------------------------ hassan.schroeder@gmail.com
http://about.me/hassanschroeder
twitter: @hassan
97619dadec2be401e5601cdb881b09d5?d=identicon&s=25 Antonio Moreno (amoreno333)
on 2014-08-20 00:39
>> 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:

http://stackoverflow.com/questions/25194587/syntax...

I consider this solved.

Thanks again for your assistance, all of you.
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.