aye_var 0.1.0 → 0.2.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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -5
  3. data/lib/aye_var/version.rb +1 -1
  4. data/lib/aye_var.rb +94 -35
  5. metadata +15 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13bedf4da163ee9b20c83ed235f78825ced3e3a6547abc98af2de5e08d06401c
4
- data.tar.gz: 4cbc787d135c70ea6c1b725ad01af3c2be374959fb1a52667f9db6271e458b32
3
+ metadata.gz: d1738b022486bc41367e9dee76d2fc4a0792a47dccb5059ac872a479b192ab8c
4
+ data.tar.gz: 41ba0ed417967bea1af88f4c828a7bae04b586d4749e93b50ce91972ad4e55bd
5
5
  SHA512:
6
- metadata.gz: eb4d35e722d2f31d4953a03687421c18b1aea499e69e77ef614285622e41e8ab23c0422b6649dfad1e54066d77362ccad1a5851c3f2a9a1a7dafe6bf44fc877f
7
- data.tar.gz: bd303daa537463d610e8b37b356e3bc44e8bd46e4e4734acc58b24ca3a262748ae39e2169cbe328b3daddf4bd84767d94514fd6ed7ebdd54aa1ad980da4a8a4a
6
+ metadata.gz: 9626b16ccb6c22998e885816359a0c304db95adff5657c69e59cdc83d3549717928b8425f69beee37269e69b8f1152c43330153c88267b0e701faa7b378ab84b
7
+ data.tar.gz: '089409f803c9dbae0d834795c373617088eef192a1f12bbb719fec2af28cb810884f8c4c3b90275b34fd92f88b04c0152f3c1a400094a3121d5734156033d288'
data/README.md CHANGED
@@ -1,14 +1,20 @@
1
- ## AyeVar
1
+ # AyeVar 🏴‍☠️
2
2
 
3
- Note, this gem is very new and experimental. The API will probably change.
3
+ Avast ye, this gem be fresh off th’ plunderin’ an’ experimental as a new recruit, me hearties! Th’ API be shiftin’ like th’ treacherous seas beneath yer barnacled hull.
4
4
 
5
- Add this gem to your gemfile.
5
+ ## What does it do?
6
+
7
+ It keelhauls them scurvy undefined instance variables by transformin’ yer code as it loads into th’ hold, savvy? Davy Jones ’imself would approve!
8
+
9
+ ## Setup
10
+
11
+ Add this precious booty to yer gemfile, shiver me timbers!
6
12
 
7
13
  ```ruby
8
14
  gem "aye_var", require: false
9
15
  ```
10
16
 
11
- Then require and initialize it in your app as early as possible. If youre using Bootsnap, it should be right after Bootsnap.
17
+ Then be requirin’ an’ initializin’ it in yer galleon as early as ye can hoist sail, ye scallywag! If ye be usin’ Bootsnap, place it right after, or by the powers, ye’ll be swimmin’ with th’ fishes!
12
18
 
13
19
  ```ruby
14
20
  require "aye_var"
@@ -16,4 +22,4 @@ require "aye_var"
16
22
  AyeVar.init(include: ["#{Dir.pwd}/**/*"])
17
23
  ```
18
24
 
19
- You can pass in an array of globs to `include:` and `exclude:`.
25
+ Ye can pass an array o’ globs to `include:` an’ `exclude:`, or I’ll feed ye to th’ kraken, ye mangy bilge-suckin’ swab!
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AyeVar
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/aye_var.rb CHANGED
@@ -1,77 +1,136 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "require-hooks/setup"
4
+ require "prism"
4
5
 
5
6
  module AyeVar
7
+ NameError = Class.new(::NameError)
8
+
9
+ def self.init(include: [], exclude: [])
10
+ RequireHooks.source_transform(patterns: include, exclude_patterns: exclude) do |path, source|
11
+ source ||= File.read(path)
12
+ Processor.call(source)
13
+ end
14
+ end
15
+
6
16
  class Processor < Prism::Visitor
7
17
  def self.call(source)
8
- object = new(source)
9
- object.visit(Prism.parse(source).value)
18
+ visitor = new
19
+ visitor.visit(Prism.parse(source).value)
10
20
 
11
21
  buffer = source.dup
12
22
 
13
- object.ivars.sort_by(&:first).reverse_each do |offset, action, name|
23
+ visitor.annotations.sort_by(&:first).reverse_each do |offset, action, name|
14
24
  case action
15
25
  when :start
16
- buffer.insert(offset, "((raise ::AyeVar::NameError.new('Undefined instance variable #{name}') unless defined?(#{name}));")
26
+ buffer.insert(offset, "((raise ::AyeVar::NameError.new('Undefined instance variable #{name}') unless defined?(#{name})); ")
17
27
  when :end
18
28
  buffer.insert(offset, ")")
29
+ else
30
+ raise "Invalid annotation"
19
31
  end
20
32
  end
21
33
 
22
34
  buffer
23
35
  end
24
36
 
25
- def initialize(source)
26
- @initializer = false
27
- @ivars = []
37
+ def initialize
38
+ @this = nil
39
+ @context = Set[]
40
+ @annotations = []
41
+ end
42
+
43
+ attr_reader :annotations
44
+
45
+ def visit_class_node(node)
46
+ new_context(node) { super }
47
+ end
48
+
49
+ def visit_module_node(node)
50
+ new_context(node) { super }
51
+ end
52
+
53
+ def visit_block_node(node)
54
+ new_context(node) { super }
55
+ end
56
+
57
+ def visit_singleton_class_node(node)
58
+ new_context(node) { super }
28
59
  end
29
60
 
30
- attr_reader :ivars
61
+ def visit_defined_node(node)
62
+ if Prism::InstanceVariableReadNode === node.value
63
+ context << node.value.name
64
+ end
65
+
66
+ super
67
+ end
31
68
 
32
69
  def visit_def_node(node)
33
- if node.name == :initialize
34
- begin
35
- @initializer = true
36
- super
37
- ensure
38
- @initializer = false
39
- end
40
- else
41
- super
70
+ new_context(node) { super }
71
+ end
72
+
73
+ def visit_if_node(node)
74
+ visit(node.predicate)
75
+
76
+ branch { visit(node.statements) }
77
+ branch { visit(node.subsequent) }
78
+ end
79
+
80
+ def visit_case_node(node)
81
+ visit(node.predicate)
82
+
83
+ node.conditions.each do |condition|
84
+ branch { visit(condition) }
42
85
  end
86
+
87
+ branch { visit(node.else_clause) }
43
88
  end
44
89
 
45
90
  def visit_instance_variable_read_node(node)
46
- location = node.location
47
91
  name = node.name
48
92
 
49
- @ivars << [location.start_character_offset, :start, name]
50
- @ivars << [location.end_character_offset, :end, name]
51
- super
52
- end
53
-
54
- def visit_instance_variable_write_node(node)
55
- unless @initializer
93
+ unless context.include?(name)
56
94
  location = node.location
57
- name = node.name
58
95
 
59
- @ivars << [location.start_character_offset, :start, name]
60
- @ivars << [location.end_character_offset, :end, name]
96
+ context << name
97
+
98
+ @annotations << [location.start_character_offset, :start, name]
99
+ @annotations << [location.end_character_offset, :end, name]
61
100
  end
62
101
 
63
102
  super
64
103
  end
65
- end
66
104
 
67
- private_constant :Processor
105
+ private def new_context(this)
106
+ original_this = @this
107
+ original_context = @context
68
108
 
69
- NameError = Class.new(::NameError)
109
+ @this = this
110
+ @context = Set[]
70
111
 
71
- def self.init(include: [], exclude: [])
72
- RequireHooks.source_transform(patterns: include, exclude_patterns: exclude) do |path, source|
73
- source ||= File.read(path)
74
- Processor.call(source)
112
+ begin
113
+ yield
114
+ ensure
115
+ @this = original_this
116
+ @context = original_context
117
+ end
118
+ end
119
+
120
+ private def branch
121
+ original_context = @context
122
+ @context = original_context.dup
123
+
124
+ begin
125
+ yield
126
+ ensure
127
+ @context = original_context
128
+ end
129
+ end
130
+
131
+ # The current context on the stack
132
+ private def context
133
+ @context
75
134
  end
76
135
  end
77
136
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aye_var
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Drapper
@@ -23,6 +23,20 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: prism
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
26
40
  description: Raise an exception when using undefined instance variables.
27
41
  email:
28
42
  - joel@drapper.me