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
@@ -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
@@ -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
@@ -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