From 53e891d760adc796b27d297f354f9692a19d4dac Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 10:56:16 +0000 Subject: [PATCH 01/12] Add frozen string literal magic comments --- Gemfile | 2 ++ Rakefile | 2 ++ features/step_definitions/additional_cli_steps.rb | 2 ++ features/support/env.rb | 2 ++ lib/rspec/its.rb | 2 ++ lib/rspec/its/version.rb | 2 ++ rspec-its.gemspec | 3 ++- spec/rspec/its_spec.rb | 2 ++ spec/spec_helper.rb | 2 ++ 9 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index b9feb99..4225e6f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'https://rubygems.org' # Specify your gem's dependencies in rspec-its.gemspec diff --git a/Rakefile b/Rakefile index 7a954d2..11ea87e 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "bundler" Bundler.setup Bundler::GemHelper.install_tasks diff --git a/features/step_definitions/additional_cli_steps.rb b/features/step_definitions/additional_cli_steps.rb index 25a1476..149c14b 100644 --- a/features/step_definitions/additional_cli_steps.rb +++ b/features/step_definitions/additional_cli_steps.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + When /^I run rspec( with the documentation option)?$/ do |documentation| rspec_its_gem_location = File.expand_path('../../../lib/rspec/its', __FILE__) require_option = "--require #{rspec_its_gem_location}" diff --git a/features/support/env.rb b/features/support/env.rb index 3d09d6c..b0016e2 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'aruba/cucumber' require 'rspec/core' require 'rspec/its' diff --git a/lib/rspec/its.rb b/lib/rspec/its.rb index 6d357a1..4be0f84 100644 --- a/lib/rspec/its.rb +++ b/lib/rspec/its.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rspec/its/version' require 'rspec/core' diff --git a/lib/rspec/its/version.rb b/lib/rspec/its/version.rb index 88da640..200c906 100644 --- a/lib/rspec/its/version.rb +++ b/lib/rspec/its/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module RSpec module Its VERSION = "1.3.1" diff --git a/rspec-its.gemspec b/rspec-its.gemspec index be6e032..5ab9a48 100644 --- a/rspec-its.gemspec +++ b/rspec-its.gemspec @@ -1,4 +1,5 @@ -# coding: utf-8 +# frozen_string_literal: true + lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'rspec/its/version' diff --git a/spec/rspec/its_spec.rb b/spec/rspec/its_spec.rb index 9f894df..65ef828 100644 --- a/spec/rspec/its_spec.rb +++ b/spec/rspec/its_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' module RSpec diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3433ee9..d3d7b35 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rspec/its' Dir['./spec/support/**/*'].each {|f| require f} From 33cd2813e8e963b26eefef49d15e6f7dccda1d48 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 16:34:35 +0000 Subject: [PATCH 02/12] Assorted formatting tweaks --- lib/rspec/its.rb | 1 - script/update_rubygems_and_install_bundler | 2 - spec/rspec/its_spec.rb | 614 ++++++++++----------- spec/spec_helper.rb | 7 +- 4 files changed, 312 insertions(+), 312 deletions(-) diff --git a/lib/rspec/its.rb b/lib/rspec/its.rb index 4be0f84..2eb9e54 100644 --- a/lib/rspec/its.rb +++ b/lib/rspec/its.rb @@ -7,7 +7,6 @@ module RSpec module Its - # Creates a nested example group named by the submitted `attribute`, # and then generates an example using the submitted block. # diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index c1be703..1d10da9 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,6 +1,4 @@ #!/bin/bash -# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. -# DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e diff --git a/spec/rspec/its_spec.rb b/spec/rspec/its_spec.rb index 65ef828..94b8a6f 100644 --- a/spec/rspec/its_spec.rb +++ b/spec/rspec/its_spec.rb @@ -2,395 +2,393 @@ require 'spec_helper' -module RSpec - describe Its do - describe "#its" do - context "with implicit subject" do - context "preserves described_class" do - its(:symbol) { expect(described_class).to be Its } - its([]) { expect(described_class).to be Its } - end - end - - context "with explicit subject" do - subject do - Class.new do - def initialize - @call_count = 0 - end +RSpec.describe RSpec::Its do + context "with implicit subject" do + context "preserves described_class" do + its(:symbol) { expect(described_class).to be RSpec::Its } + its([]) { expect(described_class).to be RSpec::Its } + end + end - def call_count - @call_count += 1 - end - end.new + context "with explicit subject" do + subject do + Class.new do + def initialize + @call_count = 0 end - before(:each, :meta) do - subject.call_count + def call_count + @call_count += 1 end + end.new + end - context "with some metadata" do - its(:call_count, :meta) { should eq(2) } - end + before(:each, :meta) do + subject.call_count + end - context "with a call counter" do - its(:call_count) { should eq(1) } - end + context "with some metadata" do + its(:call_count, :meta) { should eq(2) } + end - context "with nil value" do - subject do - Class.new do - def nil_value - nil - end - end.new - end - its(:nil_value) { should be_nil } - end + context "with a call counter" do + its(:call_count) { should eq(1) } + end - context "with nested attributes" do - subject do - Class.new do - def name - "John" - end - end.new - end - its("name") { should eq("John") } - its("name.size") { should eq(4) } - if RUBY_VERSION >= "2.4.0" - its("name.size.class") { should eq(Integer) } - else - its("name.size.class") { should eq(Fixnum) } + context "with nil value" do + subject do + Class.new do + def nil_value + nil end + end.new + end + its(:nil_value) { should be_nil } + end - context "using should_not" do - its("name") { should_not eq("Paul") } + context "with nested attributes" do + subject do + Class.new do + def name + "John" end + end.new + end + its("name") { should eq("John") } + its("name.size") { should eq(4) } + if RUBY_VERSION >= "2.4.0" + its("name.size.class") { should eq(Integer) } + else + its("name.size.class") { should eq(Fixnum) } + end - context "using is_expected" do - its("name") { is_expected.to eq("John") } - end + context "using should_not" do + its("name") { should_not eq("Paul") } + end - context "using will_not" do - its("name") { will_not raise_error } - end + context "using is_expected" do + its("name") { is_expected.to eq("John") } + end + + context "using will_not" do + its("name") { will_not raise_error } + end + + context "using are_expected" do + its("name.chars.to_a") { are_expected.to eq(%w[J o h n]) } + end + end - context "using are_expected" do - its("name.chars.to_a") { are_expected.to eq(%w[J o h n]) } + context "when it responds to #[]" do + subject do + Class.new do + def [](*objects) + objects.map do |object| + "#{object.class}: #{object.to_s}" + end.join("; ") end - end - context "when it responds to #[]" do - subject do - Class.new do - def [](*objects) - objects.map do |object| - "#{object.class}: #{object.to_s}" - end.join("; ") - end - - def name - "George" - end - end.new + def name + "George" end - its([:a]) { should eq("Symbol: a") } - its(['a']) { should eq("String: a") } - if RUBY_VERSION >= "2.4.0" - its([:b, 'c', 4]) { should eq("Symbol: b; String: c; Integer: 4") } - else - its([:b, 'c', 4]) { should eq("Symbol: b; String: c; Fixnum: 4") } + end.new + end + its([:a]) { should eq("Symbol: a") } + its(['a']) { should eq("String: a") } + if RUBY_VERSION >= "2.4.0" + its([:b, 'c', 4]) { should eq("Symbol: b; String: c; Integer: 4") } + else + its([:b, 'c', 4]) { should eq("Symbol: b; String: c; Fixnum: 4") } + end + its(:name) { should eq("George") } + context "when referring to an attribute that doesn't exist" do + context "it raises an error" do + its(:age) do + expect do + should eq(64) + end.to raise_error(NoMethodError) end - its(:name) { should eq("George") } - context "when referring to an attribute that doesn't exist" do - context "it raises an error" do - its(:age) do - expect do - should eq(64) - end.to raise_error(NoMethodError) - end - - context "using will" do - its(:age) { will raise_error(NoMethodError) } - end - end + + context "using will" do + its(:age) { will raise_error(NoMethodError) } end + end + end - context "when it's a hash" do - subject { {:a => {:deep => {:key => "value"}}} } + context "when it's a hash" do + subject { {:a => {:deep => {:key => "value"}}} } - its([:a]) { should eq({:deep => {:key => "value"}}) } - its([:a, :deep]) { should eq({:key => "value"}) } - its([:a, :deep, :key]) { should eq("value") } + its([:a]) { should eq({:deep => {:key => "value"}}) } + its([:a, :deep]) { should eq({:key => "value"}) } + its([:a, :deep, :key]) { should eq("value") } - context "when referring to a key that doesn't exist" do - its([:not_here]) { should be_nil } - its([:a, :ghost]) { should be_nil } - its([:deep, :ghost]) { expect { should eq("missing") }.to raise_error(NoMethodError) } + context "when referring to a key that doesn't exist" do + its([:not_here]) { should be_nil } + its([:a, :ghost]) { should be_nil } + its([:deep, :ghost]) { expect { should eq("missing") }.to raise_error(NoMethodError) } - context "using will" do - its([:deep, :ghost]) { will raise_error(NoMethodError) } - end - end + context "using will" do + its([:deep, :ghost]) { will raise_error(NoMethodError) } end end + end + end - context "when it does not respond to #[]" do - subject { Object.new } - - context "it raises an error" do - its([:a]) do - expect do - should eq("Symbol: a") - end.to raise_error(NoMethodError) - end + context "when it does not respond to #[]" do + subject { Object.new } - context "using will" do - its([:a]) { will raise_error(NoMethodError) } - end - end + context "it raises an error" do + its([:a]) do + expect do + should eq("Symbol: a") + end.to raise_error(NoMethodError) end - context "calling and overriding super" do - it "calls to the subject defined in the parent group" do - group = RSpec::Core::ExampleGroup.describe(Array) do - subject { [1, 'a'] } + context "using will" do + its([:a]) { will raise_error(NoMethodError) } + end + end + end - its(:last) { should eq("a") } + context "calling and overriding super" do + it "calls to the subject defined in the parent group" do + group = RSpec::Core::ExampleGroup.describe(Array) do + subject { [1, 'a'] } - describe '.first' do - def subject; - super().first; - end + its(:last) { should eq("a") } - its(:next) { should eq(2) } - end + describe '.first' do + def subject; + super().first; end - expect(group.run(NullFormatter.new)).to be_truthy + its(:next) { should eq(2) } end end - context "with nil subject" do - subject do - Class.new do - def initialize - @counter = -1 - end - - def nil_if_first_time - @counter += 1 - @counter == 0 ? nil : true - end - end.new + expect(group.run(NullFormatter.new)).to be_truthy + end + end + + context "with nil subject" do + subject do + Class.new do + def initialize + @counter = -1 end - its(:nil_if_first_time) { should be(nil) } - end - context "with false subject" do - subject do - Class.new do - def initialize - @counter = -1 - end - - def false_if_first_time - @counter += 1 - @counter > 0 - end - end.new + def nil_if_first_time + @counter += 1 + @counter == 0 ? nil : true end - its(:false_if_first_time) { should be(false) } - end + end.new + end + its(:nil_if_first_time) { should be(nil) } + end - describe 'accessing `subject` in `before` and `let`' do - subject { 'my subject' } - before { @subject_in_before = subject } - let(:subject_in_let) { subject } - let!(:eager_loaded_subject_in_let) { subject } - - # These examples read weird, because we're actually - # specifying the behaviour of `its` itself - its(nil) { expect(subject).to eq('my subject') } - its(nil) { expect(@subject_in_before).to eq('my subject') } - its(nil) { expect(subject_in_let).to eq('my subject') } - its(nil) { expect(eager_loaded_subject_in_let).to eq('my subject') } - end + context "with false subject" do + subject do + Class.new do + def initialize + @counter = -1 + end - describe "in shared_context" do - shared_context "shared stuff" do - subject { Array } - its(:name) { should eq "Array" } + def false_if_first_time + @counter += 1 + @counter > 0 end + end.new + end + its(:false_if_first_time) { should be(false) } + end - include_context "shared stuff" - end + describe 'accessing `subject` in `before` and `let`' do + subject { 'my subject' } + before { @subject_in_before = subject } + let(:subject_in_let) { subject } + let!(:eager_loaded_subject_in_let) { subject } + + # These examples read weird, because we're actually + # specifying the behaviour of `its` itself + its(nil) { expect(subject).to eq('my subject') } + its(nil) { expect(@subject_in_before).to eq('my subject') } + its(nil) { expect(subject_in_let).to eq('my subject') } + its(nil) { expect(eager_loaded_subject_in_let).to eq('my subject') } + end - describe "when extending SharedContext" do - it 'works with an implicit subject' do - shared = Module.new do - extend RSpec::SharedContext - its(:size) { should eq 0 } - end - group = RSpec::Core::ExampleGroup.describe(Array) do - include shared - end + describe "in shared_context" do + shared_context "shared stuff" do + subject { Array } + its(:name) { should eq "Array" } + end - group.run(NullFormatter.new) + include_context "shared stuff" + end - result = group.children.first.examples.first.execution_result - # Following conditional needed to work across mix of RSpec and ruby versions without warning - status = result.respond_to?(:status) ? result.status : result[:status].to_sym - expect(status).to eq(:passed) - end + describe "when extending SharedContext" do + it 'works with an implicit subject' do + shared = Module.new do + extend RSpec::SharedContext + its(:size) { should eq 0 } end - end - context "with metadata" do - context "preserves access to metadata that doesn't end in hash" do - its([], :foo) do |example| - expect(example.metadata[:foo]).to be(true) - end + group = RSpec::Core::ExampleGroup.describe(Array) do + include shared end - context "preserves access to metadata that ends in hash" do - its([], :foo, :bar => 17) do |example| - expect(example.metadata[:foo]).to be(true) - expect(example.metadata[:bar]).to be(17) - end - end - end - context "when expecting errors" do - subject do - Class.new do - def good; end + group.run(NullFormatter.new) - def bad - raise ArgumentError, "message" - end - end.new - end - - its(:good) { will_not raise_error } - its(:bad) { will raise_error(ArgumentError) } - its(:bad) { will raise_error("message") } - its(:bad) { will raise_error(ArgumentError, "message") } + result = group.children.first.examples.first.execution_result + # Following conditional needed to work across mix of RSpec and ruby versions without warning + status = result.respond_to?(:status) ? result.status : result[:status].to_sym + expect(status).to eq(:passed) end + end + end + context "with metadata" do + context "preserves access to metadata that doesn't end in hash" do + its([], :foo) do |example| + expect(example.metadata[:foo]).to be(true) + end + end + context "preserves access to metadata that ends in hash" do + its([], :foo, :bar => 17) do |example| + expect(example.metadata[:foo]).to be(true) + expect(example.metadata[:bar]).to be(17) + end + end + end - context "when expecting throws" do - subject do - Class.new do - def good; end + context "when expecting errors" do + subject do + Class.new do + def good; end - def bad - throw :abort, "message" - end - end.new + def bad + raise ArgumentError, "message" end + end.new + end - its(:good) { will_not throw_symbol } - its(:bad) { will throw_symbol } - its(:bad) { will throw_symbol(:abort) } - its(:bad) { will throw_symbol(:abort, "message") } - end - - context "with change observation" do - subject do - Class.new do - attr_reader :count - - def initialize - @count = 0 - end + its(:good) { will_not raise_error } + its(:bad) { will raise_error(ArgumentError) } + its(:bad) { will raise_error("message") } + its(:bad) { will raise_error(ArgumentError, "message") } + end - def increment - @count += 1 - end + context "when expecting throws" do + subject do + Class.new do + def good; end - def noop; end - end.new + def bad + throw :abort, "message" end + end.new + end - its(:increment) { will change { subject.count }.by(1) } - its(:increment) { will change { subject.count }.from(0) } - its(:increment) { will change { subject.count }.from(0).to(1) } - its(:increment) { will change { subject.count }.by_at_least(1) } - its(:increment) { will change { subject.count }.by_at_most(1) } + its(:good) { will_not throw_symbol } + its(:bad) { will throw_symbol } + its(:bad) { will throw_symbol(:abort) } + its(:bad) { will throw_symbol(:abort, "message") } + end - its(:noop) { will_not change { subject.count } } - its(:noop) { will_not change { subject.count }.from(0) } + context "with change observation" do + subject do + Class.new do + attr_reader :count - its(:increment) do - expect { will_not change { subject.count }.by(0) }.to \ - raise_error(NotImplementedError, '`expect { }.not_to change { }.by()` is not supported') + def initialize + @count = 0 end - its(:increment) do - expect { will_not change { subject.count }.by_at_least(2) }.to \ - raise_error(NotImplementedError, '`expect { }.not_to change { }.by_at_least()` is not supported') + def increment + @count += 1 end - its(:increment) do - expect { will_not change { subject.count }.by_at_most(3) }.to \ - raise_error(NotImplementedError, '`expect { }.not_to change { }.by_at_most()` is not supported') - end - end + def noop; end + end.new + end - context "with output capture" do - subject do - Class.new do - def stdout - print "some output" - end + its(:increment) { will change { subject.count }.by(1) } + its(:increment) { will change { subject.count }.from(0) } + its(:increment) { will change { subject.count }.from(0).to(1) } + its(:increment) { will change { subject.count }.by_at_least(1) } + its(:increment) { will change { subject.count }.by_at_most(1) } - def stderr - $stderr.print "some error" - end + its(:noop) { will_not change { subject.count } } + its(:noop) { will_not change { subject.count }.from(0) } - def noop; end - end.new - end + its(:increment) do + expect { will_not change { subject.count }.by(0) }.to \ + raise_error(NotImplementedError, '`expect { }.not_to change { }.by()` is not supported') + end - its(:stdout) { will output("some output").to_stdout } - its(:stderr) { will output("some error").to_stderr } + its(:increment) do + expect { will_not change { subject.count }.by_at_least(2) }.to \ + raise_error(NotImplementedError, '`expect { }.not_to change { }.by_at_least()` is not supported') + end - its(:noop) { will_not output("some error").to_stderr } - its(:noop) { will_not output("some output").to_stdout } - end + its(:increment) do + expect { will_not change { subject.count }.by_at_most(3) }.to \ + raise_error(NotImplementedError, '`expect { }.not_to change { }.by_at_most()` is not supported') + end + end - context "#will with non block expectations" do - subject do - Class.new do - def terminator - "back" - end - end.new + context "with output capture" do + subject do + Class.new do + def stdout + print "some output" end - its(:terminator) do - expect { will be("back") }.to \ - raise_error(ArgumentError, '`will` only supports block expectations') + def stderr + $stderr.print "some error" end - its(:terminator) do - expect { will_not be("back") }.to \ - raise_error(ArgumentError, '`will_not` only supports block expectations') - end - end + def noop; end + end.new + end - context "when example is redefined" do - subject do - Class.new do - def will_still_work; true; end - end.new + its(:stdout) { will output("some output").to_stdout } + its(:stderr) { will output("some error").to_stderr } + + its(:noop) { will_not output("some error").to_stderr } + its(:noop) { will_not output("some output").to_stdout } + end + + context "#will with non block expectations" do + subject do + Class.new do + def terminator + "back" end + end.new + end + + its(:terminator) do + expect { will be("back") }.to \ + raise_error(ArgumentError, '`will` only supports block expectations') + end + + its(:terminator) do + expect { will_not be("back") }.to \ + raise_error(ArgumentError, '`will_not` only supports block expectations') + end + end - def self.example(*_args) - raise + context "when example is redefined" do + subject do + Class.new do + def will_still_work + true end + end.new + end - its(:will_still_work) { is_expected.to be true } - end + def self.example(*_args) + raise end + + its(:will_still_work) { is_expected.to be true } end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d3d7b35..20e85f8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,13 +2,18 @@ require 'rspec/its' -Dir['./spec/support/**/*'].each {|f| require f} +Dir['./spec/support/**/*'].each { |f| require f } class NullFormatter private + def method_missing(method, *args, &block) # ignore end + + def respond_to_missing?(method, *args, &block) + # ignore + end end RSpec.configure do |config| From 10d8552489668cfe6be6895f7cc9fcb288f7f560 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 21:15:30 +0000 Subject: [PATCH 03/12] Support versions are now Ruby 3 and latest version of RSpec --- .github/workflows/ci.yml | 96 +++------------------- Gemfile | 36 +++----- features/its.feature | 33 -------- rspec-its.gemspec | 45 +++------- script/legacy_setup.sh | 16 ---- script/test_all | 10 +-- script/update_rubygems_and_install_bundler | 32 +------- spec/rspec/its_spec.rb | 12 +-- 8 files changed, 41 insertions(+), 239 deletions(-) delete mode 100755 script/legacy_setup.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94e8dc5..852882a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,6 @@ on: branches: - '*' env: - BRANCH: '3-12-maintenance' RSPEC_CI: true jobs: test: @@ -18,77 +17,30 @@ jobs: strategy: matrix: ruby: + - '3.3' - '3.2' - '3.1' - '3.0' - - 2.7 - - 2.6 - - 2.5 - - 2.4 - - 2.3 - - 2.2 - - 2.1.9 env: - BRANCH: "main" include: - - ruby: jruby-9.2.13.0 + - ruby: '3.3' + name_extra: "against RSpec 3.13" env: - JRUBY_OPTS: "--dev" - # Compatibility builds + BRANCH: "3-13-maintenance" - ruby: '3.2' - name_extra: "against RSpec 3.12" + name_extra: "against RSpec 3.13" env: - BRANCH: "3-12-maintenance" + BRANCH: "3-13-maintenance" - ruby: '3.1' - name_extra: "against RSpec 3.11" + name_extra: "against RSpec 3.13" env: - BRANCH: "3-11-maintenance" - - ruby: '3.1' - name_extra: "against RSpec 3.10" - env: - BRANCH: "3-10-maintenance" - - ruby: '3.0' - name_extra: "against RSpec 3.9" - env: - BRANCH: "3-9-maintenance" - - ruby: '3.0' - name_extra: "against RSpec 3.8" - env: - BRANCH: "3-8-maintenance" - - ruby: '3.0' - name_extra: "against RSpec 3.7" - env: - BRANCH: "3-7-maintenance" - - ruby: '3.0' - name_extra: "against RSpec 3.6" - env: - BRANCH: "3-6-maintenance" - - ruby: '3.0' - name_extra: "against RSpec 3.5" - env: - BRANCH: "3-5-maintenance" - - ruby: '3.0' - name_extra: "against RSpec 3.4" - env: - BRANCH: "3-4-maintenance" - - ruby: '3.0' - name_extra: "against RSpec 3.3" - env: - BRANCH: "3-3-maintenance" - - ruby: '3.0' - name_extra: "against RSpec 3.2" - env: - BRANCH: "3-2-maintenance" - - ruby: '3.0' - name_extra: "against RSpec 3.1" - env: - BRANCH: "3-1-maintenance" + BRANCH: "3-13-maintenance" - ruby: '3.0' - name_extra: "against RSpec 3.0" + name_extra: "against RSpec 3.13" env: - BRANCH: "3-0-maintenance" - + BRANCH: "3-13-maintenance" fail-fast: false continue-on-error: ${{ matrix.allow_failure || endsWith(matrix.ruby, 'head') }} env: ${{ matrix.env }} @@ -101,31 +53,3 @@ jobs: - run: script/update_rubygems_and_install_bundler - run: bundle install --binstubs --standalone - run: script/test_all - - legacy: - name: Legacy Ruby Builds (${{ matrix.container.version }}) - runs-on: ubuntu-20.04 - container: - image: ${{ matrix.container.tag }} - options: ${{ matrix.container.options || '--add-host github-complains-if-this-is-empty.com:127.0.0.1' }} - strategy: - fail-fast: false - matrix: - container: - - version: "2.0" - tag: ghcr.io/rspec/docker-ci:2.0.0 - - version: "1.9.3" - tag: ghcr.io/rspec/docker-ci:1.9.3 - - version: "JRuby 1.7" - tag: ghcr.io/rspec/docker-ci:jruby-1.7 - - version: "JRuby 9.1.17.0" - tag: ghcr.io/rspec/docker-ci:jruby-9.1.17.0 - options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" - env: - ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true - LEGACY_CI: true - JRUBY_OPTS: ${{ matrix.container.jruby_opts || '--dev' }} - steps: - - uses: actions/checkout@v3 - - run: script/legacy_setup.sh - - run: script/test_all diff --git a/Gemfile b/Gemfile index 4225e6f..eb60617 100644 --- a/Gemfile +++ b/Gemfile @@ -2,39 +2,25 @@ source 'https://rubygems.org' -# Specify your gem's dependencies in rspec-its.gemspec gemspec %w[rspec rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib| - branch = ENV.fetch('BRANCH','main') + branch = ENV.fetch('BRANCH', 'main') library_path = File.expand_path("../../#{lib}", __FILE__) if File.exist?(library_path) && !ENV['USE_GIT_REPOS'] - gem lib, :path => library_path + gem lib, path: library_path elsif lib == 'rspec' - gem 'rspec', :git => "https://github.com/rspec/rspec-metagem.git", :branch => branch + gem 'rspec', git: "https://github.com/rspec/rspec-metagem.git", branch: branch else - gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => branch + gem lib, git: "https://github.com/rspec/#{lib}.git", branch: branch end end -if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) - gem 'ffi', '< 1.10' -elsif RUBY_VERSION < '2.4.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) - gem 'ffi', '< 1.15' -elsif RUBY_VERSION < '2.0' - gem 'ffi', '< 1.9.19' # ffi dropped Ruby 1.8 support in 1.9.19 -elsif RUBY_VERSION < '2.3.0' - gem 'ffi', '~> 1.12.0' -else - gem 'ffi', '~> 1.15.0' -end - -# test coverage -# gem 'simplecov', :require => false - -gem 'contracts', '< 0.16' if RUBY_VERSION < '1.9.0' - -gem 'coveralls', :require => false, :platform => :mri_20 - -eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') +gem 'aruba', '~> 0.14.12' +gem 'bundler', '> 2.0.0' +gem 'coveralls', require: false +gem 'cucumber', '>= 1.3.8' +gem 'ffi', '~> 1.15.0' +gem 'matrix', '~> 0.4.2' +gem 'rake', '~> 13.0.0' diff --git a/features/its.feature b/features/its.feature index d83f01f..8446517 100644 --- a/features/its.feature +++ b/features/its.feature @@ -1,38 +1,5 @@ Feature: attribute of subject - @pre-3-9 - Scenario: specify value of a nested attribute - Given a file named "example_spec.rb" with: - """ruby - class Person - attr_reader :phone_numbers - def initialize - @phone_numbers = [] - end - end - - describe Person do - context "with one phone number (555-1212)"do - subject do - person = Person.new - person.phone_numbers << "555-1212" - person - end - - its("phone_numbers.first") { should eq("555-1212") } - end - end - """ - When I run rspec with the documentation option - Then the output should contain: - """ - Person - with one phone number (555-1212) - phone_numbers.first - should eq "555-1212" - """ - - @post-3-9 Scenario: specify value of a nested attribute Given a file named "example_spec.rb" with: """ruby diff --git a/rspec-its.gemspec b/rspec-its.gemspec index 5ab9a48..09f6434 100644 --- a/rspec-its.gemspec +++ b/rspec-its.gemspec @@ -7,44 +7,25 @@ require 'rspec/its/version' Gem::Specification.new do |spec| spec.name = "rspec-its" spec.version = RSpec::Its::VERSION - spec.authors = ["Peter Alfvin"] - spec.email = ["palfvin@gmail.com"] - spec.description = %q{RSpec extension gem for attribute matching} - spec.summary = %q{Provides "its" method formerly part of rspec-core} + spec.authors = ["The RSpec Development Team"] + spec.email = ["maintainers@rspec.info"] + spec.description = 'RSpec extension gem for attribute matching' + spec.summary = 'Provides "its" method formerly part of rspec-core' spec.homepage = "https://github.com/rspec/rspec-its" spec.license = "MIT" + spec.required_ruby_version = '> 3.0.0' - spec.metadata = { - 'bug_tracker_uri' => 'https://github.com/rspec/rspec-its/issues', - 'changelog_uri' => "https://github.com/rspec/rspec-its/blob/v#{spec.version}/Changelog.md", - 'documentation_uri' => "https://www.rubydoc.info/gems/rspec-its/#{spec.version}", - 'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/rspec', - 'source_code_uri' => 'https://github.com/rspec/rspec-its', - } + spec.metadata['bug_tracker_uri'] = 'https://github.com/rspec/rspec-its/issues' + spec.metadata['changelog_uri'] = "https://github.com/rspec/rspec-its/blob/v#{spec.version}/Changelog.md" + spec.metadata['documentation_uri'] = "https://www.rubydoc.info/gems/rspec-its/#{spec.version}" + spec.metadata['mailing_list_uri'] = 'https://groups.google.com/forum/#!forum/rspec' + spec.metadata['rubygems_mfa_required'] = true + spec.metadata['source_code_uri'] = 'https://github.com/rspec/rspec-its' spec.files = `git ls-files`.split($/) - %w[cucumber.yml] spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } - spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_runtime_dependency 'rspec-core', '>= 3.0.0' - spec.add_runtime_dependency 'rspec-expectations', '>= 3.0.0' - - if RUBY_VERSION.to_f < 1.9 || RUBY_VERSION == '1.9.2' - spec.add_development_dependency "rake", "~> 10.0.0" - elsif RUBY_VERSION.to_f < 2 - spec.add_development_dependency "rake", "~> 11.0.0" - elsif RUBY_VERSION.to_f < 2.3 - spec.add_development_dependency "rake", "~> 12.3.2" - else - spec.add_development_dependency "rake", "~> 13.0.0" - end - - spec.add_development_dependency 'bundler', '> 1.3.0' - if RUBY_VERSION.to_f < 2 - spec.add_development_dependency 'cucumber', '< 3.0.0' - else - spec.add_development_dependency 'cucumber', '>= 1.3.8' - end - spec.add_development_dependency "aruba", "~> 0.14.12" + spec.add_dependency 'rspec-core', '>= 3.13.0' + spec.add_dependency 'rspec-expectations', '>= 3.13.0' end diff --git a/script/legacy_setup.sh b/script/legacy_setup.sh deleted file mode 100755 index fd83a87..0000000 --- a/script/legacy_setup.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -e - -bundle install --standalone --binstubs - -if [ -x ./bin/rspec ]; then - echo "RSpec bin detected" -else - if [ -x ./exe/rspec ]; then - cp ./exe/rspec ./bin/rspec - echo "RSpec restored from exe" - else - echo "No RSpec bin available" - exit 1 - fi -fi diff --git a/script/test_all b/script/test_all index 43ac721..0ba5be3 100755 --- a/script/test_all +++ b/script/test_all @@ -21,16 +21,10 @@ else echo "Using $BRANCH" fi -if ruby -e 'exit(ENV.fetch("BRANCH") =~ /3-[0-8]-maintenance/ ? 0 : 1)'; then - TAGS="--tags @pre-3-9" -else - TAGS="--tags @post-3-9" -fi; - if ruby -e "exit(defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java')"; then # This is JRUBY which requires this one weird path trick... PATH="${PWD}/bin:$PATH" \ - bundle exec cucumber --strict $TAGS + bundle exec cucumber --strict else - bundle exec cucumber --strict $TAGS + bundle exec cucumber --strict fi; diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index 1d10da9..264c8d7 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -2,32 +2,6 @@ set -e -function is_ruby_31_plus { - if ruby -e "exit(RUBY_VERSION.to_f >= 3.1)"; then - return 0 - else - return 1 - fi -} - -function is_ruby_23_plus { - if ruby -e "exit(RUBY_VERSION.to_f >= 2.3)"; then - return 0 - else - return 1 - fi -} - -if is_ruby_31_plus; then - echo "Installing rubygems 3.3.6 / bundler 2.3.6" - yes | gem update --system '3.3.6' - yes | gem install bundler -v '2.3.6' -elif is_ruby_23_plus; then - echo "Installing rubygems 3.2.22 / bundler 2.2.22" - yes | gem update --system '3.2.22' - yes | gem install bundler -v '2.2.22' -else - echo "Warning installing older versions of Rubygems / Bundler" - gem update --system '2.7.8' - gem install bundler -v '1.17.3' -fi +echo "Installing latest rubygems / bundler" +yes | gem update --system +yes | gem install bundler diff --git a/spec/rspec/its_spec.rb b/spec/rspec/its_spec.rb index 94b8a6f..53abd9c 100644 --- a/spec/rspec/its_spec.rb +++ b/spec/rspec/its_spec.rb @@ -56,11 +56,7 @@ def name end its("name") { should eq("John") } its("name.size") { should eq(4) } - if RUBY_VERSION >= "2.4.0" - its("name.size.class") { should eq(Integer) } - else - its("name.size.class") { should eq(Fixnum) } - end + its("name.size.class") { should eq(Integer) } context "using should_not" do its("name") { should_not eq("Paul") } @@ -95,11 +91,7 @@ def name end its([:a]) { should eq("Symbol: a") } its(['a']) { should eq("String: a") } - if RUBY_VERSION >= "2.4.0" - its([:b, 'c', 4]) { should eq("Symbol: b; String: c; Integer: 4") } - else - its([:b, 'c', 4]) { should eq("Symbol: b; String: c; Fixnum: 4") } - end + its([:b, 'c', 4]) { should eq("Symbol: b; String: c; Integer: 4") } its(:name) { should eq("George") } context "when referring to an attribute that doesn't exist" do context "it raises an error" do From 5a469758ad154d9daf06f28cbebee52bef8a7493 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 21:35:51 +0000 Subject: [PATCH 04/12] Modernise examples --- README.md | 24 +++--- features/its.feature | 57 ++++++------ .../step_definitions/additional_cli_steps.rb | 24 +++--- features/support/env.rb | 17 ++-- lib/rspec/its.rb | 86 +++++++++---------- rspec-its.gemspec | 4 +- spec/rspec/its_spec.rb | 86 +++++++++++-------- 7 files changed, 154 insertions(+), 144 deletions(-) diff --git a/README.md b/README.md index 5833921..07f6a10 100644 --- a/README.md +++ b/README.md @@ -26,33 +26,31 @@ require 'rspec/its' ## Usage -Use the `its` method to generate a nested example group with -a single example that specifies the expected value of an attribute of the -subject using `should`, `should_not` or `is_expected`. -The `its` method can also specify the block expectations of an attribute of the -subject using `will` or `will_not`. +Use the `its` method to generate a nested example group with a single example that specifies the expected value +of an attribute of the subject using `is_expected`. The `its` method can also specify the block expectations of +an attribute of the subject using `will` or `will_not`. `its` accepts a symbol or a string, and a block representing the example. ```ruby -its(:size) { should eq(1) } -its("length") { should eq(1) } +its(:size) { is_expected.to eq(1) } +its("length") { is_expected.to eq(1) } ``` You can use a string with dots to specify a nested attribute (i.e. an attribute of the attribute of the subject). ```ruby -its("phone_numbers.size") { should_not eq(0) } +its("phone_numbers.size") { is_expected.to_not eq(0) } ``` -The following expect-style method is also available: +The following should-style method is also available: ```ruby -its(:size) { is_expected.to eq(1) } +its(:size) { should eq(1) } ``` -as is this alias for pluralized use: +as is an alias of `is_expected` for pluralized use: ```ruby its(:keys) { are_expected.to eq([:key1, :key2]) } @@ -88,13 +86,13 @@ For other objects, multiple keys within the array will be passed as separate arg ```ruby subject { Matrix[ [:a, :b], [:c, :d] ] } -its([1,1]) { should eq(:d) } +its([1,1]) { is_expected.to eq(:d) } ``` Metadata arguments are supported. ```ruby -its(:size, focus: true) { should eq(1) } +its(:size, focus: true) { is_expected.to eq(1) } ``` ## Contributing diff --git a/features/its.feature b/features/its.feature index 8446517..e650b1d 100644 --- a/features/its.feature +++ b/features/its.feature @@ -5,20 +5,21 @@ Feature: attribute of subject """ruby class Person attr_reader :phone_numbers + def initialize @phone_numbers = [] end end - describe Person do + RSpec.describe Person do context "with one phone number (555-1212)"do - subject do + subject(:person) do person = Person.new person.phone_numbers << "555-1212" person end - its("phone_numbers.first") { should eq("555-1212") } + its("phone_numbers.first") { is_expected.to eq("555-1212") } end end """ @@ -34,13 +35,13 @@ Feature: attribute of subject Scenario: specify value of an attribute of a hash Given a file named "example_spec.rb" with: """ruby - describe Hash do + RSpec.describe Hash do context "with two items" do subject do {:one => 'one', :two => 'two'} end - its(:size) { should eq(2) } + its(:size) { is_expected.to eq(2) } end end """ @@ -50,14 +51,14 @@ Feature: attribute of subject Scenario: specify value for key in a hash Given a file named "example_spec.rb" with: """ruby - describe Hash do + RSpec.describe Hash do context "with keys :one and 'two'" do subject do {:one => 1, "two" => 2} end - its([:one]) { should eq(1) } - its(["two"]) { should eq(2) } + its([:one]) { is_expected.to eq(1) } + its(["two"]) { is_expected.to eq(2) } end end """ @@ -69,15 +70,15 @@ Feature: attribute of subject """ruby require 'matrix' - describe Matrix do + RSpec.describe Matrix do context "with values [[1, 2], [3, 4]]" do subject do Matrix[[1, 2], [3, 4]] end - its([0, 1]) { should eq(2) } - its([1, 0]) { should eq(3) } - its([1, 2]) { should be_nil } + its([0, 1]) { are_expected.to eq(2) } + its([1, 0]) { are_expected.to eq(3) } + its([1, 2]) { are_expected.to be_nil } end end """ @@ -87,22 +88,22 @@ Feature: attribute of subject Scenario: failures are correctly reported as coming from the `its` line Given a file named "example_spec.rb" with: """ruby - describe Array do + RSpec.describe Array do context "when first created" do - its(:size) { should_not eq(0) } + its(:size) { is_expected.to_not eq(0) } end end """ When I run rspec - Then the output should contain "Failure/Error: its(:size) { should_not eq(0) }" - And the output should not match /#[^\n]*rspec[\x2f]its/ + Then the output should contain "Failure/Error: its(:size) { is_expected.to_not eq(0) }" + And the output should not match /#[^\n]*rspec[\x2f]its/ Scenario: examples can be specified by exact line number Given a file named "example_spec.rb" with: """ruby - describe Array do + RSpec.describe Array do context "when first created" do - its(:size) { should eq(0) } + its(:size) { is_expected.to eq(0) } end end """ @@ -112,10 +113,11 @@ Feature: attribute of subject Scenario: examples can be specified by line number within containing block Given a file named "example_spec.rb" with: """ruby - describe Array do + RSpec.describe Array do context "when first created" do - its(:size) { should eq(0) } + its(:size) { is_expected.to eq(0) } end + it "should never execute this" do expect(true).to be(false) end @@ -133,8 +135,9 @@ Feature: attribute of subject end end - describe Klass do - subject { Klass.new } + RSpec.describe Klass do + subject(:klass) { Klass.new } + its(:foo) { will_not raise_error } its(:bar) { will raise_error(NoMethodError) } end @@ -147,8 +150,9 @@ Feature: attribute of subject """ruby class Klass; end - describe Klass do - subject { Klass.new } + RSpec.describe Klass do + subject(:klass) { Klass.new } + its(:foo) { will_not raise_error } end """ @@ -166,8 +170,9 @@ Feature: attribute of subject end end - describe Klass do - subject { Klass.new } + RSpec.describe Klass do + subject(:arnie) { Klass.new } + its(:terminator) { will be("back") } end """ diff --git a/features/step_definitions/additional_cli_steps.rb b/features/step_definitions/additional_cli_steps.rb index 149c14b..391ca33 100644 --- a/features/step_definitions/additional_cli_steps.rb +++ b/features/step_definitions/additional_cli_steps.rb @@ -1,34 +1,34 @@ # frozen_string_literal: true -When /^I run rspec( with the documentation option)?$/ do |documentation| - rspec_its_gem_location = File.expand_path('../../../lib/rspec/its', __FILE__) +When(/^I run rspec( with the documentation option)?$/) do |documentation| + rspec_its_gem_location = File.expand_path('../../lib/rspec/its', __dir__) require_option = "--require #{rspec_its_gem_location}" format_option = documentation ? "--format documentation" : "" rspec_command = ['rspec', require_option, format_option, 'example_spec.rb'].join(' ') step "I run `#{rspec_command}`" end -When /^I run rspec specifying line number (\d+)$/ do |line_number| - rspec_its_gem_location = File.expand_path('../../../lib/rspec/its', __FILE__) +When(/^I run rspec specifying line number (\d+)$/) do |line_number| + rspec_its_gem_location = File.expand_path('../../lib/rspec/its', __dir__) require_option = "--require #{rspec_its_gem_location}" file_specification = "example_spec.rb:#{line_number}" rspec_command = ['rspec', require_option, file_specification].join(' ') step "I run `#{rspec_command}`" end -Then /^the example(?:s)? should(?: all)? pass$/ do - step %q{the output should contain "0 failures"} - step %q{the output should not contain "0 examples"} - step %q{the exit status should be 0} +Then(/^the example(?:s)? should(?: all)? pass$/) do + step 'the output should contain "0 failures"' + step 'the output should not contain "0 examples"' + step 'the exit status should be 0' end -Then(/^the example should fail$/) do - step %q{the output should contain "1 failure"} - step %q{the exit status should not be 0} +Then("the example should fail") do + step 'the output should contain "1 failure"' + step 'the exit status should not be 0' end Then(/^the output should contain "(.*?)" and "(.*?)"$/) do |string1, string2| unless [string1, string2].all? { |s| all_output.include?(s) } - fail %Q{Both "#{string1}" and "#{string2}" were found in:\n#{all_output}} + fail %(Both "#{string1}" and "#{string2}" were found in:\n#{all_output}) end end diff --git a/features/support/env.rb b/features/support/env.rb index b0016e2..fb57b75 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -4,14 +4,11 @@ require 'rspec/core' require 'rspec/its' -Aruba.configure do |config| - config.before(:command) do |cmd| - cmd.environment['JRUBY_OPTS'] = "-X-C #{ENV['JRUBY_OPTS']}" # disable JIT since these processes are so short lived +if RUBY_PLATFORM == 'java' + Aruba.configure do |config| + config.before(:command) do |cmd| + # disable JIT since these processes are so short lived + cmd.environment['JRUBY_OPTS'] = "-X-C #{ENV.fetch('JRUBY_OPTS', '')}" + end end -end if RUBY_PLATFORM == 'java' - -Aruba.configure do |config| - config.before(:command) do |cmd| - cmd.environment['RBXOPT'] = "-Xint=true #{ENV['RBXOPT']}" # disable JIT since these processes are so short lived - end -end if defined?(Rubinius) +end diff --git a/lib/rspec/its.rb b/lib/rspec/its.rb index 2eb9e54..a645a6c 100644 --- a/lib/rspec/its.rb +++ b/lib/rspec/its.rb @@ -6,6 +6,7 @@ RSpec::Core::ExampleGroup.define_example_method :__its_example module RSpec + # Adds the `its` to RSpec Example Groups, included by default. module Its # Creates a nested example group named by the submitted `attribute`, # and then generates an example using the submitted block. @@ -13,15 +14,15 @@ module Its # @example # # # This ... - # describe Array do - # its(:size) { should eq(0) } + # RSpec.describe Array do + # its(:size) { is_expected.to eq(0) } # end # # # ... generates the same runtime structure as this: - # describe Array do + # RSpec.describe Array do # describe "size" do - # it "should eq(0)" do - # subject.size.should eq(0) + # it "is_expected.to eq(0)" do + # expect(subject.size).to eq(0) # end # end # end @@ -32,14 +33,14 @@ module Its # # @example # - # describe Person do - # subject do + # RSpec.describe Person do + # subject(:person) do # Person.new.tap do |person| # person.phone_numbers << "555-1212" # end # end # - # its("phone_numbers.first") { should eq("555-1212") } + # its("phone_numbers.first") { is_expected.to eq("555-1212") } # end # # When the subject is a `Hash`, you can refer to the Hash keys by @@ -47,30 +48,30 @@ module Its # # @example # - # describe "a configuration Hash" do + # RSpec.describe "a configuration Hash" do # subject do # { :max_users => 3, # 'admin' => :all_permissions. # 'john_doe' => {:permissions => [:read, :write]}} # end # - # its([:max_users]) { should eq(3) } - # its(['admin']) { should eq(:all_permissions) } - # its(['john_doe', :permissions]) { should eq([:read, :write]) } + # its([:max_users]) { is_expected.to eq(3) } + # its(['admin']) { is_expected.to eq(:all_permissions) } + # its(['john_doe', :permissions]) { are_expected.to eq([:read, :write]) } # # # You can still access its regular methods this way: - # its(:keys) { should include(:max_users) } - # its(:count) { should eq(2) } + # its(:keys) { is_expected.to include(:max_users) } + # its(:count) { is_expected.to eq(2) } # end # - # With an implicit subject, `is_expected` can be used as an alternative - # to `should` (e.g. for one-liner use). An `are_expected` alias is also + # With an implicit subject, `should` can be used as an alternative + # to `is_expected` (e.g. for one-liner use). An `are_expected` alias is also # supplied. # # @example # - # describe Array do - # its(:size) { is_expected.to eq(0) } + # RSpec.describe Array do + # its(:size) { should eq(0) } # end # # With an implicit subject, `will` can be used as an alternative @@ -78,7 +79,7 @@ module Its # # @example # - # describe Array do + # RSpec.describe Array do # its(:foo) { will raise_error(NoMethodError) } # end # @@ -87,7 +88,7 @@ module Its # # @example # - # describe Array do + # RSpec.describe Array do # its(:size) { will_not raise_error } # end # @@ -97,15 +98,15 @@ module Its # @example # # # This ... - # describe Array do - # its(:size, :focus) { should eq(0) } + # RSpec.describe Array do + # its(:size, :focus) { is_expected.to eq(0) } # end # # # ... generates the same runtime structure as this: - # describe Array do + # RSpec.describe Array do # describe "size" do - # it "should eq(0)", :focus do - # subject.size.should eq(0) + # it "is expected to eq(0)", :focus do + # expect(subject.size).to eq(0) # end # end # end @@ -116,18 +117,21 @@ module Its # # @example # - # describe Person do + # RSpec.describe Person do # subject { Person.new } + # # before { subject.age = 25 } - # its(:age) { should eq(25) } + # + # its(:age) { is_expected.to eq(25) } # end def its(attribute, *options, &block) - its_caller = caller.select {|file_line| file_line !~ %r(/lib/rspec/its) } + its_caller = caller.grep_v(%r{/lib/rspec/its}) + describe(attribute.to_s, :caller => its_caller) do let(:__its_subject) do if Array === attribute if Hash === subject - attribute.inject(subject) {|inner, attr| inner[attr] } + attribute.inject(subject) { |inner, attr| inner[attr] } else subject[*attribute] end @@ -144,42 +148,38 @@ def is_expected end alias_method :are_expected, :is_expected - def will(matcher=nil, message=nil) - unless matcher.supports_block_expectations? - raise ArgumentError, "`will` only supports block expectations" - end + def will(matcher = nil, message = nil) + raise ArgumentError, "`will` only supports block expectations" unless matcher.supports_block_expectations? + expect { __its_subject }.to matcher, message end - def will_not(matcher=nil, message=nil) - unless matcher.supports_block_expectations? - raise ArgumentError, "`will_not` only supports block expectations" - end + def will_not(matcher = nil, message = nil) + raise ArgumentError, "`will_not` only supports block expectations" unless matcher.supports_block_expectations? + expect { __its_subject }.to_not matcher, message end - def should(matcher=nil, message=nil) + def should(matcher = nil, message = nil) RSpec::Expectations::PositiveExpectationHandler.handle_matcher(__its_subject, matcher, message) end - def should_not(matcher=nil, message=nil) + def should_not(matcher = nil, message = nil) RSpec::Expectations::NegativeExpectationHandler.handle_matcher(__its_subject, matcher, message) end - options << {} unless options.last.kind_of?(Hash) + options << {} unless options.last.is_a?(Hash) options.last.merge!(:caller => its_caller) __its_example(nil, *options, &block) - end end - end end RSpec.configure do |rspec| rspec.extend RSpec::Its - rspec.backtrace_exclusion_patterns << %r(/lib/rspec/its) + rspec.backtrace_exclusion_patterns << %r{/lib/rspec/its} end RSpec::SharedContext.send(:include, RSpec::Its) diff --git a/rspec-its.gemspec b/rspec-its.gemspec index 09f6434..7c6bc7c 100644 --- a/rspec-its.gemspec +++ b/rspec-its.gemspec @@ -1,6 +1,6 @@ # frozen_string_literal: true -lib = File.expand_path('../lib', __FILE__) +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'rspec/its/version' @@ -19,7 +19,7 @@ Gem::Specification.new do |spec| spec.metadata['changelog_uri'] = "https://github.com/rspec/rspec-its/blob/v#{spec.version}/Changelog.md" spec.metadata['documentation_uri'] = "https://www.rubydoc.info/gems/rspec-its/#{spec.version}" spec.metadata['mailing_list_uri'] = 'https://groups.google.com/forum/#!forum/rspec' - spec.metadata['rubygems_mfa_required'] = true + spec.metadata['rubygems_mfa_required'] = 'true' spec.metadata['source_code_uri'] = 'https://github.com/rspec/rspec-its' spec.files = `git ls-files`.split($/) - %w[cucumber.yml] diff --git a/spec/rspec/its_spec.rb b/spec/rspec/its_spec.rb index 53abd9c..4aaa0f3 100644 --- a/spec/rspec/its_spec.rb +++ b/spec/rspec/its_spec.rb @@ -28,11 +28,11 @@ def call_count end context "with some metadata" do - its(:call_count, :meta) { should eq(2) } + its(:call_count, :meta) { is_expected.to eq(2) } end context "with a call counter" do - its(:call_count) { should eq(1) } + its(:call_count) { is_expected.to eq(1) } end context "with nil value" do @@ -43,7 +43,8 @@ def nil_value end end.new end - its(:nil_value) { should be_nil } + + its(:nil_value) { is_expected.to be_nil } end context "with nested attributes" do @@ -54,24 +55,25 @@ def name end end.new end - its("name") { should eq("John") } - its("name.size") { should eq(4) } - its("name.size.class") { should eq(Integer) } - context "using should_not" do - its("name") { should_not eq("Paul") } - end + its("name") { is_expected.to eq("John") } + its("name.size") { is_expected.to eq(4) } + its("name.size.class") { is_expected.to eq(Integer) } - context "using is_expected" do - its("name") { is_expected.to eq("John") } + context "using are_expected" do + its("name.chars.to_a") { are_expected.to eq(%w[J o h n]) } end context "using will_not" do its("name") { will_not raise_error } end - context "using are_expected" do - its("name.chars.to_a") { are_expected.to eq(%w[J o h n]) } + context "using should" do + its("name") { should eq("John") } + end + + context "using should_not" do + its("name") { should_not eq("Paul") } end end @@ -80,7 +82,7 @@ def name Class.new do def [](*objects) objects.map do |object| - "#{object.class}: #{object.to_s}" + "#{object.class}: #{object}" end.join("; ") end @@ -89,15 +91,17 @@ def name end end.new end - its([:a]) { should eq("Symbol: a") } - its(['a']) { should eq("String: a") } - its([:b, 'c', 4]) { should eq("Symbol: b; String: c; Integer: 4") } - its(:name) { should eq("George") } + + its([:a]) { is_expected.to eq("Symbol: a") } + its(['a']) { is_expected.to eq("String: a") } + its([:b, 'c', 4]) { is_expected.to eq("Symbol: b; String: c; Integer: 4") } + its(:name) { is_expected.to eq("George") } + context "when referring to an attribute that doesn't exist" do context "it raises an error" do its(:age) do expect do - should eq(64) + is_expected.to eq(64) end.to raise_error(NoMethodError) end @@ -110,17 +114,17 @@ def name context "when it's a hash" do subject { {:a => {:deep => {:key => "value"}}} } - its([:a]) { should eq({:deep => {:key => "value"}}) } - its([:a, :deep]) { should eq({:key => "value"}) } - its([:a, :deep, :key]) { should eq("value") } + its([:a]) { is_expected.to eq({ deep: { key: "value" } }) } + its(%i[a deep]) { is_expected.to eq({ key: "value" }) } + its(%i[a deep key]) { is_expected.to eq("value") } context "when referring to a key that doesn't exist" do - its([:not_here]) { should be_nil } - its([:a, :ghost]) { should be_nil } - its([:deep, :ghost]) { expect { should eq("missing") }.to raise_error(NoMethodError) } + its([:not_here]) { is_expected.to be_nil } + its(%i[a ghost]) { are_expected.to be_nil } + its(%i[deep ghost]) { expect { is_expected.to eq("missing") }.to raise_error(NoMethodError) } context "using will" do - its([:deep, :ghost]) { will raise_error(NoMethodError) } + its(%i[deep ghost]) { will raise_error(NoMethodError) } end end end @@ -131,9 +135,7 @@ def name context "it raises an error" do its([:a]) do - expect do - should eq("Symbol: a") - end.to raise_error(NoMethodError) + expect { is_expected.to eq("Symbol: a") }.to raise_error(NoMethodError) end context "using will" do @@ -147,14 +149,14 @@ def name group = RSpec::Core::ExampleGroup.describe(Array) do subject { [1, 'a'] } - its(:last) { should eq("a") } + its(:last) { is_expected.to eq("a") } describe '.first' do - def subject; - super().first; + def subject + super.first end - its(:next) { should eq(2) } + its(:next) { is_expected.to eq(2) } end end @@ -175,7 +177,8 @@ def nil_if_first_time end end.new end - its(:nil_if_first_time) { should be(nil) } + + its(:nil_if_first_time) { is_expected.to be(nil) } end context "with false subject" do @@ -191,12 +194,15 @@ def false_if_first_time end end.new end - its(:false_if_first_time) { should be(false) } + + its(:false_if_first_time) { is_expected.to be(false) } end describe 'accessing `subject` in `before` and `let`' do subject { 'my subject' } + before { @subject_in_before = subject } + let(:subject_in_let) { subject } let!(:eager_loaded_subject_in_let) { subject } @@ -211,7 +217,8 @@ def false_if_first_time describe "in shared_context" do shared_context "shared stuff" do subject { Array } - its(:name) { should eq "Array" } + + its(:name) { is_expected.to eq "Array" } end include_context "shared stuff" @@ -221,8 +228,9 @@ def false_if_first_time it 'works with an implicit subject' do shared = Module.new do extend RSpec::SharedContext - its(:size) { should eq 0 } + its(:size) { is_expected.to eq 0 } end + group = RSpec::Core::ExampleGroup.describe(Array) do include shared end @@ -236,12 +244,14 @@ def false_if_first_time end end end + context "with metadata" do context "preserves access to metadata that doesn't end in hash" do its([], :foo) do |example| expect(example.metadata[:foo]).to be(true) end end + context "preserves access to metadata that ends in hash" do its([], :foo, :bar => 17) do |example| expect(example.metadata[:foo]).to be(true) @@ -307,7 +317,7 @@ def noop; end its(:increment) { will change { subject.count }.by_at_least(1) } its(:increment) { will change { subject.count }.by_at_most(1) } - its(:noop) { will_not change { subject.count } } + its(:noop) { will_not(change { subject.count }) } its(:noop) { will_not change { subject.count }.from(0) } its(:increment) do From f982d7490b108711d19eac2a75e226e11b887c81 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 21:51:12 +0000 Subject: [PATCH 05/12] Switch Ruby hash syntax --- Rakefile | 2 +- lib/rspec/its.rb | 4 ++-- spec/rspec/its_spec.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Rakefile b/Rakefile index 11ea87e..a4f4ca1 100644 --- a/Rakefile +++ b/Rakefile @@ -15,4 +15,4 @@ RSpec::Core::RakeTask.new(:spec) do |t| t.ruby_opts = %w[-w] end -task :default => [:spec, :cucumber] +task default: %i[spec cucumber] diff --git a/lib/rspec/its.rb b/lib/rspec/its.rb index a645a6c..ad777fd 100644 --- a/lib/rspec/its.rb +++ b/lib/rspec/its.rb @@ -127,7 +127,7 @@ module Its def its(attribute, *options, &block) its_caller = caller.grep_v(%r{/lib/rspec/its}) - describe(attribute.to_s, :caller => its_caller) do + describe(attribute.to_s, caller: its_caller) do let(:__its_subject) do if Array === attribute if Hash === subject @@ -169,7 +169,7 @@ def should_not(matcher = nil, message = nil) end options << {} unless options.last.is_a?(Hash) - options.last.merge!(:caller => its_caller) + options.last.merge!(caller: its_caller) __its_example(nil, *options, &block) end diff --git a/spec/rspec/its_spec.rb b/spec/rspec/its_spec.rb index 4aaa0f3..b286baf 100644 --- a/spec/rspec/its_spec.rb +++ b/spec/rspec/its_spec.rb @@ -112,7 +112,7 @@ def name end context "when it's a hash" do - subject { {:a => {:deep => {:key => "value"}}} } + subject { { a: { deep: { key: "value" } } } } its([:a]) { is_expected.to eq({ deep: { key: "value" } }) } its(%i[a deep]) { is_expected.to eq({ key: "value" }) } @@ -253,7 +253,7 @@ def false_if_first_time end context "preserves access to metadata that ends in hash" do - its([], :foo, :bar => 17) do |example| + its([], :foo, bar: 17) do |example| expect(example.metadata[:foo]).to be(true) expect(example.metadata[:bar]).to be(17) end From fead7d247f7889b0588f5f9867e5451d3f5b18d1 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 21:51:26 +0000 Subject: [PATCH 06/12] Fix CI badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07f6a10..9feecca 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RSpec::Its [![Build Status](https://travis-ci.org/rspec/rspec-its.svg)](https://travis-ci.org/rspec/rspec-its) +# RSpec::Its [![Build Status](https://github.com/rspec/rspec-its/actions/workflows/ci.yml/badge.svg)](https://github.com/rspec/rspec-its/actions/workflows/ci.yml/badge.svg) RSpec::Its provides the `its` method as a short-hand to specify the expected value of an attribute. From 33ae019eda9f1cb03571ba6bd7550e7dfc547362 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 21:51:40 +0000 Subject: [PATCH 07/12] Update for v2.0.0.pre --- Changelog.md | 7 +++++++ LICENSE.txt | 2 +- lib/rspec/its/version.rb | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 95ef607..ef8abc2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,10 @@ +### 2.0.0.pre + +Version 2.0.0 is a maintenance release, it drops support for Ruby below 3, and +changes the supported RSpec version to "main" and current release series. +(At the time of writing this is 3.13.x, but it means the current supported +release only). + ### 1.3.1 / 2024-10-23 [full changelog](http://github.com/rspec/rspec-its/compare/v1.3.0...v1.3.1) diff --git a/LICENSE.txt b/LICENSE.txt index ac5445c..e7d3ca1 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,9 +1,9 @@ (The MIT License) +Copyright (c) 2024 The RSpec Development Team Copyright (c) 2013 Peter Alfvin Copyright (c) 2012 David Chelimsky, Myron Marston Copyright (c) 2006 David Chelimsky, The RSpec Development Team -Copyright (c) 2005 Steven Baker MIT License diff --git a/lib/rspec/its/version.rb b/lib/rspec/its/version.rb index 200c906..50ec6d0 100644 --- a/lib/rspec/its/version.rb +++ b/lib/rspec/its/version.rb @@ -2,6 +2,6 @@ module RSpec module Its - VERSION = "1.3.1" + VERSION = '2.0.0.pre' end end From 9c3f0c7d78dbb9389f4ded1fcbb3a1c7853034be Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 22:26:21 +0000 Subject: [PATCH 08/12] Refactor subject capture --- lib/rspec/its.rb | 16 ++-------------- lib/rspec/its/subject.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 lib/rspec/its/subject.rb diff --git a/lib/rspec/its.rb b/lib/rspec/its.rb index ad777fd..8e67d11 100644 --- a/lib/rspec/its.rb +++ b/lib/rspec/its.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'rspec/its/subject' require 'rspec/its/version' require 'rspec/core' @@ -128,20 +129,7 @@ def its(attribute, *options, &block) its_caller = caller.grep_v(%r{/lib/rspec/its}) describe(attribute.to_s, caller: its_caller) do - let(:__its_subject) do - if Array === attribute - if Hash === subject - attribute.inject(subject) { |inner, attr| inner[attr] } - else - subject[*attribute] - end - else - attribute_chain = attribute.to_s.split('.') - attribute_chain.inject(subject) do |inner_subject, attr| - inner_subject.send(attr) - end - end - end + let(:__its_subject) { RSpec::Its::Subject.for(attribute, subject) } def is_expected expect(__its_subject) diff --git a/lib/rspec/its/subject.rb b/lib/rspec/its/subject.rb new file mode 100644 index 0000000..0efd63d --- /dev/null +++ b/lib/rspec/its/subject.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module RSpec + module Its + # @api private + # Handles turning subject into an expectation target + module Subject + def for(attribute, subject) + if Array === attribute + if Hash === subject + attribute.inject(subject) { |inner, attr| inner[attr] } + else + subject[*attribute] + end + else + attribute_chain = attribute.to_s.split('.') + attribute_chain.inject(subject) do |inner_subject, attr| + inner_subject.send(attr) + end + end + end + + module_function :for + end + end +end From 03a235a082ca0671b0cd7a987d27f4398620206d Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 22:29:18 +0000 Subject: [PATCH 09/12] Ignore bin --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4279c1c..ed06470 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ spec/reports test/tmp test/version_tmp tmp +bin/* From 5db1f3826d0522e248b83494d2ab62fdffed6876 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 23:00:07 +0000 Subject: [PATCH 10/12] Update rake --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index eb60617..c250b7d 100644 --- a/Gemfile +++ b/Gemfile @@ -23,4 +23,4 @@ gem 'coveralls', require: false gem 'cucumber', '>= 1.3.8' gem 'ffi', '~> 1.15.0' gem 'matrix', '~> 0.4.2' -gem 'rake', '~> 13.0.0' +gem 'rake', '~> 13.2.0' From 23a8169ad23bb4fc65dc7d63a45890cb49ef5093 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 23:01:21 +0000 Subject: [PATCH 11/12] Update ffi --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index c250b7d..cb8a79e 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,6 @@ gem 'aruba', '~> 0.14.12' gem 'bundler', '> 2.0.0' gem 'coveralls', require: false gem 'cucumber', '>= 1.3.8' -gem 'ffi', '~> 1.15.0' +gem 'ffi', '~> 1.17.0' gem 'matrix', '~> 0.4.2' gem 'rake', '~> 13.2.0' From 8e3ceb1ef4e18db85db5c9d9992083784d931451 Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Sun, 3 Nov 2024 23:02:26 +0000 Subject: [PATCH 12/12] Update aruba --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index cb8a79e..36a5694 100644 --- a/Gemfile +++ b/Gemfile @@ -17,7 +17,7 @@ gemspec end end -gem 'aruba', '~> 0.14.12' +gem 'aruba', '~> 2.2.0' gem 'bundler', '> 2.0.0' gem 'coveralls', require: false gem 'cucumber', '>= 1.3.8'