简体   繁体   中英

Very simple DSL in Ruby

I want to write a DSL that works like this.

text = Screen.write do
  label text: 'Something'
  label text: 'stupid'
end
puts text => Somethingstupid

I don't know much metaprogramming ( I'm starting to learn it now) and the DSL tutorials online weren't really good. I think I should have something like this:

class Screen
  attr_accessor :content
  def initialize 
    @content = ""
  end
  def self.draw (&block)
    self.instance_eval(&block)
    @content
  end

The problem is I have no idea about the label text: part. This is maybe the most important part becasue I should later add functionality to it like label text: 'border', border: '|' => |border| label text: 'border', border: '|' => |border| and label text: 'UPCASE', style: :downcase => upcase . So how should I tackle this problem. Any ideas and help are welcomed.

There're many different ways in which you can implement something like this. To keep it simple, you can just evaluate the block within a new Screen object and return the result.

class Screen
  attr_reader :texts

  def initialize
    @texts = []
  end

  def label(hash)
    # Validation + check for other keys
    texts << hash[:text]
  end

  def to_s
    texts.join
  end

  def self.write(&block)
    raise "No block given" unless block_given?

    Screen.new.tap do |s|
      s.instance_eval(&block)
    end
  end
end

text = Screen.write do
  label text: 'Something'
  label text: 'stupid'
end

p "Rendered: #{text}"

I see no reason to have any instance methods. Am I missing something?

Code

class Screen
  def self.hwrite(&block) write(false, &block) end
  def self.vwrite(&block) write(true,  &block) end

  private

  def self.write(vlabel, &block)
    @str = ''
    @vlabel = vlabel
    instance_eval &block
  end

  def self.label(h)
    @str << h[:text]
    @str << '\n' if @vlabel
    @str 
  end
end

BasicObject#instance_eval changes self within the block to Screen .

Examples

str = Screen.hwrite do
  label text: 'Something '
  label text: 'is amiss'
end
  #=> "Something is amiss"

str = Screen.vwrite do
  label text: 'Something is rotten in '
  label text: 'the state of Denmark'
end
  #=> "Something is rotten in \\nthe state of Denmark\\n"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM