0

If you view the query, @bot_client_id is a string with the same value of string. However, I get different results when using them as part of my query.

Why?

[24] pry(#<BotResponse>):1> string
=> "aiaas-1409611358153-user-0149"

[25] pry(#<BotResponse>):1> @bot_client_id
=> "aiaas-1409611358153-user-0149"

[26] pry(#<BotResponse>):1> Event.where.has{(status == 'active') & (bot_client_id == @bot_client_id)}
=> []
[27] pry(#<BotResponse>):1> Event.where.has{(status == 'active') & (bot_client_id == string)}
=> [#<Event:0x0000000464d120
  id: 22,
  bot_client_id: "aiaas-1409611358153-user-0149",
  keyword: "gratitude",
  topic: nil,
  status: "active",
  channel: "telegram",
  created_date: 2017-05-09 15:56:51 UTC,
  tickle_expression: "daily",
  time_of_day: "7:00 am",
  next_occurrence: 2017-05-14 14:00:00 UTC,
  time_zone: "America/Los_Angeles",
  recurring: true>,
 #<Event:0x0000000464cfb8
  id: 23,
  bot_client_id: "aiaas-1409611358153-user-0149",
  keyword: "daily_check",
  topic: nil,
  status: "active",
  channel: "telegram",
  created_date: 2017-05-10 04:25:47 UTC,
  tickle_expression: "daily",
  time_of_day: "9:00 am",
  next_occurrence: 2017-05-14 16:00:00 UTC,
  time_zone: "America/Los_Angeles",
  recurring: false>]
4
  • 1
    What does @bot_client_id.class return? I ask because that @bot_client_id returns a string in the console, does not necessarily mean that it is a string. It's inspect or to_s method might just be overridden. Commented May 14, 2017 at 6:48
  • What is this .has method? Is it from activerecord or some additional library? Commented May 14, 2017 at 6:49
  • It returns a string class. Is checked that as well. Commented May 15, 2017 at 4:45
  • .has is from the baby squeel gem Commented May 15, 2017 at 4:45

2 Answers 2

2

Looking at your code:

Event.where.has{(status == 'active') & (bot_client_id == @bot_client_id)}

has takes a block where status and bot_client_id are called. It seems unlikely for these two methods to be defined in an outside context.

Let's see what happens when we try to call a block with undefined variables:

>> def method_calling_block
|    yield
|  end #=> :method_calling_block

>> method_calling_block { abcd }
NameError: undefined local variable or method `abcd' for main:Object
    from (irb):44:in `block in irb_binding'
    from (irb):40:in `method_calling_block'
    from (irb):44
    from /Users/fylooi/.rvm/rubies/ruby-2.1.4/bin/irb:11:in `<main>'

If we don't call the block:

>> def method_without_calling_block
|  end #=> :method_without_calling_block

>> method_without_calling_block { abcd }  #=> nil

Looks like variables passed into a block are only evaluated when the block is processed (normally using yield or block.call). Looks like this block isn't handled the normal way.

A bit of googling reveals that .where.has{} is a method belonging to BabySqueel. The BabySqueel documentation states that:

BabySqueel's blocks use instance_eval, which means you won't have access to your instance variables or methods.

The relevant method appears to be evaluate in https://github.com/rzane/baby_squeel/blob/master/lib/baby_squeel/dsl.rb

module BabySqueel
  class DSL < Relation
    def evaluate(&block)
      if block.arity.zero?
        instance_eval(&block)
      else
        yield(self)
      end
    end 
  end
end

This means that in the context of

Event.where.has{(status == 'active') & (bot_client_id == @bot_client_id)}

@bot_client_id is evaluated within the receiver's binding (which is a BabySqueel::DSL object) instead of the caller's binding. Naturally, it returns nil.

To force the block to be evaluated the usual way (ie. using variables bound to the caller), you need to make block.arity.zero? evaluate to false by passing an argument to it.

Event.where.has{|event| (event.status == 'active') & (event.bot_client_id == @bot_client_id)}
Sign up to request clarification or add additional context in comments.

1 Comment

Ah I think you got it. Well described. Thank you.
0

As I understand has {} method is a closure which changes of rubies self scope. You could see it via following command:

p self
Event.where.has{ p self; (status == 'active') & (bot_client_id == @bot_client_id) }

If outputs are different that means @bot_client_id in second execution returns nil.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.