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 +4 -4
- data/README.md +8 -2
- data/lib/strict_ivars/name_error.rb +57 -0
- data/lib/strict_ivars/processor.rb +1 -1
- data/lib/strict_ivars/version.rb +1 -1
- data/lib/strict_ivars.rb +3 -4
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfeb3ee6871a0bbc8ba42d3b713697b66afc3158894189cc8f1aa52d78acdee2
|
4
|
+
data.tar.gz: db9a7c4610f9d2514d29ce3340326a9135b93a5106ad5389733e758c7adac828
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 731e53215db39bf6bcd9850eeb521fede63e2acad7b347f14702d921478a53adcc6635350ace7e646bd6f4dbd7416b0e38937dca70cea53624143150536924a3
|
7
|
+
data.tar.gz: b40c2da16f5179d9f15af2d39c3c506907dfc04116c66de6f1ffac9454f2fc8b52ec2a834c78fbd2a95ad1e8a36b8911e05e4b2a2270a4453bef0bcffa8b45b4
|
data/README.md
CHANGED
@@ -1,13 +1,19 @@
|
|
1
1
|
# Strict Ivars
|
2
2
|
|
3
|
-
|
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(
|
68
|
+
[location.end_character_offset, " : (::Kernel.raise(::StrictIvars::NameError.new(self, :#{name}))))"]
|
69
69
|
)
|
70
70
|
end
|
71
71
|
|
data/lib/strict_ivars/version.rb
CHANGED
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
|
-
|
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
|
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:
|
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/
|
56
|
+
homepage: https://github.com/yippee-fun/strict_ivars
|
55
57
|
licenses:
|
56
58
|
- MIT
|
57
59
|
metadata:
|
58
|
-
homepage_uri: https://github.com/
|
59
|
-
source_code_uri: https://github.com/
|
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:
|
80
|
+
summary: Make Ruby raise a NameError if you read an undefined instance variable.
|
79
81
|
test_files: []
|