Skip to content

Instantly share code, notes, and snippets.

@jbr
Forked from nkallen/MODULARITY_OLYMPICS.markdown
Created February 16, 2010 06:55

Revisions

  1. Jacob Rothstein revised this gist Feb 16, 2010. 3 changed files with 14 additions and 15 deletions.
    3 changes: 1 addition & 2 deletions _program.rb
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,7 @@
    require 'onion'

    Onion.new Stats, Memoizer, TimesOut do
    config :stats => true, :timeout => 1_000
    connection_pool ConnectionPool.new(20)
    config :stats => true, :timeout => 1_000, :pool_size => 20

    puts "Forward:"

    24 changes: 12 additions & 12 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,25 +1,25 @@
    Forward:
    Instantiating Query Object
    Selecting SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ae8f4> (connection: #<Object:0x18aed7c>)
    Selecting SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18aa010> (connection: #<Object:0x18aa4c0>)
    Did not timeout! Yay fast database!
    Measured select at 1.000089
    Measured select at 1.000166
    Instantiating Query Object
    Executing INSERT ... on #<Query:0x18ae37c> (connection: #<Object:0x18aed7c>)
    Executing INSERT ... on #<Query:0x18a9a70> (connection: #<Object:0x18aa4c0>)
    Did not timeout! Yay fast database!
    Measured execute at 1.000109
    Executing INSERT ... on #<Query:0x18ae37c> (connection: #<Object:0x18aed7c>)
    Measured execute at 1.000148
    Executing INSERT ... on #<Query:0x18a9a70> (connection: #<Object:0x18aa4c0>)
    Did not timeout! Yay fast database!
    Measured execute at 1.000064
    Measured execute at 1.000085

    Backward:
    Instantiating Query Object
    Selecting (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ad864> (connection: #<Object:0x18adcec>)
    Measured select at 1.000085
    Selecting (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18a8f1c> (connection: #<Object:0x18a93cc>)
    Measured select at 1.000116
    Did not timeout! Yay fast database!
    Instantiating Query Object
    Executing (reverse) INSERT ... on #<Query:0x18ad300> (connection: #<Object:0x18adcec>)
    Measured execute at 1.000108
    Executing (reverse) INSERT ... on #<Query:0x18a8990> (connection: #<Object:0x18a93cc>)
    Measured execute at 1.00013
    Did not timeout! Yay fast database!
    Executing (reverse) INSERT ... on #<Query:0x18ad300> (connection: #<Object:0x18adcec>)
    Measured execute at 1.000081
    Executing (reverse) INSERT ... on #<Query:0x18a8990> (connection: #<Object:0x18a93cc>)
    Measured execute at 1.000094
    Did not timeout! Yay fast database!
    2 changes: 1 addition & 1 deletion onion.rb
    Original file line number Diff line number Diff line change
    @@ -38,7 +38,7 @@ def transaction(&blk)
    end

    def connection_pool(pool = nil)
    @pool = pool || @pool
    @pool ||= ConnectionPool.new config[:pool_size]
    end

    def config(hash = nil)
  2. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion onion.rb
    Original file line number Diff line number Diff line change
    @@ -73,7 +73,7 @@ def initialize
    end

    def construct_query(query_string, connection)
    @memos[query_string] ||= yield query_string, connection
    @memos[[connection, query_string]] ||= yield query_string, connection
    end
    end

  3. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion onion.rb
    Original file line number Diff line number Diff line change
    @@ -67,7 +67,6 @@ def query
    end
    end


    class Memoizer
    def initialize
    @memos = {}
  4. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ def initialize(*classes_or_objects, &blk)
    instance_eval &blk
    end

    def build_proc(method, &innermost)
    def wrap(method, &innermost)
    reverse.inject(innermost) do |inner, item|
    if item.respond_to? method
    lambda {|*args| item.send method, *args, &inner}
    @@ -16,19 +16,19 @@ def build_proc(method, &innermost)
    end

    def construct_query(query_string, connection)
    build_proc(:construct_query) do
    wrap :construct_query do
    Query.new query_string, connection
    end.call query_string, connection
    end

    def query(query_string, connection)
    build_proc(:query) do
    wrap :query do
    construct_query(query_string, connection).query
    end.call query_string
    end

    def execute(query_string, connection)
    build_proc(:execute) do
    wrap :execute do
    construct_query(query_string, connection).execute
    end.call query_string
    end
  5. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 1 addition and 12 deletions.
    13 changes: 1 addition & 12 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -15,17 +15,6 @@ def build_proc(method, &innermost)
    end
    end

    # def self.wrappable_method(method_name, &blk)
    # define_method method_name do |*args|
    # build_proc(method_name, &blk).call *args
    # end
    # end

    # target (see WrappableMethods::ClassMethods, something is wrong with bindings)
    # wrappable_method(:construct_query) do |query_string|
    # Query.new(query_string, connection_pool)
    # end

    def construct_query(query_string, connection)
    build_proc(:construct_query) do
    Query.new query_string, connection
    @@ -92,8 +81,8 @@ def construct_query(query_string, connection)
    class Stats
    def initialize(onion) @config = onion.config end
    def respond_to?(method) @config[:stats] and super end

    def returning(value) yield(value); value end

    def query(query_string)
    time = Time.now
    returning yield(query_string) do
  6. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion onion.rb
    Original file line number Diff line number Diff line change
    @@ -85,7 +85,7 @@ def initialize
    end

    def construct_query(query_string, connection)
    @memos[query_string] ||= yield query_string
    @memos[query_string] ||= yield query_string, connection
    end
    end

  7. Jacob Rothstein revised this gist Feb 16, 2010. 3 changed files with 44 additions and 39 deletions.
    7 changes: 3 additions & 4 deletions _program.rb
    Original file line number Diff line number Diff line change
    @@ -13,10 +13,9 @@
    end

    reverse!
    puts

    puts "Backward:"


    puts "\nBackward:"

    transaction do
    query "(reverse) SELECT ... FROM ... FOR UPDATE ..."
    execute "(reverse) INSERT ..."
    22 changes: 11 additions & 11 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,25 +1,25 @@
    Forward:
    Instantiating Query Object
    Selecting SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ae890>
    Selecting SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ae8f4> (connection: #<Object:0x18aed7c>)
    Did not timeout! Yay fast database!
    Measured select at 1.00018
    Measured select at 1.000089
    Instantiating Query Object
    Executing INSERT ... on #<Query:0x18ae37c>
    Executing INSERT ... on #<Query:0x18ae37c> (connection: #<Object:0x18aed7c>)
    Did not timeout! Yay fast database!
    Measured execute at 1.000167
    Executing INSERT ... on #<Query:0x18ae37c>
    Measured execute at 1.000109
    Executing INSERT ... on #<Query:0x18ae37c> (connection: #<Object:0x18aed7c>)
    Did not timeout! Yay fast database!
    Measured execute at 1.000085
    Measured execute at 1.000064

    Backward:
    Instantiating Query Object
    Selecting (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ad968>
    Measured select at 1.000178
    Selecting (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ad864> (connection: #<Object:0x18adcec>)
    Measured select at 1.000085
    Did not timeout! Yay fast database!
    Instantiating Query Object
    Executing (reverse) INSERT ... on #<Query:0x18ad454>
    Measured execute at 1.000127
    Executing (reverse) INSERT ... on #<Query:0x18ad300> (connection: #<Object:0x18adcec>)
    Measured execute at 1.000108
    Did not timeout! Yay fast database!
    Executing (reverse) INSERT ... on #<Query:0x18ad454>
    Executing (reverse) INSERT ... on #<Query:0x18ad300> (connection: #<Object:0x18adcec>)
    Measured execute at 1.000081
    Did not timeout! Yay fast database!
    54 changes: 30 additions & 24 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    class Onion < Array #so simple it makes me cry
    class Onion < Array
    def initialize(*classes_or_objects, &blk)
    @config = {}
    super classes_or_objects.map {|q| q.new self rescue q.new rescue q}
    @@ -15,37 +15,37 @@ def build_proc(method, &innermost)
    end
    end

    def self.wrappable_method(method_name, &blk)
    define_method method_name do |*args|
    build_proc(method_name, &blk).call *args
    end
    end
    # def self.wrappable_method(method_name, &blk)
    # define_method method_name do |*args|
    # build_proc(method_name, &blk).call *args
    # end
    # end

    # target (see WrappableMethods::ClassMethods, something is wrong with bindings)
    # wrappable_method(:construct_query) do |query_string|
    # Query.new(query_string, connection_pool)
    # end

    def construct_query(query_string)
    def construct_query(query_string, connection)
    build_proc(:construct_query) do
    Query.new query_string, ConnectionPool.current_connection
    end.call query_string
    Query.new query_string, connection
    end.call query_string, connection
    end

    def query(query_string)
    def query(query_string, connection)
    build_proc(:query) do
    construct_query(query_string).query
    construct_query(query_string, connection).query
    end.call query_string
    end

    def execute(query_string)
    def execute(query_string, connection)
    build_proc(:execute) do
    construct_query(query_string).execute
    construct_query(query_string, connection).execute
    end.call query_string
    end

    def transaction
    connection_pool.with_connection{yield}
    def transaction(&blk)
    ConnectionContext.new self, connection_pool, &blk
    end

    def connection_pool(pool = nil)
    @@ -69,19 +69,22 @@ def execute
    [1,2,3]
    end

    def to_s() super + " (connection: #{@connection})" 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)
    def construct_query(query_string, connection)
    @memos[query_string] ||= yield query_string
    end
    end
    @@ -119,15 +122,18 @@ def query(query_string)

    class ConnectionPool
    def initialize(num) end
    def allocate_connection() :some_connection_representation end
    def self.current_connection() Thread.current[:onion_connection] end
    def with_connection
    #temporary kludge, gotta think
    Thread.current[:onion_connection] = allocate_connection
    yield
    Thread.current[:onion_connection] = nil
    def allocate_connection() Object.new end
    end

    class ConnectionContext
    def initialize(callee, connection_pool, &blk)
    @callee = callee
    @connection = connection_pool.allocate_connection
    instance_eval &blk
    end

    def query(string) @callee.query(string, @connection) end
    def execute(string) @callee.execute(string, @connection) end
    end


  8. Jacob Rothstein revised this gist Feb 16, 2010. 3 changed files with 36 additions and 31 deletions.
    2 changes: 1 addition & 1 deletion _program.rb
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@

    Onion.new Stats, Memoizer, TimesOut do
    config :stats => true, :timeout => 1_000
    connection_pool :whatever
    connection_pool ConnectionPool.new(20)

    puts "Forward:"

    44 changes: 35 additions & 9 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -1,22 +1,34 @@
    require 'wrappable_methods'

    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

    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

    def self.wrappable_method(method_name, &blk)
    define_method method_name do |*args|
    build_proc(method_name, &blk).call *args
    end
    end

    #target (see WrappableMethods::ClassMethods, something is wrong with bindings)
    # target (see WrappableMethods::ClassMethods, something is wrong with bindings)
    # wrappable_method(:construct_query) do |query_string|
    # Query.new(query_string, connection_pool)
    # end

    def construct_query(query_string)
    build_proc(:construct_query) do
    Query.new(query_string, connection_pool)
    Query.new query_string, ConnectionPool.current_connection
    end.call query_string
    end

    @@ -32,7 +44,9 @@ def execute(query_string)
    end.call query_string
    end

    def transaction() yield end
    def transaction
    connection_pool.with_connection{yield}
    end

    def connection_pool(pool = nil)
    @pool = pool || @pool
    @@ -44,8 +58,8 @@ def config(hash = nil)
    end

    class Query
    def initialize(query_string, connection_pool)
    @query_string, @connection_pool = query_string, connection_pool
    def initialize(query_string, connection)
    @query_string, @connection = query_string, connection
    puts "Instantiating Query Object"
    end

    @@ -103,5 +117,17 @@ def query(query_string)
    alias_method :execute, :query
    end

    class ConnectionPool
    def initialize(num) end
    def allocate_connection() :some_connection_representation end
    def self.current_connection() Thread.current[:onion_connection] end

    def with_connection
    #temporary kludge, gotta think
    Thread.current[:onion_connection] = allocate_connection
    yield
    Thread.current[:onion_connection] = nil
    end
    end


    21 changes: 0 additions & 21 deletions wrappable_methods.rb
    Original file line number Diff line number Diff line change
    @@ -1,21 +0,0 @@
    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
  9. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 0 additions and 11 deletions.
    11 changes: 0 additions & 11 deletions _README.txt
    Original file line number Diff line number Diff line change
    @@ -1,11 +0,0 @@
    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.

  10. Jacob Rothstein revised this gist Feb 16, 2010. 2 changed files with 1 addition and 7 deletions.
    2 changes: 1 addition & 1 deletion onion.rb
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,4 @@
    require 'wrappable_methods'
    require 'util'

    class Onion < Array #so simple it makes me cry
    include WrappableMethods
    @@ -77,6 +76,7 @@ class Stats
    def initialize(onion) @config = onion.config end
    def respond_to?(method) @config[:stats] and super end

    def returning(value) yield(value); value end
    def query(query_string)
    time = Time.now
    returning yield(query_string) do
    6 changes: 0 additions & 6 deletions util.rb
    Original file line number Diff line number Diff line change
    @@ -1,6 +0,0 @@
    class Object
    def returning(value)
    yield value
    value
    end
    end
  11. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 2 additions and 3 deletions.
    5 changes: 2 additions & 3 deletions wrappable_methods.rb
    Original file line number Diff line number Diff line change
    @@ -12,10 +12,9 @@ def build_proc(method, &innermost)
    end

    module ClassMethods
    def wrappable_method(method_name)
    method = lambda {yield}
    def wrappable_method(method_name, &blk)
    define_method method_name do |*args|
    build_proc(method_name, &method).call *args
    build_proc(method_name, &blk).call *args
    end
    end
    end
  12. Jacob Rothstein revised this gist Feb 16, 2010. 3 changed files with 35 additions and 13 deletions.
    26 changes: 24 additions & 2 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,25 @@
    Forward:
    [#<Stats:0x18aeebc @config={:stats=>true, :timeout=>1000}>, #<Memoizer:0x18aee94 @memos={}>, #<TimesOut:0x18aedb8 @config={:stats=>true, :timeout=>1000}>]
    "SELECT ... FROM ... FOR UPDATE ..."
    Instantiating Query Object
    Selecting SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ae890>
    Did not timeout! Yay fast database!
    Measured select at 1.00018
    Instantiating Query Object
    Executing INSERT ... on #<Query:0x18ae37c>
    Did not timeout! Yay fast database!
    Measured execute at 1.000167
    Executing INSERT ... on #<Query:0x18ae37c>
    Did not timeout! Yay fast database!
    Measured execute at 1.000085

    Backward:
    Instantiating Query Object
    Selecting (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ad968>
    Measured select at 1.000178
    Did not timeout! Yay fast database!
    Instantiating Query Object
    Executing (reverse) INSERT ... on #<Query:0x18ad454>
    Measured execute at 1.000127
    Did not timeout! Yay fast database!
    Executing (reverse) INSERT ... on #<Query:0x18ad454>
    Measured execute at 1.000081
    Did not timeout! Yay fast database!
    16 changes: 8 additions & 8 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -9,16 +9,16 @@ def initialize(*classes_or_objects, &blk)
    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

    #target (see WrappableMethods::ClassMethods, something is wrong with bindings)
    # wrappable_method(:construct_query) do |query_string|
    # Query.new(query_string, connection_pool)
    # end

    wrappable_method(:construct_query) do |query_string|
    p query_string
    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)
    6 changes: 3 additions & 3 deletions wrappable_methods.rb
    Original file line number Diff line number Diff line change
    @@ -12,10 +12,10 @@ def build_proc(method, &innermost)
    end

    module ClassMethods
    def wrappable_method(method_name, &blk)
    def wrappable_method(method_name)
    method = lambda {yield}
    define_method method_name do |*args|
    p self
    build_proc(method_name, &blk).call *args
    build_proc(method_name, &method).call *args
    end
    end
    end
  13. Jacob Rothstein revised this gist Feb 16, 2010. 2 changed files with 6 additions and 6 deletions.
    2 changes: 1 addition & 1 deletion actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,3 @@
    Forward:
    [#<Stats:0x18aecb4 @config={:stats=>true, :timeout=>1000}>, #<Memoizer:0x18aec8c @memos={}>, #<TimesOut:0x18aebb0 @config={:stats=>true, :timeout=>1000}>]
    [#<Stats:0x18aeebc @config={:stats=>true, :timeout=>1000}>, #<Memoizer:0x18aee94 @memos={}>, #<TimesOut:0x18aedb8 @config={:stats=>true, :timeout=>1000}>]
    "SELECT ... FROM ... FOR UPDATE ..."
    10 changes: 5 additions & 5 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -11,11 +11,11 @@ def initialize(*classes_or_objects, &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 construct_query(query_string)
    # build_proc(:construct_query) do
    # Query.new(query_string, connection_pool)
    # end.call query_string
    # end

    wrappable_method(:construct_query) do |query_string|
    p query_string
  14. Jacob Rothstein revised this gist Feb 16, 2010. 3 changed files with 7 additions and 24 deletions.
    26 changes: 2 additions & 24 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,25 +1,3 @@
    Forward:
    Instantiating Query Object
    Selecting SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ae96c>
    Did not timeout! Yay fast database!
    Measured select at 1.000101
    Instantiating Query Object
    Executing INSERT ... on #<Query:0x18ae458>
    Did not timeout! Yay fast database!
    Measured execute at 1.000099
    Executing INSERT ... on #<Query:0x18ae458>
    Did not timeout! Yay fast database!
    Measured execute at 1.000107

    Backward:
    Instantiating Query Object
    Selecting (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ada44>
    Measured select at 1.000102
    Did not timeout! Yay fast database!
    Instantiating Query Object
    Executing (reverse) INSERT ... on #<Query:0x18ad530>
    Measured execute at 1.000178
    Did not timeout! Yay fast database!
    Executing (reverse) INSERT ... on #<Query:0x18ad530>
    Measured execute at 1.000114
    Did not timeout! Yay fast database!
    [#<Stats:0x18aecb4 @config={:stats=>true, :timeout=>1000}>, #<Memoizer:0x18aec8c @memos={}>, #<TimesOut:0x18aebb0 @config={:stats=>true, :timeout=>1000}>]
    "SELECT ... FROM ... FOR UPDATE ..."
    4 changes: 4 additions & 0 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -17,6 +17,10 @@ def construct_query(query_string)
    end.call query_string
    end

    wrappable_method(:construct_query) do |query_string|
    p query_string
    end

    def query(query_string)
    build_proc(:query) do
    construct_query(query_string).query
    1 change: 1 addition & 0 deletions wrappable_methods.rb
    Original file line number Diff line number Diff line change
    @@ -14,6 +14,7 @@ def build_proc(method, &innermost)
    module ClassMethods
    def wrappable_method(method_name, &blk)
    define_method method_name do |*args|
    p self
    build_proc(method_name, &blk).call *args
    end
    end
  15. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 12 additions and 12 deletions.
    24 changes: 12 additions & 12 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,25 +1,25 @@
    Forward:
    Instantiating Query Object
    Selecting SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18aea34>
    Selecting SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ae96c>
    Did not timeout! Yay fast database!
    Measured select at 4.1e-05
    Measured select at 1.000101
    Instantiating Query Object
    Executing INSERT ... on #<Query:0x18ae520>
    Executing INSERT ... on #<Query:0x18ae458>
    Did not timeout! Yay fast database!
    Measured execute at 3.3e-05
    Executing INSERT ... on #<Query:0x18ae520>
    Measured execute at 1.000099
    Executing INSERT ... on #<Query:0x18ae458>
    Did not timeout! Yay fast database!
    Measured execute at 3.1e-05
    Measured execute at 1.000107

    Backward:
    Instantiating Query Object
    Selecting (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18adb0c>
    Measured select at 3.0e-05
    Selecting (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ada44>
    Measured select at 1.000102
    Did not timeout! Yay fast database!
    Instantiating Query Object
    Executing (reverse) INSERT ... on #<Query:0x18ad5f8>
    Measured execute at 3.1e-05
    Executing (reverse) INSERT ... on #<Query:0x18ad530>
    Measured execute at 1.000178
    Did not timeout! Yay fast database!
    Executing (reverse) INSERT ... on #<Query:0x18ad5f8>
    Measured execute at 1.9e-05
    Executing (reverse) INSERT ... on #<Query:0x18ad530>
    Measured execute at 1.000114
    Did not timeout! Yay fast database!
  16. Jacob Rothstein revised this gist Feb 16, 2010. 2 changed files with 14 additions and 14 deletions.
    20 changes: 10 additions & 10 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,25 +1,25 @@
    Forward:
    Instantiating Query Object
    Querying: SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ae980>
    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:0x18ae46c>
    Executing INSERT ... on #<Query:0x18ae520>
    Did not timeout! Yay fast database!
    Measured execute at 3.4e-05
    Executing: INSERT ... on #<Query:0x18ae46c>
    Measured execute at 3.3e-05
    Executing INSERT ... on #<Query:0x18ae520>
    Did not timeout! Yay fast database!
    Measured execute at 3.0e-05
    Measured execute at 3.1e-05

    Backward:
    Instantiating Query Object
    Querying: (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ada58>
    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:0x18ad544>
    Measured execute at 3.0e-05
    Executing (reverse) INSERT ... on #<Query:0x18ad5f8>
    Measured execute at 3.1e-05
    Did not timeout! Yay fast database!
    Executing: (reverse) INSERT ... on #<Query:0x18ad544>
    Measured execute at 2.6e-05
    Executing (reverse) INSERT ... on #<Query:0x18ad5f8>
    Measured execute at 1.9e-05
    Did not timeout! Yay fast database!
    8 changes: 4 additions & 4 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -47,14 +47,14 @@ def initialize(query_string, connection_pool)
    end

    def execute
    # sleep 1
    puts "Executing: #{@query_string} on #{self}"
    sleep 1
    puts "Executing #{@query_string} on #{self}"
    [1,2,3]
    end

    def query
    # sleep 1
    puts "Querying: #{@query_string} on #{self}"
    sleep 1
    puts "Selecting #{@query_string} on #{self}"
    1
    end
    end
  17. Jacob Rothstein revised this gist Feb 16, 2010. 3 changed files with 50 additions and 47 deletions.
    1 change: 1 addition & 0 deletions _program.rb
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,7 @@

    Onion.new Stats, Memoizer, TimesOut do
    config :stats => true, :timeout => 1_000
    connection_pool :whatever

    puts "Forward:"

    30 changes: 17 additions & 13 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,21 +1,25 @@
    Forward:
    Query cache miss
    Querying: SELECT ... FROM ... FOR UPDATE ...
    Instantiating Query Object
    Querying: SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ae980>
    Did not timeout! Yay fast database!
    Measured select at 1.006871
    Execute cache miss
    Executing: INSERT ...
    Measured select at 4.1e-05
    Instantiating Query Object
    Executing: INSERT ... on #<Query:0x18ae46c>
    Did not timeout! Yay fast database!
    Measured execute at 1.00025
    Measured execute at 6.0e-06
    Measured execute at 3.4e-05
    Executing: INSERT ... on #<Query:0x18ae46c>
    Did not timeout! Yay fast database!
    Measured execute at 3.0e-05

    Backward:
    Query cache miss
    Querying: (reverse) SELECT ... FROM ... FOR UPDATE ...
    Measured select at 1.000129
    Instantiating Query Object
    Querying: (reverse) SELECT ... FROM ... FOR UPDATE ... on #<Query:0x18ada58>
    Measured select at 3.0e-05
    Did not timeout! Yay fast database!
    Execute cache miss
    Executing: (reverse) INSERT ...
    Measured execute at 1.000101
    Instantiating Query Object
    Executing: (reverse) INSERT ... on #<Query:0x18ad544>
    Measured execute at 3.0e-05
    Did not timeout! Yay fast database!
    Executing: (reverse) INSERT ... on #<Query:0x18ad544>
    Measured execute at 2.6e-05
    Did not timeout! Yay fast database!
    66 changes: 32 additions & 34 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -10,63 +10,62 @@ def initialize(*classes_or_objects, &blk)
    instance_eval &blk
    end

    wrappable_method :query do |query_string|
    Query.new(query_string).query
    #refactor
    def construct_query(query_string)
    build_proc(:construct_query) do
    Query.new(query_string, connection_pool)
    end.call query_string
    end

    wrappable_method :execute do |query_string|
    Query.new(query_string).execute

    def query(query_string)
    build_proc(:query) do
    construct_query(query_string).query
    end.call query_string
    end

    def transaction
    yield
    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)
    if hash
    @config.merge! hash
    else
    @config
    end
    hash ? @config.merge!(hash) : @config
    end
    end

    class Query
    def initialize(query_string)
    @query_string = query_string
    # puts "Initializing new query object"
    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}"
    # sleep 1
    puts "Executing: #{@query_string} on #{self}"
    [1,2,3]
    end

    def query
    sleep 1
    puts "Querying: #{@query_string}"
    # sleep 1
    puts "Querying: #{@query_string} on #{self}"
    1
    end
    end

    class Memoizer
    def initialize
    @queries, @executions = {}, {}
    @memos = {}
    end

    def query(query_string)
    @queries[query_string] ||= begin
    puts 'Query cache miss'
    yield query_string
    end
    end

    def execute(query_string)
    @executions[query_string] ||= begin
    puts 'Execute cache miss'
    yield query_string
    end
    def construct_query(query_string)
    @memos[query_string] ||= yield query_string
    end
    end

    @@ -94,9 +93,8 @@ class TimesOut
    def initialize(onion) @config = onion.config end

    def query(query_string)
    value = Timeout.timeout(@config[:timeout]) { yield(query_string) }
    yield(query_string)
    puts 'Did not timeout! Yay fast database!'
    value
    end
    alias_method :execute, :query
    end
  18. Jacob Rothstein revised this gist Feb 16, 2010. 4 changed files with 20 additions and 30 deletions.
    4 changes: 2 additions & 2 deletions _program.rb
    Original file line number Diff line number Diff line change
    @@ -12,9 +12,9 @@
    end

    reverse!
    puts


    puts "\n\nBackward\n\n\n"
    puts "Backward:"

    transaction do
    query "(reverse) SELECT ... FROM ... FOR UPDATE ..."
    29 changes: 6 additions & 23 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,38 +1,21 @@
    Forward:
    Query cache miss
    Selecting: SELECT ... FROM ... FOR UPDATE ...
    Initializing new query object
    Querying: SELECT ... FROM ... FOR UPDATE ...
    Did not timeout! Yay fast database!
    Measured select (SELECT ... FROM ... FOR UPDATE ...) at 1.000472
    Measured select at 1.006871
    Execute cache miss
    Initializing new query object
    Executing: INSERT ...
    Did not timeout! Yay fast database!
    Measured execute (INSERT ...) at 1.000449
    Execute cache miss
    Initializing new query object
    Executing: INSERT ...
    Did not timeout! Yay fast database!
    Measured execute (INSERT ...) at 1.000682


    Backward

    Measured execute at 1.00025
    Measured execute at 6.0e-06

    Backward:
    Query cache miss
    Selecting: (reverse) SELECT ... FROM ... FOR UPDATE ...
    Initializing new query object
    Querying: (reverse) SELECT ... FROM ... FOR UPDATE ...
    Measured select ((reverse) SELECT ... FROM ... FOR UPDATE ...) at 1.000162
    Measured select at 1.000129
    Did not timeout! Yay fast database!
    Execute cache miss
    Initializing new query object
    Executing: (reverse) INSERT ...
    Measured execute ((reverse) INSERT ...) at 1.000119
    Measured execute at 1.000101
    Did not timeout! Yay fast database!
    Execute cache miss
    Initializing new query object
    Executing: (reverse) INSERT ...
    Measured execute ((reverse) INSERT ...) at 1.000149
    Did not timeout! Yay fast database!
    11 changes: 6 additions & 5 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,6 @@ def initialize(*classes_or_objects, &blk)
    end

    wrappable_method :query do |query_string|
    puts "Selecting: #{query_string}"
    Query.new(query_string).query
    end

    @@ -35,17 +34,19 @@ def config(hash = nil)
    class Query
    def initialize(query_string)
    @query_string = query_string
    puts "Initializing new query object"
    # puts "Initializing new query object"
    end

    def execute
    sleep 1
    puts "Executing: #{@query_string}"
    [1,2,3]
    end

    def query
    sleep 1
    puts "Querying: #{@query_string}"
    1
    end
    end

    @@ -62,7 +63,7 @@ def query(query_string)
    end

    def execute(query_string)
    @queries[query_string] ||= begin
    @executions[query_string] ||= begin
    puts 'Execute cache miss'
    yield query_string
    end
    @@ -76,14 +77,14 @@ def respond_to?(method) @config[:stats] and super end
    def query(query_string)
    time = Time.now
    returning yield(query_string) do
    puts "Measured select (#{query_string}) at #{Time.now - time}"
    puts "Measured select at #{Time.now - time}"
    end
    end

    def execute(query_string)
    time = Time.now
    returning yield(query_string) do
    puts "Measured execute (#{query_string}) at #{Time.now - time}"
    puts "Measured execute at #{Time.now - time}"
    end
    end
    end
    6 changes: 6 additions & 0 deletions util.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    class Object
    def returning(value)
    yield value
    value
    end
    end
  19. Jacob Rothstein revised this gist Feb 16, 2010. 3 changed files with 13 additions and 21 deletions.
    4 changes: 2 additions & 2 deletions _program.rb
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    Onion.new Stats, Memoizer, TimesOut do
    config :stats => true, :timeout => 1_000

    puts "FORWARD:"
    puts "Forward:"

    transaction do
    query "SELECT ... FROM ... FOR UPDATE ..."
    @@ -14,7 +14,7 @@
    reverse!


    puts "\n\n\nREVERSED ORDER\n\n\n"
    puts "\n\nBackward\n\n\n"

    transaction do
    query "(reverse) SELECT ... FROM ... FOR UPDATE ..."
    21 changes: 8 additions & 13 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -1,43 +1,38 @@
    FORWARD:
    BEGIN
    Forward:
    Query cache miss
    Selecting: SELECT ... FROM ... FOR UPDATE ...
    Initializing new query object
    Querying: SELECT ... FROM ... FOR UPDATE ...
    Did not timeout! Yay fast database!
    Measured select (SELECT ... FROM ... FOR UPDATE ...) at 1.00082
    Measured select (SELECT ... FROM ... FOR UPDATE ...) at 1.000472
    Execute cache miss
    Initializing new query object
    Executing: INSERT ...
    Did not timeout! Yay fast database!
    Measured execute (INSERT ...) at 1.000374
    Measured execute (INSERT ...) at 1.000449
    Execute cache miss
    Initializing new query object
    Executing: INSERT ...
    Did not timeout! Yay fast database!
    Measured execute (INSERT ...) at 1.000379
    COMMIT
    Measured execute (INSERT ...) at 1.000682


    Backward

    REVERSED ORDER


    BEGIN
    Query cache miss
    Selecting: (reverse) SELECT ... FROM ... FOR UPDATE ...
    Initializing new query object
    Querying: (reverse) SELECT ... FROM ... FOR UPDATE ...
    Measured select ((reverse) SELECT ... FROM ... FOR UPDATE ...) at 1.000131
    Measured select ((reverse) SELECT ... FROM ... FOR UPDATE ...) at 1.000162
    Did not timeout! Yay fast database!
    Execute cache miss
    Initializing new query object
    Executing: (reverse) INSERT ...
    Measured execute ((reverse) INSERT ...) at 1.000073
    Measured execute ((reverse) INSERT ...) at 1.000119
    Did not timeout! Yay fast database!
    Execute cache miss
    Initializing new query object
    Executing: (reverse) INSERT ...
    Measured execute ((reverse) INSERT ...) at 1.000162
    Measured execute ((reverse) INSERT ...) at 1.000149
    Did not timeout! Yay fast database!
    COMMIT
    9 changes: 3 additions & 6 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -4,9 +4,9 @@
    class Onion < Array #so simple it makes me cry
    include WrappableMethods

    def initialize(*quack, &blk)
    def initialize(*classes_or_objects, &blk)
    @config = {}
    super quack.map {|q| q.new self rescue q.new rescue q}
    super classes_or_objects.map {|q| q.new self rescue q.new rescue q}
    instance_eval &blk
    end

    @@ -20,9 +20,7 @@ def initialize(*quack, &blk)
    end

    def transaction
    puts "BEGIN"
    yield
    puts "COMMIT"
    end

    def config(hash = nil)
    @@ -53,8 +51,7 @@ def query

    class Memoizer
    def initialize
    @queries = {}
    @executions = {}
    @queries, @executions = {}, {}
    end

    def query(query_string)
  20. Jacob Rothstein revised this gist Feb 16, 2010. 3 changed files with 43 additions and 20 deletions.
    22 changes: 16 additions & 6 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -3,14 +3,19 @@ BEGIN
    Query cache miss
    Selecting: SELECT ... FROM ... FOR UPDATE ...
    Initializing new query object
    Querying: SELECT ... FROM ... FOR UPDATE ...
    Did not timeout! Yay fast database!
    Measured select (SELECT ... FROM ... FOR UPDATE ...) at 1 second
    Measured select (SELECT ... FROM ... FOR UPDATE ...) at 1.00082
    Execute cache miss
    Initializing new query object
    Executing: INSERT ...
    Did not timeout! Yay fast database!
    Measured execute (INSERT ...) at 1.000374
    Execute cache miss
    Initializing new query object
    Executing: INSERT ...
    Did not timeout! Yay fast database!
    Measured select (INSERT ...) at 1 second
    Measured select (INSERT ...) at 1 second
    Measured execute (INSERT ...) at 1.000379
    COMMIT


    @@ -22,12 +27,17 @@ BEGIN
    Query cache miss
    Selecting: (reverse) SELECT ... FROM ... FOR UPDATE ...
    Initializing new query object
    Measured select ((reverse) SELECT ... FROM ... FOR UPDATE ...) at 1 second
    Querying: (reverse) SELECT ... FROM ... FOR UPDATE ...
    Measured select ((reverse) SELECT ... FROM ... FOR UPDATE ...) at 1.000131
    Did not timeout! Yay fast database!
    Execute cache miss
    Executing: (reverse) INSERT ...
    Initializing new query object
    Measured select ((reverse) INSERT ...) at 1 second
    Executing: (reverse) INSERT ...
    Measured execute ((reverse) INSERT ...) at 1.000073
    Did not timeout! Yay fast database!
    Execute cache miss
    Initializing new query object
    Executing: (reverse) INSERT ...
    Measured execute ((reverse) INSERT ...) at 1.000162
    Did not timeout! Yay fast database!
    COMMIT
    39 changes: 26 additions & 13 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,5 @@
    require 'wrappable_methods'
    class Object
    def returning(value)
    yield value
    value
    end
    end
    require 'util'

    class Onion < Array #so simple it makes me cry
    include WrappableMethods
    @@ -17,12 +12,11 @@ def initialize(*quack, &blk)

    wrappable_method :query do |query_string|
    puts "Selecting: #{query_string}"
    Query.new(query_string)
    Query.new(query_string).query
    end

    wrappable_method :execute do |query_string|
    puts "Executing: #{query_string}"
    Query.new(query_string)
    Query.new(query_string).execute
    end

    def transaction
    @@ -41,7 +35,20 @@ def config(hash = nil)
    end

    class Query
    def initialize(query_string) puts "Initializing new query object" end
    def initialize(query_string)
    @query_string = query_string
    puts "Initializing new query object"
    end

    def execute
    sleep 1
    puts "Executing: #{@query_string}"
    end

    def query
    sleep 1
    puts "Querying: #{@query_string}"
    end
    end

    class Memoizer
    @@ -70,12 +77,18 @@ 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 (#{query_string}) at 1 second"
    puts "Measured select (#{query_string}) at #{Time.now - time}"
    end
    end

    def execute(query_string)
    time = Time.now
    returning yield(query_string) do
    puts "Measured execute (#{query_string}) at #{Time.now - time}"
    end
    end

    alias_method :execute, :query
    end


    2 changes: 1 addition & 1 deletion wrappable_methods.rb
    Original file line number Diff line number Diff line change
    @@ -18,4 +18,4 @@ def wrappable_method(method_name, &blk)
    end
    end
    end
    end
    end
  21. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -80,10 +80,12 @@ def query(query_string)


    class TimesOut
    def initialize(onion) @config = onion.config end

    def query(query_string)
    returning yield(query_string) do
    puts 'Did not timeout! Yay fast database!'
    end
    value = Timeout.timeout(@config[:timeout]) { yield(query_string) }
    puts 'Did not timeout! Yay fast database!'
    value
    end
    alias_method :execute, :query
    end
  22. Jacob Rothstein revised this gist Feb 16, 2010. 1 changed file with 33 additions and 0 deletions.
    33 changes: 33 additions & 0 deletions actual_output.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,33 @@
    FORWARD:
    BEGIN
    Query cache miss
    Selecting: SELECT ... FROM ... FOR UPDATE ...
    Initializing new query object
    Did not timeout! Yay fast database!
    Measured select (SELECT ... FROM ... FOR UPDATE ...) at 1 second
    Execute cache miss
    Executing: INSERT ...
    Initializing new query object
    Did not timeout! Yay fast database!
    Measured select (INSERT ...) at 1 second
    Measured select (INSERT ...) at 1 second
    COMMIT



    REVERSED ORDER


    BEGIN
    Query cache miss
    Selecting: (reverse) SELECT ... FROM ... FOR UPDATE ...
    Initializing new query object
    Measured select ((reverse) SELECT ... FROM ... FOR UPDATE ...) at 1 second
    Did not timeout! Yay fast database!
    Execute cache miss
    Executing: (reverse) INSERT ...
    Initializing new query object
    Measured select ((reverse) INSERT ...) at 1 second
    Did not timeout! Yay fast database!
    Did not timeout! Yay fast database!
    COMMIT
  23. Jacob Rothstein revised this gist Feb 16, 2010. 4 changed files with 98 additions and 53 deletions.
    24 changes: 24 additions & 0 deletions _program.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    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
    25 changes: 25 additions & 0 deletions expected_output.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    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!
    81 changes: 28 additions & 53 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -1,29 +1,11 @@
    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
    require 'wrappable_methods'
    class Object
    def returning(value)
    yield value
    value
    end
    end

    class Query
    def initialize(query_string) puts "Initializing new query object" end
    end

    class Onion < Array #so simple it makes me cry
    include WrappableMethods

    @@ -58,25 +40,39 @@ def config(hash = nil)
    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 ||= {}
    @queries[query_string] ||= begin
    puts 'Cache miss'
    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
    alias_method :execute, :query
    end

    class Stats
    def initialize(onion) @config = onion.config end
    def respond_to?(method) @config[:stats] and super end

    def query(query_string)
    retval = yield query_string
    puts "Measured select (#{query_string}) at 1 second"
    retval
    returning yield(query_string) do
    puts "Measured select (#{query_string}) at 1 second"
    end
    end

    alias_method :execute, :query
    @@ -85,33 +81,12 @@ def query(query_string)

    class TimesOut
    def query(query_string)
    retval = yield query_string
    puts 'Did not timeout! Yay fast database!'
    retval
    returning yield(query_string) do
    puts 'Did not timeout! Yay fast database!'
    end
    end
    alias_method :execute, :query
    end

    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

    21 changes: 21 additions & 0 deletions wrappable_methods.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    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
  24. Jacob Rothstein revised this gist Feb 16, 2010. 8 changed files with 128 additions and 246 deletions.
    11 changes: 11 additions & 0 deletions _README.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    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.

    70 changes: 0 additions & 70 deletions a_program.rb
    Original file line number Diff line number Diff line change
    @@ -1,70 +0,0 @@
    # 2010 Modularity Olympics
    # ===
    # This is a contest, open to programming languages from all over the world, to write modular and
    # extensible code to solve the following problem: Implement a service that can run queries on a database.
    # A programmer without control over the source-code of that service must be able to later add
    # enhancements such as statistics collecting, timeouts, memoization, and so forth. There are a few
    # requirements:
    # 1. the "enhancements" must be specified in a configuration object which is consumed at run-time
    # (e.g., it could be based on user-input).
    # 2. The enhancements are ordered (stats collecting wraps timeouts, not the other way around) but
    # it must be possible to reverse the order of the enhancements at run-time.
    # 3. The enhancements must be "surgical" and not "global". That is, it must be possible to simultaneously
    # have two query services, one reversed and one not reversed, and even have a query service without
    # any enhancements.
    # ===
    # You can use any programming language you like (functional, OO, stack-based) and any techniques you like.
    # Your main goal should be clarity and flexibility. Terseness is valuable where it adds to clarity.
    # Creative and unusual solutions are welcome, though.

    config = [
    [:memoizing, []],
    [:timing_out, [2.seconds]],
    [:stats_collecting, [Stats.new]]
    ]
    query_factory = config.inject(Query) do |factory, (decorator_name, settings)|
    "#{decorator_name.to_s.classify}QueryFactory".constantize.new(factory, *settings)
    end

    def run_test(query_evaluator)
    query_evaluator.transaction do |t|
    t.select("SELECT ... FROM ... FOR UPDATE ...")
    t.execute("INSERT ...")
    t.execute("INSERT ...")
    end
    end

    puts "Forward:"
    run_test(QueryEvaluator.new(ConnectionPool.new(20), query_factory))
    puts
    puts "Backward:"
    run_test(QueryEvaluator.new(ConnectionPool.new(20), ReversingQueryFactory.new(query_factory)))

    # Your program must produce exactly this output. Note the reversing order of the enhancements
    # and the memoization of Query instantiation:
    #
    # 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!
    82 changes: 0 additions & 82 deletions foundation.rb
    Original file line number Diff line number Diff line change
    @@ -1,82 +0,0 @@
    class ConnectionPool
    def initialize(size)
    @size = size
    end

    def with_connection
    yield Object.new
    end
    end

    class Stats
    def measure(name)
    result = nil
    bm = Benchmark.measure { result = yield }
    puts "Measured #{name} at #{"%.2f" % bm.real} seconds"
    result
    end
    end

    class Query
    def initialize(connection, query_string, *args)
    @connection = connection
    @query_string = query_string
    @args = args
    end

    def select
    sleep 1
    puts "Selecting #{@query_string} on #{@connection}"
    [1, 2, 3]
    end

    def execute
    sleep 1
    puts "Executing #{@query_string} on #{@connection}"
    1
    end
    end

    class QueryEvaluator
    def initialize(connection_pool, query_factory = Query)
    @query_factory = query_factory
    @connection_pool = connection_pool
    end

    def select(query_string, *args)
    @connection_pool.with_connection do |connection|
    @query_factory.new(connection, query_string, *args).select
    end
    end

    def execute(query_string, *args)
    @connection_pool.with_connection do |connection|
    @query_factory.new(connection, query_string, *args).execute
    end
    end

    def transaction
    @connection_pool.with_connection do |connection|
    yield TransactionalQueryEvaluator.new(connection, @query_factory)
    end
    end
    end

    class TransactionalQueryEvaluator
    def initialize(connection, query_factory)
    @connection = connection
    @query_factory = query_factory
    end

    def select(query_string, *args)
    @query_factory.new(@connection, query_string, *args).select
    end

    def execute(query_string, *args)
    @query_factory.new(@connection, query_string, *args).execute
    end

    def transaction
    yield self
    end
    end
    12 changes: 0 additions & 12 deletions memoizing_query_factory.rb
    Original file line number Diff line number Diff line change
    @@ -1,12 +0,0 @@
    class MemoizingQueryFactory
    def initialize(query_factory)
    @memo = Hash.new do |h, k|
    puts "Instantiating Query Object"
    h[k] = query_factory.new(*k)
    end
    end

    def new(connection, query_string, *args)
    @memo[[connection, query_string, args]]
    end
    end
    117 changes: 117 additions & 0 deletions onion.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,117 @@
    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

    class Query
    def initialize(query_string) puts "Initializing new query object" 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 Memoizer
    def query(query_string)
    @queries ||= {}
    @queries[query_string] ||= begin
    puts 'Cache miss'
    yield query_string
    end
    end
    alias_method :execute, :query
    end

    class Stats
    def initialize(onion) @config = onion.config end
    def respond_to?(method) @config[:stats] and super end

    def query(query_string)
    retval = yield query_string
    puts "Measured select (#{query_string}) at 1 second"
    retval
    end

    alias_method :execute, :query
    end


    class TimesOut
    def query(query_string)
    retval = yield query_string
    puts 'Did not timeout! Yay fast database!'
    retval
    end
    alias_method :execute, :query
    end

    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

    38 changes: 0 additions & 38 deletions query_proxy.rb
    Original file line number Diff line number Diff line change
    @@ -1,38 +0,0 @@
    class QueryProxy
    attr_accessor :query

    def initialize(query)
    @query = query
    end

    def select
    delegate("select") { @query.select }
    end

    def execute
    delegate("execute") { @query.execute }
    end

    def reverse
    case @query
    when QueryProxy
    reverse = @query.reverse
    inner = @query.query
    @query = inner
    reverse.query = self
    reverse
    else
    self
    end
    end
    end

    class ReversingQueryFactory
    def initialize(query_factory)
    @query_factory = query_factory
    end

    def new(connection, query_factory, *args)
    @query_factory.new(connection, query_factory, *args).reverse
    end
    end
    21 changes: 0 additions & 21 deletions stats_collecting_query.rb
    Original file line number Diff line number Diff line change
    @@ -1,21 +0,0 @@
    class StatsCollectingQueryFactory
    def initialize(query_factory, stats)
    @query_factory = query_factory
    @stats = stats
    end

    def new(connection, query_string, *args)
    StatsCollectingQuery.new(@query_factory.new(connection, query_string, *args), @stats)
    end
    end

    class StatsCollectingQuery < QueryProxy
    def initialize(query, stats)
    super(query)
    @stats = stats
    end

    def delegate(method)
    @stats.measure(method) { yield }
    end
    end
    23 changes: 0 additions & 23 deletions timing_out_query.rb
    Original file line number Diff line number Diff line change
    @@ -1,23 +0,0 @@
    class TimingOutQueryFactory
    def initialize(query_factory, timeout)
    @query_factory = query_factory
    @timeout = timeout
    end

    def new(connection, query_string, *args)
    TimingOutQuery.new(@query_factory.new(connection, query_string, *args), @timeout)
    end
    end

    class TimingOutQuery < QueryProxy
    def initialize(query, timeout)
    super(query)
    @timeout = timeout
    end

    def delegate(method)
    result = Timeout.timeout(@timeout.to_i) { yield }
    puts "Did not timeout! Yay fast database!"
    result
    end
    end
  25. nkallen revised this gist Feb 16, 2010. 2 changed files with 7 additions and 7 deletions.
    8 changes: 4 additions & 4 deletions a_program.rb
    Original file line number Diff line number Diff line change
    @@ -49,10 +49,10 @@ def run_test(query_evaluator)
    # Did not timeout! Yay fast database!
    # Measured select at 1.00 seconds
    # Instantiating Query Object
    # Selecting INSERT ... on #<Object:0x11f6750>
    # Executing INSERT ... on #<Object:0x11f6750>
    # Did not timeout! Yay fast database!
    # Measured select at 1.00 seconds
    # Selecting INSERT ... on #<Object:0x11f6750>
    # Executing INSERT ... on #<Object:0x11f6750>
    # Did not timeout! Yay fast database!
    # Measured select at 1.00 seconds
    #
    @@ -62,9 +62,9 @@ def run_test(query_evaluator)
    # Measured select at 1.00 seconds
    # Did not timeout! Yay fast database!
    # Instantiating Query Object
    # Selecting INSERT ... on #<Object:0x11f4ea0>
    # Executing INSERT ... on #<Object:0x11f4ea0>
    # Measured select at 1.00 seconds
    # Did not timeout! Yay fast database!
    # Selecting INSERT ... on #<Object:0x11f4ea0>
    # Executing INSERT ... on #<Object:0x11f4ea0>
    # Measured select at 1.00 seconds
    # Did not timeout! Yay fast database!
    6 changes: 3 additions & 3 deletions foundation.rb
    Original file line number Diff line number Diff line change
    @@ -67,15 +67,15 @@ def initialize(connection, query_factory)
    @connection = connection
    @query_factory = query_factory
    end

    def select(query_string, *args)
    @query_factory.new(@connection, query_string, *args).select
    end

    def execute(query_string, *args)
    @query_factory.new(@connection, query_string, *args).execute
    end

    def transaction
    yield self
    end
  26. nkallen revised this gist Feb 16, 2010. 2 changed files with 2 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion a_program.rb
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,7 @@

    config = [
    [:memoizing, []],
    [:timing_out, [1.second]],
    [:timing_out, [2.seconds]],
    [:stats_collecting, [Stats.new]]
    ]
    query_factory = config.inject(Query) do |factory, (decorator_name, settings)|
    2 changes: 1 addition & 1 deletion timing_out_query.rb
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@ def initialize(query, timeout)
    end

    def delegate(method)
    result = Timeout.timeout(@timeout) { yield }
    result = Timeout.timeout(@timeout.to_i) { yield }
    puts "Did not timeout! Yay fast database!"
    result
    end
  27. nkallen revised this gist Feb 16, 2010. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions stats_collecting_query.rb
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,11 @@
    class StatsCollectingQueryFactory
    def initialize(query_factory, timeout)
    def initialize(query_factory, stats)
    @query_factory = query_factory
    @timeout = timeout
    @stats = stats
    end

    def new(connection, query_string, *args)
    StatsCollectingQuery.new(@query_factory.new(connection, query_string, *args), @timeout)
    StatsCollectingQuery.new(@query_factory.new(connection, query_string, *args), @stats)
    end
    end

    @@ -18,4 +18,4 @@ def initialize(query, stats)
    def delegate(method)
    @stats.measure(method) { yield }
    end
    end
    end
  28. nkallen revised this gist Feb 16, 2010. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions memoizing_query_factory.rb
    Original file line number Diff line number Diff line change
    @@ -2,11 +2,11 @@ class MemoizingQueryFactory
    def initialize(query_factory)
    @memo = Hash.new do |h, k|
    puts "Instantiating Query Object"
    h = query_factory.new(*k)
    h[k] = query_factory.new(*k)
    end
    end

    def new(connection, query_string, *args)
    @memo[[connection, query_string, args]]
    end
    end
    end
  29. nkallen revised this gist Feb 16, 2010. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions a_program.rb
    Original file line number Diff line number Diff line change
    @@ -30,6 +30,7 @@ def run_test(query_evaluator)
    query_evaluator.transaction do |t|
    t.select("SELECT ... FROM ... FOR UPDATE ...")
    t.execute("INSERT ...")
    t.execute("INSERT ...")
    end
    end

  30. nkallen revised this gist Feb 16, 2010. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion a_program.rb
    Original file line number Diff line number Diff line change
    @@ -14,7 +14,7 @@
    # any enhancements.
    # ===
    # You can use any programming language you like (functional, OO, stack-based) and any techniques you like.
    # Your main goal should be clarity and flexibility. Terseness is valuable where it ads to clarity.
    # Your main goal should be clarity and flexibility. Terseness is valuable where it adds to clarity.
    # Creative and unusual solutions are welcome, though.

    config = [