flydata 0.6.3 → 0.6.4
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/Rakefile +2 -2
- data/VERSION +1 -1
- data/bin/fdredshift +78 -0
- data/circle.yml +1 -1
- data/ext/flydata/{parser/mysql → source_mysql/parser}/.gitignore +0 -0
- data/ext/flydata/{parser/mysql → source_mysql/parser}/dump_parser_ext.cpp +3 -3
- data/ext/flydata/source_mysql/parser/extconf.rb +3 -0
- data/ext/flydata/{parser/mysql → source_mysql/parser}/parser.txt +0 -0
- data/ext/flydata/{parser/mysql → source_mysql/parser}/sql_parser.cpp +0 -0
- data/ext/flydata/{parser/mysql → source_mysql/parser}/sql_parser.h +0 -0
- data/flydata-core/lib/flydata-core/mysql/binlog_pos.rb +34 -32
- data/flydata-core/lib/flydata-core/mysql/compatibility_checker.rb +20 -0
- data/flydata-core/lib/flydata-core/table_def/mysql_table_def.rb +12 -4
- data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +60 -6
- data/flydata-core/spec/mysql/binlog_pos_spec.rb +474 -0
- data/flydata-core/spec/table_def/mysql_table_def_spec.rb +57 -0
- data/flydata-core/spec/table_def/mysql_to_redshift_table_def_spec.rb +174 -20
- data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_AUTO_INCREMENT_keyword.dump +43 -0
- data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_not_null_keyword.dump +43 -0
- data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_unique_keyword.dump +43 -0
- data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_unsigned_keyword.dump +43 -0
- data/flydata-core/spec/table_def/redshift_table_def_spec.rb +41 -8
- data/flydata.gemspec +0 -0
- data/lib/flydata/cli.rb +11 -5
- data/lib/flydata/command/base.rb +14 -1
- data/lib/flydata/command/exclusive_runnable.rb +42 -12
- data/lib/flydata/command/helper.rb +6 -6
- data/lib/flydata/command/sender.rb +4 -3
- data/lib/flydata/command/setup.rb +30 -381
- data/lib/flydata/command/stop.rb +1 -0
- data/lib/flydata/command/sync.rb +273 -301
- data/lib/flydata/compatibility_check.rb +24 -117
- data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +3 -3
- data/lib/flydata/fluent-plugins/mysql/alter_table_query_handler.rb +2 -2
- data/lib/flydata/fluent-plugins/mysql/binlog_record_handler.rb +6 -6
- data/lib/flydata/fluent-plugins/mysql/truncate_table_query_handler.rb +0 -1
- data/lib/flydata/parser.rb +14 -0
- data/lib/flydata/{parser_provider.rb → parser/parser_provider.rb} +6 -4
- data/lib/flydata/parser/source_table.rb +33 -0
- data/lib/flydata/source.rb +105 -0
- data/lib/flydata/source/component.rb +21 -0
- data/lib/flydata/source/errors.rb +7 -0
- data/lib/flydata/source/generate_source_dump.rb +72 -0
- data/lib/flydata/source/parse_dump_and_send.rb +52 -0
- data/lib/flydata/source/setup.rb +31 -0
- data/lib/flydata/source/source_pos.rb +45 -0
- data/lib/flydata/source/sync.rb +56 -0
- data/lib/flydata/source/sync_generate_table_ddl.rb +43 -0
- data/lib/flydata/source_file/setup.rb +17 -0
- data/lib/flydata/source_file/sync.rb +14 -0
- data/lib/flydata/{command → source_mysql/command}/mysql.rb +2 -1
- data/lib/flydata/{command → source_mysql/command}/mysql_command_base.rb +2 -4
- data/lib/flydata/{command → source_mysql/command}/mysqlbinlog.rb +2 -1
- data/lib/flydata/{command → source_mysql/command}/mysqldump.rb +2 -1
- data/lib/flydata/source_mysql/generate_source_dump.rb +53 -0
- data/lib/flydata/source_mysql/mysql_compatibility_check.rb +114 -0
- data/lib/flydata/source_mysql/parse_dump_and_send.rb +28 -0
- data/lib/flydata/{parser/mysql → source_mysql/parser}/.gitignore +0 -0
- data/lib/flydata/{parser/mysql → source_mysql/parser}/dump_parser.rb +32 -67
- data/lib/flydata/{parser/mysql → source_mysql/parser}/mysql_alter_table.treetop +0 -0
- data/lib/flydata/source_mysql/setup.rb +24 -0
- data/lib/flydata/source_mysql/source_pos.rb +21 -0
- data/lib/flydata/source_mysql/sync.rb +45 -0
- data/lib/flydata/source_mysql/sync_generate_table_ddl.rb +40 -0
- data/lib/flydata/{mysql → source_mysql}/table_ddl.rb +6 -17
- data/lib/flydata/source_zendesk/sync_generate_table_ddl.rb +30 -0
- data/lib/flydata/source_zendesk/zendesk_flydata_tabledefs.rb +133 -0
- data/lib/flydata/sync_file_manager.rb +132 -73
- data/lib/flydata/table_ddl.rb +18 -0
- data/spec/flydata/cli_spec.rb +1 -0
- data/spec/flydata/command/exclusive_runnable_spec.rb +19 -8
- data/spec/flydata/command/sender_spec.rb +1 -1
- data/spec/flydata/command/setup_spec.rb +4 -4
- data/spec/flydata/command/sync_spec.rb +97 -134
- data/spec/flydata/compatibility_check_spec.rb +16 -289
- data/spec/flydata/fluent-plugins/mysql/alter_table_query_handler_spec.rb +3 -3
- data/spec/flydata/fluent-plugins/mysql/dml_record_handler_spec.rb +1 -1
- data/spec/flydata/fluent-plugins/mysql/shared_query_handler_context.rb +4 -2
- data/spec/flydata/fluent-plugins/mysql/truncate_query_handler_spec.rb +1 -1
- data/spec/flydata/source_mysql/generate_source_dump_spec.rb +69 -0
- data/spec/flydata/source_mysql/mysql_compatibility_check_spec.rb +280 -0
- data/spec/flydata/{parser/mysql → source_mysql/parser}/alter_table_parser_spec.rb +2 -2
- data/spec/flydata/{parser/mysql → source_mysql/parser}/dump_parser_spec.rb +75 -70
- data/spec/flydata/source_mysql/sync_generate_table_ddl_spec.rb +137 -0
- data/spec/flydata/{mysql → source_mysql}/table_ddl_spec.rb +2 -2
- data/spec/flydata/source_spec.rb +140 -0
- data/spec/flydata/source_zendesk/sync_generate_table_ddl_spec.rb +33 -0
- data/spec/flydata/sync_file_manager_spec.rb +157 -77
- data/tmpl/redshift_mysql_data_entry.conf.tmpl +1 -1
- metadata +56 -23
- data/ext/flydata/parser/mysql/extconf.rb +0 -3
- data/lib/flydata/mysql/binlog_position.rb +0 -22
- data/spec/flydata/mysql/binlog_position_spec.rb +0 -35
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
require 'mysql2'
|
|
2
1
|
require 'flydata/command_loggable'
|
|
3
|
-
require 'flydata-core/mysql/config'
|
|
4
|
-
require 'flydata-core/mysql/command_generator'
|
|
5
|
-
require 'flydata-core/mysql/compatibility_checker'
|
|
6
2
|
require 'flydata-core/errors'
|
|
7
3
|
|
|
8
4
|
module Flydata
|
|
@@ -39,6 +35,30 @@ module Flydata
|
|
|
39
35
|
|
|
40
36
|
end
|
|
41
37
|
|
|
38
|
+
# Collection of checks for initial sync
|
|
39
|
+
module InitialSyncChecks
|
|
40
|
+
# Checks permission of @dump_dir and @backup_dir
|
|
41
|
+
#
|
|
42
|
+
# Requires
|
|
43
|
+
# @dump_dir
|
|
44
|
+
# @backup_dir
|
|
45
|
+
def check_writing_permissions
|
|
46
|
+
write_errors = []
|
|
47
|
+
paths_to_check = [FLYDATA_HOME]
|
|
48
|
+
paths_to_check << @dump_dir unless @dump_dir.to_s.empty?
|
|
49
|
+
paths_to_check << @backup_dir unless @backup_dir.to_s.empty?
|
|
50
|
+
paths_to_check.each do |path|
|
|
51
|
+
full_path = File.expand_path(path)
|
|
52
|
+
full_path = File.dirname(full_path) unless File.directory?(full_path)
|
|
53
|
+
write_errors << full_path unless File.writable?(full_path) and File.executable?(full_path)
|
|
54
|
+
end
|
|
55
|
+
unless write_errors.empty?
|
|
56
|
+
error_dir = write_errors.join(", ")
|
|
57
|
+
raise FlydataCore::CompatibilityError, "We cannot access the directories: #{error_dir}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
42
62
|
class AgentCompatibilityCheck < CompatibilityCheck
|
|
43
63
|
|
|
44
64
|
TCP_PORT=45326
|
|
@@ -69,117 +89,4 @@ module Flydata
|
|
|
69
89
|
end
|
|
70
90
|
end
|
|
71
91
|
|
|
72
|
-
class MysqlCompatibilityCheck < CompatibilityCheck
|
|
73
|
-
|
|
74
|
-
def initialize(dp_hash, de_hash, options={})
|
|
75
|
-
super
|
|
76
|
-
@db_opts = FlydataCore::Mysql::Config.build_mysql_db_opts(de_hash)
|
|
77
|
-
@dump_dir = options[:dump_dir] || nil
|
|
78
|
-
@backup_dir = options[:backup_dir] || nil
|
|
79
|
-
@tables = de_hash['tables']
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def print_errors
|
|
83
|
-
return if @errors.empty?
|
|
84
|
-
log_error_stderr "There may be some compatibility issues with your MySQL credentials: "
|
|
85
|
-
@errors.each do |error|
|
|
86
|
-
log_error_stderr " * #{error.message}"
|
|
87
|
-
end
|
|
88
|
-
raise "Please correct these errors if you wish to run FlyData Sync"
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def check_mysql_user_compat
|
|
92
|
-
FlydataCore::Mysql::SyncPermissionChecker.new(@db_opts).do_check
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def check_mysql_protocol_tcp_compat
|
|
96
|
-
query = FlydataCore::Mysql::CommandGenerator.generate_mysql_show_grants_cmd(@db_opts)
|
|
97
|
-
|
|
98
|
-
Open3.popen3(query) do |stdin, stdout, stderr|
|
|
99
|
-
stdin.close
|
|
100
|
-
while !stderr.eof?
|
|
101
|
-
lines = []
|
|
102
|
-
while line = stderr.gets; lines << line.strip; end
|
|
103
|
-
err_reason = lines.join(" ")
|
|
104
|
-
log_error("Error occured during access to mysql server.", {err: err_reason})
|
|
105
|
-
|
|
106
|
-
unless /Warning: Using a password on the command line interface can be insecure/ === err_reason
|
|
107
|
-
raise FlydataCore::MysqlCompatibilityError, "Cannot connect to MySQL database. Please make sure you can connect with this command:\n $ mysql -u #{@db_opts[:username]} -h #{@db_opts[:host]} -P #{@db_opts[:port]} #{@db_opts[:database]} --protocol=tcp -p"
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def check_mysql_parameters_compat
|
|
114
|
-
begin
|
|
115
|
-
FlydataCore::Mysql::OptionalBinlogParameterChecker.new(@db_opts).do_check
|
|
116
|
-
rescue FlydataCore::MysqlCompatibilityError => e
|
|
117
|
-
log_warn_stderr(e.to_s)
|
|
118
|
-
end
|
|
119
|
-
FlydataCore::Mysql::RequiredBinlogParameterChecker.new(@db_opts).do_check
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def check_rds_master_status
|
|
123
|
-
if is_rds?
|
|
124
|
-
FlydataCore::Mysql::RdsMasterStatusChecker.new(@db_opts).do_check
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def check_mysql_binlog_retention
|
|
129
|
-
if is_rds?
|
|
130
|
-
run_rds_retention_check
|
|
131
|
-
else
|
|
132
|
-
run_mysql_retention_check
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def check_writing_permissions
|
|
137
|
-
write_errors = []
|
|
138
|
-
paths_to_check = [FLYDATA_HOME]
|
|
139
|
-
paths_to_check << @dump_dir unless @dump_dir.to_s.empty?
|
|
140
|
-
paths_to_check << @backup_dir unless @backup_dir.to_s.empty?
|
|
141
|
-
paths_to_check.each do |path|
|
|
142
|
-
full_path = File.expand_path(path)
|
|
143
|
-
full_path = File.dirname(full_path) unless File.directory?(full_path)
|
|
144
|
-
write_errors << full_path unless File.writable?(full_path) and File.executable?(full_path)
|
|
145
|
-
end
|
|
146
|
-
unless write_errors.empty?
|
|
147
|
-
error_dir = write_errors.join(", ")
|
|
148
|
-
raise FlydataCore::MysqlCompatibilityError, "We cannot access the directories: #{error_dir}"
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# If table_type='VIEW' or engine='MEMORY', raise error.
|
|
153
|
-
def check_mysql_table_types
|
|
154
|
-
return if @tables.empty?
|
|
155
|
-
option = @db_opts.dup.merge(tables: @tables)
|
|
156
|
-
FlydataCore::Mysql::TableTypeChecker.new(option).do_check
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def run_mysql_retention_check
|
|
160
|
-
FlydataCore::Mysql::NonRdsRetentionChecker.new(@db_opts).do_check
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def run_rds_retention_check
|
|
164
|
-
FlydataCore::Mysql::RdsRetentionChecker.new(@db_opts).do_check
|
|
165
|
-
rescue Mysql2::Error => e
|
|
166
|
-
if e.message =~ /command denied to user/
|
|
167
|
-
retention_hours = FlydataCore::Mysql::RdsRetentionChecker::BINLOG_RETENTION_HOURS
|
|
168
|
-
log_warn_stderr("[WARNING]Cannot verify RDS retention period on current MySQL user account.\n" +
|
|
169
|
-
"To see retention period, please run this on your RDS:\n" +
|
|
170
|
-
" $> call mysql.rds_show_configuration;\n" +
|
|
171
|
-
"Please verify that the hours is not nil and is at least #{retention_hours} hours\n" +
|
|
172
|
-
"To set binlog retention hours, you can run this on your RDS:\n" +
|
|
173
|
-
" $> call mysql.rds_set_configuration('binlog retention hours', #{retention_hours});\n"
|
|
174
|
-
)
|
|
175
|
-
else
|
|
176
|
-
raise e
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def is_rds?(hostname = @db_opts[:host])
|
|
181
|
-
hostname.match(/rds.amazonaws.com$/) != nil
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
|
|
185
92
|
end
|
|
@@ -16,7 +16,7 @@ require 'flydata/fluent-plugins/mysql/binlog_record_dispatcher'
|
|
|
16
16
|
require 'flydata/fluent-plugins/mysql/context'
|
|
17
17
|
require 'flydata/fluent-plugins/idle_event_detector'
|
|
18
18
|
require 'flydata/fluent-plugins/mysql/table_meta'
|
|
19
|
-
require 'flydata/
|
|
19
|
+
require 'flydata/source_mysql/table_ddl'
|
|
20
20
|
require 'flydata-core/fluent/config_helper'
|
|
21
21
|
require 'flydata-core/mysql/ssl'
|
|
22
22
|
|
|
@@ -83,7 +83,7 @@ class MysqlBinlogFlydataInput < MysqlBinlogInput
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
@sync_fm = Flydata::SyncFileManager.new(nil) # Passing nil for data_entry as this class does not use methods which require data_entry
|
|
86
|
-
@sent_position_file_path = @sync_fm.
|
|
86
|
+
@sent_position_file_path = @sync_fm.sent_source_pos_path(@position_file)
|
|
87
87
|
|
|
88
88
|
load_custom_conf
|
|
89
89
|
|
|
@@ -184,7 +184,7 @@ EOS
|
|
|
184
184
|
return if instance_variable_defined? :@abort
|
|
185
185
|
|
|
186
186
|
@context.table_meta.update
|
|
187
|
-
Flydata::
|
|
187
|
+
Flydata::SourceMysql::TableDdl.migrate_tables(@context.tables, @db_opts,
|
|
188
188
|
@context.sync_fm, @position_file,
|
|
189
189
|
@context) do |event|
|
|
190
190
|
@record_dispatcher.dispatch(event)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require 'flydata/parser_provider'
|
|
1
|
+
require 'flydata/parser/parser_provider'
|
|
2
2
|
require 'flydata/fluent-plugins/mysql/ddl_query_handler'
|
|
3
3
|
|
|
4
4
|
module Mysql
|
|
@@ -17,7 +17,7 @@ module Mysql
|
|
|
17
17
|
emit_record(:alter_table, record) do |opt|
|
|
18
18
|
ret = nil
|
|
19
19
|
begin
|
|
20
|
-
result = ParserProvider.parser(:mysql, :mysql_alter_table).new.parse(record["normalized_query"])
|
|
20
|
+
result = Flydata::Parser::ParserProvider.parser(:mysql, :mysql_alter_table).new.parse(record["normalized_query"])
|
|
21
21
|
if result.nil?
|
|
22
22
|
$log.error("Received unsupported alter table query. normalized query:'#{record['normalized_query']}', raw query: '#{record['query']}'")
|
|
23
23
|
else
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'fluent/plugin/in_mysql_binlog'
|
|
2
2
|
require 'binlog'
|
|
3
|
-
require 'flydata/mysql/binlog_position'
|
|
4
3
|
require 'flydata-core/record/record'
|
|
4
|
+
require 'flydata-core/mysql/binlog_pos'
|
|
5
5
|
|
|
6
6
|
module Mysql
|
|
7
7
|
class BinlogRecordHandler
|
|
@@ -21,9 +21,9 @@ module Mysql
|
|
|
21
21
|
# Load per-table binlog position
|
|
22
22
|
@table_binlog_pos = {}
|
|
23
23
|
@context.tables.each do |table_name|
|
|
24
|
-
|
|
25
|
-
if
|
|
26
|
-
@table_binlog_pos[table_name] =
|
|
24
|
+
table_binlog_pos_str = @context.sync_fm.get_table_source_raw_pos(table_name)
|
|
25
|
+
if table_binlog_pos_str
|
|
26
|
+
@table_binlog_pos[table_name] = FlydataCore::Mysql::BinlogPos.new(table_binlog_pos_str)
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
end
|
|
@@ -45,10 +45,10 @@ module Mysql
|
|
|
45
45
|
acceptable = @context.tables.include?(table)
|
|
46
46
|
|
|
47
47
|
if acceptable and @table_binlog_pos[record['table_name']]
|
|
48
|
-
if @table_binlog_pos[record['table_name']] >=
|
|
48
|
+
if @table_binlog_pos[record['table_name']] >= FlydataCore::Mysql::BinlogPos.new(binlog_pos(record))
|
|
49
49
|
acceptable = false
|
|
50
50
|
else
|
|
51
|
-
@context.sync_fm.
|
|
51
|
+
@context.sync_fm.delete_table_source_pos(record['table_name'])
|
|
52
52
|
@table_binlog_pos.delete(record['table_name'])
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Flydata
|
|
2
|
+
|
|
3
|
+
module Parser
|
|
4
|
+
module State
|
|
5
|
+
START = 'START'
|
|
6
|
+
CREATE_TABLE = 'CREATE_TABLE'
|
|
7
|
+
CREATE_TABLE_COLUMNS = 'CREATE_TABLE_COLUMNS'
|
|
8
|
+
CREATE_TABLE_CONSTRAINTS = 'CREATE_TABLE_CONSTRAINTS'
|
|
9
|
+
INSERT_RECORD = 'INSERT_RECORD'
|
|
10
|
+
PARSING_INSERT_RECORD = 'PARSING_INSERT_RECORD'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
@@ -2,7 +2,8 @@ require 'thread'
|
|
|
2
2
|
require 'treetop'
|
|
3
3
|
require 'flydata-core/table_def'
|
|
4
4
|
|
|
5
|
-
module
|
|
5
|
+
module Flydata
|
|
6
|
+
module Parser
|
|
6
7
|
class ParserProvider
|
|
7
8
|
|
|
8
9
|
@@mutex = Mutex.new
|
|
@@ -15,9 +16,9 @@ module Mysql
|
|
|
15
16
|
return @@parsers[key]
|
|
16
17
|
else
|
|
17
18
|
# lazy load
|
|
18
|
-
treetop_path = File.
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
treetop_path = File.expand_path(
|
|
20
|
+
"../source_#{db_type}/parser/#{parser_type}.treetop",
|
|
21
|
+
File.dirname(__FILE__))
|
|
21
22
|
Treetop.load treetop_path
|
|
22
23
|
@@parsers[key] = "#{parser_type}_parser".camelize.constantize
|
|
23
24
|
end
|
|
@@ -25,3 +26,4 @@ module Mysql
|
|
|
25
26
|
end
|
|
26
27
|
end
|
|
27
28
|
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Flydata
|
|
2
|
+
module Parser
|
|
3
|
+
|
|
4
|
+
class SourceTable
|
|
5
|
+
def initialize(table_name, columns = {}, primary_keys = [])
|
|
6
|
+
@table_name = table_name
|
|
7
|
+
@columns = columns
|
|
8
|
+
@column_names = columns.collect{|k,v| v[:column_name]}
|
|
9
|
+
@primary_keys = primary_keys
|
|
10
|
+
@value_converters = {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_accessor :table_name, :columns, :column_names, :primary_keys, :value_converters
|
|
14
|
+
|
|
15
|
+
def add_column(column)
|
|
16
|
+
cn = column[:column_name]
|
|
17
|
+
@columns[cn] = column
|
|
18
|
+
@column_names << cn
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def set_value_converters(converter_hash)
|
|
22
|
+
@value_converters ||= {} # for backward compatibility with an old marshal dump object
|
|
23
|
+
@columns.each_with_index do |(k, v), i|
|
|
24
|
+
type = v[:format_type]
|
|
25
|
+
if converter_hash.has_key?(type)
|
|
26
|
+
@value_converters[i] = converter_hash[type]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require 'active_support'
|
|
2
|
+
require 'flydata'
|
|
3
|
+
|
|
4
|
+
module Flydata
|
|
5
|
+
|
|
6
|
+
module Source
|
|
7
|
+
def self.create(de)
|
|
8
|
+
MasterContext.new(de)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.register(component_class, type_class)
|
|
12
|
+
@component_classes ||= {}
|
|
13
|
+
source_sym = underscored_source_module_sym(component_class)
|
|
14
|
+
unless source_sym
|
|
15
|
+
raise "Class must must be under a 'SourceXxxx' module"
|
|
16
|
+
end
|
|
17
|
+
component_sym = underscored_class_name_sym(type_class)
|
|
18
|
+
@component_classes[component_key(source_sym, component_sym)] = component_class
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
DATA_ENTRY_TYPE_MAP = {
|
|
22
|
+
"RedshiftMysqlDataEntry" => :source_mysql,
|
|
23
|
+
"RedshiftFileDataEntry" => :source_file,
|
|
24
|
+
"FileDataEntry" => :source_file,
|
|
25
|
+
"RedshiftPostgresDataEntry" => :source_postgres,
|
|
26
|
+
"RedshiftZendeskDataEntry" => :source_zendesk,
|
|
27
|
+
}
|
|
28
|
+
def self.component_class_for(component_sym, de)
|
|
29
|
+
source_sym = DATA_ENTRY_TYPE_MAP[de['type']]
|
|
30
|
+
unless source_sym
|
|
31
|
+
raise "No source components are available for this data entry"
|
|
32
|
+
end
|
|
33
|
+
@component_classes ||= {}
|
|
34
|
+
component_key = component_key(source_sym, component_sym)
|
|
35
|
+
unless @component_classes.has_key?(component_key)
|
|
36
|
+
require_component(source_sym, component_sym)
|
|
37
|
+
unless @component_classes.has_key?(component_key)
|
|
38
|
+
@component_classes[component_key] = nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
klass = @component_classes[component_key]
|
|
42
|
+
unless klass
|
|
43
|
+
raise "Component '#{component_sym}' is not defined in source '#{source_sym}'"
|
|
44
|
+
end
|
|
45
|
+
klass
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
COMMAND_SUBDIR = "command"
|
|
49
|
+
# Returns require-friendly paths to the source specific command directory.
|
|
50
|
+
# ex) ["flydata/source_mysql/command", "flydata/source_postgres/command"]
|
|
51
|
+
def self.command_dir_paths
|
|
52
|
+
result = []
|
|
53
|
+
DATA_ENTRY_TYPE_MAP.values.each do |source_key|
|
|
54
|
+
path = File.join(source_dir_path(source_key), COMMAND_SUBDIR)
|
|
55
|
+
if File.exists?(path)
|
|
56
|
+
result << File.join("flydata", source_key.to_s, COMMAND_SUBDIR)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
result
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def self.underscored_source_module_sym(klass)
|
|
65
|
+
source_module = klass.name.split("::").detect{|m| m.start_with?("Source")}
|
|
66
|
+
source_module ? underscore_sym(source_module) : nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.underscored_class_name_sym(klass)
|
|
70
|
+
underscore_sym(klass.name.split("::").last)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.underscore_sym(name)
|
|
74
|
+
ActiveSupport::Inflector.underscore(name).to_sym
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.component_key(source_key, component_key)
|
|
78
|
+
"#{source_key}:#{component_key}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def self.require_component(source_key, component_key)
|
|
82
|
+
path = source_dir_path(source_key)
|
|
83
|
+
path = File.join(path, "**", "#{component_key}.rb")
|
|
84
|
+
paths = Dir.glob(path)
|
|
85
|
+
paths.each {|pth| require pth}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def self.source_dir_path(source_key)
|
|
89
|
+
File.absolute_path(File.join(__FILE__, "..", source_key.to_s))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class MasterContext
|
|
93
|
+
def initialize(de)
|
|
94
|
+
@de = de
|
|
95
|
+
end
|
|
96
|
+
attr_reader :de
|
|
97
|
+
|
|
98
|
+
def method_missing(method_sym, *args)
|
|
99
|
+
source_class = Source.component_class_for method_sym, de
|
|
100
|
+
source_class.new(self, *args)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
3
|
+
module Flydata
|
|
4
|
+
module Source
|
|
5
|
+
|
|
6
|
+
class Component
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
def initialize(source, options = {})
|
|
10
|
+
@source = source
|
|
11
|
+
@options = options
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
protected
|
|
15
|
+
|
|
16
|
+
attr_reader :options
|
|
17
|
+
def_delegators :@source, :de
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|