masamune 0.11.0 → 0.11.1
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.
- 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
|