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.
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.
As a beginner, I've not quite got my head around self so I'm having trouble understanding how the self.blogs in initialize, and blogs then self.blogs on the next line after in the add_blog method, are all working together in the below code.
Why does blogs in the add_blog method access the same variable as self.blogs in initalize?
And then why is self.blogs used afterwards to sort the blogs array?
Also, would it matter if I used #blogs in initialize, instead of self.blogs?
class User
attr_accessor :username, :blogs
def initialize(username)
self.username = username
self.blogs = []
end
def add_blog(date, text)
added_blog = Blog.new(date, self, text)
blogs << added_blog
self.blogs = blogs.sort_by { |blog| blog.date }.reverse
added_blog
end
end
To answer your question, we have to reveal the true nature of attr_accessor.
class Foo
attr_accessor :bar
end
is completely equivalent to
class Foo
def bar
#bar
end
def bar=(value)
#bar = value
end
end
You can see that attr_accessor :bar defines two instance methods Foo#bar and Foo#bar= that access an instance variable #bar.
Lets then look at your code.
self.blogs = [] in initialize is actually calling the method User#blogs=, and through it sets the instance variable #blogs with an empty array. It can be written as self.blogs=([]) but it's noisy, isn't it? By the way, you can't omit self. here otherwise it just sets a local variable.
blogs << added_blog calls the method User#blog which returns the value of #blogs. It can also be written as self.blogs().push(added_blog), but again it's not rubyish. You can omit self. because there is no local variable named blogs in User#add_blog, so ruby falls back to call the instance method.
self.blogs = blogs.sort_by { |blog| blog.date }.reverse mixes call to User#blogs= and User#blogs.
For most method calls on self, self.method_name is equivalent to just method_name. That's not the case for methods whose name ends with an =, though.
The first thing to note, then, is that self.blogs = etc doesn't call a method named blogs and then somehow 'assign etc to it'; that line calls the method blogs=, and passes etc to it as an argument.
The reason you can't shorten that to just blogs = etc, like you can with other method calls, is because blogs = etc is indistinguishable from creating a new local variable named blogs.
When, on the previous line, you see a bare blogs, that is also a method call, and could just as easily have been written self.blogs. Writing it with an implicit receiver is just shorter. Of course, blogs is also potentially ambiguous as the use of a local variable, but in this case the parser can tell it's not, since there's no local variable named blogs assigned previously in the method (and if there had been, a bare blogs would have the value of that local variable, and self.blogs would be necessary if you had meant the method call).
As for using #blogs = instead of self.blogs =, in this case it would have the same effect, but there is a subtle difference: if you later redefine the blogs= method to have additional effects (say, writing a message to a log), the call to self.blogs = will pick up those changes, whereas the bare direct access will not. In the extreme case, if you redefine blogs= to store the value in a database rather than an instance variable, #blogs = won't even be similar anymore (though obviously that sort of major change in infrastructure will probably have knock-on effects internal to the class regardless).
#variable will directly access the instance variable for that class. Writing self.variable will send to the object a message variable. By default it will return the instance variable but it could do other things depending on how you set up your object. It could be a call to a method, or a subclass, or anything else.
The difference between calling blogs or self.blogs is totally up to syntax. If you use an opinionated syntax checker like rubocop it will tell you that you have a redundant use of self
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.
Let's say I want some instance of String behave differently from other, "normal" instances - for example cancel the effect of the "upcase" method. I do the following:
class String
def foo
def self.upcase
self
end
self
end
end
It seems to work fine, and the way I need it:
puts "bar".upcase #=> "BAR"
puts "bar".foo.upcase #=> "bar"
However, as soon as I use the tricked instance of the String as a key for a Hash, the behavior starts looking weird to me:
puts ({"bar".foo => "code"}).keys.first.upcase #=> "BAR", not "bar"!
... which is as if the foo method is ignored, and the original instance of String is used as the key.
Anyone can see what's going on here? Thanks a bunch!
Ruby's Hash has a special case for using strings as a hash key -- it makes an internal copy of the string.
Basically it's to protect you from using a string (object) as a key and then altering that string object later in the code, which could lead to some confusing situations. Mutable keys get tricky.
Rather than hack method onto string that returns an altered string class, I would just create a new subclass of string that overrides upcase and then just set its value.
Just because in Ruby you can reopen core classes and virtually re-define everything, it doesn't mean you should.
With great powers come great responsibilities and your responsability is to not redefine a core library method just because a couple of objects might need this.
If your instance doesn't behave like a Sting, declare your own class and extend String.
The usual way to extend a single object in Ruby is this:
s = "bar"
class<<s
def self.upcase
self
end
end
...but that doesn't solve your problem. It seems that Ruby has special rules for hash keys that are strings, or subclasses of strings Maybe instead of a string, you can use an object with a meaningful definition of to_s?
I have a curiosity question. If I have a ruby class and then I dynamically add class methods, class variables, etc. to it during execution is there anyway for me to save the altered class definition so that next time I start my application I can use it again?
There is no built-in way to do this. Marshal can't save methods. If these methods and variables are generated in some systematic way, you could save the data necessary for the class to recreate them. For example, if you have a make_special_method(purpose, value) method that defines these methods, create an array of the arguments you need to pass to these methods and read it in when you want to reconstitute the state of the class.
Depending on what exactly you mean, there are a couple of ways to go about this.
The simplest case is the one where you've added variables or methods to an already-existing class, as in this example:
class String
def rot13
return self.tr('a-z', 'n-za-m')
end
end
Here we've added the rot13 method to class String. As soon as this code is run, every String everywhere in your program will be able to #rot13. Thus, if you have some code that needs rot13-capable strings, you just ensure that the code above is run before the code in question, e.g. by putting the rot13 code in a file someplace and require()ing it. Very easy!
But maybe you've added a class variable to a class, and you want to preserve not just its existence but its value, as in:
class String
##number_of_tr_calls_made = 0
# Fix up #tr so that it increments ##number_of_tr_calls_made
end
Now if you want to save the value of ##number_of_tr_calls_made, you can do so in the same way you would with any other serializable Ruby value: via the Marshal library. Also easy!
But something in the way you phrased your question makes me suspect that you're doing something like this:
greeting = "Hello"
class <<greeting
def rot13
return self.tr('a-z', 'n-za-m')
end
end
encrypted_greeting = greeting.rot13
This is very different from what we did in the first example. That piece of code gave every String in your program the power to rot13 itself. This code grants that power to only the object referred to by the name 'greeting'. Internally, Ruby does this by creating an anonymous Singleton subclass of String, adding the rot13 method to it, and changing greeting's class to that anonymous subclass.
The problem here is that Singletons can't be Marshal'd (to see why, try to figure out how to maintain the Singleton invariant when any call to Marshal.load can generate copies of extant Singleton objects). Now greeting has a Singleton in its inheritance hierarchy, so if you want to save and load it you are hosed. Make a subclass instead:
class HighlySecurableString < String
def rot13
return self.tr('a-z', 'n-za-m')
end
end
greeting = HighlySecurableString.new("hello")
Simply marshalling the object (as others have said) wont work. Lets look at an example. Consider this class:
class Extras
attr_accessor :contents
def test
puts "This instance of Extras is OK. Contents is: " + #contents.to_s
end
def add_method( name )
self.class.send :define_method, name.to_sym do
puts "Called " + name.to_s
end
end
end
Now lets write a program which creates an instance, adds a method to it and saves it to disk:
require 'extras'
fresh = Extras.new
fresh.contents = 314
fresh.test # outputs "This instance of Extras is OK. Contents is: 314"
fresh.add_method( :foo )
fresh.foo # outputs "Called foo"
serial = Marshal.dump( fresh )
file = File.new "dumpedExample", 'w'
file.write serial
So we can call the normal method 'test' and the dynamic method 'foo'. Lets look at what happens if we write a program which loads the instance of Example which was saved to disk:
require 'extras'
file = File.new 'dumpedExample', 'r'
serial = file.read
reheated = Marshal.load( serial )
reheated.test # outputs "This instance of Extras is OK. Contents is 314"
reheated.foo # throws a NoMethodError exception
So we can see that while the instance (including the values of member variables) was saved the dynamic method was not.
From a design point of view it's probably better to put all your added code into a module and load that into the class again when you next run the program. We'd need a good example of how you might want to use it though to really know this.
If you need extra information to recreate the methods then have the module save these as member variables. Implement included in the module and have it look for these member variables when it is included into the class.
http://ruby-doc.org/core/classes/Marshal.html
You're editing the class on the fly, and you want to save that? You could try using the Marshal module, it'll allow you to save objects to a file, and read them back in memory dynamically.