Skip to content

Instantly share code, notes, and snippets.

@DmitryTsepelev
Last active December 14, 2021 18:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DmitryTsepelev/ab1797e7d00b484973615ba1782e0e36 to your computer and use it in GitHub Desktop.
Save DmitryTsepelev/ab1797e7d00b484973615ba1782e0e36 to your computer and use it in GitHub Desktop.

Examples for my metaprogramming/DSL talk

module MyAttrAccessor
def my_attr_accessor(attr_name)
my_attr_reader(attr_name)
my_attr_writer(attr_name)
end
def my_attr_reader(attr_name)
instance_variable_name = attr_to_variable_name(attr_name)
define_method(attr_name) do
instance_variable_get(instance_variable_name)
end
end
def my_attr_writer(attr_name)
instance_variable_name = attr_to_variable_name(attr_name)
define_method("#{attr_name}=") do |value|
instance_variable_set(instance_variable_name, value)
end
end
private
def attr_to_variable_name(attr_name)
"@#{attr_name}"
end
end
Object.extend(MyAttrAccessor)
class User
my_attr_accessor :email
def initialize(email: nil)
@email = email
end
end
user = User.new
user.email = 'john.doe@example.com'
puts user.email # => john.doe@example.com
module MyAttrAccessor
def self.[](attr_name)
Module.new do
instance_variable_name = "@#{attr_name}"
define_method(attr_name) do
instance_variable_get(instance_variable_name)
end
define_method("#{attr_name}=") do |value|
instance_variable_set(instance_variable_name, value)
end
end
end
end
class User
include MyAttrAccessor[:email]
def initialize(email: nil)
@email = email
end
end
user = User.new
user.email = 'john.doe@example.com'
puts user.email # => john.doe@example.com
class Builder
def get(path, **args)
routes[['get', path]] = args
end
def post(path, **args)
routes[['post', path]] = args
end
def routes
@routes ||= {}
end
end
class Router
NOT_FOUND = 'NOT_FOUND'
def self.draw
builder = Builder.new
yield(builder)
new(builder)
end
def initialize(builder)
@builder = builder
end
def route(method_and_path)
method, path = method_and_path.split(' ')
@builder.routes[[method.downcase, path]] || NOT_FOUND
end
end
router = Router.draw do |builder|
builder.get '/users', controller: :users, action: :index
builder.post '/users', controller: :users, action: :create
end
puts router.route('GET /users') # => {:controller=>:users, :action=>:index}
puts router.route('POST /users') # => {:controller=>:users, :action=>:create}
puts router.route('GET /orders') # => NOT_FOUND
class Router
NOT_FOUND = 'NOT_FOUND'
def self.draw(&block)
new.tap { |router| router.instance_eval(&block) }
end
def route(method_and_path)
method, path = method_and_path.split(' ')
routes[[method.downcase, path]] || NOT_FOUND
end
def get(path, **args)
routes[['get', path]] = args
end
def post(path, **args)
routes[['post', path]] = args
end
def routes
@routes ||= {}
end
end
router = Router.draw do
get '/users', controller: :users, action: :index
post '/users', controller: :users, action: :create
end
puts router.route('GET /users') # => {:controller=>:users, :action=>:index}
puts router.route('POST /users') # => {:controller=>:users, :action=>:create}
puts router.route('GET /orders') # => NOT_FOUND
class Router
class << self
def draw(&block)
new.tap { |router| router.instance_eval(&block) }
end
def generate_route_methods(*method_names)
method_names.each do |method_name|
define_method(method_name) do |path, **args|
routes[[method_name, path]] = args
end
end
end
end
generate_route_methods :get, :post, :patch, :delete
NOT_FOUND = 'NOT_FOUND'
def route(method_and_path)
method, path = method_and_path.split(' ')
routes[[method.downcase, path]] || NOT_FOUND
end
def routes
@routes ||= {}
end
end
router = Router.draw do
get '/users', controller: :users, action: :index
post '/users', controller: :users, action: :create
end
puts router.route('GET /users') # => {:controller=>:users, :action=>:index}
puts router.route('POST /users') # => {:controller=>:users, :action=>:create}
puts router.route('GET /orders') # => NOT_FOUND
class DB
def self.instance
@instance ||= new
end
def users
@users ||= {}
end
end
module CoolRecord
def self.included(base)
base.extend(ClassMethods)
end
def save
record_id =
if new_record?
self.id = (DB.instance.users.map(&:id).max || 0) + 1
else
id
end
self.class.table[record_id] = { id: record_id, email: email, name: name }
end
def new_record?
id.nil?
end
module ClassMethods
def where(conditions)
table.values
.select { |attrs| conditions.all? { |(attr_name, value)| attrs[attr_name] == value } }
.map { |attrs| self.new(**attrs) }
end
def find_by(conditions)
where(conditions).first
end
def table
DB.instance.public_send(table_name)
end
private
def table_name
"#{self.name.downcase}s"
end
end
end
class User
include CoolRecord
attr_accessor :id, :email, :name
def initialize(id: nil, email: nil, name: nil)
@id = id
@email = email
@name = name
end
end
user = User.new(email: 'john@example.com', name: 'John')
user.save
puts User.where(name: 'John').inspect # => [#<User:0x00007fa5f385c2f8 @id=1, @email="john@example.com", @name="John">]
puts User.find_by(name: 'John').inspect # => #<User:0x00007fa5f3834398 @id=1, @email="john@example.com", @name="John">
puts User.where(name: 'Jane').inspect # => []
module MyAttrAccessor
def my_attr_accessor(attr_name)
class_eval <<~TEMPLATE
def #{attr_name}
@#{attr_name}
end
def #{attr_name}=value
@#{attr_name} = value
end
TEMPLATE
end
end
Object.extend(MyAttrAccessor)
class User
my_attr_accessor :email
def initialize(email: nil)
@email = email
end
end
user = User.new
user.email = 'john.doe@example.com'
puts user.email # => john.doe@example.com
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment