masamune 0.11.0
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +54 -0
- data/Rakefile +15 -0
- data/bin/masamune-elastic-mapreduce +4 -0
- data/bin/masamune-hive +4 -0
- data/bin/masamune-psql +4 -0
- data/bin/masamune-shell +4 -0
- data/lib/masamune.rb +56 -0
- data/lib/masamune/accumulate.rb +60 -0
- data/lib/masamune/actions.rb +38 -0
- data/lib/masamune/actions/data_flow.rb +131 -0
- data/lib/masamune/actions/date_parse.rb +75 -0
- data/lib/masamune/actions/elastic_mapreduce.rb +68 -0
- data/lib/masamune/actions/execute.rb +52 -0
- data/lib/masamune/actions/filesystem.rb +37 -0
- data/lib/masamune/actions/hadoop_filesystem.rb +40 -0
- data/lib/masamune/actions/hadoop_streaming.rb +41 -0
- data/lib/masamune/actions/hive.rb +74 -0
- data/lib/masamune/actions/postgres.rb +76 -0
- data/lib/masamune/actions/postgres_admin.rb +34 -0
- data/lib/masamune/actions/s3cmd.rb +44 -0
- data/lib/masamune/actions/transform.rb +89 -0
- data/lib/masamune/after_initialize_callbacks.rb +55 -0
- data/lib/masamune/cached_filesystem.rb +110 -0
- data/lib/masamune/commands.rb +37 -0
- data/lib/masamune/commands/elastic_mapreduce.rb +119 -0
- data/lib/masamune/commands/hadoop_filesystem.rb +57 -0
- data/lib/masamune/commands/hadoop_streaming.rb +116 -0
- data/lib/masamune/commands/hive.rb +178 -0
- data/lib/masamune/commands/interactive.rb +37 -0
- data/lib/masamune/commands/postgres.rb +128 -0
- data/lib/masamune/commands/postgres_admin.rb +72 -0
- data/lib/masamune/commands/postgres_common.rb +33 -0
- data/lib/masamune/commands/retry_with_backoff.rb +60 -0
- data/lib/masamune/commands/s3cmd.rb +70 -0
- data/lib/masamune/commands/shell.rb +202 -0
- data/lib/masamune/configuration.rb +195 -0
- data/lib/masamune/data_plan.rb +31 -0
- data/lib/masamune/data_plan/builder.rb +66 -0
- data/lib/masamune/data_plan/elem.rb +190 -0
- data/lib/masamune/data_plan/engine.rb +162 -0
- data/lib/masamune/data_plan/rule.rb +292 -0
- data/lib/masamune/data_plan/set.rb +176 -0
- data/lib/masamune/environment.rb +164 -0
- data/lib/masamune/filesystem.rb +567 -0
- data/lib/masamune/has_environment.rb +40 -0
- data/lib/masamune/helpers.rb +27 -0
- data/lib/masamune/helpers/postgres.rb +84 -0
- data/lib/masamune/io.rb +33 -0
- data/lib/masamune/last_element.rb +53 -0
- data/lib/masamune/method_logger.rb +41 -0
- data/lib/masamune/multi_io.rb +39 -0
- data/lib/masamune/schema.rb +36 -0
- data/lib/masamune/schema/catalog.rb +233 -0
- data/lib/masamune/schema/column.rb +527 -0
- data/lib/masamune/schema/dimension.rb +133 -0
- data/lib/masamune/schema/event.rb +121 -0
- data/lib/masamune/schema/fact.rb +133 -0
- data/lib/masamune/schema/map.rb +265 -0
- data/lib/masamune/schema/row.rb +133 -0
- data/lib/masamune/schema/store.rb +115 -0
- data/lib/masamune/schema/table.rb +308 -0
- data/lib/masamune/schema/table_reference.rb +76 -0
- data/lib/masamune/spec_helper.rb +23 -0
- data/lib/masamune/string_format.rb +34 -0
- data/lib/masamune/tasks/elastic_mapreduce_thor.rb +60 -0
- data/lib/masamune/tasks/hive_thor.rb +55 -0
- data/lib/masamune/tasks/postgres_thor.rb +47 -0
- data/lib/masamune/tasks/shell_thor.rb +63 -0
- data/lib/masamune/template.rb +77 -0
- data/lib/masamune/thor.rb +186 -0
- data/lib/masamune/thor_loader.rb +38 -0
- data/lib/masamune/topological_hash.rb +34 -0
- data/lib/masamune/transform.rb +47 -0
- data/lib/masamune/transform/bulk_upsert.psql.erb +64 -0
- data/lib/masamune/transform/bulk_upsert.rb +52 -0
- data/lib/masamune/transform/consolidate_dimension.rb +54 -0
- data/lib/masamune/transform/deduplicate_dimension.psql.erb +52 -0
- data/lib/masamune/transform/deduplicate_dimension.rb +53 -0
- data/lib/masamune/transform/define_event_view.hql.erb +51 -0
- data/lib/masamune/transform/define_event_view.rb +60 -0
- data/lib/masamune/transform/define_index.psql.erb +34 -0
- data/lib/masamune/transform/define_schema.hql.erb +23 -0
- data/lib/masamune/transform/define_schema.psql.erb +79 -0
- data/lib/masamune/transform/define_schema.rb +56 -0
- data/lib/masamune/transform/define_table.hql.erb +34 -0
- data/lib/masamune/transform/define_table.psql.erb +95 -0
- data/lib/masamune/transform/define_table.rb +40 -0
- data/lib/masamune/transform/define_unique.psql.erb +30 -0
- data/lib/masamune/transform/insert_reference_values.psql.erb +43 -0
- data/lib/masamune/transform/insert_reference_values.rb +64 -0
- data/lib/masamune/transform/load_dimension.rb +47 -0
- data/lib/masamune/transform/load_fact.rb +45 -0
- data/lib/masamune/transform/operator.rb +96 -0
- data/lib/masamune/transform/relabel_dimension.psql.erb +76 -0
- data/lib/masamune/transform/relabel_dimension.rb +39 -0
- data/lib/masamune/transform/rollup_fact.psql.erb +79 -0
- data/lib/masamune/transform/rollup_fact.rb +149 -0
- data/lib/masamune/transform/snapshot_dimension.psql.erb +75 -0
- data/lib/masamune/transform/snapshot_dimension.rb +74 -0
- data/lib/masamune/transform/stage_dimension.psql.erb +39 -0
- data/lib/masamune/transform/stage_dimension.rb +83 -0
- data/lib/masamune/transform/stage_fact.psql.erb +80 -0
- data/lib/masamune/transform/stage_fact.rb +111 -0
- data/lib/masamune/version.rb +25 -0
- data/spec/fixtures/aggregate.sql.erb +25 -0
- data/spec/fixtures/comment.sql.erb +27 -0
- data/spec/fixtures/invalid.sql.erb +23 -0
- data/spec/fixtures/relative.sql.erb +23 -0
- data/spec/fixtures/simple.sql.erb +28 -0
- data/spec/fixtures/whitespace.sql.erb +30 -0
- data/spec/masamune/actions/elastic_mapreduce_spec.rb +108 -0
- data/spec/masamune/actions/execute_spec.rb +50 -0
- data/spec/masamune/actions/hadoop_filesystem_spec.rb +44 -0
- data/spec/masamune/actions/hadoop_streaming_spec.rb +74 -0
- data/spec/masamune/actions/hive_spec.rb +117 -0
- data/spec/masamune/actions/postgres_admin_spec.rb +58 -0
- data/spec/masamune/actions/postgres_spec.rb +134 -0
- data/spec/masamune/actions/s3cmd_spec.rb +44 -0
- data/spec/masamune/actions/transform_spec.rb +144 -0
- data/spec/masamune/after_initialization_callbacks_spec.rb +61 -0
- data/spec/masamune/cached_filesystem_spec.rb +167 -0
- data/spec/masamune/commands/hadoop_filesystem_spec.rb +50 -0
- data/spec/masamune/commands/hadoop_streaming_spec.rb +106 -0
- data/spec/masamune/commands/hive_spec.rb +117 -0
- data/spec/masamune/commands/postgres_admin_spec.rb +69 -0
- data/spec/masamune/commands/postgres_spec.rb +100 -0
- data/spec/masamune/commands/retry_with_backoff_spec.rb +116 -0
- data/spec/masamune/commands/s3cmd_spec.rb +50 -0
- data/spec/masamune/commands/shell_spec.rb +101 -0
- data/spec/masamune/configuration_spec.rb +102 -0
- data/spec/masamune/data_plan/builder_spec.rb +91 -0
- data/spec/masamune/data_plan/elem_spec.rb +102 -0
- data/spec/masamune/data_plan/engine_spec.rb +356 -0
- data/spec/masamune/data_plan/rule_spec.rb +407 -0
- data/spec/masamune/data_plan/set_spec.rb +517 -0
- data/spec/masamune/environment_spec.rb +65 -0
- data/spec/masamune/filesystem_spec.rb +1421 -0
- data/spec/masamune/helpers/postgres_spec.rb +95 -0
- data/spec/masamune/schema/catalog_spec.rb +613 -0
- data/spec/masamune/schema/column_spec.rb +696 -0
- data/spec/masamune/schema/dimension_spec.rb +137 -0
- data/spec/masamune/schema/event_spec.rb +75 -0
- data/spec/masamune/schema/fact_spec.rb +117 -0
- data/spec/masamune/schema/map_spec.rb +593 -0
- data/spec/masamune/schema/row_spec.rb +28 -0
- data/spec/masamune/schema/store_spec.rb +49 -0
- data/spec/masamune/schema/table_spec.rb +395 -0
- data/spec/masamune/string_format_spec.rb +60 -0
- data/spec/masamune/tasks/elastic_mapreduce_thor_spec.rb +57 -0
- data/spec/masamune/tasks/hive_thor_spec.rb +75 -0
- data/spec/masamune/tasks/postgres_thor_spec.rb +42 -0
- data/spec/masamune/tasks/shell_thor_spec.rb +51 -0
- data/spec/masamune/template_spec.rb +77 -0
- data/spec/masamune/thor_spec.rb +238 -0
- data/spec/masamune/transform/bulk_upsert.dimension_spec.rb +200 -0
- data/spec/masamune/transform/consolidate_dimension_spec.rb +62 -0
- data/spec/masamune/transform/deduplicate_dimension_spec.rb +84 -0
- data/spec/masamune/transform/define_event_view_spec.rb +84 -0
- data/spec/masamune/transform/define_schema_spec.rb +83 -0
- data/spec/masamune/transform/define_table.dimension_spec.rb +306 -0
- data/spec/masamune/transform/define_table.fact_spec.rb +291 -0
- data/spec/masamune/transform/define_table.table_spec.rb +525 -0
- data/spec/masamune/transform/insert_reference_values.dimension_spec.rb +111 -0
- data/spec/masamune/transform/insert_reference_values.fact_spec.rb +149 -0
- data/spec/masamune/transform/load_dimension_spec.rb +76 -0
- data/spec/masamune/transform/load_fact_spec.rb +89 -0
- data/spec/masamune/transform/relabel_dimension_spec.rb +102 -0
- data/spec/masamune/transform/rollup_fact_spec.rb +333 -0
- data/spec/masamune/transform/snapshot_dimension_spec.rb +103 -0
- data/spec/masamune/transform/stage_dimension_spec.rb +115 -0
- data/spec/masamune/transform/stage_fact_spec.rb +204 -0
- data/spec/masamune_spec.rb +32 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/support/masamune/example_group.rb +36 -0
- data/spec/support/masamune/mock_command.rb +99 -0
- data/spec/support/masamune/mock_delegate.rb +51 -0
- data/spec/support/masamune/mock_filesystem.rb +96 -0
- data/spec/support/masamune/thor_mute.rb +35 -0
- data/spec/support/rspec/example/action_example_group.rb +34 -0
- data/spec/support/rspec/example/task_example_group.rb +80 -0
- data/spec/support/rspec/example/transform_example_group.rb +36 -0
- data/spec/support/shared_examples/postgres_common_examples.rb +53 -0
- metadata +462 -0
@@ -0,0 +1,31 @@
|
|
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
|
+
module Masamune
|
24
|
+
module DataPlan
|
25
|
+
require 'masamune/data_plan/builder'
|
26
|
+
require 'masamune/data_plan/elem'
|
27
|
+
require 'masamune/data_plan/engine'
|
28
|
+
require 'masamune/data_plan/rule'
|
29
|
+
require 'masamune/data_plan/set'
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,66 @@
|
|
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 'singleton'
|
24
|
+
|
25
|
+
class Masamune::DataPlan::Builder
|
26
|
+
include Singleton
|
27
|
+
|
28
|
+
def build(namespaces, commands, sources, targets)
|
29
|
+
Masamune::DataPlan::Engine.new.tap do |engine|
|
30
|
+
sources_for, sources_anon = partition_by_for(sources)
|
31
|
+
targets_for, targets_anon = partition_by_for(targets)
|
32
|
+
|
33
|
+
commands.each do |name|
|
34
|
+
command_name = "#{namespaces.shift}:#{name}"
|
35
|
+
|
36
|
+
source_options = sources_for[name] || sources_anon.shift or next
|
37
|
+
target_options = targets_for[name] || targets_anon.shift or next
|
38
|
+
next if source_options[:skip] || target_options[:skip]
|
39
|
+
|
40
|
+
engine.add_source_rule(command_name, source_options)
|
41
|
+
engine.add_target_rule(command_name, target_options)
|
42
|
+
|
43
|
+
engine.add_command_rule(command_name, thor_command_wrapper)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def partition_by_for(annotations)
|
51
|
+
with_for, anon = annotations.partition { |opts| opts.has_key?(:for) }
|
52
|
+
decl = {}
|
53
|
+
with_for.each do |opts|
|
54
|
+
decl[opts[:for]] = opts.reject { |k,_| k == :for }
|
55
|
+
end
|
56
|
+
[decl, anon]
|
57
|
+
end
|
58
|
+
|
59
|
+
def thor_command_wrapper
|
60
|
+
Proc.new do |engine, rule, _|
|
61
|
+
engine.environment.with_exclusive_lock(rule) do
|
62
|
+
engine.environment.parent.invoke(rule)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,190 @@
|
|
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
|
+
class Masamune::DataPlan::Elem
|
24
|
+
MISSING_MODIFIED_AT = Time.new(0)
|
25
|
+
|
26
|
+
include Masamune::Accumulate
|
27
|
+
include Comparable
|
28
|
+
|
29
|
+
attr_reader :rule, :options
|
30
|
+
|
31
|
+
def initialize(rule, start_time, options = {})
|
32
|
+
@rule = rule
|
33
|
+
self.start_time = start_time
|
34
|
+
@options = options
|
35
|
+
end
|
36
|
+
|
37
|
+
def input
|
38
|
+
@input ||= start_time.strftime(strftime_format)
|
39
|
+
end
|
40
|
+
alias :path :input
|
41
|
+
alias :table :input
|
42
|
+
|
43
|
+
def partition
|
44
|
+
input.split('_').last
|
45
|
+
end
|
46
|
+
alias :suffix :partition
|
47
|
+
|
48
|
+
def exists?
|
49
|
+
if rule.for_path?
|
50
|
+
rule.engine.filesystem.exists?(path)
|
51
|
+
elsif rule.for_table_with_partition?
|
52
|
+
rule.engine.postgres_helper.table_exists?(table)
|
53
|
+
elsif rule.for_table?
|
54
|
+
table
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def complete?
|
59
|
+
if rule.for_targets?
|
60
|
+
sources.existing.map(&:start_date).uniq.length == sources.map(&:start_date).uniq.length
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def last_modified_at
|
65
|
+
if rule.for_path?
|
66
|
+
rule.engine.filesystem.stat(path).try(:mtime)
|
67
|
+
elsif rule.for_table?
|
68
|
+
rule.engine.postgres_helper.table_last_modified_at(table, @options)
|
69
|
+
end || MISSING_MODIFIED_AT
|
70
|
+
end
|
71
|
+
|
72
|
+
def explode(&block)
|
73
|
+
if rule.for_path?
|
74
|
+
file_glob = path
|
75
|
+
file_glob += '/' unless path.include?('*') || path.include?('.')
|
76
|
+
file_glob += '*' unless path.include?('*')
|
77
|
+
rule.engine.filesystem.glob(file_glob) do |new_path|
|
78
|
+
yield rule.bind_input(new_path)
|
79
|
+
end
|
80
|
+
elsif rule.for_table_with_partition?
|
81
|
+
yield self if exists?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
method_accumulate :explode
|
85
|
+
|
86
|
+
def targets(&block)
|
87
|
+
return Masamune::DataPlan::Set::EMPTY if @rule.for_targets?
|
88
|
+
rule.engine.targets_for_source(rule.name, self) do |target|
|
89
|
+
yield target
|
90
|
+
end
|
91
|
+
end
|
92
|
+
method_accumulate :targets, lambda { |elem| Masamune::DataPlan::Set.new(elem.rule.engine.get_target_rule(elem.rule.name)) }
|
93
|
+
|
94
|
+
def target
|
95
|
+
targets.first
|
96
|
+
end
|
97
|
+
|
98
|
+
def sources(&block)
|
99
|
+
return Masamune::DataPlan::Set::EMPTY if @rule.for_sources?
|
100
|
+
rule.engine.sources_for_target(rule.name, self) do |source|
|
101
|
+
yield source
|
102
|
+
end
|
103
|
+
end
|
104
|
+
method_accumulate :sources, lambda { |elem| Masamune::DataPlan::Set.new(elem.rule.engine.get_source_rule(elem.rule.name)) }
|
105
|
+
|
106
|
+
def source
|
107
|
+
sources.first
|
108
|
+
end
|
109
|
+
|
110
|
+
def start_time
|
111
|
+
@start_time.to_time.utc
|
112
|
+
end
|
113
|
+
|
114
|
+
def start_time=(start_time)
|
115
|
+
@start_time =
|
116
|
+
case start_time
|
117
|
+
when Time
|
118
|
+
rule.time_round(start_time.utc)
|
119
|
+
when Date, DateTime
|
120
|
+
rule.time_round(start_time.to_time.utc)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def start_date
|
125
|
+
@start_time.to_date
|
126
|
+
end
|
127
|
+
|
128
|
+
def stop_time
|
129
|
+
start_time.advance(@rule.time_step => 1)
|
130
|
+
end
|
131
|
+
|
132
|
+
def stop_date
|
133
|
+
stop_time.to_date
|
134
|
+
end
|
135
|
+
|
136
|
+
def glob
|
137
|
+
@options[:glob]
|
138
|
+
end
|
139
|
+
|
140
|
+
def next(i = 1)
|
141
|
+
self.class.new(@rule, start_time.advance(@rule.time_step => +1*i), @options)
|
142
|
+
end
|
143
|
+
|
144
|
+
def prev(i = 1)
|
145
|
+
self.class.new(@rule, start_time.advance(@rule.time_step => -1*i), @options)
|
146
|
+
end
|
147
|
+
|
148
|
+
def round(grain)
|
149
|
+
self.class.new(@rule.round(grain), start_time, @options)
|
150
|
+
end
|
151
|
+
|
152
|
+
def ==(other)
|
153
|
+
uniq_constraint == other.uniq_constraint
|
154
|
+
end
|
155
|
+
|
156
|
+
def eql?(other)
|
157
|
+
self == other
|
158
|
+
end
|
159
|
+
|
160
|
+
def hash
|
161
|
+
uniq_constraint.hash
|
162
|
+
end
|
163
|
+
|
164
|
+
# FIXME should consider stop_time for correctness
|
165
|
+
def <=>(other)
|
166
|
+
if start_time < other.start_time
|
167
|
+
1
|
168
|
+
elsif start_time > other.start_time
|
169
|
+
-1
|
170
|
+
elsif start_time == other.start_time
|
171
|
+
0
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def inspect
|
176
|
+
{rule: rule, input: input, start_date: start_time.to_s, stop_date: stop_time.to_s, :options => options}.to_s
|
177
|
+
end
|
178
|
+
|
179
|
+
protected
|
180
|
+
|
181
|
+
def uniq_constraint
|
182
|
+
[rule, options, rule.for_table? ? start_time : input]
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def strftime_format
|
188
|
+
@strftime_format ||= glob ? @rule.strftime_format.sub('*', glob) : @rule.strftime_format
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,162 @@
|
|
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'
|
24
|
+
require 'active_support/core_ext/numeric/time'
|
25
|
+
|
26
|
+
class Masamune::DataPlan::Engine
|
27
|
+
MAX_DEPTH = 10
|
28
|
+
|
29
|
+
include Masamune::HasEnvironment
|
30
|
+
include Masamune::Accumulate
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@target_rules = Hash.new
|
34
|
+
@source_rules = Hash.new
|
35
|
+
@command_rules = Hash.new
|
36
|
+
@targets = Hash.new { |set,rule| set[rule] = Masamune::DataPlan::Set.new(@target_rules[rule]) }
|
37
|
+
@sources = Hash.new { |set,rule| set[rule] = Masamune::DataPlan::Set.new(@source_rules[rule]) }
|
38
|
+
@set_cache = Hash.new { |cache,level| cache[level] = Hash.new }
|
39
|
+
@current_depth = 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def filesystem
|
43
|
+
@filesystem ||= Masamune::CachedFilesystem.new(environment.filesystem)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_target_rule(rule, target_options = {})
|
47
|
+
@target_rules[rule] = Masamune::DataPlan::Rule.new(self, rule, :target, target_options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_target_rule(rule)
|
51
|
+
@target_rules[rule]
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_source_rule(rule, source_options = {})
|
55
|
+
@source_rules[rule] = Masamune::DataPlan::Rule.new(self, rule, :source, source_options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_source_rule(rule)
|
59
|
+
@source_rules[rule]
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_command_rule(rule, command)
|
63
|
+
@command_rules[rule] = command
|
64
|
+
end
|
65
|
+
|
66
|
+
# TODO use constructed reference instead
|
67
|
+
def rule_for_target(target)
|
68
|
+
target_matches = @target_rules.select { |rule, matcher| matcher.primary? && matcher.matches?(target) }
|
69
|
+
source_matches = @source_rules.select { |rule, matcher| matcher.matches?(target) }
|
70
|
+
|
71
|
+
if target_matches.empty?
|
72
|
+
if source_matches.empty?
|
73
|
+
raise "No rule matches target #{target}"
|
74
|
+
else
|
75
|
+
Masamune::DataPlan::Rule::TERMINAL
|
76
|
+
end
|
77
|
+
else
|
78
|
+
logger.error("Multiple rules match target #{target}") if target_matches.length > 1
|
79
|
+
target_matches.map(&:first).first
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def targets_for_date_range(rule, start, stop, &block)
|
84
|
+
target_template = @target_rules[rule]
|
85
|
+
return unless target_template
|
86
|
+
target_template.generate(start.to_time.utc, stop.to_time.utc) do |target|
|
87
|
+
yield target
|
88
|
+
end
|
89
|
+
end
|
90
|
+
method_accumulate :targets_for_date_range, lambda { |engine, rule, _, _| Masamune::DataPlan::Set.new(engine.get_target_rule(rule)) }
|
91
|
+
|
92
|
+
def targets_for_source(rule, source, &block)
|
93
|
+
source_template = @source_rules[rule]
|
94
|
+
target_template = @target_rules[rule]
|
95
|
+
source_instance = source_template.bind_input(source)
|
96
|
+
source_template.generate_via_unify(source_instance, target_template) do |target|
|
97
|
+
yield target
|
98
|
+
end
|
99
|
+
end
|
100
|
+
method_accumulate :targets_for_source, lambda { |engine, rule, source| Masamune::DataPlan::Set.new(engine.get_target_rule(rule)) }
|
101
|
+
|
102
|
+
def sources_for_target(rule, target, &block)
|
103
|
+
source_template = @source_rules[rule]
|
104
|
+
target_template = @target_rules[rule]
|
105
|
+
target_instance = target_template.bind_input(target)
|
106
|
+
target_template.generate_via_unify(target_instance, source_template) do |source|
|
107
|
+
yield source
|
108
|
+
end
|
109
|
+
end
|
110
|
+
method_accumulate :sources_for_target, lambda { |engine, rule, target| Masamune::DataPlan::Set.new(engine.get_source_rule(rule)) }
|
111
|
+
|
112
|
+
def targets(rule)
|
113
|
+
@set_cache[:targets_for_rule][rule] ||= @targets[rule].union(@sources[rule].targets)
|
114
|
+
end
|
115
|
+
|
116
|
+
def sources(rule)
|
117
|
+
@set_cache[:sources_for_rule][rule] ||= @sources[rule].union(@targets[rule].sources).adjacent
|
118
|
+
end
|
119
|
+
|
120
|
+
def prepare(rule, options = {})
|
121
|
+
@targets[rule].merge options.fetch(:targets, [])
|
122
|
+
@sources[rule].merge options.fetch(:sources, [])
|
123
|
+
|
124
|
+
constrain_max_depth(rule) do
|
125
|
+
sources(rule).group_by { |source| rule_for_target(source.input) }.each do |derived_rule, sources|
|
126
|
+
prepare(derived_rule, targets: sources.map(&:input)) if derived_rule != Masamune::DataPlan::Rule::TERMINAL
|
127
|
+
end
|
128
|
+
end if options.fetch(:resolve, true)
|
129
|
+
clear!
|
130
|
+
end
|
131
|
+
|
132
|
+
def execute(rule, options = {})
|
133
|
+
return if targets(rule).actionable.empty?
|
134
|
+
|
135
|
+
constrain_max_depth(rule) do
|
136
|
+
sources(rule).group_by { |source| rule_for_target(source.input) }.each do |derived_rule, sources|
|
137
|
+
execute(derived_rule, options) if derived_rule != Masamune::DataPlan::Rule::TERMINAL
|
138
|
+
end
|
139
|
+
end if options.fetch(:resolve, true)
|
140
|
+
|
141
|
+
@command_rules[rule].call(self, rule, options)
|
142
|
+
clear!
|
143
|
+
end
|
144
|
+
|
145
|
+
def executing?
|
146
|
+
@current_depth > 0
|
147
|
+
end
|
148
|
+
|
149
|
+
def constrain_max_depth(rule, &block)
|
150
|
+
@current_depth += 1
|
151
|
+
raise "Max depth of #{MAX_DEPTH} exceeded for rule '#{rule}'" if @current_depth > MAX_DEPTH
|
152
|
+
yield
|
153
|
+
ensure
|
154
|
+
@current_depth -= 1
|
155
|
+
end
|
156
|
+
|
157
|
+
def clear!
|
158
|
+
@set_cache.clear
|
159
|
+
filesystem.clear!
|
160
|
+
environment.postgres_helper.clear!
|
161
|
+
end
|
162
|
+
end
|