strict_ivars 1.0.0.rc3 → 1.0.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: 937599360a03f578b3d440efc2d58eede817bc6ec71727f4ba74c1c0fb11dc0d
4
- data.tar.gz: cb212c70dab8bcf3a9d163f80f864ddf81c8f0a0e3e2ae6781f2bcff37590b88
3
+ metadata.gz: dfeb3ee6871a0bbc8ba42d3b713697b66afc3158894189cc8f1aa52d78acdee2
4
+ data.tar.gz: db9a7c4610f9d2514d29ce3340326a9135b93a5106ad5389733e758c7adac828
5
5
  SHA512:
6
- metadata.gz: 53fdba71a8e909d0afc5b797b1b4b7658a5ab956fab7946e1ead3e59be9298c5928a1b788c2be3781c46df3e2bb28505b5ca911357026aa0992c9f57a45e81e5
7
- data.tar.gz: 86d26b307d4697843d10216e7c783a5db2207945ed3bb7a9e70959dd120e9652249dfef523b2803905728c1e4f4ed1de180a50c472f983068fbb38147367996a
6
+ metadata.gz: 731e53215db39bf6bcd9850eeb521fede63e2acad7b347f14702d921478a53adcc6635350ace7e646bd6f4dbd7416b0e38937dca70cea53624143150536924a3
7
+ data.tar.gz: b40c2da16f5179d9f15af2d39c3c506907dfc04116c66de6f1ffac9454f2fc8b52ec2a834c78fbd2a95ad1e8a36b8911e05e4b2a2270a4453bef0bcffa8b45b4
data/README.md CHANGED
@@ -1,13 +1,19 @@
1
1
  # Strict Ivars
2
2
 
3
- Strict Ivars is a tiny pre-processor for Ruby that guards your instance variable reads, ensuring the instance variable is actually defined. This helps catch typos nice and early. It‘s especially good when used with [Literal](https://literal.fun) and [Phlex](https://www.phlex.fun), though it also works with ERB.
3
+ If you reference an undefined method, constant or local varaible, Ruby will helpfully raise a `NameError`. But reference an undefined _instance_ variable and Ruby just returns `nil`. This can lead to all kinds of bugs many of which can lay dormant for years before surprising you with an unexpected outage, data breach or data loss event.
4
+
5
+ Strict Ivars solves this by making Ruby raise a `NameError` any time you read an undefined instance varaible. It’s enabled with two lines of code in your boot process, then it just works in the background and you’ll never have to think about it again. Strict Ivars has no known false-positives or false-negatives.
6
+
7
+ It‘s especially good when used with [Literal](https://literal.fun) and [Phlex](https://www.phlex.fun), though it also works with regular Ruby objects and even ERB templates, which are actually pretty common spots for undefined instance variable reads to hide since that’s the main way of passing data to ERB.
8
+
9
+ When combined with Literal, you can completely remove unexpected `nil`. Literal validates your inputs and Strict Ivars ensures you’re reading the right instance variables.
4
10
 
5
11
  > [!NOTE]
6
12
  > JRuby and TruffleRuby are not currently supported.
7
13
 
8
14
  ## Setup
9
15
 
10
- Strict Ivars should be used in apps not libraries. Though you could use it in your library’s test suite.
16
+ Strict Ivars should really be used in apps not libraries. Though you could definitely use it in your library’s test suite to help catch issues in the library code.
11
17
 
12
18
  Install the gem by adding it to your `Gemfile` and running `bundle install`. You’ll probably want to set it to `require: false` here because you should require it manually at precisely the right moment.
13
19
 
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StrictIvars::NameError < ::NameError
4
+ INSTANCE_VARIABLE_METHOD = Kernel.instance_method(:instance_variables)
5
+
6
+ def initialize(object, name)
7
+ suggestion = INSTANCE_VARIABLE_METHOD.bind_call(object).max_by do |candidate|
8
+ n_common_trigrams(candidate, name) / Math.sqrt(candidate.length)
9
+ end
10
+
11
+ message = [
12
+ "Undefined instance variable `#{name}`.",
13
+ ("Did you mean `#{suggestion}`?" if suggestion),
14
+ ].join(" ")
15
+
16
+ super(message)
17
+ end
18
+
19
+ private def n_common_trigrams(left, right)
20
+ left = "\x03\x02#{left}"
21
+ right = "\x03\x02#{right}"
22
+
23
+ left_len = left.length
24
+ right_len = right.length
25
+
26
+ return 0 if left_len < 3 || right_len < 3
27
+
28
+ # Process shorter string first
29
+ if left_len > right_len
30
+ left, right = right, left
31
+ left_len, right_len = right_len, left_len
32
+ end
33
+
34
+ # Use a Set for lookup
35
+ trigrams = Set.new
36
+ count = 0
37
+
38
+ # Generate trigrams from shorter string
39
+ i = 0
40
+ left_max = left_len - 2
41
+ while i < left_max
42
+ trigram = left[i, 3]
43
+ trigrams.add(trigram)
44
+ i += 1
45
+ end
46
+
47
+ # Check trigrams from longer string
48
+ i = 0
49
+ right_max = right_len - 2
50
+ while i < right_max
51
+ count += 1 if trigrams.include?(right[i, 3])
52
+ i += 1
53
+ end
54
+
55
+ count
56
+ end
57
+ end
@@ -65,7 +65,7 @@ class StrictIvars::Processor < StrictIvars::BaseProcessor
65
65
 
66
66
  @annotations.push(
67
67
  [location.start_character_offset, "(defined?(#{name}) ? "],
68
- [location.end_character_offset, " : (::Kernel.raise(::StrictIvars::NameError.new('Undefined instance variable #{name}'))))"]
68
+ [location.end_character_offset, " : (::Kernel.raise(::StrictIvars::NameError.new(self, :#{name}))))"]
69
69
  )
70
70
  end
71
71
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StrictIvars
4
- VERSION = "1.0.0.rc3"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/strict_ivars.rb CHANGED
@@ -4,16 +4,15 @@ require "set"
4
4
  require "prism"
5
5
  require "securerandom"
6
6
 
7
- require "require-hooks/setup"
8
-
9
7
  require "strict_ivars/version"
8
+ require "strict_ivars/name_error"
10
9
  require "strict_ivars/base_processor"
11
10
  require "strict_ivars/processor"
12
11
  require "strict_ivars/configuration"
13
12
 
14
- module StrictIvars
15
- NameError = Class.new(::NameError)
13
+ require "require-hooks/setup"
16
14
 
15
+ module StrictIvars
17
16
  EMPTY_ARRAY = [].freeze
18
17
  EVERYTHING = ["**/*"].freeze
19
18
  METHOD_METHOD = Module.instance_method(:method)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strict_ivars
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
@@ -37,7 +37,8 @@ dependencies:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
- description: Raise an exception when using undefined instance variables.
40
+ description: Strict Ivars is a tiny pre-processor that guards you against undefined
41
+ instance variable reads.
41
42
  email:
42
43
  - joel@drapper.me
43
44
  executables: []
@@ -49,14 +50,15 @@ files:
49
50
  - lib/strict_ivars.rb
50
51
  - lib/strict_ivars/base_processor.rb
51
52
  - lib/strict_ivars/configuration.rb
53
+ - lib/strict_ivars/name_error.rb
52
54
  - lib/strict_ivars/processor.rb
53
55
  - lib/strict_ivars/version.rb
54
- homepage: https://github.com/joeldrapper/strict_ivars
56
+ homepage: https://github.com/yippee-fun/strict_ivars
55
57
  licenses:
56
58
  - MIT
57
59
  metadata:
58
- homepage_uri: https://github.com/joeldrapper/strict_ivars
59
- source_code_uri: https://github.com/joeldrapper/strict_ivars
60
+ homepage_uri: https://github.com/yippee-fun/strict_ivars
61
+ source_code_uri: https://github.com/yippee-fun/strict_ivars
60
62
  funding_uri: https://github.com/sponsors/joeldrapper
61
63
  rubygems_mfa_required: 'true'
62
64
  rdoc_options: []
@@ -75,5 +77,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
77
  requirements: []
76
78
  rubygems_version: 3.6.7
77
79
  specification_version: 4
78
- summary: Raise an exception when using undefined instance variables.
80
+ summary: Make Ruby raise a NameError if you read an undefined instance variable.
79
81
  test_files: []