Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Cleanroom Pattern

Seth Vargo
August 12, 2014

The Cleanroom Pattern

Ruby is an excellent programming language for creating and managing custom DSLs, but how can you securely evaluate a DSL while explicitly controlling the methods exposed to the user? Our good friends instance_eval and instance_exec are great, but they expose all methods - public, protected, and private - to the user. Even worse, they expose the ability to accidentally or intentionally alter the behavior of the system! The cleanroom pattern is a safer, more convenient, Ruby-like approach for limiting the information exposed by a DSL while giving users the ability to write awesome code!

The cleanroom pattern is a unique way for more safely evaluating Ruby DSLs without adding additional overhead.

Seth Vargo

August 12, 2014

Transcript

126 slides
  1. class Project def name(val = NULL) set_or_return(:name, val) end def

    description(val = NULL) set_or_return(:description, val) end end
  2. class # @overload name(val) # Sets the name of this

    project # @param [String] val # @overload name # Returns this project's name # @return [String] end # @overload name(val) # Sets the name of this project # @param [String] val # @overload name # Returns this project's name # @return [String] def name(val = NULL)
  3. [1] pry(main)> project = Project.new => #<Project:0x007f943ba2f158> [2] pry(main)> project.name

    => nil [3] pry(main)> project.name("hamlet") => "hamlet" [4] pry(main)> project.name
  4. [1] pry(main)> project = Project.new => #<Project:0x007f943ba2f158> [2] pry(main)> project.name

    => nil [3] pry(main)> project.name("hamlet") => "hamlet" [4] pry(main)> project.name => "hamlet"
  5. [1] pry(main)> project = Project.new [2] pry(main)> project.name [3] pry(main)>

    project.name("hamlet") [4] pry(main)> project.name
  6. BasicObject#instance_eval ... the variable self is set to obj while

    the code is executing, giving the code access to obj’s instance variables
  7. BasicObject#instance_eval ... the variable while the code is executing, the

    code access to variables giving the code access to obj’s instance variables
  8. class Project def self.load(path) contents = IO.read(path) filename = File.basename(path)

    new.tap do |i| i.instance_eval(contents, filename, 1) end end end
  9. class Project def name(val = NULL) set_or_return(:name, val) end private

    def sanitize(val) return val if val.equal?(NULL) end end
  10. class Project def name(val = NULL) set_or_return(:name, val) end private

    def sanitize(val) return val if val.equal?(NULL) val.downcase.gsub(/\s+/, "-") end end
  11. class Project def name(val = NULL) set_or_return(:name, sanitize(val)) end private

    def sanitize(val) return val if val.equal?(NULL) val.downcase.gsub(/\s+/, "-") end end
  12. BasicObject#instance_eval ... the variable while the code is executing, the

    code access to variables giving the code access to obj’s instance variables
  13. class Project def set_or_return(key, val) if val.equal?(NULL) instance_variable_get(:"@#{key}") else raise

    Error unless val.is_a?(String) instance_variable_set(:"@#{key}", val) end end end
  14. CLEANROOM class Project def self.load(path) contents = IO.read(path) filename =

    File.basename(path) new.tap do |i| i.instance_eval(contents, filename, 1) end end end

More Decks by Seth Vargo

Other Decks in Technology