rzane/baby_squeel

Converting having

Closed this issue · 11 comments

Not sure how to convert this clause from squeel:

having{`completion`.op(compl_string == 'complete' ? '=' : '<', 1.0)}

where 'compl_string is a local variable. Just replacing having with when_having and ticks with quotes doesn't work. Thanks.

rzane commented

Yeah, backticks don't work like that in baby squeel. Is that not a column? Ideally, you should avoid using a sql literal, but, if you absolutely must, I think you're looking for:

sql('completed')

No it's not a column, it's generated by an "...as('completed')" clause, is there anything better I can use? Anyway that seems to take care of the 'completed', but now I'm getting:

ActionView::Template::Error (Unsupported argument type: Float. Construct an Arel node instead.):
["    1: = render @assignments", "    2: = render_now if controller.request.format.js?"]  /usr/local/rvm/gems/ruby-2.4.1@global/gems/arel-7.1.4/lib/arel/visitors/to_sql.rb:756:in `unsupported'
  /usr/local/rvm/gems/ruby-2.4.1@global/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit'
  /usr/local/rvm/gems/ruby-2.4.1@global/gems/arel-7.1.4/lib/arel/visitors/to_sql.rb:777:in `visit_Arel_Nodes_InfixOperation'
  /usr/local/rvm/gems/ruby-2.4.1@global/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit'
...

Looks like the float 1.0 doesn't work as is? Thanks.

rzane commented

I see. Check out this issue: #41.

Basically, I think you need to say: quoted(1.0).

Ok that works thanks. But I found it also works with an integer instead of the float (but I need a float since 'completion' is float). Why would it work with one and not with the other? Also when should I use 'sql' and when 'quoted'? I also found that quoted('completed') works too. Thanks.

rzane commented

I think Arel just doesn't know how to handle floats, and I think baby squeel would have to do some nasty stuff to get that to work without using quoted.

The difference between quoted and sql can be seen here:

def sql(value)
Nodes.wrap ::Arel.sql(value)
end
# Quotes a string and marks it as SQL
def quoted(value)
sql _scope.connection.quote(value)
end

The difference is that sql just gives you literal sql and marks it as "safe" for the database.

[3] pry(main)> Arel.sql 'something'
=> "something"

ActiveRecord internally "quotes" values before they hit the database. Using baby_squeel's quoted method basically does this:

[4] pry(main)> Arel.sql Post.connection.quote('something')
=> "'something'"

I wouldn't use quoted to reference a column (or an alias). It's really only for values.

Ok it makes sense, thanks. I see that quoted knows not to generate actual quote signs around floats. Although for identifiers it would be nice if there were a way to generate backticks. Is there any? Not that I really need it.

Here's another issue with converting a having condition from squeel. In squeel I have something like:

Student.having{`team_role` < `team_size` - 1}

where team_role and team_size are aliases from a preceding select. I tried to convert it to:

Student.when_having{sql('team_role') < sql('team_size') - 1}

but I'm getting an error:

NoMethodError: undefined method `-' for BabySqueel{"team_size"}:BabySqueel::Nodes::Node
Did you mean?  -@

What's wrong? Thanks.

rzane commented

If you're just using a SQL literal anyway, there is absolutely no reason to use Baby Squeel. You're just making the query more complicated.

Just do this: having('team_role < team_size - 1')

Well, I use sql literals only because aliases don't work otherwise. I'd be glad to write them directly as:

Student.when_having{team_role < team_size - 1}

In squeel backticks were at least getting close.

rzane commented

The specific issue here was fixed with: cf7d7d0.

Yes that works, thanks. Btw is there a similar solution for the issue with the 1.0 above? I have it in a few places, that's also something that works fine with squeel. I also have nil in function calls in a number of places, that doesn't work either, although it was working fine with squeel, I guessing it's the same issue. Thanks.