Piece of Ruby code is confusing - super - ruby

I'm reading Zed Shaw's Ruby introduction book. I came across a piece of code I don't quite understand.
class Person
def initialize(name)
#name = name
#pet = nil
end
attr_accessor :pet
end
class Employee < Person
def initialize(name, salary)
super(name)
#salary = salary
end
attr_accessor :salary, :name
end
I understand the super(name) part very roughly. I believe that means it defers to the parent class? I'm not sure I understand why this would be done in the real world. Why would someone do super(name) rather than just write #name = name?
I'm still learning. Thanks in advance!

Why would someone do super(name) rather than just write #name = name?
Let's look from another angle. Why would someone duplicate [potentially complex] functionality, which already exists in parent class, rather than simply delegate the work to parent?
If you duplicate the functionality and then requirements change, you need to go change all of the copies. This is error-prone and needlessly expensive (in terms of time/efforts).

I see two different programming best-practices being used here.
First is the best-practice of never duplicating code. You'll often see code like this:
class MyClass
def blah
#blah
end
def do_something
puts blah
end
end
You may wonder "why does the programmer call the method blah() when they could just use the variable #blah instead? The answer is that #blah may be a simple variable right now, but what if you decide to save it in a hash later, accessible as myData[:blah]? It would be a pain to go through every line of code, searching for every #blah and changing it to myData[:blah]. Using the method blah() ensures that if you ever change the way #blah works, you only have to change it in one place: the method.
The same can be said for super(name) vs #name = name. Right now, the initialize() method for Person might be simple, but someday it might become really complicated. You wouldn't want to have to change the code of every class inheriting Person, so it's best to call super().
Second is the best-practice of encapsulation. Imagine for a second that your code looks like this instead:
require "person"
class Employee < Person
def initialize(name, salary)
super(name)
#salary = salary
end
attr_accessor :salary, :name
end
In object-oriented programming, it's common to use libraries made by other people, with classes and objects that you didn't write and don't have the ability to change.
Encapsulation is the idea that a class is a self-sufficient and independent machine. You pass it input data and receive output data, but you have no idea what's going on inside. So in this code, even though Employee inherits Person, Employee shouldn't make any assumptions about what the Person class is doing under the hood. Calling super(name) is trusting Person to set itself up, without worrying about the details of how a Person should be set up.
Even if the two classes are written in the same file and you can see the source code for both, it's important to follow the best practice of encapsulation to keep your code clean and robust for your future self and other programmers, because someday those two classes may be in different files or even different libraries.

Related

Listing parts of a class

I am trying to make a code with a cat, where you are able to buy certain toys and use them. However, whenever I try to list the toys in the "shop," it gives me an error every time. I'm not sure what I'm doing wrong, but it is definitely somewhere in how I'm executing the command.
class Toy
attr_reader :name
def initialize(name)
#name = name
end
def shop
puts "#{#Toy}"
end
end
toy.shop
I am not sure how to properly run this command, but I'm fairly certain the problem is in the "toy.shop" line.
A Working Rewrite and Simplification
There are a number of issues with your current code. Since there's always more than one way to do almost anything in Ruby, you can do this in other ways, too. However, here are some key issues:
You're not really leveraging #initialize.
You don't instantiate a new Toy object with Toy#new.
You don't assign Toy#new to a local toy variable.
Because toy is not defined, it doesn't have access to any of Toy's instance methods like #shop.
The non-existent toy variable is the receiver for the #shop method, so it will raise NameError if toy is undefined, or NoMethodError if toy is defined but not an instance of the Toy class or another class that can respond to the #shop method.
Based on your limited example, I'd rewrite and simplify the code as follows:
class Toy
attr_accessor :name
def shop
p "You bought a #{#name}."
end
end
toy = Toy.new
toy.name = "mouse"
toy.shop
#=> "You bought a mouse."
Why Kernel#p Instead of Kernel#puts
Note that Kernel#p will not only print your output to STDOUT, but will also return a value, which is extremely useful in a REPL or when debugging code. Kernel#puts, on the other hand, always returns nil so I generally prefer #p unless the output from #inspect is undesirable for some reason. Your mileage in that regard may vary.
See Also
The defined? keyword.
Variable assignment.
Ruby syntax.
Ruby exceptions.
First of all, this is a class, you should initialize and then call the method like this: Toy.new('some_toy').shop
And there is an error in your shop method, #Toy doesn't exists.
It should be:
def shop
puts "#{#name}"
end

When are 'attr_writer' used without 'attr_reader?

There is a question where #coreyward talks about using it alone, but doesn't give an example of when. Other sources on the web allude to some use cases, but I have yet to find any source that lists a real world example. When would you want to write data but not be able to read it?
It isn't always, necessarily, that you don't want to read it; sometimes the default writer is fine, but you want a custom reader. Take, for instance, this person class:
class Person
attr_writer :name
attr_accessor :title
def initialize(name, title)
#name = name
#title = title
end
def name
"#{#title} #{#name}"
end
end
Here, creating a reader through attr_reader or attr_accessor for name would be redundant, since I'm defining my own name method because there is a custom rule, that I should always display the name with a title.
As an aside, one could argue that my name method should be something more along the lines of name_with_title or what have you, but I still wouldn't actually want the default name method from a reader, because I still want all access to the name to go through this other method.

DelegateClass vs Class Inheritance in Ruby

Can someone please provide some insight as to when to use delegation via DelegateClass (e.g. Seller < DelegateClass(Person)) and when to use class inheritance (e.g. Seller < Person) in ruby?
class Seller < DelegateClass(Person)
def sales
...
end
end
class Seller < Person
def sales
...
end
end
When I was looking over the Ruby on Rails source on Github I found quite a few uses of DelegateClass.
There are a couple of differences that can help provide insight as to which approach to use.
1) You can safely delegate to primitives (e.g. String), but cannot always safely inherit from them
If you're building on top of Hash or String or Fixnum, you're safer using DelegateClass (or another delegator). For more on why, Steve Klabnik's cautioning is a good place to start).
2) DelegateClass makes it easy to “convert” a more general object into a more specific one
This makes it easier to accept an instance of a general object and make it behave in a way that's specific to your implementation:
class Message < DelegateClass(String)
def print
upcase
end
end
# […]
def log(message)
message = Message.new(message) unless message.is_a?(Message)
end
3) A gotcha: DelegateClass subclasses expect an instance of the delegated class as an argument to new
This can make it tricky to “subclass” classes that you're handing to library code. For example, this is a fairly common practice that won't work out of the box with DelegateClass:
class MyLogger < DelegateClass(ActiveSupport::Logger); end
Foo::ThirdParty::Library.configure do |c|
c.logger = MyLogger # no good
end
This doesn't work because our library expects to behave like most loggers and instantiate without arguments. This can be addressed by defining initialize and creating an instance of ActiveSupport::Logger, but probably not the right solution in this case.
delegates model different behaviors of Person based on the context. e.g. the same person could be a seller in one context or a buyer in a different context. Inheritance is more rigid: a Bear and Tiger inherit from Animal, but an instance of Animal would never need to sometimes behave like a Bear and sometimes behave like a Tiger. An instance of a descendent of Animal is either one or the other.

instance_variable_set in constructor

I've made a constructor like this:
class Foo
def initialize(p1, p2, opts={})
#...Initialize p1 and p2
opts.each do |k, v|
instance_variable_set("##{k}", v)
end
end
end
I'm wondering if it's a good practice to dynamically set instance variables like this or if I should better set them manually one by one as in most of the libs, and why.
Diagnosing the problem
What you're doing here is a fairly simple example of metaprogramming, i.e. dynamically generating code based on some input. Metaprogramming often reduces the amount of code you need to write, but makes the code harder to understand.
In this particular case, it also introduces some coupling concerns: the public interface of the class is directly related to the internal state in a way that makes it hard to change one without changing the other.
Refactoring the example
Consider a slightly longer example, where we make use of one of the instance variables:
class Foo
def initialize(opts={})
opts.each do |k, v|
instance_variable_set("##{k}", v)
end
end
def greet(name)
greeting = #greeting || "Hello"
puts "#{greeting}, name"
end
end
Foo.new(greeting: "Hi").greet
In this case, if someone wanted to rename the #greeting instance variable to something else, they'd possibly have a hard time understanding how to do that. It's clear that #greeting is used by the greet method, but searching the code for #greeting wouldn't help them find where it was first set. Even worse, to change this bit of internal state they'd also have to change any calls to Foo.new, because the approach we've taken ties the internal state to the public interface.
Remove the metaprogramming
Let's look at an alternative, where we just store all of the opts and treat them as state:
class Foo
def initialize(opts={})
#opts = opts
end
def greet(name)
greeting = #opts.fetch(:greeting, "Hello")
puts "#{greeting}, name"
end
end
Foo.new(greeting: "Hi").greet
By removing the metaprogramming, this clarifies the situation slightly. A new team member who's looking to change this code for the first time is going to have a slightly easier time of things, because they can use editor features (like find-and-replace) to rename the internal ivars, and the relationship between the arguments passed to the initialiser and the internal state is a bit more explicit.
Reduce the coupling
We can go even further, and decouple the internals from the interface:
class Foo
def initialize(opts={})
#greeting = opts.fetch(:greeting, "Hello")
end
def greet(name)
puts "#{#greeting}, name"
end
end
Foo.new(greeting: "Hi").greet
In my opinion, this is the best implementation we've looked at:
There's no metaprogramming, which means we can find explicit references to variables being set and used, e.g. with an editor's search features, grep, git log -S, etc.
We can change the internals of the class without changing the interface, and vice-versa.
By calling opts.fetch in the initialiser, we're making it clear to future readers of our class what the opts argument should look like, without making them read the whole class.
When to use metaprogramming
Metaprogramming can sometimes be useful, but those situations are rare. As a rough guide, I'd be more likely to use metaprogramming in framework or library code which typically needs to be more generic (e.g. the ActiveModel::AttributeAssignment module in Rails), and to avoid it in application code, which is typically more specific to a particular problem or domain.
Even in library code, I'd prefer the clarity of a few lines of repetition.
Answers to this question are always going to be based on someone's personal opinion so here's mine.
Clarity v Brevity
If you cannot know the set of options ahead of time then you have no real choice but to do as you have. However if the options are drawn from a known set then I would favour clarity over brevity and have explicit methods to set the options. These would also be a good place to add any rdoc etc.
Safety
From a safety perspective, having methods to handle the setting of an option would allow you to perform validation as required.
When you need to do such thing, the inventory of the parameters varies. In such case, there is already a handy structure within Ruby (as well as most modern languages): array and hash. In this case, you should just save the entire option as a single hash. That would make things simpler.
Instead of creating instance variables dynamically, you could use attr_accessor to declare the available instance variables and just call the setters dynamically:
class Foo
attr_accessor :bar, :baz, :qux
def initialize(opts = {})
opts.each do |k, v|
public_send("#{k}=", v)
end
end
end
Foo.new(bar: 1, baz: 2) #=> #<Foo:0x007fa8250a31e0 #bar=1, #baz=2>
Foo.new(qux: 3) #=> #<Foo:0x007facbc06ed50 #qux=3>
This approach also shows an error if an unknown option is passed:
Foo.new(quux: 4) #=> undefined method `quux=' for #<Foo:0x007fd71483aa20> (NoMethodError)

Whats a good ruby idiom for breaking up a large class into modules?

I have a large class with lots of methods and it's starting to get a bit unorganized and hard to navigate. I'd like to break it up into modules, where each module is a collection of class and instance methods. Perhaps something like this:
UPDATE: I've now realized that this is a pretty poor example. You probably wouldn't want to move validations or attributes out of the core class.
class Large
include Validations
include Attributes
include BusinessLogic
include Callbacks
end
After reading Yehuda's post about Better Ruby Idioms, I'm curious how others are tackling this problem. Here's the two methods I can think of.
First Method
module Foo
module Validations
module ClassMethods
def bar
"bar"
end
end
module InstanceMethods
def baz
"baz"
end
end
end
class Large
extend Validations::ClassMethods
include Validations::InstanceMethods
end
end
Second Method
module Foo
module Validations
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def bar
"bar"
end
end
def baz
"baz"
end
end
class Base
include Validations
end
end
My questions are:
Is there a better way to do this?
How do you get a one-liner module mixin for a set of class/instance methods with the least amount of magic?
How do you namespace these modules to the base class without namespacing the class itself?
How do you organize these files?
Breaking a class into modules, while tempting (because it's so easy in Ruby), is rarely the right answer. I usually regard the temptation to break out modules as the code's way of telling me it wants to be split into more tightly-focussed classes. A class that's so big you want to break it into multiple files is pretty much guaranteed to be violating the Single Responsibility Principle.
EDIT: To elaborate a bit on why breaking code into modules is a bad idea: it's confusing to the reader/maintainer. A class should represent a single tightly-focussed concept. It's bad enough when you have to scroll hundreds of lines to find the definition of an instance method used at the other end of a long class file. It's even worse when you come across an instance method call and have to go looking in another file for it.
After doing what Avdi said, these are the things I would do before putting anything into a module:
Whether this module can or will be used in any other class?
Would it make sense to extract the functionality of these modules into a different or base class?
If the answer for 1 is no and 2 is yes then IMHO that indicates to better have a class rather a module.
Also, I think putting attributes in a module is conceptually wrong because classes never share their attributes or instance variables or in other words their internal state with any other class. The attributes of a class belongs to that class only.
Business logics do definitely belong to the class itself and if the business logic of class A has some common responsibilities with class C then that needs to be extracted into a base class to make it clear instead of just putting it into a module.
The standard idiom seems to be
foo.rb
foo/base.rb
foo/validations.rb
foo/network.rb
foo/bar.rb
and foo.rb would be something like
class Foo
include Foo::Base
include Foo::Validations
include Foo::Network
include Foo::Bar
end
This is the standard idiom, and it works fairly well for letting you break things up. Don't do class methods vs instance methods. Those are generally pretty arbitrary distinctions, and you're better off putting code that deals with similar subjects together. That will minimize how many files you have to touch for any given change.
BEWARE: Rails can get confused by nesting models like this, at least if everything were classes. I think it'll do better with all the nested files just being modules, but you'll have to see. I'm still suggesting this because it's the normal idiom used by the Ruby community, but you may have to avoid having both a foo.rb and a foo/ directory amongst your Rails models (if that's the kind of class you're talking about).
Although including different modules will work, it is generally more troublesome than simply reopening the class in multiple places.
There is a (very simple) gem that you can use to makes this as pretty as can be: concerned_with
Example (from the readme)
# app/models/user.rb
class User < ActiveRecord::Base
concerned_with :validations,
:authentication
end
# app/models/user/validations.rb
class User < ActiveRecord::Base
validates_presence_of :name
end
#app/models/user/authentication.rb
class User < ActiveRecord::Base
def self.authenticate(name, password)
find_by_name_and_password(name, password)
end
end
I tend to use Ruby's duck typing approach to interfaces, which basically allows you to send any message to any object, which then evaluates what to do with it.
This approach allows me to stick to the same pattern Avdi mentions, keeping classes small and concise- only ever being responsible for one thing.
The great thing about Ruby is that you can delegate responsibilities to other concise classes, without muddling any of the logic together. For example:
class Dog
def initialize(name)
#name = name
end
def bark
"woof"
end
def fetch(object)
"here's that #{object}"
end
def sit
"sitting down"
end
private
attr_accessor :name
end
Here we have my dog class that has loads of dog related methods. They're all specific to dog, so could happily reside here. However, there would be a problem if these methods got a bit complex, calling other methods or perhaps this dog learns a bunch of new tricks!? So I could separate these out into their own classes and then delegate responsibility to those, like so:
class Tricks
def initialize(name)
#name = name
end
def fetch(object)
"here's that #{object}"
end
def sit
"sitting down"
end
def come_when_called(my_name)
"I'm coming" if my_name == name
end
def put_toy_away(object)
"#{fetch(object)}, I'll put it away"
end
private
attr_reader :name
end
class Dog
def initialize(name)
#name = name
end
delegate :sit, :fetch, :come_when_called, :put_away_toy, to: :tricks_klass
def bark
"woof"
end
private
attr_accessor :name
def tricks_klass
#tricks_klass ||= Tricks.new(name)
end
end
So now, that Dog class really starts to behave like an interface to dog-related behaviors, whilst these tricks are no longer coupled to it. This'll make testing easier by being able to instantiate a Tricks object and test it more generically, without the need for a Dog (because they don't always listen).
Now, we could have a Cat class that delegates responsibility to this Tricks class as well- although, that'd be one smart Cat!
You could also now use the Tricks class on its own- that's the power encapsulating single behavior its own class. You could even separate these behaviors even further- but only you as the developer know if that's worth while!

Resources