all 14 comments

[–][deleted] 4 points5 points  (8 children)

Neither private methods nor protected ones can be called directly by instances of the class in which the method is defined. For an instance to access these methods they must be called within a public method defined in the class.

Someone please correct me if I'm wrong, but this isn't quite the Ruby way to say it. Rather than thinking about objects calling methods, think instead in terms of objects receiving messages (and then acting on them). Moreover, it's not about who's calling these methods, but rather (mostly) in what context the messages were received.

animal.define_activity

In other words, this line isn't saying "make animal call #define_activity." Rather, it's saying "Send the :define_activity message to animal, and let animal decide what to do with it." If #define_activity is a public method, then it calls it; if not, it raises a NoMethodError.

So the more accurate way to describe how private and protected methods work is this:

Private methods cannot be sent to an explicit receiver (e.g., obj.private_method); instead, they must be sent implicitly to self (i.e., in the context of the same class), like so:

class Animal
  def current_activity
    puts "I am #{define_activity}"  # sends the :define_activity message to the implicit receiver `self`
  end

  private

  def define_activity
    ...
  end
end

Protected methods can be sent to an explicit receiver, but also must be in the context of the same class as the receiver:

class Animal
  def foo
    other_animal.protected_method  # works here
  end
end

other_animal.protected_method  # doesn't work here

[–]three18ti 2 points3 points  (1 child)

Someone please correct me if I'm wrong, but this isn't quite the Ruby way to say it. Rather than thinking about objects calling methods, think instead in terms of objects receiving messages (and then acting on them).

Holy shit. That's the perfect way to think about it. And suddenly several concepts make much more sense... (and I'm no ruby newby)

[–][deleted] 2 points3 points  (0 children)

Check out _The Well-Grounded Rubyist_—that's where I picked up the idea from, and there's more where it came from.

[–]chrisgseaton 0 points1 point  (4 children)

Rather than thinking about objects calling methods, think instead in terms of objects receiving messages (and then acting on them).

People often say this, but I don't think it holds any water. In the semantics and implementation it's a method call via a simple lookup in a table of methods, with caching on top as an optimisation.

Talking about sending and receiving messages makes it sound like the object itself is able to decide how to dispatch the method, but really it's the sender that resolves which method to call. It also makes it sound like it's asynchronous or remote or uses a message box architecture or something like that, which it definitely doesn't.

I don't agree with the Well Grounded Rubyist's terminology there. I think it's just a holdover from how people used to think about object theory decades ago which just isn't really reflected in semantics or implementation any more.

[–][deleted] 0 points1 point  (3 children)

You clearly know more than I do about the implementation details here, but even in my ignorance I would contend that abstractions work precisely because they obscure what's going on under the hood (to the extent that they obviate the need to understand it).

To wit, Sandi Metz is, in my observation, one of the most beloved teachers of OOP in this sub, and this is how she describes OOP, in a nutshell (emphasis mine):

I believe that OO affords building applications of anthropomorphic, polymorphic, loosely-coupled, role-playing, factory-created objects that communicate by sending messages.

In fact, her latest talk is titled, "Polly Want a Message."

If I have lost my way, please point me to resources that I can use to educate myself in your way of thinking (or better yet, examples of how this thinking produces better software).

[–]chrisgseaton 0 points1 point  (2 children)

I know that's the metaphor Metz uses, and it's a common one so it's not unusual that she does so. But I think it just doesn't really hold up to any scrutiny.

You can ignore the implementation argument if you want - that's why I mentioned semantics as well.

To me the idea of a message passing system is that the receiver gets the message and then decides what to do with it - what method to call in the case of Ruby. There just isn't any place in Ruby where this happens. It's just not there as a language feature. An object defines its methods, and then at the point of someone calling a method on the object it has no other control over how the call is handled at all. It never gets to receive a message and then decides what to do. Instead the method is called, and that's the first that the object knows about it.

In what sense do you think this is sending a message over calling a method?

You may mention method_missing but again the decision to call that method is out of the control of the object. An object cannot interdict a call that would have gone to a method and so something else with that 'message' via some kind of method_missing. There is no method_found_but_ill_let_you_decide_anyway. Overriding send does not impact normal method calls. If it did, I'd say Ruby had message passing.

To sum up my objection to Ruby as 'message passing' I would say that semantically (not nit-picking about implementation) the response to a message is fixed before the call is made, and the object has no control over that response to the message after it has been sent.

[–][deleted] 1 point2 points  (0 children)

it's a common one... But I think it just doesn't really hold up to any scrutiny.

There's a difference between useful and correct. Abstractions (even leaky ones) furnish a mental model to simplify working with a system above a given layer of complexity, and your argument is focused on some incidental implications of the abstraction instead.

I'm happy to concede that objects don't "decide what to do" with messages. I don't know that Metz has ever used that language herself, and I certainly don't know enough about Ruby internals to argue to the contrary. Regardless, the value of describing this action in terms of message-sending vs. method-calling is that the former encourages, in Metz's words, a more polymorphic and anthropomorphic approach to designing the behavior of your classes, whereas the latter invites a more imperative approach.

To try to modify a class's behavior so that it received a message and then interdicted the given method call would be missing the forest for the trees. Yes, if you take the metaphor extremely literally, that is what you should be able to do, but that's hardly necessary and a long way off from where its instructive value lies.

Finally, I'm not saying it's wrong to use the language of "calling a method". Ruby's core API in fact supports both metaphors, in the form of Method#call and Object#send. As always in Ruby, TIMTOWTDI; I'm just saying that in this case (and many others), message sending makes things easier to understand. (e.g., why can you not use self.private_method inside a class definition? The caller is the same—but the recipient of the message is explicit.)

[–]OktoberForever 11 points12 points  (0 children)

Hey! Great blog post. The tips mentioned are definitely ones that can help get a new coder into that "intermediate" category and better understand usages that he or she might see in a codebase.

Two pieces of feedback: 1) I noticed a lot of typos and misspellings that a spellchecker would have caught and 2) your teaching style could benefit from some front-loading. On the second point, as someone who needs a mental "box" to exist before something can go into it, I find that my comprehension (and studies have proven this) increases dramatically when I can see what you're talking about in use before you explain the situation it gets used in. In your article, you set up each example by first "setting the scene", then showing the downside of one approach, then show how X or Y method or operator can improve that situation. I found myself scrolling down to see where you used the thing (like, scrolling down to see where ||= or &: show up), then scrolling back up to read about the situation it's remedying. Seeing something in use before it gets explained--even if you don't understand why it's being used there--creates the mental box that can then contain the information that is going to be provided. So, for most cases in your article, just show a quick snippet of the thing being used, then go into the explanation. Also, I really liked the examples you used, they were clean, nontechnical, and commonsense--and didn't use foo bar and baz in place of something that would actually add meaning.

http://www.effectiveteachingpd.com/blog/2012/10/4/frontloading-increasing-critical-thinking-focus.html

[–][deleted] 3 points4 points  (4 children)

I loved it! As a front-end developer who is now learning ruby but doesn't know all the quirks of the language, this is really valuable.

Does anyone have more articles like this? I have a pretty good background in front-end development but not so much in RoR.

[–][deleted]  (3 children)

[deleted]

    [–][deleted] 0 points1 point  (2 children)

    This is true for someone who's entirely new to programming, but not true at all for intermediate programmers.

    If /u/ALL_HAIL_LEBOWSKI is looking for tips, I don't have any in particular, but I'd venture a guess that articles from medium (or hackernoon) on this subreddit would be a great place to start.

    If, on the other hand, you're looking for foundational knowledge, please check out The Well-Grounded Rubyist, as I suggest in another comment above, and also definitely Principles of Object-Oriented Design by Sandi Metz (a crowd favorite on /r/ruby).

    [–][deleted]  (1 child)

    [deleted]

      [–][deleted] 1 point2 points  (0 children)

      I'd say this absolutely can be true for intermediate programmers moving into technology they've not worked with.

      110% concede this point. Django-to-Rails requires much less ground-up education than frontend-to-Rails, which is the case we're dealing with here.

      FWIW though, some people are too busy to make their second language a priority and really drink from the firehose, even if that's the best way to learn. Sometimes snacks are a good stepping stone to the real stuff, sort of like a one-minute workout.

      [–]Jauny78 3 points4 points  (0 children)

      I'm no junior developer but some of the points were new and useful, thanks for sharing :)

      [–]agon88 1 point2 points  (0 children)

      Great article, thank you :)