masamune 0.11.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/masamune.rb +0 -1
- data/lib/masamune/actions.rb +1 -0
- data/lib/masamune/actions/execute.rb +1 -1
- data/lib/masamune/actions/invoke_parallel.rb +73 -0
- data/lib/masamune/cached_filesystem.rb +2 -4
- data/lib/masamune/commands/shell.rb +6 -2
- data/lib/masamune/data_plan/elem.rb +9 -10
- data/lib/masamune/data_plan/engine.rb +10 -11
- data/lib/masamune/data_plan/rule.rb +5 -7
- data/lib/masamune/data_plan/set.rb +28 -28
- data/lib/masamune/environment.rb +1 -12
- data/lib/masamune/filesystem.rb +10 -10
- data/lib/masamune/schema/catalog.rb +9 -9
- data/lib/masamune/schema/column.rb +2 -2
- data/lib/masamune/schema/table.rb +3 -2
- data/lib/masamune/version.rb +1 -1
- data/{lib/masamune/accumulate.rb → spec/masamune/actions/invoke_parallel_spec.rb} +18 -30
- data/spec/masamune/cached_filesystem_spec.rb +23 -21
- data/spec/masamune/data_plan/set_spec.rb +30 -30
- data/spec/masamune/filesystem_spec.rb +8 -8
- data/spec/masamune/schema/column_spec.rb +10 -0
- data/spec/masamune/schema/table_spec.rb +3 -0
- data/spec/support/masamune/mock_filesystem.rb +4 -5
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06ac5ed8ef2f9949a444065bef219d2efd144b94
|
4
|
+
data.tar.gz: bf620007bd3016250133cda30feb6c03182ab1d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 589c931738aec24331ada2bab69a52175be9d27dc8a11e3a695ca7abbd38762582a5e3c93dafc61a6bbc7fac7f76d19ed6206a55a8e333795e6c113f0d64761c
|
7
|
+
data.tar.gz: cb8d8062f36d5ebc0bc72c3a8f3ba3c8acff5afa2f14186ddcf8b6844ca77cc9fc333b3802cab27443843ec8c8ed191e10f1274aea196b5be4c1acce7c8e8fbf
|
data/lib/masamune.rb
CHANGED
data/lib/masamune/actions.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2014-2015, VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'active_support/concern'
|
24
|
+
require 'parallel'
|
25
|
+
|
26
|
+
module Masamune::Actions
|
27
|
+
module InvokeParallel
|
28
|
+
extend ActiveSupport::Concern
|
29
|
+
|
30
|
+
include Masamune::Actions::Execute
|
31
|
+
|
32
|
+
included do |base|
|
33
|
+
base.class_option :max_tasks, aliases: '-p', :type => :numeric, :desc => 'Maximum number of tasks to execute in parallel', :default => 4
|
34
|
+
end
|
35
|
+
|
36
|
+
def invoke_parallel(*task_group)
|
37
|
+
opts = task_group.last.is_a?(Hash) ? task_group.pop.dup : {}
|
38
|
+
max_tasks = [opts.delete(:max_tasks), task_group.count].min
|
39
|
+
console("Setting max_tasks to #{max_tasks}")
|
40
|
+
bail_fast task_group, opts if opts[:version]
|
41
|
+
Parallel.each(task_group, in_processes: max_tasks) do |task_name|
|
42
|
+
begin
|
43
|
+
execute(thor_wrapper, task_name, *task_args(opts), interactive: false, detach: false)
|
44
|
+
rescue SystemExit
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def thor_wrapper
|
52
|
+
'thor'
|
53
|
+
end
|
54
|
+
|
55
|
+
def bail_fast(task_group, opts = {})
|
56
|
+
task_name = task_group.first
|
57
|
+
execute($PROGRAM_NAME, task_name, *task_args(opts))
|
58
|
+
exit
|
59
|
+
end
|
60
|
+
|
61
|
+
def task_args(opts = {})
|
62
|
+
opts.map do |k,v|
|
63
|
+
case v
|
64
|
+
when true
|
65
|
+
"--#{k.to_s.gsub('_', '-')}"
|
66
|
+
when false
|
67
|
+
else
|
68
|
+
["--#{k.to_s.gsub('_', '-')}", v]
|
69
|
+
end
|
70
|
+
end.flatten.compact
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -22,8 +22,6 @@
|
|
22
22
|
|
23
23
|
module Masamune
|
24
24
|
class CachedFilesystem < SimpleDelegator
|
25
|
-
include Masamune::Accumulate
|
26
|
-
|
27
25
|
def initialize(filesystem)
|
28
26
|
super filesystem
|
29
27
|
@filesystem = filesystem
|
@@ -38,12 +36,12 @@ module Masamune
|
|
38
36
|
@cache.key?(file) || glob(file).include?(file) || @cache.key?(file)
|
39
37
|
end
|
40
38
|
|
41
|
-
def glob(file_or_glob
|
39
|
+
def glob(file_or_glob)
|
40
|
+
return Set.new(to_enum(:glob, file_or_glob)) unless block_given?
|
42
41
|
glob_stat(file_or_glob) do |entry|
|
43
42
|
yield entry.name unless entry.name == dirname(file_or_glob)
|
44
43
|
end
|
45
44
|
end
|
46
|
-
method_accumulate :glob
|
47
45
|
|
48
46
|
def stat(file_or_dir)
|
49
47
|
raise ArgumentError, 'cannot contain wildcard' if file_or_dir.include?('*')
|
@@ -41,13 +41,13 @@ module Masamune::Commands
|
|
41
41
|
@stderr_line_no = 0
|
42
42
|
end
|
43
43
|
|
44
|
-
def replace
|
44
|
+
def replace(opts = {})
|
45
45
|
logger.debug('replace: ' + command_args.join(' '))
|
46
46
|
before_execute
|
47
47
|
around_execute do
|
48
48
|
pid = Process.fork
|
49
49
|
if pid
|
50
|
-
|
50
|
+
detach if opts.fetch(:detach, true)
|
51
51
|
Process.waitpid(pid)
|
52
52
|
exit
|
53
53
|
else
|
@@ -198,5 +198,9 @@ module Masamune::Commands
|
|
198
198
|
return code unless status
|
199
199
|
status.exitstatus
|
200
200
|
end
|
201
|
+
|
202
|
+
def detach
|
203
|
+
STDIN.close; STDOUT.close; STDERR.close
|
204
|
+
end
|
201
205
|
end
|
202
206
|
end
|
@@ -23,7 +23,6 @@
|
|
23
23
|
class Masamune::DataPlan::Elem
|
24
24
|
MISSING_MODIFIED_AT = Time.new(0)
|
25
25
|
|
26
|
-
include Masamune::Accumulate
|
27
26
|
include Comparable
|
28
27
|
|
29
28
|
attr_reader :rule, :options
|
@@ -69,39 +68,39 @@ class Masamune::DataPlan::Elem
|
|
69
68
|
end || MISSING_MODIFIED_AT
|
70
69
|
end
|
71
70
|
|
72
|
-
def explode
|
71
|
+
def explode
|
72
|
+
return Set.new(to_enum(__method__)) unless block_given?
|
73
73
|
if rule.for_path?
|
74
74
|
file_glob = path
|
75
75
|
file_glob += '/' unless path.include?('*') || path.include?('.')
|
76
76
|
file_glob += '*' unless path.include?('*')
|
77
|
-
rule.engine.filesystem.glob(file_glob) do |new_path|
|
77
|
+
rule.engine.filesystem.glob(file_glob).each do |new_path|
|
78
78
|
yield rule.bind_input(new_path)
|
79
79
|
end
|
80
80
|
elsif rule.for_table_with_partition?
|
81
81
|
yield self if exists?
|
82
82
|
end
|
83
83
|
end
|
84
|
-
method_accumulate :explode
|
85
84
|
|
86
|
-
def targets
|
85
|
+
def targets
|
87
86
|
return Masamune::DataPlan::Set::EMPTY if @rule.for_targets?
|
88
|
-
rule.engine.
|
87
|
+
return Masamune::DataPlan::Set.new(rule.engine.get_target_rule(rule.name), to_enum(__method__)) unless block_given?
|
88
|
+
rule.engine.targets_for_source(rule.name, self).each do |target|
|
89
89
|
yield target
|
90
90
|
end
|
91
91
|
end
|
92
|
-
method_accumulate :targets, lambda { |elem| Masamune::DataPlan::Set.new(elem.rule.engine.get_target_rule(elem.rule.name)) }
|
93
92
|
|
94
93
|
def target
|
95
94
|
targets.first
|
96
95
|
end
|
97
96
|
|
98
|
-
def sources
|
97
|
+
def sources
|
99
98
|
return Masamune::DataPlan::Set::EMPTY if @rule.for_sources?
|
100
|
-
rule.engine.
|
99
|
+
return Masamune::DataPlan::Set.new(rule.engine.get_source_rule(rule.name), to_enum(__method__)) unless block_given?
|
100
|
+
rule.engine.sources_for_target(rule.name, self).each do |source|
|
101
101
|
yield source
|
102
102
|
end
|
103
103
|
end
|
104
|
-
method_accumulate :sources, lambda { |elem| Masamune::DataPlan::Set.new(elem.rule.engine.get_source_rule(elem.rule.name)) }
|
105
104
|
|
106
105
|
def source
|
107
106
|
sources.first
|
@@ -27,7 +27,6 @@ class Masamune::DataPlan::Engine
|
|
27
27
|
MAX_DEPTH = 10
|
28
28
|
|
29
29
|
include Masamune::HasEnvironment
|
30
|
-
include Masamune::Accumulate
|
31
30
|
|
32
31
|
def initialize
|
33
32
|
@target_rules = Hash.new
|
@@ -80,34 +79,34 @@ class Masamune::DataPlan::Engine
|
|
80
79
|
end
|
81
80
|
end
|
82
81
|
|
83
|
-
def targets_for_date_range(rule, start, stop
|
82
|
+
def targets_for_date_range(rule, start, stop)
|
83
|
+
return Masamune::DataPlan::Set.new(get_target_rule(rule), to_enum(:targets_for_date_range, rule, start, stop)) unless block_given?
|
84
84
|
target_template = @target_rules[rule]
|
85
85
|
return unless target_template
|
86
|
-
target_template.generate(start.to_time.utc, stop.to_time.utc) do |target|
|
86
|
+
target_template.generate(start.to_time.utc, stop.to_time.utc).each do |target|
|
87
87
|
yield target
|
88
88
|
end
|
89
89
|
end
|
90
|
-
method_accumulate :targets_for_date_range, lambda { |engine, rule, _, _| Masamune::DataPlan::Set.new(engine.get_target_rule(rule)) }
|
91
90
|
|
92
|
-
def targets_for_source(rule, source
|
91
|
+
def targets_for_source(rule, source)
|
92
|
+
return Masamune::DataPlan::Set.new(get_target_rule(rule), to_enum(:targets_for_source, rule, source)) unless block_given?
|
93
93
|
source_template = @source_rules[rule]
|
94
94
|
target_template = @target_rules[rule]
|
95
95
|
source_instance = source_template.bind_input(source)
|
96
|
-
source_template.generate_via_unify(source_instance, target_template) do |target|
|
96
|
+
source_template.generate_via_unify(source_instance, target_template).each do |target|
|
97
97
|
yield target
|
98
98
|
end
|
99
99
|
end
|
100
|
-
method_accumulate :targets_for_source, lambda { |engine, rule, source| Masamune::DataPlan::Set.new(engine.get_target_rule(rule)) }
|
101
100
|
|
102
|
-
def sources_for_target(rule, target
|
101
|
+
def sources_for_target(rule, target)
|
102
|
+
return Masamune::DataPlan::Set.new(get_source_rule(rule), to_enum(:sources_for_target, rule, target)) unless block_given?
|
103
103
|
source_template = @source_rules[rule]
|
104
104
|
target_template = @target_rules[rule]
|
105
105
|
target_instance = target_template.bind_input(target)
|
106
|
-
target_template.generate_via_unify(target_instance, source_template) do |source|
|
106
|
+
target_template.generate_via_unify(target_instance, source_template).each do |source|
|
107
107
|
yield source
|
108
108
|
end
|
109
109
|
end
|
110
|
-
method_accumulate :sources_for_target, lambda { |engine, rule, target| Masamune::DataPlan::Set.new(engine.get_source_rule(rule)) }
|
111
110
|
|
112
111
|
def targets(rule)
|
113
112
|
@set_cache[:targets_for_rule][rule] ||= @targets[rule].union(@sources[rule].targets)
|
@@ -146,7 +145,7 @@ class Masamune::DataPlan::Engine
|
|
146
145
|
@current_depth > 0
|
147
146
|
end
|
148
147
|
|
149
|
-
def constrain_max_depth(rule
|
148
|
+
def constrain_max_depth(rule)
|
150
149
|
@current_depth += 1
|
151
150
|
raise "Max depth of #{MAX_DEPTH} exceeded for rule '#{rule}'" if @current_depth > MAX_DEPTH
|
152
151
|
yield
|
@@ -31,8 +31,6 @@ require 'date'
|
|
31
31
|
class Masamune::DataPlan::Rule
|
32
32
|
TERMINAL = nil
|
33
33
|
|
34
|
-
include Masamune::Accumulate
|
35
|
-
|
36
34
|
attr_reader :engine, :name, :type, :options
|
37
35
|
|
38
36
|
def initialize(engine, name, type, options = {})
|
@@ -128,7 +126,8 @@ class Masamune::DataPlan::Rule
|
|
128
126
|
rule.bind_date(elem.start_time)
|
129
127
|
end
|
130
128
|
|
131
|
-
def generate(start_time, stop_time
|
129
|
+
def generate(start_time, stop_time)
|
130
|
+
return Set.new(to_enum(:generate, start_time, stop_time)) unless block_given?
|
132
131
|
instance = bind_date(start_time)
|
133
132
|
|
134
133
|
begin
|
@@ -136,9 +135,9 @@ class Masamune::DataPlan::Rule
|
|
136
135
|
instance = instance.next
|
137
136
|
end while instance.start_time <= stop_time
|
138
137
|
end
|
139
|
-
method_accumulate :generate
|
140
138
|
|
141
|
-
def generate_via_unify(elem, rule
|
139
|
+
def generate_via_unify(elem, rule)
|
140
|
+
return Set.new(to_enum(:generate_via_unify, elem, rule)) unless block_given?
|
142
141
|
instance = unify(elem, rule)
|
143
142
|
|
144
143
|
stop_time = instance.start_time.advance(time_step => 1)
|
@@ -147,7 +146,6 @@ class Masamune::DataPlan::Rule
|
|
147
146
|
instance = instance.next
|
148
147
|
end while instance.start_time < stop_time
|
149
148
|
end
|
150
|
-
method_accumulate :generate_via_unify
|
151
149
|
|
152
150
|
def tz
|
153
151
|
ActiveSupport::TimeZone[@options.fetch(:tz, 'UTC')]
|
@@ -199,6 +197,7 @@ class Masamune::DataPlan::Rule
|
|
199
197
|
end
|
200
198
|
|
201
199
|
def adjacent_matches(instance)
|
200
|
+
return Set.new(to_enum(:adjacent_matches, instance)) unless block_given?
|
202
201
|
(-window .. -1).each do |i|
|
203
202
|
yield instance.prev(i.abs)
|
204
203
|
end
|
@@ -207,7 +206,6 @@ class Masamune::DataPlan::Rule
|
|
207
206
|
yield instance.next(i)
|
208
207
|
end
|
209
208
|
end
|
210
|
-
method_accumulate :adjacent_matches
|
211
209
|
|
212
210
|
def inspect
|
213
211
|
{type: type, pattern: pattern, options: options}.to_s
|
@@ -25,8 +25,6 @@ require 'set'
|
|
25
25
|
class Masamune::DataPlan::Set < Set
|
26
26
|
EMPTY = self.new
|
27
27
|
|
28
|
-
include Masamune::Accumulate
|
29
|
-
|
30
28
|
attr_reader :rule
|
31
29
|
|
32
30
|
def initialize(rule, enum = nil)
|
@@ -46,50 +44,51 @@ class Masamune::DataPlan::Set < Set
|
|
46
44
|
super convert_elem(elem)
|
47
45
|
end
|
48
46
|
|
49
|
-
def missing
|
47
|
+
def missing
|
48
|
+
return self.class.new(rule, to_enum(__method__)) unless block_given?
|
50
49
|
self.each do |elem|
|
51
|
-
yield elem if elem.explode.
|
50
|
+
yield elem if elem.explode.count < 1
|
52
51
|
end
|
53
52
|
end
|
54
|
-
method_accumulate :missing, lambda { |set| set.class.new(set.rule) }
|
55
53
|
|
56
|
-
def existing
|
54
|
+
def existing
|
55
|
+
return self.class.new(rule, to_enum(__method__)) unless block_given?
|
57
56
|
self.each do |elem|
|
58
|
-
elem.explode do |new_elem|
|
57
|
+
elem.explode.each do |new_elem|
|
59
58
|
yield new_elem
|
60
59
|
end
|
61
60
|
end
|
62
61
|
end
|
63
|
-
method_accumulate :existing, lambda { |set| set.class.new(set.rule) }
|
64
62
|
|
65
|
-
def adjacent
|
63
|
+
def adjacent
|
64
|
+
return self.class.new(rule, to_enum(__method__)) unless block_given?
|
66
65
|
self.each do |elem|
|
67
|
-
@rule.adjacent_matches(elem) do |adj_elem|
|
66
|
+
@rule.adjacent_matches(elem).each do |adj_elem|
|
68
67
|
yield adj_elem
|
69
68
|
end
|
70
69
|
end
|
71
70
|
end
|
72
|
-
method_accumulate :adjacent, lambda { |set| set.class.new(set.rule) }
|
73
71
|
|
74
|
-
def stale
|
72
|
+
def stale
|
75
73
|
return Masamune::DataPlan::Set::EMPTY if empty? || @rule.for_sources?
|
74
|
+
return self.class.new(rule, to_enum(__method__)) unless block_given?
|
76
75
|
self.each do |target|
|
77
76
|
yield target if target.sources.existing.any? { |source| target_stale?(source, target) }
|
78
77
|
end
|
79
78
|
end
|
80
|
-
method_accumulate :stale, lambda { |set| set.class.new(set.rule) }
|
81
79
|
|
82
|
-
def incomplete
|
80
|
+
def incomplete
|
83
81
|
return Masamune::DataPlan::Set::EMPTY if empty? || @rule.for_sources?
|
82
|
+
return self.class.new(rule, to_enum(__method__)) unless block_given?
|
84
83
|
set = Set.new
|
85
84
|
self.each do |target|
|
86
85
|
yield target if set.add?(target) unless target.complete?
|
87
86
|
end
|
88
87
|
end
|
89
|
-
method_accumulate :incomplete, lambda { |set| set.class.new(set.rule) }
|
90
88
|
|
91
|
-
def actionable
|
89
|
+
def actionable
|
92
90
|
return Masamune::DataPlan::Set::EMPTY if empty? || @rule.for_sources?
|
91
|
+
return self.class.new(rule, to_enum(__method__)) unless block_given?
|
93
92
|
set = Set.new
|
94
93
|
missing.each do |target|
|
95
94
|
yield target if set.add?(target)
|
@@ -101,41 +100,42 @@ class Masamune::DataPlan::Set < Set
|
|
101
100
|
yield target if set.add?(target)
|
102
101
|
end
|
103
102
|
end
|
104
|
-
method_accumulate :actionable, lambda { |set| set.class.new(set.rule) }
|
105
103
|
|
106
|
-
def updateable
|
104
|
+
def updateable
|
107
105
|
return Masamune::DataPlan::Set::EMPTY if empty? || @rule.for_sources?
|
106
|
+
return self.class.new(rule, to_enum(__method__)) unless block_given?
|
108
107
|
set = Set.new
|
109
108
|
actionable.each do |target|
|
110
109
|
yield target if set.add?(target) && target.sources.existing.any?
|
111
110
|
end
|
112
111
|
end
|
113
|
-
method_accumulate :updateable, lambda { |set| set.class.new(set.rule) }
|
114
112
|
|
115
113
|
# TODO detect & warn or correct if coarser grain set is incomplete
|
116
|
-
def with_grain(grain
|
114
|
+
def with_grain(grain)
|
115
|
+
return self.class.new(rule.round(grain), to_enum(:with_grain, grain)) unless block_given?
|
117
116
|
seen = Set.new
|
118
117
|
self.each do |elem|
|
119
118
|
granular_elem = elem.round(grain)
|
120
119
|
yield granular_elem if seen.add?(granular_elem)
|
121
120
|
end
|
122
121
|
end
|
123
|
-
method_accumulate :with_grain, lambda { |set, grain| set.class.new(set.rule.round(grain)) }
|
124
122
|
|
125
123
|
def targets
|
126
124
|
return Masamune::DataPlan::Set::EMPTY if empty? || @rule.for_targets?
|
127
|
-
self.class.new(self.first.targets.rule)
|
128
|
-
|
129
|
-
|
125
|
+
return self.class.new(self.first.targets.rule, to_enum(__method__)) unless block_given?
|
126
|
+
self.each do |elem|
|
127
|
+
elem.targets.each do |target|
|
128
|
+
yield target
|
130
129
|
end
|
131
130
|
end
|
132
131
|
end
|
133
132
|
|
134
133
|
def sources
|
135
134
|
return Masamune::DataPlan::Set::EMPTY if empty? || @rule.for_sources?
|
136
|
-
self.class.new(self.first.sources.rule)
|
137
|
-
|
138
|
-
|
135
|
+
return self.class.new(self.first.sources.rule, to_enum(__method__)) unless block_given?
|
136
|
+
self.each do |elem|
|
137
|
+
elem.sources.each do |source|
|
138
|
+
yield source
|
139
139
|
end
|
140
140
|
end
|
141
141
|
end
|
@@ -159,7 +159,7 @@ class Masamune::DataPlan::Set < Set
|
|
159
159
|
when nil
|
160
160
|
when Array
|
161
161
|
enum.flatten.uniq
|
162
|
-
when Set, self.class
|
162
|
+
when Set, self.class, Enumerator
|
163
163
|
enum
|
164
164
|
when String
|
165
165
|
[enum]
|
data/lib/masamune/environment.rb
CHANGED
@@ -89,9 +89,7 @@ module Masamune
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def logger
|
92
|
-
@logger ||= Logger.new(log_file_io)
|
93
|
-
symlink_latest_log
|
94
|
-
end
|
92
|
+
@logger ||= Logger.new(log_file_io)
|
95
93
|
end
|
96
94
|
|
97
95
|
def console(*a)
|
@@ -151,14 +149,5 @@ module Masamune
|
|
151
149
|
configuration.debug ? $stderr : nil
|
152
150
|
end
|
153
151
|
end
|
154
|
-
|
155
|
-
def symlink_latest_log
|
156
|
-
return unless filesystem.has_path?(:log_dir)
|
157
|
-
latest = filesystem.path(:log_dir, 'latest')
|
158
|
-
FileUtils.rm(latest) if File.exists?(latest)
|
159
|
-
FileUtils.ln_s(log_file_name, latest)
|
160
|
-
rescue => e
|
161
|
-
logger.error(e)
|
162
|
-
end
|
163
152
|
end
|
164
153
|
end
|
data/lib/masamune/filesystem.rb
CHANGED
@@ -25,7 +25,6 @@ require 'masamune/has_environment'
|
|
25
25
|
module Masamune
|
26
26
|
class Filesystem
|
27
27
|
include Masamune::HasEnvironment
|
28
|
-
include Masamune::Accumulate
|
29
28
|
include Masamune::Actions::S3Cmd
|
30
29
|
include Masamune::Actions::HadoopFilesystem
|
31
30
|
|
@@ -88,7 +87,7 @@ module Masamune
|
|
88
87
|
path[0] != '/'
|
89
88
|
end
|
90
89
|
|
91
|
-
def parent_paths(path
|
90
|
+
def parent_paths(path)
|
92
91
|
if prefix = remote_prefix(path)
|
93
92
|
node = path.split(prefix).last
|
94
93
|
else
|
@@ -96,25 +95,26 @@ module Masamune
|
|
96
95
|
node = path
|
97
96
|
end
|
98
97
|
|
99
|
-
return if prefix.blank? && node.blank?
|
98
|
+
return [] if prefix.blank? && node.blank?
|
100
99
|
parent_paths = node ? File.expand_path(node, '/').split('/') : []
|
101
100
|
parent_paths.reject! { |x| x.blank? }
|
102
101
|
parent_paths.prepend('/') if node =~ %r{\A/}
|
103
102
|
tmp = []
|
103
|
+
result = []
|
104
104
|
parent_paths.each do |part|
|
105
105
|
tmp << part
|
106
106
|
current_path = prefix + File.join(tmp)
|
107
107
|
break if current_path == path
|
108
|
-
|
108
|
+
result << current_path
|
109
109
|
end
|
110
|
+
result
|
110
111
|
end
|
111
|
-
method_accumulate :parent_paths
|
112
112
|
|
113
113
|
def root_path?(path)
|
114
114
|
raise ArgumentError, 'path cannot be nil' if path.nil?
|
115
115
|
raise ArgumentError, 'path cannot be blank' if path.blank?
|
116
116
|
raise ArgumentError, 'path cannot be relative' if relative_path?(path)
|
117
|
-
parent_paths(path).
|
117
|
+
parent_paths(path).count < 1
|
118
118
|
end
|
119
119
|
|
120
120
|
def resolve_file(paths = [])
|
@@ -167,7 +167,8 @@ module Masamune
|
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
|
-
def glob_stat(pattern
|
170
|
+
def glob_stat(pattern)
|
171
|
+
return Set.new(to_enum(:glob_stat, pattern)) unless block_given?
|
171
172
|
case type(pattern)
|
172
173
|
when :hdfs
|
173
174
|
hadoop_fs('-ls', '-R', pattern, safe: true) do |line|
|
@@ -193,7 +194,6 @@ module Masamune
|
|
193
194
|
end
|
194
195
|
end
|
195
196
|
end
|
196
|
-
method_accumulate :glob_stat
|
197
197
|
|
198
198
|
def stat(file_or_dir)
|
199
199
|
raise ArgumentError, 'cannot contain wildcard' if file_or_dir.include?('*')
|
@@ -219,7 +219,8 @@ module Masamune
|
|
219
219
|
end
|
220
220
|
end
|
221
221
|
|
222
|
-
def glob(pattern
|
222
|
+
def glob(pattern)
|
223
|
+
return Set.new(to_enum(:glob, pattern)) unless block_given?
|
223
224
|
case type(pattern)
|
224
225
|
when :hdfs
|
225
226
|
file_glob, file_regexp = glob_split(pattern)
|
@@ -245,7 +246,6 @@ module Masamune
|
|
245
246
|
end
|
246
247
|
end
|
247
248
|
end
|
248
|
-
method_accumulate :glob
|
249
249
|
|
250
250
|
def glob_sort(pattern, options = {})
|
251
251
|
result = glob(pattern)
|
@@ -115,7 +115,7 @@ module Masamune::Schema
|
|
115
115
|
@stores[store_id.to_sym]
|
116
116
|
end
|
117
117
|
|
118
|
-
def table(id, options = {}
|
118
|
+
def table(id, options = {})
|
119
119
|
@context.push(options)
|
120
120
|
yield if block_given?
|
121
121
|
@context.tables[id] ||= Masamune::Schema::Table.new(@context.options.merge(id: id))
|
@@ -124,7 +124,7 @@ module Masamune::Schema
|
|
124
124
|
@context.pop
|
125
125
|
end
|
126
126
|
|
127
|
-
def dimension(id, options = {}
|
127
|
+
def dimension(id, options = {})
|
128
128
|
@context.push(options)
|
129
129
|
yield if block_given?
|
130
130
|
@context.dimensions[id] ||= Masamune::Schema::Dimension.new(@context.options.merge(id: id))
|
@@ -133,7 +133,7 @@ module Masamune::Schema
|
|
133
133
|
@context.pop
|
134
134
|
end
|
135
135
|
|
136
|
-
def column(id, options = {}
|
136
|
+
def column(id, options = {})
|
137
137
|
@context.options[:columns] << dereference_column(id, options)
|
138
138
|
end
|
139
139
|
|
@@ -152,7 +152,7 @@ module Masamune::Schema
|
|
152
152
|
@context.options[:rows] << Masamune::Schema::Row.new(attributes)
|
153
153
|
end
|
154
154
|
|
155
|
-
def fact(id, options = {}
|
155
|
+
def fact(id, options = {})
|
156
156
|
@context.push(options)
|
157
157
|
grain = Array.wrap(options.delete(:grain) || [])
|
158
158
|
fact_attributes(grain).each do |attributes|
|
@@ -164,15 +164,15 @@ module Masamune::Schema
|
|
164
164
|
@context.pop
|
165
165
|
end
|
166
166
|
|
167
|
-
def partition(id, options = {}
|
167
|
+
def partition(id, options = {})
|
168
168
|
@context.options[:columns] << Masamune::Schema::Column.new(options.merge(id: id, partition: true))
|
169
169
|
end
|
170
170
|
|
171
|
-
def measure(id, options = {}
|
171
|
+
def measure(id, options = {})
|
172
172
|
@context.options[:columns] << Masamune::Schema::Column.new(options.merge(id: id, measure: true))
|
173
173
|
end
|
174
174
|
|
175
|
-
def file(id, options = {}
|
175
|
+
def file(id, options = {})
|
176
176
|
format_options = options.extract!(:format, :headers)
|
177
177
|
@context.push(options)
|
178
178
|
yield if block_given?
|
@@ -182,7 +182,7 @@ module Masamune::Schema
|
|
182
182
|
@context.pop
|
183
183
|
end
|
184
184
|
|
185
|
-
def event(id, options = {}
|
185
|
+
def event(id, options = {})
|
186
186
|
@context.push(options)
|
187
187
|
yield if block_given?
|
188
188
|
@context.events[id] = HasMap.new Masamune::Schema::Event.new(@context.options.merge(id: id))
|
@@ -190,7 +190,7 @@ module Masamune::Schema
|
|
190
190
|
@context.pop
|
191
191
|
end
|
192
192
|
|
193
|
-
def attribute(id, options = {}
|
193
|
+
def attribute(id, options = {})
|
194
194
|
@context.options[:attributes] << Masamune::Schema::Event::Attribute.new(options.merge(id: id))
|
195
195
|
end
|
196
196
|
|
@@ -376,7 +376,7 @@ module Masamune::Schema
|
|
376
376
|
|
377
377
|
# TODO: Add ELEMENT REFERENCES
|
378
378
|
def reference_constraint
|
379
|
-
return if parent.temporary?
|
379
|
+
return if parent && parent.temporary?
|
380
380
|
return if degenerate?
|
381
381
|
return if array_value?
|
382
382
|
if reference && reference.surrogate_key.type == type
|
@@ -465,7 +465,7 @@ module Masamune::Schema
|
|
465
465
|
if reference
|
466
466
|
!(reference.null || reference.default)
|
467
467
|
else
|
468
|
-
surrogate_key || natural_key
|
468
|
+
surrogate_key || natural_key || !(null || default)
|
469
469
|
end
|
470
470
|
end
|
471
471
|
|
@@ -126,7 +126,8 @@ module Masamune::Schema
|
|
126
126
|
# TODO: Default to GIN for array columns
|
127
127
|
def index_columns
|
128
128
|
index_column_map.map do |_, column_names|
|
129
|
-
|
129
|
+
unique_index = reverse_unique_constraints_map.key?(column_names.sort)
|
130
|
+
[column_names, unique_index, short_md5(column_names)]
|
130
131
|
end
|
131
132
|
end
|
132
133
|
|
@@ -292,7 +293,7 @@ module Masamune::Schema
|
|
292
293
|
column.unique.each do |unique|
|
293
294
|
map[unique] << column.name
|
294
295
|
end
|
295
|
-
end
|
296
|
+
end unless temporary?
|
296
297
|
Hash[map.sort_by { |k, v| v.length }]
|
297
298
|
end
|
298
299
|
end
|
data/lib/masamune/version.rb
CHANGED
@@ -20,41 +20,29 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
23
|
+
require 'spec_helper'
|
24
|
+
|
25
|
+
describe Masamune::Actions::InvokeParallel do
|
26
|
+
let(:klass) do
|
27
|
+
Class.new(Thor) do
|
28
|
+
include Masamune::HasEnvironment
|
29
|
+
include Masamune::Actions::InvokeParallel
|
31
30
|
end
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
def method_accumulate(method, accumulator = lambda { |_, *args| Array.new })
|
35
|
-
self.class_eval do
|
36
|
-
new_method = "#{method}_with_accumulate".to_sym
|
37
|
-
old_method = "#{method}_without_accumulate".to_sym
|
38
|
-
alias_method old_method, method
|
33
|
+
let(:instance) { klass.new }
|
39
34
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
accumulate(old_method, accumulator, *args)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
alias_method method, new_method
|
52
|
-
end
|
35
|
+
describe '.invoke_parallel' do
|
36
|
+
context 'with a simple thor command' do
|
37
|
+
before do
|
38
|
+
mock_command(/\Athor list/, mock_success)
|
39
|
+
end
|
40
|
+
|
41
|
+
subject do
|
42
|
+
instance.invoke_parallel('list', max_tasks: 1)
|
53
43
|
end
|
54
|
-
end
|
55
44
|
|
56
|
-
|
57
|
-
base.extend ClassMethods
|
45
|
+
it { expect { subject }.to_not raise_error }
|
58
46
|
end
|
59
47
|
end
|
60
48
|
end
|
@@ -40,8 +40,8 @@ describe Masamune::CachedFilesystem do
|
|
40
40
|
expect(cached_filesystem.exists?('/a/b/c/3.txt')).to eq(true)
|
41
41
|
expect(cached_filesystem.exists?('/a/b/c/4.txt')).to eq(false)
|
42
42
|
expect(cached_filesystem.exists?('/a/b/c')).to eq(true)
|
43
|
-
expect(cached_filesystem.glob('/a/b/c/*')).
|
44
|
-
expect(cached_filesystem.glob('/a/b/c/*.txt')).
|
43
|
+
expect(cached_filesystem.glob('/a/b/c/*').count).to eq(3)
|
44
|
+
expect(cached_filesystem.glob('/a/b/c/*.txt').count).to eq(3)
|
45
45
|
expect(cached_filesystem.stat('/a/b/c/1.txt')).to_not be_nil
|
46
46
|
expect(cached_filesystem.stat('/a/b/c/2.txt')).to_not be_nil
|
47
47
|
expect(cached_filesystem.stat('/a/b/c/3.txt')).to_not be_nil
|
@@ -60,7 +60,9 @@ describe Masamune::CachedFilesystem do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'calls Filesystem#glob_stat once for multiple calls' do
|
63
|
-
expect(cached_filesystem.glob('/a/b/*')).to
|
63
|
+
expect(cached_filesystem.glob('/a/b/*')).to include '/a/b/c/1.txt'
|
64
|
+
expect(cached_filesystem.glob('/a/b/*')).to include '/a/b/c/2.txt'
|
65
|
+
expect(cached_filesystem.glob('/a/b/*')).to include '/a/b/c/3.txt'
|
64
66
|
expect(cached_filesystem.exists?('/a/b/c/1.txt')).to eq(true)
|
65
67
|
expect(cached_filesystem.exists?('/a/b/c/2.txt')).to eq(true)
|
66
68
|
expect(cached_filesystem.exists?('/a/b/c/3.txt')).to eq(true)
|
@@ -87,8 +89,8 @@ describe Masamune::CachedFilesystem do
|
|
87
89
|
expect(cached_filesystem.exists?('/y=2013/m=1/d=22/00000')).to eq(true)
|
88
90
|
expect(cached_filesystem.exists?('/y=2013/m=1/d=22')).to eq(true)
|
89
91
|
expect(cached_filesystem.exists?('/y=2013/m=1/d=2')).to eq(false)
|
90
|
-
expect(cached_filesystem.glob('/y=2013/m=1/*')).
|
91
|
-
expect(cached_filesystem.glob('/y=2013/m=1/d=22/*')).
|
92
|
+
expect(cached_filesystem.glob('/y=2013/m=1/*').count).to eq(1)
|
93
|
+
expect(cached_filesystem.glob('/y=2013/m=1/d=22/*').count).to eq(1)
|
92
94
|
expect(cached_filesystem.stat('/y=2013/m=1/d=22/00000')).not_to be_nil
|
93
95
|
expect(cached_filesystem.stat('/y=2013/m=1/d=22')).not_to be_nil
|
94
96
|
expect(cached_filesystem.stat('/y=2013/m=1')).not_to be_nil
|
@@ -113,15 +115,15 @@ describe Masamune::CachedFilesystem do
|
|
113
115
|
expect(cached_filesystem.exists?('/logs/box4_123.txt')).to eq(false)
|
114
116
|
expect(cached_filesystem.exists?('/logs/box4_456.txt')).to eq(false)
|
115
117
|
expect(cached_filesystem.exists?('/logs/box')).to eq(false)
|
116
|
-
expect(cached_filesystem.glob('/logs/*')).
|
117
|
-
expect(cached_filesystem.glob('/logs/*.txt')).
|
118
|
-
expect(cached_filesystem.glob('/logs/box1_*.txt')).
|
119
|
-
expect(cached_filesystem.glob('/logs/box2_*.txt')).
|
120
|
-
expect(cached_filesystem.glob('/logs/box3_*.txt')).
|
121
|
-
expect(cached_filesystem.glob('/logs/box*.txt').
|
122
|
-
expect(cached_filesystem.glob('/logs/box*.csv')).to
|
123
|
-
expect(cached_filesystem.glob('/logs/box')).to
|
124
|
-
expect(cached_filesystem.glob('/logs/box/*')).to
|
118
|
+
expect(cached_filesystem.glob('/logs/*').count).to eq(3)
|
119
|
+
expect(cached_filesystem.glob('/logs/*.txt').count).to eq(3)
|
120
|
+
expect(cached_filesystem.glob('/logs/box1_*.txt').count).to eq(1)
|
121
|
+
expect(cached_filesystem.glob('/logs/box2_*.txt').count).to eq(1)
|
122
|
+
expect(cached_filesystem.glob('/logs/box3_*.txt').count).to eq(1)
|
123
|
+
expect(cached_filesystem.glob('/logs/box*.txt').count).to eq(3)
|
124
|
+
expect(cached_filesystem.glob('/logs/box*.csv').count).to eq(0)
|
125
|
+
expect(cached_filesystem.glob('/logs/box').count).to eq(0)
|
126
|
+
expect(cached_filesystem.glob('/logs/box/*').count).to eq(0)
|
125
127
|
expect(cached_filesystem.stat('/logs/box1_123.txt')).to_not be_nil
|
126
128
|
expect(cached_filesystem.stat('/logs/box1_456.txt')).to be_nil
|
127
129
|
expect(cached_filesystem.stat('/logs/box2_123.txt')).to_not be_nil
|
@@ -149,13 +151,13 @@ describe Masamune::CachedFilesystem do
|
|
149
151
|
expect(cached_filesystem.exists?('/a/b/c')).to eq(true)
|
150
152
|
expect(cached_filesystem.exists?('/a/b')).to eq(true)
|
151
153
|
expect(cached_filesystem.exists?('/a')).to eq(true)
|
152
|
-
expect(cached_filesystem.glob('/a')).to
|
153
|
-
expect(cached_filesystem.glob('/a/*')).
|
154
|
-
expect(cached_filesystem.glob('/a/b')).to
|
155
|
-
expect(cached_filesystem.glob('/a/b/*')).
|
156
|
-
expect(cached_filesystem.glob('/a/b/c')).
|
157
|
-
expect(cached_filesystem.glob('/a/b/c/*')).to
|
158
|
-
expect(cached_filesystem.glob('/a/b/c/*.txt')).to
|
154
|
+
expect(cached_filesystem.glob('/a').count).to eq(0)
|
155
|
+
expect(cached_filesystem.glob('/a/*').count).to eq(1)
|
156
|
+
expect(cached_filesystem.glob('/a/b').count).to eq(0)
|
157
|
+
expect(cached_filesystem.glob('/a/b/*').count).to eq(1)
|
158
|
+
expect(cached_filesystem.glob('/a/b/c').count).to eq(1)
|
159
|
+
expect(cached_filesystem.glob('/a/b/c/*').count).to eq(0)
|
160
|
+
expect(cached_filesystem.glob('/a/b/c/*.txt').count).to eq(0)
|
159
161
|
expect(cached_filesystem.stat('/a/b/c/1.txt')).to be_nil
|
160
162
|
expect(cached_filesystem.stat('/a/b/c/2.txt')).to be_nil
|
161
163
|
expect(cached_filesystem.stat('/a/b/c/3.txt')).to be_nil
|
@@ -43,7 +43,7 @@ describe Masamune::DataPlan::Set do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
context 'when all missing' do
|
46
|
-
it { expect(missing.
|
46
|
+
it { expect(missing.count).to eq(3) }
|
47
47
|
it { expect(missing).to include '/table/y=2013/m=01/d=01' }
|
48
48
|
it { expect(missing).to include '/table/y=2013/m=01/d=02' }
|
49
49
|
it { expect(missing).to include '/table/y=2013/m=01/d=03' }
|
@@ -53,7 +53,7 @@ describe Masamune::DataPlan::Set do
|
|
53
53
|
before do
|
54
54
|
fs.touch!('/table/y=2013/m=01/d=01', '/table/y=2013/m=01/d=02')
|
55
55
|
end
|
56
|
-
it { expect(missing.
|
56
|
+
it { expect(missing.count).to eq(3) }
|
57
57
|
it { expect(missing).to include '/table/y=2013/m=01/d=01' }
|
58
58
|
it { expect(missing).to include '/table/y=2013/m=01/d=02' }
|
59
59
|
it { expect(missing).to include '/table/y=2013/m=01/d=03' }
|
@@ -63,7 +63,7 @@ describe Masamune::DataPlan::Set do
|
|
63
63
|
before do
|
64
64
|
fs.touch!('/table/y=2013/m=01/d=01/0000', '/table/y=2013/m=01/d=02/0000')
|
65
65
|
end
|
66
|
-
it { expect(missing.
|
66
|
+
it { expect(missing.count).to eq(1) }
|
67
67
|
it { expect(missing).to include '/table/y=2013/m=01/d=03' }
|
68
68
|
end
|
69
69
|
|
@@ -93,7 +93,7 @@ describe Masamune::DataPlan::Set do
|
|
93
93
|
before do
|
94
94
|
fs.touch!('/log/20130101.random_1.log', '/log/20130101.random_2.log')
|
95
95
|
end
|
96
|
-
it { expect(existing.
|
96
|
+
it { expect(existing.count).to eq(1) }
|
97
97
|
it { expect(existing).to include '/log/20130101.random_1.log' }
|
98
98
|
end
|
99
99
|
|
@@ -102,7 +102,7 @@ describe Masamune::DataPlan::Set do
|
|
102
102
|
fs.touch!('/log/20130101.random_1.log', '/log/20130101.random_2.log')
|
103
103
|
fs.touch!('/log/20130102.random_1.log', '/log/20130102.random_2.log')
|
104
104
|
end
|
105
|
-
it { expect(existing.
|
105
|
+
it { expect(existing.count).to eq(2) }
|
106
106
|
it { expect(existing).to include '/log/20130101.random_1.log' }
|
107
107
|
it { expect(existing).to include '/log/20130102.random_1.log' }
|
108
108
|
end
|
@@ -119,7 +119,7 @@ describe Masamune::DataPlan::Set do
|
|
119
119
|
before do
|
120
120
|
fs.touch!('/log/20130101.random_1.log', '/log/20130101.random_2.log')
|
121
121
|
end
|
122
|
-
it { expect(existing.
|
122
|
+
it { expect(existing.count).to eq(2) }
|
123
123
|
it { expect(existing).to include '/log/20130101.random_1.log' }
|
124
124
|
it { expect(existing).to include '/log/20130101.random_2.log' }
|
125
125
|
end
|
@@ -129,7 +129,7 @@ describe Masamune::DataPlan::Set do
|
|
129
129
|
fs.touch!('/log/20130101.random_1.log', '/log/20130101.random_2.log')
|
130
130
|
fs.touch!('/log/20130102.random_1.log', '/log/20130102.random_2.log')
|
131
131
|
end
|
132
|
-
it { expect(existing.
|
132
|
+
it { expect(existing.count).to eq(4) }
|
133
133
|
it { expect(existing).to include '/log/20130101.random_1.log' }
|
134
134
|
it { expect(existing).to include '/log/20130101.random_2.log' }
|
135
135
|
it { expect(existing).to include '/log/20130102.random_1.log' }
|
@@ -156,7 +156,7 @@ describe Masamune::DataPlan::Set do
|
|
156
156
|
allow(instance.rule).to receive(:window) { 1 }
|
157
157
|
end
|
158
158
|
|
159
|
-
it { expect(sources.
|
159
|
+
it { expect(sources.count).to eq(3) }
|
160
160
|
it { expect(sources).to include '/log/20121231.*.log' }
|
161
161
|
it { expect(sources).to include '/log/20130101.*.log' }
|
162
162
|
it { expect(sources).to include '/log/20130102.*.log' }
|
@@ -169,7 +169,7 @@ describe Masamune::DataPlan::Set do
|
|
169
169
|
before do
|
170
170
|
fs.touch!('/log/20130101.random_1.log', '/log/20130101.random_2.log')
|
171
171
|
end
|
172
|
-
it { expect(existing.
|
172
|
+
it { expect(existing.count).to eq(2) }
|
173
173
|
it { expect(existing).to include '/log/20130101.random_1.log' }
|
174
174
|
it { expect(existing).to include '/log/20130101.random_2.log' }
|
175
175
|
end
|
@@ -181,7 +181,7 @@ describe Masamune::DataPlan::Set do
|
|
181
181
|
fs.touch!('/log/20130102.random_1.log', '/log/20130102.random_2.log')
|
182
182
|
fs.touch!('/log/20130103.random_1.log', '/log/20130103.random_2.log')
|
183
183
|
end
|
184
|
-
it { expect(existing.
|
184
|
+
it { expect(existing.count).to eq(6) }
|
185
185
|
it { expect(existing).to include '/log/20121231.random_1.log' }
|
186
186
|
it { expect(existing).to include '/log/20121231.random_2.log' }
|
187
187
|
it { expect(existing).to include '/log/20130101.random_1.log' }
|
@@ -230,7 +230,7 @@ describe Masamune::DataPlan::Set do
|
|
230
230
|
fs.touch!('/log/20130101.random_1.log', mtime: future_time)
|
231
231
|
end
|
232
232
|
|
233
|
-
it { expect(stale_targets.
|
233
|
+
it { expect(stale_targets.count).to eq(1) }
|
234
234
|
it { expect(stale_targets).to include '/table/y=2013/m=01/d=01' }
|
235
235
|
end
|
236
236
|
|
@@ -239,7 +239,7 @@ describe Masamune::DataPlan::Set do
|
|
239
239
|
fs.touch!('/log/20130101.random_2.log', mtime: future_time)
|
240
240
|
end
|
241
241
|
|
242
|
-
it { expect(stale_targets.
|
242
|
+
it { expect(stale_targets.count).to eq(1) }
|
243
243
|
it { expect(stale_targets).to include '/table/y=2013/m=01/d=01' }
|
244
244
|
end
|
245
245
|
|
@@ -248,7 +248,7 @@ describe Masamune::DataPlan::Set do
|
|
248
248
|
fs.touch!('/log/20130101.random_1.log', mtime: present_time)
|
249
249
|
end
|
250
250
|
|
251
|
-
it { expect(stale_targets.
|
251
|
+
it { expect(stale_targets.count).to eq(1) }
|
252
252
|
it { expect(stale_targets).to include '/table/y=2013/m=01/d=01' }
|
253
253
|
end
|
254
254
|
|
@@ -260,7 +260,7 @@ describe Masamune::DataPlan::Set do
|
|
260
260
|
fs.touch!('/log/20130103.random_2.log', mtime: future_time)
|
261
261
|
end
|
262
262
|
|
263
|
-
it { expect(stale_targets.
|
263
|
+
it { expect(stale_targets.count).to eq(3) }
|
264
264
|
it { expect(stale_targets).to include '/table/y=2013/m=01/d=01' }
|
265
265
|
it { expect(stale_targets).to include '/table/y=2013/m=01/d=02' }
|
266
266
|
it { expect(stale_targets).to include '/table/y=2013/m=01/d=03' }
|
@@ -288,7 +288,7 @@ describe Masamune::DataPlan::Set do
|
|
288
288
|
context 'when :day' do
|
289
289
|
let(:grain) { :day }
|
290
290
|
it 'has 6 items' do
|
291
|
-
expect(subject.
|
291
|
+
expect(subject.count).to eq(6)
|
292
292
|
end
|
293
293
|
it { is_expected.to include '/table/y=2012/m=12/d=29' }
|
294
294
|
it { is_expected.to include '/table/y=2012/m=12/d=30' }
|
@@ -301,7 +301,7 @@ describe Masamune::DataPlan::Set do
|
|
301
301
|
context 'when :month' do
|
302
302
|
let(:grain) { :month }
|
303
303
|
it 'has 3 items' do
|
304
|
-
expect(subject.
|
304
|
+
expect(subject.count).to eq(3)
|
305
305
|
end
|
306
306
|
it { is_expected.to include '/table/y=2012/m=12' }
|
307
307
|
it { is_expected.to include '/table/y=2013/m=01' }
|
@@ -311,7 +311,7 @@ describe Masamune::DataPlan::Set do
|
|
311
311
|
context 'when :year' do
|
312
312
|
let(:grain) { :year }
|
313
313
|
it 'has 2 items' do
|
314
|
-
expect(subject.
|
314
|
+
expect(subject.count).to eq(2)
|
315
315
|
end
|
316
316
|
it { is_expected.to include '/table/y=2012' }
|
317
317
|
it { is_expected.to include '/table/y=2013' }
|
@@ -366,7 +366,7 @@ describe Masamune::DataPlan::Set do
|
|
366
366
|
end
|
367
367
|
|
368
368
|
context 'when all incomplete' do
|
369
|
-
it { expect(incomplete.
|
369
|
+
it { expect(incomplete.count).to eq(2) }
|
370
370
|
it { expect(incomplete).to include '/table/y=2014/m=01' }
|
371
371
|
it { expect(incomplete).to include '/table/y=2014/m=02' }
|
372
372
|
end
|
@@ -378,7 +378,7 @@ describe Masamune::DataPlan::Set do
|
|
378
378
|
end
|
379
379
|
end
|
380
380
|
|
381
|
-
it { expect(incomplete.
|
381
|
+
it { expect(incomplete.count).to eq(1) }
|
382
382
|
it { expect(incomplete).to include '/table/y=2014/m=02' }
|
383
383
|
end
|
384
384
|
|
@@ -390,7 +390,7 @@ describe Masamune::DataPlan::Set do
|
|
390
390
|
end
|
391
391
|
end
|
392
392
|
|
393
|
-
it { expect(incomplete.
|
393
|
+
it { expect(incomplete.count).to eq(0) }
|
394
394
|
end
|
395
395
|
end
|
396
396
|
|
@@ -432,7 +432,7 @@ describe Masamune::DataPlan::Set do
|
|
432
432
|
it 'actionable is equivalent to stale' do
|
433
433
|
expect(actionable).to eq(instance.stale)
|
434
434
|
end
|
435
|
-
it { expect(updateable.
|
435
|
+
it { expect(updateable.count).to eq(3) }
|
436
436
|
it { expect(updateable).to include '/table/y=2013/m=01/d=01' }
|
437
437
|
it { expect(updateable).to include '/table/y=2013/m=01/d=02' }
|
438
438
|
it { expect(updateable).to include '/table/y=2013/m=01/d=03' }
|
@@ -464,7 +464,7 @@ describe Masamune::DataPlan::Set do
|
|
464
464
|
it 'actionable is equivalent to missing' do
|
465
465
|
expect(actionable).to eq(instance.missing)
|
466
466
|
end
|
467
|
-
it { expect(updateable.
|
467
|
+
it { expect(updateable.count).to eq(1) }
|
468
468
|
it { expect(updateable).to include '/table/y=2013/m=01/d=01' }
|
469
469
|
end
|
470
470
|
|
@@ -477,7 +477,7 @@ describe Masamune::DataPlan::Set do
|
|
477
477
|
it 'actionable is equivalent to missing' do
|
478
478
|
expect(actionable).to eq(instance.missing)
|
479
479
|
end
|
480
|
-
it { expect(updateable.
|
480
|
+
it { expect(updateable.count).to eq(3) }
|
481
481
|
it { expect(updateable).to include '/table/y=2013/m=01/d=01' }
|
482
482
|
it { expect(updateable).to include '/table/y=2013/m=01/d=02' }
|
483
483
|
it { expect(updateable).to include '/table/y=2013/m=01/d=03' }
|
@@ -495,9 +495,9 @@ describe Masamune::DataPlan::Set do
|
|
495
495
|
|
496
496
|
context 'when sources are missing' do
|
497
497
|
it 'should chain expectedly' do
|
498
|
-
expect(instance.targets.
|
499
|
-
expect(instance.targets.sources.existing.
|
500
|
-
expect(instance.targets.sources.existing.targets.
|
498
|
+
expect(instance.targets.count).to eq(2)
|
499
|
+
expect(instance.targets.sources.existing.count).to eq(0)
|
500
|
+
expect(instance.targets.sources.existing.targets.count).to eq(0)
|
501
501
|
end
|
502
502
|
end
|
503
503
|
|
@@ -507,10 +507,10 @@ describe Masamune::DataPlan::Set do
|
|
507
507
|
end
|
508
508
|
|
509
509
|
it 'should chain expectedly' do
|
510
|
-
expect(instance.targets.
|
511
|
-
expect(instance.targets.sources.existing.
|
512
|
-
expect(instance.targets.sources.existing.targets.
|
513
|
-
expect(instance.targets.sources.existing.targets.sources.existing.
|
510
|
+
expect(instance.targets.count).to eq(2)
|
511
|
+
expect(instance.targets.sources.existing.count).to eq(4)
|
512
|
+
expect(instance.targets.sources.existing.targets.count).to eq(2)
|
513
|
+
expect(instance.targets.sources.existing.targets.sources.existing.count).to eq(4)
|
514
514
|
end
|
515
515
|
end
|
516
516
|
end
|
@@ -590,20 +590,20 @@ shared_examples_for 'Filesystem' do
|
|
590
590
|
|
591
591
|
context 'local no matches' do
|
592
592
|
let(:pattern) { File.join(new_dir, '*') }
|
593
|
-
it {
|
593
|
+
it { expect(subject.count).to eq(0) }
|
594
594
|
it { expect { |b| instance.glob(pattern, &b) }.to_not yield_control }
|
595
595
|
end
|
596
596
|
|
597
597
|
context 'local one matches' do
|
598
598
|
let(:pattern) { File.join(File.dirname(old_file), '*') }
|
599
|
-
it {
|
599
|
+
it { expect(subject.count).to eq(1) }
|
600
600
|
it { expect { |b| instance.glob(pattern, &b) }.to yield_with_args(old_file) }
|
601
601
|
end
|
602
602
|
|
603
603
|
context 'local one matches (recursive)' do
|
604
604
|
let(:pattern) { File.join(tmp_dir, '*') }
|
605
605
|
it 'has 2 items' do
|
606
|
-
expect(subject.
|
606
|
+
expect(subject.count).to eq(2)
|
607
607
|
end
|
608
608
|
it { is_expected.to include old_dir }
|
609
609
|
it { is_expected.to include old_file }
|
@@ -613,7 +613,7 @@ shared_examples_for 'Filesystem' do
|
|
613
613
|
context 'local one matches (with suffix)' do
|
614
614
|
let(:pattern) { File.join(File.dirname(old_file), '*.txt') }
|
615
615
|
it 'has 1 item' do
|
616
|
-
expect(subject.
|
616
|
+
expect(subject.count).to eq(1)
|
617
617
|
end
|
618
618
|
it { is_expected.to include old_file }
|
619
619
|
it { expect { |b| instance.glob(pattern, &b) }.to yield_with_args(old_file) }
|
@@ -629,7 +629,7 @@ shared_examples_for 'Filesystem' do
|
|
629
629
|
and_yield('')
|
630
630
|
end
|
631
631
|
let(:pattern) { File.join(new_dir, '*') }
|
632
|
-
it {
|
632
|
+
it { expect(subject.count).to eq(0) }
|
633
633
|
it { expect { |b| instance.glob('file://' + pattern, &b) }.to_not yield_control }
|
634
634
|
end
|
635
635
|
|
@@ -643,7 +643,7 @@ shared_examples_for 'Filesystem' do
|
|
643
643
|
and_yield("drwxrwxrwt - root wheel 68 2015-02-24 12:09 #{old_file}")
|
644
644
|
end
|
645
645
|
let(:pattern) { File.join(File.dirname(old_file), '*') }
|
646
|
-
it {
|
646
|
+
it { expect(subject.count).to eq(1) }
|
647
647
|
it { expect { |b| instance.glob('file://' + pattern, &b) }.to yield_with_args('file://' + old_file) }
|
648
648
|
end
|
649
649
|
|
@@ -655,7 +655,7 @@ shared_examples_for 'Filesystem' do
|
|
655
655
|
expect(filesystem).to receive(:s3cmd).with('ls', '--recursive', "s3://bucket/dir/*", safe: true).at_most(:once)
|
656
656
|
end
|
657
657
|
|
658
|
-
it {
|
658
|
+
it { expect(subject.count).to eq(0) }
|
659
659
|
end
|
660
660
|
|
661
661
|
context 's3 no matches with implicit glob results' do
|
@@ -667,7 +667,7 @@ shared_examples_for 'Filesystem' do
|
|
667
667
|
and_yield(%q(2013-05-24 18:53 2912 s3://bucket/dir/02.txt))
|
668
668
|
end
|
669
669
|
|
670
|
-
it {
|
670
|
+
it { expect(subject.count).to eq(0) }
|
671
671
|
end
|
672
672
|
|
673
673
|
context 's3 one matches' do
|
@@ -652,6 +652,16 @@ describe Masamune::Schema::Column do
|
|
652
652
|
|
653
653
|
context 'by default' do
|
654
654
|
let(:column) { described_class.new id: 'name', type: :string }
|
655
|
+
it { is_expected.to eq(true) }
|
656
|
+
end
|
657
|
+
|
658
|
+
context 'when :null true' do
|
659
|
+
let(:column) { described_class.new id: 'name', type: :string, null: true }
|
660
|
+
it { is_expected.to eq(false) }
|
661
|
+
end
|
662
|
+
|
663
|
+
context 'when column has default' do
|
664
|
+
let(:column) { described_class.new id: 'name', type: :string, default: 'missing' }
|
655
665
|
it { is_expected.to eq(false) }
|
656
666
|
end
|
657
667
|
|
@@ -82,6 +82,9 @@ describe Masamune::Schema::Table do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it { expect(table.name).to eq('user_table') }
|
85
|
+
it { expect(table.unique_columns).to include :tenant_id }
|
86
|
+
it { expect(table.unique_columns).to include :user_id }
|
87
|
+
it { expect(table.stage_table.unique_columns).to be_empty }
|
85
88
|
end
|
86
89
|
|
87
90
|
context 'with enum column' do
|
@@ -23,8 +23,6 @@
|
|
23
23
|
require 'delegate'
|
24
24
|
|
25
25
|
class Masamune::MockFilesystem < Delegator
|
26
|
-
include Masamune::Accumulate
|
27
|
-
|
28
26
|
def initialize
|
29
27
|
@filesystem = Masamune::Filesystem.new
|
30
28
|
@filesystem.add_path :root_dir, File.expand_path('../../../', __FILE__)
|
@@ -42,19 +40,20 @@ class Masamune::MockFilesystem < Delegator
|
|
42
40
|
@files.keys.include?(file)
|
43
41
|
end
|
44
42
|
|
45
|
-
def glob(pattern
|
43
|
+
def glob(pattern)
|
44
|
+
return Set.new(to_enum(:glob, pattern)) unless block_given?
|
46
45
|
file_regexp = glob_to_regexp(pattern)
|
47
46
|
@files.keys.each do |name|
|
48
47
|
yield name if name =~ file_regexp
|
49
48
|
end
|
50
49
|
end
|
51
|
-
method_accumulate :glob
|
52
50
|
|
53
51
|
def glob_sort(pattern, options = {})
|
54
52
|
glob(pattern)
|
55
53
|
end
|
56
54
|
|
57
|
-
def glob_stat(pattern
|
55
|
+
def glob_stat(pattern)
|
56
|
+
return Set.new(to_enum(:glob_stat, pattern)) unless block_given?
|
58
57
|
file_regexp = glob_to_regexp(pattern, recursive: true)
|
59
58
|
@files.each do |name, stat|
|
60
59
|
yield stat if name =~ file_regexp
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: masamune
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Andrews
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: parallel
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: pry
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -183,7 +197,6 @@ files:
|
|
183
197
|
- bin/masamune-psql
|
184
198
|
- bin/masamune-shell
|
185
199
|
- lib/masamune.rb
|
186
|
-
- lib/masamune/accumulate.rb
|
187
200
|
- lib/masamune/actions.rb
|
188
201
|
- lib/masamune/actions/data_flow.rb
|
189
202
|
- lib/masamune/actions/date_parse.rb
|
@@ -193,6 +206,7 @@ files:
|
|
193
206
|
- lib/masamune/actions/hadoop_filesystem.rb
|
194
207
|
- lib/masamune/actions/hadoop_streaming.rb
|
195
208
|
- lib/masamune/actions/hive.rb
|
209
|
+
- lib/masamune/actions/invoke_parallel.rb
|
196
210
|
- lib/masamune/actions/postgres.rb
|
197
211
|
- lib/masamune/actions/postgres_admin.rb
|
198
212
|
- lib/masamune/actions/s3cmd.rb
|
@@ -291,6 +305,7 @@ files:
|
|
291
305
|
- spec/masamune/actions/hadoop_filesystem_spec.rb
|
292
306
|
- spec/masamune/actions/hadoop_streaming_spec.rb
|
293
307
|
- spec/masamune/actions/hive_spec.rb
|
308
|
+
- spec/masamune/actions/invoke_parallel_spec.rb
|
294
309
|
- spec/masamune/actions/postgres_admin_spec.rb
|
295
310
|
- spec/masamune/actions/postgres_spec.rb
|
296
311
|
- spec/masamune/actions/s3cmd_spec.rb
|
@@ -393,6 +408,7 @@ test_files:
|
|
393
408
|
- spec/masamune/actions/hadoop_filesystem_spec.rb
|
394
409
|
- spec/masamune/actions/hadoop_streaming_spec.rb
|
395
410
|
- spec/masamune/actions/hive_spec.rb
|
411
|
+
- spec/masamune/actions/invoke_parallel_spec.rb
|
396
412
|
- spec/masamune/actions/postgres_admin_spec.rb
|
397
413
|
- spec/masamune/actions/postgres_spec.rb
|
398
414
|
- spec/masamune/actions/s3cmd_spec.rb
|