how to declare functions in module in ruby - ruby

In the below code, what is the difference between declaring two methods differently. Second method is declared using Week but first method is declared without using Week. And we are also not able to access second method by the class object d1. It gives the error
undefined method `weeks_in_year' for #<Decade:0x2c08a28> (NoMethodError)
then what is the use of declaring methods using Week prefix in second method when it is of no use.
module Week
def weeks_in_month
puts "You have four weeks in a month"
end
def Week.weeks_in_year
puts "You have 52 weeks in a year"
end
end
class Decade
include Week
end
d1=Decade.new
d1.weeks_in_month
d1.weeks_in_year

The way you have defined the method weeks_in_year is a class method of the Week class, not an instance method. That's why it didn't get inherited and you got the error as you posted.
You can use module_function to use the same method as a class method or instance method.
module Week
def weeks_in_month
puts "You have four weeks in a month"
end
def weeks_in_year
puts "You have 52 weeks in a year"
end
module_function :weeks_in_year
end
class Decade
include Week
def wrapper_of_weeks_in_year
weeks_in_year
end
end
d1 = Decade.new
d1.weeks_in_month
# You have four weeks in a month
d1.wrapper_of_weeks_in_year
# You have 52 weeks in a year
Week.weeks_in_year
# You have 52 weeks in a year
While you will be using module_function, The instance-method versions are made private. That's why you need to use a wrapper method to call it as direct invocation is not possible.

Related

Refinements and namespaces

Trying to patch net/http and have it only apply to one service class. Refinements seem to be the way to go. The monkey patch below works but the refinement doesn't. Is this a namespace issue? The project is on ruby 2.3.0 but have tried with 2.4.1 as well and only the monkey patch seems to get applied.
With a monkey patch:
module Net
class HTTPGenericRequest
def write_header(sock, ver, path)
puts "monkey patched!"
# patch stuff...
end
end
end
Service.new.make_request
# monkey patched!
With a refinement:
module NetHttpPatch
refine Net::HTTPGenericRequest do
def write_header(sock, ver, path)
puts "refined!"
# patch stuff...
end
end
end
class Service
using NetHttpPatch
end
Service.new.make_request
# :(
UPDATE:
This seems to be similar scope wise? Obviously more complex things are happening when net/http makes a request does it lose scope then?
module TimeExtension
refine Fixnum do
def hours
self * 60
end
end
end
class Service
using TimeExtension
def one_hour
puts 1.hours
end
end
puts Service.new.one_hour
# 60
UPDATE UPDATE:
nvm, I see what's happening now :) have to keep your brain from mixing using up with how mixins work.
module TimeExtension
refine Fixnum do
def hours
self * 60
end
end
end
class Foo
def one_hour
puts 1.hours
end
end
class Service
using TimeExtension
def one_hour
puts 1.hours
end
def another_hour
Foo.new.one_hour
end
end
puts Service.new.one_hour
# 60
puts Service.new.another_hour
# undefined method `hours' for 1:Fixnum (NoMethodError)
Is this a namespace issue?
It is a scope issue. Refinements are lexically scoped:
class Service
using NetHttpPatch
# Refinement is in scope here
end
# different lexical scope, Refinement is not in scope here
class Service
# another different lexical scope, Refinement is *not* in scope here!
end
Originally, there was only main::using, which was script-scoped, i.e. the Refinement was in scope for the entire remainder of the script. Module#using came later, it scopes the Refinement to the lexical class definition body.

Modules and Accessing Variables from Modules (Ruby Language)

I have the following Module which has 1 variable which contains a string for the first day of a hypothetical year, 1 method which outputs a string and another method which also outputs a string:
module Week
first_day = "Sunday"
def weeks_in_month
puts "There are 4 weeks in a month"
end
def weeks_in_year
puts "There are 52 weeks in a year"
end
end
I now have a class who's only purpose is to print out the variable located in the module.(this is just for testing purposes)
class Decade
include Week
def firstday
puts Week::first_day
end
end
I now instantiate Decade and access the methods located in the module using Decades object. My program runs into a problem when calling the firstday method
z = Decade.new
z.weeks_in_month
z.weeks_in_year
z.firstday #Errors here
The error I get is:
undefined method `first_day' for Week:Module (NoMethodError)
I am new to Ruby and am just getting used to Modules, so any help would be appreciated.
When writing a module the convention is to declare constants like this:
module Week
FIRST_DAY = 'Sunday'
end
Note that they're in ALL_CAPS. Anything that begins with a capital letter is treated as a constant. Lower-case names of that sort are treated as local variables.
Generally it's bad form to access the constants of another module, it limits your ability to refactor how those are stored. Instead define a public accessor method:
module Week
def first_day
FIRST_DAY
end
end
Now you can call that externally:
Week.first_day
Note you can also change how that's implemented:
module Week
DAYS = %w[
Sunday
Monday
Tuesday
...
Saturday
]
def first_day
DAYS.first
end
extend self # Makes methods callable like Week.first_day
end
The nice thing about that is the first_day method does exactly the same thing, no other code has to change. This makes refactoring significantly easier. Imagine if you had to track down and replace all those instances to Week::FIRST_DAY.
There's some other things to note here. The first is that any time you call include on a module then you get the methods and constants loaded in locally. The second thing is when you define a mix-in module, be careful with your names to avoid potential conflict with the target class.
Since you've mixed it in, you don't need the namespace prefix, just calling first_day should do it.

Ruby undefined method 'each' for class

I am getting an error when I create a class of Stats and another a container class. The error is
test.rb:43:in `<main>' undefined method `each' for #<Boyfriends:0x2803db8 #boyfriends=[, , , ]> (NoMethodError)
which makes absolute sense because that class indeed does not contain that method, but should ruby search the parents and grandparents classes for the method? The script displays the desired output; it just inlays the error with the output like so
test.rb:43:in `<main>'I love Rikuo because he is 8 years old and has a 13 inch nose
I love dolar because he is 12 years old and has a 18 inch nose
I love ghot because he is 53 years old and has a 0 inch nose
I love GRATS because he is unknown years old and has a 9999 inch nose
: undefined method `each' for #<Boyfriends:0x2803db8 #boyfriends=[, , , ]> (NoMethodError)
Here is the code
class Boyfriends
def initialize
#boyfriends = Array.new
end
def append(aBoyfriend)
#boyfriends.push(aBoyfriend)
self
end
def deleteFirst
#boyfriends.shift
end
def deleteLast
#boyfriends.pop
end
def [](key)
return #boyfriends[key] if key.kind_of?(Integer)
return #boyfriends.find { |aBoyfriend| aBoyfriend.name }
end
end
class BoyfriendStats
def initialize(name, age, nose_size)
#name = name
#age = age
#nose_size = nose_size
end
def to_s
puts "I love #{#name} because he is #{#age} years old and has a #{#nose_size} inch nose"
end
attr_reader :name, :age, :nose_size
attr_writer :name, :age, :nose_size
end
list = Boyfriends.new
list.append(BoyfriendStats.new("Rikuo", 8, 13)).append(BoyfriendStats.new("dolar", 12, 18)).append(BoyfriendStats.new("ghot", 53, 0)).append(BoyfriendStats.new("GRATS", "unknown", 9999))
list.each { |boyfriend| boyfriend.to_s }
Which makes absolute sense because that class indeed does not contain that method, but as I've been reading should ruby search the classes parents and grandparents for the method?
That's correct, but you didn't declare any superclasses so the superclass will be Object. Which also doesn't have an each method.
If you want an enumerable method, you'll have to define it yourself - you'll probably want to iterate over the array.
In that case, you could just define an own each method that just passes the passed block down to the arrays each method:
class Boyfriends
def each(&block)
#boyfriends.each(&block)
end
end
The &block here let's you capture a passed block by name. If you're new to ruby, this probably doesn't mean much to you, and explaining how it works is somewhat beyond the scope of this question. The accepted answer in this Question does a pretty good job of explaining how blocks and yield work.
once you got an each method, you can also pull in Enumerable for a number of convenience methods:
class Boyfriends
include Enumerable
end
Also, to_s is a method that should return a string, so you should remove the puts in BoyfriendStats#to_s.

Constants in classes created with Class.new

I'm writing a little piece of code in Ruby (1.9.3), and I use a pair of simple "enum-like" classes, that define some constants with const_set and some behavior of these constants (e.g. the class Days may have the constants MON, TUE... and Days::MON.succ should evaluate to TUE).
I'm really comfortable with these classes. However, while growing my code, i sometimes need to add more of them, and I don't like the idea of having five or more classes that share 99% of source code, e.g.:
class Days
NAMES = %w( MON TUE ... )
INSTANCES = []
def initialize(num)
#num = num
end
# An example operation
def +(n)
INSTANCES[(#num + n) % INSTANCES.length]
end
# Another example operation
def succ
self + 1
end
def to_s
NAMES[#num]
end
NAMES.each_with_index do |name, idx|
instance = new(idx)
INSTANCES[idx] = instance
const_set name, instance
end
end
class Months
NAMES = %w( JAN FEB ... )
...
end
I was wondering if Ruby's metaprogramming capabilities could be used to generate these classes. However, I'm having an hard time creating NAMES, INSTANCES and the "enum-named" constants (e.g. MON, TUE, ...).
Being const_set a class method of Class, in this code it's context (the value of self) is respectively Days and Months.
When creating a factory method, I'm compelled to do something like this:
def enum_new(names_array)
Class.new do
const_set "NAMES" []
names_array.each_with_index do |name, idx|
NAMES[idx] = name
end
...
end
end
Days = enum_new(%w| MON TUE ... |)
Months = enum_new(%w| JAN FEB ... |)
but this won't work (at least, not like i hoped it to), because const_set won't be called in the context of the class whose name is magically set (i.e. Days and Months), but apparently in the context of Class; therefore, not only it won't be accessible from the instance methods, but it will be overwrited every time enum_new is called with a new array of names as argument. A similar problem shows up when using class variables, because they'll be shared between any class generated with the method (because they'll become class variables of Class, i guess).
Is there any way to create constants in a class generated with Class.new, obtaining this way classes identical in everything to the original Days and Months classes, without having to pollute my code with almost identical classes?
Thanks for your attention and patience! :)
Yes. Do your initialization in a class_exec block, into which you can pass your name data and where self refers to the right class:
theClass=Class.new
theClass.class_exec(names) do |names|
#initialize constants here...
end

Count of methods in Ruby just drop during object creation

Why does the total count of methods reduce, from 81 to 46 while instantiating an object from 'Class' class-objects?
Here's the code I'm running:
class Automobile
def wheels(wheel)
puts "#{wheel}"
end
end
class Car < Automobile
def gears
puts "Automatic Transmission"
end
end
limo = Car.new
benz = Automobile.new
puts Automobile.methods.count
puts Car.methods.count
puts benz.methods.count
puts limo.methods.count
I guess subclass is not inheriting certain methods, I thought they are class methods, so I did some tests and realized methods displayed by "puts Anyclass.methods" are not class methods. They must be instance methods.
How is this achieved in Ruby, to deter a subclass from inheriting certain methods?
Your entire question seems to be based on the incorrect belief that the result of Car.methods is not the class methods of the Car class, but its instance methods. The result of Car.methods is the list of methods of the Car class itself. To get the instance methods, you would have to write Car.instance_methods. That's why you see that the instances have fewer methods than the classes.
For me, here are the results of running your code:
puts Automobile.methods.count
#=> 95
puts Car.methods.count
#=> 95 (exactly as you'd expect, since it didn't define any new class methods)
puts benz.methods.count
#=> 57 (this is 1 more than the result of Object.instance_methods.count, since you added #wheels)
puts limo.methods.count
#=> 58 (1 more than benz.methods.count, since you added #gears)

Resources