“...I've been working since 2008 with Ruby / Ruby on Rails, love a bit of Elixir / Phoenix and learning Rust. I also poke through other people's code and make PRs for OpenSource Ruby projects that sometimes make it. Currently working for InPay...”

Rob Lacey (contact@robl.me)
Senior Software Engineer, Brighton, UK

rewhere

Interesting problem requiring us to remove the where values on an ActiveRecord::Relation. The original heavy handed method seems to play havoc with chained where calls and the id of the original proxy_association.owner would get used in a later where. Here we can see that our for_me method seems to cause the else_id to be the same as our recipient_id

[5] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_7::Nested_1>)> user.somethings.where(else_id: something.else_id).to_sql
=> "SELECT `somethings`.* FROM `somethings` WHERE `somethings`.`user_id` = 20323 AND `somethings`.`else_id` = 6465  ORDER BY id DESC"
[6] pry(#<RSpec::Core::ExampleGroup::Nested_1::Nested_7::Nested_1>)> user.somethings.for_me.where(else_id: something.else_id).to_sql
=> "SELECT `somethings`.* FROM `somethings` WHERE `somethings`.`recipient_id` = 20323 AND `somethings`.`else_id` = 20323  ORDER BY id DESC

when in doubt read the Rails source

# remove user_id = proxy_association.owner.id from scope
    # so it can be reapplied by to_me or for_me
    def global_scope
-      s = respond_to?(:scoped) ? scoped : all
-      s.where_values = []
-      s
+      unscope(where: :user_id)
    end

which is the way that rewhere reassigns values in where rather than chaining again

2.2.2 :006 > Something.where(user_id: 1).where(user_id: 2).to_sql
=> "SELECT `somethings`.* FROM `somethings` WHERE `somethings`.`user_id` = 1 AND `somethings`.`user_id` = 2"
2.2.2 :007 > Something.where(user_id: 1).rewhere(user_id: 2).to_sql
=> "SELECT `somethings`.* FROM `somethings` WHERE `somethings`.`user_id` = 2"