katte 0.0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. data/.gitignore +17 -0
  2. data/.ruby-version +1 -0
  3. data/Gemfile +9 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +107 -0
  6. data/Rakefile +1 -0
  7. data/bin/katte +7 -0
  8. data/katte.gemspec +19 -0
  9. data/lib/katte.rb +50 -0
  10. data/lib/katte/command.rb +33 -0
  11. data/lib/katte/config.rb +21 -0
  12. data/lib/katte/driver.rb +71 -0
  13. data/lib/katte/environment.rb +14 -0
  14. data/lib/katte/filter.rb +22 -0
  15. data/lib/katte/node.rb +5 -0
  16. data/lib/katte/node/base.rb +48 -0
  17. data/lib/katte/node/collection.rb +47 -0
  18. data/lib/katte/plugins.rb +11 -0
  19. data/lib/katte/plugins/base.rb +38 -0
  20. data/lib/katte/plugins/file_type.rb +47 -0
  21. data/lib/katte/plugins/file_type/bash.rb +9 -0
  22. data/lib/katte/plugins/file_type/debug.rb +14 -0
  23. data/lib/katte/plugins/file_type/hive.rb +9 -0
  24. data/lib/katte/plugins/file_type/r.rb +9 -0
  25. data/lib/katte/plugins/file_type/ruby.rb +9 -0
  26. data/lib/katte/plugins/node.rb +6 -0
  27. data/lib/katte/plugins/node/debug.rb +11 -0
  28. data/lib/katte/plugins/node/file.rb +45 -0
  29. data/lib/katte/plugins/output.rb +13 -0
  30. data/lib/katte/plugins/output/debug.rb +14 -0
  31. data/lib/katte/plugins/output/file.rb +31 -0
  32. data/lib/katte/plugins/output/stderr.rb +9 -0
  33. data/lib/katte/plugins/output/stdoe.rb +14 -0
  34. data/lib/katte/recipe.rb +8 -0
  35. data/lib/katte/recipe/file_type.rb +29 -0
  36. data/lib/katte/recipe/node.rb +71 -0
  37. data/lib/katte/recipe/node_factory.rb +57 -0
  38. data/lib/katte/runner.rb +66 -0
  39. data/lib/katte/runner/callback.rb +36 -0
  40. data/lib/katte/thread_pool.rb +41 -0
  41. data/lib/katte/version.rb +3 -0
  42. data/spec/katte/config_spec.rb +16 -0
  43. data/spec/katte/driver_spec.rb +58 -0
  44. data/spec/katte/filter_spec.rb +19 -0
  45. data/spec/katte/node/base_spec.rb +20 -0
  46. data/spec/katte/node/collection_spec.rb +36 -0
  47. data/spec/katte/plugins/file_type_spec.rb +59 -0
  48. data/spec/katte/plugins/output_spec.rb +31 -0
  49. data/spec/katte/recipe/node_factory_spec.rb +20 -0
  50. data/spec/katte/runner_spec.rb +34 -0
  51. data/spec/katte/thread_pool_spec.rb +47 -0
  52. data/spec/recipes/custom/sample_1.sh +3 -0
  53. data/spec/recipes/custom/sample_2.sh +3 -0
  54. data/spec/recipes/test/sample.sh +5 -0
  55. data/spec/recipes/test/sample/sub.sh +3 -0
  56. data/spec/spec_helper.rb +7 -0
  57. metadata +117 -0
@@ -0,0 +1,47 @@
1
+ class Katte::Node::Collection
2
+ include Enumerable
3
+
4
+ def each &proc
5
+ @nodes.values.each &proc
6
+ end
7
+
8
+ def initialize
9
+ @nodes = {}
10
+ end
11
+
12
+ def all
13
+ @nodes.values
14
+ end
15
+ def find(node_name)
16
+ @nodes[node_name]
17
+ end
18
+
19
+ def add(node, params = {})
20
+ @nodes[node.name] = node
21
+ end
22
+ alias :<< :add
23
+
24
+ def connect
25
+ cache ||= {} # connection cache
26
+
27
+ @nodes.values.each do |node|
28
+ # connect self to parents if exist
29
+ node.requires.each do |req, prm|
30
+ if @nodes[req]
31
+ node.add_parent(@nodes[req], *prm)
32
+ @nodes[req].add_child(node, *prm)
33
+ else
34
+ (cache[req] ||= []) << [node, prm]
35
+ end
36
+ end
37
+
38
+ # connect children to self
39
+ if children = cache.delete(node.name)
40
+ children.each {|c, prm|
41
+ c.add_parent(node, *prm)
42
+ node.add_child(c, *prm)
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ require 'katte/plugins/base'
2
+ require 'katte/plugins/file_type'
3
+ require 'katte/plugins/output'
4
+ require 'katte/plugins/node'
5
+
6
+ module Katte::Plugins
7
+ end
8
+
9
+ Dir[File.expand_path('../plugins/file_type/*.rb', __FILE__),
10
+ File.expand_path('../plugins/output/*.rb' , __FILE__),
11
+ File.expand_path('../plugins/node/*.rb' , __FILE__)].each {|p| require p }
@@ -0,0 +1,38 @@
1
+ module Katte::Plugins
2
+ module Base
3
+ def class_methods
4
+ @class_methods ||= Module.new
5
+ end
6
+ private :class_methods
7
+
8
+ def included(klass)
9
+ klass.extend class_methods
10
+ end
11
+
12
+ def index(keyword = nil)
13
+ return @index unless keyword
14
+ @index = keyword
15
+ end
16
+ def define_keyword(keyword)
17
+ klass = self
18
+
19
+ class_methods.send(:define_method, keyword) {|value|
20
+ define_method(keyword) { value }
21
+
22
+ if klass.index == keyword
23
+ klass.register(value, self.new)
24
+ end
25
+ }
26
+ end
27
+
28
+ def plugins
29
+ @plugins ||= {}
30
+ end
31
+ def register(index, plugin)
32
+ plugins[index] = plugin
33
+ end
34
+ def find(index)
35
+ plugins[index]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Katte::Plugins::FileType
3
+ extend Katte::Plugins::Base
4
+
5
+ define_keyword :comment_by
6
+ define_keyword :extname
7
+ index :extname
8
+
9
+ def simple_exec(node, program, *args)
10
+ node.open {|out, err|
11
+ system(Katte.app.env.to_hash, program, *args, :out => out, :err => err)
12
+ }
13
+ end
14
+
15
+ def parse(path)
16
+ c = Regexp.escape(comment_by)
17
+ @comment_pattern ||= /^#{c}|^\s*$/
18
+ @directive_pattern ||= %r{
19
+ ^#{c}\s* # comment
20
+ (?<key>\w+)\s*:\s* # key
21
+ (?<value>[^(\s]+) # value
22
+ (\((?<params>[^)]*)\))? # param (optional)
23
+ $
24
+ }x
25
+
26
+ directive = Hash.new {|h,k| h[k] = [] }
27
+ open(path) do |io|
28
+ while line = io.gets
29
+ line.chomp!
30
+ break unless @comment_pattern.match(line)
31
+ next unless m = @directive_pattern.match(line)
32
+
33
+ key = m[:key]
34
+ value = m[:value].strip
35
+ params = m[:params] && m[:params].split(',').map(&:strip).map(&method(:convert_variable))
36
+
37
+ directive[key] << [value, params]
38
+ end
39
+ end
40
+ directive
41
+ end
42
+
43
+ def convert_variable(value)
44
+ # #{xx} というパターンを env['xx'] で置換する
45
+ value.gsub(/\#\{((?:\\\{|[^\{])+)\}/) {|m| Katte.app.env.to_hash[$1]}
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ class Katte::Plugins::FileType::Bash
2
+ include Katte::Plugins::FileType
3
+ extname 'sh'
4
+ comment_by '#'
5
+
6
+ def execute(node)
7
+ simple_exec(node, 'bash', node.path)
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ class Katte::Plugins::FileType::Debug
2
+ include Katte::Plugins::FileType
3
+
4
+ extname :debug
5
+
6
+ class Abort < StandardError; end
7
+
8
+ def execute(node)
9
+ node.options['callback'].each {|cb| cb.call(node) }
10
+ return true
11
+ rescue Abort => e
12
+ return false
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ class Katte::Plugins::FileType::Hive
2
+ include Katte::Plugins::FileType
3
+ extname 'sql'
4
+ comment_by '--'
5
+
6
+ def execute(node)
7
+ simple_exec(node, 'hive', '-f', node.path)
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Katte::Plugins::FileType::R
2
+ include Katte::Plugins::FileType
3
+ extname 'R'
4
+ comment_by '#'
5
+
6
+ def execute(node)
7
+ simple_exec(node, 'Rscript', node.path)
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Katte::Plugins::FileType::Rubyn
2
+ include Katte::Plugins::FileType
3
+ extname 'rb'
4
+ comment_by '#'
5
+
6
+ def execute(node)
7
+ simple_exec(node, 'ruby', node.path)
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module Katte::Plugins::Node
2
+ extend Katte::Plugins::Base
3
+
4
+ define_keyword :name
5
+ index :name
6
+ end
@@ -0,0 +1,11 @@
1
+ class Katte::Plugins::Node::Debug
2
+ include Katte::Node::Base
3
+ include Katte::Plugins::Node
4
+
5
+ name 'debug'
6
+
7
+ def run(driver)
8
+ children.each {|child| driver.next(self, child.name) }
9
+ driver.done(self)
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ class Katte::Plugins::Node::File
2
+ include Katte::Node::Base
3
+ include Katte::Plugins::Node
4
+
5
+ name 'file'
6
+
7
+ def duration; 30; end
8
+
9
+ def add_child(node, *params)
10
+ file = params.first
11
+ return unless file
12
+ add_watch_list(file, node)
13
+ end
14
+
15
+ def run(driver)
16
+ until watching_files.empty?
17
+ update_watch_list do |file, nodes|
18
+ if FileTest.exist? file
19
+ nodes.each {|n| driver.next(self, n.name) }
20
+ true
21
+ else
22
+ false
23
+ end
24
+ end
25
+
26
+ watching_files.each {|f| Katte.app.logger.info("wating: #{f}") }
27
+ sleep duration * 60 unless watching_files.empty?
28
+ end
29
+
30
+ driver.done(self)
31
+ end
32
+
33
+ private
34
+ def watching_files
35
+ (@watch_list || {}).keys
36
+ end
37
+ def add_watch_list(file, node)
38
+ @watch_list ||= {}
39
+ (@watch_list[file] ||= []) << node
40
+ end
41
+ def update_watch_list(&proc)
42
+ @watch_list ||= {}
43
+ @watch_list = @watch_list.reject(&proc)
44
+ end
45
+ end
@@ -0,0 +1,13 @@
1
+ module Katte::Plugins::Output
2
+ extend Katte::Plugins::Base
3
+
4
+ define_keyword :name
5
+ index :name
6
+
7
+ def out(node, output)
8
+ output
9
+ end
10
+ def err(node, output)
11
+ output
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ class Katte::Plugins::Output::Debug
2
+ include Katte::Plugins::Output
3
+ name :debug
4
+
5
+ def history
6
+ @history ||= Queue.new
7
+ end
8
+
9
+ def out(node, data)
10
+ history.push(node: node, out: data)
11
+
12
+ data
13
+ end
14
+ end
@@ -0,0 +1,31 @@
1
+ require 'fileutils'
2
+
3
+ class Katte::Plugins::Output::File_
4
+ include Katte::Plugins::Output
5
+ name :file
6
+
7
+ def out(node, data)
8
+ return data if data.empty?
9
+
10
+ file = File.join(Katte.app.config.result_root,
11
+ node.name + '.out',
12
+ Katte.app.env.to_hash['date'] + ".txt")
13
+
14
+ FileUtils.makedirs(File.dirname(file))
15
+
16
+ File.open(file, 'w') {|f| f.print data }
17
+ end
18
+
19
+ def err(node, data)
20
+ return data if data.empty?
21
+
22
+ file = File.join(Katte.app.config.log_root,
23
+ 'recipes',
24
+ node.name + '.log',
25
+ Katte.app.env.to_hash['date'] + ".txt")
26
+
27
+ FileUtils.makedirs(File.dirname(file))
28
+
29
+ File.open(file, 'a') {|f| f.print data }
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ class Katte::Plugins::Output::Stderr
2
+ include Katte::Plugins::Output
3
+ name :stderr
4
+
5
+ def err(node, data)
6
+ STDERR.puts data
7
+ data
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ class Katte::Plugins::Output::Stdoe
2
+ include Katte::Plugins::Output
3
+ name :stdoe
4
+
5
+ def out(node, data)
6
+ STDIN.puts data
7
+ data
8
+ end
9
+
10
+ def err(node, data)
11
+ STDERR.puts data
12
+ data
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ class Katte
2
+ module Recipe
3
+ end
4
+ end
5
+
6
+ require 'katte/recipe/file_type'
7
+ require 'katte/recipe/node'
8
+ require 'katte/recipe/node_factory'
@@ -0,0 +1,29 @@
1
+ module Katte::Recipe::FileType
2
+ class <<self
3
+ def table
4
+ @table ||= {}
5
+ end
6
+ private :table
7
+
8
+ def register(file_type, extname = nil)
9
+ extname ||= file_type.extname
10
+ table[extname] = file_type
11
+ end
12
+
13
+ def find(extname)
14
+ table[extname] || Katte::Plugins::FileType.find(extname)
15
+ end
16
+ def register_builtin_file_types
17
+ Dir[File.expand_path('../plugins/file_type/*.rb', __FILE__)].each {|p|
18
+ require p
19
+ name = File.basename(p, ".rb")
20
+ klass = "katte/plugins/file_type/#{name}".camelize.constantize
21
+ }
22
+ register(Katte::Plugins::FileType::Bash.new)
23
+ register(Katte::Plugins::FileType::Debug.new)
24
+ register(Katte::Plugins::FileType::Hive.new)
25
+ register(Katte::Plugins::FileType::R.new)
26
+ register(Katte::Plugins::FileType::Ruby.new)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,71 @@
1
+ require 'katte/node/base'
2
+ require 'katte/recipe/node_factory'
3
+ require 'katte/thread_pool'
4
+
5
+ module Katte::Recipe
6
+ class Node
7
+ include Katte::Node::Base
8
+
9
+ %w(name path file_type output period options).each {|attr|
10
+ attr_accessor attr
11
+ }
12
+
13
+ def initialize(params)
14
+ @name = params[:name]
15
+ @path = params[:path]
16
+ @requires = params[:require]
17
+ @file_type = params[:file_type]
18
+ @output = params[:output] || []
19
+ @period = params[:period] || 'day'
20
+ @options = params[:options] || {}
21
+
22
+ @parents = []
23
+ @children = []
24
+ end
25
+
26
+ def open
27
+ out_r, out_w = IO.pipe
28
+ err_r, err_w = IO.pipe
29
+
30
+ result = yield out_w, err_w
31
+
32
+ [out_w, err_w].each {|io| io.close unless io.closed? }
33
+ out_a, err_a = out_r.to_a.join, err_r.to_a.join
34
+ [out_r, err_r].each {|io| io.close unless io.closed? }
35
+
36
+ Katte.app.logger.warn("#{self.name} -- result is empty") if out_a.empty?
37
+
38
+ @output.reduce(out_a) {|stream, o| o.out(self, stream) }
39
+ @output.reduce(err_a) {|stream, o| o.err(self, stream) }
40
+
41
+ result
42
+ ensure
43
+ [out_r, out_w, err_r, err_w].each {|io| io.close unless io.closed? }
44
+ end
45
+
46
+ def run(driver)
47
+ unless @file_type
48
+ Katte.app.logger.error("no file type specified for %s" % @name)
49
+ return
50
+ end
51
+
52
+ Katte::ThreadPool.instance.push {
53
+ Katte.app.logger.info("[start] #{self.name}")
54
+ result = file_type.execute(self)
55
+ Katte.app.logger.info(sprintf("[%s] #{self.name}", (result ? "success" : "fail")))
56
+
57
+ result ? driver.done(self) : driver.fail(self)
58
+ }
59
+ end
60
+ def descendants
61
+ Enumerator.new {|enum| _descendants(enum) }
62
+ end
63
+ def _descendants(enum)
64
+ children.each {|node|
65
+ enum << node
66
+ node._descendants(enum)
67
+ }
68
+ enum
69
+ end
70
+ end
71
+ end