arproxy 0.2.9 → 1.0.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: 97417dad77bf18a6a058b5a7bf30cfd2118e70a2db8da4eb9574f5d6694b1119
4
- data.tar.gz: 76361afc976b7fd086c702fb7dd0629b3d0ad85776667b1438d323db64173139
3
+ metadata.gz: e079e98bb0b0f67bb3bc2189c4ee95b27e7177b4db0e9ba52ac746b8cc492366
4
+ data.tar.gz: e6e388e344e42d79aca1a40eddb3ee4e2b08f55a2e470ee34bc75ac97ec8963c
5
5
  SHA512:
6
- metadata.gz: 0515356f2ddafdb1f77bbb6dfb49769fbc4d08d1381bf2657f32210d3dd9841f3e29d5ca157f6e24e23392d219f0123dc703c0e1c34e083ee06ed58d3f09ec32
7
- data.tar.gz: 95ce473dc91f7d5a38009eb105b35b3f8760f31f8fbb85b9fe45c66151864c98120585f6476363dc395f3a1d66a3d3433e4d2355c420b36dff4e392464c9c51d
6
+ metadata.gz: 97cba7c8a4a525a072ddc48bc89425b53a98f6f2f3445f4e3733a9b97feb3f83fc303536ce0962a23891b1281e6feb949e85ce36a90e209d2c62f3e614319c4b
7
+ data.tar.gz: dff9dc34f6349c4fd61aa9cbaa786b81a11d648c26d3905a600221b27a8235933158ecae199c44036de498bcae648bc976807f666d93c584b6aa48b63bd1227f
data/lib/arproxy/base.rb CHANGED
@@ -1,9 +1,5 @@
1
1
  module Arproxy
2
+ # This class is no longer used since Arproxy v1.
2
3
  class Base
3
- attr_accessor :proxy_chain, :next_proxy
4
-
5
- def execute(sql, name=nil, **kwargs)
6
- next_proxy.execute sql, name, **kwargs
7
- end
8
4
  end
9
5
  end
@@ -1,5 +1,7 @@
1
- require "active_record"
2
- require "active_record/base"
1
+ require 'active_record'
2
+ require 'active_record/base'
3
+ require 'arproxy/base'
4
+ require 'arproxy/error'
3
5
 
4
6
  module Arproxy
5
7
  class Config
@@ -9,22 +11,31 @@ module Arproxy
9
11
  def initialize
10
12
  @proxies = []
11
13
  if defined?(Rails)
12
- @adapter = Rails.application.config_for(:database)["adapter"]
14
+ @adapter = Rails.application.config_for(:database)['adapter']
13
15
  end
14
16
  end
15
17
 
16
18
  def use(proxy_class, *options)
19
+ if proxy_class.is_a?(Class) && proxy_class.ancestors.include?(Arproxy::Base)
20
+ raise Arproxy::Error, "Error on loading a proxy `#{proxy_class.inspect}`: the superclass `Arproxy::Base` is no longer supported since Arproxy v1. Use `Arproxy::Proxy` instead. See: https://github.com/cookpad/arproxy/blob/main/UPGRADING.md"
21
+ end
22
+
17
23
  ::Arproxy.logger.debug("Arproxy: Mounting #{proxy_class.inspect} (#{options.inspect})")
18
24
  @proxies << [proxy_class, options]
19
25
  end
20
26
 
21
27
  def plugin(name, *options)
22
28
  plugin_class = Plugin.get(name)
29
+
30
+ if plugin_class.is_a?(Class) && plugin_class.ancestors.include?(Arproxy::Base)
31
+ raise Arproxy::Error, "Error on loading a plugin `#{plugin_class.inspect}`: the superclass `Arproxy::Base` is no longer supported since Arproxy v1. Use `Arproxy::Proxy` instead. See: https://github.com/cookpad/arproxy/blob/main/UPGRADING.md"
32
+ end
33
+
23
34
  use(plugin_class, *options)
24
35
  end
25
36
 
26
37
  def adapter_class
27
- raise Arproxy::Error, "config.adapter must be set" unless @adapter
38
+ raise Arproxy::Error, 'config.adapter must be set' unless @adapter
28
39
  case @adapter
29
40
  when String, Symbol
30
41
  eval "::ActiveRecord::ConnectionAdapters::#{camelized_adapter_name}Adapter"
@@ -37,19 +48,19 @@ module Arproxy
37
48
 
38
49
  private
39
50
 
40
- def camelized_adapter_name
41
- adapter_name = @adapter.to_s.split("_").map(&:capitalize).join
51
+ def camelized_adapter_name
52
+ adapter_name = @adapter.to_s.split('_').map(&:capitalize).join
42
53
 
43
- case adapter_name
44
- when 'Sqlite3'
45
- 'SQLite3'
46
- when 'Sqlserver'
47
- 'SQLServer'
48
- when 'Postgresql'
49
- 'PostgreSQL'
50
- else
51
- adapter_name
54
+ case adapter_name
55
+ when 'Sqlite3'
56
+ 'SQLite3'
57
+ when 'Sqlserver'
58
+ 'SQLServer'
59
+ when 'Postgresql'
60
+ 'PostgreSQL'
61
+ else
62
+ adapter_name
63
+ end
52
64
  end
53
- end
54
65
  end
55
66
  end
@@ -0,0 +1,118 @@
1
+ module Arproxy
2
+ class ConnectionAdapterPatch
3
+ attr_reader :adapter_class
4
+
5
+ def initialize(adapter_class)
6
+ @adapter_class = adapter_class
7
+ @applied_patches = Set.new
8
+ end
9
+
10
+ def self.register_patches(adapter_name, patches: [], binds_patches: [])
11
+ @@patches ||= {}
12
+ @@patches[adapter_name] = {
13
+ patches: patches,
14
+ binds_patches: binds_patches
15
+ }
16
+ end
17
+
18
+ if ActiveRecord.version >= Gem::Version.new('8.0')
19
+ register_patches('Mysql2', patches: [], binds_patches: [:raw_execute])
20
+ register_patches('Trilogy', patches: [], binds_patches: [:raw_execute])
21
+ elsif ActiveRecord.version >= Gem::Version.new('7.0')
22
+ register_patches('Mysql2', patches: [:raw_execute], binds_patches: [])
23
+ register_patches('Trilogy', patches: [:raw_execute], binds_patches: [])
24
+ else
25
+ register_patches('Mysql2', patches: [:execute], binds_patches: [])
26
+ register_patches('Trilogy', patches: [:raw_execute], binds_patches: [])
27
+ end
28
+
29
+ if ActiveRecord.version >= Gem::Version.new('8.0')
30
+ register_patches('PostgreSQL', patches: [], binds_patches: [:raw_execute])
31
+ register_patches('SQLServer', patches: [], binds_patches: [:raw_execute])
32
+ register_patches('SQLite', patches: [], binds_patches: [:raw_execute])
33
+ elsif ActiveRecord.version >= Gem::Version.new('7.1')
34
+ register_patches('PostgreSQL', patches: [:raw_execute], binds_patches: [:exec_no_cache, :exec_cache])
35
+ register_patches('SQLServer', patches: [:raw_execute], binds_patches: [:internal_exec_query])
36
+ register_patches('SQLite', patches: [:raw_execute], binds_patches: [:internal_exec_query])
37
+ else
38
+ register_patches('PostgreSQL', patches: [:execute], binds_patches: [:exec_no_cache, :exec_cache])
39
+ register_patches('SQLServer', patches: [:execute], binds_patches: [:exec_query])
40
+ register_patches('SQLite', patches: [:execute], binds_patches: [:exec_query])
41
+ end
42
+
43
+ def enable!
44
+ patches = @@patches[adapter_class::ADAPTER_NAME]
45
+ if patches
46
+ patches[:patches]&.each do |patch|
47
+ apply_patch patch
48
+ end
49
+ patches[:binds_patches]&.each do |binds_patch|
50
+ apply_patch_binds binds_patch
51
+ end
52
+ else
53
+ raise Arproxy::Error, "Unexpected connection adapter: patches not registered for #{adapter_class&.name}"
54
+ end
55
+ ::Arproxy.logger.debug("Arproxy: Enabled (#{adapter_class::ADAPTER_NAME})")
56
+ end
57
+
58
+ def disable!
59
+ @applied_patches.dup.each do |target_method|
60
+ adapter_class.class_eval do
61
+ if instance_methods.include?(:"#{target_method}_with_arproxy")
62
+ alias_method target_method, :"#{target_method}_without_arproxy"
63
+ remove_method :"#{target_method}_with_arproxy"
64
+ end
65
+ end
66
+ @applied_patches.delete(target_method)
67
+ end
68
+ ::Arproxy.logger.debug("Arproxy: Disabled (#{adapter_class::ADAPTER_NAME})")
69
+ end
70
+
71
+ private
72
+
73
+ def apply_patch(target_method)
74
+ return if @applied_patches.include?(target_method)
75
+ adapter_class.class_eval do
76
+ raw_execute_method_name = :"#{target_method}_without_arproxy"
77
+ patched_execute_method_name = :"#{target_method}_with_arproxy"
78
+ break if instance_methods.include?(patched_execute_method_name)
79
+ define_method(patched_execute_method_name) do |sql, name=nil, **kwargs|
80
+ context = QueryContext.new(
81
+ raw_connection: self,
82
+ execute_method_name: raw_execute_method_name,
83
+ with_binds: false,
84
+ name: name,
85
+ kwargs: kwargs,
86
+ )
87
+ ::Arproxy.proxy_chain.head.execute(sql, context)
88
+ end
89
+ alias_method raw_execute_method_name, target_method
90
+ alias_method target_method, patched_execute_method_name
91
+ end
92
+ @applied_patches << target_method
93
+ end
94
+
95
+ def apply_patch_binds(target_method)
96
+ return if @applied_patches.include?(target_method)
97
+ adapter_class.class_eval do
98
+ raw_execute_method_name = :"#{target_method}_without_arproxy"
99
+ patched_execute_method_name = :"#{target_method}_with_arproxy"
100
+ break if instance_methods.include?(patched_execute_method_name)
101
+ define_method(patched_execute_method_name) do |sql, name=nil, binds=[], **kwargs|
102
+ context = QueryContext.new(
103
+ raw_connection: self,
104
+ execute_method_name: raw_execute_method_name,
105
+ with_binds: true,
106
+ name: name,
107
+ binds: binds,
108
+ kwargs: kwargs,
109
+ )
110
+ ::Arproxy.proxy_chain.head.execute(sql, context)
111
+ end
112
+ alias_method raw_execute_method_name, target_method
113
+ alias_method target_method, patched_execute_method_name
114
+ end
115
+ @applied_patches << target_method
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,15 @@
1
+ require 'arproxy/query_context'
2
+
3
+ module Arproxy
4
+ class Proxy
5
+ attr_accessor :context, :next_proxy
6
+
7
+ def execute(sql, context)
8
+ unless context.instance_of?(QueryContext)
9
+ raise Arproxy::Error, "`context` is expected a `Arproxy::QueryContext` but got `#{context.class}`"
10
+ end
11
+
12
+ next_proxy.execute(sql, context)
13
+ end
14
+ end
15
+ end
@@ -1,20 +1,21 @@
1
- module Arproxy
2
- autoload :ChainTail, "arproxy/chain_tail"
1
+ require 'arproxy/proxy_chain_tail'
2
+ require 'arproxy/connection_adapter_patch'
3
3
 
4
+ module Arproxy
4
5
  class ProxyChain
5
- attr_reader :head, :tail
6
+ attr_reader :head, :tail, :patch
6
7
 
7
- def initialize(config)
8
+ def initialize(config, patch)
8
9
  @config = config
10
+ @patch = patch
9
11
  setup
10
12
  end
11
13
 
12
14
  def setup
13
- @tail = ChainTail.new self
15
+ @tail = ProxyChainTail.new
14
16
  @head = @config.proxies.reverse.inject(@tail) do |next_proxy, proxy_config|
15
17
  cls, options = proxy_config
16
18
  proxy = cls.new(*options)
17
- proxy.proxy_chain = self
18
19
  proxy.next_proxy = next_proxy
19
20
  proxy
20
21
  end
@@ -28,31 +29,11 @@ module Arproxy
28
29
  end
29
30
 
30
31
  def enable!
31
- @config.adapter_class.class_eval do
32
- def execute_with_arproxy(sql, name=nil, **kwargs)
33
- ::Arproxy.proxy_chain.connection = self
34
- ::Arproxy.proxy_chain.head.execute sql, name, **kwargs
35
- end
36
- alias_method :execute_without_arproxy, :execute
37
- alias_method :execute, :execute_with_arproxy
38
- ::Arproxy.logger.debug("Arproxy: Enabled")
39
- end
32
+ @patch.enable!
40
33
  end
41
34
 
42
35
  def disable!
43
- @config.adapter_class.class_eval do
44
- alias_method :execute, :execute_without_arproxy
45
- ::Arproxy.logger.debug("Arproxy: Disabled")
46
- end
36
+ @patch.disable!
47
37
  end
48
-
49
- def connection
50
- Thread.current[:arproxy_connection]
51
- end
52
-
53
- def connection=(val)
54
- Thread.current[:arproxy_connection] = val
55
- end
56
-
57
38
  end
58
39
  end
@@ -0,0 +1,18 @@
1
+ require 'arproxy/proxy'
2
+ require 'arproxy/query_context'
3
+
4
+ module Arproxy
5
+ class ProxyChainTail < Proxy
6
+ def execute(sql, context)
7
+ unless context.instance_of?(QueryContext)
8
+ raise Arproxy::Error, "`context` is expected a `Arproxy::QueryContext` but got `#{context.class}`"
9
+ end
10
+
11
+ if context.with_binds?
12
+ context.raw_connection.send(context.execute_method_name, sql, context.name, context.binds, **context.kwargs)
13
+ else
14
+ context.raw_connection.send(context.execute_method_name, sql, context.name, **context.kwargs)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Arproxy
2
+ class QueryContext
3
+ attr_accessor :raw_connection, :execute_method_name, :with_binds, :name, :binds, :kwargs
4
+
5
+ def initialize(raw_connection:, execute_method_name:, with_binds:, name: nil, binds: [], kwargs: {})
6
+ @raw_connection = raw_connection
7
+ @execute_method_name = execute_method_name
8
+ @with_binds = with_binds
9
+ @name = name
10
+ @binds = binds
11
+ @kwargs = kwargs
12
+ end
13
+
14
+ def with_binds?
15
+ !!@with_binds
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,3 @@
1
1
  module Arproxy
2
- VERSION = '0.2.9'
2
+ VERSION = '1.0.0'
3
3
  end
data/lib/arproxy.rb CHANGED
@@ -1,73 +1,81 @@
1
- require "logger"
2
- require "arproxy/base"
3
- require "arproxy/config"
4
- require "arproxy/proxy_chain"
5
- require "arproxy/error"
6
- require "arproxy/plugin"
1
+ require 'logger'
2
+ require 'arproxy/base'
3
+ require 'arproxy/config'
4
+ require 'arproxy/connection_adapter_patch'
5
+ require 'arproxy/proxy_chain'
6
+ require 'arproxy/error'
7
+ require 'arproxy/plugin'
7
8
 
8
9
  module Arproxy
9
- @config = @enabled = nil
10
+ @config = nil
11
+ @enabled = nil
12
+ @patch = nil
10
13
 
11
14
  module_function
12
- def clear_configuration
13
- @config = Config.new
14
- end
15
-
16
- def configure
17
- clear_configuration unless @config
18
- yield @config
19
- end
20
-
21
- def enable!
22
- if enable?
23
- Arproxy.logger.warn "Arproxy has been already enabled"
24
- return
15
+
16
+ def clear_configuration
17
+ @config = nil
25
18
  end
26
19
 
27
- unless @config
28
- raise Arproxy::Error, "Arproxy should be configured"
20
+ def configure
21
+ @config ||= Config.new
22
+ yield @config
29
23
  end
30
24
 
31
- @proxy_chain = ProxyChain.new @config
32
- @proxy_chain.enable!
25
+ def enable!
26
+ if enable?
27
+ Arproxy.logger.warn 'Arproxy has already been enabled'
28
+ return
29
+ end
30
+
31
+ unless @config
32
+ raise Arproxy::Error, 'Arproxy has not been configured'
33
+ end
33
34
 
34
- @enabled = true
35
- end
35
+ @patch = ConnectionAdapterPatch.new(@config.adapter_class)
36
+ @proxy_chain = ProxyChain.new(@config, @patch)
37
+ @proxy_chain.enable!
36
38
 
37
- def disable!
38
- unless enable?
39
- Arproxy.logger.warn "Arproxy is not enabled yet"
40
- return
39
+ @enabled = true
41
40
  end
42
41
 
43
- if @proxy_chain
44
- @proxy_chain.disable!
45
- @proxy_chain = nil
42
+ def disable!
43
+ unless enable?
44
+ Arproxy.logger.warn 'Arproxy is not enabled yet'
45
+ return
46
+ end
47
+
48
+ if @proxy_chain
49
+ @proxy_chain.disable!
50
+ @proxy_chain = nil
51
+ end
52
+
53
+ @enabled = false
46
54
  end
47
- @enabled = false
48
- end
49
-
50
- def enable?
51
- !!@enabled
52
- end
53
-
54
- def reenable!
55
- if enable?
56
- @proxy_chain.reenable!
57
- else
58
- enable!
55
+
56
+ def enable?
57
+ !!@enabled
58
+ end
59
+
60
+ def reenable!
61
+ if enable?
62
+ @proxy_chain.reenable!
63
+ else
64
+ enable!
65
+ end
66
+ end
67
+
68
+ def logger
69
+ @logger ||= @config && @config.logger ||
70
+ defined?(::Rails) && ::Rails.logger ||
71
+ ::Logger.new(STDOUT)
72
+ end
73
+
74
+ def proxy_chain
75
+ @proxy_chain
76
+ end
77
+
78
+ def connection_adapter_patch
79
+ @patch
59
80
  end
60
- end
61
-
62
- def logger
63
- @logger ||= begin
64
- @config && @config.logger ||
65
- defined?(::Rails) && ::Rails.logger ||
66
- ::Logger.new(STDOUT)
67
- end
68
- end
69
-
70
- def proxy_chain
71
- @proxy_chain
72
- end
73
81
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arproxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Issei Naruta
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-18 00:00:00.000000000 Z
11
+ date: 2025-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,83 +16,31 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.2.0
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 4.2.0
27
- - !ruby/object:Gem::Dependency
28
- name: bundler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: rake
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: 12.3.3
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: 12.3.3
55
- - !ruby/object:Gem::Dependency
56
- name: rspec
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '3.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '3.0'
69
- - !ruby/object:Gem::Dependency
70
- name: appraisal
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '2.1'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '2.1'
83
- description: Arproxy is a proxy between ActiveRecord and database adapter
84
- email: naruta@cookpad.com
26
+ version: '6.1'
27
+ description: Arproxy is a proxy layer that allows hooking into ActiveRecord query
28
+ execution and injecting custom processing
29
+ email: mimitako@gmail.com
85
30
  executables: []
86
31
  extensions: []
87
32
  extra_rdoc_files: []
88
33
  files:
89
34
  - lib/arproxy.rb
90
35
  - lib/arproxy/base.rb
91
- - lib/arproxy/chain_tail.rb
92
36
  - lib/arproxy/config.rb
37
+ - lib/arproxy/connection_adapter_patch.rb
93
38
  - lib/arproxy/error.rb
94
39
  - lib/arproxy/plugin.rb
40
+ - lib/arproxy/proxy.rb
95
41
  - lib/arproxy/proxy_chain.rb
42
+ - lib/arproxy/proxy_chain_tail.rb
43
+ - lib/arproxy/query_context.rb
96
44
  - lib/arproxy/version.rb
97
45
  homepage: https://github.com/cookpad/arproxy
98
46
  licenses:
@@ -113,8 +61,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
61
  - !ruby/object:Gem::Version
114
62
  version: '0'
115
63
  requirements: []
116
- rubygems_version: 3.2.22
64
+ rubygems_version: 3.5.11
117
65
  signing_key:
118
66
  specification_version: 4
119
- summary: Proxy between ActiveRecord and DB adapter
67
+ summary: A proxy layer between ActiveRecord and database adapters
120
68
  test_files: []
@@ -1,11 +0,0 @@
1
- module Arproxy
2
- class ChainTail < Base
3
- def initialize(proxy_chain)
4
- self.proxy_chain = proxy_chain
5
- end
6
-
7
- def execute(sql, name=nil, **kwargs)
8
- self.proxy_chain.connection.execute_without_arproxy sql, name, **kwargs
9
- end
10
- end
11
- end