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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 455e11c334ebe5c0a3e917dbd84599176e4d16d5c905a0bfbd48e6b36b5975b6
4
- data.tar.gz: 246699204b50ed9bf04b4a79639e66dcbd4219ee8fffb013823ddaeda38017e9
3
+ metadata.gz: 5245a782dcce416f03a31c38710e8e5a3acb18a64d0082c44ebb004a1e8121ec
4
+ data.tar.gz: 264532835fce24060de4579d46cf95a09b777fb05b7d0c9e6f3c16aa02360ce1
5
5
  SHA512:
6
- metadata.gz: 19c691ff6b6d087bfdc489ff90ee655dbfc6fce1a44a5640b7681d11750dbef24f03a207a37554f2ad38c62050adffea47c153eec5c41b88d3c34f545247ca8b
7
- data.tar.gz: eedfb4aa235247374887463df3c116152278de8e3855d9f7d8fed870342b1a320c570b61a902bb8e367b2db7befcf0174aed7fdf08b80fa5a15cbc4cfdde0d3e
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
@@ -10,3 +10,4 @@ gem "debug", ">= 1.0.0"
10
10
  gem "rake", "~> 13.0"
11
11
  gem "rspec", "~> 3.0"
12
12
  gem "rubocop", "~> 1.59.0"
13
+ gem "rubocop-rspec"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- spanner-translator (0.3.1)
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.constants,
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
- case l
54
- when /(create_table|end)/
55
- l.strip
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
@@ -6,6 +6,8 @@ module Spanner
6
6
  module Translator
7
7
  module Rules
8
8
  module Check # rubocop:disable Style/Documentation
9
+ ORDERED_CHECK_RULES = [].freeze
10
+
9
11
  autoload :HasAnyIndex, "spanner/translator/rules/check/has_any_index"
10
12
  end
11
13
  end
@@ -4,18 +4,25 @@ module Spanner
4
4
  module Translator
5
5
  module Rules
6
6
  module Translation
7
- # Add t.time :committed_at with allow_commit_timestamp: true before the first t.index
8
- #
9
- # FIXME (@abachman): if table has no indexes, this rule will not be applied
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
- def on_send(node)
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
- committed_at_column = 't.time "committed_at", null: false, allow_commit_timestamp: true'
17
- @rewriter.insert_before(node.loc.expression, "#{committed_at_column}\n ")
18
- @seen["committed_at"] = true
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
- # creation for the default primary key.
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, "t.integer \"#{primary_key_name}\", limit: 8, null: false\n ")
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, "t.string #{new_arguments_for_column_definition(node)}")
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"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Spanner
4
4
  module Translator
5
- VERSION = "0.3.1"
5
+ VERSION = "0.3.4"
6
6
  end
7
7
  end
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.1
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-10 00:00:00.000000000 Z
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