Ruby API development: module/class structure to set instance variables via do/block
In this blog post I’ll show an example Ruby module/class structure to allow users to set configuration settings via a do/block, like:
Api.config do |c|
c.first_name = 'Eric'
c.last_name = 'London'
end
The following code shows how a simple module/class structure could be defined to allow end-users to set instance variables on a module’s singleton class:
#!/usr/bin/env ruby
module Api
# mixes in module methods as class methods
extend self
def config
if block_given?
# pass block to settings singleton
yield Settings
else
# return instance variables
vars = {}
Settings.instance_variables.each do |key|
vars[key] = Settings.instance_variable_get key
end
vars
end
end
end
module Api
class Settings
# dynamic method call
def self.method_missing(method, *args, &block)
# check for assignment methods
if method.to_s =~ /=$/
key = method.to_s.gsub(/=$/,'')
set_var(key, args.first)
else
super
end
end
# or, an explicit method list:
# def self.first_name=(value)
# # blah
# end
class << self
private
def set_var(key, value)
self.instance_variable_set "@#{key}".to_s, value
end
end
end
end
Usage and output:
Api.config do |c|
c.first_name = 'Eric'
c.last_name = 'London'
end
require 'pp'
pp Api.config
{:@first_name=>"Eric", :@last_name=>"London"}
Update 2013-10-20:
Below is another example that allows you to call methods on the singleton class, instead of using the assignment operator with a block parameter; as such:
Api.config do
first_name 'Eric'
last_name 'London'
end
Example code:
#!/usr/bin/env ruby
module Api
# mixes in module methods as class methods
extend self
def config(*args, &block)
if block_given?
return Settings.instance_exec &block
else
# return instance variables
vars = {}
Settings.instance_variables.each do |key|
vars[key] = Settings.instance_variable_get key
end
vars
end
end
end
module Api
class Settings
# dynamic method call
def self.method_missing(method, *args, &block)
if [
:first_name,
:last_name,
].include? method
set_var method, args.first
else
super
end
end
class << self
private
def set_var(key, value)
self.instance_variable_set "@#{key}".to_s, value
end
end
end
end
Usage and output:
Api.config do
first_name 'Eric'
last_name 'London'
end
require 'pp'
pp Api.config
{:@first_name=>"Eric", :@last_name=>"London"}