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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4aa0472dd6fa4bb472d79caf9a6c17a4570baad2
4
- data.tar.gz: 853b036996129473d7f9f8dee3129f198988face
3
+ metadata.gz: 06ac5ed8ef2f9949a444065bef219d2efd144b94
4
+ data.tar.gz: bf620007bd3016250133cda30feb6c03182ab1d4
5
5
  SHA512:
6
- metadata.gz: b48a634347ae17d3a214be9ee13b021a7099c992b6e23382347ab44019b7d5209e6c3d73af0957067fff8abb9b3365c6a40fe880ef88cb56f67f4f7cfcab62cc
7
- data.tar.gz: 861919413965988c50128a731f05e0ba9fd1bc9a5fc7a410038b23ce1e2e06fb4624889b11fb0142f321d0d2a8cc657e15a22f06a7efbc646674a4e99c228c16
6
+ metadata.gz: 589c931738aec24331ada2bab69a52175be9d27dc8a11e3a695ca7abbd38762582a5e3c93dafc61a6bbc7fac7f76d19ed6206a55a8e333795e6c113f0d64761c
7
+ data.tar.gz: cb8d8062f36d5ebc0bc72c3a8f3ba3c8acff5afa2f14186ddcf8b6844ca77cc9fc333b3802cab27443843ec8c8ed191e10f1274aea196b5be4c1acce7c8e8fbf
data/lib/masamune.rb CHANGED
@@ -26,7 +26,6 @@ module Masamune
26
26
  require 'masamune/io'
27
27
  require 'masamune/template'
28
28
  require 'masamune/commands'
29
- require 'masamune/accumulate'
30
29
  require 'masamune/last_element'
31
30
  require 'masamune/actions'
32
31
  require 'masamune/helpers'
@@ -34,5 +34,6 @@ module Masamune
34
34
  require 'masamune/actions/elastic_mapreduce'
35
35
  require 'masamune/actions/execute'
36
36
  require 'masamune/actions/transform'
37
+ require 'masamune/actions/invoke_parallel'
37
38
  end
38
39
  end
@@ -46,7 +46,7 @@ module Masamune::Actions
46
46
  end if block_given?
47
47
 
48
48
  command = Masamune::Commands::Shell.new(klass.new(self), {fail_fast: false}.merge(opts))
49
- command.execute
49
+ opts.fetch(:interactive, true) ? command.execute : command.replace(opts)
50
50
  end
51
51
  end
52
52
  end
@@ -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, &block)
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
- STDIN.close; STDOUT.close; STDERR.close
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(&block)
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(&block)
85
+ def targets
87
86
  return Masamune::DataPlan::Set::EMPTY if @rule.for_targets?
88
- rule.engine.targets_for_source(rule.name, self) do |target|
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(&block)
97
+ def sources
99
98
  return Masamune::DataPlan::Set::EMPTY if @rule.for_sources?
100
- rule.engine.sources_for_target(rule.name, self) do |source|
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, &block)
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, &block)
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, &block)
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, &block)
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, &block)
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, &block)
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(&block)
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.empty?
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(&block)
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(&block)
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(&block)
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(&block)
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(&block)
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(&block)
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, &block)
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).tap do |set|
128
- self.each do |elem|
129
- set.merge elem.targets
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).tap do |set|
137
- self.each do |elem|
138
- set.merge elem.sources
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]
@@ -89,9 +89,7 @@ module Masamune
89
89
  end
90
90
 
91
91
  def logger
92
- @logger ||= Logger.new(log_file_io).tap do
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
@@ -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, &block)
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
- yield current_path
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).length < 1
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, &block)
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, &block)
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 = {}, &block)
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 = {}, &block)
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 = {}, &block)
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 = {}, &block)
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 = {}, &block)
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 = {}, &block)
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 = {}, &block)
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 = {}, &block)
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 = {}, &block)
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
- [column_names, reverse_unique_constraints_map.key?(column_names.sort), short_md5(column_names)]
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
@@ -21,5 +21,5 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
  module Masamune
24
- VERSION = '0.11.0'
24
+ VERSION = '0.11.1'
25
25
  end
@@ -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
- module Masamune
24
- module Accumulate
25
- def accumulate(method, accumulator, *args)
26
- accumulator.call(self, *args).tap do |elems|
27
- send(method, *args) do |elem|
28
- elems << elem
29
- end
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
- module ClassMethods
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
- define_method(new_method) do |*args, &block|
41
- if block
42
- instance = accumulator.call(self, *args)
43
- send(old_method, *args) do |elem|
44
- next if instance.respond_to?(:add?) && !instance.add?(elem)
45
- block.call elem
46
- end
47
- else
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
- def self.included(base)
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/*')).not_to be_empty
44
- expect(cached_filesystem.glob('/a/b/c/*.txt')).not_to be_empty
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 eq(['/a/b/c/1.txt', '/a/b/c/2.txt', '/a/b/c/3.txt'])
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/*')).not_to be_empty
91
- expect(cached_filesystem.glob('/y=2013/m=1/d=22/*')).not_to be_empty
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/*')).not_to be_empty
117
- expect(cached_filesystem.glob('/logs/*.txt')).not_to be_empty
118
- expect(cached_filesystem.glob('/logs/box1_*.txt')).not_to be_empty
119
- expect(cached_filesystem.glob('/logs/box2_*.txt')).not_to be_empty
120
- expect(cached_filesystem.glob('/logs/box3_*.txt')).not_to be_empty
121
- expect(cached_filesystem.glob('/logs/box*.txt').size).to eq(3)
122
- expect(cached_filesystem.glob('/logs/box*.csv')).to be_empty
123
- expect(cached_filesystem.glob('/logs/box')).to be_empty
124
- expect(cached_filesystem.glob('/logs/box/*')).to be_empty
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 be_empty
153
- expect(cached_filesystem.glob('/a/*')).not_to be_empty
154
- expect(cached_filesystem.glob('/a/b')).to be_empty
155
- expect(cached_filesystem.glob('/a/b/*')).not_to be_empty
156
- expect(cached_filesystem.glob('/a/b/c')).not_to be_empty
157
- expect(cached_filesystem.glob('/a/b/c/*')).to be_empty
158
- expect(cached_filesystem.glob('/a/b/c/*.txt')).to be_empty
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.size).to eq(3) }
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.size).to eq(3) }
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.size).to eq(1) }
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.size).to eq(1) }
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.size).to eq(2) }
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.size).to eq(2) }
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.size).to eq(4) }
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.size).to eq(3) }
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.size).to eq(2) }
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.size).to eq(6) }
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.size).to eq(1) }
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.size).to eq(1) }
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.size).to eq(1) }
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.size).to eq(3) }
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.size).to eq(6)
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.size).to eq(3)
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.size).to eq(2)
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.size).to eq(2) }
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.size).to eq(1) }
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.size).to eq(0) }
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.size).to eq(3) }
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.size).to eq(1) }
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.size).to eq(3) }
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.size).to eq(2)
499
- expect(instance.targets.sources.existing.size).to eq(0)
500
- expect(instance.targets.sources.existing.targets.size).to eq(0)
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.size).to eq(2)
511
- expect(instance.targets.sources.existing.size).to eq(4)
512
- expect(instance.targets.sources.existing.targets.size).to eq(2)
513
- expect(instance.targets.sources.existing.targets.sources.existing.size).to eq(4)
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 { is_expected.to be_empty }
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 { is_expected.not_to be_empty }
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.size).to eq(2)
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.size).to eq(1)
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 { is_expected.to be_empty }
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 { is_expected.not_to be_empty }
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 { is_expected.to be_empty }
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 { is_expected.to be_empty }
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, &block)
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, &block)
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.0
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-09 00:00:00.000000000 Z
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