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.
Related
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
Meta info: Found similar posts, zero were for Ruby.
Created title using multiple terms for search accessibility
Okay now onto the question...
I'm trying to generate new variables (moreso) automatically, I wanted to create a mechanism that did so.
class User
attr_accessor :name
def initialize(name)
#name = name
end
end
def CreateUser(name)
name = User.new(name)
end
CreateUser("Arnold")
p Arnold
I used a single argument in the name parameter of method "CreateUser" as both the name of the variable that I want to declare and the information sent into the code creating a member of my class.
I quickly remembered that in Ruby, variables inside of a method are local to only the method. If that weren't the case, and I could spit out the new variable from the method then I could generate my users easily - but this isn't the case. Is there a work around for this? Or is declaring a new variable manually just the name of the game?
Your question is a little confusing. At first glance, it seems like you're looking for some metaprogramming technique to dynamically create instance variables, such as:
class User
def new_variable name, value
instance_variable_set "##{name}", value
end
end
However, upon looking more closely at the problem you're actually trying to solve, it seems like the real issue is that you aren't making your methods part of your original object. This is probably what you really want:
class User
attr_accessor :name
def initialize name
#name = name
end
def create_user
# do whatever you need to do to "create" a user
pp name
end
end
u = User.new 'Arnold'
u.create_user
Enabling an object to call instance methods on data stored within the object is part of encapsulation. That is most likely what you're really after.
I can imagine when you want some attribute to be only readable, or be writable and readable, but not when you want an attribute to be writable and not readable.
In some cases, you might want to read some attributes but not be able to change them. For example, if you have an Animal class and want to initialize it with a name, age, and color, you could write
attr_accessor :age, :color
and then for name have
attr_reader :name
because a name doesn't change. A cat can get older or change color, but the name (for the sake of this question) once assigned should not be changed.
On the other hand, I cannot rationalize a scenario (aside form passwords) where you would need attr_writer (as opposed to attr_accessor or attr_reader). Under what circumstances would you want to write something, but not want to read it? What type of scenario or situation calls for attr_writer?
If you can think of a scenario in other programming languages, that is welcome but my immediate scope of Object Orientation knowledge is within Ruby.
What if you want a trivial writer and a custom reader? It's common to want to memoize a default value when calling a reader if the ivar isn't already set.
class Oof
attr_writer :rab
def rab
#rab ||= "rab, of course!"
end
end
oof = Oof.new
oof.rab # => "rab, of course!"
oof.rab = "zab"
oof.rab # => "zab"
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.
I'm using Sinatra (1.2) and RSpec (2.5) and would like to create a new object with an attribute TDD style. This is how the end result should look like:
class User
def initialize(name)
#name = name
end
end
I know I have to write the example before the implementation but I'm trying to explain my question here. :) Here is the not working spec I have so far:
describe User
it "creates a new user object" do
name = mock("A name")
user = mock(User) # shouldn't do this, see the reply's
user.should_receive(:name=).with(name)
User.new(name)
end
end
When I run RSpec I get the "expected: 1 time, received 0 times" error. Any idea how I can explain RSpec I would like to assign the name attribute?
Note: I'm not using Rails, not using ActiveRecord or anything, just Ruby.
First of all, let me explain why the spec you have written doesn't work:
You set an expectation that the mock object returned by mock(User) should receive name=. There are two problems with this. First of all the mock will receive nothing, because it is never called. mock(User) returns a mock object, and it cannot be used to set expectations for what the User class object will receive (to do that simply do User.should_receive(...)). Secondly, even if you had set the expectation on the User class object, that object will never receive name=. There are two reasons for this, too: firstly because name= (had it existed) would be an instance method, and as such not called on the class object, and secondly, you declare no name= instance method. What your code does is that it sets an instance variable.
Now, how should you write a test for this? You shouldn't. Tests are to define and assert the behaviour, not the implementation. Setting an instance variable is pure implementation. There is in your example code no way to get the value of the #name instance variable from outside of the class, therefore there is no reason to write a test for it.
Obviously your code is just an example, anything useful would do something with the #name variable, and that is what you should test. Start by writing a test for what a User object will be used for, then write all the implementation needed to fulfill that test (but no more). Write a test that reflects how the object will be used in actual production code.
I'd really recommend you don't approach this using mocks. It's not what they're for. In fact, specifying getters/setters like this is not really what TDD is for. The idea is to let a requirement drive the setters/getters into existence. For example, there might be a requirement that the user's name appear in a welcome message when he/she logs in. Then you might do something like this:
describe 'login process' do
it "displays user's name after successful login" do
user = User.new("Cimm", "cimm#somewhere.com", "secret")
post "/login", :email => "cimm#somewhere.com", :password => "secret"
last_response.body.should =~ /Welcome Cimm/m
end
end
This specifies behavior, and forces you to implement a means of setting the name attribute (via the constructor, in this case) and a means of accessing it. No need to specify the constructor directly.
Do you really want to mock the very object you are developing?
require 'rspec'
class User
attr_accessor :name
def initialize(name)
#name = name
end
end
describe User do
subject {User.new "other name"}
it "creates a new user object" do
subject.should respond_to :name=
end
end