“Metaprogramming is writing code that writes code.”
Rails.env == "production"
Rails.env.production?
class StringInquirer < String
private def method_missing method_name, *arguments
if method_name[-1] == '?'
self == method_name[0..-2]
else
super
end
end
end
class StringInquirer < String
private def respond_to_missing? method_name, include_private = false
method_name[-1] == '?'
end
end
Rails.env.respond_to? :development? # => true
1.day.ago # => 2014-08-11 22:41:47 +0800
1.day - 2.hours # => 22 hours
class Numeric
def seconds
self
end
alias second seconds
def minutes
self * 60
end
alias minute minutes
def hours
self * 60.minutes
end
alias hour hours
def days
self * 24.hours
end
alias day days
def ago
Time.now - self
end
end
class BlankSlate < BasicObject
undef ==
undef equal?
end
class Duration < BlankSlate
def initialize proxy
@proxy = proxy
end
def + other
::Duration.new @proxy + other
end
def - other
::Duration.new @proxy - other
end
def -@
::Duration.new -@proxy
end
def ago
::Time.now - @proxy
end
private def method_missing method, *args, &block
@proxy.send method, *args, &block
end
end
class Numeric
def seconds
Duration.new self
end
alias second seconds
def minutes
Duration.new self * 60.seconds
end
alias minute minutes
def hours
Duration.new self * 60.minutes
end
alias hour hours
def days
Duration.new self * 24.hours
end
alias day days
end
How to avoid 1.hour.hour
?
class Duration
def inspect
value = @proxy
parts = %w[days hours minutes seconds].inject([]) do |units, unit|
if value >= 1.send(unit)
quotient, value = value.divmod 1.send(unit)
units + [[quotient, unit]]
else
units
end
end
parts.map! {|value, unit| "#{value} #{value == 1 ? unit.chop : unit}" }
parts.join(', ')
end
end
class C
include M
extend M::ClassMethods
end
module M
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
module ClassMethods
# ...
end
end
module M
extend Concern
included do
scope :disabled, -> { where(disabled: true) }
end
module ClassMethods
# ...
end
end
module Concern
def self.extended base
base.instance_variable_set(:@_dependencies, [])
end
def included base = nil, &block
if base.nil?
@_included_blocks ||= []
@_included_blocks << block
else
super
end
end
def append_features base
if base.instance_variable_defined?(:@_dependencies)
base.instance_variable_get(:@_dependencies) << self
return false
else
return false if base < self
@_dependencies.each { |dep| base.send(:include, dep) }
super
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
if instance_variable_defined?(:@_included_blocks)
@_included_blocks.each {|block| base.class_eval(&block) }
end
end
end
end
class Bachue
def program code
# ...
end
end
class Bachue
def program_with_coffee code
log.info "start drinking ..."
program_without_coffee code
log.info "end drinking."
end
alias program_without_coffee program
alias program program_with_coffee
end
class Bachue
def program code
# ...
end
end
class Bachue
def program_with_coffee code
log.info "start drinking ..."
program_without_coffee code
log.info "end drinking."
end
alias_method_chain :program, :coffee
end
class Class
def alias_method_chain(target, feature)
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
yield aliased_target, punctuation if block_given?
with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
alias_method without_method, target
alias_method target, with_method
case
when public_method_defined?(without_method)
public target
when protected_method_defined?(without_method)
protected target
when private_method_defined?(without_method)
private target
end
end
end
class Bachue
hook def before_program code
puts 'start drinking ...'
end
hook def after_program code
puts 'end drinking.'
end
hook def around_program code
puts 'start coding ...'
yield code
puts 'end coding.'
end
end
class Class
def hook hook_method
unless hook_method.to_s =~ /^(before|around|after)_(.+)$/
raise 'Hook method must start with before, around or after'
end
type, method_name = $1.to_sym, $2
feature = "#{type}_#{SecureRandom.hex 8}__"
alias_method_chain method_name, feature do |method_name, punctuation|
define_method "#{method_name}_with_#{feature}#{punctuation}" do |*args, &block|
old_method = "#{method_name}_without_#{feature}#{punctuation}"
case type
when :before
send hook_method, *args, &block
send old_method, *args, &block
when :after
send old_method, *args, &block
send hook_method, *args, &block
when :around
send(hook_method, *args) {|*args, &block| send old_method, *args, &block }
end
end
end
end
end
Companies::CloudComputing::EMC <=> "companies/cloud_computing/emc.rb"
paths.add "app", eager_load: true, glob: "*"
paths.add "app/assets", glob: "*"
paths.add "app/controllers", eager_load: true
paths.add "app/helpers", eager_load: true
paths.add "app/models", eager_load: true
paths.add "app/mailers", eager_load: true
paths.add "app/views"
paths.add "app/controllers/concerns", eager_load: true
paths.add "app/models/concerns", eager_load: true
paths.add "lib", load_path: true
paths.add "lib/assets", glob: "*"
paths.add "lib/tasks", glob: "**/*.rake"
paths.add "config"
paths.add "config/environments", glob: "#{Rails.env}.rb"
paths.add "config/initializers", glob: "**/*.rb"
paths.add "config/locales", glob: "*.{rb,yml}"
paths.add "config/routes.rb"
paths.add "db"
paths.add "db/migrate"
paths.add "db/seeds.rb"
paths.add "vendor", load_path: true
paths.add "vendor/assets", glob: "*"
class SuperClass
FIND_ME = "Found in SuperClass"
end
module ParentLexicalScope
FIND_ME = "Found in ParentLexicalScope"
class SubClass < SuperClass
p FIND_ME
end
end
def const_missing(const_name)
Dependencies.load_missing_constant(self, const_name)
end
def load_missing_constant(from_mod, const_name)
qualified_name = qualified_name_for from_mod, const_name
path_suffix = qualified_name.underscore
file_path = search_for_file path_suffix
if file_path
expanded = File.expand_path(file_path).sub(/\.rb\z/, '')
require_or_load(expanded, qualified_name)
unless from_mod.const_defined?(const_name, false)
raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it"
end
return from_mod.const_get(const_name)
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
return mod
elsif (parent = from_mod.parent) && parent != from_mod &&
! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
begin
return parent.const_missing(const_name)
rescue NameError => e
raise unless e.missing_name? qualified_name_for(parent, const_name)
end
end
raise NameError, "uninitialized constant #{qualified_name}"
end
“There’s no such thing as metaprogramming. It’s just programming all the way through.”