Jonathan
Jonathan

Reputation: 11494

undefined method `and' for #<Arel::Attributes::Attribute

I'm having an issue getting a query to work.

I'm essentially trying to write something like the following SQL, with the literal 5 replaced with a variable:

SELECT * 
FROM "my_table"
WHERE 5 BETWEEN "my_table"."minimum" AND "my_table"."maximum"

This is what I have at the moment:

    MyModel.where(
      Arel::Nodes::Between.new(
        my_variable, (MyModel.arel_table[:minimum]).and(MyModel.arel_table[:maximum])
      )
    )

Please ignore the way I am using arel_table, the actual query has multiple joins and is more complex, but this is the most minimum reproducible example I have to demonstrate the problem.

The error, as in the subject of the question is as follows:

undefined method `and' for #<Arel::Attributes::Attribute:0x00007f55e15514f8>

Upvotes: 0

Views: 1308

Answers (1)

engineersmnky
engineersmnky

Reputation: 29478

and method is for Arel::Nodes::Node i.e. MyModel.arel_attribute[:name].eq(Arel::Nodes::Quoted.new('engineersmnky')) This is an Arel::Nodes::Equality and you can chain with and.

That being said you can construct an Arel::Nodes::And for yourself via

Arel::Nodes::And.new([left,right])

Then we can pass this to the Between class like so

Arel::Nodes::Between.new(
  Arel::Nodes::Quoted.new(my_variable),
  Arel::Nodes::And.new([
    MyModel.arel_table[:minimum], 
    MyModel.arel_table[:maximum]])
)

The Arel::Nodes::Quoted (also: Arel::Nodes.build_quoted(arg)) is not needed in your case since your my_variable is an Integer which can be visited and will be treated as an Arel::Nodes::SqlLiteral but I find it best to let arel decide how to handle the quoting in case your my_variable ends up being some other un-visitable Object

There are other ways to create a Between and other ways to create an And depending on what objects you are dealing with.

between is a Arel::Predication and these predications are available to Arel::Nodes::Attribute objects e.g.

 MyModel.arel_table[:minimum].between([1,6])

and as mentioned is available to Arel::Nodes::Node and instances of this class provides a convenience method (create_and) for creating an And so we could do the following:

Arel::Nodes::Node.new.create_and([
     MyModel.arel_table[:minimum],
     MyModel.arel_table[:maximum]])

There are a number of other ways to hack this functionality together by using other Arel classes but this should get you headed in the right direction.

Upvotes: 2

Related Questions