Writing a DSL in Ruby    Why Ruby makes it soooo easy!
DSL!!!!11one
domain specific      language TL;DR➡ Customized to specific problem domain➡ Evaluated in context of the domain➡ Helps creati...
internal vs. external„Internal DSLs are particular ways of using a host language to give the host language the        feel...
internal vs. external„Internal DSLs are particular ways of using a host language to give the host language the        feel...
internal vs. external„Internal DSLs are particular ways of using a host language to give the host language the        feel...
Rails, collection of DSLs          • Routes          • Tag- / Form-Helpers          • Builder          • Associations, Mig...
all about API designRuby makes it easy to create DSL:           • expressive & concise syntax           • open classes    ...
Gregory Brown   Russ Olsen
DSL by EXAMPLE
Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, para...
Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, para...
Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, para...
Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, para...
Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, para...
BlocksTwitter.configure do |config|  config.consumer_key = YOUR_CONSUMER_KEY  config.consumer_secret = YOUR_CONSUMER_SECRE...
Blocks        Twitter.configure do |config|          config.consumer_key = YOUR_CONSUMER_KEY          config.consumer_secr...
Blocks        Twitter.configure do |config|          config.consumer_key = YOUR_CONSUMER_KEY          config.consumer_secr...
Blocks        Twitter.configure do |config|          config.consumer_key = YOUR_CONSUMER_KEY          config.consumer_secr...
Blocks        Twitter.configure do |config|          config.consumer_key = YOUR_CONSUMER_KEY          config.consumer_secr...
Instance EvalTwitter.configure do |config|  config.consumer_key = YOUR_CONSUMER_KEY  config.consumer_secret = YOUR_CONSUME...
Instance EvalTwitter.configure do |config|  consumer_key = YOUR_CONSUMER_KEY  config.consumer_key = YOUR_CONSUMER_KEY  con...
Instance EvalTwitter.configure do |config|  consumer_key = YOUR_CONSUMER_KEY  config.consumer_key = YOUR_CONSUMER_KEY  con...
Method MissingClient.find_by_firstname_and_lastname_and_gender(uschi, mueller, :female)
Method MissingClient.find_by_firstname_and_lastname_and_gender(uschi, mueller, :female)            METHOD_PATTERN = /^find...
Method MissingClient.find_by_firstname_and_lastname_and_gender(uschi, mueller, :female)            METHOD_PATTERN = /^find...
Code Generation    client.firstname = uschi    puts client.lastname
Code Generation             client.firstname = uschi             puts client.lastname def generate(name)   self.class.send...
Makrosclass Client < ActiveRecord::Base  has_one :address  has_many :orders  belongs_to :roleend
Makrosclass Client < ActiveRecord::Base  has_one :address  has_many :orders  belongs_to :roleend     class << self       d...
Hooks$ ruby simple_test.rb
Hooks$ ruby simple_test.rb at_exit do   # shut down code end
Hooks                 $ ruby simple_test.rb                  at_exit do                    # shut down code               ...
Hooks                      $ ruby simple_test.rb                       at_exit do                         # shut down code...
Core Extensions     text.blank?
Core Extensions               text.blank? class Object   def blank?     nil? || (respond_to?(:empty?) && empty?)   end end
Core Extensions               text.blank? class Object   def blank?     nil? || (respond_to?(:empty?) && empty?)   end unl...
Core Extensions                  text.blank?module MyGem     class Object  module CoreExtensions    module blank?       de...
Ruby techniques✓Method Params    ✓Makros✓Blocks           ✓Code generation✓Instance Eval    ✓Hooks✓Method Missing   ✓Core ...
External DSLRuby Tools: • Switch Cases • Regular ExpressionsRuby Parser: • Treetop • RACC
Links★ Eloquent Ruby★ Ruby Best Practices★ Domain Specific Languages★ Rebuil★ Treetop
Over and Out
Over and Out
Dsl
Dsl
Upcoming SlideShare
Loading in...5
×

Dsl

1,057

Published on

Writing a DSL in Ruby

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,057
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
31
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Dsl

    1. 1. Writing a DSL in Ruby Why Ruby makes it soooo easy!
    2. 2. DSL!!!!11one
    3. 3. domain specific language TL;DR➡ Customized to specific problem domain➡ Evaluated in context of the domain➡ Helps creating ubiquitous language
    4. 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 syntaxand you write a full parser to process them.“
    5. 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 syntaxand you write a full parser to process them.“
    6. 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 Mand you write a full parser to process them.“
    7. 7. Rails, collection of DSLs • Routes • Tag- / Form-Helpers • Builder • Associations, Migrations, ... • Rake / Thor • Bundler • [...]
    8. 8. all about API designRuby makes it easy to create DSL: • expressive & concise syntax • open classes • mixins • operator overloading • [...]
    9. 9. Gregory Brown Russ Olsen
    10. 10. DSL by EXAMPLE
    11. 11. Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
    12. 12. Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, parameters) # does itend
    13. 13. Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, parameters) def doit_with(mandatory, optional=nil, parameters=nil) # does it # does itend end
    14. 14. Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, parameters) def doit_with(mandatory, optional=nil, parameters=nil) # does it # does itend enddef doit_with(options={:named => parameter}) # does itend
    15. 15. Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, parameters) def doit_with(mandatory, optional=nil, parameters=nil) # does it # does itend enddef doit_with(options={:named => parameter}) def doit_with(mandatory, *optional_paramters) # does it # does itend end
    16. 16. Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, parameters) def doit_with(mandatory, optional=nil, parameters=nil) # does it # does itend enddef doit_with(options={:named => parameter}) def doit_with(mandatory, *optional_paramters) # does it # does itend 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
    17. 17. BlocksTwitter.configure do |config| config.consumer_key = YOUR_CONSUMER_KEY config.consumer_secret = YOUR_CONSUMER_SECRETend
    18. 18. Blocks Twitter.configure do |config| config.consumer_key = YOUR_CONSUMER_KEY config.consumer_secret = YOUR_CONSUMER_SECRET enddef doit_with(&explicit_block) explicit_block.callend
    19. 19. Blocks Twitter.configure do |config| config.consumer_key = YOUR_CONSUMER_KEY config.consumer_secret = YOUR_CONSUMER_SECRET enddef doit_with(&explicit_block) def doit_with_implicit_block explicit_block.call yield if block_given?end end
    20. 20. Blocks Twitter.configure do |config| config.consumer_key = YOUR_CONSUMER_KEY config.consumer_secret = YOUR_CONSUMER_SECRET enddef 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
    21. 21. Blocks Twitter.configure do |config| config.consumer_key = YOUR_CONSUMER_KEY config.consumer_secret = YOUR_CONSUMER_SECRET enddef 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 enddef doit_with(&arity_sensitive_block) arity_sensitive_block.call foo, bar if arity_sensitive_block.arity == 2end
    22. 22. Instance EvalTwitter.configure do |config| config.consumer_key = YOUR_CONSUMER_KEY config.consumer_secret = YOUR_CONSUMER_SECRETend
    23. 23. Instance EvalTwitter.configure do |config| consumer_key = YOUR_CONSUMER_KEY config.consumer_key = YOUR_CONSUMER_KEY consumer_secret = YOUR_CONSUMER_SECRET config.consumer_secret = YOUR_CONSUMER_SECRETend
    24. 24. Instance EvalTwitter.configure do |config| consumer_key = YOUR_CONSUMER_KEY config.consumer_key = YOUR_CONSUMER_KEY consumer_secret = YOUR_CONSUMER_SECRET config.consumer_secret = YOUR_CONSUMER_SECRETend 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
    25. 25. Method MissingClient.find_by_firstname_and_lastname_and_gender(uschi, mueller, :female)
    26. 26. Method MissingClient.find_by_firstname_and_lastname_and_gender(uschi, mueller, :female) METHOD_PATTERN = /^find_by_/ def method_missing(method, *args, &block) if method.to_s =~ METHOD_PATTERN # finder magic else super end end
    27. 27. Method MissingClient.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
    28. 28. Code Generation client.firstname = uschi puts client.lastname
    29. 29. 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
    30. 30. Makrosclass Client < ActiveRecord::Base has_one :address has_many :orders belongs_to :roleend
    31. 31. Makrosclass Client < ActiveRecord::Base has_one :address has_many :orders belongs_to :roleend class << self def has_something # macro definition end end
    32. 32. Hooks$ ruby simple_test.rb
    33. 33. Hooks$ ruby simple_test.rb at_exit do # shut down code end
    34. 34. Hooks $ ruby simple_test.rb at_exit do # shut down code endinherited, included, method_missing, method_added,method_removed, trace_var, set_trace_func, at_exit ...
    35. 35. Hooks $ ruby simple_test.rb at_exit do # shut down code endset_trace_func proc { |event, file, line, id, binding, classname| inherited, included, method_missing, method_added, printf "%8s %s:%-2d trace_var, set_trace_func, line, id,... method_removed, %10s %8sn", event, file, at_exit classname}
    36. 36. Core Extensions text.blank?
    37. 37. Core Extensions text.blank? class Object def blank? nil? || (respond_to?(:empty?) && empty?) end end
    38. 38. Core Extensions text.blank? class Object def blank? nil? || (respond_to?(:empty?) && empty?) end unless method_defined?(:blank?) end
    39. 39. Core Extensions text.blank?module MyGem class Object module CoreExtensions module blank? def Object defnil? || (respond_to?(:empty?) && empty?) blank? end unless method_defined?(:blank?) respond_to?(:empty?) ? empty? : !self end end end endendObject.send :include, MyGem::CoreExtensions::Object
    40. 40. Ruby techniques✓Method Params ✓Makros✓Blocks ✓Code generation✓Instance Eval ✓Hooks✓Method Missing ✓Core Extensions
    41. 41. External DSLRuby Tools: • Switch Cases • Regular ExpressionsRuby Parser: • Treetop • RACC
    42. 42. Links★ Eloquent Ruby★ Ruby Best Practices★ Domain Specific Languages★ Rebuil★ Treetop
    43. 43. Over and Out
    44. 44. Over and Out
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×