Different Ways of Namespacing in Ruby - ruby

AFAIK I know these two ways of namespacing in ruby:
module Cat
class Lion
def hunt
p 'roaming for prey ...'
end
end
class Cheetah
def hunt
Lion.new.hunt
p 'Oops there is a lion. Hide first ...'
end
end
end
class Cat::MountainLion
def hunt
Lion.new.hunt
p 'roaming for prey ... (since I dont live in the same continent as lion)'
end
end
Cat::Cheetah.new.hunt
Cat::MountainLion.new.hunt
Why is it the Cat::MountainLion.new.hunt doesn't work? Are namespace declared as module differs to those declared as prefix to class class Cat::?

These two ways differ in the constant lookup. The former looks for Lion constant in Cat namespace, which is correct. The latter looks for ::Lion, in global namespace, which is obviously incorrect, since you don't have such constant.
For more information about this topic, please visit this page:
https://cirw.in/blog/constant-lookup.html

Related

How to fix rubocop offense for a class class a::b::c

I have following file
lib/a/b/c.rb
class a::b::c
def request(env)
#some code here
end
end
Now i am using rubocop style
Style/ClassAndModuleChildren:
Enabled: true
I am getting rubocop offense for this
lib/a/b/c.rb:1:7: C: Use nested module/class definitions instead of compact style.
class a::b::c
When i update my code to following offence get fixed
Style 1
class a
class b
class c
def request(env)
#some code here
end
end
end
end
Style 2
module a
module b
class c
def request(env)
#some code here
end
end
end
end
I think i should use Style 2 as i am using require 'a' in one of my file.
Please let me know how to fix this type & offences and reason for it
The reason this is marked as offence is that constants resolution works lexically, rather than semantically in ruby. This is not intuitive and might lead to some obscure errors (for example if you have two classes with the same name in two different scopes). Compare these two cases:
# Given
module Foo
X = 42
end
# This
module Foo
class Bar
def baz
puts X
end
end
end
# VS
class Foo::Bar
def baz
puts X
end
end
Now when you call:
Foo::Bar.new.baz
In the first case you will get 42, in the second - NameError: uninitialized constant Foo::Bar::X
As for which is the correct way to fix it: Note that using the short syntax will give you an error if Foo doesn't already exist. The answer is - you should use whatever Foo is. If it's a class - use class, if it's a module - module.
Class class is essentially derived from Module class, that’s why Class is a Module with some added functionality; basically the difference is in that Classes might be instantiated.
So, the answer to your question would be: use whatever is more suitable in your case (and by the information given it is impossible to say, what it is.)
require 'a' has nothing to do with the case, since require directive just forces ruby interpreter to load the respective code.
NB: that kind of question is the exact reason why Rubocop complains: it prevents you from using something you are not certain about and forces you to understand, is it in fact Module, or Class by it’s nature.
BTW, since you mentioned you have require 'a' somewhere, it probably means that in this a.rb you have already either module A or class A.
BTW#2 Both class and module names must begin with capital letter, because they are to be constants.

Get list of classes and methods that call a specific global method

I have a method called $muffinize and I would like to find where it can be found in my code. In other words, given the following code:
class A
def foo
$muffinize(1)
end
def bar
...
end
end
class B
def shoop
$muffinize(2)
end
def woop
...
end
end
class C
def nope
...
end
end
I would like to the result to be (written to a file):
A:foo
B:shoop
I was thinking of accomplishing this with a Regex, but I was wondering if there would be some way of accomplishing this with Ruby meta-programming (which I might be accidentally using as a buzz-word)?
Kernel.caller() will help you show the line number and method that is calling it at runtime. If you put something like puts caller(1,1) in your muffinize function it will output those locations, but only if they are called at runtime.
If you want to do offline source analysis, you need to parse the AST (abstract syntax tree) with something like https://github.com/whitequark/parser.
Here is a quick example with ripper (built into new rubies) - this isn't strictly an AST but it's not extracting classes either
#!/usr/local/env ruby
require 'ripper'
#require 'pry'
contents = File.open('example.rb').read
code = Ripper.lex(contents)
code.each do |line|
if(line[1] == :on_ident and line[2] == "muffinize")
puts "muffinize found at line #{line.first.first}"
end
end
Ignoring the fact that your code isn't even syntactically valid, this is simply not possible.
Here's a simple example:
class A
def foo
bar
muffinize(1)
end
end
A#foo will call Object#muffinize if and only if bar terminates. Which means that figuring out whether or not A#foo calls Object#muffinize requires to solve the Halting Problem.
By getting a list of classes and methods via ri, I was then able to analyze each method to retreive their source code using the method_source gem and then searching for muffinize. This does not rule out the possibility of muffinize from appearing in a comment or a string, but I consider the likelihood of this happening to be small enough to ignore.

Is type conversion possible with inheritance in Ruby?

I'm using Ruby, and writing classes with inheritance.
For example:
class Canine
def initialize
end
def make_noise
puts "whoosh whoosh"
end
end
class Dog < Canine
def initialize
end
def make_noise
puts "wong wong"
super
end
end
Now I have a dog object:
jack = Dog.new
Is it possible to call the make_noise() method of Canine through the dog object?
In other languages it would be a typecast, something like:
(Canine)jack.make_noise
Note this is not Ruby syntax, hence, my question.
Is it possible to do this in Ruby? And if so, how?
You can do something like this:
Canine.instance_method(:make_noise).bind(jack).call
A better plan would be to just give the method in the super class an alias, or rename it.
Ruby does not allow casting or conversion in this fashion, at least not in the conventional sense. This is rarely necessary anyway, since Ruby is based on duck typing and not a rigid type system.
Are you expecting "whoosh whoosh" from the call? That's something that would only happen with non-virtual methods in a more strictly typed language like C++. It goes against proper object oriented design.
If you explore the design patterns employed in object-oriented design, you can always solve this sort of problem another way.
What you might want here is a presenter to handle the make_noise functionality.
Otherwise you'll need to write a to_canine method that can convert to the base type, though it's still not clear why you'd need such a thing.
You can do it like this:
d = Dog.new
d.class.superclass.instance_method(:make_noise).bind(d).call
or
Canine.instance_method(:make_noise).bind(d).call
. . . not pretty! I'm not sure if there's a better way
Edit: I think I agree with other answers here, in that Ruby's approach to OO will give you access to other patterns that achieve whatever goals this construct might help you with (perhaps in other languages). I don't see this kind of class/superclass method munging in practice on projects I have been involved in.
I am not sure why you need this, depending on needs it may be done totally differently, but with limited knowledge I would suggest this
class Dog < Canine
def initialize
end
def make_noise only_parent=false
puts "wong wong" if !only_parent
super
end
end
or
class Dog < Canine
def initialize
end
alias :make_super_noise :make_noise
def make_noise
puts "whoosh whoosh"
super
end
end

Ruby: define function prototypes?

It is possible to define a prototype of a function or in some way indicate to Ruby that a function exists even though it may not be defined yet?
I have lots of classes like this:
class Program
FIRST = Block.FIRST
FOLLOW = Set.new['$']
end
class Block
FIRST = Declaration.FIRST
FOLLOW = Set.new['.']
end
class Declaration
FIRST = ConstDecl.FIRST + VarDecl.FIRST + ProcDecl.FIRST
end
class ConstDecl
FIRST = Set.new['const'] + EMPTY_SET
end
Which as you can see reference fields from classes that are defined below them, Is there a way to indicate to Ruby that these classes exist, and ask Ruby to look for them?
The simplest way I can think of is something like this:
class Program
def self.first; Block.first end
def self.follow; Set.new['$'] end
end
class Block
def self.first; Declaration.first end
def self.follow; Set.new['.'] end
end
class Declaration
def self.first; ConstDecl.first + VarDecl.first + ProcDecl.first end
end
class ConstDecl
def self.first; Set.new['const'] + EMPTY_SET end
end
This doesn't seem like good design to me, though, I'd probably make those objects instead of classes and use a proper type hierarchy.
A major difference between Ruby and other languages which you may be accustomed to (like C/C++) is that before execution, a C/C++ program is processed by a compiler which matches up uses of variables/functions to their definitions. Ruby programs are simply executed from top to bottom, one statement at a time. So when a line which references Block.FIRST is executed, the Ruby interpreter can't "look forward" in the program code and see what value will be assigned to Block.FIRST later. It knows nothing about what will come later; it only knows what it has executed so far.
Perhaps one of the strongest characteristics of Ruby is that almost everything is dynamic and can be changed at run-time. If you are coming from a C/C++ background, this is the first thing you need to get your head around to understand Ruby. For example, constants in Ruby can be assigned conditionally:
class Block
if rand % 2 == 0
FIRST = '.'
else
FIRST = '$'
end
end
If the Ruby interpreter was required to "look forward" to see what the value of Block.FIRST should be, what should it predict in the above case?
This is a conceptual shift from what you are used to, and it will require you to structure your programs in a different way, and think about your programs in a different way. If you try to write C/C++/Java in Ruby, you will be fighting all the way.
In this case, I recommend you simply reverse the order of your definitions and go "bottom-up". There are other ways to achieve the same effect, but that is the simplest one.
you can try defined?(function_name)

What's the difference between these Ruby namespace conventions?

So Module can be used in Ruby to provide namespacing in addition to mixins, as so:
module SomeNamespace
class Animal
end
end
animal = SomeNamespace::Animal.new
But I've also seen the following used:
module SomeNamespace
end
class SomeNamespace::Animal
end
animal = SomeNamespace::Animal.new
My question is how they're different (if they are) and which is more idiomatic Ruby?
The difference lies in nesting.
In the example below, you can see that the former method using class Foo, can get the outer scope's constant variables BAR_A without errors.
Meanwhile, class Baz will bomb with an error of uninitialized constant A::B::Baz::BAR_A. As it doesn't bring in A::* implicitly, only A::B::*explicitly.
module A
BAR_A = 'Bar A!'
module B
BAR_B = 'Bar B!'
class Foo
p BAR_A
p BAR_B
end
end
end
class A::B::Baz
p BAR_A
p BAR_B
end
Both behaviors have their place. There's no real consensus in the community in my opinion as to which is the One True Ruby Way (tm). I personally use the former, most of the time.
The only difference between the two approaches is that the second one will throw uninitialized constant Object::SomeNamespace if the namespace hasn't previously been declared.
When declared in a single file, I would opt for the first one because you don't have to repeat SomeNamespace.
When using multiple files I also use the second one, to avoid running into the following problem:
# in a.rb
require 'b'
module SomeNamespace
def self.animal
Animal.new
end
end
# in b.rb
class SomeNamespace::Animal
end
# irb
require 'a' # explodes with the uninitialized constant error
This example may be contrived, but it's easy to trigger it if your code base is a little bit bigger. I usually use the explicit way (your first one) to avoid this.
One thing that may be helpful when using the second form is that it will detect typos in the namespace.
There doesn't seem to be an established way to create namespaces, Devise for example mixes both approaches: first one, second one.

Resources