3. domain specific
language TL;DR
➡ Customized to specific problem domain
➡ Evaluated in context of the domain
➡ Helps creating ubiquitous language
4. internal vs. external
„Internal DSLs are particular ways of using a
host language to give the host language the
feel of a particular language.“
„External DSLs have their own custom syntax
and you write a full parser to process them.“
5. internal vs. external
„Internal DSLs are particular ways of using a
host language to give the host language the
feel of a particular language.“
„External DSLs have their own custom syntax
and you write a full parser to process them.“
6. internal vs. external
„Internal DSLs are particular ways of using a
host language to give the host language the
feel of a particular language.“
er
wl
Fo
tin
ar
„External DSLs have their own custom syntax
M
and you write a full parser to process them.“
14. Method Params
link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
def doit_with(some, parameters)
# does it
end
15. Method Params
link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
def doit_with(some, parameters) def doit_with(mandatory, optional=nil, parameters=nil)
# does it # does it
end end
16. Method Params
link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
def doit_with(some, parameters) def doit_with(mandatory, optional=nil, parameters=nil)
# does it # does it
end end
def doit_with(options={:named => 'parameter'})
# does it
end
17. Method Params
link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
def doit_with(some, parameters) def doit_with(mandatory, optional=nil, parameters=nil)
# does it # does it
end end
def doit_with(options={:named => 'parameter'}) def doit_with(mandatory, *optional_paramters)
# does it # does it
end end
18. Method Params
link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
def doit_with(some, parameters) def doit_with(mandatory, optional=nil, parameters=nil)
# does it # does it
end end
def doit_with(options={:named => 'parameter'}) def doit_with(mandatory, *optional_paramters)
# does it # does it
end end
def doit_with(*args)
options = args.pop if Hash === args.last
name = args.first
# [...]
raise ArgumentError, 'Bad combination of parameters' unless args.size == 1
# does it
end
20. Blocks
Twitter.configure do |config|
config.consumer_key = YOUR_CONSUMER_KEY
config.consumer_secret = YOUR_CONSUMER_SECRET
end
def doit_with(&explicit_block)
explicit_block.call
end
21. Blocks
Twitter.configure do |config|
config.consumer_key = YOUR_CONSUMER_KEY
config.consumer_secret = YOUR_CONSUMER_SECRET
end
def doit_with(&explicit_block) def doit_with_implicit_block
explicit_block.call yield if block_given?
end end
22. Blocks
Twitter.configure do |config|
config.consumer_key = YOUR_CONSUMER_KEY
config.consumer_secret = YOUR_CONSUMER_SECRET
end
def doit_with(&explicit_block) def doit_with_implicit_block
explicit_block.call yield if block_given?
end end
def doit_with_block_for_configuration
yield self
end
23. Blocks
Twitter.configure do |config|
config.consumer_key = YOUR_CONSUMER_KEY
config.consumer_secret = YOUR_CONSUMER_SECRET
end
def doit_with(&explicit_block) def doit_with_implicit_block
explicit_block.call yield if block_given?
end end
def doit_with_block_for_configuration
yield self
end
def doit_with(&arity_sensitive_block)
arity_sensitive_block.call 'foo', 'bar' if
arity_sensitive_block.arity == 2
end
25. Instance Eval
Twitter.configure do |config|
consumer_key = YOUR_CONSUMER_KEY
config.consumer_key = YOUR_CONSUMER_KEY
consumer_secret = YOUR_CONSUMER_SECRET
config.consumer_secret = YOUR_CONSUMER_SECRET
end
26. Instance Eval
Twitter.configure do |config|
consumer_key = YOUR_CONSUMER_KEY
config.consumer_key = YOUR_CONSUMER_KEY
consumer_secret = YOUR_CONSUMER_SECRET
config.consumer_secret = YOUR_CONSUMER_SECRET
end
def doit_with(&instance_eval_block)
if instance_eval_block.arity == 0
# could also be class_eval, module_eval
self.instance_eval(&instance_eval_block)
else
instance_eval_block.call self
end
end
29. Method Missing
Client.find_by_firstname_and_lastname_and_gender('uschi', 'mueller', :female)
METHOD_PATTERN = /^find_by_/
METHOD_PATTERN = /^find_by_/
def method_missing(method, *args, &block)
if method.to_s =~ METHOD_PATTERN
def method_missing(method, *args, &block)
# finder magic
if method.to_s =~ METHOD_PATTERN
else
# finder magic
super
else
end
end super
end
end respond_to?(method)
def
return true if method =~ METHOD_PATTERN
super
end
31. Code Generation
client.firstname = 'uschi'
puts client.lastname
def generate(name)
self.class.send(:define_method, name){puts name}
eval("def #{name}() puts '#{name}' end")
def some_method(name)
puts name
end
# [...]
end
32. Makros
class Client < ActiveRecord::Base
has_one :address
has_many :orders
belongs_to :role
end
33. Makros
class Client < ActiveRecord::Base
has_one :address
has_many :orders
belongs_to :role
end
class << self
def has_something
# macro definition
end
end