activerecord-originator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fa3749b460f4bf88f4ae707b69db1e2c32f41d7bfac6780ebafbc4e96375a730
4
+ data.tar.gz: b130e6f429f7cd6acc167715a08797f9816f16d06fa990fc1ddda22c4d17700b
5
+ SHA512:
6
+ metadata.gz: 30ce59ca0d48a39793d27a959db9a93dff186006ac83bb4fe220469cde766ac7627c9fe1e0b9f429ecb16e339481f95dbbc39ad7d656d82b1875ef88281f4216
7
+ data.tar.gz: c502f0bf40254b8d92484e4a9fa5e5bdc20eb28a096e4a265fb94616dcca7f3ce4dc6f37e9c2e9a740666f4aeac31e68886fcd50ceb16797c43abd46506480fd
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,21 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.1
3
+ NewCops: enable
4
+
5
+ Style:
6
+ Enabled: false
7
+
8
+ Metrics:
9
+ Enabled: false
10
+
11
+ Naming:
12
+ Enabled: false
13
+
14
+ Layout/LineLength:
15
+ Enabled: false
16
+
17
+ Layout/FirstHashElementIndentation:
18
+ Enabled: false
19
+
20
+ Lint/AssignmentInCondition:
21
+ Enabled: false
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Masataka Pocke Kuwabara
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # ActiveRecord::Originator
2
+
3
+ Add SQL comments to indicate the origin of the SQL.
4
+
5
+ This gem adds SQL comments indicating the origin of the part of the query. This is useful for debugging large queries.
6
+
7
+ Rails tells us where the SQL is executed, but it doesn't tell us where the SQL is constructed.
8
+ This gem lets you know where the SQL is constructed! For example:
9
+
10
+ ```sql
11
+ Article Load (0.1ms) SELECT "articles".* FROM "articles" WHERE "articles"."status" = ? /* app/models/article.rb:3:in `published' */
12
+ AND "articles"."category_id" = ? /* app/controllers/articles_controller.rb:3:in `index' */
13
+ ORDER BY "articles"."created_at" DESC /* app/models/article.rb:4:in `order_by_latest' */
14
+ ```
15
+
16
+ You can see where `.where` and `.order` methods are called without reading the source code. It is helpful if the query builder is complex.
17
+
18
+ ## Installation
19
+
20
+ Install the gem and add to the application's Gemfile by executing:
21
+
22
+ ```console
23
+ $ bundle add activerecord-originator
24
+ ```
25
+
26
+ You do not need to do anything else. The gem will automatically add comments to the SQL.
27
+ Check the logs to see the comments.
28
+
29
+ ## Development
30
+
31
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
32
+
33
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
34
+
35
+ ## Contributing
36
+
37
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pocke/activerecord-originator.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task :steep do
13
+ sh 'steep', 'check'
14
+ end
15
+
16
+ task default: %i[rubocop steep spec]
data/Steepfile ADDED
@@ -0,0 +1,7 @@
1
+ D = Steep::Diagnostic
2
+
3
+ target :lib do
4
+ signature "sig"
5
+ check "lib"
6
+ configure_code_diagnostics(D::Ruby.strict)
7
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Originator
5
+ module ArelNodeExtension
6
+ def initialize(...)
7
+ __skip__ = super
8
+ @ar_originator_backtrace = caller
9
+ end
10
+
11
+ attr_reader :ar_originator_backtrace
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Originator
5
+ module ArelVisitorExtension
6
+ def accept(object, collector)
7
+ super(object, CollectorProxy.new(collector))
8
+ end
9
+
10
+ private
11
+
12
+ %i[
13
+ Ascending
14
+ Descending
15
+ Equality
16
+ NotEqual
17
+ InnerJoin
18
+ HomogeneousIn
19
+ GreaterThanOrEqual
20
+ GreaterThan
21
+ LessThan
22
+ LessThanOrEqual
23
+ GreaterThanOrEqual
24
+ ].each do |klass_name|
25
+ define_method(:"visit_Arel_Nodes_#{klass_name}") do |o, collector|
26
+ __skip__ = begin
27
+ comment = originator_comment(o)
28
+ res = super(o, collector)
29
+ if comment && !collector.last_str.end_with?(comment)
30
+ res << comment if comment && !collector.last_str.end_with?(comment)
31
+ end
32
+ res
33
+ end
34
+ end
35
+ end
36
+
37
+ def originator_comment(o)
38
+ traces = o.ar_originator_backtrace
39
+ if c = Originator.backtrace_cleaner
40
+ traces = c.clean(traces)
41
+ end
42
+ frame = traces.first
43
+ return unless frame
44
+
45
+ " /* #{escape_comment(frame)} */\n"
46
+ end
47
+
48
+ def escape_comment(comment)
49
+ while comment.include?('/*') || comment.include?('*/')
50
+ comment = comment.gsub('/*', '').gsub('*/', '')
51
+ end
52
+ comment
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Originator
5
+ # This class is a proxy to deduplicate the originator comment.
6
+ class CollectorProxy
7
+ attr_accessor :preparable
8
+
9
+ def initialize(collector)
10
+ @collector = collector
11
+ @last_str = nil
12
+ end
13
+
14
+ attr_reader :last_str
15
+
16
+ def <<(str)
17
+ @collector << str
18
+ @last_str = str
19
+ self
20
+ end
21
+
22
+ def add_bind(...)
23
+ @collector.add_bind(...)
24
+ self
25
+ end
26
+
27
+ def add_binds(...)
28
+ @collector.add_binds(...)
29
+ self
30
+ end
31
+
32
+ def value
33
+ @collector.value
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/lazy_load_hooks'
4
+
5
+ ActiveSupport.on_load(:active_record) do
6
+ ActiveRecord::Originator.backtrace_cleaner = Rails.backtrace_cleaner
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Originator
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record'
4
+ require 'active_support/core_ext/module/attribute_accessors'
5
+ require 'active_support/core_ext/class/subclasses'
6
+
7
+ require_relative "originator/version"
8
+
9
+ require_relative "originator/arel_node_extension"
10
+ require_relative "originator/arel_visitor_extension"
11
+ require_relative "originator/collector_proxy"
12
+
13
+ Arel::Nodes::Node.descendants.each do |klass|
14
+ klass.prepend ActiveRecord::Originator::ArelNodeExtension
15
+ end
16
+ Arel::Visitors::ToSql.prepend ActiveRecord::Originator::ArelVisitorExtension
17
+
18
+ module ActiveRecord
19
+ module Originator
20
+ mattr_accessor :backtrace_cleaner
21
+ end
22
+ end
23
+
24
+ require_relative "originator/railtie" if defined?(Rails)
@@ -0,0 +1,100 @@
1
+ ---
2
+ path: ".gem_rbs_collection"
3
+ gems:
4
+ - name: activemodel
5
+ version: '7.0'
6
+ source:
7
+ type: git
8
+ name: ruby/gem_rbs_collection
9
+ revision: 372c833bb52be1bc34aef5e2793ab47c35c0dba9
10
+ remote: https://github.com/ruby/gem_rbs_collection.git
11
+ repo_dir: gems
12
+ - name: activerecord
13
+ version: '7.0'
14
+ source:
15
+ type: git
16
+ name: ruby/gem_rbs_collection
17
+ revision: 372c833bb52be1bc34aef5e2793ab47c35c0dba9
18
+ remote: https://github.com/ruby/gem_rbs_collection.git
19
+ repo_dir: gems
20
+ - name: activesupport
21
+ version: '7.0'
22
+ source:
23
+ type: git
24
+ name: ruby/gem_rbs_collection
25
+ revision: 372c833bb52be1bc34aef5e2793ab47c35c0dba9
26
+ remote: https://github.com/ruby/gem_rbs_collection.git
27
+ repo_dir: gems
28
+ - name: base64
29
+ version: '0'
30
+ source:
31
+ type: stdlib
32
+ - name: bigdecimal
33
+ version: '0'
34
+ source:
35
+ type: stdlib
36
+ - name: concurrent-ruby
37
+ version: '1.1'
38
+ source:
39
+ type: git
40
+ name: ruby/gem_rbs_collection
41
+ revision: 372c833bb52be1bc34aef5e2793ab47c35c0dba9
42
+ remote: https://github.com/ruby/gem_rbs_collection.git
43
+ repo_dir: gems
44
+ - name: connection_pool
45
+ version: '2.4'
46
+ source:
47
+ type: git
48
+ name: ruby/gem_rbs_collection
49
+ revision: 372c833bb52be1bc34aef5e2793ab47c35c0dba9
50
+ remote: https://github.com/ruby/gem_rbs_collection.git
51
+ repo_dir: gems
52
+ - name: date
53
+ version: '0'
54
+ source:
55
+ type: stdlib
56
+ - name: erb
57
+ version: '0'
58
+ source:
59
+ type: stdlib
60
+ - name: i18n
61
+ version: '1.10'
62
+ source:
63
+ type: git
64
+ name: ruby/gem_rbs_collection
65
+ revision: 372c833bb52be1bc34aef5e2793ab47c35c0dba9
66
+ remote: https://github.com/ruby/gem_rbs_collection.git
67
+ repo_dir: gems
68
+ - name: logger
69
+ version: '0'
70
+ source:
71
+ type: stdlib
72
+ - name: minitest
73
+ version: '0'
74
+ source:
75
+ type: stdlib
76
+ - name: monitor
77
+ version: '0'
78
+ source:
79
+ type: stdlib
80
+ - name: mutex_m
81
+ version: '0'
82
+ source:
83
+ type: stdlib
84
+ - name: securerandom
85
+ version: '0'
86
+ source:
87
+ type: stdlib
88
+ - name: singleton
89
+ version: '0'
90
+ source:
91
+ type: stdlib
92
+ - name: time
93
+ version: '0'
94
+ source:
95
+ type: stdlib
96
+ - name: timeout
97
+ version: '0'
98
+ source:
99
+ type: stdlib
100
+ gemfile_lock_path: Gemfile.lock
@@ -0,0 +1,13 @@
1
+ sources:
2
+ - type: git
3
+ name: ruby/gem_rbs_collection
4
+ remote: https://github.com/ruby/gem_rbs_collection.git
5
+ revision: main
6
+ repo_dir: gems
7
+
8
+ path: .gem_rbs_collection
9
+
10
+ gems:
11
+ - name: activerecord-originator
12
+ ignore: true
13
+ - name: activerecord
@@ -0,0 +1,9 @@
1
+ module ActiveRecord
2
+ module Originator
3
+ module ArelNodeExtension : Arel::Nodes::Node
4
+ @ar_originator_backtrace: Array[String]
5
+
6
+ def initialize: () -> void
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module Originator
3
+ module ArelVisitorExtension : _ArelVisitor
4
+ def accept: (untyped object, _Collector collector) -> untyped
5
+
6
+ private
7
+
8
+ def originator_comment: (untyped o) -> String?
9
+
10
+ def escape_comment: (String comment) -> String
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveRecord
2
+ module Originator
3
+ # This class is a proxy to deduplicate the originator comment.
4
+ class CollectorProxy
5
+ @collector: untyped
6
+
7
+ @last_str: String | nil
8
+
9
+ def initialize: (untyped collector) -> void
10
+
11
+ def <<: (String str) -> self
12
+
13
+ def add_bind: (*untyped, **untyped) -> self
14
+
15
+ def add_binds: (*untyped, **untyped) -> self
16
+
17
+ def value: () -> untyped
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module Originator
3
+ VERSION: String
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module Originator
3
+ attr_accessor self.backtrace_cleaner: ActiveSupport::BacktraceCleaner?
4
+ end
5
+ end
data/sig/polyfill.rbs ADDED
@@ -0,0 +1,16 @@
1
+ module Rails
2
+ def self.backtrace_cleaner: () -> untyped
3
+ end
4
+
5
+ module ActiveRecord::Originator
6
+ interface _ArelVisitor
7
+ def accept: (untyped, untyped) -> untyped
8
+ end
9
+
10
+ interface _Collector
11
+ def <<: (String) -> self
12
+ def add_bind: (untyped obj) -> self
13
+ def add_binds: (untyped, ?untyped) -> self
14
+ def value: () -> untyped
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-originator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Masataka Pocke Kuwabara
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-03-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
41
+ description: This gem adds comments to the SQL to indicate where the SQL parts are
42
+ constructed.
43
+ email:
44
+ - kuwabara@pocke.me
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".rspec"
50
+ - ".rubocop.yml"
51
+ - LICENSE
52
+ - README.md
53
+ - Rakefile
54
+ - Steepfile
55
+ - lib/activerecord/originator.rb
56
+ - lib/activerecord/originator/arel_node_extension.rb
57
+ - lib/activerecord/originator/arel_visitor_extension.rb
58
+ - lib/activerecord/originator/collector_proxy.rb
59
+ - lib/activerecord/originator/railtie.rb
60
+ - lib/activerecord/originator/version.rb
61
+ - rbs_collection.lock.yaml
62
+ - rbs_collection.yaml
63
+ - sig/activerecord/originator.rbs
64
+ - sig/activerecord/originator/arel_node_extension.rbs
65
+ - sig/activerecord/originator/arel_visitor_extension.rbs
66
+ - sig/activerecord/originator/collector_proxy.rbs
67
+ - sig/activerecord/originator/version.rbs
68
+ - sig/polyfill.rbs
69
+ homepage: https://github.com/pocke/activerecord-originator
70
+ licenses:
71
+ - MIT
72
+ metadata:
73
+ homepage_uri: https://github.com/pocke/activerecord-originator
74
+ source_code_uri: https://github.com/pocke/activerecord-originator
75
+ changelog_uri: https://github.com/pocke/activerecord-originator/blob/main/CHANGELOG.md
76
+ rubygems_mfa_required: 'true'
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: 3.1.0
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubygems_version: 3.6.0.dev
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Add SQL comments to indicate the origin of the SQL.
96
+ test_files: []