adaptive_alias 0.0.3 → 0.1.0

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