SlideShare a Scribd company logo
1 of 55
Download to read offline
Metaprogramação
na prática
Guilherme Carreiro
TDC Florianópolis - 16.05.2015
Guilherme Carreiro
Campinas, SP
Ruby, JavaScript, Java
dextra/projects
karreiro/picuture_from
discourse/OneBox
Guilherme Carreiro
Campinas, SP
Ruby, JavaScript, Java
dextra/projects
karreiro/picuture_from
discourse/OneBox
eval('`rm -rf /`')
class User < ActiveRecord::Base
# empty
end
user = User.new(name: 'Guilherme', active: true)
user.active?
# => true
Metaprogramming is writing
code that manipulates code
at runtime.
class User < ActiveRecord::Base
# empty
end
user = User.new(name: 'Guilherme', active: true)
user.active?
# => true
class User
attr_accessor :name, :active
end
user = User.new.tap do |u|
u.name = 'Guilherme'
u.active = true
end
user.active?
# => NoMethodError: undefined method `active?' for
#<User:0x007fe5d189a2b0 @name="Guilherme", @active=true>
#eval
class User
def self.define_methods
attributes = [:name?, :active?]
attributes.each do |attribute|
class_eval <<-EOMETHOD
def #{attribute}
true
end
EOMETHOD
end
end
end
user = User.new
user.name?
# => NoMethodError: undefined method `name?’ for #<User:0x007f...>
User.define_methods
user.name?
# => true
class User
class_eval do
def self.class_name
'~ User ~'
end
end
end
User.class_name
# => "~ User ~”
user = User.new
user.instance_eval do
def self.instance_name
'~ user ~'
end
end
user.instance_name
# => "~ user ~”
class_eval vs. instance_eval
Prefira const_get,
instance_variable_get, ou...
#define_method
class User < ActiveRecord::Base
enum status: { active: 0, archived: 1 }
end
user = User.new
user.status
# => 'archived'
user.active?
# => false
user.active!
# => true
user.active?
# => true
module ActiveRecord
module Enum
# (...)
# Dado que value: 'active', name: 'status' e i: 0
# def active?() status == 0 end
define_method("#{value}?") { self[name] == i }
# def active!() update! status: :active end
define_method("#{value}!") { update! name => value }
# (...)
end
end
class User
attr_accessor :name, :status
def active?
self.status == 'active'
end
def archived?
self.status == 'archived'
end
def deleted?
self.status == 'deleted'
end
end
User.new.tap { |u| u.status = 'active' }.active?
# => true
User.new.tap { |u| u.status = 'active' }.archived?
# => false
User.new.tap { |u| u.status = 'active' }.deleted?
# => false
class User
attr_accessor :name, :status
STATUSES = [:active, :archived, :deleted]
STATUSES.each do |status|
define_method "#{status}?" do
self.status == status.to_s
end
end
end
User.new.tap { |u| u.status = 'active' }.active?
# => true
User.new.tap { |u| u.status = 'active' }.archived?
# => false
User.new.tap { |u| u.status = 'active' }.deleted?
# => false
#method_missing
class User
attr_accessor :name, :active
end
user = User.new.tap do |u|
u.name = 'Guilherme'
u.active = true
end
class CustomRecord
def method_missing(name, *args, &block)
name.to_s.end_with?('?') || super
!!instance_variable_get("@#{name}".gsub /?$/, '')
end
end
class User < CustomRecord
attr_accessor :name, :active
end
user = User.new.tap do |u|
u.name = 'Guilherme'
u.active = true
end
user.active?
# => true
class CustomRecord
def method_missing(name, *args, &block)
name.to_s.end_with?('?') || super
!!instance_variable_get("@#{name}".gsub /?$/, '')
end
end
class User < CustomRecord
attr_accessor :name, :active
end
user = User.new.tap do |u|
u.name = 'Guilherme'
u.active = true
end
user.active?
# => true
user.respond_to? :active?
# => false
class CustomRecord
def method_missing(name, *args, &block)
name.to_s.end_with?('?') || super
!!instance_variable_get("@#{name}".gsub /?$/, '')
end
def respond_to_missing?(name, include_all)
name.to_s.end_with?('?') || super
end
end
class User < CustomRecord
attr_accessor :name, :active
end
user = User.new.tap do |u|
u.name = 'Guilherme'
u.active = true
end
user.respond_to? :active?
# => true
class CustomRecord
def method_missing(name, *args, &block)
name.to_s.end_with?('?') || super
self.class.send :define_method, name do
!!instance_variable_get("@#{name}".gsub /?$/, '')
end
send(name)
end
def respond_to_missing?(name, include_all)
name.to_s.end_with?('?') || super
end
end
user = User.new.tap { |u| u.name = 'Guilherme'; u.active = true }
class CustomRecord
def method_missing(name, *args, &block)
'TDC' * 1_000_000 # complex stuff
name.to_s.end_with?('?') || super
self.class.send :define_method, name do
!!instance_variable_get("@#{name}".gsub /?$/, '')
end
send(name)
end
def respond_to_missing?(name, include_all)
name.to_s.end_with?('?') || super
end
end
user = User.new.tap { |u| u.name = 'Guilherme'; u.active = true }
puts Benchmark.measure { user.active? }
# => 0.000000 0.000000 0.000000 ( 0.001539)
puts Benchmark.measure { user.active? }
# => 0.000000 0.000000 0.000000 ( 0.000021)
class User
def initialize(friends)
@friends = friends
end
end
guilherme = User.new [
'Danilo', 'Eder', 'Luan', 'Paulo', 'Sabrina', 'Tiago'
]
guilherme.number_of_friends
# => 6
attr_*
class User
attr_accessor :name, :active
end
user = User.new.tap do |u|
u.name = 'Guilherme'
u.active = true
end
user.name
# => "Guilherme"
static VALUE
rb_mod_attr_accessor(int argc, VALUE *argv, VALUE klass)
{
int i;
for (i=0; i<argc; i++) {
rb_attr(klass, rb_to_id(argv[i]), TRUE, TRUE, TRUE);
}
return Qnil;
}
void
rb_attr(VALUE klass, ID id, int read, int write, int ex)
{
// (...) algumas coisas
if (read) {
rb_add_method(klass, id, VM_METHOD_TYPE_IVAR, (void *)attriv…
}
if (write) {
rb_add_method(klass, rb_id_attrset(id), VM_METHOD_TYPE_ATTRSET…
}
}
Qual é o melhor?
Qual é o melhor?
eval, method_missing, define_method
http://tenderlovemaking.com/2013/03/03/dynamic_method_definitions.html
#send
def search_articles_by_user(user)
if logged_user.admin?
Article.admin_scope.search_by(user)
elsif logged_user.customer_admin?
Article.customer_admin_scope.search_by(user)
elsif logged_user.customer?
Article.customer_scope.search_by(user)
elsif logged_user.user?
Article.user_scope.search_by(user)
end
end
def search_articles_by_user(user)
user_levels = [:admin, :customer_admin, :customer, :user]
user_levels.each do |level|
if logged_user.send "#{level}?"
Article.send("#{level}_scope").search_by(user)
end
end
end
Hooks
Hooks
included, extended, prepended, inherited
class User
include ImageTrick
attr_accessor :name, :image
has_attached_file :image
end
user = User.new.tap { |u| u.image = '123456.png' }
user.image_url
# => "https://imagetrick.com/123456.png"
module ImageTrick
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def has_attached_file(name)
define_method "#{name}_url" do
image_name = instance_variable_get("@#{name}")
"https://imagetrick.com/#{image_name}"
end
end
end
end
class User
include ImageTrick
attr_accessor :name, :image
has_attached_file :image
end
user = User.new.tap { |u| u.image = '123456.png' }
user.image_url
# => "https://imagetrick.com/123456.png"
Monkey Patch
(Open classes)
module ImageTrick
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def has_attached_file(name)
define_method "#{name}_url" do
image_name = instance_variable_get("@#{name}")
"https://imagetrick.com/#{image_name}"
end
end
end
end
class User
include ImageTrick
attr_accessor :name, :image
has_attached_file :image
end
user = User.new.tap { |u| u.image = '123456.png' }
user.image_url
# => "https://imagetrick.com/123456.png"
module ImageTrick
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def has_attached_file(name)
define_method "#{name}_url" do
image_name = instance_variable_get("@#{name}")
"https://imagetrick.com/#{image_name}"
end
end
end
end
module ImageTrick
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def has_attached_file(name)
define_method "#{name}_url" do
image_name = instance_variable_get("@#{name}")
"https://imagetrick.com/#{image_name}"
end
end
end
end
class String
def serialized
self.gsub(/[^a-zA-Z0-9.:/-_]+/, '-').downcase
end
end
module ImageTrick
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def has_attached_file(name)
define_method "#{name}_url" do
image_name = instance_variable_get("@#{name}")
"https://imagetrick.com/#{image_name}".serialized
end
end
end
end
class String
def serialized
self.gsub(/[^a-zA-Z0-9.:/-_]+/, '-').downcase
end
end
module ImageTrick
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
def has_attached_file(name)
define_method "#{name}_url" do
image_name = instance_variable_get("@#{name}")
"https://imagetrick.com/#{image_name}".serialized
end
end
end
end
"Some string".serialized
# => "some-string"
module ImageTrick
module RefinedString
refine String do
def serialized
self.gsub(/[^a-zA-Z0-9.:/-_]+/, '-').downcase
end
end
end
def self.included(klass)
klass.extend(ClassMethods)
end
module ClassMethods
using RefinedString
def has_attached_file(name)
define_method "#{name}_url" do
image_name = instance_variable_get("@#{name}")
"https://imagetrick.com/#{image_name}".serialized
end
end
end
end
"Some string".serialized
# => undefined method `serialized' for "Some string” (NoMethodError)
…
Obrigado.
Guilherme Carreiro
GitHub: @karreiro, Twitter: @g_carreiro

More Related Content

What's hot

Patterns for JVM languages - Geecon 2014
Patterns for JVM languages - Geecon 2014Patterns for JVM languages - Geecon 2014
Patterns for JVM languages - Geecon 2014
Jaroslaw Palka
 
Mp24: The Bachelor, a facebook game
Mp24: The Bachelor, a facebook gameMp24: The Bachelor, a facebook game
Mp24: The Bachelor, a facebook game
Montreal Python
 
4. Метапрограмиране
4. Метапрограмиране4. Метапрограмиране
4. Метапрограмиране
Stefan Kanev
 

What's hot (18)

JavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UX
 
Oop php 5
Oop php 5Oop php 5
Oop php 5
 
Javascript
JavascriptJavascript
Javascript
 
Javascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introductionJavascript & jQuery: A pragmatic introduction
Javascript & jQuery: A pragmatic introduction
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4
 
Beginning Object-Oriented JavaScript
Beginning Object-Oriented JavaScriptBeginning Object-Oriented JavaScript
Beginning Object-Oriented JavaScript
 
Secrets of JavaScript Libraries
Secrets of JavaScript LibrariesSecrets of JavaScript Libraries
Secrets of JavaScript Libraries
 
Dependency Injection Smells
Dependency Injection SmellsDependency Injection Smells
Dependency Injection Smells
 
Zend framework 04 - forms
Zend framework 04 - formsZend framework 04 - forms
Zend framework 04 - forms
 
날로 먹는 Django admin 활용
날로 먹는 Django admin 활용날로 먹는 Django admin 활용
날로 먹는 Django admin 활용
 
Factory Girl
Factory GirlFactory Girl
Factory Girl
 
Data::FormValidator Simplified
Data::FormValidator SimplifiedData::FormValidator Simplified
Data::FormValidator Simplified
 
Patterns in PHP
Patterns in PHPPatterns in PHP
Patterns in PHP
 
Patterns for JVM languages - Geecon 2014
Patterns for JVM languages - Geecon 2014Patterns for JVM languages - Geecon 2014
Patterns for JVM languages - Geecon 2014
 
Mp24: The Bachelor, a facebook game
Mp24: The Bachelor, a facebook gameMp24: The Bachelor, a facebook game
Mp24: The Bachelor, a facebook game
 
Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)
 
Drupal sins 2016 10-06
Drupal sins 2016 10-06Drupal sins 2016 10-06
Drupal sins 2016 10-06
 
4. Метапрограмиране
4. Метапрограмиране4. Метапрограмиране
4. Метапрограмиране
 

Similar to TDC 2015 - Metaprogramação na prática

Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
ShriKant Vashishtha
 

Similar to TDC 2015 - Metaprogramação na prática (20)

TDC 2015 - DSLs em Ruby
TDC 2015 - DSLs em RubyTDC 2015 - DSLs em Ruby
TDC 2015 - DSLs em Ruby
 
WordPress Capabilities Magic
WordPress Capabilities MagicWordPress Capabilities Magic
WordPress Capabilities Magic
 
Implementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord modelsImplementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord models
 
Ruby on rails
Ruby on rails Ruby on rails
Ruby on rails
 
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
Stanfy MadCode Meetup #11: Why do you need to switch from Obj-C to Swift, or ...
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
Using and reusing CakePHP plugins
Using and reusing CakePHP pluginsUsing and reusing CakePHP plugins
Using and reusing CakePHP plugins
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
 
Intro programacion funcional
Intro programacion funcionalIntro programacion funcional
Intro programacion funcional
 
Using Actions and Filters in WordPress to Make a Plugin Your Own
Using Actions and Filters in WordPress to Make a Plugin Your OwnUsing Actions and Filters in WordPress to Make a Plugin Your Own
Using Actions and Filters in WordPress to Make a Plugin Your Own
 
Building Large jQuery Applications
Building Large jQuery ApplicationsBuilding Large jQuery Applications
Building Large jQuery Applications
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
 
Django Admin: Widgetry & Witchery
Django Admin: Widgetry & WitcheryDjango Admin: Widgetry & Witchery
Django Admin: Widgetry & Witchery
 
Twig tips and tricks
Twig tips and tricksTwig tips and tricks
Twig tips and tricks
 
Let's write secure Drupal code! - DrupalCamp London 2019
Let's write secure Drupal code! - DrupalCamp London 2019Let's write secure Drupal code! - DrupalCamp London 2019
Let's write secure Drupal code! - DrupalCamp London 2019
 
JavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashJavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and Lodash
 
WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3
 

Recently uploaded

Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
FIDO Alliance
 
“Iamnobody89757” Understanding the Mysterious of Digital Identity.pdf
“Iamnobody89757” Understanding the Mysterious of Digital Identity.pdf“Iamnobody89757” Understanding the Mysterious of Digital Identity.pdf
“Iamnobody89757” Understanding the Mysterious of Digital Identity.pdf
Muhammad Subhan
 
Hyatt driving innovation and exceptional customer experiences with FIDO passw...
Hyatt driving innovation and exceptional customer experiences with FIDO passw...Hyatt driving innovation and exceptional customer experiences with FIDO passw...
Hyatt driving innovation and exceptional customer experiences with FIDO passw...
FIDO Alliance
 

Recently uploaded (20)

AI in Action: Real World Use Cases by Anitaraj
AI in Action: Real World Use Cases by AnitarajAI in Action: Real World Use Cases by Anitaraj
AI in Action: Real World Use Cases by Anitaraj
 
Design Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptxDesign Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptx
 
Event-Driven Architecture Masterclass: Challenges in Stream Processing
Event-Driven Architecture Masterclass: Challenges in Stream ProcessingEvent-Driven Architecture Masterclass: Challenges in Stream Processing
Event-Driven Architecture Masterclass: Challenges in Stream Processing
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
Google I/O Extended 2024 Warsaw
Google I/O Extended 2024 WarsawGoogle I/O Extended 2024 Warsaw
Google I/O Extended 2024 Warsaw
 
Top 10 CodeIgniter Development Companies
Top 10 CodeIgniter Development CompaniesTop 10 CodeIgniter Development Companies
Top 10 CodeIgniter Development Companies
 
WebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM PerformanceWebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM Performance
 
How to Check CNIC Information Online with Pakdata cf
How to Check CNIC Information Online with Pakdata cfHow to Check CNIC Information Online with Pakdata cf
How to Check CNIC Information Online with Pakdata cf
 
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
 
How we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdfHow we scaled to 80K users by doing nothing!.pdf
How we scaled to 80K users by doing nothing!.pdf
 
الأمن السيبراني - ما لا يسع للمستخدم جهله
الأمن السيبراني - ما لا يسع للمستخدم جهلهالأمن السيبراني - ما لا يسع للمستخدم جهله
الأمن السيبراني - ما لا يسع للمستخدم جهله
 
2024 May Patch Tuesday
2024 May Patch Tuesday2024 May Patch Tuesday
2024 May Patch Tuesday
 
AI+A11Y 11MAY2024 HYDERBAD GAAD 2024 - HelloA11Y (11 May 2024)
AI+A11Y 11MAY2024 HYDERBAD GAAD 2024 - HelloA11Y (11 May 2024)AI+A11Y 11MAY2024 HYDERBAD GAAD 2024 - HelloA11Y (11 May 2024)
AI+A11Y 11MAY2024 HYDERBAD GAAD 2024 - HelloA11Y (11 May 2024)
 
“Iamnobody89757” Understanding the Mysterious of Digital Identity.pdf
“Iamnobody89757” Understanding the Mysterious of Digital Identity.pdf“Iamnobody89757” Understanding the Mysterious of Digital Identity.pdf
“Iamnobody89757” Understanding the Mysterious of Digital Identity.pdf
 
Working together SRE & Platform Engineering
Working together SRE & Platform EngineeringWorking together SRE & Platform Engineering
Working together SRE & Platform Engineering
 
How to Check GPS Location with a Live Tracker in Pakistan
How to Check GPS Location with a Live Tracker in PakistanHow to Check GPS Location with a Live Tracker in Pakistan
How to Check GPS Location with a Live Tracker in Pakistan
 
ChatGPT and Beyond - Elevating DevOps Productivity
ChatGPT and Beyond - Elevating DevOps ProductivityChatGPT and Beyond - Elevating DevOps Productivity
ChatGPT and Beyond - Elevating DevOps Productivity
 
Hyatt driving innovation and exceptional customer experiences with FIDO passw...
Hyatt driving innovation and exceptional customer experiences with FIDO passw...Hyatt driving innovation and exceptional customer experiences with FIDO passw...
Hyatt driving innovation and exceptional customer experiences with FIDO passw...
 
Introduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDMIntroduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDM
 
JohnPollard-hybrid-app-RailsConf2024.pptx
JohnPollard-hybrid-app-RailsConf2024.pptxJohnPollard-hybrid-app-RailsConf2024.pptx
JohnPollard-hybrid-app-RailsConf2024.pptx
 

TDC 2015 - Metaprogramação na prática

  • 2. Guilherme Carreiro Campinas, SP Ruby, JavaScript, Java dextra/projects karreiro/picuture_from discourse/OneBox
  • 3.
  • 4. Guilherme Carreiro Campinas, SP Ruby, JavaScript, Java dextra/projects karreiro/picuture_from discourse/OneBox
  • 5.
  • 7. class User < ActiveRecord::Base # empty end user = User.new(name: 'Guilherme', active: true) user.active? # => true
  • 8.
  • 9. Metaprogramming is writing code that manipulates code at runtime.
  • 10. class User < ActiveRecord::Base # empty end user = User.new(name: 'Guilherme', active: true) user.active? # => true
  • 11. class User attr_accessor :name, :active end user = User.new.tap do |u| u.name = 'Guilherme' u.active = true end user.active? # => NoMethodError: undefined method `active?' for #<User:0x007fe5d189a2b0 @name="Guilherme", @active=true>
  • 12.
  • 13. #eval
  • 14. class User def self.define_methods attributes = [:name?, :active?] attributes.each do |attribute| class_eval <<-EOMETHOD def #{attribute} true end EOMETHOD end end end user = User.new user.name? # => NoMethodError: undefined method `name?’ for #<User:0x007f...> User.define_methods user.name? # => true
  • 15. class User class_eval do def self.class_name '~ User ~' end end end User.class_name # => "~ User ~” user = User.new user.instance_eval do def self.instance_name '~ user ~' end end user.instance_name # => "~ user ~” class_eval vs. instance_eval
  • 18. class User < ActiveRecord::Base enum status: { active: 0, archived: 1 } end user = User.new user.status # => 'archived' user.active? # => false user.active! # => true user.active? # => true
  • 19. module ActiveRecord module Enum # (...) # Dado que value: 'active', name: 'status' e i: 0 # def active?() status == 0 end define_method("#{value}?") { self[name] == i } # def active!() update! status: :active end define_method("#{value}!") { update! name => value } # (...) end end
  • 20. class User attr_accessor :name, :status def active? self.status == 'active' end def archived? self.status == 'archived' end def deleted? self.status == 'deleted' end end User.new.tap { |u| u.status = 'active' }.active? # => true User.new.tap { |u| u.status = 'active' }.archived? # => false User.new.tap { |u| u.status = 'active' }.deleted? # => false
  • 21. class User attr_accessor :name, :status STATUSES = [:active, :archived, :deleted] STATUSES.each do |status| define_method "#{status}?" do self.status == status.to_s end end end User.new.tap { |u| u.status = 'active' }.active? # => true User.new.tap { |u| u.status = 'active' }.archived? # => false User.new.tap { |u| u.status = 'active' }.deleted? # => false
  • 23. class User attr_accessor :name, :active end user = User.new.tap do |u| u.name = 'Guilherme' u.active = true end
  • 24. class CustomRecord def method_missing(name, *args, &block) name.to_s.end_with?('?') || super !!instance_variable_get("@#{name}".gsub /?$/, '') end end class User < CustomRecord attr_accessor :name, :active end user = User.new.tap do |u| u.name = 'Guilherme' u.active = true end user.active? # => true
  • 25. class CustomRecord def method_missing(name, *args, &block) name.to_s.end_with?('?') || super !!instance_variable_get("@#{name}".gsub /?$/, '') end end class User < CustomRecord attr_accessor :name, :active end user = User.new.tap do |u| u.name = 'Guilherme' u.active = true end user.active? # => true user.respond_to? :active? # => false
  • 26. class CustomRecord def method_missing(name, *args, &block) name.to_s.end_with?('?') || super !!instance_variable_get("@#{name}".gsub /?$/, '') end def respond_to_missing?(name, include_all) name.to_s.end_with?('?') || super end end class User < CustomRecord attr_accessor :name, :active end user = User.new.tap do |u| u.name = 'Guilherme' u.active = true end user.respond_to? :active? # => true
  • 27. class CustomRecord def method_missing(name, *args, &block) name.to_s.end_with?('?') || super self.class.send :define_method, name do !!instance_variable_get("@#{name}".gsub /?$/, '') end send(name) end def respond_to_missing?(name, include_all) name.to_s.end_with?('?') || super end end user = User.new.tap { |u| u.name = 'Guilherme'; u.active = true }
  • 28. class CustomRecord def method_missing(name, *args, &block) 'TDC' * 1_000_000 # complex stuff name.to_s.end_with?('?') || super self.class.send :define_method, name do !!instance_variable_get("@#{name}".gsub /?$/, '') end send(name) end def respond_to_missing?(name, include_all) name.to_s.end_with?('?') || super end end user = User.new.tap { |u| u.name = 'Guilherme'; u.active = true } puts Benchmark.measure { user.active? } # => 0.000000 0.000000 0.000000 ( 0.001539) puts Benchmark.measure { user.active? } # => 0.000000 0.000000 0.000000 ( 0.000021)
  • 29.
  • 30. class User def initialize(friends) @friends = friends end end guilherme = User.new [ 'Danilo', 'Eder', 'Luan', 'Paulo', 'Sabrina', 'Tiago' ] guilherme.number_of_friends # => 6
  • 31.
  • 33. class User attr_accessor :name, :active end user = User.new.tap do |u| u.name = 'Guilherme' u.active = true end user.name # => "Guilherme"
  • 34. static VALUE rb_mod_attr_accessor(int argc, VALUE *argv, VALUE klass) { int i; for (i=0; i<argc; i++) { rb_attr(klass, rb_to_id(argv[i]), TRUE, TRUE, TRUE); } return Qnil; } void rb_attr(VALUE klass, ID id, int read, int write, int ex) { // (...) algumas coisas if (read) { rb_add_method(klass, id, VM_METHOD_TYPE_IVAR, (void *)attriv… } if (write) { rb_add_method(klass, rb_id_attrset(id), VM_METHOD_TYPE_ATTRSET… } }
  • 35. Qual é o melhor?
  • 36. Qual é o melhor? eval, method_missing, define_method http://tenderlovemaking.com/2013/03/03/dynamic_method_definitions.html
  • 37. #send
  • 38. def search_articles_by_user(user) if logged_user.admin? Article.admin_scope.search_by(user) elsif logged_user.customer_admin? Article.customer_admin_scope.search_by(user) elsif logged_user.customer? Article.customer_scope.search_by(user) elsif logged_user.user? Article.user_scope.search_by(user) end end
  • 39. def search_articles_by_user(user) user_levels = [:admin, :customer_admin, :customer, :user] user_levels.each do |level| if logged_user.send "#{level}?" Article.send("#{level}_scope").search_by(user) end end end
  • 40.
  • 41. Hooks
  • 43. class User include ImageTrick attr_accessor :name, :image has_attached_file :image end user = User.new.tap { |u| u.image = '123456.png' } user.image_url # => "https://imagetrick.com/123456.png"
  • 44. module ImageTrick def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def has_attached_file(name) define_method "#{name}_url" do image_name = instance_variable_get("@#{name}") "https://imagetrick.com/#{image_name}" end end end end class User include ImageTrick attr_accessor :name, :image has_attached_file :image end user = User.new.tap { |u| u.image = '123456.png' } user.image_url # => "https://imagetrick.com/123456.png"
  • 46. module ImageTrick def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def has_attached_file(name) define_method "#{name}_url" do image_name = instance_variable_get("@#{name}") "https://imagetrick.com/#{image_name}" end end end end class User include ImageTrick attr_accessor :name, :image has_attached_file :image end user = User.new.tap { |u| u.image = '123456.png' } user.image_url # => "https://imagetrick.com/123456.png"
  • 47. module ImageTrick def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def has_attached_file(name) define_method "#{name}_url" do image_name = instance_variable_get("@#{name}") "https://imagetrick.com/#{image_name}" end end end end
  • 48. module ImageTrick def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def has_attached_file(name) define_method "#{name}_url" do image_name = instance_variable_get("@#{name}") "https://imagetrick.com/#{image_name}" end end end end
  • 49. class String def serialized self.gsub(/[^a-zA-Z0-9.:/-_]+/, '-').downcase end end module ImageTrick def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def has_attached_file(name) define_method "#{name}_url" do image_name = instance_variable_get("@#{name}") "https://imagetrick.com/#{image_name}".serialized end end end end
  • 50. class String def serialized self.gsub(/[^a-zA-Z0-9.:/-_]+/, '-').downcase end end module ImageTrick def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def has_attached_file(name) define_method "#{name}_url" do image_name = instance_variable_get("@#{name}") "https://imagetrick.com/#{image_name}".serialized end end end end "Some string".serialized # => "some-string"
  • 51. module ImageTrick module RefinedString refine String do def serialized self.gsub(/[^a-zA-Z0-9.:/-_]+/, '-').downcase end end end def self.included(klass) klass.extend(ClassMethods) end module ClassMethods using RefinedString def has_attached_file(name) define_method "#{name}_url" do image_name = instance_variable_get("@#{name}") "https://imagetrick.com/#{image_name}".serialized end end end end "Some string".serialized # => undefined method `serialized' for "Some string” (NoMethodError)
  • 52.
  • 53.
  • 54.