2014-11-22 2 views
2

Я хочу написать DSL, который работает вот так.Очень простой DSL в Ruby

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. Итак, как мне решить эту проблему. Любые идеи и помощь приветствуются.

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

Благодарим вас за ответ. Благодаря этому я смог реализовать все функции 'label'. У меня есть еще одна вещь, чтобы спросить. Как мне реализовать «вертикальный» метод, чтобы он работал следующим образом: 'screen = Screen.write do // vertical do // Текст ярлыка: '1' // Текст ярлыка: '2' // label текст: '3' // end // end // '. Извините за форматирование, для чего используется '//'. Это должно создать строку 'screen.to_s =" 1 \ n2 \ n3 \ n "'. Я не уверен, как это сделать, поскольку я должен перебирать разные хэши-метки, но я даю блок? – pesho

+0

pesho, с использованием метода, который я использовал: 'str = Screen.write do; текст ярлыка: '1 \ n'; текст ярлыка: '2 \ n'; текст метки: '3 \ n'; end'? То есть 'puts str # => 1 \ n2 \ n3 \ n' –

+0

@CarySwoveland Извините, но я должен сделать это так, как я показал. Я знаю, что использование '1 \ n' было бы проще, но я хочу, чтобы это было так, чтобы я мог позже объединить его с методом' horizontal' и использовать комбинацию боя. Первая «горизонтальная» вертикальная или наоборот. – pesho

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" 
Смежные вопросы