flydata 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|