what is motivation for setting klass = self in this section of code
block, discourse, ruby, ruby-on-rails
Solution
First of all:
Is the block creating a new scope where self refers to the class of MessageBus?
Nope.
If MessageBus.subscribe was an instance of a MessageBus (say m_bus.subscribe) would self refer to m_bus in the block?
Nope.
Does the fact that ensure_class_listener is class method have any bearing on this?
Nope.
Let's start with a simple example:
def test_self
p self
2.times do |n|
p self
end
end
test_self
prints out
main
main
main
As you can see self refers to the same object, the top level `main` object.
Now, to the interesting part:
class Foo
def test_self(&block)
block.call
end
end
p self
Foo.new.test_self do
p self
end
gives
main
main
Not too surprising, we are passing a block along and calling from within our object. But if we try with this:
class Foo
def test_self(&block)
instance_eval(&block)
end
end
p self
Foo.new.test_self do
p self
end
gives
main
#<Foo:0x007f908a97c698>
WUT???
Ruby's `instance_eval` can take a block and run it using the current object as `self`: in this way the same block of code changed its meaning.
Therefore, my assumption is that MessageBus is doing something equivalent: for this reason, we can't pass self from within the block because it will change its meaning when being instance_evaled
EDIT!!!
I've looked at the message bus implementation and there isn't a good reason why we should do `klass = self`.
Look here, we take the block and save it in an internal data structure:
def subscribe_impl(channel, site_id, &blk)
# ...
@subscriptions[site_id][channel] << blk
ensure_subscriber_thread
blk
end
Now let's look at what ensure_subscriber_thread does:
multi_each(globals,locals, global_globals, local_globals) do |c|
# ...
c.call msg
# ...
end
So it just calls the block, no `instance_eval` or `instance_exec` at all!
My new Hypothesis
Discourse is an application with a lof of Javascript; it is a very common pattern in Javascript to do this:
var self = this;
$("ul.posts").click(function() {
// here this does refer to the DOM element
self.doStuff();
})
So I guess it just leaked into ruby aswell, note that it doesn't do anything wrong, it's just useless! :D
Problem
I was looking through some code in discourse and stumbled across this and was wondering why klass = self. As I know they are better ruby developers than I, there must be a good reason. Why wouldn't they call self.remove_from_cache!(message["key"], false)? Is the block creating a new scope where self refers to the class of MessageBus? Are there other examples of where you would need to create this type of construct in Ruby or is this the main one? If MessageBus.subscribe was an instance of a MessageBus (say m_bus.subscribe) would self refer to m_bus in the block? Does the fact that ensure_class_listener is class method have any bearing on this? Sorry for all the questions but just want to be sure. thx https://github.com/discourse/discourse/blob/master/app/models/site_customization.rb#L118 ``` def self.ensure_cache_listener unless @subscribed klass = self MessageBus.subscribe("/site_customization") do |msg| message = msg.data # what would self her refer to # what would self her refer to # would self.remove_from_cache!(message["key"], false) klass.remove_from_cache!(message["key"], false) end @subscribed = true end end ``` EDIT #1 The implementation of MessageBus.subscribe appears to be here: https://github.com/SamSaffron/message_bus/blob/master/lib/message_bus.rb#L217