-
-
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 | |
puts "FORWARD:" | |
transaction do | |
query "SELECT ... FROM ... FOR UPDATE ..." | |
execute "INSERT ..." | |
execute "INSERT ..." | |
end | |
reverse! | |
puts "\n\n\nREVERSED ORDER\n\n\n" | |
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 #<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' | |
class Object | |
def returning(value) | |
yield value | |
value | |
end | |
end | |
class Onion < Array #so simple it makes me cry | |
include WrappableMethods | |
def initialize(*quack, &blk) | |
@config = {} | |
super quack.map {|q| q.new self rescue q.new rescue q} | |
instance_eval &blk | |
end | |
wrappable_method :query do |query_string| | |
puts "Selecting: #{query_string}" | |
Query.new(query_string) | |
end | |
wrappable_method :execute do |query_string| | |
puts "Executing: #{query_string}" | |
Query.new(query_string) | |
end | |
def transaction | |
puts "BEGIN" | |
yield | |
puts "COMMIT" | |
end | |
def config(hash = nil) | |
if hash | |
@config.merge! hash | |
else | |
@config | |
end | |
end | |
end | |
class Query | |
def initialize(query_string) puts "Initializing new query object" end | |
end | |
class Memoizer | |
def initialize | |
@queries = {} | |
@executions = {} | |
end | |
def query(query_string) | |
@queries[query_string] ||= begin | |
puts 'Query cache miss' | |
yield query_string | |
end | |
end | |
def execute(query_string) | |
@queries[query_string] ||= begin | |
puts 'Execute cache miss' | |
yield query_string | |
end | |
end | |
end | |
class Stats | |
def initialize(onion) @config = onion.config end | |
def respond_to?(method) @config[:stats] and super end | |
def query(query_string) | |
returning yield(query_string) do | |
puts "Measured select (#{query_string}) at 1 second" | |
end | |
end | |
alias_method :execute, :query | |
end | |
class TimesOut | |
def query(query_string) | |
returning yield(query_string) do | |
puts 'Did not timeout! Yay fast database!' | |
end | |
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
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