Dot syntax vs param passing syntax - ruby

Are only the core Ruby methods callable using object.functionName syntax? Is it possible to create methods on my own that are callable in the dot syntax fashion?
For this method:
def namechanger (name)
nametochange = name
puts "This is the name to change: #{nametochange}"
end
First one below works, the second does not.
namechanger("Steve")
"Steve".namechanger
I get an error on "Steve".namechanger
The error is:
rb:21:in `<main>': private method `namechanger' called for "Steve":String (NoMethodError)

Yes, you can add methods to the String class to achieve your desired effect; the variable "self" refers to the object which receives the method call.
class String
def namechanger
"This is the name to change: #{self}"
end
end
"Steve".namechanger # => This is the name to change: Steve
This practice is known as monkey patching and should be used carefully.

Instead of monkeypatching, you could alway subclass and thus:
Be precise about what you think the object really is.
Gain all the methods of String
For example:
class PersonName < String
def namechanger
puts "This is the name to change: #{self}"
end
end
s = PersonName.new( "Iain" )
s.namechanger
This is the name to change: Iain
=> nil

What you have here in the first form is a method which takes a parameter, which is very different than a method that doesn't take any parameters. Let me illustrate
ruby
namechanger("Steve")
Looks for a method named namechanger and passes a string argument to it. Straight forward. It looks up in some unknown context, probably the locals of another method which will look it up on the object that receives that method.
ruby
"Steve".namechanger
is a method that takes no arguments that exists on String. Typically these methods use the implicit self parameter to operate on some data.

If you want to be able to call "Steve".namechanger, you have to make namechanger a method of the String class like this:
class String
def namechanger
puts "This is the name to change: #{self}"
end
end
This is generally referred to as "monkey patching" and you might want to improve your general Ruby proficiency a bit before you get into the related considerations and discussions.

You could do
class String
def namechanger
puts "This is the name to change: #{self}"
end
end
The difference is that your first example is a method that's (basically) globally defined, which takes a string and operates on it. This code above however defines a method called "namechanger" which takes no parameters, and defines it directly on the String class. So any and all strings in your application will then have this method.
But as pst said, you should probably not dive into that style of programming until you get a little more familiar with Ruby, so that you can more easily see the upsides and downsides of doing monkeypatching like this. One of the considerations is that you probably have many strings that don't represent names, and it doesn't make a lot of sense for those strings to have a method called namechanger.
That said, if your goal is just to have a little fun with Ruby, to see what you can do, go for it, but remember to be more careful in projects that will have a longer lifespan.

Related

Ruby: understanding data structure

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.

How and why does a method's name become a symbol in Ruby?

I am wondering why and how a method's name become a symbol in Ruby automatically. When I run the code below:
# create a BankAccount class
class BankAccount
def initialize(name)
#name = name
#transactions = []
end
def get_name
#name
end
end
aaron = BankAccount.new("Aaron")
aaron.methods
It will automatically create an ":get_name" symbol, what does this refers to? and how does this happens?
What I mean is that in JavaScript, I can define a name of method by:
let fn = function(){
return "This is a function"
}
console.log(fn)
Through console.log(fn), I can get the "fn".
But in Ruby, if that fn will become :fn? or what the difference of "fn" and ":fn"?
Sorry for unclear description?
I check the documentation.
Does the name of the function become an symbol while defining the method by the Method#name ??
Great question.
Your observation is correct.
Method names are internally stored as symbols. Each Ruby class has an internal hash, that maps symbols to an instruction sequence of bytecode. Whereas Javascript either does not distinguish between strings and symbols or does not expose this distinction to the programmer, the latter is more likely.
Symbols are a concept that goes back to early languages like Smalltalk and Scheme. They are internal representations of a string with the additional contract that all symbols with the same name are always the same object. This allows the language to optimize internal processes like calling a method or looking up a class name.
Fun fact, Ruby symbols are represented as tagged integers internally and there is an internal table that maps these magic numbers to identifier names.
A method name doesn't "automatically" become anything. The methods method simply returns what methods have been defined and describes them as symbols, nothing more.
If you want to do some research into a method specifically, do this:
aaron.method(:get_name)
# => #<Method: BankAccount#get_name>
Note that this is exactly equivalent:
aaron.method("get_name")
The method method does not care if you give it a string or symbol, it converts as necessary.
Then you can interrogate that with things like:
aaron.method(:get_name).source_location
Anything else the Method class supports is likewise valid.

Method call before method in Ruby?

I am new to Ruby and I saw methods defined like:
def method_one
puts "method 1"
end
class MyClass
method_one
def method_two
puts "method 2"
end
end
The way method_one is used reminds me of Python decorators.The output of
c = MyClass.new
c.method_two
is
method 1
method 2
I have been trying to search for more information about this syntax/language feature in the Ruby documentation on the web but I don't know what keywords to search for.
What this is thing called?
TL;DR
This code doesn't do what you think it does. Don't do stuff like this.
Ruby's Top-Level Object
Ruby lets you define methods outside a class. These methods exist on a top-level object, which you can (generally) treat as a sort of catch-all namespace. You can see various posts like What is the Ruby Top-Level? for more details, but you shouldn't really need to care.
In your original post, method_one is just a method defined in the top-level. It is therefore available to classes and methods nested within the top-level, such as MyClass.
Methods in Classes
Despite what you think, the following doesn't actually declare a :method_one class or instance method on MyClass:
class MyClass
method_one
def method_two; end
end
Instead, Ruby calls the top-level ::method_one during the definition of the class, but it never becomes a class method (e.g. MyClass::method_one) or an instance method (e.g. MyClass.new.method_one). There might be a few use cases for doing this (e.g. printing debugging information, test injection, etc.) but it's confusing, error-prone, and generally to be avoided unless you have a really strong use case for it.
Better Options
In general, when you see something like this outside an academic lesson, the programmer probably meant to do one of the following:
Extend a class.
Add a singleton method to a class.
Include a module in a class.
Set up a closure during class definition.
The last gets into murky areas of metaprogramming, at which point you should probably be looking at updating your class initializer, or passing Proc or lambda objects around instead. Ruby lets you do all sorts of weird and wonderful things, but that doesn't mean you should.
I think you're a little mislead; the output of:
c = MyClass.new
c.method_two
is
#<MyClass:0x007feda41acf18>
"method 2"
You're not going to see method one until the class is loaded or if you're in IRB you enter the last end statement.
I would suggest looking into ruby's initialize method.

Why can't I call .capitalize method on my Ruby object?

Here is the test code for Ruby.
class Me
puts "i am me"
end
who = Me.new
myself = who.capitalize
I would expect that the string "I am me" would return, but I am mistaken. Ruby doesn't recognize the .capitalize method for a newly created object from a unique class. It's fine with returning who as the "i am me" string, but if I try to call .capitalize it doesn't recognize the method. Why?
That's because the method capitalize is defined in the String class.
First of all, in the line puts "i am me" you are just executing the method puts. Once executed, the string "i am me" is gone, because you don't hold any reference to it, and it has nothing to do with the class Me.
Perhaps this code reflects what you try to do
class Me
# Returns the string, can be executed by instances of class Me
def to_s
"i am me"
end
end
puts Me.new.to_s.capitalize
I thought all Ruby objects can call .capitalize method
No. You can call some_obj.some_method if and only if the class of some_obj or one of its superclasses or included modules defines the method some_method. This means that for a method to be callable on any object, it needs to be defined in the Object class, which is the superclass of all classes, (or one of its included modules).
capitalize is not defined on Object it is defined on String and for good reason. 42.capitalize or [].capitalize would make no sense.
class Me
puts "i am me"
end
This code probably doesn't do what you think it does. When the class definition of Me is first read, the string i am me will be printed. This does not define any behaviour of the Me class. Basically it's the same as:
class Me
end
puts "i am me"
I.e. you're creating an empty class.
If your intention was that the string i am me should be printed every time you do Me.new, puts "i am me" needs to be placed in Me's initialize method. But even so, you won't be able to call String methods on Me objects.
Also you seem to be under the impression that you can change a string after it has already been printed. This is of course not possible. Once you call puts on a string, it appears on the screen and you can't take it back (well you could using termios or terminal-specific control sequences, but that's not the point).
As I can see in my docs, only String and Symbol have capitalize method. So only them and their child classes have it.

Saving Dynamic Ruby Classes

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.

Resources