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.
Related
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.
Here is my code:
module Star
def Star.line
puts '*' * 20
end
end
module Dollar
def Star.line
puts '$' * 20
end
end
module At
def line
puts '#' * 20
end
end
include At
Dollar::line # => "####################"
Star::line # => "$$$$$$$$$$$$$$$$$$$$"
Dollar::line # => "####################"
line # => "####################"
Can anyone explain how I get this result? I do not understand the method lookup flow here.
This is how I see it:
Dollar::line
There is no such method defined in this module so It's calling At::line because you included this module.
Star::line
It uses last defining from Dollar module(it goes after original Star definition so it's overridden).
Dollar::line
Third call is the same as the first one.
line
And the last one is At::line because You made an include.
Is
module Dollar
def Star.line
intentional or is a typo?
Looks like Dollar.line is not defined, and the method line in At is used instead.
First you need to understand that Ruby looks up constants somewhat similarly to methods. It starts by looking for the constant in the current lexical scope. If it doesn't find the constant there, it goes up one level and looks there, and so on. If it can't find the constant anywhere else, it eventually searches the top level, which is why you can access modules like Kernel from anywhere in your code.
module Star
end
Star.object_id # 20
module Dollar
Star.object_id # 20. No Star in current scope, so gets the top-level star
end
module At
module Star
end
Star.object_id # 10. There is now a Star in this scope, so we don't get the top-level one
end
The next thing to understand is that methods defined at the top level in Ruby are made instance methods of Object. Since everything in Ruby is an instance of Object, such methods can always be called.
Finally, consider what include does: it takes instance methods from a module and makes them instance methods in the current scope. So if you include something at the top level, all of those methods get added to Object!
So your code is essentially equivalent to this:
module Star
def self.line
puts '*' * 20
end
# this overwrites the previous definition
def self.line
puts '$' * 20
end
end
# because of the way constants are looked up, the def ends up in Star
module Dollar
end
module At
def line
puts '#' * 20
end
end
# the include does this, so now every object (including Dollar) can call line
def line
puts '#' * 20
end
# except Star already has its own line method, so the one from Object won't be called for it
Star.line # "$$$$$$$$$$$$$$$$$$$$"
Are there any plans to implement ruby behavior similar to the CoffeeScript feature of specifying an instance variable name in a method argument list?
Like
class User
def initialize(#name, age)
# #name is set implicitly, but #age isn't.
# the local variable "age" will be set, just like it currently works.
end
end
I'm aware of this question: in Ruby can I automatically populate instance variables somehow in the initialize method? , but all the solutions (including my own) don't seem to fit the ruby simplicity philosophy.
And, would there be any downsides for having this behavior?
UPDATE
One of the reasons for this is the DRY (don't repeat yourself) philosophy of the ruby community. I often find myself needing to repeat the name of an argument variable because I want it to be assigned to the instance variable of the same name.
def initialize(name)
# not DRY
#name = name
end
One downside I can think of is that it may look as though a method is doing nothing if it has no body. If you're scanning quickly, this may look like a no-op. But I think given time, we can adapt.
Another downside: if you're setting other instance variables in the body, and you try to be readable by putting all the assignments at the beginning, it can take more cognitive "power" to see that there assignments also happening in the argument list. But I don't think this is any harder than, say, seeing a constant or method call and having to jump to its definition.
# notice: instance var assignments are happening in 2 places!
def initialize(#name)
#errors = []
end
After some pondering, I wondered if it's possible to actually get the argument names from a ruby method. If so, I could use a special argument prefix like "iv_" to indicate which args should be set as instance variables.
And it is possible: How to get argument names using reflection.
Yes! So I can maybe write a module to handle this for me. Then I got stuck because if I call the module's helper method, it doesn't know the values of the arguments because they're local to the caller. Ah, but ruby has Binding objects.
Here's the module (ruby 1.9 only):
module InstanceVarsFromArgsSlurper
# arg_prefix must be a valid local variable name, and I strongly suggest
# ending it with an underscore for readability of the slurped args.
def self.enable_for(mod, arg_prefix)
raise ArgumentError, "invalid prefix name" if arg_prefix =~ /[^a-z0-9_]/i
mod.send(:include, self)
mod.instance_variable_set(:#instance_vars_from_args_slurper_prefix, arg_prefix.to_s)
end
def slurp_args(binding)
defined_prefix = self.class.instance_variable_get(:#instance_vars_from_args_slurper_prefix)
method_name = caller[0][/`.*?'/][1..-2]
param_names = method(method_name).parameters.map{|p| p.last.to_s }
param_names.each do |pname|
# starts with and longer than prefix
if pname.start_with?(defined_prefix) and (pname <=> defined_prefix) == 1
ivar_name = pname[defined_prefix.size .. -1]
eval "##{ivar_name} = #{pname}", binding
end
end
nil
end
end
And here's the usage:
class User
InstanceVarsFromArgsSlurper.enable_for(self, 'iv_')
def initialize(iv_name, age)
slurp_args(binding) # this line does all the heavy lifting
p [:iv_name, iv_name]
p [:age, age]
p [:#name, #name]
p [:#age, #age]
end
end
user = User.new("Methuselah", 969)
p user
Output:
[:iv_name, "Methuselah"]
[:age, 969]
[:#name, "Methuselah"]
[:#age, nil]
#<User:0x00000101089448 #name="Methuselah">
It doesn't let you have an empty method body, but it is DRY. I'm sure it can be enhanced further by merely specifying which methods should have this behavior (implemented via alias_method), rather than calling slurp_args in each method - the specification would have to be after all the methods are defined though.
Note that the module and helper method name could probably be improved. I just used the first thing that came to mind.
Well, actually...
class User
define_method(:initialize) { |#name| }
end
User.new(:name).instance_variable_get :#name
# => :name
Works in 1.8.7, but not in 1.9.3. Now, just where did I learn about this...
I think you answered your own question, it does not fit the ruby simplicity philosophy. It would add additional complexity for how parameters are handled in methods and moves the logic for managing variables up into the method parameters. I can see the argument that it makes the code less readable a toss up, but it does strike me as not very verbose.
Some scenarios the # param would have to contend with:
def initialize( first, last, #scope, #opts = {} )
def search( #query, condition )
def ratchet( #*arg )
Should all of these scenarios be valid? Just the initialize? The #*arg seems particularly dicey in my mind. All these rules and exclusions make the Ruby language more complicated. For the benefit of auto instance variables, I do not think it would be worth it.
Is there a practical application to the "crazy-ness" below?
It seems like this is a way for ted to always be able to return himself to the world and people will think they are talking to ted who they expect to act a certain way and be a certain age... but he isn't acting the way he portrays himself and is lying about his age to someone.
What 'trickery' is possible when an object is returned and you check on what that object represents and is capable of... but really that object was acting another way and capable of other things before returning.
class Person
def age
21
end
def who_am_i?
puts "I am #{self} / #{object_id} and I am #{age} years old"
self
end
end
ted = Person.new
def ted.singleton_who_am_i?
class << self
def age
0
end
end
puts "I am #{self} / #{object_id} and I am #{age} years old"
self
end
puts ted.who_am_i? == ted.singleton_who_am_i?
>> I am #<Person:0x100138340> / 2148123040 and I am 21 years old
>> I am #<Person:0x100138340> / 2148123040 and I am 0 years old
>> true
http://andrzejonsoftware.blogspot.ca/2011/02/dci-and-rails.html
in DCI, your data model gets different type of behavior based on the context it is used it. Usually it is done with object.extend, but it is pretty much what you are doing above -- taking advantage of the metaclass.
Another example (and probably why things work that way) is the way classes work in ruby. If you say
class Foo
end
that is the same thing as saying
Foo = Class.new
end
meaning that what you are doing is assigning a new instance of class Class to a constant. When you define a method on that class, you don't want it applied to all instance of class Class, you only want it on the class you are defining. So when you say
class Foo
def self.bar
end
end
it is the exact thing as saying
class Foo
end
def Foo.bar
end
which is exactly the same principal as you are talking about in your question
(sorry if that was unclear)
Ruby is a very dynamic language letting you inject code into objects at runtime. There are some good uses for it but it can also make code very hard to debug and understand.
It's totally counter-intuitive for a method that queries an object to modify that object. Nobody would expect a call to who_am_i to modify the object.
On the other hand replacing methods like that can make unit testing classes really straight forward.
If you want to test how the class behaves with different ages you can inject code like that before your tests.
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