Here is my class:
class Book
def read
# something
end
end
And another one:
class Magazine
def read
# something
end
end
Now, I want to decorate both read methods in these two classes. I want them both to print "hello" right before they start working. How do I do that? I suspect, I need to use mixins, but can't figure out exactly how.
You can prepend module:
module Decorator
def read
puts "hello"
super
end
end
class Book
prepend Decorator
def read
puts "bye"
end
end
class Magazine
prepend Decorator
def read
puts "cya"
end
end
Book.new.read
# "hello"
# "bye"
Magazine.new.read
# "hello"
# "cya"
As well there is more clean way to do this kind of modifications with refinements:
class Book
def read
puts "bye"
end
end
class Magazine
def read
puts "cya"
end
end
module Decorator
def read
puts "hello"
super
end
end
module DecoratedBook
refine Book do
prepend Decorator
end
end
module DecoratedMagazine
refine Magazine do
prepend Decorator
end
end
using DecoratedBook
using DecoratedMagazine
Book.new.read
# "hello"
# "bye"
Magazine.new.read
# "hello"
# "cya"
Notice: this code may not run in irb.
Also you can do it right in the runtime:
module Decorator
def read
puts "hello"
super
end
end
[Book, Magazine].each do |klass|
klass.prepend Decorator
end
Book.new.read
# "hello"
# "bye"
Magazine.new.read
# "hello"
# "cya"
May be you want something as:
class ReaderDecorator
def initialize(obj)
#obj = obj
end
def read
puts 'Hello'
#obj.read
end
end
ReaderDecorator.new(Book.new).read
ReaderDecorator.new(Magazine.new).read
Related
I am confused on how and when method_added(method) gets called.
If I have
class Car
def method_added(method)
puts 'method added called'
super
end
def one
puts 'one method'
end
def two
puts 'two method'
end
end
Car.new.one
Why does this not work?
This is almost straight from the docs. Note this is plain Ruby, it is not a Rails thing.
class Car
def self.method_added(method_name)
puts "Adding #{method_name}"
end
def one
end
end
# => Adding one
Can anybody explain def self.extended(base), what does it mean here or any idea?
module Paperclip
module Storage
module Dropbox
def self.extended(base)
base.instance_eval do
#options[:dropbox_options] ||= {}
#options[:path] = nil if #options[:path] ==
self.class.default_options[:path]
#options[:dropbox_visibility] ||= "public"
#path_generator = PathGenerator.new(self, #options)
#dropbox_client # Force creation of dropbox_client
end
end
end
end
end
The self.extended method is called when the module is extended. It allows methods to be executed in the context of the base (where the module is extended).
You can try it yourself and understand this with a simple example code. Just paste in a file ruby file and run it.
Example for self.extended
module A
def self.extended(base)
puts "#{self} extended in #{base}"
end
end
class Apple
extend A
end
# This should print: "A extended in Apple"
Example for self.included
module A
def self.included(base)
puts "#{self} included in #{base}"
end
end
class Apple
include A
end
# This should print: "A included in Apple"
You can read more here: http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/
Say, I have the following Ruby:
module Nameable
def name
"John"
end
end
class User
include Nameable
def email
"john#example.com"
end
end
Is there a way to display, print or review the "expanded source code" of the entire "User"? I'm not sure what the term would be, but with "expanded source code" I mean the code, in Ruby (so not an AST), that has the included parts included:
class User
def name
"John"
end
def email
"john#example.com"
end
end
Bonuspoints when a solution can also display the code for inherited behaviour.
You can use method_source gem.
require 'method_source'
def flatten_source_code(klass)
excluded_parents = [Kernel, Object, BasicObject]
type = klass.instance_of?(Module) ? "module" : "class"
puts "#{type} #{klass}"
(klass.ancestors-excluded_parents).reverse.each do |ancestor|
ancestor.instance_methods(false).each do |method_name|
method = ancestor.instance_method(method_name)
script, line = method.source_location
if script then
puts
puts " # #{script} (line #{line})"
puts method.source
else
puts
puts " # def #{method_name}"
puts " # end"
end
end
end
puts "end"
end
module Nameable
def name
"John"
end
end
class User
include Nameable
def email
"john#example.com"
end
end
flatten_source_code(User)
puts
flatten_source_code(Nameable)
#=>
# class User
#
# # flatten_source_code.rb (line 27)
# def name
# "John"
# end
#
# # flatten_source_code.rb (line 34)
# def email
# "john#example.com"
# end
# end
# module Nameable
#
# # flatten_source_code.rb (line 27)
# def name
# "John"
# end
# end
It will not work perfectly for more complex cases, but it does the job for your example.
Just for fun, with :
module Nameable
def name
"John"
end
def email
"included john#example.com"
end
end
module AnotherModule
def email
"prepended john#example.com"
end
end
class User
include Nameable
prepend AnotherModule
def email
"john#example.com"
end
end
flatten_source_code(User)
puts "u = User.new"
puts "puts u.name"
puts "puts u.email"
Launching ruby flatten_source_code.rb | ruby returns :
John
prepended john#example.com
So the displayed Ruby code is valid and respects inheritance order.
It will define the same method multiple times, though.
You could keep an Hash of |method_name,method_source|. If a method overrides an old one and calls super, you could define the old method as as a private old_method_name_from_Module_blabla and replace super accordingly.
In the code below, I would like to call the class method done of the class that includes the module from inside self.hello
Explanation:
A::bonjour will call Mod::hello so will B::ciao
I would like to be able to detect the "calling class" (A or B) in Mod::hello in order to be able to call the A::done or B::done
module Mod
def self.hello
puts "saying hello..."
end
end
class A
include Mod
def self.bonjour
Mod::hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
Mod::hello
end
def self.done
puts "finitto"
end
end
While (perhaps) not as clean as Niklas' answer, it's still easily doable, and IMO cleaner than the usage pattern shown in the OP which relies on knowing which module is mixed in.
(I prefer not having to pass an argument to mixin methods like this when other means exist.)
The output:
pry(main)> A::bonjour
saying hello...
fini
pry(main)> B::ciao
saying hello...
finitto
The guts:
module Mod
module ClassMethods
def hello
puts "saying hello..."
done
end
end
def self.included(clazz)
clazz.extend ClassMethods
end
end
The modified class declarations, removing the explicit module reference:
class A
include Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
hello
end
def self.done
puts "finitto"
end
end
You may also supply a default implementation of done:
module Mod
module ModMethods
def hello
puts "saying hello..."
done
end
def done
throw "Missing implementation of 'done'"
end
end
def self.included(clazz)
clazz.extend ModMethods
end
end
As a comment to this post points out, if the snippet in the OP is a faithful representation of the actual usecase, you might as well use extend (instead of include), leaving everything cleaner:
module Mod
def hello
puts "saying hello..."
done
end
def done
raise NotImplementError("Missing implementation of 'done'")
end
end
And the classes using extend:
class A
extend Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end
I'm writing a module in Ruby 1.9.2 that defines several methods. When any of these methods is called, I want each of them to execute a certain statement first.
module MyModule
def go_forth
a re-used statement
# code particular to this method follows ...
end
def and_multiply
a re-used statement
# then something completely different ...
end
end
But I want to avoid putting that a re-used statement code explicitly in every single method. Is there a way to do so?
(If it matters, a re-used statement will have each method, when called, print its own name. It will do so via some variant of puts __method__.)
Like this:
module M
def self.before(*names)
names.each do |name|
m = instance_method(name)
define_method(name) do |*args, &block|
yield
m.bind(self).(*args, &block)
end
end
end
end
module M
def hello
puts "yo"
end
def bye
puts "bum"
end
before(*instance_methods) { puts "start" }
end
class C
include M
end
C.new.bye #=> "start" "bum"
C.new.hello #=> "start" "yo"
This is exactly what aspector is created for.
With aspector you don't need to write the boilerplate metaprogramming code. You can even go one step further to extract the common logic into a separate aspect class and test it independently.
require 'aspector'
module MyModule
aspector do
before :go_forth, :add_multiply do
...
end
end
def go_forth
# code particular to this method follows ...
end
def and_multiply
# then something completely different ...
end
end
You can implement it with method_missing through proxy Module, like this:
module MyModule
module MyRealModule
def self.go_forth
puts "it works!"
# code particular to this method follows ...
end
def self.and_multiply
puts "it works!"
# then something completely different ...
end
end
def self.method_missing(m, *args, &block)
reused_statement
if MyModule::MyRealModule.methods.include?( m.to_s )
MyModule::MyRealModule.send(m)
else
super
end
end
def self.reused_statement
puts "reused statement"
end
end
MyModule.go_forth
#=> it works!
MyModule.stop_forth
#=> NoMethodError...
You can do this by metaprogramming technique, here's an example:
module YourModule
def included(mod)
def mod.method_added(name)
return if #added
#added = true
original_method = "original #{name}"
alias_method original_method, name
define_method(name) do |*args|
reused_statement
result = send original_method, *args
puts "The method #{name} called!"
result
end
#added = false
end
end
def reused_statement
end
end
module MyModule
include YourModule
def go_forth
end
def and_multiply
end
end
works only in ruby 1.9 and higher
UPDATE: and also can't use block, i.e. no yield in instance methods
I dunno, why I was downvoted - but a proper AOP framework is better than meta-programming hackery. And thats what OP was trying to achieve.
http://debasishg.blogspot.com/2006/06/does-ruby-need-aop.html
Another Solution could be:
module Aop
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def before_filter(method_name, options = {})
aop_methods = Array(options[:only]).compact
return if aop_methods.empty?
aop_methods.each do |m|
alias_method "#{m}_old", m
class_eval <<-RUBY,__FILE__,__LINE__ + 1
def #{m}
#{method_name}
#{m}_old
end
RUBY
end
end
end
end
module Bar
def hello
puts "Running hello world"
end
end
class Foo
include Bar
def find_hello
puts "Running find hello"
end
include Aop
before_filter :find_hello, :only => :hello
end
a = Foo.new()
a.hello()
It is possible with meta-programming.
Another alternative is Aquarium. Aquarium is a framework that implements Aspect-Oriented Programming (AOP) for Ruby. AOP allow you to implement functionality across normal object and method boundaries. Your use case, applying a pre-action on every method, is a basic task of AOP.