Skip to content

Instantly share code, notes, and snippets.

@ashmoran
Created August 22, 2010 20:19
Show Gist options
  • Select an option

  • Save ashmoran/544233 to your computer and use it in GitHub Desktop.

Select an option

Save ashmoran/544233 to your computer and use it in GitHub Desktop.
# Yet another version with suggestions from workmad3 :-)
# This time, pass in the keyword args into the block.
# This means duplicating the definition of valid params, but
# also means you get normal local variables in the method body.
module DefK
# NOTE: params is for syntactic sugar, and has no bearing on the
# number of arguments you can call
def def_k(method_name, *params, kparams, &method_body)
define_method method_name do |*args|
kargs =
if args.last.is_a?(Hash)
kparams.merge(args.delete_at(-1))
else
kparams
end
args.concat(kargs.values)
method_body.call(*args)
end
end
end
class Class
include DefK
end
class Duck
def_k :quack, name: "Donald" do |name|
"QUACK #{name}"
end
end
class Book
class << self
def_k :find, :selector, conditions: "", joins: [ ] do |selector, conditions, joins|
sql = ["SELECT * FROM books"]
joins.each do |join_table|
sql << "LEFT OUTER JOIN #{join_table} ON"
sql << "books.#{join_table.to_s.chop}_id"
sql << "= #{join_table}.id"
end
sql << "WHERE #{conditions}" unless conditions.empty?
sql << "LIMIT 1" if selector == :first
sql.join(" ") + ";"
end
end
end
describe Duck do
let(:duck) { Duck.new }
describe "#quack" do
it "quacks like Donald" do
duck.quack.should eq "QUACK Donald"
end
it "quacks like Daffy" do
duck.quack(name: "Daffy").should eq "QUACK Daffy"
end
end
end
describe Book do
describe "#find" do
it "supports :all" do
Book.find(:all).should eq "SELECT * FROM books;"
end
it "supports :first" do
Book.find(:first).should eq "SELECT * FROM books LIMIT 1;"
end
it "supports conditions" do
Book.find(:all, conditions: "name = 'Animal Farm'").should eq "SELECT * FROM books WHERE name = 'Animal Farm';"
end
it "supports joins" do
Book.find(:all, joins: %w[ authors publishers ]).should eq(
"SELECT * FROM books " +
"LEFT OUTER JOIN authors ON books.author_id = authors.id " +
"LEFT OUTER JOIN publishers ON books.publisher_id = publishers.id;"
)
end
end
end
# This version with workmad3's OpenStruct suggestion
require 'ostruct'
module DefK
# NOTE: params is for syntactic sugar, and has no bearing on the
# number of arguments you can call
def def_k(method_name, *params, kparams, &method_body)
define_method method_name do |*args|
if args.last.is_a?(Hash)
args[-1] = kparams.merge(args.last)
else
args << kparams
end
args[-1] = OpenStruct.new(args[-1])
method_body.call(*args)
end
end
end
class Class
include DefK
end
class Duck
def_k :quack, name: "Donald" do |kargs|
"QUACK #{kargs.name}"
end
end
class Book
class << self
def_k :find, :selector, conditions: "", joins: [ ] do |selector, kargs|
sql = ["SELECT * FROM books"]
kargs.joins.each do |join_table|
sql << "LEFT OUTER JOIN #{join_table} ON"
sql << "books.#{join_table.to_s.chop}_id"
sql << "= #{join_table}.id"
end
sql << "WHERE #{kargs.conditions}" unless kargs.conditions.empty?
sql << "LIMIT 1" if selector == :first
sql.join(" ") + ";"
end
end
end
describe Duck do
let(:duck) { Duck.new }
describe "#quack" do
it "quacks like Donald" do
duck.quack.should eq "QUACK Donald"
end
it "quacks like Daffy" do
duck.quack(name: "Daffy").should eq "QUACK Daffy"
end
end
end
describe Book do
describe "#find" do
it "supports :all" do
Book.find(:all).should eq "SELECT * FROM books;"
end
it "supports :first" do
Book.find(:first).should eq "SELECT * FROM books LIMIT 1;"
end
it "supports conditions" do
Book.find(:all, conditions: "name = 'Animal Farm'").should eq "SELECT * FROM books WHERE name = 'Animal Farm';"
end
it "supports joins" do
Book.find(:all, joins: %w[ authors publishers ]).should eq(
"SELECT * FROM books " +
"LEFT OUTER JOIN authors ON books.author_id = authors.id " +
"LEFT OUTER JOIN publishers ON books.publisher_id = publishers.id;"
)
end
end
end
@ashmoran
Copy link
Author

That's the first thing I though of after hours of staring at args[-1]. It's only a code spike, there are loads of defects in the snippets above. I just wanted to see if the concept had any legs (it does, but it's limping at the moment...).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment