Skip to content

Instantly share code, notes, and snippets.

@josevalim
Created June 27, 2009 08:24
Show Gist options
  • Save josevalim/136948 to your computer and use it in GitHub Desktop.
Save josevalim/136948 to your computer and use it in GitHub Desktop.
# PROBLEM
#
# In order to provide agnosticism, a controller generator should know
# which methods the ORM make available to find, save, update, etc.
#
# SOLUTION #1
#
# ORM should expose a generator builder that tell us what to do.
# This builder exposes AR methods and return custom implementations.
#
module ActiveRecord
module Generators
class Builder
# Builder.find("Foo", "1") #=> "Foo.find(1)"
def self.find(class_name, param)
"#{class_name}.find(#{param})"
end
def initialize(name)
@name = name
end
# builder = Builder.new("@foo")
# builder.save #=> "@foo.save"
def save
"#{name}.save"
end
end
end
end
# SOLUTION #2
#
# Unfortunately, SOLUTION #1 does not solve test frameworks problem.
# Since some follow a mock approach, the builder above does not provide
# structured information. For example, there is no way to know what is
# the save method for Sequel ORM without reverse engineering the string.
#
# Another approach would be define a simple syntax to specify how any
# of these methods should be built.
#
module ActiveRecord
module Generators
class Builder
# Invoking:
#
# Builder.construct_find("Foo", "1")
#
# Returns:
#
# [ "Foo", [ :find, [ 1 ] ] ]
#
def self.construct_find(class_name, param)
[ class_name, [ :find, [ param ] ] ]
end
# Method missing will then be responsible for creating the string.
# If the user do "Builder.find", method missing will invoke
# construct_find and build the string.
#
# This is needed to deal with Ruby different ways to invoke a method:
#
# 1) a + b
# 2) a[b]
# 3) a[b] = c
# 4) a.method
# 5) a.method = c
#
# Builders returns arrays similar do s-expressions:
#
# ["Foo", [:find, [1]]] #=> Foo.find(1)
# ["Foo", [:find, [1,2]]] #=> Foo.find(1, 2)
#
# But it's somehow simplified because expressions can be given as arg:
#
# ["Foo", [:find, ["1 + 2"]]] #=> Foo.find(1 + 2)
#
# Consequently, strings must be inspected:
#
# ["Foo", [:find, ["foo".inspect]]] #=> Foo.find("foo")
#
# More examples:
#
# ["Foo", [:where, [1], :all]] #=> Foo.where(1).all
#
# And blocks? Since we do not need to know the code inside the block
# we just need to specify it and give hard coded content:
#
# ["Foo", [:build, [ [:block, "{ |b| b.where(1) }"] ], :all]] #=> Foo.build{ |b| b.where(1) }.all
#
def method_missing(method, *args, &block)
# magic
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment