adaptive_alias 0.0.3 → 0.2.1

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: 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