Dsl
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share
  • 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
1,320
On Slideshare
1,320
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