strict_ivars 0.5.0 → 1.0.0.rc1
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 +72 -43
- data/lib/strict_ivars/base_processor.rb +57 -0
- data/lib/strict_ivars/configuration.rb +2 -1
- data/lib/strict_ivars/processor.rb +5 -45
- data/lib/strict_ivars/version.rb +1 -1
- data/lib/strict_ivars.rb +65 -10
- metadata +2 -2
- data/lib/strict_ivars/patch_eval.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b910c7decefccc4495a320886042153d9178c12f76cda7ccb549c304be2b6725
|
4
|
+
data.tar.gz: 8af1c060ed4ad3b1eef1b306c336a5bac415018f19bc73954e056bb47f9d86ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 957162900af7f34fe4e111065f8af1886327a3a3ae5ebdc8de4529869b02d4d71b1d2b27fdbf21ac5aba316e150463d990bdb4c065658b7381d599c520026a9b
|
7
|
+
data.tar.gz: 7748495c8e7a2e7cdddbd39b46bcb4e4d217db9b1f4f070fb810445f17b2fcd260356376ee5a7e936790410f83017dc0fdc232a69bdd1da8515204d39e8401ff
|
data/README.md
CHANGED
@@ -1,51 +1,58 @@
|
|
1
1
|
# Strict Ivars
|
2
2
|
|
3
|
-
Strict Ivars is a tiny pre-processor for Ruby that guards your instance variable reads, ensuring the instance variable is actually defined. This helps catch typos nice and early. It‘s especially good when used with [Literal](https://literal.fun).
|
3
|
+
Strict Ivars is a tiny pre-processor for Ruby that guards your instance variable reads, ensuring the instance variable is actually defined. This helps catch typos nice and early. It‘s especially good when used with [Literal](https://literal.fun) and [Phlex](https://www.phlex.fun), though it also works with ERB.
|
4
4
|
|
5
|
-
|
5
|
+
> [!NOTE]
|
6
|
+
> JRuby and TruffleRuby are not currently supported.
|
6
7
|
|
7
|
-
|
8
|
+
## Setup
|
8
9
|
|
9
|
-
|
10
|
+
Strict Ivars should be used in apps not libraries. Though you could use it in your library’s test suite.
|
11
|
+
|
12
|
+
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.
|
10
13
|
|
11
14
|
```ruby
|
12
|
-
|
13
|
-
foo if @bar
|
14
|
-
end
|
15
|
+
gem "strict_ivars", require: false
|
15
16
|
```
|
16
17
|
|
17
|
-
|
18
|
+
Now the gem is installed, you should require and initialize the gem as early as possible in your boot process. Ideally, this should be right after Bootsnap is set up. In Rails, this will be in your `boot.rb` file.
|
18
19
|
|
19
20
|
```ruby
|
20
|
-
|
21
|
-
foo if (defined?(@bar) ? @bar : raise)
|
22
|
-
end
|
21
|
+
require "strict_ivars"
|
23
22
|
```
|
24
23
|
|
25
|
-
|
24
|
+
You can pass an array of globs to `StrictIvars.init` as `include:` and `exclude:`
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
StrictIvars.init(include: ["#{Dir.pwd}/**/*"], exclude: ["#{Dir.pwd}/vendor/**/*"])
|
28
|
+
```
|
29
|
+
|
30
|
+
This example include everything in the current directory apart from the `./vendor` folder (which is where GitHub Actions installs gems).
|
31
|
+
|
32
|
+
If you’re using Bootsnap, you should clear your bootsnap cache by deleting the folder `tmp/cache/bootsnap`.
|
26
33
|
|
27
|
-
|
34
|
+
## How does it work?
|
28
35
|
|
29
|
-
|
36
|
+
When Strict Ivars detects that you are loading code from paths its configured to handle, it quickly looks for instance variable reads and guards them with a `defined?` check.
|
30
37
|
|
31
|
-
|
38
|
+
For example, it will replace this:
|
32
39
|
|
33
40
|
```ruby
|
34
|
-
|
35
|
-
@
|
41
|
+
def example
|
42
|
+
foo if @bar
|
36
43
|
end
|
37
44
|
```
|
38
45
|
|
39
|
-
|
46
|
+
...with something like this:
|
40
47
|
|
41
48
|
```ruby
|
42
|
-
|
43
|
-
|
49
|
+
def example
|
50
|
+
foo if (defined?(@bar) ? @bar : raise)
|
44
51
|
end
|
45
|
-
|
46
|
-
@foo
|
47
52
|
```
|
48
53
|
|
54
|
+
The replacement happens on load, so you never see this in your source code. It’s also always wrapped in parentheses and takes up a single line, so it won’t mess up the line numbers in exceptions.
|
55
|
+
|
49
56
|
**Writes:**
|
50
57
|
|
51
58
|
Strict Ivars doesn’t apply to writes, since these are considered the authoritative source of the instance variable definitions.
|
@@ -56,7 +63,7 @@ Strict Ivars doesn’t apply to writes, since these are considered the authorita
|
|
56
63
|
|
57
64
|
**Or-writes:**
|
58
65
|
|
59
|
-
|
66
|
+
Or-writes are considered an authoritative definition, not a read.
|
60
67
|
|
61
68
|
```ruby
|
62
69
|
@foo ||= 1
|
@@ -64,47 +71,67 @@ This is considered a definition and not guarded.
|
|
64
71
|
|
65
72
|
**And-writes:**
|
66
73
|
|
67
|
-
|
74
|
+
And-writes are considered an authoritative definition, not a read.
|
68
75
|
|
69
76
|
```ruby
|
70
77
|
@foo &&= 1
|
71
78
|
```
|
72
79
|
|
73
|
-
##
|
74
|
-
|
75
|
-
Install the gem by adding it to your `Gemfile` and running `bundle install`.
|
80
|
+
## Common issues
|
76
81
|
|
77
|
-
|
82
|
+
#### Implicitly depending on undefined instance variables
|
78
83
|
|
79
84
|
```ruby
|
80
|
-
|
85
|
+
def description
|
86
|
+
return @description if @description.present?
|
87
|
+
@description = get_description
|
88
|
+
end
|
81
89
|
```
|
82
90
|
|
83
|
-
|
91
|
+
This example is relying on Ruby’s behaviour of returning `nil` for undefiend instance variables, which is completely unnecessary. Instead of using `present?`, we could use `defined?` here.
|
84
92
|
|
85
93
|
```ruby
|
86
|
-
|
94
|
+
def description
|
95
|
+
return @description if defined?(@description)
|
96
|
+
@description = get_description
|
97
|
+
end
|
98
|
+
```
|
87
99
|
|
88
|
-
|
100
|
+
Alternatively, as long as `get_description` doesn’t return `nil` and expect us to memoize it, we could use an “or-write” `||=`
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
def description
|
104
|
+
@description ||= get_description
|
105
|
+
end
|
89
106
|
```
|
90
107
|
|
91
|
-
|
108
|
+
#### Rendering instance variables that are only set somtimes
|
109
|
+
|
110
|
+
It’s common to render an instance variable in an ERB view that you only set on some controllers.
|
92
111
|
|
93
|
-
|
112
|
+
```erb
|
113
|
+
<div data-favourites="<%= @user_favourites %>"></div>
|
114
|
+
```
|
94
115
|
|
95
|
-
|
116
|
+
The best solution to this to always set it on all controllers, but set it to `nil` in the cases where you don’t have anything to render. This will prevent you from making a typo in your views.
|
96
117
|
|
97
|
-
|
118
|
+
Alternatively, you could update the view to be explicit about the fact this ivar may not be set.
|
98
119
|
|
99
|
-
|
120
|
+
```erb
|
121
|
+
<div data-favourites="<%= (@user_favourites ||= nil) %>"></div>
|
122
|
+
```
|
100
123
|
|
101
|
-
|
124
|
+
Better yet, add a `defined?` check:
|
102
125
|
|
103
|
-
|
126
|
+
```erb
|
127
|
+
<% if defined?(@user_favourites) %>
|
128
|
+
<div data-favourites="<%= @user_favourites %>"></div>
|
129
|
+
<% end %>
|
130
|
+
```
|
104
131
|
|
105
132
|
## Performance
|
106
133
|
|
107
|
-
####
|
134
|
+
#### Boot performance
|
108
135
|
|
109
136
|
Using Strict Ivars will impact startup performance since it needs to process each Ruby file you require. However, if you are using Bootsnap, the processed RubyVM::InstructionSequences will be cached and you probably won’t notice the incremental cache misses day-to-day.
|
110
137
|
|
@@ -112,12 +139,14 @@ Using Strict Ivars will impact startup performance since it needs to process eac
|
|
112
139
|
|
113
140
|
In my benchmarks on Ruby 3.4 with YJIT, it’s difficult to tell if there is any performance difference with or without the `defined?` guards at runtime. Sometimes it’s about 1% faster with the guards than without. Sometimes the other way around.
|
114
141
|
|
115
|
-
On my laptop, a method that returns an instance varible takes about 15ns and a method that checks if an instance varible is defined and then returns it takes about 15ns.
|
142
|
+
On my laptop, a method that returns an instance varible takes about 15ns and a method that checks if an instance varible is defined and then returns it takes about 15ns. All this is to say, I don’t think there will be any measurable runtime performance impact, at least not in Ruby 3.4.
|
143
|
+
|
144
|
+
#### Dynamic evals
|
116
145
|
|
117
|
-
|
146
|
+
There is a small additional cost to dynamically evaluating code via `eval`, `class_eval`, `module_eval`, `instance_eval` and `binding.eval`. Dynamic evaluation usually only happens at boot time but it can happen at runtime depending on how you use it.
|
118
147
|
|
119
148
|
## Uninstall
|
120
149
|
|
121
150
|
Becuase Strict Ivars only ever makes your code safer, you can always back out without anything breaking.
|
122
151
|
|
123
|
-
To uninstall Strict Ivars, first remove the require and initialization code from wherever you added it and then remove the gem from your `Gemfile`. If you
|
152
|
+
To uninstall Strict Ivars, first remove the require and initialization code from wherever you added it and then remove the gem from your `Gemfile`. If you are using Bootsnap, there’s a good chance it cached some pre-processed code with the instance variable read guards in it. To clear this, you’ll need to delete your bootsnap cache, which should be in `tmp/cache/bootsnap`.
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
class StrictIvars::BaseProcessor < Prism::Visitor
|
6
|
+
EVAL_METHODS = Set[:class_eval, :module_eval, :instance_eval, :eval].freeze
|
7
|
+
|
8
|
+
#: (String) -> String
|
9
|
+
def self.call(source)
|
10
|
+
visitor = new
|
11
|
+
visitor.visit(Prism.parse(source).value)
|
12
|
+
buffer = source.dup
|
13
|
+
annotations = visitor.annotations
|
14
|
+
annotations.sort_by!(&:first)
|
15
|
+
|
16
|
+
annotations.reverse_each do |offset, string|
|
17
|
+
buffer.insert(offset, string)
|
18
|
+
end
|
19
|
+
|
20
|
+
buffer
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@context = Set[]
|
25
|
+
@annotations = []
|
26
|
+
end
|
27
|
+
|
28
|
+
#: Array[[Integer, :start_ivar_read | :end_ivar_read, Symbol]]
|
29
|
+
attr_reader :annotations
|
30
|
+
|
31
|
+
def visit_call_node(node)
|
32
|
+
name = node.name
|
33
|
+
|
34
|
+
if EVAL_METHODS.include?(name) && (arguments = node.arguments)
|
35
|
+
location = arguments.location
|
36
|
+
|
37
|
+
if node.receiver
|
38
|
+
receiver_local = "__eval_receiver_#{SecureRandom.hex(8)}__"
|
39
|
+
receiver_location = node.receiver.location
|
40
|
+
|
41
|
+
@annotations.push(
|
42
|
+
[receiver_location.start_character_offset, "(#{receiver_local} = "],
|
43
|
+
[receiver_location.end_character_offset, ")"],
|
44
|
+
[location.start_character_offset, "*(::StrictIvars.__process_eval_args__(#{receiver_local}, :#{name}, "],
|
45
|
+
[location.end_character_offset, "))"]
|
46
|
+
)
|
47
|
+
else
|
48
|
+
@annotations.push(
|
49
|
+
[location.start_character_offset, "*(::StrictIvars.__process_eval_args__(self, :#{name}, "],
|
50
|
+
[location.end_character_offset, "))"]
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
@@ -22,7 +22,8 @@ class StrictIvars::Configuration
|
|
22
22
|
end
|
23
23
|
|
24
24
|
#: (String) -> bool
|
25
|
-
def match(path)
|
25
|
+
def match?(path)
|
26
|
+
return false unless String === path
|
26
27
|
path = File.absolute_path(path)
|
27
28
|
return false if @exclude.any? { |pattern| File.fnmatch?(pattern, path) }
|
28
29
|
return true if @include.any? { |pattern| File.fnmatch?(pattern, path) }
|
@@ -1,36 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class StrictIvars::Processor <
|
4
|
-
#: (String) -> String
|
5
|
-
def self.call(source)
|
6
|
-
visitor = new
|
7
|
-
visitor.visit(Prism.parse(source).value)
|
8
|
-
buffer = source.dup
|
9
|
-
annotations = visitor.annotations
|
10
|
-
annotations.sort_by!(&:first)
|
11
|
-
|
12
|
-
annotations.reverse_each do |offset, action, name|
|
13
|
-
case action
|
14
|
-
when :start
|
15
|
-
buffer.insert(offset, "(defined?(#{name}) ? ")
|
16
|
-
when :end
|
17
|
-
buffer.insert(offset, " : (::Kernel.raise(::StrictIvars::NameError.new('Undefined instance variable #{name}'))))")
|
18
|
-
else
|
19
|
-
raise "Invalid annotation"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
buffer
|
24
|
-
end
|
25
|
-
|
26
|
-
def initialize
|
27
|
-
@context = Set[]
|
28
|
-
@annotations = []
|
29
|
-
end
|
30
|
-
|
31
|
-
#: Array[[Integer, :start | :end, Symbol]]
|
32
|
-
attr_reader :annotations
|
33
|
-
|
3
|
+
class StrictIvars::Processor < StrictIvars::BaseProcessor
|
34
4
|
#: (Prism::ClassNode) -> void
|
35
5
|
def visit_class_node(node)
|
36
6
|
new_context { super }
|
@@ -51,17 +21,6 @@ class StrictIvars::Processor < Prism::Visitor
|
|
51
21
|
new_context { super }
|
52
22
|
end
|
53
23
|
|
54
|
-
#: (Prism::DefinedNode) -> void
|
55
|
-
def visit_defined_node(node)
|
56
|
-
value = node.value
|
57
|
-
|
58
|
-
if Prism::InstanceVariableReadNode === value
|
59
|
-
@context << value.name
|
60
|
-
end
|
61
|
-
|
62
|
-
super
|
63
|
-
end
|
64
|
-
|
65
24
|
#: (Prism::DefNode) -> void
|
66
25
|
def visit_def_node(node)
|
67
26
|
new_context { super }
|
@@ -95,9 +54,10 @@ class StrictIvars::Processor < Prism::Visitor
|
|
95
54
|
|
96
55
|
@context << name
|
97
56
|
|
98
|
-
@annotations
|
99
|
-
[location.start_character_offset,
|
100
|
-
[location.end_character_offset, :
|
57
|
+
@annotations.push(
|
58
|
+
[location.start_character_offset, "(defined?(#{name}) ? "],
|
59
|
+
[location.end_character_offset, " : (::Kernel.raise(::StrictIvars::NameError.new('Undefined instance variable #{name}'))))"]
|
60
|
+
)
|
101
61
|
end
|
102
62
|
|
103
63
|
super
|
data/lib/strict_ivars/version.rb
CHANGED
data/lib/strict_ivars.rb
CHANGED
@@ -3,28 +3,83 @@
|
|
3
3
|
require "prism"
|
4
4
|
require "require-hooks/setup"
|
5
5
|
require "strict_ivars/version"
|
6
|
+
require "strict_ivars/base_processor"
|
7
|
+
require "strict_ivars/processor"
|
6
8
|
require "strict_ivars/configuration"
|
7
9
|
|
8
10
|
module StrictIvars
|
9
11
|
NameError = Class.new(::NameError)
|
10
12
|
|
13
|
+
EMPTY_ARRAY = [].freeze
|
14
|
+
EVERYTHING = ["**/*"].freeze
|
15
|
+
METHOD_METHOD = Module.instance_method(:method)
|
16
|
+
|
11
17
|
CONFIG = Configuration.new
|
12
18
|
|
19
|
+
# Initializes StrictIvars so that code loaded after this point will be
|
20
|
+
# guarded against undefined instance variable reads. You can pass an array
|
21
|
+
# of globs to `include:` and `exclude:`.
|
22
|
+
#
|
23
|
+
# ```ruby
|
24
|
+
# StrictIvars.init(
|
25
|
+
# include: ["#{Dir.pwd}/**/*"],
|
26
|
+
# exclude: ["#{Dir.pwd}/vendor/**/*"]
|
27
|
+
# )
|
28
|
+
# ```
|
13
29
|
#: (include: Array[String], exclude: Array[String]) -> void
|
14
|
-
def self.init(include:
|
15
|
-
|
16
|
-
|
17
|
-
CONFIG.include(*include)
|
18
|
-
CONFIG.exclude(*exclude)
|
30
|
+
def self.init(include: EMPTY_ARRAY, exclude: EMPTY_ARRAY)
|
31
|
+
CONFIG.include(*include) unless include.length == 0
|
32
|
+
CONFIG.exclude(*exclude) unless exclude.length == 0
|
19
33
|
|
20
34
|
RequireHooks.source_transform(
|
21
|
-
patterns:
|
22
|
-
exclude_patterns:
|
35
|
+
patterns: EVERYTHING,
|
36
|
+
exclude_patterns: EMPTY_ARRAY
|
23
37
|
) do |path, source|
|
24
38
|
source ||= File.read(path)
|
25
|
-
|
39
|
+
|
40
|
+
if CONFIG.match?(path)
|
41
|
+
Processor.call(source)
|
42
|
+
else
|
43
|
+
BaseProcessor.call(source)
|
44
|
+
end
|
26
45
|
end
|
27
46
|
end
|
28
|
-
end
|
29
47
|
|
30
|
-
|
48
|
+
# For internal use only. This method pre-processes arguments to an eval method.
|
49
|
+
#: (Object, Symbol, *untyped)
|
50
|
+
def self.__process_eval_args__(receiver, method_name, *args)
|
51
|
+
method = METHOD_METHOD.bind_call(receiver, method_name)
|
52
|
+
owner = method.owner
|
53
|
+
|
54
|
+
source, file = nil
|
55
|
+
|
56
|
+
case method_name
|
57
|
+
when :class_eval, :module_eval
|
58
|
+
if Module == owner
|
59
|
+
source, file = args
|
60
|
+
end
|
61
|
+
when :instance_eval
|
62
|
+
if BasicObject == owner
|
63
|
+
source, file = args
|
64
|
+
end
|
65
|
+
when :eval
|
66
|
+
if Kernel == owner
|
67
|
+
source, binding, file = args
|
68
|
+
elsif Binding == owner
|
69
|
+
source, file = args
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if String === source
|
74
|
+
file ||= caller_locations(1, 1).first&.path
|
75
|
+
|
76
|
+
if CONFIG.match?(file)
|
77
|
+
args[0] = Processor.call(source)
|
78
|
+
else
|
79
|
+
args[0] = BaseProcessor.call(source)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
args
|
84
|
+
end
|
85
|
+
end
|
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: 0.
|
4
|
+
version: 1.0.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Drapper
|
@@ -47,8 +47,8 @@ files:
|
|
47
47
|
- LICENSE.txt
|
48
48
|
- README.md
|
49
49
|
- lib/strict_ivars.rb
|
50
|
+
- lib/strict_ivars/base_processor.rb
|
50
51
|
- lib/strict_ivars/configuration.rb
|
51
|
-
- lib/strict_ivars/patch_eval.rb
|
52
52
|
- lib/strict_ivars/processor.rb
|
53
53
|
- lib/strict_ivars/version.rb
|
54
54
|
homepage: https://github.com/joeldrapper/strict_ivars
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module StrictIvars
|
4
|
-
module ModuleEvalPatch
|
5
|
-
#: (String, ?String, ?Integer) -> void
|
6
|
-
#: () { () -> void } -> void
|
7
|
-
def class_eval(*args)
|
8
|
-
source, file, lineno = args
|
9
|
-
|
10
|
-
file ||= caller_locations(1, 1).first.path
|
11
|
-
|
12
|
-
if source && file && CONFIG.match(file)
|
13
|
-
args[0] = Processor.call(source)
|
14
|
-
end
|
15
|
-
|
16
|
-
super
|
17
|
-
end
|
18
|
-
|
19
|
-
#: (String, ?String, ?Integer) -> void
|
20
|
-
#: () { () -> void } -> void
|
21
|
-
def module_eval(*args)
|
22
|
-
source, file, lineno = args
|
23
|
-
|
24
|
-
file ||= caller_locations(1, 1).first.path
|
25
|
-
|
26
|
-
if source && file && CONFIG.match(file)
|
27
|
-
args[0] = Processor.call(source)
|
28
|
-
end
|
29
|
-
|
30
|
-
super
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
module InstanceEvalPatch
|
35
|
-
#: (String, ?String, ?Integer) -> void
|
36
|
-
#: () { () -> void } -> void
|
37
|
-
def instance_eval(*args)
|
38
|
-
source, file, lineno = args
|
39
|
-
|
40
|
-
file ||= caller_locations(1, 1).first.path
|
41
|
-
|
42
|
-
if source && file && CONFIG.match(file)
|
43
|
-
args[0] = Processor.call(source)
|
44
|
-
end
|
45
|
-
|
46
|
-
super
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
module KernelEvalPatch
|
51
|
-
#: (String, Binding, ?String, ?Integer) -> void
|
52
|
-
def eval(*args)
|
53
|
-
source, binding, file, lineno = args
|
54
|
-
|
55
|
-
file ||= caller_locations(1, 1).first.path
|
56
|
-
|
57
|
-
if source && file && CONFIG.match(file)
|
58
|
-
args[0] = Processor.call(source.to_s)
|
59
|
-
end
|
60
|
-
|
61
|
-
super
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
module BindingEvalPatch
|
66
|
-
#: (String, ?String, ?Integer) -> void
|
67
|
-
def eval(*args)
|
68
|
-
source, file, lineno = args
|
69
|
-
|
70
|
-
file ||= caller_locations(1, 1).first.path
|
71
|
-
|
72
|
-
if source && file && CONFIG.match(file)
|
73
|
-
args[0] = Processor.call(source.to_s)
|
74
|
-
end
|
75
|
-
|
76
|
-
super
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
Kernel.prepend(KernelEvalPatch)
|
81
|
-
Module.prepend(ModuleEvalPatch)
|
82
|
-
Binding.prepend(BindingEvalPatch)
|
83
|
-
BasicObject.prepend(InstanceEvalPatch)
|
84
|
-
end
|