Class: Object
- Inherits:
- BasicObject
- Defined in:
- lib/qualitysmith_extensions/object/mcall.rb,
lib/qualitysmith_extensions/object/default.rb,
lib/qualitysmith_extensions/object/if_else.rb,
lib/qualitysmith_extensions/object/methods.rb,
lib/qualitysmith_extensions/object/send_if.rb,
lib/qualitysmith_extensions/module/class_methods.rb,
lib/qualitysmith_extensions/object/ignore_access.rb,
lib/qualitysmith_extensions/object/singleton_send.rb,
lib/qualitysmith_extensions/object/send_if_not_nil.rb,
lib/qualitysmith_extensions/object/ancestry_of_method.rb
Overview
–
- Author
-
Tyler Rick
- Copyright
-
Copyright © 2007 QualitySmith, Inc.
- License
-
Ruby License
- Submit to Facets?
-
Maybe.
- Developer notes
-
++
Instance Method Summary collapse
-
#ancestry_of_method(method_name) ⇒ Object
Returns the module/class which defined the given method.
- #class_methods(include_super = true) ⇒ Object (also: #module_methods)
- #default!(default_value) ⇒ Object
-
#if(condition, else_value = nil) ⇒ Object
(also: #if_else)
Returns
self
ifcondition
; otherwise, returnselse_value
. -
#ignore_access ⇒ Object
(also: #access)
Sends all messages to receiver, bypassing access restrictions, allowing you to call private methods (like class_variable_get) without having to write ugly send() calls.
-
#mcall(moduule, message, *args, &block) ⇒ Object
(also: #msend)
Calls the method implementation from the module of your choice (
moduule
) on the object of your choice (self
). -
#methods_with_sorting_and_include_super(include_super = true) ⇒ Object
Ruby’s built-in Object#methods says: Returns a list of the names of methods publicly accessible in obj.
-
#send_if(condition, message, *args, &block_to_always_execute) ⇒ Object
Sends
message
toself
(includingblock_to_always_execute
if supplied) ifcondition
is met. - #send_if_not_nil(message, *args) ⇒ Object
-
#send_if_true(condition, *args, &block) ⇒ Object
Lets you reduce duplication a little bit.
-
#send_unless(condition, *args, &block) ⇒ Object
Opposite of send_if.
-
#singleton_send(moduule, message, *args, &block) ⇒ Object
(also: #ss)
Creates a singleton method and then calls it.
- #unless(condition, else_value) ⇒ Object (also: #unless_else)
Instance Method Details
#ancestry_of_method(method_name) ⇒ Object
Returns the module/class which defined the given method. If more than one module/class defined the method, returns the closest ancestor to have defined it (would be self
if it is defined in self
).
This is (as far as I know – patches welcome) the method that would would be called if you actually called the method. So if you override
It does this by first checking searching the methods defined in each ancestor in turn (in the order that self.ancestors
returns them) and returning the first module/class that satisfies the search.
This looks at the results of methods
, which means that if you call this on a module/class, it will not return any instance methods, only class methods.
class Base
def self.it; end
end
class SubWithIt < Base
def self.it; end
end
class SubWithoutIt < Base
end
SubWithIt.ancestry_of_instance_method(:it) # => SubWithIt # (Stops with self)
SubWithoutIt.ancestry_of_instance_method(:it) # => Base # (Goes one step up the ancestry tree)
If you call this on an object that is not a module or a class (in other words, if you call it on an instance of some class), then it will assume you actually want to know about an instance method defined in self.class or one of the ancestors of self.class. (Since non-modules don’t even technically have the concept of ancestors.) Therefore, this:
class Klass
def it; end
end
o = Klass.new
o.ancestry_of_method(:it) # => Klass
is really just a shorthand way of doing:
o.class.ancestry_of_instance_method(:it) # => Klass
If the method is a singleton method of self
, it will return self
:
class << (foo = SubWithIt.new)
def it; end
end
foo.ancestry_of_method(:it) # => #<SubWithIt:0xb7e5614c>
Returns nil if it cannot be found in self or in any ancestor.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/qualitysmith_extensions/object/ancestry_of_method.rb', line 60 def ancestry_of_method(method_name) method_name = method_name.to_s (self if self.methods(false).include?(method_name)) \ || if self.is_a?(Module) self.ancestors.find do |ancestor| ancestor.methods(false).include? method_name end or # The above search does not take into account *instance* methods provided by Class, Module, or Kernel. # Remember that ancestors and instances/class/superclass are different concepts, and that although classes/modules # do not have Class or Module as an "ancestor", they are still *instances* of Module or Class (which is a subclass of module). # self.ancestors does NOT include Class or Module, and yet we're still able to "inherit" instance methods from Class or Module. # So we have to do this extra search in case the method came from one of the instance methods of Class or Module or Kernel # (are there any other cases I'm missing?). begin # self.class.ancestors is usually [Class, Module, Object, PP::ObjectMixin, Kernel] self.class.ancestors.find do |ancestor| ancestor.instance_methods(false).include? method_name # || ancestor.private_instance_method_defined?( method_name.to_sym ) end end else self.class.ancestry_of_instance_method(method_name) end end |
#class_methods(include_super = true) ⇒ Object Also known as: module_methods
11 12 13 14 15 16 17 |
# File 'lib/qualitysmith_extensions/module/class_methods.rb', line 11 def class_methods(include_super = true) ( methods(include_super) \ - instance_methods ). sort end |
#default!(default_value) ⇒ Object
31 32 33 |
# File 'lib/qualitysmith_extensions/object/default.rb', line 31 def default!(default_value) # This should have no effect on any objects other than instances of NilClass. end |
#if(condition, else_value = nil) ⇒ Object Also known as: if_else
Returns self
if condition
; otherwise, returns else_value
.
Example:
"Average: #{array.average}". if_else array.size >= 3, ''
That is another way to say this:
array.size >= 3 ? "Average: #{array.average}" : '' )
Sometimes you want to do ‘something unless condition’ and you want that whole expression to return ” (or some other value) if the condition is false, rather than nil, which is what it currently returns.
Important: “self
” will always be “evaluated”. So if the receiver of this message is some dangerous call (has side effects), then you would be advised to use the normal if/then/else or ?/: constructs instead.
For example, method_with_adverse_side_effects
will be called unconditionally in this case (whether or not ready?
returns false
):
obj.method_with_adverse_side_effects. if_else ready?, NotReady
But it will not be called in this case if ready?
returns false
:
ready? ? obj.method_with_adverse_side_effects : NotReady)
“Isn’t this method useless?” … Yes, basically. Its main advantage is that it lets you put the condition after the normal value, which may make it easier to follow the normal execution flow when reading the source.
This is similar to something I saw in another language (Python?) where a similar syntax is built right into the language. Something like this:
normal_value if condition else else_value
I thought that was a neat idea so I implemented it as best I could in Ruby.
This might also be interesting for folks coming from Smalltalk, where if
really is just a message that you pass to an object along with a block (?) to be executed if condition
is true
and a block to be executed if condition
is false
.
42 43 44 45 46 |
# File 'lib/qualitysmith_extensions/object/if_else.rb', line 42 def if(condition, else_value = nil) condition ? self : else_value end |
#ignore_access ⇒ Object Also known as: access
Sends all messages to receiver, bypassing access restrictions, allowing you to call private methods (like class_variable_get) without having to write ugly send() calls.
o.class.ignore_access.class_variable_set(:@@v, 'new value')
is equivalent to:
o.class.send(:class_variable_set, :@@v, 'new value')
If you tried to just call the method directly, like this:
o.class.class_variable_set(:@@v, 'new value')
you would get a NoMethodError:
NoMethodError: private method `class_variable_set' called for Klass:Class
36 37 38 39 40 |
# File 'lib/qualitysmith_extensions/object/ignore_access.rb', line 36 def ignore_access @_ignore_access_functor ||= Functor.new do |op,*args| self.send(op,*args) end end |
#mcall(moduule, message, *args, &block) ⇒ Object Also known as: msend
Calls the method implementation from the module of your choice (moduule
) on the object of your choice (self
).
The only (huge) catch is that self
must either be an instance of moduule
or have moduule
as an ancestor… which severely limits its usefullness. (Compare with singleton_send.)
It is still useful, though, if you want to call some “original” implementation provided by Kernel (or some other base module) and the people that overrode it didn’t play nice and use alias_method_chain
.
No matter! If the class of the object you are calling this on has Kernel as an ancestor, then you can call any method from Kernel on this object!
This implementation is gratefully owed to the folks who wrote PP (/usr/lib/ruby/1.8/pp.rb)
19 20 21 |
# File 'lib/qualitysmith_extensions/object/mcall.rb', line 19 def mcall(moduule, , *args, &block) moduule.instance_method().bind(self).call(*args, &block) end |
#methods_with_sorting_and_include_super(include_super = true) ⇒ Object
Ruby’s built-in Object#methods says:
Returns a list of the names of methods publicly accessible in obj. This will include all the methods accessible in obj's ancestors.
But sometimes you don’t want all of obj’s ancestors!
This Object#methods
adds the following features to the built-in Object#methods
:
-
Provides the same
include_super
option that Module#instance_methods has (Backwards compatible, because default istrue
) -
Returns an array of symbols rather than strings (Not backwards compatible)
-
Sorts the array for you so you don’t have to! (Not backwards compatible)
24 25 26 27 28 29 30 |
# File 'lib/qualitysmith_extensions/object/methods.rb', line 24 def methods_with_sorting_and_include_super(include_super = true) if include_super methods_without_sorting_and_include_super else (methods_without_sorting_and_include_super - Object.methods_without_sorting_and_include_super) end.sort.map(&:to_sym) end |
#send_if(condition, message, *args, &block_to_always_execute) ⇒ Object
Sends message
to self
(including block_to_always_execute
if supplied) if condition
is met. If condition
is not met, block_to_always_execute
will still be called (if supplied), but we will not pass the message. (If condition
is not met and block_to_always_execute
is not supplied, it will simply return self
.)
In summary:
-
block
: always executed -
message
: only sent ifcondition
If a block (block_to_always_execute
) is supplied, it is passed on to the message if the condition is met; (otherwise it is simply called without sending the message).
This is useful if you want to wrap a block with some method but you only want the method itself to be used some of the time. For example, if it’s for benchmarking, you may only want to enable it during development but disable during production to save on some overhead.
Note: this cannot be used to call methods that expect blocks (Ruby 1.9 maybe?)
37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/qualitysmith_extensions/object/send_if.rb', line 37 def send_if(condition, , *args, &block_to_always_execute) if condition self.__send__(, *args, &block_to_always_execute) else if block_given? block_to_always_execute.call else self end end end |
#send_if_not_nil(message, *args) ⇒ Object
10 11 12 13 14 15 16 |
# File 'lib/qualitysmith_extensions/object/send_if_not_nil.rb', line 10 def send_if_not_nil(, *args) if send(, *args) else self end end |
#send_if_true(condition, *args, &block) ⇒ Object
Lets you reduce duplication a little bit. Can do this:
@foo.send_if_true(color)
instead of this:
@foo.send_if(color, color)
59 60 61 |
# File 'lib/qualitysmith_extensions/object/send_if.rb', line 59 def send_if_true(condition, *args, &block) self.send_if(condition, condition, *args, &block) end |
#send_unless(condition, *args, &block) ⇒ Object
Opposite of send_if
50 51 52 |
# File 'lib/qualitysmith_extensions/object/send_if.rb', line 50 def send_unless(condition, *args, &block) self.send_if(!condition, *args, &block) end |
#singleton_send(moduule, message, *args, &block) ⇒ Object Also known as: ss
Creates a singleton method and then calls it.
More specificaly, it extend
s self
with the methods from moduule
and then sends the supplied message
and args
(if any).
Examples:
"whatever".ss(MyColorizer, :colorize, :blue)
Advantages:
-
Keeps things object-oriented. Better than having global/class methods.
-
(
"whatever".ss(MyColorizer, :colorize).ss(SomeOtherClass, :another_class_method)
instead of-
SomeOtherClass::another_class_method(MyColorizer::colorize("whatever"))
)
-
-
Method calls are listed in the order in which they are called.
-
-
Still lets you keep your methods in a namespace.
-
Doesn’t clutter up the base classes with methods that are only useful within a very small context. The methods are only added to the objects you specify. So you can “use” the base class without cluttering up all instances of the class with your methods.
-
Useful for cases where creating a subclass wouldn’t help because the methods you are calling would still return instances of the base class.
Disadvantages:
-
You have to have/create a module for the functions you want to use.
-
Can’t just call .sigleton_send(self, :some_method) if you want to use
some_method
that’s defined inself
. -
So what do we call the module containing the “singleton method”? String::MyColorizer? MyColorizer::String? MyStringColorizer?
-
Adding methods to the base class using Facets’ own namespacing facilities (Module#namespace and Module#include_as) might actually be a more sensible alternative a lot of the time than bothering to create singleton methods for single objects! That would look somethig like:
class String
namespace :my_colorizer do
def colorize(...); ...; end
end
end
"whatever".my_colorizer.colorize(:blue)
or
class String
include_as :my_colorizer => MyColorizer
end
"whatever".my_colorizer.colorize(:blue)
61 62 63 64 |
# File 'lib/qualitysmith_extensions/object/singleton_send.rb', line 61 def singleton_send(moduule, , *args, &block) self.extend(moduule) self.send(, *args, &block) end |
#unless(condition, else_value) ⇒ Object Also known as: unless_else
49 50 51 52 53 |
# File 'lib/qualitysmith_extensions/object/if_else.rb', line 49 def unless(condition, else_value) !condition ? self : else_value end |