Skip to content

Commit

Permalink
Merge pull request #9 from nerdrew/cvar-on-injecting-class
Browse files Browse the repository at this point in the history
only set static dep on defining class
  • Loading branch information
nerdrew authored Jun 29, 2022
2 parents 02b16f5 + dccf33a commit 0629229
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 6 deletions.
26 changes: 26 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Change Log

# v1.2.0

- Ruby 3.x made it an error to override a class variable in a parent class. There was a bug with `inject_static` where
if a subclass was the first to call a static dependency, the class variable would only be set on that subclass (and
children of that subsclass). If the injecting class then called the static dependency, it would override the already
set child's class variable, which is now an error.

```ruby
class Parent
include Interjectable
inject_static(:boom) { "goats" }
end

class Child < Parent; end

Child.boom # => sets Child's @@boom = "goats"
Parent.boom # => sets Parent's @@boom = "goats" and *clear* Child's @@boom.
Child.boom # => Error on Ruby 3.x because you are trying to read an overriden class variable.
```

Fix: always set the class variable on the class that called `inject_static`.

# v1.1.3

- Fix `test_inject` for sub-sub-classes.

# v1.1.2

- Fix visibility issue with `Module.define_method` for Ruby < 2.5.0.
Expand Down
12 changes: 7 additions & 5 deletions lib/interjectable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,26 +64,28 @@ def inject_static(dependency, &default_block)
raise MethodAlreadyDefined, "#{dependency} is already defined"
end

injecting_class = self

cvar_name = :"@@#{dependency}"
setter = :"#{dependency}="

define_method(setter) do |value|
self.class.send(setter, value)
injecting_class.send(setter, value)
end

define_singleton_method(setter) do |value|
class_variable_set(cvar_name, value)
injecting_class.class_variable_set(cvar_name, value)
end

define_method(dependency) do
self.class.send(dependency)
injecting_class.send(dependency)
end

define_singleton_method(dependency) do
if class_variable_defined?(cvar_name)
class_variable_get(cvar_name)
injecting_class.class_variable_get(cvar_name)
else
class_variable_set(cvar_name, instance_eval(&default_block))
injecting_class.class_variable_set(cvar_name, instance_eval(&default_block))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/interjectable/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Interjectable
VERSION = "1.1.3"
VERSION = "1.2.0"
end
11 changes: 11 additions & 0 deletions spec/interjectable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,17 @@
expect(subclass_instance.static_dependency).to eq(:some_other_value)
expect(subclass.static_dependency).to eq(:some_other_value)
end

it "does not error when lazily setting the dep from a subclass first" do
expect(subclass.static_dependency).to eq(:some_value)
expect(klass.static_dependency).to eq(:some_value)
expect(subclass.static_dependency).to eq(:some_value)
end

it "only defines the class variable on the injecting class" do
expect(subclass.static_dependency).to eq(:some_value)
expect(klass.class_variable_get(:@@static_dependency)).to eq(:some_value)
end
end
end
end
Expand Down

0 comments on commit 0629229

Please sign in to comment.