deprecool 0.1.0 → 0.1.2

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: 8979f5deb74f90a5e9e6061319b79a1d920c6153bf3a32d378293514c6f5c547
4
- data.tar.gz: 7a17ed36ecfb88aab21b1032a1fa74ae2165e8126826bae9cff2a341c5ae47a7
3
+ metadata.gz: 959b5cdb23ec0d5eef80f87185fb7a31a5646cf991cced0030d4d839fb835906
4
+ data.tar.gz: 87eac39b80c35af2e3bc868ab99b03d534f46f06e0f6ef6945381e5d00e8798b
5
5
  SHA512:
6
- metadata.gz: d61efb1f6c1a4b36570040aa91dbf20b856fa63b8564390732e3ef14de6ee704f899d6aace163c338db6bec7511f2d189e158d5819f80b75a9002709a1214c4c
7
- data.tar.gz: 62ada454736be50a5ff349562d2500d8266ecbe23829cd5a078446abd768fb7677e21e676201da08c4b6f9e1e6329afa0a5a3e3c4b2891c0355777de10166bd4
6
+ metadata.gz: d6da4e681409697beeaad714e669ad6854000a66a9086a8bf593ded61016e3741beb5147d616a42d99c3e19b3d51ad886f432d056ba58db2e56a559b0188ae87
7
+ data.tar.gz: cd41a34a6787fb74f7985e9180893d46a765a19d369b90c09d23738a378ee645bce9d02109fda4e7806195f82bc94f9b0d2ac300ec6c69bf6d0ccb65c9e41e9a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Deprecool
2
2
 
3
- ## [0.1.0] - 2026-08-13
3
+ ## [0.1.2] - 2026-08-14
4
+
5
+ - Add Finder for Rails's deprecation of `write_attribute(:id, value)`
6
+
7
+ ## [0.1.0], [0.1.1] - 2026-08-13
4
8
 
5
9
  - Initial Release
data/README.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  A static ruby code analyzer to find your deprecated code!
4
4
 
5
- For any developer who has ever ignored a gem's warning and then ran into an error when they went to update
5
+ A helpful tool for any developer who has ever ignored a gem's warning and then ran into an error when they went to update
6
6
 
7
7
  ## Usage
8
8
 
9
- Add to your gemfile: `gem 'deprecool'`
9
+ Add to your Gemfile: `gem 'deprecool'`, and `bundle install`
10
+
10
11
  Or directly `gem install deprecool`
11
12
 
12
13
  Then you can run `deprecool` in the command line to get:
@@ -24,12 +25,39 @@ Commands:
24
25
 
25
26
  - `--gem` takes a comma separated list of gems to scan for:
26
27
 
28
+ _(make sure there are no spaces between the gem names)_
29
+
27
30
  ```
28
31
  deprecool scan --gem=rails,ruby
29
32
  ```
30
- _(make sure there are no spaces between the gem names)_
31
33
 
32
34
  - `--format=json` if you want the output as json instead of the default text
33
35
  - `--lockfile` takes a path to your `Gemfile.lock`, it defaults to using the current directory
34
36
  - `--all` run every finder regardless of applicability
35
37
  - `--paths` an array of the files or directories to look for ruby file in, defaults to `'.'`
38
+
39
+ ## Contributing
40
+
41
+ This project has an incredibly large scope and I can't do it alone!
42
+
43
+ If you see a deprecation warning in your code that isn't reflected here please either create an issue or make a PR
44
+
45
+ Deprecool is designed to be able to support new deprecation warnings very easily.
46
+
47
+ After cloning the repo, simply add two files:
48
+
49
+ ```
50
+ lib/deprecool/finders/< gem name >/< gem version>/<short_name_of_warning>.rb
51
+
52
+ test/deprecool/finders/< gem name >/< gem version>/<short_name_of_warning>_test.rb
53
+ ```
54
+
55
+ Or, run the `exe/contribute` script and it will prompt you for:
56
+ - the gem name
57
+ - the version the deprecation first appears in
58
+ - a short name for the warning's finder class
59
+
60
+ _(if the script doesn't work, run `chmod +x exe/contribute` and try again)_
61
+
62
+ Then use `rake` to run the test suite.
63
+
data/lib/deprecool/cli.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'json'
4
4
  require 'dry/cli'
5
- require 'debug'
6
5
 
7
6
  module Deprecool
8
7
  # Command-line entry point. Scans the given files/directories and reports
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # requiring this here so we don't have to remember to add
4
- # it in individual finders
5
3
  require_relative 'helpers/prism_helpers'
6
4
 
7
5
  module Deprecool
@@ -106,7 +104,6 @@ module Deprecool
106
104
  # Prism::VisitClassNode => on_class_node
107
105
  # Prism::VisitDefNode => on_def_node
108
106
  # Prism::VisitModuleNode => on_module_node
109
- # Prism::
110
107
  def hook_methods
111
108
  instance_methods(false).grep(/\Aon_\w+_node\z/)
112
109
  end
@@ -116,7 +113,7 @@ module Deprecool
116
113
 
117
114
  def initialize(file_path, source, offenses)
118
115
  @file_path = file_path
119
- @source = source # the result of Prism.parse
116
+ @source = source # the AST from Prism.parse
120
117
  @offenses = offenses
121
118
  end
122
119
 
@@ -25,11 +25,10 @@ module Deprecool
25
25
 
26
26
  def on_call_node(node)
27
27
  return unless node.name == :serialize
28
+ return unless arguments_are_length(node.arguments, 2)
29
+ return unless unwrap_arguments(node.arguments)[1].is_a?(Prism::ConstantReadNode)
28
30
 
29
- arguments_array = unwrap_arguments(node.arguments)
30
- return unless arguments_array && arguments_array[1]
31
-
32
- add_offense(node, confidence: :high) if arguments_array[1].is_a?(Prism::ConstantReadNode)
31
+ add_offense(node, confidence: :high)
33
32
  end
34
33
  end
35
34
  end
@@ -7,7 +7,7 @@ module Deprecool
7
7
  class ToSqlBinds < Deprecool::Finder
8
8
  gem :rails
9
9
  deprecated_in '8.2.0'
10
- removed_in '8.3.0'
10
+ removed_in '9.0.0'
11
11
  title 'Passing "binds" into "to_sql" is deprecated'
12
12
  summary 'Since Rails 5.2, bind parameters live on the Arel AST ' \
13
13
  'to_sql_and_binds no longer uses binds for SQL construction, ' \
@@ -26,9 +26,7 @@ module Deprecool
26
26
  # if a method called 'to_sql' is passed two arguments and the second is an
27
27
  # array that's probably good enough
28
28
  return unless node.name == :to_sql
29
-
30
- arguments_array = unwrap_arguments(node.arguments)
31
- return unless arguments_array && arguments_array[1]
29
+ return unless arguments_are_length(node.arguments, 2)
32
30
 
33
31
  add_offense(node, confidence: :high)
34
32
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deprecool
4
+ module Finders
5
+ module Rails
6
+ module V8_2_0
7
+ class WriteAttributeId < Deprecool::Finder
8
+ gem :rails
9
+ deprecated_in '8.2.0'
10
+ removed_in '9.0.0'
11
+ title 'Using "write_attribute" for the primary key is deprecated'
12
+ summary 'Just like Rails 7.1\'s deprecation of calling "read_attribute(:id)", '\
13
+ '"write_attribute(:id)" is now deprecated and will be removed in the next version of Rails. ' \
14
+ 'making :id refer only to the id column, not the primary key.'
15
+ suggestion 'Use "#id=" on a model whose primary key is not named "id"'
16
+ reference 'Original Rails Commit: https://github.com/rails/rails/commit/1a90632bbe62dae2626de7a058bc18c29f77aea6'
17
+ effort :low
18
+
19
+ def on_call_node(node)
20
+ return unless node.name == :write_attribute
21
+ return unless arguments_are_length(node.arguments, 2)
22
+ return unless arguments_contain(node.arguments, "id", position: 0)
23
+
24
+ add_offense(node, confidence: :high)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -36,4 +36,28 @@ module PrismHelpers
36
36
 
37
37
  node.child_nodes
38
38
  end
39
+
40
+ def arguments_are_length(node, length)
41
+ return false unless node.is_a?(Prism::ArgumentsNode)
42
+
43
+ unwrap_arguments(node.arguments).length == length
44
+ end
45
+
46
+ def arguments_contain(node, value, position: -1, kwarg: nil)
47
+ return false unless node.is_a?(Prism::ArgumentsNode)
48
+
49
+ arguments = unwrap_arguments(node)
50
+
51
+ return (value_from_argument(arguments[position]) == value) if position >= 0
52
+
53
+ arguments.any? do |arg|
54
+ value_from_argument(arg) == value
55
+ end
56
+ end
57
+
58
+ def value_from_argument(node)
59
+ case node
60
+ when Prism::SymbolNode, Prism::StringNode then node.unescaped
61
+ end
62
+ end
39
63
  end
@@ -1,12 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deprecool
4
- # Selects which finders should run for a given application, based on the gems
5
- # (and Ruby) it actually uses and at what versions.
4
+ # Selects which finders should run for a given application
6
5
  module Registry
7
6
  extend self
8
7
 
9
- # Every registered finder.
8
+ # All the subclasses of Finder
10
9
  def all_finders
11
10
  Finder.registry
12
11
  end
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deprecool
4
- # Parses a source file with Prism and runs a set of finders against it in a
5
- # single AST traversal.
4
+ # Parses a source file with Prism and runs a set of finders against it
6
5
  class Scanner
7
6
  def initialize(finders)
8
7
  @finders = Array(finders)
@@ -12,8 +11,8 @@ module Deprecool
12
11
  scan_source(File.read(path), path)
13
12
  end
14
13
 
15
- # Returns an Array<Offense>. Files that fail to parse yield no offenses;
16
- # parse errors are surfaced separately via {#parse_errors}.
14
+ # pass Prism's AST over to the dispatcher to gather offending
15
+ # snippets of code into an array
17
16
  def scan_source(source, path = '(source)')
18
17
  result = Prism.parse(source)
19
18
  return [] unless result.success?
@@ -25,36 +24,39 @@ module Deprecool
25
24
  # and offenses are only needed for the add_offense method, not the actual
26
25
  # searching for an offense?
27
26
  instances = @finders.map { |finder| finder.new(path, source, offenses) }
28
- DispatchVisitor.new(instances).visit(result.value)
27
+ FinderDispatcher.new(instances).visit(result.value)
29
28
  offenses
30
29
  end
31
30
 
32
- # Combines the given Finder classes into one class by
31
+ # Combines the given Finder instances into one class by
33
32
  # defining one method per Prism::Visitor hook (e.g. visit_call_node)
34
33
  # based on all the given finders that define a method that matches that
35
34
  # hook method name
36
- class DispatchVisitor < Prism::Visitor
37
- # finder_instances is the array passed to the Scanner.new class,
38
- # so this would be [Ruby::V4_0_0::ToSetArguments, ..]
35
+ #
36
+ # This pattern makes it so that the Finders can remain as simple as
37
+ # possible while passing off their functionality info this class
38
+ class FinderDispatcher < Prism::Visitor
39
+ # finder_instances is the array passed to the Scanner instance,
40
+ # i.e [Ruby::V4_0_0::ToSetArguments.new, ...]
39
41
  def initialize(finder_instances)
40
42
  super()
41
43
 
42
44
  dispatch = Hash.new { |hash, key| hash[key] = [] }
43
45
  finder_instances.each do |instance|
44
46
  # find the hooks and add the instances to the hash of hook methods
45
- # so { on_call_node: [ToSetArguments.new, ObjectSpaceId2ref.new,.. ], ... }
47
+ # so { on_call_node: [ToSetArguments.new, ObjectSpaceId2ref.new, ... ], ... }
46
48
  instance.class.hook_methods.each { |hook| dispatch[hook] << instance }
47
49
  end
48
50
 
49
51
  dispatch.each do |hook, finders|
50
- # convert the instances' hooks to what prism::visitor expects
51
- # so finder classes must use this pattern to name the visit_node methods
52
+ # convert the instances' hooks to what Prism::Visitor expects.
53
+ # Finder classes must use this pattern to name their visit_*_node methods
52
54
  #
53
55
  # 'on_call_node' => good
54
56
  # 'find_call_node' => bad
55
57
  visit_method = hook.to_s.sub(/\Aon_/, 'visit_').to_sym
56
58
 
57
- # define the prism::visitor hook to loop through each of the
59
+ # define the Prism::Visitor hook method to loop through each of the
58
60
  # related finders and call the name of the hook like so:
59
61
  #
60
62
  # def visit_call_node(node)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deprecool
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.2'
5
5
  end
6
6
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deprecool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zac Radford
@@ -58,6 +58,7 @@ files:
58
58
  - lib/deprecool/finders/rails/v7.1.0/serializer_positional_class_argument.rb
59
59
  - lib/deprecool/finders/rails/v8.2.0/redis_cache_store_default_redis_options.rb
60
60
  - lib/deprecool/finders/rails/v8.2.0/to_sql_binds.rb
61
+ - lib/deprecool/finders/rails/v8.2.0/write_attribute_id.rb
61
62
  - lib/deprecool/finders/ruby/v4.0.0/object_space_id2ref.rb
62
63
  - lib/deprecool/finders/ruby/v4.0.0/to_set_arguments.rb
63
64
  - lib/deprecool/helpers/prism_helpers.rb