arql 0.1.30
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/.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
|