PraggerのconfigをRubyで書けるようにしてみた。

今更ながらPraggerを試してみた‥けどあまり萌えない - http://rubikitch.com/に移転しましたより、とりあえずはじめの一歩。

とりあえず、Array#call, Array#mapconcat!, Kernel#callを追加して、最初のeval_praggerを消しただけ。

#!/usr/bin/env ruby
$KCODE='u'
require 'yaml'
require 'optparse'
require 'kconv'
require 'pathname'
require 'base64'

$plugins = {}
class Plugin
  attr_reader :source
  def initialize(file)
    instance_eval( @source = File.read(file).toutf8, file , 1)
  end
  def self.load_plugins(folder = (Pathname.new(__FILE__).parent + "plugin"))
    Pathname.glob(folder + "**/*.rb").sort.each do |file|
      begin
        $plugins[ file.relative_path_from(folder).to_s.gsub("/","::")[0..-4] ]= Plugin.new(file)
      rescue LoadError
      end
    end
  end
end

class Array
  def call(module_name, config={})
    STDERR.puts "exec plugin #{module_name}"
    replace $plugins[module_name].send(module_name.sub(/.*::/,""), config, self.dup)
  end
  def mapconcat!
    replace inject([]){|data,x| data.concat(yield(x))}
  end
end

def call(module_name, config={})
  [].call(module_name, config)
end

def eval_pragger(command_array,data)
  command_array.inject(data) do |data, command|
    STDERR.puts "exec plugin #{command["module"]}"
    $plugins[command["module"]].send(command["module"].sub(/.*::/,""), command["config"] || {}, data.dup)
  end
end

Plugin.load_plugins()

おかげで以下のようなconfig.yaml

- module: concat
  config:
    - module: RSS::load
      config:
        url: http://www.ruby-lang.org/ja/feeds/news.rss
- module: concat
  config:
    - module: RSS::load
      config:
        url: http://www.ruby-lang.org/en/feeds/news.rss
- module: stdout

より簡潔なconfig.rbで書くことができた。

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'rb-pragger'
a = %w[http://www.ruby-lang.org/ja/feeds/news.rss http://www.ruby-lang.org/en/feeds/news.rss]
a.mapconcat! {|url| call("RSS::load", "url"=>url)}
a.call("stdout")

moduleとconfigしか要素がないのに、いちいち書くのは冗長すぎるし、YAMLだとDRY原則に反しててやってられないんだが。

とりあえず今はquick hackなので、より洗練された記法は後程考えるとしよう。

ってはてなキーワードPraggerをたどってみたら同じようなこと考えている人発見!こんな感じのDSLはいいんだけど、eval_praggerが使えないのが痛い。やっぱ俺は配列にメッセージを送る方が好きだな。処理を考えると自然だし。

追記

RSS::loadプラグインはurlに配列持てるのな。ならこれでいいんじゃん。メソッド名は「call」よりも「plugin」のほうがいいかな。

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'rb-pragger'
urls = %w[http://www.ruby-lang.org/ja/feeds/news.rss http://www.ruby-lang.org/en/feeds/news.rss]
a = call("RSS::load", "url"=> urls)
a.call("stdout")