izolenta 0.0.2 → 0.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7209aebff713c525a2b8e8d5c2f501d9759f32407b3cb42a2f6b14422279d8d8
4
- data.tar.gz: d19e4c2ca5203587e7d1786a3137dc71cf6a715b9f32ea81529f3c4fc3d9ca1c
3
+ metadata.gz: c88c2f580d9d1d4e4f8fba35e5a865a18520c63aee5c24d6457aaa32fb6a2da8
4
+ data.tar.gz: 8b5315fb79d1dfc3a5a1186190f296848c81876a8bfeb5f29e897ce814b6e45d
5
5
  SHA512:
6
- metadata.gz: 868873584c72ff2fa9e03028916303a2de9d3f29223f2cfd33cf8b03147ece4a8d3fcb38c3befad464cad32175d39436b60dcd8428ffd0c3e1f19d9d257ffac1
7
- data.tar.gz: a1f6ac0f5758d250a87e026df7ef41d09d78cdc5f50a0c017205f1c079b34fcdd46bdb6280b2fe5d5b99571c29c5ce4fac366e0a060b7aa2b0ea6835b7ca9584
6
+ metadata.gz: 6394a0583ce73f68e0bd50f1d35feb8c2dd5a74f4f6ae729d5e446291b7495098b4772d84039eb185254cbbc7f153e90910f0dc9d20edfe3057223bea1040f2e
7
+ data.tar.gz: 560940e4fa253be7315f351e9c757d73d995eafe7d8584ad08ffb6ec9b235aa8c8386f09b01f6d5a4244c499be2fae5e814580a920391abb20c3352df13a21eb
data/.rubocop.yml ADDED
@@ -0,0 +1,92 @@
1
+ inherit_gem:
2
+ rubocop-shopify: rubocop.yml
3
+ #
4
+ #Style/SingleLineMethods:
5
+ # Description: 'Avoid single-line methods.'
6
+ # StyleGuide: '#no-single-line-methods'
7
+ # Enabled: false
8
+ # VersionAdded: '0.9'
9
+ # VersionChanged: '1.8'
10
+ # AllowIfMethodIsEmpty: true
11
+ #
12
+ #Style/AsciiComments:
13
+ # Description: 'Use only ascii symbols in comments.'
14
+ # StyleGuide: '#english-comments'
15
+ # Enabled: false
16
+ # VersionAdded: '0.9'
17
+ # VersionChanged: '1.21'
18
+ # AllowedChars:
19
+ # - ©
20
+ #
21
+ #Layout/LineLength:
22
+ # Description: 'Checks that line length does not exceed the configured limit.'
23
+ # StyleGuide: '#max-line-length'
24
+ # Enabled: true
25
+ # VersionAdded: '0.25'
26
+ # VersionChanged: '1.4'
27
+ # Max: 120
28
+ # # To make it possible to copy or click on URIs in the code, we allow lines
29
+ # # containing a URI to be longer than Max.
30
+ # AllowHeredoc: true
31
+ # AllowURI: true
32
+ # URISchemes:
33
+ # - http
34
+ # - https
35
+ # # The IgnoreCopDirectives option causes the LineLength rule to ignore cop
36
+ # # directives like '# rubocop: enable ...' when calculating a line's length.
37
+ # IgnoreCopDirectives: true
38
+ # # The AllowedPatterns option is a list of !ruby/regexp and/or string
39
+ # # elements. Strings will be converted to Regexp objects. A line that matches
40
+ # # any regular expression listed in this option will be ignored by LineLength.
41
+ # AllowedPatterns: []
42
+ # IgnoredPatterns: [] # deprecated
43
+ # Exclude:
44
+ # - "./test/**/**/*"
45
+ #
46
+ #Metrics/ClassLength:
47
+ # Description: 'Avoid classes longer than 100 lines of code.'
48
+ # Enabled: false
49
+ # VersionAdded: '0.25'
50
+ # VersionChanged: '0.87'
51
+ # CountComments: false # count full line comments?
52
+ # Max: 100
53
+ # CountAsOne: []
54
+ #
55
+ #Lint/MissingCopEnableDirective:
56
+ # Description: 'Checks for a `# rubocop:enable` after `# rubocop:disable`.'
57
+ # Enabled: true
58
+ # VersionAdded: '0.52'
59
+ # # Maximum number of consecutive lines the cop can be disabled for.
60
+ # # 0 allows only single-line disables
61
+ # # 1 would mean the maximum allowed is the following:
62
+ # # # rubocop:disable SomeCop
63
+ # # a = 1
64
+ # # # rubocop:enable SomeCop
65
+ # # .inf for any size
66
+ # MaximumRangeSize: .inf
67
+ #
68
+ #Style/MethodCallWithArgsParentheses:
69
+ # Enabled: true
70
+ # IgnoredMethods:
71
+ # - require
72
+ # - require_relative
73
+ # - require_dependency
74
+ # - yield
75
+ # - raise
76
+ # - puts
77
+ # Exclude:
78
+ # - "/**/Gemfile"
79
+ # - "./db/**/*"
80
+ #
81
+ #Layout/EmptyLinesAroundBlockBody:
82
+ # # its more documentation than code, so it should be readable and
83
+ # # there are huge amount of multiline description, looks nasty without spaces
84
+ # Exclude:
85
+ # - "./app_doc/**/*"
86
+ #
87
+ #Style/ClassAndModuleChildren:
88
+ # Enabled: false
89
+ #
90
+ #Lint/UnderscorePrefixedVariableName:
91
+ # Exclude:
92
+ # - "./test/**/**/*"
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ #0.0.5
2
+ - rubocop-shopify added to dev dependencies
3
+ - fixed ruby 3.0 incompatibility in delegate_uniqueness, now gem could be used with ruby 3+
4
+
5
+ #0.0.4
6
+ - removed 'OR REPLACE' in trigger definition, lowering Postgres version constraint
7
+ - trigger_condition added ( could replace partial uniq index )
8
+
9
+ #0.0.3
10
+ - wrapper_function options added, you can define function to convert to uniquely sortable types, for instance array to string
11
+ - moved all helper functions to internal module, just to keep things clear on the migrations
12
+
1
13
  #0.0.2
2
14
  - delegate_uniqueness helper is available as a migration method
3
15
  - functionality worked and tested
data/Gemfile CHANGED
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in izolenta.gemspec
4
6
  gemspec
5
7
 
6
- gem "rake", "~> 12.0"
7
8
  gem "minitest", "~> 5.0"
9
+ gem "rake", "~> 12.0"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- izolenta (0.0.2)
4
+ izolenta (0.0.5)
5
5
  pg
6
6
 
7
7
  GEM
@@ -18,18 +18,41 @@ GEM
18
18
  minitest (>= 5.1)
19
19
  tzinfo (~> 2.0)
20
20
  zeitwerk (~> 2.3)
21
+ ast (2.4.2)
21
22
  byebug (11.1.3)
22
23
  coderay (1.1.3)
23
24
  concurrent-ruby (1.1.9)
24
25
  i18n (1.8.11)
25
26
  concurrent-ruby (~> 1.0)
27
+ json (2.6.2)
26
28
  method_source (1.0.0)
27
29
  minitest (5.14.4)
28
- pg (1.2.3)
30
+ parallel (1.22.1)
31
+ parser (3.1.2.0)
32
+ ast (~> 2.4.1)
33
+ pg (1.4.1)
29
34
  pry (0.13.1)
30
35
  coderay (~> 1.1)
31
36
  method_source (~> 1.0)
37
+ rainbow (3.1.1)
32
38
  rake (12.3.3)
39
+ regexp_parser (2.5.0)
40
+ rexml (3.2.5)
41
+ rubocop (1.31.2)
42
+ json (~> 2.3)
43
+ parallel (~> 1.10)
44
+ parser (>= 3.1.0.0)
45
+ rainbow (>= 2.2.2, < 4.0)
46
+ regexp_parser (>= 1.8, < 3.0)
47
+ rexml (>= 3.2.5, < 4.0)
48
+ rubocop-ast (>= 1.18.0, < 2.0)
49
+ ruby-progressbar (~> 1.7)
50
+ unicode-display_width (>= 1.4.0, < 3.0)
51
+ rubocop-ast (1.18.0)
52
+ parser (>= 3.1.1.0)
53
+ rubocop-shopify (2.8.0)
54
+ rubocop (~> 1.31)
55
+ ruby-progressbar (1.11.0)
33
56
  ruby_jard (0.3.1)
34
57
  byebug (>= 9.1, < 12.0)
35
58
  pry (~> 0.13.0)
@@ -37,6 +60,7 @@ GEM
37
60
  tty-screen (0.8.1)
38
61
  tzinfo (2.0.4)
39
62
  concurrent-ruby (~> 1.0)
63
+ unicode-display_width (2.2.0)
40
64
  zeitwerk (2.5.1)
41
65
 
42
66
  PLATFORMS
@@ -47,6 +71,8 @@ DEPENDENCIES
47
71
  izolenta!
48
72
  minitest (~> 5.0)
49
73
  rake (~> 12.0)
74
+ rubocop
75
+ rubocop-shopify
50
76
  ruby_jard
51
77
 
52
78
  BUNDLED WITH
data/README.md CHANGED
@@ -39,6 +39,22 @@ class YourMigration < ActiveRecord::Migration[5.0]
39
39
  delegate_uniqueness( :your_table_name, :column_name )
40
40
  end
41
41
  end
42
+
43
+ class WithWrapperFunctionMigration < ActiveRecord::Migration[5.0]
44
+ # define some where before 'type_conversion_function' to use it later
45
+ def change
46
+ delegate_uniqueness( :your_table_name, :column_name, wrapper_function: 'type_conversion_function' )
47
+ end
48
+ end
49
+
50
+
51
+ class WithWrapperFunctionMigration < ActiveRecord::Migration[5.0]
52
+ # apply trigger condition for partial uniqueness
53
+ def change
54
+ delegate_uniqueness( :your_table_name, :column_name, trigger_condition: 'NEW.type IS NOT NULL' )
55
+ end
56
+ end
57
+
42
58
  ```
43
59
 
44
60
  ## Development
@@ -50,6 +66,10 @@ docker-compose run test /bin/bash
50
66
  > service postgresql start && rake test
51
67
  ```
52
68
 
69
+ ## Future Features
70
+ - Sequel migration helpers
71
+ - Existing data sync
72
+
53
73
  ## Contributing
54
74
 
55
75
  Bug reports and pull requests are welcome on GitHub at https://github.com/alekseyl/izolenta. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/izolenta/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rake/testtask"
3
5
 
@@ -7,4 +9,4 @@ Rake::TestTask.new(:test) do |t|
7
9
  t.test_files = FileList["test/**/*_test.rb"]
8
10
  end
9
11
 
10
- task :default => :test
12
+ task default: :test
data/izolenta.gemspec CHANGED
@@ -1,4 +1,6 @@
1
- require_relative 'lib/izolenta/version'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/izolenta/version"
2
4
 
3
5
  Gem::Specification.new do |spec|
4
6
  spec.name = "izolenta"
@@ -6,8 +8,8 @@ Gem::Specification.new do |spec|
6
8
  spec.authors = ["alekseyl"]
7
9
  spec.email = ["leshchuk@gmail.com"]
8
10
 
9
- spec.summary = %q{Migration helpers for delegated uniqueness in Postgres}
10
- spec.description = %q{Migration helpers for delegated uniqueness in Postgres}
11
+ spec.summary = "Migration helpers for delegated uniqueness in Postgres"
12
+ spec.description = "Migration helpers for delegated uniqueness in Postgres"
11
13
  spec.homepage = "https://github.com/alekseyl/izolenta"
12
14
  spec.license = "MIT"
13
15
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
@@ -20,14 +22,15 @@ Gem::Specification.new do |spec|
20
22
 
21
23
  # Specify which files should be added to the gem when it is released.
22
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
27
  end
26
28
  spec.bindir = "exe"
27
29
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
30
  spec.require_paths = ["lib"]
29
31
 
30
- spec.add_dependency "pg"
31
- spec.add_development_dependency "activerecord", ">= 5"
32
- spec.add_development_dependency "ruby_jard"
32
+ spec.add_dependency("pg")
33
+ spec.add_development_dependency("activerecord", ">= 5")
34
+ spec.add_development_dependency("rubocop-shopify")
35
+ spec.add_development_dependency("ruby_jard")
33
36
  end
@@ -1,54 +1,86 @@
1
- module Izolenta::ActiveRecordMigration
2
-
3
- def delegate_uniqueness(origin_table, column, options = {})
4
- helper_table_name = "#{column}_#{origin_table}_uniqs"
5
- reversible do |dir|
6
- dir.up {
7
- create_helper_table(helper_table_name, column, get_column_type(origin_table, column ) )
8
- create_sync_trigger( origin_table, column, helper_table_name )
9
- }
10
-
11
- dir.down {
12
- drop_table( helper_table_name )
13
- drop_sync_trigger( origin_table, column )
14
- }
15
- end
16
- end
1
+ # frozen_string_literal: true
17
2
 
18
- def create_helper_table(helper_table, column_name, column_type )
19
- ActiveRecord::Base.connection.execute <<~CREATE_TABLE
20
- CREATE TABLE #{helper_table} ( #{column_name} #{column_type} );
21
- CREATE_TABLE
3
+ if defined? ActiveRecord
4
+ module Izolenta
5
+ module ActiveRecordMigration
6
+ # options:
7
+ # wrapper_function: 'some_func' # some_func should be defined prior
8
+ def delegate_uniqueness(origin_table, column, **options)
9
+ helper_table_name = "#{column}_#{origin_table}_uniqs"
22
10
 
23
- add_index( helper_table, column_name, unique: true )
24
- end
11
+ reversible do |dir|
12
+ dir.up do
13
+ Helpers.create_helper_table(helper_table_name, column,
14
+ Helpers.get_new_column_type(origin_table, column, options))
15
+ add_index(helper_table_name, column, unique: true)
25
16
 
26
- def create_sync_trigger(table, column_name, helper_table_name)
27
- trg_name = "#{table}_#{column_name}_trg"
28
- ActiveRecord::Base.connection.execute <<~SYNC_TRIGGER
29
- CREATE OR REPLACE FUNCTION #{trg_name}() RETURNS trigger AS $$
30
- BEGIN
31
- INSERT INTO #{helper_table_name} VALUES ( NEW.#{column_name} );
32
- RETURN NEW;
33
- END $$ LANGUAGE plpgSQL;
34
-
35
- CREATE OR REPLACE TRIGGER #{trg_name} BEFORE INSERT ON #{table} FOR EACH ROW
36
- EXECUTE FUNCTION #{trg_name}();
37
- SYNC_TRIGGER
38
- end
17
+ Helpers.create_sync_trigger(origin_table, column, helper_table_name, options)
18
+ end
39
19
 
40
- def drop_sync_trigger(table, column_name)
41
- trg_name = "#{table}_#{column_name}_trg"
20
+ dir.down do
21
+ drop_table(helper_table_name)
22
+ Helpers.drop_sync_trigger(origin_table, column)
23
+ end
24
+ end
25
+ end
42
26
 
43
- ActiveRecord::Base.connection.execute <<~SYNC_TRIGGER
44
- DROP TRIGGER IF EXISTS #{trg_name} ON #{table};
45
- SYNC_TRIGGER
46
- end
27
+ # helpers na
28
+ module Helpers
29
+ class << self
30
+ def create_helper_table(helper_table, column_name, column_type)
31
+ ActiveRecord::Base.connection.execute("CREATE TABLE #{helper_table} ( #{column_name} #{column_type} );")
32
+ end
47
33
 
48
- def get_column_type(origin_table, column)
49
- ActiveRecord::Base.connection.schema_cache.columns_hash(origin_table.to_s)[column.to_s]&.sql_type
50
- end
34
+ def create_sync_trigger(table, column_name, helper_table_name, options)
35
+ trg_name = "#{table}_#{column_name}_trg"
36
+ insert_value = if options[:wrapper_function]
37
+ "#{options[:wrapper_function]}(NEW.#{column_name})"
38
+ else
39
+ "NEW.#{column_name}"
40
+ end
41
+
42
+ trigger_condition = "WHEN( #{options[:trigger_condition]} )" if options[:trigger_condition]
43
+
44
+ ActiveRecord::Base.connection.execute(<<~SYNC_TRIGGER)
45
+ CREATE OR REPLACE FUNCTION #{trg_name}() RETURNS trigger AS $$
46
+ BEGIN#{" "}
47
+ INSERT INTO #{helper_table_name} VALUES ( #{insert_value} );
48
+ RETURN NEW;
49
+ END $$ LANGUAGE plpgSQL;
51
50
 
52
- end if defined? ActiveRecord
51
+ CREATE TRIGGER #{trg_name} BEFORE INSERT ON #{table}#{" "}
52
+ FOR EACH ROW
53
+ #{trigger_condition}
54
+ EXECUTE FUNCTION #{trg_name}();
55
+ SYNC_TRIGGER
56
+ end
57
+
58
+ def drop_sync_trigger(table, column_name)
59
+ trg_name = "#{table}_#{column_name}_trg"
60
+
61
+ ActiveRecord::Base.connection.execute(<<~SYNC_TRIGGER)
62
+ DROP TRIGGER IF EXISTS #{trg_name} ON #{table};
63
+ SYNC_TRIGGER
64
+ end
65
+
66
+ def get_new_column_type(origin_table, column, wrapper_function: nil, **)
67
+ wrapper_function ? get_function_type(wrapper_function) : get_column_type(origin_table, column)
68
+ end
69
+
70
+ def get_column_type(origin_table, column)
71
+ ActiveRecord::Base.connection.schema_cache.columns_hash(origin_table.to_s)[column.to_s]&.sql_type
72
+ end
73
+
74
+ def get_function_type(wrapper_function)
75
+ ActiveRecord::Base
76
+ .connection
77
+ .execute("SELECT typname FROM pg_type WHERE oid=(SELECT prorettype FROM pg_proc WHERE proname ='#{wrapper_function}')") # rubocop:disable Layout/LineLength
78
+ .first["typname"]
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
53
85
 
54
- ActiveRecord::Migration.include(Izolenta::ActiveRecordMigration) if defined? ActiveRecord
86
+ ActiveRecord::Migration.include(Izolenta::ActiveRecordMigration) if defined? ActiveRecord
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Izolenta
2
- VERSION = "0.0.2"
4
+ VERSION = "0.0.5"
3
5
  end
data/lib/izolenta.rb CHANGED
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "izolenta/version"
2
- require 'izolenta/active_record_migration'
4
+ require "izolenta/active_record_migration"
3
5
 
4
6
  module Izolenta
5
7
  class Error < StandardError; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: izolenta
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - alekseyl
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-23 00:00:00.000000000 Z
11
+ date: 2022-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop-shopify
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: ruby_jard
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -60,6 +74,7 @@ extensions: []
60
74
  extra_rdoc_files: []
61
75
  files:
62
76
  - ".gitignore"
77
+ - ".rubocop.yml"
63
78
  - ".ruby-gemset"
64
79
  - ".ruby-version"
65
80
  - ".travis.yml"