ivar 0.2.0 → 0.4.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.augment-guidelines +5 -3
  3. data/.devcontainer/devcontainer.json +28 -20
  4. data/.devcontainer/post-create.sh +18 -0
  5. data/.editorconfig +35 -0
  6. data/.rubocop.yml +6 -0
  7. data/.standard.yml +1 -1
  8. data/.vscode/extensions.json +3 -1
  9. data/.vscode/launch.json +25 -0
  10. data/.vscode/settings.json +38 -2
  11. data/CHANGELOG.md +83 -1
  12. data/README.md +272 -207
  13. data/Rakefile +1 -1
  14. data/VERSION.md +44 -0
  15. data/examples/check_all_block_example.rb +84 -0
  16. data/examples/check_all_example.rb +42 -0
  17. data/examples/inheritance_with_kwarg_init.rb +156 -0
  18. data/examples/inheritance_with_positional_init.rb +142 -0
  19. data/examples/mixed_positional_and_kwarg_init.rb +125 -0
  20. data/examples/require_check_all_example.rb +23 -0
  21. data/examples/sandwich_inheritance.rb +1 -1
  22. data/examples/sandwich_with_accessors.rb +78 -0
  23. data/examples/sandwich_with_block_values.rb +54 -0
  24. data/examples/sandwich_with_checked.rb +0 -1
  25. data/examples/sandwich_with_checked_once.rb +0 -1
  26. data/examples/sandwich_with_initial_values.rb +52 -0
  27. data/examples/sandwich_with_ivar_block.rb +6 -9
  28. data/examples/sandwich_with_ivar_macro.rb +4 -4
  29. data/examples/sandwich_with_kwarg_init.rb +78 -0
  30. data/examples/sandwich_with_positional_init.rb +50 -0
  31. data/examples/sandwich_with_shared_values.rb +54 -0
  32. data/hooks/README.md +42 -0
  33. data/hooks/install.sh +12 -0
  34. data/hooks/pre-commit +54 -0
  35. data/ivar.gemspec +5 -4
  36. data/lib/ivar/check_all.rb +7 -0
  37. data/lib/ivar/check_all_manager.rb +72 -0
  38. data/lib/ivar/check_policy.rb +29 -0
  39. data/lib/ivar/checked/class_methods.rb +19 -0
  40. data/lib/ivar/checked/instance_methods.rb +35 -0
  41. data/lib/ivar/checked.rb +17 -24
  42. data/lib/ivar/declaration.rb +30 -0
  43. data/lib/ivar/explicit_declaration.rb +56 -0
  44. data/lib/ivar/explicit_keyword_declaration.rb +24 -0
  45. data/lib/ivar/explicit_positional_declaration.rb +19 -0
  46. data/lib/ivar/macros.rb +48 -111
  47. data/lib/ivar/manifest.rb +124 -0
  48. data/lib/ivar/policies.rb +13 -1
  49. data/lib/ivar/project_root.rb +59 -0
  50. data/lib/ivar/targeted_prism_analysis.rb +144 -0
  51. data/lib/ivar/validation.rb +6 -29
  52. data/lib/ivar/version.rb +1 -1
  53. data/lib/ivar.rb +141 -9
  54. data/script/console +11 -0
  55. data/script/de-lint +2 -0
  56. data/script/de-lint-unsafe +2 -0
  57. data/script/lint +2 -0
  58. data/script/release +134 -0
  59. data/script/setup +8 -0
  60. data/script/test +2 -0
  61. metadata +41 -5
  62. data/examples/sandwich_with_kwarg.rb +0 -45
  63. data/lib/ivar/auto_check.rb +0 -77
  64. data/lib/ivar/prism_analysis.rb +0 -102
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ module Ivar
6
+ # Analyzes a class to find instance variable references in specific instance methods
7
+ # Unlike PrismAnalysis, this targets only the class's own methods (not inherited)
8
+ # and precisely locates instance variable references within each method definition
9
+ class TargetedPrismAnalysis
10
+ attr_reader :ivars, :references
11
+
12
+ def initialize(klass)
13
+ @klass = klass
14
+ @references = []
15
+ @method_locations = {}
16
+ collect_method_locations
17
+ analyze_methods
18
+ @ivars = unique_ivar_names
19
+ end
20
+
21
+ # Returns a list of hashes each representing a code reference to an ivar
22
+ # Each hash includes var name, path, line number, and column number
23
+ def ivar_references
24
+ @references
25
+ end
26
+
27
+ private
28
+
29
+ def unique_ivar_names
30
+ @references.map { |ref| ref[:name] }.uniq.sort
31
+ end
32
+
33
+ def collect_method_locations
34
+ # Get all instance methods defined directly on this class (not inherited)
35
+ instance_methods = @klass.instance_methods(false) | @klass.private_instance_methods(false)
36
+ instance_methods.each do |method_name|
37
+ # Try to get the method from the stash first, then fall back to the current method
38
+ method_obj = Ivar.get_stashed_method(@klass, method_name) || @klass.instance_method(method_name)
39
+ next unless method_obj.source_location
40
+
41
+ file_path, line_number = method_obj.source_location
42
+ @method_locations[method_name] = {path: file_path, line: line_number}
43
+ end
44
+ end
45
+
46
+ def analyze_methods
47
+ # Group methods by file to avoid parsing the same file multiple times
48
+ methods_by_file = @method_locations.group_by { |_, location| location[:path] }
49
+
50
+ methods_by_file.each do |file_path, methods_in_file|
51
+ code = File.read(file_path)
52
+ result = Prism.parse(code)
53
+
54
+ methods_in_file.each do |method_name, location|
55
+ visitor = MethodTargetedInstanceVariableReferenceVisitor.new(
56
+ file_path,
57
+ method_name,
58
+ location[:line]
59
+ )
60
+
61
+ result.value.accept(visitor)
62
+ @references.concat(visitor.references)
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ # Visitor that collects instance variable references within a specific method definition
69
+ class MethodTargetedInstanceVariableReferenceVisitor < Prism::Visitor
70
+ attr_reader :references
71
+
72
+ def initialize(file_path, target_method_name, target_line)
73
+ super()
74
+ @file_path = file_path
75
+ @target_method_name = target_method_name
76
+ @target_line = target_line
77
+ @references = []
78
+ @in_target_method = false
79
+ end
80
+
81
+ # Only visit the method definition we're targeting
82
+ def visit_def_node(node)
83
+ # Check if this is our target method
84
+ if node.name.to_sym == @target_method_name && node.location.start_line == @target_line
85
+ # Found our target method, now collect all instance variable references within it
86
+ collector = IvarCollector.new(@file_path, @target_method_name)
87
+ node.body&.accept(collector)
88
+ @references = collector.references
89
+ false
90
+ else
91
+ # Sometimes methods are found inside other methods...
92
+ node.body&.accept(self)
93
+ true
94
+ end
95
+ end
96
+ end
97
+
98
+ # Helper visitor that collects all instance variable references
99
+ class IvarCollector < Prism::Visitor
100
+ attr_reader :references
101
+
102
+ def initialize(file_path, method_name)
103
+ super()
104
+ @file_path = file_path
105
+ @method_name = method_name
106
+ @references = []
107
+ end
108
+
109
+ def visit_instance_variable_read_node(node)
110
+ add_reference(node)
111
+ true
112
+ end
113
+
114
+ def visit_instance_variable_write_node(node)
115
+ add_reference(node)
116
+ true
117
+ end
118
+
119
+ def visit_instance_variable_operator_write_node(node)
120
+ add_reference(node)
121
+ true
122
+ end
123
+
124
+ def visit_instance_variable_target_node(node)
125
+ add_reference(node)
126
+ true
127
+ end
128
+
129
+ private
130
+
131
+ def add_reference(node)
132
+ location = node.location
133
+ reference = {
134
+ name: node.name.to_sym,
135
+ path: @file_path,
136
+ line: location.start_line,
137
+ column: location.start_column,
138
+ method: @method_name
139
+ }
140
+
141
+ @references << reference
142
+ end
143
+ end
144
+ end
@@ -9,46 +9,23 @@ module Ivar
9
9
  # @param add [Array<Symbol>] Additional instance variables to allow
10
10
  # @param policy [Symbol, Policy] The policy to use for handling unknown variables
11
11
  def check_ivars(add: [], policy: nil)
12
- # Get the policy to use
13
12
  policy ||= get_check_policy
14
-
15
- # Get the class analysis from the cache
16
- analysis = Ivar.get_analysis(self.class)
17
-
18
- # Get all instance variables defined in the current object
19
- # These are the ones the user has explicitly defined before calling check_ivars
20
- defined_ivars = instance_variables.map(&:to_sym)
21
-
22
- # Add any additional allowed variables
23
- allowed_ivars = defined_ivars + add
24
-
25
- # Get all instance variable references from the analysis
26
- # This includes location information for each reference
27
- references = analysis.ivar_references
28
-
29
- # Find references to unknown variables (those not in allowed_ivars)
30
- unknown_refs = references.reject { |ref| allowed_ivars.include?(ref[:name]) }
31
-
32
- # Handle unknown variables according to the policy
13
+ analyses = Ivar.get_ancestral_analyses(self.class)
14
+ manifest = Ivar.get_or_create_manifest(self.class)
15
+ declared_ivars = manifest.all_declarations.map(&:name)
16
+ allowed_ivars = (Ivar.known_internal_ivars | instance_variables | declared_ivars | add).uniq
17
+ instance_refs = analyses.flat_map(&:references)
18
+ unknown_refs = instance_refs.reject { |ref| allowed_ivars.include?(ref[:name]) }
33
19
  policy_instance = Ivar.get_policy(policy)
34
20
  policy_instance.handle_unknown_ivars(unknown_refs, self.class, allowed_ivars)
35
21
  end
36
22
 
37
- # For backward compatibility - delegates to check_ivars with warn_once policy
38
- # @param add [Array<Symbol>] Additional instance variables to allow
39
- def check_ivars_once(add: [])
40
- check_ivars(add: add, policy: :warn_once)
41
- end
42
-
43
23
  private
44
24
 
45
25
  # Get the check policy for this instance
46
26
  # @return [Symbol, Policy] The check policy
47
27
  def get_check_policy
48
- # If the class has an ivar_check_policy method, use that
49
28
  return self.class.ivar_check_policy if self.class.respond_to?(:ivar_check_policy)
50
-
51
- # Otherwise, use the global default
52
29
  Ivar.check_policy
53
30
  end
54
31
  end
data/lib/ivar/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ivar
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/ivar.rb CHANGED
@@ -1,42 +1,124 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "ivar/version"
4
- require_relative "ivar/prism_analysis"
5
4
  require_relative "ivar/policies"
6
5
  require_relative "ivar/validation"
7
6
  require_relative "ivar/macros"
8
- require_relative "ivar/auto_check"
7
+ require_relative "ivar/project_root"
8
+ require_relative "ivar/check_all_manager"
9
+ require_relative "ivar/check_policy"
10
+ require_relative "ivar/checked"
11
+ require_relative "ivar/manifest"
12
+ require_relative "ivar/targeted_prism_analysis"
9
13
  require "prism"
10
14
  require "did_you_mean"
15
+ require "pathname"
11
16
 
12
17
  module Ivar
13
18
  @analysis_cache = {}
14
19
  @checked_classes = {}
15
- @default_check_policy = :warn
20
+ @default_check_policy = :warn_once
21
+ @manifest_registry = {}
22
+ @project_root = nil
23
+ MUTEX = Mutex.new
24
+ PROJECT_ROOT_FINDER = ProjectRoot.new
25
+ CHECK_ALL_MANAGER = CheckAllManager.new
26
+
27
+ # Pattern for internal instance variables
28
+ INTERNAL_IVAR_PREFIX = "@__ivar_"
29
+
30
+ # Checks if an instance variable name is an internal variable
31
+ # @param ivar_name [Symbol, String] The instance variable name to check
32
+ # @return [Boolean] Whether the variable is an internal variable
33
+ def self.internal_ivar?(ivar_name)
34
+ ivar_name.to_s.start_with?(INTERNAL_IVAR_PREFIX)
35
+ end
36
+
37
+ # Returns a list of known internal instance variables
38
+ # @return [Array<Symbol>] List of known internal instance variables
39
+ def self.known_internal_ivars
40
+ [
41
+ :@__ivar_check_policy,
42
+ :@__ivar_initialized_vars,
43
+ :@__ivar_method_impl_stash,
44
+ :@__ivar_skip_init
45
+ ]
46
+ end
47
+
48
+ def self.get_ancestral_analyses(klass)
49
+ klass
50
+ .ancestors.filter_map { |ancestor| maybe_get_analysis(ancestor) }
51
+ .reverse
52
+ end
53
+
54
+ def self.maybe_get_analysis(klass)
55
+ if klass.include?(Validation)
56
+ get_analysis(klass)
57
+ end
58
+ end
16
59
 
17
60
  # Returns a cached analysis for the given class or module
18
61
  # Creates a new analysis if one doesn't exist in the cache
62
+ # Thread-safe: Multiple readers are allowed, but writers block all other access
19
63
  def self.get_analysis(klass)
20
- @analysis_cache[klass] ||= PrismAnalysis.new(klass)
64
+ return @analysis_cache[klass] if @analysis_cache.key?(klass)
65
+
66
+ MUTEX.synchronize do
67
+ @analysis_cache[klass] ||= TargetedPrismAnalysis.new(klass)
68
+ end
21
69
  end
22
70
 
23
71
  # Checks if a class has been validated already
24
72
  # @param klass [Class] The class to check
25
73
  # @return [Boolean] Whether the class has been validated
74
+ # Thread-safe: Read-only operation
26
75
  def self.class_checked?(klass)
27
- @checked_classes.key?(klass)
76
+ MUTEX.synchronize { @checked_classes.key?(klass) }
28
77
  end
29
78
 
30
79
  # Marks a class as having been checked
31
80
  # @param klass [Class] The class to mark as checked
81
+ # Thread-safe: Write operation protected by mutex
32
82
  def self.mark_class_checked(klass)
33
- @checked_classes[klass] = true
83
+ MUTEX.synchronize { @checked_classes[klass] = true }
34
84
  end
35
85
 
36
86
  # For testing purposes - allows clearing the cache
87
+ # Thread-safe: Write operation protected by mutex
37
88
  def self.clear_analysis_cache
38
- @analysis_cache.clear
39
- @checked_classes.clear
89
+ MUTEX.synchronize do
90
+ @analysis_cache.clear
91
+ @checked_classes.clear
92
+ @manifest_registry.clear
93
+ end
94
+ PROJECT_ROOT_FINDER.clear_cache
95
+ end
96
+
97
+ # Get or create a manifest for a class or module
98
+ # @param klass [Class, Module] The class or module to get a manifest for
99
+ # @param create [Boolean] Whether to create a new manifest if one doesn't exist
100
+ # @return [Manifest, nil] The manifest for the class or module, or nil if not found and create_if_missing is false
101
+ def self.get_manifest(klass, create: true)
102
+ return @manifest_registry[klass] if @manifest_registry.key?(klass)
103
+ return nil unless create
104
+
105
+ MUTEX.synchronize do
106
+ @manifest_registry[klass] ||= Manifest.new(klass)
107
+ end
108
+ end
109
+
110
+ # Alias for get_manifest that makes it clearer that it may create a manifest
111
+ # @param klass [Class, Module] The class or module to get a manifest for
112
+ # @return [Manifest] The manifest for the class or module
113
+ def self.get_or_create_manifest(klass)
114
+ get_manifest(klass, create: true)
115
+ end
116
+
117
+ # Check if a manifest exists for a class or module
118
+ # @param klass [Class, Module] The class or module to check
119
+ # @return [Boolean] Whether a manifest exists for the class or module
120
+ def self.manifest_exists?(klass)
121
+ @manifest_registry.key?(klass)
40
122
  end
41
123
 
42
124
  # Get the default check policy
@@ -48,6 +130,56 @@ module Ivar
48
130
  # Set the default check policy
49
131
  # @param policy [Symbol, Policy] The default check policy
50
132
  def self.check_policy=(policy)
51
- @default_check_policy = policy
133
+ MUTEX.synchronize { @default_check_policy = policy }
134
+ end
135
+
136
+ def self.project_root=(explicit_root)
137
+ @project_root = explicit_root
138
+ end
139
+
140
+ # Determines the project root directory based on the caller's location
141
+ # Delegates to ProjectRoot class
142
+ # @param caller_location [String, nil] Optional file path to start from (defaults to caller's location)
143
+ # @return [String] The absolute path to the project root directory
144
+ def self.project_root(caller_location = nil)
145
+ @project_root ||= PROJECT_ROOT_FINDER.find(caller_location)
146
+ end
147
+
148
+ # Enables automatic inclusion of Ivar::Checked in all classes and modules
149
+ # defined within the project root.
150
+ #
151
+ # @param block [Proc] Optional block. If provided, auto-checking is only active
152
+ # for the duration of the block. Otherwise, it remains active indefinitely.
153
+ # @return [void]
154
+ def self.check_all(&block)
155
+ root = project_root
156
+ CHECK_ALL_MANAGER.enable(root, &block)
157
+ end
158
+
159
+ # Disables automatic inclusion of Ivar::Checked in classes and modules.
160
+ # @return [void]
161
+ def self.disable_check_all
162
+ CHECK_ALL_MANAGER.disable
163
+ end
164
+
165
+ # Gets a method from the stash or returns nil if not found
166
+ # @param klass [Class] The class that owns the method
167
+ # @param method_name [Symbol] The name of the method to retrieve
168
+ # @return [UnboundMethod, nil] The stashed method or nil if not found
169
+ def self.get_stashed_method(klass, method_name)
170
+ (klass.instance_variable_get(:@__ivar_method_impl_stash) || {})[method_name]
171
+ end
172
+
173
+ # Stashes a method implementation for a class
174
+ # @param klass [Class] The class that owns the method
175
+ # @param method_name [Symbol] The name of the method to stash
176
+ # @return [UnboundMethod, nil] The stashed method or nil if the method doesn't exist
177
+ def self.stash_method(klass, method_name)
178
+ return nil unless klass.method_defined?(method_name) || klass.private_method_defined?(method_name)
179
+
180
+ method_impl = klass.instance_method(method_name)
181
+ stash = klass.instance_variable_get(:@__ivar_method_impl_stash) ||
182
+ klass.instance_variable_set(:@__ivar_method_impl_stash, {})
183
+ stash[method_name] = method_impl
52
184
  end
53
185
  end
data/script/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "ivar"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/script/de-lint ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ STANDARDOPTS="--fix $*" bundle exec rake standard
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ STANDARDOPTS="--fix-unsafely $*" bundle exec rake standard
data/script/lint ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ STANDARDOPTS="$*" bundle exec rake standard
data/script/release ADDED
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # This script helps with releasing a new version of the gem
5
+ # Usage: script/release [major|minor|patch] [options]
6
+ #
7
+ # Options:
8
+ # --yes, -y Skip confirmation prompt
9
+
10
+ require "bundler/gem_tasks"
11
+ require_relative "../lib/ivar/version"
12
+
13
+ def error(message)
14
+ puts "\e[31mError: #{message}\e[0m"
15
+ exit 1
16
+ end
17
+
18
+ def success(message)
19
+ puts "\e[32m#{message}\e[0m"
20
+ end
21
+
22
+ def info(message)
23
+ puts "\e[34m#{message}\e[0m"
24
+ end
25
+
26
+ def get_new_version(current_version, bump_type)
27
+ major, minor, patch = current_version.split(".").map(&:to_i)
28
+
29
+ case bump_type
30
+ when "major"
31
+ "#{major + 1}.0.0"
32
+ when "minor"
33
+ "#{major}.#{minor + 1}.0"
34
+ when "patch"
35
+ "#{major}.#{minor}.#{patch + 1}"
36
+ else
37
+ error "Invalid bump type. Use 'major', 'minor', or 'patch'."
38
+ end
39
+ end
40
+
41
+ def update_version_file(new_version)
42
+ version_file_path = "lib/ivar/version.rb"
43
+ version_content = File.read(version_file_path)
44
+ updated_content = version_content.gsub(/VERSION = "[0-9]+\.[0-9]+\.[0-9]+"/, "VERSION = \"#{new_version}\"")
45
+ File.write(version_file_path, updated_content)
46
+ end
47
+
48
+ def update_changelog(new_version)
49
+ changelog_path = "CHANGELOG.md"
50
+ changelog_content = File.read(changelog_path)
51
+
52
+ # Check if there are unreleased changes
53
+ unless changelog_content.include?("## [Unreleased]")
54
+ error "No unreleased changes found in CHANGELOG.md. Add changes before releasing."
55
+ end
56
+
57
+ # Update the changelog with the new version
58
+ today = Time.now.strftime("%Y-%m-%d")
59
+ updated_content = changelog_content.gsub(
60
+ "## [Unreleased]",
61
+ "## [Unreleased]\n\n## [#{new_version}] - #{today}"
62
+ )
63
+
64
+ File.write(changelog_path, updated_content)
65
+ end
66
+
67
+ def run_tests
68
+ info "Running tests..."
69
+ system("bundle exec rake test") || error("Tests failed. Fix the tests before releasing.")
70
+ end
71
+
72
+ def run_linter
73
+ info "Running linter..."
74
+ system("bundle exec rake standard") || error("Linter found issues. Fix them before releasing.")
75
+ end
76
+
77
+ def clean_build_artifacts
78
+ info "Cleaning build artifacts..."
79
+ system("bundle exec rake clean clobber")
80
+ # Also remove any stray .gem files in the project root
81
+ Dir.glob("*.gem").each do |gem_file|
82
+ info "Removing stray gem file: #{gem_file}"
83
+ File.delete(gem_file)
84
+ end
85
+ end
86
+
87
+ def commit_and_tag(new_version)
88
+ info "Committing version bump..."
89
+ system("git add lib/ivar/version.rb CHANGELOG.md")
90
+ system("git commit -m \"Bump version to #{new_version}\"")
91
+
92
+ info "Creating tag v#{new_version}..."
93
+ system("git tag -a v#{new_version} -m \"Version #{new_version}\"")
94
+
95
+ info "To push the new version, run:"
96
+ puts " git push origin main && git push origin v#{new_version}"
97
+ end
98
+
99
+ # Main script
100
+ error "Please specify a version bump type: major, minor, or patch" if ARGV.empty?
101
+
102
+ # Parse arguments
103
+ args = ARGV.dup
104
+ skip_confirmation = args.delete("--yes") || args.delete("-y")
105
+ bump_type = args[0].downcase if args[0]
106
+
107
+ error "Please specify a version bump type: major, minor, or patch" unless bump_type
108
+
109
+ current_version = Ivar::VERSION
110
+ new_version = get_new_version(current_version, bump_type)
111
+
112
+ info "Current version: #{current_version}"
113
+ info "New version: #{new_version}"
114
+
115
+ # Skip confirmation if --yes/-y option is provided
116
+ confirmation = "y" if skip_confirmation
117
+
118
+ unless confirmation
119
+ puts "Continue? (y/n)"
120
+ confirmation = $stdin.gets.chomp.downcase
121
+ end
122
+
123
+ if confirmation == "y"
124
+ clean_build_artifacts
125
+ run_tests
126
+ run_linter
127
+ update_version_file(new_version)
128
+ update_changelog(new_version)
129
+ commit_and_tag(new_version)
130
+ success "Version bumped to #{new_version}!"
131
+ success "Run 'git push origin main && git push origin v#{new_version}' to trigger the release workflow."
132
+ else
133
+ info "Release cancelled."
134
+ end
data/script/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/script/test ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ TESTOPTS="$*" bundle exec rake test
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ivar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Avdi Grimm
@@ -45,29 +45,65 @@ files:
45
45
  - ".augment-guidelines"
46
46
  - ".devcontainer/Dockerfile"
47
47
  - ".devcontainer/devcontainer.json"
48
+ - ".devcontainer/post-create.sh"
49
+ - ".editorconfig"
50
+ - ".rubocop.yml"
48
51
  - ".standard.yml"
49
52
  - ".vscode/extensions.json"
53
+ - ".vscode/launch.json"
50
54
  - ".vscode/settings.json"
51
55
  - CHANGELOG.md
52
56
  - CODE_OF_CONDUCT.md
53
57
  - LICENSE.txt
54
58
  - README.md
55
59
  - Rakefile
60
+ - VERSION.md
61
+ - examples/check_all_block_example.rb
62
+ - examples/check_all_example.rb
63
+ - examples/inheritance_with_kwarg_init.rb
64
+ - examples/inheritance_with_positional_init.rb
65
+ - examples/mixed_positional_and_kwarg_init.rb
66
+ - examples/require_check_all_example.rb
56
67
  - examples/sandwich_inheritance.rb
68
+ - examples/sandwich_with_accessors.rb
69
+ - examples/sandwich_with_block_values.rb
57
70
  - examples/sandwich_with_checked.rb
58
71
  - examples/sandwich_with_checked_once.rb
72
+ - examples/sandwich_with_initial_values.rb
59
73
  - examples/sandwich_with_ivar_block.rb
60
74
  - examples/sandwich_with_ivar_macro.rb
61
- - examples/sandwich_with_kwarg.rb
75
+ - examples/sandwich_with_kwarg_init.rb
76
+ - examples/sandwich_with_positional_init.rb
77
+ - examples/sandwich_with_shared_values.rb
78
+ - hooks/README.md
79
+ - hooks/install.sh
80
+ - hooks/pre-commit
62
81
  - ivar.gemspec
63
82
  - lib/ivar.rb
64
- - lib/ivar/auto_check.rb
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/prism_analysis.rb
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
@@ -85,7 +121,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
121
  requirements:
86
122
  - - ">="
87
123
  - !ruby/object:Gem::Version
88
- version: 3.4.0
124
+ version: 3.3.0
89
125
  required_rubygems_version: !ruby/object:Gem::Requirement
90
126
  requirements:
91
127
  - - ">="