adaptive_alias 0.0.3 → 0.1.0

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: 81757ab8f4098990940d499ee2286c8c831fdac6462dfcafcd59b868ff596ec2
4
+ data.tar.gz: b88816e4745352dc63fb29a4d102a52935538bd0f5ef5591f8ce56e9057dc9d5
5
5
  SHA512:
6
- metadata.gz: fd166ed45adb2164fc4c6061908a8056b659dbd1fa744e28085ecf132efd7004056f32ac68b654fee7525707c971ca9f45233dffad4b67241f97e33bd9b62c4c
7
- data.tar.gz: 0b6ae8bd8b5dce5468e2ef51c607e74a9971be52378d08f27c3f7717993eded6ada7d0a676c8f0127c051a09f8f590eb64e1ea0271600f296745e96528fc9238
6
+ metadata.gz: 60cb4e6e2f31787f93f6edf5415bd3fbe283999c50d0fba89d9e48e055a5c06ca7479ee8a158ec4618453352bc8034a18e9acbafae5905f4e4f2a7a0ee8ba54f
7
+ data.tar.gz: e80d6cdd3e41254d07e70e4440bc120b6940bfddb31f54709e97becafc18dd4eef2341832fc049d750e247e002a8097f4d9128697de0f657c8fdd1b47310d103
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,10 @@
1
1
  ## Change Log
2
2
 
3
+ ### [v0.0.3](https://github.com/khiav223577/adaptive_alias/compare/v0.0.2...v0.0.3) 2022/07/27
4
+ - [#3](https://github.com/khiav223577/adaptive_alias/pull/3) Fix: prevent calling custom column names from raising missing attributes (@khiav223577)
5
+
3
6
  ### [v0.0.2](https://github.com/khiav223577/adaptive_alias/compare/v0.0.1...v0.0.2) 2022/07/26
4
7
  - [#2](https://github.com/khiav223577/adaptive_alias/pull/2) Doesn't need rails_compatibility in runtime (@khiav223577)
8
+
9
+ ### v0.0.1 2022/07/22
5
10
  - [#1](https://github.com/khiav223577/adaptive_alias/pull/1) Implement adaptive_alias features (@khiav223577)
@@ -9,12 +9,14 @@ module ActiveRecord::AttributeMethods::Read
9
9
  _read_attribute(name, &block)
10
10
  end
11
11
 
12
+ # This method exists to avoid the expensive primary_key check internally, without
13
+ # breaking compatibility with the write_attribute API
12
14
  def _read_attribute(attr_name, &block) # :nodoc:
13
15
  name = attr_name.to_s
14
16
  name = self.class.attribute_aliases[name] || name
15
17
 
16
18
  sync_with_transaction_state if @transaction_state&.finalized?
17
- return yield if block_given? and AdaptiveAlias.missing_value?(@attributes, self.class, name)
19
+ return yield(name) if block_given? and AdaptiveAlias.missing_value?(@attributes, self.class, name)
18
20
  return @attributes.fetch_value(name, &block)
19
21
  end
20
22
  end
@@ -3,19 +3,10 @@ require 'active_model'
3
3
  module ActiveModel::AttributeMethods
4
4
  module ClassMethods
5
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
6
+ mod.module_eval "remove_method(:'#{name}')", __FILE__, __LINE__ + 1
13
7
  end
14
8
 
15
9
  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
10
  self.attribute_aliases = attribute_aliases.except(new_name.to_s)
20
11
 
21
12
  attribute_method_matchers.each do |matcher|
@@ -0,0 +1,22 @@
1
+ require 'active_record'
2
+
3
+ module ActiveRecord::AttributeMethods::Write
4
+ def write_attribute(attr_name, value)
5
+ name = attr_name.to_s
6
+ name = self.class.attribute_aliases[name] || name
7
+
8
+ name = @primary_key if name == 'id' && @primary_key
9
+ _write_attribute(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
+
18
+ sync_with_transaction_state if @transaction_state&.finalized?
19
+ @attributes.write_from_user(name, value)
20
+ value
21
+ end
22
+ end
@@ -4,6 +4,10 @@ module AdaptiveAlias
4
4
  def find_target(*)
5
5
  AdaptiveAlias.rescue_statement_invalid(nil){ super }
6
6
  end
7
+
8
+ def create!(attributes = {}, &block)
9
+ AdaptiveAlias.rescue_statement_invalid(association_scope){ super }
10
+ end
7
11
  end
8
12
  end
9
13
  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,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,6 +14,7 @@ module AdaptiveAlias
14
14
  def remove!
15
15
  super
16
16
  @klass.remove_alias_attribute(@old_column)
17
+ @klass.define_attribute_method(@old_column)
17
18
  ForwardPatch.new(@klass, @old_column, @new_column).apply!
18
19
  end
19
20
  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,39 +16,65 @@ 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
35
51
 
36
- @fix_missing_attribute = proc do
52
+ expected_attribute_err_msgs = [
53
+ "can't write unknown attribute `#{current_column}`".freeze,
54
+ "missing attribute: #{current_column}".freeze,
55
+ ].freeze
56
+
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
67
  @fix_association = proc do |target, error|
44
- next false if patch.removed || error.message != expected_error_message
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
74
  if target
49
75
  hash = target.where_values_hash
50
76
  hash[alias_column] = hash.delete(current_column) if hash.key?(current_column)
77
+ hash[alias_column.to_s] = hash.delete(current_column.to_s) if hash.key?(current_column.to_s)
51
78
  target.instance_variable_set(:@arel, nil)
52
79
  target.unscope!(:where).where!(hash)
53
80
  end
@@ -57,7 +84,7 @@ module AdaptiveAlias
57
84
  end
58
85
 
59
86
  def log_warning
60
- if @prev_warning_time == nil || @prev_warning_time < AdaptiveAlias.log_interval.ago
87
+ if @prev_warning_time == nil || @prev_warning_time < Time.now - AdaptiveAlias.log_interval
61
88
  @prev_warning_time = Time.now
62
89
  AdaptiveAlias.unexpected_old_column_proc&.call
63
90
  end
@@ -65,11 +92,15 @@ module AdaptiveAlias
65
92
 
66
93
  def remove!
67
94
  @removed = true
68
- @klass.send(:reload_schema_from_cache)
69
- @klass.initialize_find_by_cache
95
+ @klass.reset_column_information
96
+ @klass.columns_hash
70
97
  @fix_association = nil
71
98
  @fix_missing_attribute = nil
72
99
  end
100
+
101
+ def mark_removable
102
+ @removable = true
103
+ end
73
104
  end
74
105
  end
75
106
  end
@@ -14,6 +14,7 @@ module AdaptiveAlias
14
14
  def remove!
15
15
  super
16
16
  @klass.remove_alias_attribute(@new_column)
17
+ @klass.define_attribute_method(@new_column)
17
18
  BackwardPatch.new(@klass, @old_column, @new_column).apply!
18
19
  end
19
20
  end
@@ -1,3 +1,3 @@
1
1
  module AdaptiveAlias
2
- VERSION = '0.0.3'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -2,6 +2,7 @@
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'
6
7
  require 'adaptive_alias/patches/backward_patch'
7
8
  require 'adaptive_alias/patches/forward_patch'
@@ -31,27 +32,31 @@ module AdaptiveAlias
31
32
  extend ActiveSupport::Concern
32
33
 
33
34
  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
35
+ patch = (column_names.include?(new_column) ? Patches::BackwardPatch : Patches::ForwardPatch).new(self, old_column, new_column)
36
+ patch.apply!
37
+ patch.mark_removable
39
38
  end
40
39
  end
41
40
  end
42
41
 
43
- def rescue_statement_invalid(relation)
42
+ def rescue_statement_invalid(relation, &block)
44
43
  yield
45
44
  rescue ActiveRecord::StatementInvalid => error
46
45
  raise error if AdaptiveAlias.current_patches.all?{|_key, patch| !patch.fix_association.call(relation, error) }
47
- retry
46
+
47
+ result = rescue_statement_invalid(relation, &block)
48
+ AdaptiveAlias.current_patches.each_value(&:mark_removable)
49
+ return result
48
50
  end
49
51
 
50
- def rescue_missing_attribute
52
+ def rescue_missing_attribute(klass, &block)
51
53
  yield
52
54
  rescue ActiveModel::MissingAttributeError => error
53
- raise error if AdaptiveAlias.current_patches.all?{|_key, patch| !patch.fix_missing_attribute.call }
54
- retry
55
+ raise error if AdaptiveAlias.current_patches.all?{|_key, patch| !patch.fix_missing_attribute.call(klass, error) }
56
+
57
+ result = rescue_missing_attribute(klass, &block)
58
+ AdaptiveAlias.current_patches.each_value(&:mark_removable)
59
+ return result
55
60
  end
56
61
 
57
62
  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.1.0
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-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -125,6 +125,7 @@ files:
125
125
  - lib/adaptive_alias.rb
126
126
  - lib/adaptive_alias/active_model_patches/read_attribute.rb
127
127
  - lib/adaptive_alias/active_model_patches/remove_alias_attribute.rb
128
+ - lib/adaptive_alias/active_model_patches/write_attribute.rb
128
129
  - lib/adaptive_alias/hooks/association.rb
129
130
  - lib/adaptive_alias/hooks/association_scope.rb
130
131
  - lib/adaptive_alias/hooks/relation.rb