Ruby中非常简单的DSL

时间:2014-11-22 17:32:07

标签: ruby metaprogramming dsl

我想写一个像这样的DSL

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

我不太了解元编程(我现在开始学习它),在线DSL教程并不是很好。我想我应该有这样的事情:

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

问题是我不知道label text:部分。这可能是最重要的部分,因为我稍后应该添加label text: 'border', border: '|' => |border|label text: 'UPCASE', style: :downcase => upcase等功能。那么我应该如何解决这个问题呢。欢迎任何想法和帮助。

2 个答案:

答案 0 :(得分:3)

您可以通过许多不同的方式实现此类内容。为了简单起见,您只需在新的Screen对象中评估块并返回结果。

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}"

答案 1 :(得分:0)

我认为没有理由拥有任何实例方法。我错过了什么吗?

<强>代码

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将块中的self更改为Screen

<强>实施例

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"