adaptive_alias 0.0.3 → 0.2.1

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: 24cc5b94c147a0fdb9dd7a92598bfbb6c76c9bdc59dc23f827f57b3cc22098c0
4
- data.tar.gz: 73c1f0a540da8acebaae0cb98a372ee0d8b162e77d574e01dab81a25f002760a
3
+ metadata.gz: 0375ca9867e41c41fe18b38cb1f37ff2a0067367304f6481250cd573c5dc5be4
4
+ data.tar.gz: bc0ebb4c4d63ea36e203b94f5c3ea0411c4fab408e93ea6c9dc0de5354c2db8c
5
5
  SHA512:
6
- metadata.gz: fd166ed45adb2164fc4c6061908a8056b659dbd1fa744e28085ecf132efd7004056f32ac68b654fee7525707c971ca9f45233dffad4b67241f97e33bd9b62c4c
7
- data.tar.gz: 0b6ae8bd8b5dce5468e2ef51c607e74a9971be52378d08f27c3f7717993eded6ada7d0a676c8f0127c051a09f8f590eb64e1ea0271600f296745e96528fc9238
6
+ metadata.gz: f0d5f519ab910bcf93c982f2ea6ca9c3e944ea0e90a07d6e938668b4575d450d6d019a60b62dd2020c373434e8ece4f7c1d9ea148ca269a4823718b80f8a8c07
7
+ data.tar.gz: 9a699915af111f6e01aa6fac45a888583a629b1f6eb0d75c84a15188f329e3f34401a330a6ee33040453b4e9f2d6a018bca61626e0784944004832c3db6d6bc3
data/.rubocop.yml CHANGED
@@ -222,9 +222,9 @@ Metrics/CyclomaticComplexity:
222
222
  Enabled: true
223
223
 
224
224
  Layout/LineLength:
225
- Description: 'Limit lines to 120 characters.'
225
+ Description: 'Limit lines to 160 characters.'
226
226
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits'
227
- Max: 120
227
+ Max: 160
228
228
  Enabled: true
229
229
  Exclude:
230
230
  - 'config/locales/**'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## Change Log
2
2
 
3
+ ### [v0.2.0](https://github.com/khiav223577/adaptive_alias/compare/v0.1.0...v0.2.0) 2022/08/05
4
+ - [#11](https://github.com/khiav223577/adaptive_alias/pull/11) [Feature] Support polymorphic (@khiav223577)
5
+ - [#10](https://github.com/khiav223577/adaptive_alias/pull/10) [Enhance] Prevent adding methods directly in class / module (@khiav223577)
6
+ - [#9](https://github.com/khiav223577/adaptive_alias/pull/9) [Enhance] Prevent changing original order of where conditions (@khiav223577)
7
+ - [#8](https://github.com/khiav223577/adaptive_alias/pull/8) [Test] Make sure we are reset to use original patch when some test cases fail (@khiav223577)
8
+ - [#7](https://github.com/khiav223577/adaptive_alias/pull/7) [Test] Add test cases to test destroy (@khiav223577)
9
+
10
+ ### [v0.1.0](https://github.com/khiav223577/adaptive_alias/compare/v0.0.3...v0.1.0) 2022/08/01
11
+ - [#6](https://github.com/khiav223577/adaptive_alias/pull/6) [Feature] Deal with creating records (@khiav223577)
12
+ - [#5](https://github.com/khiav223577/adaptive_alias/pull/5) [Enhance] Prevent infinite loop if something went wrong (@khiav223577)
13
+ - [#4](https://github.com/khiav223577/adaptive_alias/pull/4) [Fix] Attributes writer method should be defined after schema changes (@khiav223577)
14
+
15
+ ### [v0.0.3](https://github.com/khiav223577/adaptive_alias/compare/v0.0.2...v0.0.3) 2022/07/27
16
+ - [#3](https://github.com/khiav223577/adaptive_alias/pull/3) [Fix] Prevent calling custom column names from raising missing attributes (@khiav223577)
17
+
3
18
  ### [v0.0.2](https://github.com/khiav223577/adaptive_alias/compare/v0.0.1...v0.0.2) 2022/07/26
4
- - [#2](https://github.com/khiav223577/adaptive_alias/pull/2) Doesn't need rails_compatibility in runtime (@khiav223577)
5
- - [#1](https://github.com/khiav223577/adaptive_alias/pull/1) Implement adaptive_alias features (@khiav223577)
19
+ - [#2](https://github.com/khiav223577/adaptive_alias/pull/2) [Enhance] Doesn't need rails_compatibility in runtime (@khiav223577)
20
+
21
+ ### v0.0.1 2022/07/22
22
+ - [#1](https://github.com/khiav223577/adaptive_alias/pull/1) [Feature] Implement adaptive_alias features (@khiav223577)
@@ -0,0 +1,17 @@
1
+ require 'active_record'
2
+
3
+ module AdaptiveAlias
4
+ module ActiveModelPatches
5
+ module ApplyScope
6
+ def apply_scope(scope, table, key, value)
7
+ klass = table.instance_variable_get(:@klass) || table.send(:type_caster).send(:types)
8
+ key = klass.attribute_aliases[key] || key
9
+ super(scope, table, key, value)
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ class ActiveRecord::Associations::AssociationScope
16
+ prepend AdaptiveAlias::ActiveModelPatches::ApplyScope
17
+ end
@@ -1,20 +1,31 @@
1
1
  require 'active_record'
2
2
 
3
- module ActiveRecord::AttributeMethods::Read
4
- def read_attribute(attr_name, &block) # :nodoc:
5
- name = attr_name.to_s
6
- name = self.class.attribute_aliases[name] || name
3
+ module AdaptiveAlias
4
+ module ActiveModelPatches
5
+ module ReadAttribute
6
+ def read_attribute(attr_name, &block) # :nodoc:
7
+ name = attr_name.to_s
8
+ name = self.class.attribute_aliases[name] || name
7
9
 
8
- name = @primary_key if name == 'id' && @primary_key
9
- _read_attribute(name, &block)
10
- end
10
+ name = @primary_key if name == 'id' && @primary_key
11
+ _read_attribute(name, &block)
12
+ end
11
13
 
12
- def _read_attribute(attr_name, &block) # :nodoc:
13
- name = attr_name.to_s
14
- name = self.class.attribute_aliases[name] || name
14
+ # This method exists to avoid the expensive primary_key check internally, without
15
+ # breaking compatibility with the write_attribute API
16
+ def _read_attribute(attr_name, &block) # :nodoc:
17
+ name = attr_name.to_s
18
+ name = self.class.attribute_aliases[name] || name
15
19
 
16
- sync_with_transaction_state if @transaction_state&.finalized?
17
- return yield if block_given? and AdaptiveAlias.missing_value?(@attributes, self.class, name)
18
- return @attributes.fetch_value(name, &block)
20
+ sync_with_transaction_state if @transaction_state&.finalized?
21
+ return yield(name) if block_given? and AdaptiveAlias.missing_value?(@attributes, self.class, name)
22
+ return @attributes.fetch_value(name, &block)
23
+ end
24
+ end
19
25
  end
20
26
  end
27
+
28
+ # Nested module include is not supported until ruby 3.0
29
+ class ActiveRecord::Base
30
+ prepend AdaptiveAlias::ActiveModelPatches::ReadAttribute
31
+ end
@@ -1,27 +1,31 @@
1
1
  require 'active_model'
2
2
 
3
- module ActiveModel::AttributeMethods
4
- module ClassMethods
5
- def remove_proxy_call(mod, name)
6
- defn = if NAME_COMPILABLE_REGEXP.match?(name)
7
- "undef #{name}"
8
- else
9
- "remove_method(:'#{name}')"
10
- end
11
-
12
- mod.module_eval defn, __FILE__, __LINE__ + 1
13
- end
3
+ module AdaptiveAlias
4
+ module ActiveModelPatches
5
+ module RemoveAliasAttribute
6
+ def remove_proxy_call(mod, name)
7
+ mod.module_eval "remove_method(:'#{name}')", __FILE__, __LINE__ + 1
8
+ end
14
9
 
15
- def remove_alias_attribute(new_name)
16
- # association_scope -> add_constraints -> last_chain_scope -> where!(key => model[foreign_key])
17
- # self[attr_name] -> read_attribute(attr_name) -> attribute_aliases
18
- # where! -> where_clause_factory.build -> attributes = predicate_builder.resolve_column_aliases(opts) -> attribute_aliases
19
- self.attribute_aliases = attribute_aliases.except(new_name.to_s)
10
+ def remove_alias_attribute(new_name)
11
+ self.attribute_aliases = attribute_aliases.except(new_name.to_s)
20
12
 
21
- attribute_method_matchers.each do |matcher|
22
- matcher_new = matcher.method_name(new_name).to_s
23
- remove_proxy_call self, matcher_new
13
+ attribute_method_matchers.each do |matcher|
14
+ matcher_new = matcher.method_name(new_name).to_s
15
+ remove_proxy_call self, matcher_new
16
+ end
24
17
  end
25
18
  end
26
19
  end
27
20
  end
21
+
22
+ # Nested module include is not supported until ruby 3.0
23
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3')
24
+ AdaptiveAlias::ActiveModelPatches::RemoveAliasAttribute.instance_methods.each do |method|
25
+ ActiveModel::AttributeMethods::ClassMethods.define_method(method, AdaptiveAlias::ActiveModelPatches::RemoveAliasAttribute.instance_method(method))
26
+ end
27
+ else
28
+ module ActiveModel::AttributeMethods::ClassMethods
29
+ include AdaptiveAlias::ActiveModelPatches::RemoveAliasAttribute
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ require 'active_record'
2
+
3
+ module AdaptiveAlias
4
+ module ActiveModelPatches
5
+ module WriteAttribute
6
+ def write_attribute(attr_name, value)
7
+ name = attr_name.to_s
8
+ name = self.class.attribute_aliases[name] || name
9
+ super(name, value)
10
+ end
11
+
12
+ # This method exists to avoid the expensive primary_key check internally, without
13
+ # breaking compatibility with the write_attribute API
14
+ def _write_attribute(attr_name, value) # :nodoc:
15
+ name = attr_name.to_s
16
+ name = self.class.attribute_aliases[name] || name
17
+ super(name, value)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ class ActiveRecord::Base
24
+ prepend AdaptiveAlias::ActiveModelPatches::WriteAttribute
25
+ end
@@ -2,7 +2,11 @@ module AdaptiveAlias
2
2
  module Hooks
3
3
  module Association
4
4
  def find_target(*)
5
- AdaptiveAlias.rescue_statement_invalid(nil){ super }
5
+ AdaptiveAlias.rescue_statement_invalid(nil, reflection){ super }
6
+ end
7
+
8
+ def create!(attributes = {}, &block)
9
+ AdaptiveAlias.rescue_statement_invalid(association_scope, reflection){ super }
6
10
  end
7
11
  end
8
12
  end
@@ -1,8 +1,8 @@
1
1
  module AdaptiveAlias
2
2
  module Hooks
3
3
  module AssociationScope
4
- def last_chain_scope(*)
5
- AdaptiveAlias.rescue_missing_attribute{ super }
4
+ def last_chain_scope(_scope, reflection, owner)
5
+ AdaptiveAlias.rescue_missing_attribute(owner.class){ super }
6
6
  end
7
7
  end
8
8
  end
@@ -2,15 +2,15 @@ module AdaptiveAlias
2
2
  module Hooks
3
3
  module Relation
4
4
  def pluck(*)
5
- AdaptiveAlias.rescue_statement_invalid(self){ super }
5
+ AdaptiveAlias.rescue_statement_invalid(self, nil){ super }
6
6
  end
7
7
 
8
8
  def select_all(*)
9
- AdaptiveAlias.rescue_statement_invalid(self){ super }
9
+ AdaptiveAlias.rescue_statement_invalid(self, nil){ super }
10
10
  end
11
11
 
12
12
  def exec_queries(*)
13
- AdaptiveAlias.rescue_statement_invalid(self){ super }
13
+ AdaptiveAlias.rescue_statement_invalid(self, nil){ super }
14
14
  end
15
15
  end
16
16
  end
@@ -2,7 +2,7 @@ module AdaptiveAlias
2
2
  module Hooks
3
3
  module SingularAssociation
4
4
  def reader(*)
5
- AdaptiveAlias.rescue_missing_attribute{ super }
5
+ AdaptiveAlias.rescue_missing_attribute(owner.class){ super }
6
6
  end
7
7
  end
8
8
  end
@@ -14,7 +14,10 @@ module AdaptiveAlias
14
14
  def remove!
15
15
  super
16
16
  @klass.remove_alias_attribute(@old_column)
17
- ForwardPatch.new(@klass, @old_column, @new_column).apply!
17
+ @klass.define_attribute_method(@old_column)
18
+ new_patch = ForwardPatch.new(@klass, @old_column, @new_column)
19
+ new_patch.apply!
20
+ return new_patch
18
21
  end
19
22
  end
20
23
  end
@@ -6,6 +6,7 @@ module AdaptiveAlias
6
6
  attr_reader :fix_association
7
7
  attr_reader :fix_missing_attribute
8
8
  attr_reader :removed
9
+ attr_reader :removable
9
10
 
10
11
  def initialize(klass, old_column, new_column)
11
12
  @klass = klass
@@ -15,49 +16,79 @@ module AdaptiveAlias
15
16
 
16
17
  def add_hooks!(current_column:, alias_column:, log_warning: false)
17
18
  patch = self
19
+ klass = @klass
18
20
  old_column = @old_column
19
21
  new_column = @new_column
20
22
 
21
- AdaptiveAlias.get_or_create_model_module(@klass).instance_exec do
23
+ AdaptiveAlias.get_or_create_model_module(klass).instance_exec do
22
24
  remove_method(new_column) if method_defined?(new_column)
23
25
  define_method(new_column) do
24
- AdaptiveAlias.rescue_missing_attribute{ self[new_column] }
26
+ AdaptiveAlias.rescue_missing_attribute(klass){ self[new_column] }
27
+ end
28
+
29
+ remove_method("#{new_column}=") if method_defined?("#{new_column}=")
30
+ define_method("#{new_column}=") do |*args|
31
+ AdaptiveAlias.rescue_missing_attribute(klass){ super(*args) }
25
32
  end
26
33
 
27
34
  remove_method(old_column) if method_defined?(old_column)
28
35
  define_method(old_column) do
29
36
  patch.log_warning if log_warning
30
- AdaptiveAlias.rescue_missing_attribute{ self[old_column] }
37
+ AdaptiveAlias.rescue_missing_attribute(klass){ self[old_column] }
38
+ end
39
+
40
+ remove_method("#{old_column}=") if method_defined?("#{old_column}=")
41
+ define_method("#{old_column}=") do |*args|
42
+ patch.log_warning if log_warning
43
+ AdaptiveAlias.rescue_missing_attribute(klass){ super(*args) }
31
44
  end
32
45
  end
33
46
 
34
- expected_error_message = "Mysql2::Error: Unknown column '#{@klass.table_name}.#{current_column}' in 'where clause'".freeze
47
+ expected_association_err_msgs = [
48
+ "Mysql2::Error: Unknown column '#{klass.table_name}.#{current_column}' in 'where clause'".freeze,
49
+ "Mysql2::Error: Unknown column '#{current_column}' in 'field list'".freeze,
50
+ ].freeze
51
+
52
+ expected_attribute_err_msgs = [
53
+ "can't write unknown attribute `#{current_column}`".freeze,
54
+ "missing attribute: #{current_column}".freeze,
55
+ ].freeze
35
56
 
36
- @fix_missing_attribute = proc do
57
+ @fix_missing_attribute = proc do |error_klass, error|
58
+ next false if not patch.removable
37
59
  next false if patch.removed
60
+ next false if klass != error_klass
61
+ next false if not expected_attribute_err_msgs.include?(error.message)
38
62
 
39
63
  patch.remove!
40
64
  next true
41
65
  end
42
66
 
43
- @fix_association = proc do |target, error|
44
- next false if patch.removed || error.message != expected_error_message
67
+ @fix_association = proc do |relation, reflection, error|
68
+ next false if not patch.removable
69
+ next false if patch.removed
70
+ next false if not expected_association_err_msgs.include?(error.message)
45
71
 
46
72
  patch.remove!
47
73
 
48
- if target
49
- hash = target.where_values_hash
50
- hash[alias_column] = hash.delete(current_column) if hash.key?(current_column)
51
- target.instance_variable_set(:@arel, nil)
52
- target.unscope!(:where).where!(hash)
74
+ if relation
75
+ relation.where_clause.send(:predicates).each do |node|
76
+ next if node.left.name != current_column.to_s
77
+ next if klass.table_name != node.left.relation.name
78
+
79
+ node.left = node.left.clone
80
+ node.left.name = alias_column.to_s
81
+ end
53
82
  end
54
83
 
84
+ reflection.clear_association_scope_cache if reflection
85
+
55
86
  next true
56
87
  end
57
88
  end
58
89
 
59
90
  def log_warning
60
- if @prev_warning_time == nil || @prev_warning_time < AdaptiveAlias.log_interval.ago
91
+ if @prev_warning_time == nil || @prev_warning_time < Time.now - AdaptiveAlias.log_interval
61
92
  @prev_warning_time = Time.now
62
93
  AdaptiveAlias.unexpected_old_column_proc&.call
63
94
  end
@@ -65,11 +96,15 @@ module AdaptiveAlias
65
96
 
66
97
  def remove!
67
98
  @removed = true
68
- @klass.send(:reload_schema_from_cache)
69
- @klass.initialize_find_by_cache
99
+ @klass.reset_column_information
100
+ @klass.columns_hash
70
101
  @fix_association = nil
71
102
  @fix_missing_attribute = nil
72
103
  end
104
+
105
+ def mark_removable
106
+ @removable = true
107
+ end
73
108
  end
74
109
  end
75
110
  end
@@ -14,7 +14,10 @@ module AdaptiveAlias
14
14
  def remove!
15
15
  super
16
16
  @klass.remove_alias_attribute(@new_column)
17
- BackwardPatch.new(@klass, @old_column, @new_column).apply!
17
+ @klass.define_attribute_method(@new_column)
18
+ new_patch = BackwardPatch.new(@klass, @old_column, @new_column)
19
+ new_patch.apply!
20
+ return new_patch
18
21
  end
19
22
  end
20
23
  end
@@ -1,3 +1,3 @@
1
1
  module AdaptiveAlias
2
- VERSION = '0.0.3'
2
+ VERSION = '0.2.1'
3
3
  end
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'adaptive_alias/version'
4
4
  require 'adaptive_alias/active_model_patches/read_attribute'
5
+ require 'adaptive_alias/active_model_patches/write_attribute'
5
6
  require 'adaptive_alias/active_model_patches/remove_alias_attribute'
7
+ require 'adaptive_alias/active_model_patches/apply_scope'
6
8
  require 'adaptive_alias/patches/backward_patch'
7
9
  require 'adaptive_alias/patches/forward_patch'
8
10
 
@@ -31,27 +33,31 @@ module AdaptiveAlias
31
33
  extend ActiveSupport::Concern
32
34
 
33
35
  included do
34
- if column_names.include?(new_column)
35
- Patches::BackwardPatch.new(self, old_column, new_column).apply!
36
- else
37
- Patches::ForwardPatch.new(self, old_column, new_column).apply!
38
- end
36
+ patch = (column_names.include?(new_column) ? Patches::BackwardPatch : Patches::ForwardPatch).new(self, old_column, new_column)
37
+ patch.apply!
38
+ patch.mark_removable
39
39
  end
40
40
  end
41
41
  end
42
42
 
43
- def rescue_statement_invalid(relation)
43
+ def rescue_statement_invalid(relation, reflection, &block)
44
44
  yield
45
45
  rescue ActiveRecord::StatementInvalid => error
46
- raise error if AdaptiveAlias.current_patches.all?{|_key, patch| !patch.fix_association.call(relation, error) }
47
- retry
46
+ raise error if AdaptiveAlias.current_patches.all?{|_key, patch| !patch.fix_association.call(relation, reflection, error) }
47
+
48
+ result = rescue_statement_invalid(relation, reflection, &block)
49
+ AdaptiveAlias.current_patches.each_value(&:mark_removable)
50
+ return result
48
51
  end
49
52
 
50
- def rescue_missing_attribute
53
+ def rescue_missing_attribute(klass, &block)
51
54
  yield
52
55
  rescue ActiveModel::MissingAttributeError => error
53
- raise error if AdaptiveAlias.current_patches.all?{|_key, patch| !patch.fix_missing_attribute.call }
54
- retry
56
+ raise error if AdaptiveAlias.current_patches.all?{|_key, patch| !patch.fix_missing_attribute.call(klass, error) }
57
+
58
+ result = rescue_missing_attribute(klass, &block)
59
+ AdaptiveAlias.current_patches.each_value(&:mark_removable)
60
+ return result
55
61
  end
56
62
 
57
63
  def get_or_create_model_module(klass)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adaptive_alias
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - khiav reoy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-27 00:00:00.000000000 Z
11
+ date: 2022-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -123,8 +123,10 @@ files:
123
123
  - gemfiles/6.1.gemfile
124
124
  - gemfiles/7.0.gemfile
125
125
  - lib/adaptive_alias.rb
126
+ - lib/adaptive_alias/active_model_patches/apply_scope.rb
126
127
  - lib/adaptive_alias/active_model_patches/read_attribute.rb
127
128
  - lib/adaptive_alias/active_model_patches/remove_alias_attribute.rb
129
+ - lib/adaptive_alias/active_model_patches/write_attribute.rb
128
130
  - lib/adaptive_alias/hooks/association.rb
129
131
  - lib/adaptive_alias/hooks/association_scope.rb
130
132
  - lib/adaptive_alias/hooks/relation.rb