Skip to content

Commit

Permalink
Implement MemoizedHelpers#let?
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepingkingstudios committed Jun 9, 2024
1 parent 81fd0fb commit 4905c55
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 11 deletions.
20 changes: 20 additions & 0 deletions lib/rspec/sleeping_king_studios/deferred/dsl/memoized_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def self.extended(other)
)
end

# @api private
def call(example_group)
super

Expand Down Expand Up @@ -66,6 +67,25 @@ def let!(helper_name, &)
before(:example) { send(helper_name) }
end

# Defines an optional memoized helper.
#
# The helper will use the parent value if defined; otherwise, will use the
# given value.
#
# @param helper_name [String, Symbol] the name of the helper method.
# @param block [Block] the implementation of the helper method.
#
# @return [void]
def let?(helper_name, &block)
wrapped = lambda do
next super() if defined?(super())

instance_exec(&block)
end

let(helper_name, &wrapped)
end

# Defines a memoized subject helper.
#
# @param helper_name [String, Symbol] the name of the helper method.
Expand Down
9 changes: 9 additions & 0 deletions spec/integration/deferred/optional_helpers_spec.fixture.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

require 'support/integration/deferred/orbit_examples'

RSpec.describe Spec::Models::Rocket do
subject(:rocket) { described_class.new('Imp IV') }

include Spec::Integration::Deferred::OrbitExamples
end
27 changes: 27 additions & 0 deletions spec/integration/deferred/optional_helpers_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

require 'rspec/sleeping_king_studios/deferred/examples'

require 'support/sandbox'

RSpec.describe RSpec::SleepingKingStudios::Deferred::Examples do
let(:fixture_file) do
%w[spec/integration/deferred/optional_helpers_spec.fixture.rb]
end
let(:result) do
Spec::Support::Sandbox.run(fixture_file)
end
let(:expected_examples) do
<<~EXAMPLES.lines.map(&:strip)
Spec::Models::Rocket#launch when the rocket is launched should set the orbit
Spec::Models::Rocket#launch with orbit: value when the rocket is launched should set the orbit
Spec::Models::Rocket#orbit is expected to equal nil
EXAMPLES
end

it 'should apply the deferred examples', :aggregate_failures do
expect(result.summary).to be == '3 examples, 0 failures'

expect(result.example_descriptions).to be == expected_examples
end
end
47 changes: 47 additions & 0 deletions spec/support/integration/deferred/orbit_examples.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

require 'support/integration/deferred'

module Spec::Integration::Deferred
module ShouldSetTheOrbit
include RSpec::SleepingKingStudios::Deferred::Examples

let?(:expected_orbit) { nil }

it 'should set the orbit' do
launch_rocket

expect(subject.orbit).to eq expected_orbit
end
end

module OrbitExamples
include RSpec::SleepingKingStudios::Deferred::Examples

describe '#launch' do
let(:launch_site) { 'KSC' }
let(:options) { {} }

def launch_rocket
subject.launch(launch_site:, **options)
end

context 'when the rocket is launched' do # rubocop:disable RSpec/EmptyExampleGroup
include Spec::Integration::Deferred::ShouldSetTheOrbit
end

describe 'with orbit: value' do
let(:expected_orbit) { options[:orbit] }
let(:options) { { orbit: { periapsis: '100 km', apoapsis: '300 km' } } }

context 'when the rocket is launched' do # rubocop:disable RSpec/EmptyExampleGroup
include Spec::Integration::Deferred::ShouldSetTheOrbit
end
end
end

describe '#orbit' do
it { expect(subject.orbit).to be nil }
end
end
end
5 changes: 4 additions & 1 deletion spec/support/models/rocket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@ def initialize(name)

@launched = false
@launch_site = nil
@orbit = nil
@payload = {}
end

attr_reader \
:launch_site,
:orbit,
:payload

def launch(launch_site:, payload: {})
def launch(launch_site:, orbit: nil, payload: {})
@launched = true
@launch_site = launch_site
@orbit = orbit
@payload = payload
end

Expand Down
38 changes: 28 additions & 10 deletions spec/support/shared_examples/deferred_examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ module DeferredExamples

shared_examples 'should define memoized helpers' do
shared_examples 'should define a memoized helper macro' \
do |method_name, before: false, subject: false|
do |method_name, before: false, optional: false, subject: false|
shared_context 'when the helper is defined' do
before(:example) { define_helper }
end
Expand Down Expand Up @@ -545,19 +545,27 @@ def call_helper
parent_group.let(helper_name, &existing_block)
end

include_examples 'should call and memoize the block value'
if optional
include_examples 'should call the existing implementation'
else
include_examples 'should call and memoize the block value'

include_examples 'should wrap the existing implementation'
include_examples 'should wrap the existing implementation'
end
end

context 'when a method is declared in a parent example group' do
before(:example) do
parent_group.define_method(helper_name, &existing_block)
end

include_examples 'should call and memoize the block value'
if optional
include_examples 'should call the existing implementation'
else
include_examples 'should call and memoize the block value'

include_examples 'should wrap the existing implementation'
include_examples 'should wrap the existing implementation'
end
end

context 'when a helper is defined in the example scope' do
Expand Down Expand Up @@ -597,15 +605,23 @@ def call_helper
ancestor_class.let(helper_name) { -1 }
end

include_examples 'should call and memoize the block value'
if optional
include_examples 'should call the existing implementation'
else
include_examples 'should call and memoize the block value'
end
end

context 'when a method is defined in inherited examples' do
before(:example) do
ancestor_class.define_method(helper_name) { -1 }
end

include_examples 'should call and memoize the block value'
if optional
include_examples 'should call the existing implementation'
else
include_examples 'should call and memoize the block value'
end
end

if before
Expand Down Expand Up @@ -717,9 +733,11 @@ def call_helper
before: true
end

# describe '.let?' do
# pending
# end
describe '.let?' do
include_examples 'should define a memoized helper macro',
:let?,
optional: true
end

describe '.subject' do
include_examples 'should define a memoized helper macro',
Expand Down

0 comments on commit 4905c55

Please sign in to comment.