Skip to content

Commit

Permalink
Refactor in order to add support for class variables
Browse files Browse the repository at this point in the history
  • Loading branch information
svoop committed Aug 24, 2023
1 parent 2188a0a commit def3780
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 38 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Main

Nothing so far
#### Additions
* Refactor to support class variables as well

## 0.1.0

Expand Down
31 changes: 20 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,6 @@ require 'minitest/substitute'

## Usage

This lightweight gem implements features on a "as needed" basis, as of now:

* substitution of instance variables
* substitution of global variables

Please [create an issue describing your use case](https://github.com/svoop/minitest-substitute/issues) in case you need more features such as the substitution of class variables or substitution via accessor methods.

### Block

To substitute the value of an instance variable for the duration of a block:
Expand All @@ -59,11 +52,27 @@ class Config
end
end

Config.instance_variable_get('@version') # => 1
with '@version', 2, on: Config do
Config.instance_variable_get('@version') # => 2
config = Config.new

config.instance_variable_get('@version') # => 1
with '@version', 2, on: config do
config.instance_variable_get('@version') # => 2
end
config.instance_variable_get('@version') # => 1
```

Class variables can be substituted as well:

```ruby
class Config
@@counter = 0
end

Config.class_variable_get('@@counter') # => 0
with '@@counter', 42, on: Config do
Config.class_variable_get('@@counter') # => 42
end
Config.instance_variable_get('@version') # => 1
Config.class_variable_get('@@counter') # => 0
```

Same goes for global variables:
Expand Down
1 change: 1 addition & 0 deletions lib/minitest/substitute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_relative 'substitute/version'

require_relative 'substitute/substitutor'
require_relative 'substitute/with'
require_relative 'substitute/spec'

Expand Down
11 changes: 5 additions & 6 deletions lib/minitest/substitute/spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
require 'minitest/spec'

Minitest::Spec::DSL.class_eval do
class Substitutor
include Minitest::Substitute::With
end

# Substitute the variable value for the duration of the current description
#
Expand All @@ -14,12 +11,13 @@ class Substitutor
# @param on [Object, nil] substitute in the context of this object
# @yield temporary substitution value (takes precedence over +substitute+ param)
def with(variable, substitute=nil, on: self)
substitute = yield if block_given?
substitutor = Minitest::Substitute::Substitutor.new(variable, substitute, on: on)
before do
substitute = yield if block_given?
Substitutor.send(:commit_substitution, variable, substitute, on: on)
substitutor.commit
end
after do
Substitutor.send(:rollback_substitution, variable, on: on)
substitutor.rollback
end
end

Expand All @@ -44,4 +42,5 @@ def after(_type=nil, &block)
end
end) #.then &:include
end

end
38 changes: 38 additions & 0 deletions lib/minitest/substitute/substitutor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

module Minitest
module Substitute
class Substitutor

EVAL_METHODS = [:instance_eval, :instance_eval, :class_eval].freeze

def initialize(variable, substitute, on:)
@variable, @substitute, @on = variable, substitute, on
@original = get
end

def commit
set @substitute
end

def rollback
set @original
end

private

def eval_method
EVAL_METHODS[@variable.count('@')]
end

def get
@on.send(eval_method, @variable.to_s)
end

def set(value)
@on.send(eval_method, "#{@variable} = value")
end

end
end
end
19 changes: 3 additions & 16 deletions lib/minitest/substitute/with.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
module Minitest
module Substitute
module With
@original = {}
class << self
attr_accessor :original
end

# Substitute the variable value for the duration of the given block
#
Expand All @@ -16,22 +12,13 @@ class << self
# @yield block during which the substitution is made
# @return [Object] return value of the yielded block
def with(variable, substitute, on: self)
commit_substitution(variable, substitute, on: on)
substitutor = Minitest::Substitute::Substitutor.new(variable, substitute, on: on)
substitutor.commit
yield.tap do
rollback_substitution(variable, on: on)
substitutor.rollback
end
end

private

def commit_substitution(variable, substitute, on:)
Minitest::Substitute::With.original[variable.hash] = on.instance_eval variable.to_s
on.instance_eval "#{variable} = substitute"
end

def rollback_substitution(variable, on:)
on.instance_eval "#{variable} = Minitest::Substitute::With.original.delete(variable.hash)"
end
end
end
end
2 changes: 2 additions & 0 deletions spec/factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ def initialize
@version = 1
@released_on = '2023-08-24'
end

@@counter = 0
end

$_spec_config_instance = Config.new
Expand Down
26 changes: 22 additions & 4 deletions spec/lib/minitest/substitute/spec_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
require_relative '../../../factory'

describe :with do
10.times do # test value reset even though order is random
10.times do # test rollback even though order is random
describe "instance variable" do
context 'untouched' do
it "returns the original value" do
Expand All @@ -22,7 +22,25 @@
end
end

10.times do # test value reset even though order is random
10.times do # test rollback even though order is random
describe "class variable" do
context 'untouched' do
it "returns the original value" do
_(Config.class_variable_get(:@@counter)).must_equal 0
end
end

context 'substituted' do
with '@@counter', 42, on: Config

it "returns the substitute value" do
_(Config.class_variable_get(:@@counter)).must_equal 42
end
end
end
end

10.times do # test rollback even though order is random
describe "global variable" do
context 'untouched' do
it "returns the original value" do
Expand All @@ -40,7 +58,7 @@
end
end

10.times do # test value reset even though order is random
10.times do # test rollback even though order is random
describe "environment variable" do
context 'untouched' do
it "returns the original value" do
Expand All @@ -58,7 +76,7 @@
end
end

10.times do # test value reset even though order is random
10.times do # test rollback even though order is random
describe "multiple substitutions" do
context 'untouched' do
it "returns the original values" do
Expand Down
26 changes: 26 additions & 0 deletions spec/lib/minitest/substitute/substitutor_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require_relative '../../../spec_helper'
require_relative '../../../factory'

describe :eval_method do
subject do
Minitest::Substitute::Substitutor
end

it "returns instance_eval for instance variables" do
_(subject.new('@version', nil, on: $_spec_config_instance).send(:eval_method)).must_equal :instance_eval
end

it "returns class_eval for class variables" do
_(subject.new('@@counter', nil, on: Config).send(:eval_method)).must_equal :class_eval
end

it "returns instance_eval for global variables" do
_(subject.new('$foobar', nil, on: Object).send(:eval_method)).must_equal :instance_eval
end

it "returns instance_eval for global accessors" do
_(subject.new('ENV', nil, on: Object).send(:eval_method)).must_equal :instance_eval
end
end
10 changes: 10 additions & 0 deletions spec/lib/minitest/substitute/with_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@
end
end

describe "class variable" do
it "substitutes the value for the duration of a block" do
_(Config.class_variable_get(:@@counter)).must_equal 0
with '@@counter', 42, on: Config do
_(Config.class_variable_get(:@@counter)).must_equal 42
end
_(Config.class_variable_get(:@@counter)).must_equal 0
end
end

describe "global variable" do
it "substitutes the value for the duration of a block" do
_($_spec_global_variable).must_equal :original
Expand Down

0 comments on commit def3780

Please sign in to comment.