lib/db_mod/statements/statement.rb
require_relative 'parameters'
module DbMod
module Statements
# Provides the +def_statement+ function which allows
# {DbMod} modules to declare SQL statements that can
# then be executed later using a specially defined
# instance method.
#
# To declare prepared statements, see +def_prepared+
# in {DbMod::Statements::Prepared}.
#
# +def_statement+ accepts two parameters:
# * *name* [Symbol]: The name that will be given to the
# method that can be used to execute the SQL statement
# and return the result.
# * *sql* [String]: The SQL statement that shoul be executed
# when the method is called. Parameters may be declared
# using the $ symbol followed by a number +($1, $2, $3)+ or
# a name +($one, $two, $under_scores)+. The two styles may
# not be mixed in the same statement. The defined function
# can then be passed parameters that will be used to fill
# in the statement before execution.
#
# module MyModule
# include DbMod
#
# def_prepared :my_prepared, <<-SQL
# SELECT *
# FROM stuff
# WHERE a = $1 AND b = $2
# SQL
#
# def_prepared :my_named_prepared, <<-SQL
# SELECT *
# FROM stuff
# WHERE a = $a AND b = $b
# SQL
# end
#
# include MyModule
# db_connect db: 'mydb'
# my_prepared(1,2)
# my_named_prepared(a: 1, b: 2)
module Statement
# Defines a module-specific +def_statement+ function
# for a module that has just had {DbMod} included.
#
# @param mod [Module] module with {DbMod} included
# @see DbMod.included
def self.setup(mod)
Statement.define_def_statement(mod)
end
private
# Add a +def_statement+ method definition to a module.
# This method allows modules to declare SQL statements
# that can be accessed via an instance method with
# arbitrary name.
#
# @param mod [Module] a module with {DbMod} included
# @raise ArgumentError if there is a problem parsing
# method parameters from the SQL statement
def self.define_def_statement(mod)
class << mod
define_method(:def_statement) do |name, sql, &block|
sql = sql.dup
name = name.to_sym
params = Parameters.parse_params! sql
Statement.define_statement_method(self, name, params, sql, &block)
end
end
end
# Define a method in the given module with the given name
# and parameters, that will call the given sql statement
# and return the results.
#
# @param mod [Module] module declaring the method
# @param name [Symbol] method name
# @param params [Fixnum,Array<Symbol>]
# expected parameter count, or a list of argument names.
# An empty array produces a no-argument method.
# @param sql [String] sql statement to execute
# @param block [Proc] A dsl block may be passed, which will be evaluated
# using a {Configuration::MethodConfiguration} object as scope
# @see Configuration.def_configurable
def self.define_statement_method(mod, name, params, sql, &block)
if params == []
Configuration.def_configurable(mod, name, ->(*) { query sql }, &block)
else
method = ->(*args) { conn.exec_params(sql, args) }
Configuration.def_configurable(mod, name, method, params, &block)
end
end
end
end
end