Uploaded on

Writing a DSL in Ruby

Writing a DSL in Ruby

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
983
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
30
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    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

Transcript

  • 1. Writing a DSL in Ruby Why Ruby makes it soooo easy!
  • 2. DSL!!!!11one
  • 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 syntaxand 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 syntaxand 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 Mand you write a full parser to process them.“
  • 7. Rails, collection of DSLs • Routes • Tag- / Form-Helpers • Builder • Associations, Migrations, ... • Rake / Thor • Bundler • [...]
  • 8. all about API designRuby makes it easy to create DSL: • expressive & concise syntax • open classes • mixins • operator overloading • [...]
  • 9. Gregory Brown Russ Olsen
  • 10. DSL by EXAMPLE
  • 11. Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
  • 12. Method Paramslink_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"def doit_with(some, parameters) # does itend
  • 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. 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. 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. 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. BlocksTwitter.configure do |config| config.consumer_key = YOUR_CONSUMER_KEY config.consumer_secret = YOUR_CONSUMER_SECRETend
  • 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. 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. 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. 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. Instance EvalTwitter.configure do |config| config.consumer_key = YOUR_CONSUMER_KEY config.consumer_secret = YOUR_CONSUMER_SECRETend
  • 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. 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. Method MissingClient.find_by_firstname_and_lastname_and_gender(uschi, mueller, :female)
  • 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. 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. Code Generation client.firstname = uschi puts client.lastname
  • 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. Makrosclass Client < ActiveRecord::Base has_one :address has_many :orders belongs_to :roleend
  • 31. Makrosclass Client < ActiveRecord::Base has_one :address has_many :orders belongs_to :roleend class << self def has_something # macro definition end end
  • 32. Hooks$ ruby simple_test.rb
  • 33. Hooks$ ruby simple_test.rb at_exit do # shut down code end
  • 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. 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. Core Extensions text.blank?
  • 37. Core Extensions text.blank? class Object def blank? nil? || (respond_to?(:empty?) && empty?) end end
  • 38. Core Extensions text.blank? class Object def blank? nil? || (respond_to?(:empty?) && empty?) end unless method_defined?(:blank?) end
  • 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. Ruby techniques✓Method Params ✓Makros✓Blocks ✓Code generation✓Instance Eval ✓Hooks✓Method Missing ✓Core Extensions
  • 41. External DSLRuby Tools: • Switch Cases • Regular ExpressionsRuby Parser: • Treetop • RACC
  • 42. Links★ Eloquent Ruby★ Ruby Best Practices★ Domain Specific Languages★ Rebuil★ Treetop
  • 43. Over and Out
  • 44. Over and Out