Introducing Object#self

I just came across a situation where it would be extremely handy to
have a method that returns the receiver.

Imagine the following example:

filter = %w(with_votes without_votes).include?(params[:filter]) ?
params[:filter].to_sym : :self
Idea.published.send(filter).all

The implementation would be pretty simple:

class Object
  def self
    self
  end
end

Should this be a part of Rails?

I just came across a situation where it would be extremely handy to

have a method that returns the receiver.

Imagine the following example:

filter = %w(with_votes without_votes).include?(params[:filter]) ?

params[:filter].to_sym : :self

Idea.published.send(filter).all

The implementation would be pretty simple:

class Object

def self

self

end

end

Should this be a part of Rails?

Doesn’t tap method in 1.9 do that already?

Anuj

Very similar.

tap will yield the block passed to it then return self:

def tap

yield self

self

end

Object#tap needs a block.

Joao

I just came across a situation where it would be extremely handy to
have a method that returns the receiver.

Imagine the following example:

filter = %w(with_votes without_votes).include?(params[:filter]) ?
params[:filter].to_sym : :self
Idea.published.send(filter).all

The implementation would be pretty simple:

class Object
def self
self
end
end

Should this be a part of Rails?

Doesn't tap method in 1.9 do that already?

Object#tap requires a block.

Rails has Object#presence, which is

def presence
  self if present?
end

So for *most objects* it does what you want (except for empty arrays,
strings, etc)

I wouldn’t call this use case where it would be “extremely handy”. Here is better code for it:

filters = Idea.scopes.keys & Array.wrap(params[:filter]).map(&:to_sym)

records = filters.inject(Idea.published) { |model, name| model.send(name) }.all

First bonus feature is that it knows about all your named scopes, you don’t have to specify them in the controller (business logic in your controller = wrong).

Another bonus feature — multiple filters can be given:

GET /resource?filter[]=foo&filter[]=bar

Third bonus feature is that we didn’t need to extend the language with a method that doesn’t do absolutely anything.

(Even tap does something and is incredibly useful.)

I'll admit mine was a bad example. However, the most common (only?)
use case I could see for it is in fact with #send.

Your idea for filtering is great, by the way.

I'll admit mine was a bad example. However, the most common (only?)
use case I could see for it is in fact with #send.

Your idea for filtering is great, by the way.

Instead of send you can use instance_eval. Where you'd do something like
obj.send(x? ? :self : :other) you can do obj.instance_eval { x? ? self : other }

Cheers
-foca

I won't comment on your use case, others have already done that.

However. I think naming the method self is potentially confusing,
since self is a keyword which means the receiver of the current
message.

class Foo

    def m(arg)
       arg.self
    end
end

I'd have to think about what self meant anytime I read that.

Smalltalk has a similar method, which is usually used to end a cascade
of message sends (something which ruby doesn't have).

   pt = Point.new x:1; y:2; yourself

A rather concocted example, but the point is that the yourself method
returns the receiver, and is IMHO a much better method name.

By way of explanation, a semicolon after a message invocation
indicates that the subsequent message is sent to the receiver of the
preceding message, instead of the result of that mesage. so the above
would be equivalent to:

tmp = Point.new
tmp x: 1
tmp y: 2
pt = tmp yourself

(somehow managed to forget to hit send on this one yesterday. sigh.)

Smalltalk has a method Object#yourself which does just that. The most common use case is with cascaded message sends, but Ruby doesn't have those. I've wanted #yourself in Ruby now and then too. Since Matz was very familiar with Smalltalk when he created Ruby I assume he didn't think it was necessary, but it's one of the questions I always want to ask him but forget to when I get the chance.

+1

Also had the need for something like this in the past.

Object#presence won't cut it when you're short-circuiting some part
of computation inside of the chained calls, for example this

@users.send(@admins_only ? :select : :presence, &:admin?).sort

will fail when @users array is blank.

Also isn't this function sort of OO version of
http://en.wikipedia.org/wiki/Identity_function ?

Btw. the use cases like this could be made even simpler by
adding/using Object#send_if

@users.send_if(@admins_only, :select, &:admin?).sort

And it looks like people have been doing this for quite a while
already :slight_smile:

http://www.google.com/codesearch?q=lang%3Aruby+def\ssend_if

Why not rewrite that as:

if @admins_only
  @users.select(&:admin?).sort
else
  @users.sort
end

or even

@users = @users.select(&:admin?) if @admins_only
@users.sort

I think that is infinitely easier to read and understand (it took me a
while to figure out what the hell your code was doing). Just because
you *can* make everything into a one-liner in Ruby, doesn't mean you
*should*.

Also from the naming of the variables it sounds like you are doing
this in the controller, that's the wrong place to do this sort of
thing, imho. Put it in the model instead!

@users.readable_by(@user)

This clearly communicates what you are trying to achieve.

/Jonas

I fully support Jonas.

Anyway, what’s the track record of getting a language extension into Active Support if it’s not being used inside the framework? I’d say very low.

Never mind that example, I made it up just to illustrate the issue
which
was that the short-circuiting had to happen in the middle of long
method
chain and I didn't want to introduce local variable just for this
optional
filtering step:

SomeModel.some_scope.all.
  group_by(&:user).<<optional filtering here >>.
  map { ... }.sort_by(&:second).reverse.map(&:first)

Though I must admit this is hardcore and happened to my only once or
twice :slight_smile:

As for "track record of getting a language extension into
ActiveSupport
if it's not being used inside the framework", here it is:

http://github.com/rails/rails/commit/51730792ca9

I wasn't talking about that specific code sample either. Long chaining
of methods is an anti-pattern and we shouldn't be encouraging it. Put
it in the model and decompose it.

/Jonas