ivar 0.2.0 → 0.4.6
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/.augment-guidelines +5 -3
- data/.devcontainer/devcontainer.json +28 -20
- data/.devcontainer/post-create.sh +18 -0
- data/.editorconfig +35 -0
- data/.rubocop.yml +6 -0
- data/.standard.yml +1 -1
- data/.vscode/extensions.json +3 -1
- data/.vscode/launch.json +25 -0
- data/.vscode/settings.json +38 -2
- data/CHANGELOG.md +99 -1
- data/README.md +272 -207
- data/Rakefile +1 -1
- data/VERSION.md +46 -0
- data/examples/check_all_block_example.rb +84 -0
- data/examples/check_all_example.rb +42 -0
- data/examples/inheritance_with_kwarg_init.rb +156 -0
- data/examples/inheritance_with_positional_init.rb +142 -0
- data/examples/mixed_positional_and_kwarg_init.rb +125 -0
- data/examples/require_check_all_example.rb +23 -0
- data/examples/sandwich_inheritance.rb +1 -1
- data/examples/sandwich_with_accessors.rb +78 -0
- data/examples/sandwich_with_block_values.rb +54 -0
- data/examples/sandwich_with_checked.rb +0 -1
- data/examples/sandwich_with_checked_once.rb +0 -1
- data/examples/sandwich_with_initial_values.rb +52 -0
- data/examples/sandwich_with_ivar_block.rb +6 -9
- data/examples/sandwich_with_ivar_macro.rb +4 -4
- data/examples/sandwich_with_kwarg_init.rb +78 -0
- data/examples/sandwich_with_positional_init.rb +50 -0
- data/examples/sandwich_with_shared_values.rb +54 -0
- data/hooks/README.md +42 -0
- data/hooks/install.sh +12 -0
- data/hooks/pre-commit +54 -0
- data/lib/ivar/check_all.rb +7 -0
- data/lib/ivar/check_all_manager.rb +72 -0
- data/lib/ivar/check_policy.rb +29 -0
- data/lib/ivar/checked/class_methods.rb +19 -0
- data/lib/ivar/checked/instance_methods.rb +35 -0
- data/lib/ivar/checked.rb +17 -24
- data/lib/ivar/declaration.rb +30 -0
- data/lib/ivar/explicit_declaration.rb +56 -0
- data/lib/ivar/explicit_keyword_declaration.rb +24 -0
- data/lib/ivar/explicit_positional_declaration.rb +19 -0
- data/lib/ivar/macros.rb +48 -111
- data/lib/ivar/manifest.rb +124 -0
- data/lib/ivar/policies.rb +13 -1
- data/lib/ivar/project_root.rb +59 -0
- data/lib/ivar/targeted_prism_analysis.rb +144 -0
- data/lib/ivar/validation.rb +6 -29
- data/lib/ivar/version.rb +1 -1
- data/lib/ivar.rb +141 -9
- data/script/console +11 -0
- data/script/de-lint +2 -0
- data/script/de-lint-unsafe +2 -0
- data/script/lint +2 -0
- data/script/release +213 -0
- data/script/setup +8 -0
- data/script/test +2 -0
- metadata +46 -8
- data/examples/sandwich_with_kwarg.rb +0 -45
- data/ivar.gemspec +0 -49
- data/lib/ivar/auto_check.rb +0 -77
- data/lib/ivar/prism_analysis.rb +0 -102
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ivar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Avdi Grimm
|
8
|
+
autorequire:
|
8
9
|
bindir: bin
|
9
10
|
cert_chain: []
|
10
|
-
date:
|
11
|
+
date: 2025-05-07 00:00:00.000000000 Z
|
11
12
|
dependencies:
|
12
13
|
- !ruby/object:Gem::Dependency
|
13
14
|
name: prism
|
@@ -45,29 +46,64 @@ files:
|
|
45
46
|
- ".augment-guidelines"
|
46
47
|
- ".devcontainer/Dockerfile"
|
47
48
|
- ".devcontainer/devcontainer.json"
|
49
|
+
- ".devcontainer/post-create.sh"
|
50
|
+
- ".editorconfig"
|
51
|
+
- ".rubocop.yml"
|
48
52
|
- ".standard.yml"
|
49
53
|
- ".vscode/extensions.json"
|
54
|
+
- ".vscode/launch.json"
|
50
55
|
- ".vscode/settings.json"
|
51
56
|
- CHANGELOG.md
|
52
57
|
- CODE_OF_CONDUCT.md
|
53
58
|
- LICENSE.txt
|
54
59
|
- README.md
|
55
60
|
- Rakefile
|
61
|
+
- VERSION.md
|
62
|
+
- examples/check_all_block_example.rb
|
63
|
+
- examples/check_all_example.rb
|
64
|
+
- examples/inheritance_with_kwarg_init.rb
|
65
|
+
- examples/inheritance_with_positional_init.rb
|
66
|
+
- examples/mixed_positional_and_kwarg_init.rb
|
67
|
+
- examples/require_check_all_example.rb
|
56
68
|
- examples/sandwich_inheritance.rb
|
69
|
+
- examples/sandwich_with_accessors.rb
|
70
|
+
- examples/sandwich_with_block_values.rb
|
57
71
|
- examples/sandwich_with_checked.rb
|
58
72
|
- examples/sandwich_with_checked_once.rb
|
73
|
+
- examples/sandwich_with_initial_values.rb
|
59
74
|
- examples/sandwich_with_ivar_block.rb
|
60
75
|
- examples/sandwich_with_ivar_macro.rb
|
61
|
-
- examples/
|
62
|
-
-
|
76
|
+
- examples/sandwich_with_kwarg_init.rb
|
77
|
+
- examples/sandwich_with_positional_init.rb
|
78
|
+
- examples/sandwich_with_shared_values.rb
|
79
|
+
- hooks/README.md
|
80
|
+
- hooks/install.sh
|
81
|
+
- hooks/pre-commit
|
63
82
|
- lib/ivar.rb
|
64
|
-
- lib/ivar/
|
83
|
+
- lib/ivar/check_all.rb
|
84
|
+
- lib/ivar/check_all_manager.rb
|
85
|
+
- lib/ivar/check_policy.rb
|
65
86
|
- lib/ivar/checked.rb
|
87
|
+
- lib/ivar/checked/class_methods.rb
|
88
|
+
- lib/ivar/checked/instance_methods.rb
|
89
|
+
- lib/ivar/declaration.rb
|
90
|
+
- lib/ivar/explicit_declaration.rb
|
91
|
+
- lib/ivar/explicit_keyword_declaration.rb
|
92
|
+
- lib/ivar/explicit_positional_declaration.rb
|
66
93
|
- lib/ivar/macros.rb
|
94
|
+
- lib/ivar/manifest.rb
|
67
95
|
- lib/ivar/policies.rb
|
68
|
-
- lib/ivar/
|
96
|
+
- lib/ivar/project_root.rb
|
97
|
+
- lib/ivar/targeted_prism_analysis.rb
|
69
98
|
- lib/ivar/validation.rb
|
70
99
|
- lib/ivar/version.rb
|
100
|
+
- script/console
|
101
|
+
- script/de-lint
|
102
|
+
- script/de-lint-unsafe
|
103
|
+
- script/lint
|
104
|
+
- script/release
|
105
|
+
- script/setup
|
106
|
+
- script/test
|
71
107
|
- sig/ivar.rbs
|
72
108
|
- test_file.rb
|
73
109
|
homepage: https://github.com/avdi/ivar
|
@@ -78,6 +114,7 @@ metadata:
|
|
78
114
|
homepage_uri: https://github.com/avdi/ivar
|
79
115
|
source_code_uri: https://github.com/avdi/ivar
|
80
116
|
changelog_uri: https://github.com/avdi/ivar/blob/main/CHANGELOG.md
|
117
|
+
post_install_message:
|
81
118
|
rdoc_options: []
|
82
119
|
require_paths:
|
83
120
|
- lib
|
@@ -85,14 +122,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
122
|
requirements:
|
86
123
|
- - ">="
|
87
124
|
- !ruby/object:Gem::Version
|
88
|
-
version: 3.
|
125
|
+
version: 3.3.0
|
89
126
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
127
|
requirements:
|
91
128
|
- - ">="
|
92
129
|
- !ruby/object:Gem::Version
|
93
130
|
version: '0'
|
94
131
|
requirements: []
|
95
|
-
rubygems_version: 3.
|
132
|
+
rubygems_version: 3.5.3
|
133
|
+
signing_key:
|
96
134
|
specification_version: 4
|
97
135
|
summary: Automatically check instance variables for typos.
|
98
136
|
test_files: []
|
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "ivar"
|
4
|
-
|
5
|
-
class SandwichWithKwarg
|
6
|
-
include Ivar::Checked
|
7
|
-
|
8
|
-
# Pre-declare instance variables to be initialized from keyword arguments
|
9
|
-
ivar kwarg: [:@bread, :@cheese, :@condiments]
|
10
|
-
|
11
|
-
def initialize(pickles: false, side: nil)
|
12
|
-
# Note: @bread, @cheese, and @condiments are already set from keyword arguments
|
13
|
-
# We only need to handle the remaining keyword arguments
|
14
|
-
@pickles = pickles
|
15
|
-
@side = side
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_s
|
19
|
-
result = "A #{@bread} sandwich with #{@cheese}"
|
20
|
-
result += " and #{@condiments.join(", ")}" unless @condiments.empty?
|
21
|
-
result += " with pickles" if @pickles
|
22
|
-
result += " and a side of #{@side}" if @side
|
23
|
-
result
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Create a sandwich with keyword arguments
|
28
|
-
sandwich = SandwichWithKwarg.new(
|
29
|
-
bread: "wheat",
|
30
|
-
cheese: "muenster",
|
31
|
-
condiments: ["mayo", "mustard"],
|
32
|
-
side: "chips"
|
33
|
-
)
|
34
|
-
|
35
|
-
puts sandwich # Outputs: A wheat sandwich with muenster and mayo, mustard and a side of chips
|
36
|
-
|
37
|
-
# Create another sandwich with different keyword arguments
|
38
|
-
sandwich2 = SandwichWithKwarg.new(
|
39
|
-
bread: "rye",
|
40
|
-
cheese: "swiss",
|
41
|
-
condiments: ["mustard"],
|
42
|
-
pickles: true
|
43
|
-
)
|
44
|
-
|
45
|
-
puts sandwich2 # Outputs: A rye sandwich with swiss and mustard with pickles
|
data/ivar.gemspec
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "lib/ivar/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = "ivar"
|
7
|
-
spec.version = Ivar::VERSION
|
8
|
-
spec.authors = ["Avdi Grimm"]
|
9
|
-
spec.email = ["avdi@avdi.codes"]
|
10
|
-
|
11
|
-
spec.summary = "Automatically check instance variables for typos."
|
12
|
-
spec.description = <<~EOF
|
13
|
-
Ruby instance variables are so convenient - you don't even need to declare them!
|
14
|
-
But... they are also dangerous, because a mispelled variable name results in `nil`
|
15
|
-
instead of an error.
|
16
|
-
|
17
|
-
Why not have the best of both worlds? Ivar lets you use plain-old instance variables,
|
18
|
-
and automatically checks for typos.
|
19
|
-
|
20
|
-
Ivar waits until an instance is created to do the checking, then uses Prism to look
|
21
|
-
for variables that don't match what was set in initialization. So it's a little bit
|
22
|
-
dynamic, a little bit static. It doesn't encumber your instance variable reads and
|
23
|
-
writes with any extra checking. And with the `:warn_once` policy, it won't overwhelm
|
24
|
-
you with output.
|
25
|
-
EOF
|
26
|
-
|
27
|
-
spec.homepage = "https://github.com/avdi/ivar"
|
28
|
-
spec.license = "MIT"
|
29
|
-
spec.required_ruby_version = ">= 3.4.0"
|
30
|
-
|
31
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
32
|
-
|
33
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
34
|
-
spec.metadata["source_code_uri"] = "https://github.com/avdi/ivar"
|
35
|
-
spec.metadata["changelog_uri"] = "https://github.com/avdi/ivar/blob/main/CHANGELOG.md"
|
36
|
-
|
37
|
-
# Specify which files should be added to the gem when it is released.
|
38
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
39
|
-
spec.files = Dir.chdir(__dir__) do
|
40
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
41
|
-
(File.expand_path(f) == __FILE__) ||
|
42
|
-
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
43
|
-
end
|
44
|
-
end
|
45
|
-
spec.require_paths = ["lib"]
|
46
|
-
|
47
|
-
# Dependencies
|
48
|
-
spec.add_dependency "prism", "~> 1.2"
|
49
|
-
end
|
data/lib/ivar/auto_check.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "validation"
|
4
|
-
require_relative "macros"
|
5
|
-
|
6
|
-
module Ivar
|
7
|
-
# Module for adding ivar_check_policy to classes
|
8
|
-
module CheckPolicy
|
9
|
-
# Set the check policy for this class
|
10
|
-
# @param policy [Symbol, Policy] The check policy
|
11
|
-
# @return [Symbol, Policy] The check policy
|
12
|
-
def ivar_check_policy(policy = nil, **options)
|
13
|
-
if policy.nil?
|
14
|
-
# Getter - return the current policy
|
15
|
-
@__ivar_check_policy || Ivar.check_policy
|
16
|
-
else
|
17
|
-
# Setter - set the policy
|
18
|
-
@__ivar_check_policy = options.empty? ? policy : [policy, options]
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Hook method called when the module is included
|
23
|
-
def inherited(subclass)
|
24
|
-
super
|
25
|
-
# Copy the check policy to the subclass
|
26
|
-
subclass.instance_variable_set(:@__ivar_check_policy, @__ivar_check_policy)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# Provides automatic validation for instance variables
|
31
|
-
# When included, automatically calls check_ivars after initialization
|
32
|
-
module Checked
|
33
|
-
# When this module is included in a class, it extends the class
|
34
|
-
# with ClassMethods and includes the Validation module
|
35
|
-
def self.included(base)
|
36
|
-
base.include(Validation)
|
37
|
-
base.include(PreInitializeIvars)
|
38
|
-
base.extend(ClassMethods)
|
39
|
-
base.extend(CheckPolicy)
|
40
|
-
base.extend(Macros)
|
41
|
-
base.prepend(InstanceMethods)
|
42
|
-
|
43
|
-
# Set default policy for Checked to :warn
|
44
|
-
base.ivar_check_policy(:warn)
|
45
|
-
end
|
46
|
-
|
47
|
-
# Class methods added to the including class
|
48
|
-
module ClassMethods
|
49
|
-
# Hook method called when the module is included
|
50
|
-
def inherited(subclass)
|
51
|
-
super
|
52
|
-
# Ensure subclasses also get the initialize wrapper
|
53
|
-
subclass.prepend(Ivar::Checked::InstanceMethods)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# Instance methods that will be prepended to the including class
|
58
|
-
module InstanceMethods
|
59
|
-
# Wrap the initialize method to automatically call check_ivars
|
60
|
-
def initialize(*args, **kwargs, &block)
|
61
|
-
# Initialize pre-declared instance variables
|
62
|
-
initialize_pre_declared_ivars
|
63
|
-
# Execute the initialization block if provided
|
64
|
-
execute_ivar_init_block
|
65
|
-
|
66
|
-
# Process keyword arguments
|
67
|
-
remaining_kwargs = initialize_from_kwargs(kwargs)
|
68
|
-
|
69
|
-
# Call the original initialize method with remaining arguments
|
70
|
-
super(*args, **remaining_kwargs, &block)
|
71
|
-
|
72
|
-
# Automatically check instance variables
|
73
|
-
check_ivars
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
data/lib/ivar/prism_analysis.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "prism"
|
4
|
-
|
5
|
-
module Ivar
|
6
|
-
# Analyzes a class to find all instance variables using Prism
|
7
|
-
class PrismAnalysis
|
8
|
-
attr_reader :ivars
|
9
|
-
|
10
|
-
def initialize(klass)
|
11
|
-
@klass = klass
|
12
|
-
@references = nil
|
13
|
-
collect_references
|
14
|
-
@ivars = unique_ivar_names
|
15
|
-
end
|
16
|
-
|
17
|
-
# Returns a list of hashes each representing a code reference to an ivar
|
18
|
-
# Each hash includes var name, path, line number, and column number
|
19
|
-
def ivar_references
|
20
|
-
@references
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def collect_references
|
26
|
-
source_files = collect_source_files
|
27
|
-
@references = []
|
28
|
-
|
29
|
-
source_files.each do |file_path|
|
30
|
-
code = File.read(file_path)
|
31
|
-
result = Prism.parse(code)
|
32
|
-
visitor = IvarReferenceVisitor.new(file_path)
|
33
|
-
result.value.accept(visitor)
|
34
|
-
@references.concat(visitor.references)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def unique_ivar_names
|
39
|
-
@references.map { |ref| ref[:name] }.uniq.sort
|
40
|
-
end
|
41
|
-
|
42
|
-
def collect_source_files
|
43
|
-
# Get all instance methods
|
44
|
-
instance_methods = @klass.instance_methods(false) | @klass.private_instance_methods(false)
|
45
|
-
|
46
|
-
# Collect source files for all methods
|
47
|
-
source_files = Set.new
|
48
|
-
instance_methods.each do |method_name|
|
49
|
-
next unless @klass.instance_method(method_name).source_location
|
50
|
-
|
51
|
-
source_files << @klass.instance_method(method_name).source_location.first
|
52
|
-
end
|
53
|
-
|
54
|
-
source_files
|
55
|
-
end
|
56
|
-
|
57
|
-
# Visitor that collects instance variable references with location information
|
58
|
-
class IvarReferenceVisitor < Prism::Visitor
|
59
|
-
attr_reader :references
|
60
|
-
|
61
|
-
def initialize(file_path)
|
62
|
-
super()
|
63
|
-
@file_path = file_path
|
64
|
-
@references = []
|
65
|
-
end
|
66
|
-
|
67
|
-
def visit_instance_variable_read_node(node)
|
68
|
-
add_reference(node)
|
69
|
-
true
|
70
|
-
end
|
71
|
-
|
72
|
-
def visit_instance_variable_write_node(node)
|
73
|
-
add_reference(node)
|
74
|
-
true
|
75
|
-
end
|
76
|
-
|
77
|
-
def visit_instance_variable_operator_write_node(node)
|
78
|
-
add_reference(node)
|
79
|
-
true
|
80
|
-
end
|
81
|
-
|
82
|
-
def visit_instance_variable_target_node(node)
|
83
|
-
add_reference(node)
|
84
|
-
true
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def add_reference(node)
|
90
|
-
location = node.location
|
91
|
-
reference = {
|
92
|
-
name: node.name.to_sym,
|
93
|
-
path: @file_path,
|
94
|
-
line: location.start_line,
|
95
|
-
column: location.start_column
|
96
|
-
}
|
97
|
-
|
98
|
-
@references << reference
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|