raka 0.3.2 → 0.3.6

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.
data/bin/raka CHANGED
@@ -17,8 +17,6 @@ def detect_main
17
17
  return rakas[0] if rakas.length == 1
18
18
  end
19
19
 
20
- entry = detect_main
21
-
22
20
  options = { rake: {}, raka_finished: false }
23
21
  def set_option(opts, key, value)
24
22
  if opts[:raka_finished]
@@ -28,17 +26,31 @@ def set_option(opts, key, value)
28
26
  end
29
27
  end
30
28
  parser = OptionParser.new do |opts|
31
- opts.banner = 'Usage: raka [options] [raka file] -- [rake options]'
29
+ opts.banner = 'Usage: raka [options] <output> -- [rake options]'
32
30
 
33
31
  opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
34
32
  set_option(options, :verbose, v)
35
33
  end
34
+
35
+ opts.on('-f', '--file FILE', String, 'Run even when up to date') do |s|
36
+ set_option(options, :file, s)
37
+ end
38
+
39
+ opts.on('-j', '--jobs JOBS', Integer, 'Run in parallel') do |n|
40
+ set_option(options, :jobs, n)
41
+ end
36
42
  end
37
43
 
44
+ if ARGV.empty?
45
+ puts parser.help
46
+ exit(1)
47
+ end
38
48
  both_args = ARGV.join(' ').split(' -- ')
39
- extra_args = both_args[1]
40
49
  self_args = both_args[0].split(/\s+/)
41
50
  parser.parse!(self_args)
51
+ extra_args = (both_args[1] || ' ').lstrip
52
+
53
+ entry = options[:file] || detect_main
42
54
 
43
55
  env = if options[:verbose]
44
56
  'LOG_LEVEL=0 '
@@ -46,13 +58,26 @@ env = if options[:verbose]
46
58
  ''
47
59
  end
48
60
  targets = self_args.join(' ')
49
- cmd = "#{env}rake -f #{entry} #{extra_args} #{targets}"
61
+ cmd = ''
62
+ opt_str = "-f #{entry}"
63
+ opt_str += " -m -j #{options[:jobs]}" if options.key?(:jobs)
64
+ cmd += "#{env}rake #{opt_str} #{extra_args} #{targets}"
50
65
  puts cmd
51
- output, err, code = Open3.capture3(cmd)
52
- if code == 0
53
- # TOOD: if empty, print all exist
54
- puts output unless output.empty?
55
- else
56
- puts 'Error: rake returns the following information:'
66
+ output = []
67
+ ro, out = IO.pipe
68
+ re, err = IO.pipe
69
+ pid = fork do
70
+ status = system(cmd, out: out, err: err)
71
+ puts 'Error: rake returns the following information:' unless status
72
+ exit($CHILD_STATUS.exitstatus)
57
73
  end
58
- puts err
74
+ out.close
75
+ err.close
76
+ ro.each_line do |l|
77
+ puts l
78
+ output << l.chomp
79
+ end
80
+ re.each_line { |l| puts l; }
81
+
82
+ Process.wait(pid)
83
+ puts 'All targets are up to date' if output.empty? && $CHILD_STATUS.exitstatus == 0
@@ -28,8 +28,7 @@ class DSLCompiler
28
28
  # Raka task structure, input task is rake's task pushed into blocks
29
29
  def dsl_task(token, task)
30
30
  name = task.name
31
- deps = task.prerequisites
32
-
31
+ deps = task.prerequisites
33
32
  output_info = token._parse_output_ name
34
33
  task_info = {
35
34
  name: name,
@@ -41,6 +40,10 @@ class DSLCompiler
41
40
  OpenStruct.new(output_info.to_h.merge(task_info))
42
41
  end
43
42
 
43
+ def stem(path)
44
+ File.basename(path, File.extname(path))
45
+ end
46
+
44
47
  # resolve auto variables with only output info,
45
48
  # useful when resolve extra deps (task is not available yet)
46
49
  def resolve_by_output(target, output_info)
@@ -50,8 +53,10 @@ class DSLCompiler
50
53
  .gsub('$(scope)', info.scope.nil? ? '' : info.scope)
51
54
  .gsub('$(target_scope)', info.target_scope.nil? ? '' : info.target_scope)
52
55
  .gsub('$(output)', info.output)
53
- .gsub('$(output_stem)', info.stem)
56
+ .gsub('$(output_stem)', stem(info.stem))
54
57
  .gsub('$(input_stem)', info.input_stem.nil? ? '' : info.input_stem)
58
+ .gsub('$(func)', info.func.nil? ? '' : info.func)
59
+ .gsub('$(ext)', info.ext)
55
60
  .gsub('$@', info.name)
56
61
 
57
62
  protect_percent_symbol text do |safe_text|
@@ -75,7 +80,8 @@ class DSLCompiler
75
80
 
76
81
  protect_percent_symbol text do |safe_text|
77
82
  # add numbered auto variables like $0, $2 referring to the first and third deps
78
- safe_text.gsub(/\$\(dep(\d+)\)/, '%{\1}') % array_to_hash(task.deps)
83
+ safe_text = safe_text.gsub(/\$\(dep(\d+)\)/, '%{\1}') % array_to_hash(task.deps)
84
+ safe_text.gsub(/\$\(dep(\d+)_stem\)/, '%{\1}') % array_to_hash(task.deps.map {|d| stem(d)})
79
85
  end
80
86
  end
81
87
 
@@ -125,6 +131,7 @@ class DSLCompiler
125
131
 
126
132
  # compile token = rhs to rake rule
127
133
  # rubocop:disable Style/MethodLength
134
+ # rubocop:disable Style/PerceivedComplexity
128
135
  def compile(lhs, rhs)
129
136
  unless @env.instance_of?(Object)
130
137
  raise "DSL compile error: seems not a valid @env of rake with class #{@env.class}"
@@ -164,10 +171,12 @@ class DSLCompiler
164
171
  end
165
172
 
166
173
  # We generate a rule for each possible input type
167
- @options.input_types.each do |ext|
174
+
175
+ (lhs._options_[:input_exts] || @options.input_types).each do |ext|
168
176
  # We find auto source from both THE scope and the root
169
177
  create_rule lhs, ext, actions, extra_deps, extra_tasks
170
178
  end
171
179
  end
172
180
  end
173
181
  # rubocop:enable Style/MethodLength
182
+ # rubocop:enable Style/PerceivedComplexity
File without changes
File without changes
File without changes
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../protocol'
4
+
5
+ # r language protocol
6
+ class R
7
+ def initialize(libs = [], **kwargs)
8
+ @libs = libs
9
+ super(**kwargs)
10
+ end
11
+
12
+ def build(code, _)
13
+ libraries = @libs.map { |name| "suppressPackageStartupMessages(library(#{name}))" }
14
+
15
+ [libraries, code].join "\n"
16
+ end
17
+
18
+ def run_script(env, fname, _task)
19
+ env.send :sh, "Rscript #{fname}"
20
+ end
21
+ end
22
+
23
+ creator :r, R
File without changes
File without changes
@@ -86,11 +86,23 @@ end
86
86
  # helper functions to implement LanguageImpl
87
87
  def run_cmd(env, cmd)
88
88
  env.logger.debug(cmd)
89
- Open3.popen3(cmd) do |_stdin, stdout, stderr, _thread|
90
- env.logger.debug(stdout.read)
91
- err = stderr.read
92
- env.logger.info(err) unless err.empty?
89
+ out_r, out_w = IO.pipe
90
+ err_r, err_w = IO.pipe
91
+ if env.logger.level <= 0
92
+ pid = spawn(cmd, out: out_w)
93
+ Thread.new do
94
+ env.logger.debug(out_r.gets) until out_r.eof
95
+ end
96
+ elsif env.logger.level == 1
97
+ pid = spawn(cmd, out: out_w)
98
+ else
99
+ pid = spawn(cmd, out: out_w, err: err_w)
93
100
  end
101
+
102
+ Process.wait pid
103
+ out_w.close
104
+ err_w.close
105
+ err_r.close
94
106
  end
95
107
 
96
108
  def pick_kwargs(klass, kwargs)
@@ -29,11 +29,17 @@ end
29
29
  class Token
30
30
  attr_reader :chain
31
31
 
32
- def initialize(compiler, context, chain, inline_scope)
32
+ def initialize(compiler, context, chain, inline_scope, input_exts: nil)
33
33
  @compiler = compiler
34
34
  @context = context
35
35
  @chain = chain
36
36
  @inline_scope = inline_scope
37
+ @options = {}
38
+ @options[:input_exts] = input_exts
39
+ end
40
+
41
+ def _options_
42
+ @options
37
43
  end
38
44
 
39
45
  def _captures_(target)
@@ -74,7 +80,7 @@ class Token
74
80
 
75
81
  # attach a new item to the chain
76
82
  def _attach_(item)
77
- Token.new(@compiler, @context, @chain + [item], @inline_scope)
83
+ Token.new(@compiler, @context, @chain + [item], @inline_scope, @options)
78
84
  end
79
85
 
80
86
  # rubocop:disable Style/MissingRespondToMissing # for DSL not essential
data/lib/raka.rb CHANGED
@@ -2,13 +2,14 @@
2
2
 
3
3
  require 'logger'
4
4
 
5
- require_relative './compile'
6
- require_relative './protocol'
7
- require_relative './token'
5
+ require_relative './raka/compile'
6
+ require_relative './raka/protocol'
7
+ require_relative './raka/token'
8
8
 
9
9
  # initialize raka
10
10
  class Raka
11
11
  Pattern = Pattern
12
+ P = Pattern
12
13
  attr_reader :logger
13
14
 
14
15
  def create_logger(level)
@@ -24,7 +25,7 @@ class Raka
24
25
  env = @env
25
26
  options = @options
26
27
  scopes = @scopes
27
- @env.define_singleton_method(ext_alias || ext) do |*args|
28
+ @env.define_singleton_method(ext_alias || ext) do |*args, **kw|
28
29
  # Here the compiler are bound with @options so that when we change @options
29
30
  # using methods like scope in Rakefile, the subsequent rules defined will honor
30
31
  # the new settings
@@ -32,7 +33,7 @@ class Raka
32
33
  inline_scope_pattern = !args.empty? ? args[0] : nil
33
34
  Token.new(
34
35
  DSLCompiler.new(env, options), Context.new(ext, scopes.clone),
35
- [], inline_scope_pattern
36
+ [], inline_scope_pattern, **kw
36
37
  )
37
38
  end
38
39
  end
@@ -40,7 +41,7 @@ class Raka
40
41
  def initialize(env, options)
41
42
  @env = env
42
43
  defaults = {
43
- output_types: [:csv], input_types: [],
44
+ output_types: [:csv], input_types: nil,
44
45
  type_aliases: {},
45
46
  scopes: [],
46
47
  lang: ['lang/shell'],
@@ -50,10 +51,13 @@ class Raka
50
51
 
51
52
  create_logger options.log_level || (ENV['LOG_LEVEL'] || Logger::INFO).to_i
52
53
 
53
- @options.input_types |= @options.output_types # any output can be used as intermediate
54
+ # if input_types given, obey it, otherwise use all output types as possible input types
55
+ unless @options.input_types
56
+ @options.input_types = @options.output_types # any output can be used as intermediate
57
+ end
54
58
  # specify root of scopes in options, scopes will append to each root
55
59
  @scopes = options.scopes.empty? ? [] : [options.scopes]
56
- @options.lang.each { |path| load File::join(File::dirname(__FILE__), "#{path}/impl.rb") }
60
+ @options.lang.each { |path| load File::join(File::dirname(__FILE__), "raka/#{path}/impl.rb") }
57
61
  @options.user_lang.each { |path| load path.to_s + '.rb' }
58
62
 
59
63
  # These are where the dsl starts
@@ -67,4 +71,8 @@ class Raka
67
71
  block.call
68
72
  @scopes.pop
69
73
  end
74
+
75
+ def stem(path)
76
+ File.basename(path, File.extname(path))
77
+ end
70
78
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: raka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - yarray
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-03 00:00:00.000000000 Z
11
+ date: 2022-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -122,18 +122,16 @@ files:
122
122
  - README.md
123
123
  - VERSION
124
124
  - bin/raka
125
- - lib/compile.rb
126
- - lib/interface.rbs
127
- - lib/lang/psql/impl.rb
128
- - lib/lang/python/impl.rb
129
- - lib/lang/r/impl.rb
130
- - lib/lang/r/io.R
131
- - lib/lang/shell/impl.rb
132
- - lib/output_type.rb
133
- - lib/protocol.rb
134
125
  - lib/raka.rb
135
- - lib/temp.json
136
- - lib/token.rb
126
+ - lib/raka/compile.rb
127
+ - lib/raka/interface.rbs
128
+ - lib/raka/lang/psql/impl.rb
129
+ - lib/raka/lang/python/impl.rb
130
+ - lib/raka/lang/r/impl.rb
131
+ - lib/raka/lang/shell/impl.rb
132
+ - lib/raka/output_type.rb
133
+ - lib/raka/protocol.rb
134
+ - lib/raka/token.rb
137
135
  homepage: http://github.com/yarray/raka
138
136
  licenses:
139
137
  - MIT
data/lib/lang/r/impl.rb DELETED
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../protocol'
4
-
5
- # r language protocol
6
- class R
7
- def initialize(src, libs = [], **kwargs)
8
- @src = src
9
- @libs = libs
10
- super(**kwargs)
11
- end
12
-
13
- def build(code, _)
14
- libraries = ([
15
- :pipeR
16
- ] + @libs).map { |name| "suppressPackageStartupMessages(library(#{name}))" }
17
-
18
- sources = ["source('#{File.dirname(__FILE__)}/io.R')"] +
19
- (@src ? [@src] : []).map { |name| "source('#{SRC_DIR}/#{name}.R')" }
20
-
21
- extra = [
22
- '`|` <- `%>>%`',
23
- "conn_args <- list(host='#{HOST}', user='#{USER}', dbname='#{DB}', port='#{PORT}')",
24
- 'args <- commandArgs(trailingOnly = T)',
25
- 'sql_input <- init_sql_input(conn_args, args[1])',
26
- 'table_input <- init_table_input(conn_args, args[1])',
27
- 'table_output <- init_table_output(conn_args, args[1])'
28
- ]
29
-
30
- [libraries, sources, extra, code].join "\n"
31
- end
32
-
33
- def run_script(env, fname, task)
34
- env.send :sh, "Rscript #{fname} '#{task.scope || 'public'}'"
35
- end
36
- end
37
-
38
- creator :r, R
data/lib/lang/r/io.R DELETED
@@ -1,113 +0,0 @@
1
- suppressPackageStartupMessages(library(RPostgreSQL))
2
- suppressPackageStartupMessages(library(ggplot2))
3
- suppressPackageStartupMessages(library(gridExtra))
4
-
5
- # input
6
- # -----
7
- file_input <- function(fileName, coltypes = NA) {
8
- read.table(fileName, header = T, sep = ',', colClasses = coltypes)
9
- }
10
-
11
- join_file_input <- function(namesstr, key) {
12
- files <- strsplit(namesstr, split = '\\+')[[1]]
13
- dataList <- lapply(files, (function(f) fileInput(f)))
14
- do.call(merge, c(dataList, by = key))
15
- }
16
-
17
- init_table_input <- function(conn_args, schema) {
18
- function(name, cols = '*', where = 'true') {
19
- colstr <- paste(cols, collapse = ',')
20
-
21
- sql <- sprintf("SELECT %s FROM %s WHERE %s", colstr, name, where)
22
- init_sql_input(conn_args, schema)(sql)
23
- }
24
- }
25
-
26
- init_sql_input <- function(conn_args, schema) {
27
- function(sql) {
28
- conn <- do.call(dbConnect, c(dbDriver('PostgreSQL'), conn_args))
29
- on.exit(dbDisconnect(conn))
30
- dbGetQuery(conn, paste('SET search_path TO', paste(c(schema, 'public'), sep = ',')))
31
- buffer <- dbSendQuery(conn, sql)
32
- fetch(buffer, n=-1)
33
- }
34
- }
35
-
36
- create_parents <- function(output) {
37
- dir.create(dirname(output), showWarnings = FALSE, recursive = TRUE)
38
- }
39
-
40
- # Output
41
- # ------
42
- # TODO consider deprecating this, perhaps more specific ones like graph_output is still useful
43
- auto_output <- function(report, ...) {
44
- actual <- switch(class(report)[1],
45
- gg = ggplot_output,
46
- data.frame = csv_output,
47
- print)
48
- actual(report, ...)
49
- }
50
-
51
- rplot_output <- function(f, output, size = c(8, 8)) {
52
- create_parents(output)
53
- size <- ceiling(size / 2.54 * 300 / 96) # convert cm to inch
54
- par(mar = c(0,0,0,0))
55
- # use pdf since png can be very weird, e.g. radarplot
56
- pdf(output, width = size[1], height = size[2], pointsize = 12)
57
- f()
58
- dev.off()
59
- }
60
-
61
- # use facet or latex subfigure, do not handle list anymore
62
- ggplot_output <- function(report, output, size = c(15, 15), fontScale = 1, compact = F) {
63
- create_parents(output)
64
- if (compact) {
65
- margin <- 1
66
- } else {
67
- margin <- 5
68
- }
69
-
70
- report <- report + theme_bw() +
71
- theme(axis.title = element_text(size = 13 * fontScale),
72
- axis.text = element_text(size = 11 * fontScale),
73
- legend.title = element_text(size = 13 * fontScale),
74
- legend.text = element_text(size = 11 * fontScale),
75
- legend.key.size = unit(13 * fontScale, 'pt'),
76
- legend.margin = unit(0, "cm"),
77
- legend.background = element_rect(fill = alpha('white', 0)),
78
- plot.margin = unit(c(margin, margin, 0, 0),"mm"))
79
-
80
- width <- size[1]
81
- height <- size[2]
82
-
83
- ggsave(report, filename = output,
84
- dpi = 300, units = 'cm', width = width, height = height, limitsize = FALSE)
85
- }
86
-
87
- csv_output <- function(report, output) {
88
- create_parents(output)
89
- write.table(format(report, digits = 4), file = toString(output), row.names = FALSE,
90
- sep = ',', quote = FALSE)
91
- }
92
-
93
- txt_output <- function(report, output) {
94
- create_parents(output)
95
- write.table(format(report, digits = 4), file = toString(output), row.names = FALSE,
96
- sep = ',', quote = FALSE)
97
- }
98
-
99
- init_table_output <- function(conn_args, schema) {
100
- function(report, output, placeholder = NULL) {
101
- conn <- do.call(dbConnect, c(dbDriver('PostgreSQL'), conn_args))
102
- on.exit(dbDisconnect(conn))
103
- dbGetQuery(conn, paste('SET search_path TO', paste(c(schema, 'public'), sep = ',')))
104
-
105
- if (dbExistsTable(conn, output)) {
106
- dbRemoveTable(conn, output)
107
- }
108
- dbWriteTable(conn, output, report, row.names = F)
109
- if (!is.null(placeholder)) {
110
- write.table(data.frame(), file = placeholder, col.names = FALSE)
111
- }
112
- }
113
- }