When you reference a constant that doesn’t exist, Ruby passes the name of
the constant to const_missing as a symbol.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Class names are just constants, so# a reference to an unknown Rake class such as # Task was routed to Module#const_missing.## gems/rake-0.9.2.2/lib/rake/ext/module.rbclassModuledefconst_missing(const_name)caseconst_namewhen:TaskRake.application.const_warning(const_name)Rake::Taskwhen:FileTaskRake.application.const_warning(const_name)Rake::FileTaskwhen:FileCreationTask# ...endendend
Class
classes themselves are nothing but objects.
1
2
"hello".class# => StringString.class# => Class
the methods of a class are the instance methods of Class.
1
2
# The "false" argument here means: ignore inherited methodsClass.instance_methods(false)# => [:allocate, :new, :superclass]
class names are nothing but constants
Any reference that begins with an uppercase letter, including the names of
classes and modules, is a constant.
if that module is already in the chain, Ruby silently ignores the second inclusion.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
moduleM1;endmoduleM2includeM1endmoduleM3prependM1includeM2end# M3 prepends M1 and then includes M2. # When M2 also includes M1, that include has no effect,# because M1 is already in the chain of ancestors. M3.ancestors# => [M1, M3, M2]
method
When you call a method, Ruby does two things:
It finds the method. This is a process called method lookup.
method lookup : to find a method, Ruby goes in the receiver’s class, and from there it climbs the ancestors chain until it finds the method.
It executes the method. To do that, Ruby needs something called self.
send , Dynamically calling methods
with send , the name of the method that you want to call becomes just a regular
argument. You can wait literally until the very last moment to decide which
method to call, while the code is running.
classLawyer;endnick=Lawyer.newnick.talk_simple➤NoMethodError:undefinedmethod`talk_simple' for #<Lawyer:0x007f801aa81938>
1
2
nick.send:method_missing,:my_method➤NoMethodError:undefinedmethod`my_method' for #<Lawyer:0x007f801b0f4978>
Overriding method_missing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
classLawyerdefmethod_missing(method,*args)puts"You called: #{method}(#{args.join(', ')})"puts"(You also passed it a block)"ifblock_given?endendbob=Lawyer.newbob.talk_simple('a','b')do# a blockend➤Youcalled: talk_simple(a,b)(Youalsopasseditablock)
Ghost Methods ,幽灵方法
From the caller’s side, a message that’s processed by method_missing looks like
a regular call—but on the receiver’s side, it has no corresponding method.
This trick is called a Ghost Method.
require"ghee"gh=Ghee.basic_auth("usr","pwd")# Your GitHub username and passwordall_gists=gh.users("nusco").gistsa_gist=all_gists[20]# url, description 都是 Ghost Methodsa_gist.url# => "https://api.github.com/gists/535077"a_gist.description# => "Spell: Dynamic Proxy"a_gist.star
Refactoring the Computer Class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
classComputerdefinitialize(computer_id,data_source)@id=computer_id@data_source=data_sourceend➤defmethod_missing(name)➤# If it doesn’t have one, the call falls back to BasicObject#method_missing➤superif!@data_source.respond_to?("get_#{name}_info")➤info=@data_source.send("get_#{name}_info",@id)➤price=@data_source.send("get_#{name}_price",@id)➤result="#{name.capitalize}: #{info} ($#{price})"➤return"* #{result}"ifprice>=100➤result➤endend
classComputer# ...➤defrespond_to_missing?(method,include_private=false)➤# In this case, super is the default Object#respond_to_missing?➤# which always returns false.➤@data_source.respond_to?("get_#{method}_info")||super➤endend
you might want to remove most methods from the class,
preventing such name clashes from ever happening again. A skinny class
Spell: Blank Slate with a minimal number of methods is called a Blank Slate.
Inheriting from BasicObject is the quicker way to define a Blank Slate in Ruby.
Removing Methods , Module#undef_method or Module#remove_method
You can remove a method from a class by using either Module#undef_method or Module#remove_method.
undef_method removes any method, including the inherited ones.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# keeps instance_eval and all the “reserved methods”,# -- methods that are used internally by Ruby# gems/builder-3.2.2/lib/blankslate.rbclassBlankSlate# Hide the method named +name+ in the BlankSlate class. Don't# hide +instance_eval+ or any method beginning with "__".defself.hide(name)# ...ifinstance_methods.include?(name._blankslate_as_name)&&name!~/^(__|instance_eval$)/undef_methodnameendend# ...instance_methods.each{|m|hide(m)}end