Skip to content

Instantly share code, notes, and snippets.

@sczizzo
Created October 13, 2016 17:08
Show Gist options
  • Save sczizzo/d1fb18b690ed52425a740b35fe7a8dc8 to your computer and use it in GitHub Desktop.
Save sczizzo/d1fb18b690ed52425a740b35fe7a8dc8 to your computer and use it in GitHub Desktop.
Have some respect, and show some class!
module Classy
module Errors
def self.included(base)
base.extend(ClassMethods.clone)
base.private_class_method :error
end
module ClassMethods
def error(name, super_klass = StandardError)
klass_name = name.to_s.split('_').map(&:capitalize).join
klass = Class.new(super_klass)
const_set "#{klass_name}Error", klass
end
end
private
def error!(name)
klass_name = name.to_s.split('_').map(&:capitalize).join
raise self.class.const_get("#{klass_name}Error")
end
end
module Builder
def self.included(base)
base.extend(ClassMethods.clone)
base.private_class_method :builder
end
module ClassMethods
DEFAULT_BUILDER_OPTIONS = {
set_instance_variables?: true
}.freeze
def builder(options = DEFAULT_BUILDER_OPTIONS, &block)
@@opt_args = []
@@rest_args = []
@@block_args = []
@@klass = Class.new(Object)
block.parameters.each do |kind, arg|
case kind
when :opt
@@opt_args << arg
@@klass.send :define_method, arg.to_s do |opt|
instance_variable_set "@#{arg}", opt
end
when :rest
@@rest_args << arg
@@klass.send :define_method, arg.to_s do |*rest|
instance_variable_set "@#{arg}", rest
end
when :block
@@block_args << arg
@@klass.send :define_method, arg.to_s do |&bblock|
instance_variable_set "@#{arg}", bblock
end
end
end
if options[:set_instance_variables?]
define_method :initialize do |*new_args, &new_block|
opts = @@opt_args.map { |oa| [oa, new_args.shift] }
rests = @@rest_args.map { |ra| [ra, new_args] }
blocks = @@block_args.map { |ba| [ba, new_block] }
(opts + rests + blocks).each do |name, val|
instance_variable_set "@#{name}", val
end
block_args = opts.map(&:last)
rests.map(&:last).each { |rest| block_args += rest }
instance_exec(*block_args, &block)
end
else
define_method :initialize, &block
end
end
def build(&block)
builder = @@klass.new
builder.instance_eval(&block) if block_given?
args = @@opt_args.map do |arg|
builder.instance_variable_get "@#{arg}"
end
if rest_arg = @@rest_args.first
ra = builder.instance_variable_get("@#{rest_arg}")
args += ra if ra
end
if block_arg = @@block_args.first
ba = builder.instance_variable_get("@#{block_arg}")
new(*args, &ba)
else
new(*args)
end
end
end
end
end
class Message
include Classy::Errors
include Classy::Builder
error :bad_subject
error :no_subject
error :no_body
attr_reader :subject, :body, :args
builder do |subject, body, *args, &delivery|
error! :no_subject unless @subject
error! :bad_subject unless @subject =~ /awesome/i
error! :no_body unless @body
end
def deliver
instance_eval(&@delivery) if @delivery
end
end
class AutoDeliveredMessage < Message
def initialize(*as)
super(*as)
deliver
end
end
require 'json'
m1 = Message.build do
subject 'testy awesome test'
body 'ya boi'
end
m2 = Message.build do
subject 'aight but awesome'
body 'ya boi'
args 'one', 'two', 'three'
delivery do
puts JSON.pretty_generate(
kind: :message,
subject: subject,
body: body,
args: args
)
end
end
m3 = Message.new('not awesome', 'but works', 'test', 'test', 'test') do
puts JSON.pretty_generate(
kind: :message,
subject: subject,
body: body,
args: args
)
end
m1.deliver
m2.deliver
m3.deliver
AutoDeliveredMessage.build do
subject 'pretty awesome'
body 'and works'
delivery do
puts JSON.pretty_generate(
kind: :message,
subject: subject,
body: body,
args: args
)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment