This question is regarding programming practices in Ruby.
Is it preferable to create instance variables in ruby inside a static method? Or should they be created in an initialize method?
Well initialize is made for that, why would you do something different.
class SomeClass
def initialize(first, second)
#first = first
#second = second
end
end
Related
I'm building a system that uses a lot of small classes to do data processing (each class does a different processing step, nice & SRP like). I initialize each class with some parameters then call an execute() method on the class, which then uses other private methods to do some of the data calculations.
My question is: if I'm not accessing any of the parameters outside of the class, should I just use the instance variables in the private methods? Or define an attr_reader?
For example, here's a bit of contrived code that I whipped up:
class Car
attr_reader :make
def initialize(make, model)
#make = make
#model = model
end
def execute
apply_paint
rotate_tires
end
private
def apply_paint
if make == "Toyota"
Painter.paint(self, "red")
else
Painter.paint(self, "green")
end
end
def rotate_tires
if #model == "Sequoia"
set_tire_size(:large)
else
set_tire_size(:normal)
end
end
end
So which is better? How I handled "make" or how I handled "model"?
If I know that I'm not using either of those variables outside of this class at all, should I just stick with instance variables? Or is it nicer to use the attr_reader because then I could convert them into actual methods without changing the rest of the code... i.e. since apply_paint uses make instead of #make, I could change make to be a local method if need be and not change the apply_paint method.
I keep waffling back-n-forth and can't figure out which is better. Maybe this is just a preference thing, but I wanted to see what thoughts others had on this issue.
Use attr_ methods. You can (and probably should) make them private if you're not accessing them outside of the class. Why use them? You improve readability by stating your intentions towards how it should be used. An attr_reader should be interpreted as "this variable should only be read".
I am learning Ruby OOP and have been faced with the following question.
What could we add to the class below to access the instance variable
#volume?
class Cube
def initialize(volume)
#volume = volume
end
end
My initial thought was to add attr_reader :volume to access the instance variable.
Instead the model answer suggests adding a new method as below.
def get_volume
#volume
end
Why is this the preferred method?
Both methods would output 100 if cube.volume or cube.get_volume were called.
attr_reader. In general methods with get_ prefix are rather avoided in Ruby community (in opposite to commonly seen in Java/C# code)
If it is a dynamically created variable, then you can use instance_vairable_get, like below -
instance_variable_get("#volume")
Since it is desired to get the variable volume ,I will suggest the use of attr_reader :volume. This creates a proxy method volume so no need to add( and later have to maintain) an extra method get_volume for this sole purpose.
https://ruby-doc.org/core-2.1.1/Module.html#method-i-attr_reader
You should use attr_reader :volume instead of using get_volume method.
Stil attr_reader and your method get_volume both are functioning same.
Generally get_ methods should be avoided in ruby.
Why can the following part # def games # #games = games # end come at the very end (bottom) of the code and still work? I thought Ruby reads the code from top to bottom. If I do not define games at the top, shouldn't it give an error?
class Library
# def games
# #games
# end
def initialize(games)
#games = games
end
def add_game(game)
games << game
end
# The following lines should come at the top of this code.
def games
#games
end
end
games = ['WoW','SC2','D3']
lib = Library.new(games)
lib.games #=> WoW,SC2,D3
lib.add_game('Titan')
lib.games #=> WoW,SC2,D3,Titan
When the method is defined, ruby is not running it. It's just available for the instance to use after you've invoked the class.
I generally put my methods in alphabetical order to make it easier to navigate my code as it grows. This is a personal preference.
Ruby allows you to structure and organize your classes/modules however is logical/beneficial to you.
To clarify, Ruby classes are executed when they're defined, but methods are not.
example.rb
class Example
puts "hello"
def my_method
puts "world"
end
end
Run it
$ ruby example.rb
hello
Because Ruby executes classes, that's how things like macros work in Ruby classes.
class Example2
attr_accessor :foo
end
attr_accessor is a method that gets called when the class is executed. In this case attr_acessor will setup get and set functions for the #foo instance variable.
If Ruby didn't execute your classes, this code would have to be called manually in some sort of initializer.
All you need to do is learn to differentiate between calling a method and defining a method. Defined methods will not be automatically executed.
The reason it is so is because of the way a class is built by Ruby: Every instance method definition inside a Ruby class gets defined first, during the top-down parsing. Then when you invoke each method it just matters whether its defined or not and not how its ordered.
Having said that order is important if you are redefining a method below. Then precedence will be given to lower definition.
This question already has answers here:
What does a Java static method look like in Ruby?
(2 answers)
Closed 8 years ago.
I'm trying to define a method called function. It works when I do this:
a = A.new
def a.function
puts 100
end
But I want the method to work for any instance variable, not just a. For example, when I call function on an instance of A other than a, nothing happens. So I used A::f to define the function rather than a.function.
class A
attr_accessor :function
attr_accessor :var
end
def A::function
self.var = 0x123
end
a = A.new
a.function
puts a.var
This compiles fine but when I try to call the function I get an error. Why is it not working and how can I do what I'm attempting?
You're really tangled up here. I suggest you check out _why's poignant guide to ruby and get a handle on what is going on here.
As an attempt to steer you right, though…
a isn't an instance variable. It's a local variable that you're using to reference an instance of A.
def a.foo defines a method on the eigenclass of a.
attr_accessor :function already defined A#function (that is, an instance method on A called function) that essentially looks like this: def function; #function; end
def A::function defines a class method on A that you could access via A.function (not an instance of A as in a.function.
MRI doesn't really compile ruby like you might anticipate. It runs it, dynamically interpreting statements in realtime.
You probably want to stick with defining standard instance methods in the traditional manner, and avoid using “function” as a placeholder name since it is a reserved word and has special meaning in other languages. I'll use “foo” here:
class A
def foo
'Awww foo.'
end
end
That's it, you can now create an instance of A (a = A.new) and call foo on it (a.foo) and you'll get back 'Aww foo.'.
class A
def function
puts 100
end
end
a = A.new
a.function #=> "100"
That's a classic instance method. Is that what you're looking for or am I missing something?
If you're trying to define methods dynamically, you could use Class#define_method. Otherwise, if you are just wanting to define the method for the class, defining it in the scope of class A will suffice.
Anyway, could you be more specific on what are you trying to accomplish and what kind of error you're having, please?
I am using RSpec and want to test the constructor of a Singleton class more than one time.
How can I do this?
Best regards
Singleton classes essentially do this
def self.instance
#instance ||= new
end
private_class_method :new
So you can bypass the memoization altogether by calling the private method new using send
let(:instance) { GlobalClass.send(:new) }
A nice benefit of this way is that no global state is modified as a result of your tests running.
Probably a better way, from this answer:
let(:instance) { Class.new(GlobalClass).instance }
This creates an anonymous class which inherits from GlobalClass, which all class-level instance variables are stored in. This is then thrown away after each test, leaving GlobalClass untouched.
# singleton_spec.rb
require "singleton"
class Global
include Singleton
def initialize
puts "Initializing"
end
end
describe Global do
before do
Singleton.__init__(Global)
end
it "test1" do
Global.instance
end
it "test2" do
Global.instance
end
end
% rspec singleton_spec.rb -fd
Global
Initializing
test1
Initializing
test2
have a look at http://blog.ardes.com/2006/12/11/testing-singletons-with-ruby:
require 'singleton'
class <<Singleton
def included_with_reset(klass)
included_without_reset(klass)
class <<klass
def reset_instance
Singleton.send :__init__, self
self
end
end
end
alias_method :included_without_reset, :included
alias_method :included, :included_with_reset
end
One pattern I've seen is having the singleton be a sub-class of the real class. You use the Singleton version in production code, but the base (non-singleton) class for testing.
Example:
class MyClass
attr_accessor :some_state
def initialize
#some_state = {}
end
end
class MySingletonClass < MyClass
include Singleton
end
...but I'm looking for a better way myself.
Part of my problem is that I'm using JRuby and hooking into the Java System Preferences, which are global. The rest I think I can refactor out.
One reason people use singletons because "global variables are bad, m'kay?" A singleton is a global variable, sequestered in a name space, and with lazy instantiation. Consider whether a true global variable might simplify things, especially if you don't need lazy instantiation.
Refactor it into a class that can be constructed multiple times. This has the side-effect (some would say benefit) of removing the Singleton nature from the class.
Look at it another way: you've found a need to call the constructor more than once. Why should the class only construct one instance? What benefit is Singleton providing?
Does RSpec allow you do perform pre-test actions? Is so, you could add another method to you static class that cleared anything done during the constructor. Then just call that prior to each and every test.
You can simply make a new it and block for each spec. Break down your spec to a testable unit. Use "before" and "after" to set up and clears up anything you did.
before(:each) and after(:each) are executed for every it block.