-
-
Save jbr/305362 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'onion' | |
Onion.new Stats, Memoizer, TimesOut do | |
config :stats => true, :timeout => 1_000 | |
connection_pool :whatever | |
puts "Forward:" | |
transaction do | |
query "SELECT ... FROM ... FOR UPDATE ..." | |
execute "INSERT ..." | |
execute "INSERT ..." | |
end | |
reverse! | |
puts | |
puts "Backward:" | |
transaction do | |
query "(reverse) SELECT ... FROM ... FOR UPDATE ..." | |
execute "(reverse) INSERT ..." | |
execute "(reverse) INSERT ..." | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I want to turn this into a programming contest. Everyone should do this using their favorite technique and language. But, I want to add three more challenges (that admittedly stack the deck in my favor). | |
1. create a stats collecting query decorate that measures the time it takes to execute the query | |
(assume you have a stats object that responds to #measure and takes a block: stats.measure { sleep 1.second } | |
2. read "configuration" from a hash like: | |
{ :timeout => 1.second, :stats => true } | |
that adds the timeout and stats decorators to the factory if present in the hash, otherwise not. | |
3. at the end write a program that REVERSES the order of the decorators. So if you did Timeout(Stats(Query)) make it Stats(Timeout(Query)). Note that you must do this programmatically so write a generic decorator reversing function. You may expand the proxy interface to have a method called "underlying", "delegate" or some such. | |
The contest is: do this in your language in as "elegant" a way as you like. The goal is clarity and modularity. | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Forward: | |
Instantiating Query Object | |
Selecting SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18aea34> | |
Did not timeout! Yay fast database! | |
Measured select at 4.1e-05 | |
Instantiating Query Object | |
Executing INSERT ... on #<Query:0x18ae520> | |
Did not timeout! Yay fast database! | |
Measured execute at 3.3e-05 | |
Executing INSERT ... on #<Query:0x18ae520> | |
Did not timeout! Yay fast database! | |
Measured execute at 3.1e-05 | |
Backward: | |
Instantiating Query Object | |
Selecting (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18adb0c> | |
Measured select at 3.0e-05 | |
Did not timeout! Yay fast database! | |
Instantiating Query Object | |
Executing (reverse) INSERT ... on #<Query:0x18ad5f8> | |
Measured execute at 3.1e-05 | |
Did not timeout! Yay fast database! | |
Executing (reverse) INSERT ... on #<Query:0x18ad5f8> | |
Measured execute at 1.9e-05 | |
Did not timeout! Yay fast database! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Forward: | |
Instantiating Query Object | |
Selecting SELECT ... FROM ... FOR UPDATE ... on #<Object:0x11f6750> | |
Did not timeout! Yay fast database! | |
Measured select at 1.00 seconds | |
Instantiating Query Object | |
Executing INSERT ... on #<Object:0x11f6750> | |
Did not timeout! Yay fast database! | |
Measured select at 1.00 seconds | |
Executing INSERT ... on #<Object:0x11f6750> | |
Did not timeout! Yay fast database! | |
Measured select at 1.00 seconds | |
Backward: | |
Instantiating Query Object | |
Selecting SELECT ... FROM ... FOR UPDATE ... on #<Object:0x11f4ea0> | |
Measured select at 1.00 seconds | |
Did not timeout! Yay fast database! | |
Instantiating Query Object | |
Executing INSERT ... on #<Object:0x11f4ea0> | |
Measured select at 1.00 seconds | |
Did not timeout! Yay fast database! | |
Executing INSERT ... on #<Object:0x11f4ea0> | |
Measured select at 1.00 seconds | |
Did not timeout! Yay fast database! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'wrappable_methods' | |
require 'util' | |
class Onion < Array #so simple it makes me cry | |
include WrappableMethods | |
def initialize(*classes_or_objects, &blk) | |
@config = {} | |
super classes_or_objects.map {|q| q.new self rescue q.new rescue q} | |
instance_eval &blk | |
end | |
#refactor | |
def construct_query(query_string) | |
build_proc(:construct_query) do | |
Query.new(query_string, connection_pool) | |
end.call query_string | |
end | |
def query(query_string) | |
build_proc(:query) do | |
construct_query(query_string).query | |
end.call query_string | |
end | |
def execute(query_string) | |
build_proc(:execute) do | |
construct_query(query_string).execute | |
end.call query_string | |
end | |
def transaction() yield end | |
def connection_pool(pool = nil) | |
@pool = pool || @pool | |
end | |
def config(hash = nil) | |
hash ? @config.merge!(hash) : @config | |
end | |
end | |
class Query | |
def initialize(query_string, connection_pool) | |
@query_string, @connection_pool = query_string, connection_pool | |
puts "Instantiating Query Object" | |
end | |
def execute | |
sleep 1 | |
puts "Executing #{@query_string} on #{self}" | |
[1,2,3] | |
end | |
def query | |
sleep 1 | |
puts "Selecting #{@query_string} on #{self}" | |
1 | |
end | |
end | |
class Memoizer | |
def initialize | |
@memos = {} | |
end | |
def construct_query(query_string) | |
@memos[query_string] ||= yield query_string | |
end | |
end | |
class Stats | |
def initialize(onion) @config = onion.config end | |
def respond_to?(method) @config[:stats] and super end | |
def query(query_string) | |
time = Time.now | |
returning yield(query_string) do | |
puts "Measured select at #{Time.now - time}" | |
end | |
end | |
def execute(query_string) | |
time = Time.now | |
returning yield(query_string) do | |
puts "Measured execute at #{Time.now - time}" | |
end | |
end | |
end | |
class TimesOut | |
def initialize(onion) @config = onion.config end | |
def query(query_string) | |
yield(query_string) | |
puts 'Did not timeout! Yay fast database!' | |
end | |
alias_method :execute, :query | |
end | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Object | |
def returning(value) | |
yield value | |
value | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module WrappableMethods | |
def self.included(klass) klass.extend ClassMethods end | |
def build_proc(method, &innermost) | |
reverse.inject(innermost) do |inner, item| | |
if item.respond_to?(method) | |
lambda {|*args| item.send method, *args, &inner} | |
else | |
inner | |
end | |
end | |
end | |
module ClassMethods | |
def wrappable_method(method_name, &blk) | |
define_method method_name do |*args| | |
build_proc(method_name, &blk).call *args | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment