I understand that instance variables are mean to be states, and constants are meant to be constant. Is there any reason (besides convention) to use a constant instead of an instance variable? Is there a memory/speed advantage to using constants?
There's a few things to consider here:
Will the value change within the life-cycle of an object?
Will you need to override the value in sub-classes?
Do you need to configure the value at run-time?
The best kind of constants are those that don't really change short of updating the software:
class ExampleClass
STATES = %i[
off
on
broken
].freeze
end
Generally you use these constants internally in the class and avoid sharing them. When you share them you're limited in how they're used. For example, if another class referenced ExampleClass::STATES then you can't change that structure without changing other code.
You can make this more abstract by providing an interface:
class ExampleClass
def self.states
STATES
end
end
If you change the structure of that constant in the future you can always preserve the old behaviour:
class ExampleClass
STATES = {
on: 'On',
off: 'Off',
broken: 'Broken'
}.freeze
def self.states
STATES.keys
end
end
When you're talking about instance variables you mean things you can configure:
class ConfigurableClass
INITIAL_STATE_DEFAULT = :off
def self.initial_state
#initial_state || INITIAL_STATE_DEFAULT
end
def self.initial_state=(value)
#initial_state = value ? value.to_sym
end
end
Constants are great in that they're defined once and used for the duration of the process, so technically they're faster. Instance variables are still pretty quick, and are often a necessity as illustrated above.
Constants, unlike instance variables, are global. And they will at least complain if you try to re-assign their value.
While there might be a theoretical difference in memory/speed, it will be irrelevant in practice.
You may not realize this, but classes and modules are considered constants.
pry(main)> Foo
NameError: uninitialized constant Foo
The best advice I can give as to when you should use constants are when they are exactly that, constant. For example, if I was making a scope in rails to find all the of recent Foos for instance, I would create a constant that shows what recent is.
class Foo < ActiveRecord::Base
DAYS_TILL_OLD = 7.days
scope :recent, -> { where "created_at > ?", DateTime.now - DAYS_TILL_OLD }
end
Related
Most of the Factorybot factories are like:
FactoryBot.define do
factory :product do
association :shop
title { 'Green t-shirt' }
price { 10.10 }
end
end
It seems that inside the ":product" block we are building a data structure, but it's not the typical hashmap, the "keys" are not declared through symbols and commas aren't used.
So my question is: what kind of data structure is this? and how it works?
How declaring "association" inside the block doesn't trigger a:
NameError: undefined local variable or method `association'
when this would happen on many other situations. Is there a subject in compsci related to this?
The block is not a data structure, it's code. association and friends are all method calls, probably being intercepted by method_missing. Here's an example using that same technique to build a regular hash:
class BlockHash < Hash
def method_missing(key, value=nil)
if value.nil?
return self[key]
else
self[key] = value
end
end
def initialize(&block)
self.instance_eval(&block)
end
end
With which you can do this:
h = BlockHash.new do
foo 'bar'
baz :zoo
end
h
#=> {:foo=>"bar", :baz=>:zoo}
h.foo
#=> "bar"
h.baz
#=> :zoo
I have not worked with FactoryBot so I'm going to make some assumptions based on other libraries I've worked with. Milage may vary.
The basics:
FactoryBot is a class (Obviously)
define is a static method in FactoryBot (I'm going to assume I still haven't lost you ;) ).
Define takes a block which is pretty standard stuff in ruby.
But here's where things get interesting.
Typically when a block is executed it has a closure relative to where it was declared. This can be changed in most languages but ruby makes it super easy. instance_eval(block) will do the trick. That means you can have access to methods in the block that weren't available outside the block.
factory on line 2 is just such a method. You didn't declare it, but the block it's running in isn't being executed with a standard scope. Instead your block is being immediately passed to FactoryBot which passes it to a inner class named DSL which instance_evals the block so its own factory method will be run.
line 3-5 don't work that way since you can have an arbitrary name there.
ruby has several ways to handle missing methods but the most straightforward is method_missing. method_missing is an overridable hook that any class can define that tells ruby what to do when somebody calls a method that doesn't exist.
Here it's checking to see if it can parse the name as an attribute name and use the parameters or block to define an attribute or declare an association. It sounds more complicated than it is. Typically in this situation I would use define_method, define_singleton_method, instance_variable_set etc... to dynamically create and control the underlying classes.
I hope that helps. You don't need to know this to use the library the developers made a domain specific language so people wouldn't have to think about this stuff, but stay curious and keep growing.
I found examples of a mixin that makes assumptions about what instance variables an including class has. Something like this:
module Fooable
def calculate
#val_one + #val_two
end
end
class Bar
attr_accessor :val_one, :val_two
include Fooable
end
I found arguments for and against whether it's a good practice. The obvious alternative is passing val_one and val_two as parameters, but that doesn't seem as common, and having more heavily parameterized methods could be a downside.
Is there conventional wisdom regarding a mixin's dependence on class state? What are the advantages/disadvantages of reading values from instance variables vs. passing them in as parameters? Alternatively, does the answer change if you start modifying instance variables instead of just reading them?
It is not a problem at all to assume in a module some properties about the class that includes/prepends it. That is usually done. In fact, the Enumerable module assumes that a class that includes/prepends it has a each method, and has many methods that depend on it. Likewise, the Comparable module assumes that the including/prepending class has <=>. I cannot immediately come up with an example of an instance variable, but there is not a crucial difference between methods and instance variables regarding this point; the same should be said about instance variables.
Disadvantage of passing arguments without using instance variable is that your method call will be verbose and less flexible.
Rule of thumb: Mixins should never make any assumptions about the classes/modules they may be included in. However, as it usually goes, any rule has exceptions.
But first, let's talk about the first part. Specifically, accessing (depending on) including class instance variables. If your mixin depends on anything within the including class, then it means that you can not change that "anything" in the parent class with a guarantee that it would not break something. Also, you will have to document that dependency of mixin not only in documentation related to mixin, but also in the documentation of the class/module that includes the mixin. Because, down the road, the requirements may change or someone might see an opportunity in refactoring your class/module code. Obviously, that person will not dig for that class's documentation or know that that specific class/module has a section in your documentation.
Anyhow, by depending on including class internals, not only your mixin made itself dependant, but also ended up making any class/module that includes it a dependant. Which is definitely not a good thing. Because, you can not control who or which class/module has included your mixin, you will never have a confidence to introduce a change. Not having that confidence to change without a fear of breaking anything is project drainer!
The "workaround" may be - "covering it with test". But, consider yourself or someone else maintaining that code in 2 years. Will you remember to cover your new class, that includes the mixin, to make sure it complies with all mixin dependency requirements? I am sure you or the new maintainer will not.
So, from the maintenance or basic OOP principles, your mixin must not depend on any including class/module.
Now, let's talk about that there is alway an exception to the rule bit.
You can make an exception, provided that mixin dependency does not introduce "surprises" to your code. So, it is ok, if the mixin dependencies are well known among your team or they are a convention. Another case could be when the mixin is used internally and you control who uses it (basically, when you are using it within your own project).
The key advantage of OOP in developing maintainable systems was its ability to hide/encapsulate the implementation details. Making your mixin dependant on any class that includes it, is throwing all the years of OOP experience out the window.
I'd say a mixin should not make assumptions about a specific class it is included in, but it's totally fine to make assumptions about a common parent class (respectively its public methods).
Good example: It's fine to call params in a mixin that will be included in controllers.
Or, to be more precise according your example, I'd think something like this would totally be fine:
class Calculation
attr_accesor :operands
end
module SumOperation
def sum
self.operands.sum
end
end
class MyCustomCalculation < Calculation
include SumOperation
end
You should not hesitate, even for a second, to include instance variables in your mixin module when the situation calls for it.
Suppose, for example, you wrote:
class A
def initialize(h)
#h = h
end
def confirm_colour(colour)
#h[:colour] == colour
end
def confirm_size(size)
#h[:size] == size
end
def confirm_all(colour, size)
confirm_colour(colour) && confirm_size(size)
end
end
a = A.new(:colour=>:blue, :size=>:medium, :weight=>10)
a.confirm_all(:blue, :medium)
#=> true
a.confirm_all(:blue, :large)
#=> false
Now suppose someone asks for the weight be checked as well. We could add the method
def confirm_weight(weight)
#h[:weight] == weight
end
and change confirm_all to
def confirm_all(colour, size)
confirm_colour(colour) && confirm_size(size) && confirm_weight(size)
end
but there is a better way: put all the checks in a module.
module Checks
def confirm_colour(g)
#h[:colour] == g[:colour]
end
def confirm_size(g)
#h[:size] == g[:size]
end
def confirm_weight(g)
#h[:weight] == g[:weight]
end
end
Then include the module in A and run through all the checks.
class A
include Checks
def initialize(h)
#h = h
end
def confirm_all(g)
Checks.instance_methods.all? { |m| send(m, g) }
end
end
a = A.new(:colour=>:blue, :size=>:medium, :weight=>10)
a.confirm_all(:colour=>:blue, :size=>:medium, :weight=>10)
#=> true
a.confirm_all(:colour=>:blue, :size=>:large, :weight=>10)
#=> false
This has the advantage that when checks are to be added or removed, only the module is affected; no changes need to be made to the class. This example is admittedly contrived, but it is a short step to real-world situations.
Although it is common to make these assumptions, you may want to consider a different pattern in order to make more composable and testable code: Dependency Injection.
module Fooable
def add(one, two)
one + two
end
end
class Bar
attr_accessor :val_one, :val_two
include Fooable
def calculate
add #val_one, #val_two
end
end
Although it adds an extra layer of indirection, often times it will be worth it because of the potential to use the concern across a larger number of classes, as well as making it easier to test code.
In general which is better for a global cache: global variable, constant, or class instance variable?
Here is an example of each:
module Foo
$FOO_CACHE = {}
def self.access_to_cache
$FOO_CACHE
end
end
module Foo
CACHE = {}
def self.access_to_cache
CACHE
end
end
module Foo
#cache = {}
def self.access_to_cache
#cache
end
end
This is ultimately pretty subjective, but I’ll address each option one-by-one:
Global variable: no …because putting a global variable inside a module (or a class, or anything for that matter) doesn’t make much sense, it’s going to be in scope everywhere anyway. Besides the fact that if you can use something other than a global variable, you should always do so.
Constant: no …because the cache is not constant! While Ruby doesn't enforce that constants can’t change, that doesn’t mean you should do it. There’s a reason they’re called constants.
Class instance variable: yes …because it’s the only one here that makes any sense (though the name might not, technically here it’s a module instance variable, but that’s being rather pedantic). This is the only one of the three that both makes semantic sense to modify and is encapsulated by some scope.
With strings one can do this:
a = "hello"
a.upcase!
p a #=> "HELLO"
But how would I write my own method like that?
Something like (although that doesn't work obviously):
class MyClass
def positify!
self = [0, self].max
end
end
I know there are some tricks one can use on String but what if I'm trying to do something like this for Object?
Many classes are immutable (e.g. Numeric, Symbol, ...), so have no method allowing you to change their value.
On the other hand, any Object can have instance variables and these can be modified.
There is an easy way to delegate the behavior to a known object (say 42) and be able to change, later on, to another object, using SimpleDelegator. In the example below, quacks_like_an_int behaves like an Integer:
require 'delegate'
quacks_like_an_int = SimpleDelegator.new(42)
quacks_like_an_int.round(-1) # => 40
quacks_like_an_int.__setobj__(666)
quacks_like_an_int.round(-1) # => 670
You can use it to design a class too, for example:
require 'delegate'
class MutableInteger < SimpleDelegator
def plus_plus!
__setobj__(self + 1)
self
end
def positify!
__setobj__(0) if self < 0
self
end
end
i = MutableInteger.new(-42)
i.plus_plus! # => -41
i.positify! # => 0
Well, the upcase! method doesn't change the object identity, it only changes its internal structure (s.object_id == s.upcase!.object_id).
On the other hand, numbers are immutable objects and therefore, you can't change their value without changing their identity. AFAIK, there's no way for an object to self-change its identity, but, of course, you may implement positify! method that changes properties of its object - and this would be an analogue of what upcase! does for strings.
Assignment, or binding of local variables (using the = operator) is built-in to the core language and there is no way to override or customize it. You could run a preprocessor over your Ruby code which would convert your own, custom syntax to valid Ruby, though. You could also pass a Binding in to a custom method, which could redefine variables dynamically. This wouldn't achieve the effect you are looking for, though.
Understand that self = could never work, because when you say a = "string"; a = "another string" you are not modifying any objects; you are rebinding a local variable to a different object. Inside your custom method, you are in a different scope, and any local variables which you bind will only exist in that scope; it won't have any effect on the scope which you called the method from.
You cannot change self to point to anything other than its current object. You can make changes to instance variables, such as in the case string which is changing the underlying characters to upper case.
As pointed out in this answer:
Ruby and modifying self for a Float instance
There is a trick mentioned here that is a work around, which is to write you class as a wrapper around the other object. Then your wrapper class can replace the wrapped object at will. I'm hesitant on saying this is a good idea though.
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.