arql 0.1.30
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +72 -0
- data/LICENSE.txt +21 -0
- data/README.md +456 -0
- data/Rakefile +2 -0
- data/arql.gemspec +40 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/arql +7 -0
- data/exe/arql_setsid_wrapper +5 -0
- data/lib/arql.rb +12 -0
- data/lib/arql/app.rb +137 -0
- data/lib/arql/cli.rb +141 -0
- data/lib/arql/commands.rb +9 -0
- data/lib/arql/commands/info.rb +52 -0
- data/lib/arql/commands/models.rb +40 -0
- data/lib/arql/commands/reconnect.rb +23 -0
- data/lib/arql/commands/redefine.rb +15 -0
- data/lib/arql/commands/show_sql.rb +25 -0
- data/lib/arql/commands/table.rb +55 -0
- data/lib/arql/connection.rb +10 -0
- data/lib/arql/definition.rb +180 -0
- data/lib/arql/ext.rb +5 -0
- data/lib/arql/ext/array.rb +65 -0
- data/lib/arql/ext/kernel.rb +5 -0
- data/lib/arql/ext/object.rb +18 -0
- data/lib/arql/ext/string.rb +5 -0
- data/lib/arql/ext/time.rb +15 -0
- data/lib/arql/id.rb +59 -0
- data/lib/arql/multi_io.rb +28 -0
- data/lib/arql/repl.rb +38 -0
- data/lib/arql/ssh_proxy.rb +31 -0
- data/lib/arql/ssh_proxy_patch.rb +88 -0
- data/lib/arql/version.rb +3 -0
- metadata +235 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Arql::Commands
|
2
|
+
module Reconnect
|
3
|
+
class << self
|
4
|
+
def reconnect
|
5
|
+
Arql::SSHProxy.reconnect if Arql::App.config[:ssh].present?
|
6
|
+
ActiveRecord::Base.connection.reconnect! unless ActiveRecord::Base.connection.active?
|
7
|
+
end
|
8
|
+
|
9
|
+
def reconnect!
|
10
|
+
Arql::SSHProxy.reconnect! if Arql::App.config[:ssh].present?
|
11
|
+
ActiveRecord::Base.connection.reconnect!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Pry.commands.block_command 'reconnect' do
|
16
|
+
Reconnect.reconnect
|
17
|
+
end
|
18
|
+
|
19
|
+
Pry.commands.block_command 'reconnect!' do
|
20
|
+
Reconnect.reconnect!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Arql::Commands
|
2
|
+
module ShowSql
|
3
|
+
class << self
|
4
|
+
def show
|
5
|
+
return if Arql::App.log_io.is_a?(Arql::MultiIO) && Arql::App.log_io.include?(STDOUT)
|
6
|
+
Arql::App.log_io ||= Arql::MultiIO.new
|
7
|
+
ActiveRecord::Base.logger = Logger.new(Arql::App.log_io)
|
8
|
+
Arql::App.log_io << STDOUT
|
9
|
+
end
|
10
|
+
|
11
|
+
def hide
|
12
|
+
return if !Arql::App.log_io.is_a?(Arql::MultiIO) || !Arql::App.log_io.include?(STDOUT)
|
13
|
+
Arql::App.log_io.delete(STDOUT)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Pry.commands.block_command 'show-sql' do
|
18
|
+
ShowSql.show
|
19
|
+
end
|
20
|
+
|
21
|
+
Pry.commands.block_command 'hide-sql' do
|
22
|
+
ShowSql.hide
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'terminal-table'
|
2
|
+
|
3
|
+
module Arql::Commands
|
4
|
+
module Table
|
5
|
+
class << self
|
6
|
+
def get_table_name(name)
|
7
|
+
name = name.to_s
|
8
|
+
return name if name =~ /^[a-z]/
|
9
|
+
if Object.const_defined?(name)
|
10
|
+
klass = Object.const_get(name)
|
11
|
+
return klass.table_name if klass < ActiveRecord::Base
|
12
|
+
end
|
13
|
+
name
|
14
|
+
end
|
15
|
+
|
16
|
+
def table_info_table(table_name)
|
17
|
+
Terminal::Table.new do |t|
|
18
|
+
table_info(table_name).each { |row| t << (row || :separator) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def table_info(table_name)
|
23
|
+
t = []
|
24
|
+
t << ['PK', 'Name', 'SQL Type', 'Ruby Type', 'Limit', 'Precision', 'Scale', 'Default', 'Nullable', 'Comment']
|
25
|
+
t << nil
|
26
|
+
connection = ::ActiveRecord::Base.connection
|
27
|
+
connection.columns(table_name).each do |column|
|
28
|
+
pk = if column.name == connection.primary_key(table_name)
|
29
|
+
'Y'
|
30
|
+
else
|
31
|
+
''
|
32
|
+
end
|
33
|
+
t << [pk, column.name, column.sql_type,
|
34
|
+
column.sql_type_metadata.type, column.sql_type_metadata.limit || '',
|
35
|
+
column.sql_type_metadata.precision || '', column.sql_type_metadata.scale || '', column.default || '',
|
36
|
+
column.null, column.comment || '']
|
37
|
+
end
|
38
|
+
t
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Pry.commands.block_command 't' do |name|
|
43
|
+
table_name = Table::get_table_name(name)
|
44
|
+
puts
|
45
|
+
puts "Table: #{table_name}"
|
46
|
+
puts Table::table_info_table(table_name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module Kernel
|
52
|
+
def table(name)
|
53
|
+
Arql::Commands::Table::table_info(Arql::Commands::Table::get_table_name(name))
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
module Arql
|
2
|
+
module Extension
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def t
|
6
|
+
puts Terminal::Table.new { |t|
|
7
|
+
v.each { |row| t << (row || :separator) }
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def v
|
12
|
+
t = []
|
13
|
+
t << ['Attribute Name', 'Attribute Value', 'SQL Type', 'Comment']
|
14
|
+
t << nil
|
15
|
+
self.class.connection.columns(self.class.table_name).each do |column|
|
16
|
+
t << [column.name, read_attribute(column.name), column.sql_type, column.comment || '']
|
17
|
+
end
|
18
|
+
t
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_insert_sql
|
22
|
+
self.class.to_insert_sql([self])
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_upsert_sql
|
26
|
+
self.class.to_upsert_sql([self])
|
27
|
+
end
|
28
|
+
|
29
|
+
included do
|
30
|
+
end
|
31
|
+
|
32
|
+
class_methods do
|
33
|
+
def t
|
34
|
+
table_name = Commands::Table::get_table_name(name)
|
35
|
+
puts "\nTable: #{table_name}"
|
36
|
+
puts Commands::Table::table_info_table(table_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def v
|
40
|
+
table_name = Commands::Table::get_table_name(name)
|
41
|
+
Commands::Table::table_info(table_name)
|
42
|
+
end
|
43
|
+
def to_insert_sql(records, batch_size=1)
|
44
|
+
to_sql(records, :skip, batch_size)
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_upsert_sql(records, batch_size=1)
|
48
|
+
to_sql(records, :update, batch_size)
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_sql(records, on_duplicate, batch_size)
|
52
|
+
records.in_groups_of(batch_size, false).map do |group|
|
53
|
+
ActiveRecord::InsertAll.new(self, group.map(&:attributes), on_duplicate: on_duplicate).send(:to_sql) + ';'
|
54
|
+
end.join("\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_create_sql
|
58
|
+
ActiveRecord::Base.connection.exec_query("show create table #{table_name}").rows.last.last
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Definition
|
64
|
+
class << self
|
65
|
+
def models
|
66
|
+
@@models ||= []
|
67
|
+
end
|
68
|
+
|
69
|
+
def redefine
|
70
|
+
options = @@options
|
71
|
+
@@models.each do |model|
|
72
|
+
Object.send :remove_const, model[:model].name.to_sym if model[:model]
|
73
|
+
Object.send :remove_const, model[:abbr].to_sym if model[:abbr]
|
74
|
+
end
|
75
|
+
@@models = []
|
76
|
+
ActiveRecord::Base.connection.tap do |conn|
|
77
|
+
conn.tables.each do |table_name|
|
78
|
+
conn.primary_key(table_name).tap do |pkey|
|
79
|
+
table_name.camelize.tap do |const_name|
|
80
|
+
const_name = 'Modul' if const_name == 'Module'
|
81
|
+
const_name = 'Clazz' if const_name == 'Class'
|
82
|
+
Class.new(ActiveRecord::Base) do
|
83
|
+
include Arql::Extension
|
84
|
+
self.primary_key = pkey
|
85
|
+
self.table_name = table_name
|
86
|
+
self.inheritance_column = nil
|
87
|
+
self.default_timezone = :local
|
88
|
+
if options[:created_at].present?
|
89
|
+
define_singleton_method :timestamp_attributes_for_create do
|
90
|
+
options[:created_at]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
if options[:updated_at].present?
|
95
|
+
define_singleton_method :timestamp_attributes_for_update do
|
96
|
+
options[:updated_at]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end.tap do |clazz|
|
100
|
+
Object.const_set(const_name, clazz).tap do |const|
|
101
|
+
const_name.gsub(/[a-z]*/, '').tap do |abbr|
|
102
|
+
unless Object.const_defined?(abbr)
|
103
|
+
Object.const_set abbr, const
|
104
|
+
abbr_const = abbr
|
105
|
+
end
|
106
|
+
|
107
|
+
@@models << {
|
108
|
+
model: const,
|
109
|
+
abbr: abbr_const,
|
110
|
+
table: table_name
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def initialize(options)
|
123
|
+
@@options = options
|
124
|
+
@@models = []
|
125
|
+
ActiveRecord::Base.connection.tap do |conn|
|
126
|
+
conn.tables.each do |table_name|
|
127
|
+
conn.primary_key(table_name).tap do |pkey|
|
128
|
+
table_name.camelize.tap do |const_name|
|
129
|
+
const_name = 'Modul' if const_name == 'Module'
|
130
|
+
const_name = 'Clazz' if const_name == 'Class'
|
131
|
+
Class.new(ActiveRecord::Base) do
|
132
|
+
include Arql::Extension
|
133
|
+
self.primary_key = pkey
|
134
|
+
self.table_name = table_name
|
135
|
+
self.inheritance_column = nil
|
136
|
+
self.default_timezone = :local
|
137
|
+
if options[:created_at].present?
|
138
|
+
define_singleton_method :timestamp_attributes_for_create do
|
139
|
+
options[:created_at]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
if options[:updated_at].present?
|
144
|
+
define_singleton_method :timestamp_attributes_for_update do
|
145
|
+
options[:updated_at]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end.tap do |clazz|
|
149
|
+
Object.const_set(const_name, clazz).tap do |const|
|
150
|
+
const_name.gsub(/[a-z]*/, '').tap do |abbr|
|
151
|
+
unless Object.const_defined?(abbr)
|
152
|
+
Object.const_set abbr, const
|
153
|
+
abbr_const = abbr
|
154
|
+
end
|
155
|
+
|
156
|
+
@@models << {
|
157
|
+
model: const,
|
158
|
+
abbr: abbr_const,
|
159
|
+
table: table_name
|
160
|
+
}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
::ActiveRecord::Relation.class_eval do
|
171
|
+
def t
|
172
|
+
records.t
|
173
|
+
end
|
174
|
+
|
175
|
+
def v
|
176
|
+
records.v
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
data/lib/arql/ext.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
class Array
|
2
|
+
def to_insert_sql(batch_size=500)
|
3
|
+
raise 'All element should be an ActiveRecord instance object' unless all? { |e| e.is_a?(ActiveRecord::Base) }
|
4
|
+
group_by(&:class).map do |(klass, records)|
|
5
|
+
klass.to_insert_sql(records, batch_size)
|
6
|
+
end.join("\n")
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_upsert_sql(batch_size=500)
|
10
|
+
raise 'All element should be an ActiveRecord instance object' unless all? { |e| e.is_a?(ActiveRecord::Base) }
|
11
|
+
group_by(&:class).map do |(klass, records)|
|
12
|
+
klass.to_upsert_sql(records, batch_size)
|
13
|
+
end.join("\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
def t(*attrs)
|
17
|
+
if attrs.present? && present? && first.is_a?(ActiveRecord::Base)
|
18
|
+
puts Terminal::Table.new { |t|
|
19
|
+
t << attrs
|
20
|
+
t << :separator
|
21
|
+
each do |e|
|
22
|
+
t << e.attributes.values_at(*attrs.map(&:to_s))
|
23
|
+
end
|
24
|
+
}
|
25
|
+
else
|
26
|
+
table = Terminal::Table.new { |t|
|
27
|
+
v.each { |row| t << (row || :separator)}
|
28
|
+
}.to_s
|
29
|
+
|
30
|
+
terminal_width = `tput cols`.to_i
|
31
|
+
if table.lines.first.size > terminal_width
|
32
|
+
table = table.lines.map(&:chomp)
|
33
|
+
puts table[0..2].join("\n")
|
34
|
+
puts table[3..-1].join("\n#{'-' * terminal_width}\n")
|
35
|
+
else
|
36
|
+
puts table
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def v
|
42
|
+
return self unless present?
|
43
|
+
t = []
|
44
|
+
if map(&:class).uniq.size == 1
|
45
|
+
if first.is_a?(ActiveRecord::Base)
|
46
|
+
t << first.attribute_names
|
47
|
+
t << nil
|
48
|
+
each do |e|
|
49
|
+
t << e.attributes.values_at(*first.attribute_names).map(&:as_json)
|
50
|
+
end
|
51
|
+
elsif first.is_a?(Array)
|
52
|
+
t = map { |a| a.map(&:as_json) }
|
53
|
+
elsif first.is_a?(Hash) || first.is_a?(ActiveSupport::HashWithIndifferentAccess)
|
54
|
+
t << first.keys
|
55
|
+
t << nil
|
56
|
+
each do |e|
|
57
|
+
t << e.values_at(*first.keys).map(&:as_json)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
return self
|
61
|
+
end
|
62
|
+
end
|
63
|
+
t
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'active_support/core_ext/time/conversions'
|
2
|
+
require 'active_support/core_ext/object/json'
|
3
|
+
|
4
|
+
class Time
|
5
|
+
DATE_FORMATS ||= {}
|
6
|
+
DATE_FORMATS[:default] = '%Y-%m-%d %H:%M:%S'
|
7
|
+
|
8
|
+
def inspect
|
9
|
+
to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_json(*args)
|
13
|
+
to_s
|
14
|
+
end
|
15
|
+
end
|
data/lib/arql/id.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Arql
|
2
|
+
class ID
|
3
|
+
@worker_id_bits = 5
|
4
|
+
@data_center_id_bits = 5
|
5
|
+
@max_worker_id = -1 ^ (-1 << @worker_id_bits)
|
6
|
+
@max_data_center_id = -1 ^ (-1 << @data_center_id_bits)
|
7
|
+
|
8
|
+
@sequence_bits = 12
|
9
|
+
@worker_id_shift = @sequence_bits
|
10
|
+
@data_center_id_shift = @sequence_bits + @worker_id_shift
|
11
|
+
@timestamp_left_shift = @sequence_bits + @worker_id_bits + @data_center_id_bits
|
12
|
+
@sequence_mask = -1 ^ (-1 << @sequence_bits)
|
13
|
+
|
14
|
+
@id_epoch = (Time.new(2018, 1, 1, 0, 0, 0).to_f * 1000).to_i
|
15
|
+
@worker_id = 0
|
16
|
+
@data_center_id = 0
|
17
|
+
@sequence = 0
|
18
|
+
|
19
|
+
@last_timestamp = -1
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def long
|
23
|
+
ts = (Time.now.to_f * 1000).to_i
|
24
|
+
if ts < @last_timestamp
|
25
|
+
raise 'Clock moved backwards.'
|
26
|
+
end
|
27
|
+
|
28
|
+
if ts == @last_timestamp
|
29
|
+
@sequence = (@sequence + 1) & @sequence_mask
|
30
|
+
if (@sequence == 0)
|
31
|
+
ts = til_next_millis(@last_timestamp)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
@sequence = 0
|
35
|
+
end
|
36
|
+
@last_timestamp = ts
|
37
|
+
|
38
|
+
((ts - @id_epoch) << @timestamp_left_shift) | (@data_center_id << @data_center_id_shift) | (@worker_id << @worker_id_shift) | @sequence
|
39
|
+
end
|
40
|
+
|
41
|
+
def uuid
|
42
|
+
require 'securerandom'
|
43
|
+
SecureRandom.uuid.gsub('-', '')
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def til_next_millis(last_timestamp)
|
49
|
+
ts = (Time.now.to_f * 1000).to_i
|
50
|
+
while ts <= last_timestamp
|
51
|
+
ts = (Time.now.to_f * 1000).to_i
|
52
|
+
end
|
53
|
+
ts
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
::ID = Arql::ID
|