How does the syntax MODULE::METHODNAME('string') work - ruby

I recently had cause to use the nokogiri gem to parse html but while i going through their documentation, i came across this ruby syntax that i hadn't seen before
html_doc = Nokogiri::HTML('<html><body><h1>Mr. Belvedere Fan Club</h1></body></html>')
xml_doc = Nokogiri::XML('<root><aliens><alien><name>Alf</name></alien></aliens></root>')
The part of interest for me is Nokogiri::HTML('...'). This looks very much like a method invocation but i know ruby method names cannot be in capital letters. So i looked through code files nokogiri gem and i came across the following definition
module Nokogiri
class << self
###
# Parse HTML. Convenience method for Nokogiri::HTML::Document.parse
def HTML thing, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML, &block
Nokogiri::HTML::Document.parse(thing, url, encoding, options, &block)
end
end
# more code
end
I tried reproducing the same code
module How
class << self
def DOESTHISWORK
puts "In How Method"
end
end
end
How::DOESTHISWORK
But it keeps coming back with the error "uninitialized constant How::DOESTHISWORK (NameError)". I know it has to do with the method name starting in capitals but i just haven't been able to figure out how it works in nokogiri.

The difference is in the Nokogiri example the method is being called with parentheses and a parameter value which identifies it as a method call. Your DOESTHISWORK method takes no parameters but can be called with empty parentheses e.g.
irb(main):028:0> How::DOESTHISWORK()
In How Method
=> nil
If you add a parameter to your method that can also serve to identify it as a method like so:
irb(main):036:0> How::DOESTHISWORK 'some param'
Starting method names with a lowercase letter is good practice but isn't enforced. Something that begins with a capital letter is assumed to be a constant and will be looked up as such, this is why the parentheses or parameter is needed to indicate a method is being referred to. Another example:
irb(main):051:0> def Example
irb(main):052:1> puts "An example!"
irb(main):053:1> end
=> nil
irb(main):054:0> Example
NameError: uninitialized constant Example
from (irb):54
from /Users/mike/.rbenv/versions/1.9.3-p194/bin/irb:12:in `<main>'
irb(main):055:0> Example()
An example!
=> nil

I also found this post to be very helpful
What are the restrictions for method names in Ruby?
It's good practice, while not mandatory, to start the method name with
a lower-case character, because names that start with capital letters
are constants in Ruby. It's still possible to use a constant name for
a method, but you won't be able to invoke it without parentheses,
because the interpeter will look-up for the name as a constant

Related

What is the method called when the URI('http://google.com') start?

Almost all of you used URI module to convert a url string to an object in order to make some validation or change.
Example:
require 'uri'
URI('https://google.com')
# => #<URI::HTTPS https://google.com>
As you can see, the result is the HTTPS object under the URI module.
So, there is a question what is run when you write module/class name with round braces like the line of code above.
I thought, it is implicit calling of call method, but I got NoMethodError.
Example:
class MyClass
def self.call
puts 'You were right!'
end
end
MyClass()
# => NoMethodError: undefined method `MyClass' for main:Object
Funny enough, with the code you've shown, MyClass.() works (which is an alias for .call).
However in the case of URI, this actually a method (methods can begin with capitals). You can see the source code here: https://apidock.com/ruby/Kernel/URI/instance

How to serialize Exception

According to ruby-doc and apidock, you can serialize and deserialize an exception using to_json and json_create.
But after having wasted some time trying to use them, I still haven't found a way.
Calling exc.to_json gives me an empty hash, and Exception.json_create(hash) gives me this error: undefined method 'json_create' for Exception:Class
I guess I could easily recreate those functions since the source is available, but I'd rather understand what I'm doing wrong... Any idea?
The JSON module doesn't extend Exception by default. You have to require "json/add/exception". I'm not sure if this is documented anywhere:
require "json/add/exception"
begin
nil.foo
rescue => exception
ex = exception
end
puts ex.to_json
# => {"json_class":"NoMethodError","m":"undefined method `foo' for nil:NilClass","b":["prog.rb:5:in `<main>'"]}
Check out ext/json/lib/json/add in the Ruby source to see which classes work this way. If you do require "json/add/core" it will load JSON extensions for Date, DateTime, Exception, OpenStruct, Range, Regexp, Struct, Symbol, Time, and others.
The answer from Jordan is correct. If you have a case eg. that you need to serialize an Exception and send to to ActiveJob where you want to reconstruct it, then you need to use .as_json method.
require "json/add/exception"
begin
nil.foo
rescue => exception
ex = exception
end
puts ex.to_json.class
# => String
string = ex.to_json
puts Exception.json_create(string).message
# => m
puts ex.as_json.class
# => Hash
hash = ex.as_json
puts Exception.json_create(hash).message
# => undefined method `foo' for nil:NilClass
You need to read the source code to understand why Exception.json_create(string).messagereturns m :)
It's also important to note that the Exception.json_create example doesn't keep the error class.
Exception.json_create(JSON.parse(ArgumentError.new('error').to_json))
# => #<Exception: error>
Try instead:
require "json/add/exception"
def deserialize_exception(json)
hash = JSON.parse(json)
hash['json_class'].constantize.json_create(hash)
end

What does &. (ampersand dot) mean in Ruby?

I came across this line of ruby code. What does &. mean in this?
#object&.method
It is called the Safe Navigation Operator. Introduced in Ruby 2.3.0, it lets you call methods on objects without worrying that the object may be nil(Avoiding an undefined method for nil:NilClass error), similar to the try method in Rails.
So you can write
#person&.spouse&.name
instead of
#person.spouse.name if #person && #person.spouse
From the Docs:
my_object.my_method
This sends the my_method message to my_object. Any
object can be a receiver but depending on the method's visibility
sending a message may raise a NoMethodError.
You may use &. to designate a receiver, then my_method is not invoked
and the result is nil when the receiver is nil. In that case, the
arguments of my_method are not evaluated.
Note: Even though #Santosh gave a clear and full answer, I would like add some more background and add an important note regarding its use with non instance variables.
It is called "Safe Navigation Operator" (aka "Optional chaining operator", "Null-conditional operator", etc.). Matz seems to call it "lonely operator". It was introduced in Ruby 2.3. It sends a method to an object only if it is not nil.
Example:
# Call method `.profile` on `user` only if `user` is not `nil`
#user&.profile
# Equivalent to
unless #user.nil?
#user.profile
end
"Edge case" with local variables:
Please note, above code uses instance variables. If you want to use safe navigation operator with local variables, you will have to check that your local variables are defined first.
# `user` local variable is not defined previous
user&.profile
# This code would throw the following error:
NameError: undefined local variable or method `user' for main:Object
To fix this issue, check if your local variable is defined first or set it to nil:
# Option 1: Check the variable is defined
if defined?(user)
user&.profile
end
# Option 2: Define your local variable. Example, set it to nil
user = nil
user&.profile # Works and does not throw any errors
Method background
Rails has try method that basically does the same. It uses send method internally to call a method. Matz suggested that it is slow and this should be a built-in language feature.
Many other programming languages have similar features: Objective C, Swift, Scala, CoffeeScript, etc. However, a common syntax is ?. (question dot). But, this syntax could not be adopted by Ruby. Because ? was allowed in method names and thus, ?. symbol sequence is already a valid Ruby code. For example:
2.even?.class # => TrueClass
That's why Ruby community had to come up with different syntax. It was an active discussion and different options were considered (.?, ?, &&, etc.). Here is a list of some considerations:
u.?profile.?thumbnails
u\profile\thumbnails
u!profile!thumbnails
u ? .profile ? .thumbnails
u && .profile && .thumbnails
# And finally
u&.profile&.thumbnails
While choosing the syntax, developers looked at different edge cases and the discussion is quite useful to go through. If you want to go through all variants and nuance of the operator, please see this feature introduction discussion on official Ruby issue tracker.
Be wary! Though the safe navigation operator is convenient it can also be easy to trick yourself into changing your logic with it. I recommend avoiding the use of it in flow control. Example:
str = nil
puts "Hello" if str.nil? || str.empty?
# The above line is different than the below line
puts "Hello" if str&.empty?
In the first example, str.nil? returns true and str.empty? is never called, causing the puts statement to be executed. In the second example however, str&.empty? returns nil which is falsey, and the puts statement is never executed.
safe navigation operator (&.): tells Ruby to only call the next method if the receiver isn’t nil. Otherwise, the expression returns nil.
Practical In Action
Let’s construct a Roster object for a Sports team. The Roster will contain multiple Player objects.
class Roster
attr_accessor :players
end
class Player
attr_accessor :name, :position
def initialize(name, position)
#name = name
#position = position
end
end
With these two objects, we can create a roster for a 2-on-2 women’s basketball tournament:
moore = Player.new("Maya Moore", "Forward")
taurasi = Player.new("Diana Taurasi", "Guard")
tourney_roster1 = Roster.new
tourney_roster1.players = [moore, taurasi]
If we want to know the forward for our 2-on-2 team, we might find the name this way:
if tourney_roster1.players.first.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
But what if our opposing roster isn’t set correctly?
tourney_roster2 = Roster.new
if tourney_roster2.players.first.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
tourney_roster2 hasn’t yet been set with any players. The preceding code will raise a NoMethodError because tourney_roster2.players returns nil. We can add conditional statements to avoid this, but it makes our if statement verbose and unclear:
if tourney_roster2.players &&
tourney_roster2.players.first &&
tourney_roster2.players.first.position == "Forward"
Instead, we can use the safe navigation operator to avoid the NoMethodError:
if tourney_roster2.players&.first&.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
Thus,
>> tourney_roster2.players&.first == nil
#=> true
>> tourney_roster2.players&.first&.position == nil
#=> true
Some legitimate use cases: The safe navigation operator comes in handy when working with multiple objects, as shown here, and when chaining methods together.
it used for nil check, such as in kotlin and swift
For example;
with Object -> Swift and Kotlin
model = car?.model
this model can be nil(Swift) or null(Kotlin) if we have not defined the model value in car class.
we use that ampersand instead of question mark in ruby
model = car&.model
if use car.model without ampersand and if model is nil the system cannot continue running.
Here's a short-read (3 mins) I found on this - it is pretty good.
To add to the above, it acts like the try! method in Rails, not the try method.
Because it will raise a NoMethodError exception if the receiver is not nil and does not implement the tried method.
Example taken from the above article:
account = Account.new(owner: Object.new)
account&.owner&.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
account.try(:owner).try(:address)
# => nil
account.try!(:owner).try!(:address)
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
For all of those who came here from Typescript, it's the same as the ? operator
what does mean object&.an_attribute in ruby on rails?
I am new in Ruby on rails and I saw this kind of code but I don't understand it:
In Ruby, like in most mainstream programming languages, user code cannot modify the fundamental workings of the programming languages, nor can it change the programming language's syntax.
Since Ruby on Rails is just Ruby code, it should be immediately obvious that this cannot possibly have anything to do with Ruby on Rails.
Therefore, we need to look at Ruby for an explanation, not Ruby on Rails.
The safe navigation operator or safe navigator is specified in language/safe_navigator_spec.rb of the ruby/spec, in particular here:
context "when context is nil" do
it "always returns nil" do
eval("nil&.unknown").should == nil
eval("[][10]&.unknown").should == nil
end
it "can be chained" do
eval("nil&.one&.two&.three").should == nil
end
it "doesn't evaluate arguments" do
obj = Object.new
obj.should_not_receive(:m)
eval("nil&.unknown(obj.m) { obj.m }")
end
end
It is documented in the Calling Methods section of the Ruby Syntax documentation:
&., called “safe navigation operator”, allows to skip method call when receiver is nil. It returns nil and doesn't evaluate method's arguments if the call is skipped.

What does IRB use to determine how to represent a returned instance?

Given a Ruby class:
class Foo
def initialize(options={})
#sensitive = options.delete :sensitive
end
end
If I create an instance of that class in IRB, I get to see instance vars and memory address.
irb(main):002:0> Foo.new(sensitive: 'foo')
=> #<Foo:0x007fe766134a98 #sensitive="foo">
If I create an instance of AWS::S3, I don't:
irb(main):003:0> require 'aws-sdk'
=> true
irb(main):004:0> AWS::S3.new(access_key_id: 'aki', secret_access_key: 'sak')
=> <AWS::S3>
Note that AWS::S3 is not a singleton (at least not in the sense of explicitly including the Singleton module).
Is there anything I can do to tell IRB not to output instance vars and/or memory address?
(I've already tried .to_s but I get a string containing the memory address for instances of both classes without any instance vars.)
If you start IRB irb --noecho, it will suppress all IRB inspections. But I think this is not your question.
IRB use #inpect method. Read the line from the Documentation :
Returns a string containing a human-readable representation of obj. By default, show the class name and the list of the instance variables and their values (by calling inspect on each of them). User defined classes should override this method to make better representation of obj. When overriding this method, it should return a string whose encoding is compatible with the default external encoding.
Example :
class Foo
def initialize
#x = 10
end
# customized inspection
def inspect
"0x%7x" % self.object_id.to_s
end
end
foo = Foo.new
foo # => 0x118e27c
Note : I used String#% method inside my customized #inspect method.
The standard method being used to render human-readable debugging output (not just in IRb but in general, e.g. on Rails error pages etc.) is #inspect. Depending on which extensions you loaded, your command line options or whether you are using Pry instead of IRb, it may also look for a #pretty_inspect first.

How can I mimic Node.js's require function in Ruby?

In node.js you can write:
var lib = require('lib');
but in Ruby the require function simply runs the code in the file and true is returned.
Currently, I'm using a very dirty solution:
main.rb:
$stuff = []
require './file1.rb'
require './file2.rb'
# and so on
file1.rb:
$stuff << something
and so on.
How can I eliminate the use of a global variable?
eg:
main.rb:
$stuff = []
$stuff << cool_require './file1.rb'
# etc
file1.rb:
exports.what = something
One of the biggest errors when working with a language, is trying to make the language working like a different one.
Ruby is not NodeJs, there are features built-in into each language that are unique to the language and cannot be reproduced easily.
In other words, there is no way to implement the NodeJS require behavior in Ruby because in Ruby there is no notion of export. When you require a file, every method/class included in the required file are made available to the scope.
In Ruby there are objects and method visibility. The way you have to make a method visible or not is to declare it as public or private/protected.
Well, first consider that Ruby is not Node.js. As Simone Carletti said, there are some features that are unique to each language. Sometimes it's good to take from other language but sometimes it's bad.
There are few things that you must keep in mind:
meth is method invocation, to pass method you use method method: method(:meth) or package it into module/class
you can use class/module by assigning it to some 2nd variable:
class A;
def self.aa; puts 'aa'; end;
end;
New_a = A;
New_a.aa # aa;
eval is dangerous method(you can evaluate unknown code)
Method:
Here is one way you can do. It is not idiot-proof tough. It is not 100% safe(eval). :
file1.rb:
Module.new do
def self.meth1
42
end
def self.meth2
'meth2'
end
end
This file contain module with 2 methods.
I am using Module.new because it returns object that you want. You can assign it later into variable/constant.
I am using self.meth* so you don't have to include but run instantly it like this: module_name.meth1()
req.rb:
def cool_require name
eval(File.read name)
end
Some_variable = cool_require('req.rb')
puts Some_variable.meth1 # 42
puts Some_variable.meth2 # meth2
cool_require reads filename(argument name) and evaluate it(it is just like you would type it in irb/pry)
Some_variable is constant. It won't disappear that easily.
2 last line is how it works. As fair I remember, that's how node.js' require works.

Resources