spanner-translator 0.3.1 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +12 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +10 -1
- data/lib/rubocop/ast/send_node.rb +2 -2
- data/lib/spanner/translator/cli.rb +4 -9
- data/lib/spanner/translator/configuration.rb +2 -1
- data/lib/spanner/translator/rules/check.rb +2 -0
- data/lib/spanner/translator/rules/translation/add_committed_at_timestamp.rb +17 -10
- data/lib/spanner/translator/rules/translation/add_explicit_primary_key.rb +5 -3
- data/lib/spanner/translator/rules/translation/add_index_options.rb +1 -0
- data/lib/spanner/translator/rules/translation/add_timestamps.rb +48 -0
- data/lib/spanner/translator/rules/translation/ensure_primary_key_columns_exist.rb +71 -0
- data/lib/spanner/translator/rules/translation/enum_column_to_string_with_constraint.rb +3 -2
- data/lib/spanner/translator/rules/translation/time_columns_to_time.rb +1 -1
- data/lib/spanner/translator/rules/translation.rb +4 -0
- data/lib/spanner/translator/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5245a782dcce416f03a31c38710e8e5a3acb18a64d0082c44ebb004a1e8121ec
|
4
|
+
data.tar.gz: 264532835fce24060de4579d46cf95a09b777fb05b7d0c9e6f3c16aa02360ce1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06645ad45c11413b73284d32b5fed8dc6dbb9ba1f2c2d67b78c29dae3f188dac3bace1220fbe7342990777c2c608f6f8849deabc66ef92146e5187a81594cdf9
|
7
|
+
data.tar.gz: 846b937be1eb9b5e829216567b0070790d99cef0f1c2b1b889364de6c5bc93717970c023a1658002c6c7a3f36f44cb0cee4705b3cb7dcc2a7167eb815e7e3aa7
|
data/.rubocop.yml
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rspec
|
3
|
+
|
1
4
|
AllCops:
|
2
5
|
TargetRubyVersion: 3.2.2
|
3
6
|
NewCops: enable
|
@@ -25,3 +28,12 @@ Metrics/BlockLength:
|
|
25
28
|
Max: 120
|
26
29
|
Exclude:
|
27
30
|
- 'spec/**/*'
|
31
|
+
|
32
|
+
RSpec/ExampleLength:
|
33
|
+
Max: 120
|
34
|
+
|
35
|
+
RSpec/ContextWording:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
RSpec/MultipleMemoizedHelpers:
|
39
|
+
Enabled: false
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
spanner-translator (0.3.
|
4
|
+
spanner-translator (0.3.4)
|
5
5
|
rubocop-ast (~> 1.30.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -59,6 +59,14 @@ GEM
|
|
59
59
|
unicode-display_width (>= 2.4.0, < 3.0)
|
60
60
|
rubocop-ast (1.30.0)
|
61
61
|
parser (>= 3.2.1.0)
|
62
|
+
rubocop-capybara (2.20.0)
|
63
|
+
rubocop (~> 1.41)
|
64
|
+
rubocop-factory_bot (2.25.1)
|
65
|
+
rubocop (~> 1.41)
|
66
|
+
rubocop-rspec (2.26.1)
|
67
|
+
rubocop (~> 1.40)
|
68
|
+
rubocop-capybara (~> 2.17)
|
69
|
+
rubocop-factory_bot (~> 2.22)
|
62
70
|
ruby-progressbar (1.13.0)
|
63
71
|
stringio (3.0.8)
|
64
72
|
unicode-display_width (2.5.0)
|
@@ -73,6 +81,7 @@ DEPENDENCIES
|
|
73
81
|
rake (~> 13.0)
|
74
82
|
rspec (~> 3.0)
|
75
83
|
rubocop (~> 1.59.0)
|
84
|
+
rubocop-rspec
|
76
85
|
spanner-translator!
|
77
86
|
|
78
87
|
BUNDLED WITH
|
@@ -4,7 +4,7 @@ module RuboCop
|
|
4
4
|
module AST
|
5
5
|
# Spanner-specific extensions for RuboCop::AST::SendNode objects
|
6
6
|
class SendNode
|
7
|
-
%i[index column bigint datetime timestamp].each do |t_method_name|
|
7
|
+
%i[index column bigint integer datetime timestamp timestamps].each do |t_method_name|
|
8
8
|
define_method(:"t_#{t_method_name}?") do
|
9
9
|
sending_t? && method_name == t_method_name
|
10
10
|
end
|
@@ -31,7 +31,7 @@ module RuboCop
|
|
31
31
|
find_hash_option(option_name.to_sym)&.source
|
32
32
|
end
|
33
33
|
|
34
|
-
%i[null name unique limit default].each do |option_name|
|
34
|
+
%i[null name unique limit default primary_key].each do |option_name|
|
35
35
|
define_method(:"#{option_name}_option") do
|
36
36
|
find_hash_option(option_name)
|
37
37
|
end
|
@@ -10,7 +10,7 @@ module Spanner
|
|
10
10
|
class << self
|
11
11
|
# process code translation, raising errors if a CheckRule fails.
|
12
12
|
def process_code!(code,
|
13
|
-
check_classes: Rules::Check
|
13
|
+
check_classes: Rules::Check::ORDERED_CHECK_RULES,
|
14
14
|
rule_classes: Rules::Translation::ORDERED_SCHEMA_RULES, options: {})
|
15
15
|
check_classes.each do |rule_class|
|
16
16
|
process_check_rule(Spanner::Translator::Rules::Check.const_get(rule_class), code)
|
@@ -50,14 +50,9 @@ module Spanner
|
|
50
50
|
# dumb create_table block code formatting
|
51
51
|
def formatted_create_table(code)
|
52
52
|
code.lines.map do |l|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
when /t\.index/
|
57
|
-
" #{l.strip}" unless Spanner::Translator.configuration.skip_indexes
|
58
|
-
else
|
59
|
-
" #{l.strip}"
|
60
|
-
end
|
53
|
+
next if /^\s*$/ =~ l
|
54
|
+
|
55
|
+
/(create_table|end)/ =~ l ? l.strip : " #{l.strip}"
|
61
56
|
end.compact.join("\n")
|
62
57
|
end
|
63
58
|
end
|
@@ -4,7 +4,7 @@ module Spanner
|
|
4
4
|
module Translator
|
5
5
|
# Configuration
|
6
6
|
class Configuration
|
7
|
-
attr_accessor :rules, :checks, :db_schema, :skip_indexes
|
7
|
+
attr_accessor :rules, :checks, :db_schema, :skip_indexes, :primary_key_type
|
8
8
|
attr_reader :default_primary_keys
|
9
9
|
|
10
10
|
def initialize
|
@@ -17,6 +17,7 @@ module Spanner
|
|
17
17
|
@db_schema = "db/schema.rb"
|
18
18
|
@default_primary_keys = ["id"]
|
19
19
|
@skip_indexes = false
|
20
|
+
@primary_key_type = :integer
|
20
21
|
end
|
21
22
|
|
22
23
|
# Set primary key column(s), make sure it is an array of symbols
|
@@ -4,18 +4,25 @@ module Spanner
|
|
4
4
|
module Translator
|
5
5
|
module Rules
|
6
6
|
module Translation
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
7
|
+
# In the create_table block, add t.time :committed_at with
|
8
|
+
# allow_commit_timestamp: true before the first t.index or at the end of
|
9
|
+
# the block if no t.index is found.
|
11
10
|
class AddCommittedAtTimestamp < BaseRule
|
12
|
-
|
13
|
-
return if @seen["committed_at"]
|
14
|
-
return unless node.t_index?
|
11
|
+
COMMITTED_AT_COLUMN = 't.time "committed_at", null: false, allow_commit_timestamp: true'
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
def on_block(node)
|
14
|
+
# only pay attention to create_table blocks
|
15
|
+
return unless node.send_node.method_name == :create_table
|
16
|
+
|
17
|
+
# try all body children to see if any are t.index, if so, insert before
|
18
|
+
node.body.children.each do |child|
|
19
|
+
next unless child.is_a?(RuboCop::AST::SendNode) && child.t_index?
|
20
|
+
|
21
|
+
return @rewriter.insert_before(child.loc.expression, "#{COMMITTED_AT_COLUMN}\n ")
|
22
|
+
end
|
23
|
+
|
24
|
+
# otherwise, insert at end of block
|
25
|
+
@rewriter.insert_before(node.loc.end, " #{COMMITTED_AT_COLUMN}\n")
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
@@ -6,8 +6,9 @@ module Spanner
|
|
6
6
|
module Translator
|
7
7
|
module Rules
|
8
8
|
module Translation
|
9
|
-
# Based on the `create_table` statement, add an explicit column
|
10
|
-
#
|
9
|
+
# Based on the `create_table` statement, add an explicit column creation
|
10
|
+
# for the default primary key when the default is "id" and not already
|
11
|
+
# defined.
|
11
12
|
#
|
12
13
|
# Scenarios:
|
13
14
|
# create_table "users" do |t|
|
@@ -25,7 +26,8 @@ module Spanner
|
|
25
26
|
|
26
27
|
primary_key_name = "id"
|
27
28
|
|
28
|
-
@rewriter.insert_before(node.loc.expression,
|
29
|
+
@rewriter.insert_before(node.loc.expression,
|
30
|
+
"t.integer \"#{primary_key_name}\", null: false # generated\n ")
|
29
31
|
@seen["primary_key"] = true
|
30
32
|
end
|
31
33
|
end
|
@@ -14,6 +14,7 @@ module Spanner
|
|
14
14
|
def on_send(node)
|
15
15
|
capture_column_nullity(node)
|
16
16
|
return unless node.t_index?
|
17
|
+
return @rewriter.remove(node.loc.expression) if Spanner::Translator.configuration.skip_indexes
|
17
18
|
|
18
19
|
@rewriter.replace(node.loc.expression, "t.index #{new_arguments_for_column_definition(node)}")
|
19
20
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spanner
|
4
|
+
module Translator
|
5
|
+
module Rules
|
6
|
+
module Translation
|
7
|
+
# In the create_table block, add t.time :committed_at with
|
8
|
+
# allow_commit_timestamp: true before the first t.index or at the end of
|
9
|
+
# the block if no t.index is found.
|
10
|
+
class AddTimestamps < BaseRule
|
11
|
+
COLUMN_DEF = "t.timestamps # generated"
|
12
|
+
|
13
|
+
def on_send(node)
|
14
|
+
return unless node.t_datetime? && %w[created_at updated_at].include?(node.first_argument.value)
|
15
|
+
|
16
|
+
full_line = Parser::Source::Range.new(@rewriter.source_buffer,
|
17
|
+
node.loc.expression.begin_pos,
|
18
|
+
node.loc.expression.end_pos)
|
19
|
+
@rewriter.remove(full_line)
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_block(node) # rubocop:disable Metrics/AbcSize
|
23
|
+
# only pay attention to create_table blocks
|
24
|
+
return unless node.send_node.method_name == :create_table
|
25
|
+
|
26
|
+
# check all already has `t.timestamps` bail out
|
27
|
+
return if timestamp_child?(node.body.children)
|
28
|
+
|
29
|
+
# try all body children to see if any are t.index, if so, insert before
|
30
|
+
t_index = node.body.children.find { |child| child.is_a?(RuboCop::AST::SendNode) && child.t_index? }
|
31
|
+
return @rewriter.insert_before(t_index.loc.expression, "#{COLUMN_DEF}\n ") if t_index
|
32
|
+
|
33
|
+
# otherwise, insert at end of block
|
34
|
+
@rewriter.insert_before(node.loc.end, " #{COLUMN_DEF}\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def timestamp_child?(children)
|
40
|
+
children.any? do |child|
|
41
|
+
child.is_a?(RuboCop::AST::SendNode) && child.t_timestamps?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Spanner
|
6
|
+
module Translator
|
7
|
+
module Rules
|
8
|
+
module Translation
|
9
|
+
# In the create_table block, add t.time :committed_at with
|
10
|
+
# allow_commit_timestamp: true before the first t.index or at the end of
|
11
|
+
# the block if no t.index is found.
|
12
|
+
class EnsurePrimaryKeyColumnsExist < BaseRule
|
13
|
+
ID_COLUMN_TEMPLATE = %(t.%<type>s "%<colname>s", null: false # generated\n )
|
14
|
+
|
15
|
+
def on_block(node)
|
16
|
+
# only pay attention to create_table blocks
|
17
|
+
return unless node.send_node.method_name == :create_table
|
18
|
+
|
19
|
+
# get primary_key hash argument value
|
20
|
+
specified_primary_keys = value_of(node.send_node.primary_key_option.value)
|
21
|
+
return unless specified_primary_keys
|
22
|
+
|
23
|
+
# get primary_key column name[s]
|
24
|
+
undefined_columns = undefined_primary_key_columns node.body.children, specified_primary_keys
|
25
|
+
|
26
|
+
# otherwise, insert at beginning of block
|
27
|
+
undefined_columns.each do |colname|
|
28
|
+
@rewriter.insert_before(node.body.loc.expression, id_column_source(colname))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def value_of(node)
|
35
|
+
case node
|
36
|
+
when RuboCop::AST::ArrayNode
|
37
|
+
node.children.map { |c| value_of(c) }
|
38
|
+
when RuboCop::AST::StrNode, RuboCop::AST::SymbolNode
|
39
|
+
node.value.to_s
|
40
|
+
else
|
41
|
+
raise "Unexpected node type in argument slot: #{node.class}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def undefined_primary_key_columns(children, columns)
|
46
|
+
columns = Array(columns)
|
47
|
+
|
48
|
+
children.each do |child|
|
49
|
+
next unless string_argument_haver?(child)
|
50
|
+
|
51
|
+
colname = child.arguments.first.value.to_s
|
52
|
+
columns.delete(colname)
|
53
|
+
end
|
54
|
+
|
55
|
+
columns
|
56
|
+
end
|
57
|
+
|
58
|
+
def string_argument_haver?(child)
|
59
|
+
child.is_a?(RuboCop::AST::SendNode) &&
|
60
|
+
child.sending_t? &&
|
61
|
+
child.arguments.first.is_a?(RuboCop::AST::StrNode)
|
62
|
+
end
|
63
|
+
|
64
|
+
def id_column_source(colname)
|
65
|
+
format(ID_COLUMN_TEMPLATE, colname: colname, type: Spanner::Translator.configuration.primary_key_type)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -14,10 +14,11 @@ module Spanner
|
|
14
14
|
|
15
15
|
constraint_name = generate_constraint_name(node)
|
16
16
|
unless_seen(constraint_name) do
|
17
|
-
@rewriter.replace(node.loc.expression,
|
17
|
+
@rewriter.replace(node.loc.expression,
|
18
|
+
"t.string #{new_arguments_for_column_definition(node)} # generated")
|
18
19
|
constraint = %["#{column_name(node)} IN (#{choices_for_enum_string(node)})"]
|
19
20
|
@rewriter.insert_after(node.last_sibling.source_range,
|
20
|
-
"\n t.check_constraint #{constraint}, name: \"#{constraint_name}\"")
|
21
|
+
"\n t.check_constraint #{constraint}, name: \"#{constraint_name}\" # generated")
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
@@ -13,7 +13,7 @@ module Spanner
|
|
13
13
|
conditionally_propagate_argument new_arguments, node, "null"
|
14
14
|
conditionally_propagate_argument new_arguments, node, "default"
|
15
15
|
|
16
|
-
@rewriter.replace(node.loc.expression, "t.time #{new_arguments.join(", ")}")
|
16
|
+
@rewriter.replace(node.loc.expression, "t.time #{new_arguments.join(", ")} # generated")
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -10,20 +10,24 @@ module Spanner
|
|
10
10
|
ORDERED_SCHEMA_RULES = %w[
|
11
11
|
AddExplicitPrimaryKey
|
12
12
|
AddCommittedAtTimestamp
|
13
|
+
AddTimestamps
|
13
14
|
AddIndexOptions
|
14
15
|
EnumColumnToStringWithConstraint
|
15
16
|
TimeColumnsToTime
|
16
17
|
BigintToInteger
|
17
18
|
ReplaceCreateTableArgs
|
19
|
+
EnsurePrimaryKeyColumnsExist
|
18
20
|
].freeze
|
19
21
|
|
20
22
|
autoload :AddExplicitPrimaryKey, "spanner/translator/rules/translation/add_explicit_primary_key"
|
21
23
|
autoload :AddCommittedAtTimestamp, "spanner/translator/rules/translation/add_committed_at_timestamp"
|
22
24
|
autoload :AddIndexOptions, "spanner/translator/rules/translation/add_index_options"
|
23
25
|
autoload :AddTestCase, "spanner/translator/rules/translation/add_test_case"
|
26
|
+
autoload :AddTimestamps, "spanner/translator/rules/translation/add_timestamps"
|
24
27
|
autoload :BigintToInteger, "spanner/translator/rules/translation/bigint_to_integer"
|
25
28
|
autoload :EnumColumnToStringWithConstraint,
|
26
29
|
"spanner/translator/rules/translation/enum_column_to_string_with_constraint"
|
30
|
+
autoload :EnsurePrimaryKeyColumnsExist, "spanner/translator/rules/translation/ensure_primary_key_columns_exist"
|
27
31
|
autoload :ReplaceCreateTableArgs, "spanner/translator/rules/translation/replace_create_table_args"
|
28
32
|
autoload :ReplaceApplicationRecord, "spanner/translator/rules/translation/replace_application_record"
|
29
33
|
autoload :TimeColumnsToTime, "spanner/translator/rules/translation/time_columns_to_time"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spanner-translator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Bachman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop-ast
|
@@ -57,8 +57,10 @@ files:
|
|
57
57
|
- lib/spanner/translator/rules/translation/add_explicit_primary_key.rb
|
58
58
|
- lib/spanner/translator/rules/translation/add_index_options.rb
|
59
59
|
- lib/spanner/translator/rules/translation/add_test_case.rb
|
60
|
+
- lib/spanner/translator/rules/translation/add_timestamps.rb
|
60
61
|
- lib/spanner/translator/rules/translation/base_rule.rb
|
61
62
|
- lib/spanner/translator/rules/translation/bigint_to_integer.rb
|
63
|
+
- lib/spanner/translator/rules/translation/ensure_primary_key_columns_exist.rb
|
62
64
|
- lib/spanner/translator/rules/translation/enum_column_to_string_with_constraint.rb
|
63
65
|
- lib/spanner/translator/rules/translation/replace_application_record.rb
|
64
66
|
- lib/spanner/translator/rules/translation/replace_create_table_args.rb
|