11. Repeat with me
The superclass of the eigenclass of an
object is the object’s class.
The superclass of the eigenclass of a
class is the eigenclass of the class’s
superclass.
27. A typical example
class User
ROLES = [“user”, “admin”]
# scopes for each role
scope :user, where(role: “user”)
scope :admin, where(role: “admin”)
# Identifying methods for each role
def user?
role == “user”
end
def admin?
role == “admin”
end
end
28. A typical example
Now let’s add some more roles:
ROLES = [“guest”, “user”,
“confirmed_user”, “moderator”,
“manager”, “admin”, “superadmin”]
31. A typical example
class User
ROLES = [“guest”, “user”, “confirmed_user”, “moderator”,
“manager”, “admin”, “superadmin”]
ROLES.each do |user_role|
scope user_role.to_sym, where(role: user_role)
define_method “#{user_role}?” do
role == user_role
end
end
end
34. It does just that: run a string as if it was
code.
class_eval and instance_eval do the
same, just changing the context.
We can also use it on an object using
send.
36. An example
class Movie
translate :title, :overview
def translate(*attributes)
attributes.each do |attr|
define_method attr do
send(“#{attr}_#{I18n.locale}”)
end
end
end
end
37. You can even define
new methods like this!
String.class_eval(“def say_hello; puts „hello‟; end”)
“a string”.say_hello
#=> “hello”
41. An example
class MetalDetector
def method_missing(method, *args, &block)
if method =~ /metal/
puts “Metal detected!”
else
super
end
end
end
metal_detector = MetalDetector.new
metal_detector.bringing_some_metal_with_me
# => “Metal detected!”
42. A pretty exemplary use of
method_missing use are
Rails’ dynamic finders
(deprecated in Rails 4)
43. Calling finding methods with any
combination of attributes will work.
User.find_by_name_and_surname(“Mickey”, “Mouse”)
User.find_by_surname_and_movie(“Mouse”, “Fantasia”)
User.find_by_movie_and_job_and_name(“Fantasia”, “sorcerer”, “Mickey”)
47. Defining setters
def method_missing(method, *args, &block)
unless self.class.attribute_methods_generated?
self.class.define_attribute_methods
if respond_to_without_attributes?(method)
send(method, *args, &block)
else
super
end
else
super
end
end
48. Finally it gets to something like this:
def define_write_method(attr_name)
method_definition = “def #{attr_name}=(new_value);
write_attribute(„#{attr_name}‟, new_value); end”
class_eval(method_definition, __FILE__, __LINE)
end
(as told, it’s a pretty abbreviated version)
49. To know more
Episode 8 of Metaprogramming Ruby: Inside ActiveRecord
https://github.com/rails/rails/blob/master/activerecord/lib/active_r
ecord/attribute_methods.rb
https://github.com/rails/rails/blob/master/activemodel/lib/active_
model/attribute_methods.rb
https://github.com/rails/rails/blob/master/activerecord/lib/active_r
ecord/attribute_methods/write.rb
50. But in the end of the movie, all the
magic backslashes…
58. If you use any kind of eval, remember to
think in all possible cases, specially if
users are involved.
You don’t want to evaluate
“User.destroy_all” on your own
application!
59. Even if users should be able to evaluate
code in your server, there are ways to
protect you:
Clean Rooms
62. class MetalDetector
def method_missing(method, *args)
if method =~ /metal/
puts “Metal detected!”
else
super
end
end
end
metal_detector = MetalDetector.new
metal_detector.metal
# => “Metal detected!”
metal_detector.respond_to?(:metal)
#=> false
63. There is a work-around: to monkey
patch the respond_to? method.
Feels kinda hacky.
And still, it can be hard to maintain.
64. If you want to know more about the dangers of
method_missing, Paolo Perrotta (the author of
Metaprogramming Ruby) has a full presentation
about it: The revenge of method_missing()