Let's say we have class
class Evaluator
attr_reader :response
def initialize(response)
#response = response
end
def order
#_order ||= Order.find(response.order_id)
end
end
I'd like to ask about #_order which exists only for caching purposes. This convention is to prevent accidental use of an instance variable (when it is not initialized yet, for example). I'd like to force using order instead of #order. What do you think about it? Do you know any better way of achieving this goal?
You cannot make instance variables inaccessible from subclasses, in Ruby. Instance variables are protected (in common programming terms) by default, which means that they are not accessible from the outside of the class, but are accessible from within the class hierarchy.
You can only control the accessibility of methods with the use of private, protected and public.
You can make it painfully obvious that one should not use the member, like calling it #lazy_loaded_order, or even generalize it like this:
def order
#lazy_members[:order] ||= Order.find(response.order_id)
end
This way, it is obvious that anyone trying to use #lazy_members will do it on his own risk...
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.
I have a Project model and a Developer model. I have the concept of calculating the "interestingness" for a project for a particular developer:
class Project < ActiveRecord::Base
def interestingness_for(developer)
some_integer_based_on_some_calculations
end
end
I think it would be neat, instead of having something like Project.order_by_interestingness_for(bill), to be able to say
Project.order(:interestingness, :developer => bill)
and have it be a scope as opposed to just a function, so I can do stuff like
Project.order(:interestingness, :developer => bill).limit(10)
I don't know how to do this, though, because it's not obvious to me how to override a scope. Any advice?
Assuming you will not need to use the standard ActiveRecord order query method for the Project class, you can override it like any other class method:
def self.order(type, options)
self.send(:"special_#{type}_calculation_via_scopes", options)
end
Then the trick is to ensure you create the needed calculation methods (which will vary according to your interestingness and other algorithms). And that the calculation methods only use scopes or other AR query interface methods. If you aren't comfortable converting the method logic to a SQL equivalent using the query interface, you can try using the Squeel DSL gem which can potentially work with the method directly depending on your specific calculation.
If you may be needing the classic order method (and this is usually a safe assumption), then don't override it. Either create a proxy non-ActiveRecord object for this purpose, or use a different naming convention.
If you really want to, you can use aliasing to achieve a similar effect, but it may have unintended consequences for the long term if the second argument ('options' in this case) suddenly takes on another meaning as Rails progresses. Here is an example of what you can use:
def self.order_with_options(type, options = nil)
if options.nil?
order_without_options(type)
else
self.send(:"special_#{type}_calculation_via_scopes", options)
end
end
class << self
alias_method_chain :order, :options
end
This is not specific to Ruby, but I happen to be using Ruby at the moment.
Here is a class that provides text printing functionality.
class Printer
# some static methods
def self.fancy_print(text)
# do fancy formatting
end
def self.print(text)
fancy_print(text)
end
# some instance methods
def initialize(text)
#text = text
end
def print
#somehow call fancy_print(#text)
end
end
Is it bad design to provide both instance and static methods in a class?
Sometimes I would like to keep several instances of Printer lying around. Other times, I just need to say Printer.print(text) and just grab that text without bothering to store it for later, hence resulting in a static print method and an instance print method.
No, it's not bad design.
It's completely normal–some methods are class methods, some are instance methods. I'm not sure what the perceived issue might even be. It's somewhat difficult to even find classes with only one or the other, although in Java you frequently see static utility classes.
Edit IMO the question is misleading; you're actually asking if it's bad design to have static and instance methods of the same name.
It's tricky if both are public because it isn't necessarily obvious how to use the class. In other words, is it designed to be used via the static, or instance method? Both? What's different about them? Should that difference be... differentiated by a name?
Ultimately it depends on context and usage–there's no single answer.
That's okay, I think. Here's how you would code the Printer#print method
class Printer
def print
self.class.fancy_print(#text)
end
end
There is nothing wrong with having class methods and instance methods for a specific class, as long as they make sense belonging to the class.
I.e. what does fancy_print do? Is it something specific to the Printer class. If it is, then you shouldn't worry.
On the other hand, if it is to be used for any string value, you can do something cheekier, i.e. adding this method to the string class
String.class_eval do
def fancy_print
## implementation
end
end
Now you can do "Hello".fancy_print
Hope this answer was of any use. Also write test cases!
Is there any way to make instance variables "private"(C++ or Java definition) in ruby? In other words I want following code to result in an error.
class Base
def initialize()
#x = 10
end
end
class Derived < Base
def x
#x = 20
end
end
d = Derived.new
Like most things in Ruby, instance variables aren't truly "private" and can be accessed by anyone with d.instance_variable_get :#x.
Unlike in Java/C++, though, instance variables in Ruby are always private. They are never part of the public API like methods are, since they can only be accessed with that verbose getter. So if there's any sanity in your API, you don't have to worry about someone abusing your instance variables, since they'll be using the methods instead. (Of course, if someone wants to go wild and access private methods or instance variables, there isn’t a way to stop them.)
The only concern is if someone accidentally overwrites an instance variable when they extend your class. That can be avoided by using unlikely names, perhaps calling it #base_x in your example.
Never use instance variables directly. Only ever use accessors. You can define the reader as public and the writer private by:
class Foo
attr_reader :bar
private
attr_writer :bar
end
However, keep in mind that private and protected do not mean what you think they mean. Public methods can be called against any receiver: named, self, or implicit (x.baz, self.baz, or baz). Protected methods may only be called with a receiver of self or implicitly (self.baz, baz). Private methods may only be called with an implicit receiver (baz).
Long story short, you're approaching the problem from a non-Ruby point of view. Always use accessors instead of instance variables. Use public/protected/private to document your intent, and assume consumers of your API are responsible adults.
It is possible (but inadvisable) to do exactly what you are asking.
There are two different elements of the desired behavior. The first is storing x in a read-only value, and the second is protecting the getter from being altered in subclasses.
Read-only value
It is possible in Ruby to store read-only values at initialization time. To do this, we use the closure behavior of Ruby blocks.
class Foo
def initialize (x)
define_singleton_method(:x) { x }
end
end
The initial value of x is now locked up inside the block we used to define the getter #x and can never be accessed except by calling foo.x, and it can never be altered.
foo = Foo.new(2)
foo.x # => 2
foo.instance_variable_get(:#x) # => nil
Note that it is not stored as the instance variable #x, yet it is still available via the getter we created using define_singleton_method.
Protecting the getter
In Ruby, almost any method of any class can be overwritten at runtime. There is a way to prevent this using the method_added hook.
class Foo
def self.method_added (name)
raise(NameError, "cannot change x getter") if name == :x
end
end
class Bar < Foo
def x
20
end
end
# => NameError: cannot change x getter
This is a very heavy-handed method of protecting the getter.
It requires that we add each protected getter to the method_added hook individually, and even then, you will need to add another level of method_added protection to Foo and its subclasses to prevent a coder from overwriting the method_added method itself.
Better to come to terms with the fact that code replacement at runtime is a fact of life when using Ruby.
Unlike methods having different levels of visibility, Ruby instance variables are always private (from outside of objects). However, inside objects instance variables are always accessible, either from parent, child class, or included modules.
Since there probably is no way to alter how Ruby access #x, I don't think you could have any control over it. Writing #x would just directly pick that instance variable, and since Ruby doesn't provide visibility control over variables, live with it I guess.
As #marcgg says, if you don't want derived classes to touch your instance variables, don't use it at all or find a clever way to hide it from seeing by derived classes.
It isn't possible to do what you want, because instance variables aren't defined by the class, but by the object.
If you use composition rather than inheritance, then you won't have to worry about overwriting instance variables.
If you want protection against accidental modification. I think attr_accessor can be a good fit.
class Data
attr_accessor :id
private :id
end
That will disable writing of id but would be readable. You can however use public attr_reader and private attr_writer syntax as well. Like so:
class Data
attr_reader :id
private
attr_writer :id
end
I know this is old, but I ran into a case where I didn't as much want to prevent access to #x, I did want to exclude it from any methods that use reflection for serialization. Specifically I use YAML::dump often for debug purposes, and in my case #x was of class Class, which YAML::dump refuses to dump.
In this case I had considered several options
Addressing this just for yaml by redefining "to_yaml_properties"
def to_yaml_properties
super-["#x"]
end
but this would have worked just for yaml and if other dumpers (to_xml ?) would not be happy
Addressing for all reflection users by redefining "instance_variables"
def instance_variables
super-["#x"]
end
Also, I found this in one of my searches, but have not tested it as the above seem simpler for my needs
So while these may not be exactly what the OP said he needed, if others find this posting while looking for the variable to be excluded from listing, rather than access - then these options may be of value.