10. Základné kamene
metaprogramovania
● premakaný objektový model
○ dynamic method lookup
○ moduly (include, extend)
● open classes
● self a kontexty
● všetko je vykonateľný kód
● callbacky
● pohadzovateľný kód (bloky)
11.
12.
13. Moduly
● Namespace
● Kontajner pre metódy
class Class < Module
14. module Greeter
def greet
puts "Hallo Welt"
end
end
class Application
include Greeter
end
Application.new.greet
#=> "Hallo Welt"
Application.greet
#=> Undefined method "greet"
15. module Greeter
def greet
puts "Hallo Welt"
end
end
class Application
extend Greeter
end
Application.new.greet
#=> Undefined method "greet"
Application.greet
#=> "Hallo Welt"
16. class Presentation < AR::Base
acts_as_searchable
end
Presentation.new
("Metaprogramming magic")
presentation.matches?(/magic/)
Presentation.search(/magic/i)
17. module Searchable
def acts_as_searchable
include InstanceMethods
extend ClassMethods
end
module ClassMethods
def search(pattern)
all.select { |m| m.description =~ pattern }
end
end
module InstanceMethods
def matches?(pattern)
description =~ pattern
end
end
end
ActiveRecord::Base.extend Searchable
18. Open Classes
class String
def ends_with? pattern
...
end
end
21. module Nokogiri
module Decorators
module Slop
def method_missing name, *args, &block
list = xpath("/#{name}")
list.length == 1 ? list.first : list
end
end
end
end
22. Product.find_by_name_and_description('Rubyslava', 'Meta')
class ActiveRecord::Base
def method_missing(name, *args, &block)
if name.to_s =~ /^find_by_(.+)$/
run_find_by_method($1, *args, &block)
else
super
end
end
def run_find_by_method(attrs, *args, &block)
# args => ['Rubyslava', 'Meta']
attrs = attrs.split('_and_')
#=> ['name', 'description']
attrs_with_args = [attrs, args].transpose
#=> [['name', 'Rubyslava'], ['description', 'Meta']
conditions = Hash[attrs_with_args]
#=> {'name' => 'Rubyslava', 'description' => 'Meta'}
where(conditions).all
end
end
23. class Color
RGB = { red: '#ff0000',
blue: '#0000ff', orange: '#bb12ee' }
def initialize(rgb)
@rgb = rgb
end
def to_s
@rgb
end
def self.const_missing(name)
rgb = RGB[name.downcase]
if rgb
Color.new(rgb)
else
super
end
end
end
puts Color::Blue
24. Dynamic method call
def dispatcher(klass, method)
klass.send method
end
method_names = public_instance_methods(true)
tests = method_names.delete_if do |method_name|
method_name !~ /^test./
end
tests.each do |test|
send test
end
26. module SimpleRouting
def resources(*names)
names.each do |resource|
define_method "#{resource}_path" do
"localhost/#{resource}"
end
end
end
end
class Application
extend SimpleRouting
resources :events, :presenters, :topics
def test_paths
puts events_path #=> localhost/events
puts presenters_path #=> localhost/presenters
puts topics_path #=> localhost/topics
end
end
27. H ks
● included
● extended
● inherited
● method_added
● method_removed
● method_undefined
● singleton_method_added
● ...
28. class DatabaseTasks
extend SimpleRake
desc "Load database migrations"
def migrate
puts "Migrating.."
end
desc "Rollback last migrations"
def rollback
puts "Rolling back"
end
end
DatabaseTasks.list_tasks
29. module SimpleRake
@@methods = {}
def desc(description)
@@description = description
end
def method_added(name)
@@methods[name] = @@description
end
def list_tasks
@@methods.each do |method, desc|
puts "#{method} # #{desc}"
end
end
end
31. class Presentation < AR::Base
include Searchable
end
Presentation.new
("Metaprogramming magic")
presentation.matches?(/magic/)
Presentation.search(/magic/i)
32. module Searchable
def self.included(base)
base.extend ClassMethods
end
def matches?(pattern)
description =~ pattern
end
module ClassMethods
def search(pattern)
all.select do |m|
m.description =~ pattern
end
end
end
end