spanner-translator 0.3.1 → 0.3.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/.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
|