プラガブルなデータ加工ツールのなにか

自分なりに使いやすいようにやった方がやっぱり楽。


基本的な sort・uniq・grep などは自前で持つようにする。
あと、引きずりまわすデータの型は、ハッシュを配列にいれたものにする。
ハッシュのキーはシンボルのみを扱う。

コンフィグ

define_task do |ctx|

  call_plugin 'Fetch::file_stat', :path => 'D:/home/develop/plugin'

  sort :key => 'size'
  sort :key => 'name', :order => 'desc'

  require 'pp'
  pp ctx.data

end

データダンプ例

[
  {
    :name=>"D:/home/develop/plugin/test.rb",
    :ftype=>"file",
    :size=>93,
  },
  {
    :name=>"D:/home/develop/plugin/inline_ruby.rb",
    :ftype=>"file",
    :size=>424,
  },
  {
    :name=>"D:/home/develop/plugin/Publish",
    :ftype=>"directory",
    :size=>0,
  },
  {
    :name=>"D:/home/develop/plugin/Foo.rb",
    :ftype=>"file",
    :size=>308,
  },
  {
    :name=>"D:/home/develop/plugin/Filter",
    :ftype=>"directory",
    :size=>0,
  },
]

ソースコード

まだまだこれから。

module Xry
  class DSL
    def initialize(dsl_file='xry.dsl', plugin_dir = './plugin')
      @plugin_dir = plugin_dir
      @plugin = Xry::Plugin.new.load(@plugin_dir)

      instance_eval(File::open(dsl_file, "r").read, dsl_file, 1)
    end

    def define_task(&block)
      return nil if block.nil?

      context = Xry::Context.new(@plugin)
      context.instance_eval(&block)
      context
    end
  end

  class Plugin
    def initialize
      @methods = {}
    end

    def load(dir)
      Dir::glob("#{dir}/**/*.rb") {|file|
        key = file.sub(%r{^#{dir}/(.+?)\.rb}, "\\1").gsub(%r{/}, "::")
        @methods[key] = Xry::Plugin::Method.new(file)

#       $stderr.puts "load plugin: #{key}"
      }

      self
    end

    def send(name, *argv)
      unless @methods.key?(name)
        $stderr.puts "not found plugin method: name=[#{name}]"
        return
      end

      @methods[name].send(name.sub(/.*::/, ""), *argv)
    end

    class Method
      def initialize(filename)
        instance_eval(File::open(filename, "r").read, filename, 1)
      end
    end
  end

  class Context
    attr_accessor :data

    def initialize(plugin)
      @data = []
      @plugin = plugin
    end

    def call_plugin(*argv, &block)
      return self  if argv.size == 0

      if argv.size == 1
        config = {}
      else
        config = argv[1]
      end

      name = argv[0]
      
      self.instance_eval(&block)  unless block.nil?

      puts "execute plugin: #{name}"
      @data = @plugin.send(name, config, @data.clone)

      self
    end

    def sort(option, &block)
      # argv -> (key, order, &block)

      return @data  unless option.key?(:key)

      if !block.nil?
        @data = @data.instance_eval(&block)
	  elsif option.key?(:order) && option[:order].to_s == 'asc'
        # ascending: 昇順
        @data = @data.sort {|a, b| a[option[:key].to_sym] <=> b[option[:key].to_sym]}
      else
        # descending: 降順
        @data = @data.sort {|a, b| b[option[:key].to_sym] <=> a[option[:key].to_sym]}
	  end
    end

    def grep
      # argv -> (key, pattern, option)
      @data
    end

    def reverse
      # argv -> ()
      @data
    end

    def head
      # argv -> (n)
      @data
    end

    def tail
      # argv -> (n)
      @data
    end

    def concat
      # argv -> (&block)
      @data
    end

    def branch
      # argv -> (&block)
      @data
    end

    def delete
      # argv -> (key, pattern, option)
      @data
    end

    def print
      # argv -> (&block)
      @data
    end
  end
end

if $0 == __FILE__
  xry = Xry::DSL.new()
end