summaryrefslogtreecommitdiff
path: root/spec/ruby/core/data
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/data')
-rw-r--r--spec/ruby/core/data/deconstruct_keys_spec.rb96
-rw-r--r--spec/ruby/core/data/fixtures/classes.rb13
-rw-r--r--spec/ruby/core/data/hash_spec.rb2
-rw-r--r--spec/ruby/core/data/initialize_spec.rb98
-rw-r--r--spec/ruby/core/data/inspect_spec.rb6
-rw-r--r--spec/ruby/core/data/shared/inspect.rb62
-rw-r--r--spec/ruby/core/data/to_h_spec.rb8
-rw-r--r--spec/ruby/core/data/to_s_spec.rb61
-rw-r--r--spec/ruby/core/data/with_spec.rb14
9 files changed, 219 insertions, 141 deletions
diff --git a/spec/ruby/core/data/deconstruct_keys_spec.rb b/spec/ruby/core/data/deconstruct_keys_spec.rb
index df378f8b98..7e81f966ea 100644
--- a/spec/ruby/core/data/deconstruct_keys_spec.rb
+++ b/spec/ruby/core/data/deconstruct_keys_spec.rb
@@ -15,7 +15,7 @@ describe "Data#deconstruct_keys" do
-> {
d.deconstruct_keys
- }.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
+ }.should.raise(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/)
end
it "returns only specified keys" do
@@ -34,29 +34,6 @@ describe "Data#deconstruct_keys" do
d.deconstruct_keys(['x', 'y']).should == {'x' => 1, 'y' => 2}
end
- it "accepts argument position number as well but returns them as keys" do
- klass = Data.define(:x, :y)
- d = klass.new(1, 2)
-
- d.deconstruct_keys([0, 1]).should == {0 => 1, 1 => 2}
- d.deconstruct_keys([0] ).should == {0 => 1}
- d.deconstruct_keys([-1] ).should == {-1 => 2}
- end
-
- it "ignores incorrect position numbers" do
- klass = Data.define(:x, :y)
- d = klass.new(1, 2)
-
- d.deconstruct_keys([0, 3]).should == {0 => 1}
- end
-
- it "support mixing attribute names and argument position numbers" do
- klass = Data.define(:x, :y)
- d = klass.new(1, 2)
-
- d.deconstruct_keys([0, :x]).should == {0 => 1, :x => 1}
- end
-
it "returns an empty hash when there are more keys than attributes" do
klass = Data.define(:x, :y)
d = klass.new(1, 2)
@@ -72,14 +49,6 @@ describe "Data#deconstruct_keys" do
d.deconstruct_keys([:x, :a]).should == {x: 1}
end
- it "returns at first not existing argument position number" do
- klass = Data.define(:x, :y)
- d = klass.new(1, 2)
-
- d.deconstruct_keys([3, 0]).should == {}
- d.deconstruct_keys([0, 3]).should == {0 => 1}
- end
-
it "accepts nil argument and return all the attributes" do
klass = Data.define(:x, :y)
d = klass.new(1, 2)
@@ -87,44 +56,55 @@ describe "Data#deconstruct_keys" do
d.deconstruct_keys(nil).should == {x: 1, y: 2}
end
- it "tries to convert a key with #to_int if index is not a String nor a Symbol, but responds to #to_int" do
- klass = Data.define(:x, :y)
- d = klass.new(1, 2)
+ ruby_version_is "4.0" do # https://bugs.ruby-lang.org/issues/21844
+ it "tries to convert a key with #to_str if index is not a String nor a Symbol, but responds to #to_str" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
- key = mock("to_int")
- key.should_receive(:to_int).and_return(1)
+ key = mock("to_str")
+ key.should_receive(:to_str).and_return("y")
- d.deconstruct_keys([key]).should == { key => 2 }
- end
+ d.deconstruct_keys([key]).should == { "y" => 2 }
+ end
- it "raises a TypeError if the conversion with #to_int does not return an Integer" do
- klass = Data.define(:x, :y)
- d = klass.new(1, 2)
+ it "raise an error on argument position number" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
- key = mock("to_int")
- key.should_receive(:to_int).and_return("not an Integer")
+ -> {
+ d.deconstruct_keys([0, 1])
+ }.should.raise(TypeError, "0 is not a symbol nor a string")
+ end
- -> {
- d.deconstruct_keys([key])
- }.should raise_error(TypeError, /can't convert MockObject to Integer/)
- end
+ it "raises a TypeError if the conversion with #to_str does not return a String" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
- it "raises TypeError if index is not a String, a Symbol and not convertible to Integer " do
- klass = Data.define(:x, :y)
- d = klass.new(1, 2)
+ key = mock("to_str")
+ key.should_receive(:to_str).and_return(0)
- -> {
- d.deconstruct_keys([0, []])
- }.should raise_error(TypeError, "no implicit conversion of Array into Integer")
+ -> {
+ d.deconstruct_keys([key])
+ }.should raise_consistent_error(TypeError, /can't convert MockObject into String/)
+ end
+
+ it "raises TypeError if index is not a Symbol and not convertible to String" do
+ klass = Data.define(:x, :y)
+ d = klass.new(1, 2)
+
+ -> {
+ d.deconstruct_keys([0, []])
+ }.should.raise(TypeError, "0 is not a symbol nor a string")
+ end
end
it "raise TypeError if passed anything except nil or array" do
klass = Data.define(:x, :y)
d = klass.new(1, 2)
- -> { d.deconstruct_keys('x') }.should raise_error(TypeError, /expected Array or nil/)
- -> { d.deconstruct_keys(1) }.should raise_error(TypeError, /expected Array or nil/)
- -> { d.deconstruct_keys(:x) }.should raise_error(TypeError, /expected Array or nil/)
- -> { d.deconstruct_keys({}) }.should raise_error(TypeError, /expected Array or nil/)
+ -> { d.deconstruct_keys('x') }.should.raise(TypeError, /expected Array or nil/)
+ -> { d.deconstruct_keys(1) }.should.raise(TypeError, /expected Array or nil/)
+ -> { d.deconstruct_keys(:x) }.should.raise(TypeError, /expected Array or nil/)
+ -> { d.deconstruct_keys({}) }.should.raise(TypeError, /expected Array or nil/)
end
end
diff --git a/spec/ruby/core/data/fixtures/classes.rb b/spec/ruby/core/data/fixtures/classes.rb
index ffd361d781..147293ee45 100644
--- a/spec/ruby/core/data/fixtures/classes.rb
+++ b/spec/ruby/core/data/fixtures/classes.rb
@@ -1,6 +1,7 @@
module DataSpecs
if Data.respond_to?(:define)
Measure = Data.define(:amount, :unit)
+ Single = Data.define(:value)
class MeasureWithOverriddenName < Measure
def self.name
@@ -8,6 +9,12 @@ module DataSpecs
end
end
+ class SingleWithOverriddenName < Single
+ def self.name
+ "A"
+ end
+ end
+
class DataSubclass < Data; end
MeasureSubclass = Class.new(Measure) do
@@ -24,5 +31,11 @@ module DataSpecs
ScratchPad.record [:initialize, rest, kw]
end
end
+
+ Area = Data.define(:width, :height, :area) do
+ def initialize(width:, height:)
+ super(width: width, height: height, area: width * height)
+ end
+ end
end
end
diff --git a/spec/ruby/core/data/hash_spec.rb b/spec/ruby/core/data/hash_spec.rb
index c23f08a71d..bab146c92e 100644
--- a/spec/ruby/core/data/hash_spec.rb
+++ b/spec/ruby/core/data/hash_spec.rb
@@ -6,7 +6,7 @@ describe "Data#hash" do
a = DataSpecs::Measure.new(42, "km")
b = DataSpecs::Measure.new(42, "km")
a.hash.should == b.hash
- a.hash.should be_an_instance_of(Integer)
+ a.hash.should.instance_of?(Integer)
end
it "returns different hashes for objects with different values" do
diff --git a/spec/ruby/core/data/initialize_spec.rb b/spec/ruby/core/data/initialize_spec.rb
index 9d272780a8..0320ca880c 100644
--- a/spec/ruby/core/data/initialize_spec.rb
+++ b/spec/ruby/core/data/initialize_spec.rb
@@ -2,6 +2,16 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Data#initialize" do
+ context "with no members" do
+ ruby_bug "#21819", ""..."4.0.1" do
+ it "is frozen" do
+ data = Data.define
+
+ data.new.should.frozen?
+ end
+ end
+ end
+
it "accepts positional arguments" do
data = DataSpecs::Measure.new(42, "km")
@@ -37,10 +47,27 @@ describe "Data#initialize" do
data.unit.should == "km"
end
+ it "accepts the last entry when a keyword is given as both String and Symbol" do
+ data = DataSpecs::Single.new("value" => -1, value: 42)
+
+ data.value.should == 42
+ end
+
+ it "accepts positional arguments with empty keyword arguments" do
+ data = DataSpecs::Single.new(42, **{})
+
+ data.value.should == 42
+
+ data = DataSpecs::Measure.new(42, "km", **{})
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
it "raises ArgumentError if no arguments are given" do
-> {
DataSpecs::Measure.new
- }.should raise_error(ArgumentError) { |e|
+ }.should.raise(ArgumentError) { |e|
e.message.should.include?("missing keywords: :amount, :unit")
}
end
@@ -48,19 +75,61 @@ describe "Data#initialize" do
it "raises ArgumentError if at least one argument is missing" do
-> {
DataSpecs::Measure.new(unit: "km")
- }.should raise_error(ArgumentError) { |e|
+ }.should.raise(ArgumentError) { |e|
e.message.should.include?("missing keyword: :amount")
}
end
+ ruby_version_is "4.0" do # https://bugs.ruby-lang.org/issues/21844
+ it "raises ArgumentError if at least one argument is missing and other is provided as both String and Symbol" do
+ -> {
+ DataSpecs::Measure.new(unit: "km", "unit" => "km")
+ }.should.raise(ArgumentError) { |e|
+ e.message.should.include?("missing keyword: :amount")
+ }
+ end
+ end
+
it "raises ArgumentError if unknown keyword is given" do
-> {
DataSpecs::Measure.new(amount: 42, unit: "km", system: "metric")
- }.should raise_error(ArgumentError) { |e|
+ }.should.raise(ArgumentError) { |e|
e.message.should.include?("unknown keyword: :system")
}
end
+ ruby_version_is "4.0" do # https://bugs.ruby-lang.org/issues/21844
+ it "raises ArgumentError if unknown keyword is given which is convertable to String" do
+ key = mock("to_str")
+ key.should_receive(:to_str).and_return("system")
+
+ -> {
+ DataSpecs::Measure.new(amount: 42, unit: "km", key => "metric")
+ }.should.raise(ArgumentError) { |e|
+ e.message.should.include?('unknown keyword: "system"')
+ }
+ end
+
+ it "raises TypeError when the keyword is not convertable to String" do
+ -> {
+ DataSpecs::Measure.new(1 => 2)
+ }.should.raise(TypeError) { |e|
+ e.message.should == "1 is not a symbol nor a string"
+ }
+ end
+
+ it "raises TypeError if the conversion with #to_str does not return a String" do
+ klass = Data.define(:x, :y)
+
+ key = mock("to_str")
+ key.should_receive(:to_str).and_return(0)
+
+ -> {
+ klass.new(key => 2)
+ }.should raise_consistent_error(TypeError, /can't convert MockObject into String/)
+ end
+ end
+
it "supports super from a subclass" do
ms = DataSpecs::MeasureSubclass.new(amount: 1, unit: "km")
@@ -69,7 +138,7 @@ describe "Data#initialize" do
end
it "supports Data with no fields" do
- -> { DataSpecs::Empty.new }.should_not raise_error
+ -> { DataSpecs::Empty.new }.should_not.raise
end
it "can be overridden" do
@@ -110,5 +179,26 @@ describe "Data#initialize" do
DataSpecs::DataWithOverriddenInitialize[amount: 42, unit: "m"]
ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}]
end
+
+ it "accepts positional arguments with empty keyword arguments" do
+ data = DataSpecs::SingleWithOverriddenName.new(42, **{})
+
+ data.value.should == 42
+
+ data = DataSpecs::MeasureWithOverriddenName.new(42, "km", **{})
+
+ data.amount.should == 42
+ data.unit.should == "km"
+ end
+
+ # See https://github.com/ruby/psych/pull/765
+ it "can be deserialized by calling Data.instance_method(:initialize)" do
+ d1 = DataSpecs::Area.new(width: 2, height: 3)
+ d1.area.should == 6
+
+ d2 = DataSpecs::Area.allocate
+ Data.instance_method(:initialize).bind_call(d2, **d1.to_h)
+ d2.should == d1
+ end
end
end
diff --git a/spec/ruby/core/data/inspect_spec.rb b/spec/ruby/core/data/inspect_spec.rb
index 38642910a0..e5b9689ca5 100644
--- a/spec/ruby/core/data/inspect_spec.rb
+++ b/spec/ruby/core/data/inspect_spec.rb
@@ -1,6 +1,8 @@
require_relative '../../spec_helper'
-require_relative 'shared/inspect'
+require_relative 'fixtures/classes'
describe "Data#inspect" do
- it_behaves_like :data_inspect, :inspect
+ it "is an alias of Data#to_s" do
+ DataSpecs::Measure.instance_method(:inspect).should == DataSpecs::Measure.instance_method(:to_s)
+ end
end
diff --git a/spec/ruby/core/data/shared/inspect.rb b/spec/ruby/core/data/shared/inspect.rb
deleted file mode 100644
index 6cd5664da7..0000000000
--- a/spec/ruby/core/data/shared/inspect.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require_relative '../fixtures/classes'
-
-describe :data_inspect, shared: true do
- it "returns a string representation showing members and values" do
- a = DataSpecs::Measure.new(42, "km")
- a.send(@method).should == '#<data DataSpecs::Measure amount=42, unit="km">'
- end
-
- it "returns a string representation without the class name for anonymous structs" do
- Data.define(:a).new("").send(@method).should == '#<data a="">'
- end
-
- it "returns a string representation without the class name for structs nested in anonymous classes" do
- c = Class.new
- c.class_eval <<~DOC
- Foo = Data.define(:a)
- DOC
-
- c::Foo.new("").send(@method).should == '#<data a="">'
- end
-
- it "returns a string representation without the class name for structs nested in anonymous modules" do
- m = Module.new
- m.class_eval <<~DOC
- Foo = Data.define(:a)
- DOC
-
- m::Foo.new("").send(@method).should == '#<data a="">'
- end
-
- it "does not call #name method" do
- struct = DataSpecs::MeasureWithOverriddenName.new(42, "km")
- struct.send(@method).should == '#<data DataSpecs::MeasureWithOverriddenName amount=42, unit="km">'
- end
-
- it "does not call #name method when struct is anonymous" do
- klass = Class.new(DataSpecs::Measure) do
- def self.name
- "A"
- end
- end
- struct = klass.new(42, "km")
- struct.send(@method).should == '#<data amount=42, unit="km">'
- end
-
- context "recursive structure" do
- it "returns string representation with recursive attribute replaced with ..." do
- a = DataSpecs::Measure.allocate
- a.send(:initialize, amount: 42, unit: a)
-
- a.send(@method).should == "#<data DataSpecs::Measure amount=42, unit=#<data DataSpecs::Measure:...>>"
- end
-
- it "returns string representation with recursive attribute replaced with ... when an anonymous class" do
- klass = Class.new(DataSpecs::Measure)
- a = klass.allocate
- a.send(:initialize, amount: 42, unit: a)
-
- a.send(@method).should =~ /#<data amount=42, unit=#<data #<Class:0x.+?>:\.\.\.>>/
- end
- end
-end
diff --git a/spec/ruby/core/data/to_h_spec.rb b/spec/ruby/core/data/to_h_spec.rb
index 64816b7251..41925cf3b2 100644
--- a/spec/ruby/core/data/to_h_spec.rb
+++ b/spec/ruby/core/data/to_h_spec.rb
@@ -28,18 +28,18 @@ describe "Data#to_h" do
data = DataSpecs::Measure.new(amount: 42, unit: 'km')
-> do
data.to_h { |k, v| [k.to_s, v*v, 1] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
+ end.should.raise(ArgumentError, /element has wrong array length/)
-> do
data.to_h { |k, v| [k] }
- end.should raise_error(ArgumentError, /element has wrong array length/)
+ end.should.raise(ArgumentError, /element has wrong array length/)
end
it "raises TypeError if block returns something other than Array" do
data = DataSpecs::Measure.new(amount: 42, unit: 'km')
-> do
data.to_h { |k, v| "not-array" }
- end.should raise_error(TypeError, /wrong element type String/)
+ end.should.raise(TypeError, /wrong element type String/)
end
it "coerces returned pair to Array with #to_ary" do
@@ -57,7 +57,7 @@ describe "Data#to_h" do
-> do
data.to_h { |k| x }
- end.should raise_error(TypeError, /wrong element type MockObject/)
+ end.should.raise(TypeError, /wrong element type MockObject/)
end
end
end
diff --git a/spec/ruby/core/data/to_s_spec.rb b/spec/ruby/core/data/to_s_spec.rb
index 2b4a670e8e..e436c21109 100644
--- a/spec/ruby/core/data/to_s_spec.rb
+++ b/spec/ruby/core/data/to_s_spec.rb
@@ -1,6 +1,63 @@
require_relative '../../spec_helper'
-require_relative 'shared/inspect'
+require_relative 'fixtures/classes'
describe "Data#to_s" do
- it_behaves_like :data_inspect, :to_s
+ it "returns a string representation showing members and values" do
+ a = DataSpecs::Measure.new(42, "km")
+ a.to_s.should == '#<data DataSpecs::Measure amount=42, unit="km">'
+ end
+
+ it "returns a string representation without the class name for anonymous structs" do
+ Data.define(:a).new("").to_s.should == '#<data a="">'
+ end
+
+ it "returns a string representation without the class name for structs nested in anonymous classes" do
+ c = Class.new
+ c.class_eval <<~DOC
+ Foo = Data.define(:a)
+ DOC
+
+ c::Foo.new("").to_s.should == '#<data a="">'
+ end
+
+ it "returns a string representation without the class name for structs nested in anonymous modules" do
+ m = Module.new
+ m.class_eval <<~DOC
+ Foo = Data.define(:a)
+ DOC
+
+ m::Foo.new("").to_s.should == '#<data a="">'
+ end
+
+ it "does not call #name method" do
+ struct = DataSpecs::MeasureWithOverriddenName.new(42, "km")
+ struct.to_s.should == '#<data DataSpecs::MeasureWithOverriddenName amount=42, unit="km">'
+ end
+
+ it "does not call #name method when struct is anonymous" do
+ klass = Class.new(DataSpecs::Measure) do
+ def self.name
+ "A"
+ end
+ end
+ struct = klass.new(42, "km")
+ struct.to_s.should == '#<data amount=42, unit="km">'
+ end
+
+ context "recursive structure" do
+ it "returns string representation with recursive attribute replaced with ..." do
+ a = DataSpecs::Measure.allocate
+ a.send(:initialize, amount: 42, unit: a)
+
+ a.to_s.should == "#<data DataSpecs::Measure amount=42, unit=#<data DataSpecs::Measure:...>>"
+ end
+
+ it "returns string representation with recursive attribute replaced with ... when an anonymous class" do
+ klass = Class.new(DataSpecs::Measure)
+ a = klass.allocate
+ a.send(:initialize, amount: 42, unit: a)
+
+ a.to_s.should =~ /#<data amount=42, unit=#<data #<Class:0x.+?>:\.\.\.>>/
+ end
+ end
end
diff --git a/spec/ruby/core/data/with_spec.rb b/spec/ruby/core/data/with_spec.rb
index fd0a99d1fa..b74df185c7 100644
--- a/spec/ruby/core/data/with_spec.rb
+++ b/spec/ruby/core/data/with_spec.rb
@@ -28,7 +28,7 @@ describe "Data#with" do
-> {
data.with(4, "m")
- }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)")
+ }.should.raise(ArgumentError, "wrong number of arguments (given 2, expected 0)")
end
it "does not depend on the Data.new method" do
@@ -44,14 +44,12 @@ describe "Data#with" do
data_copy.unit.should == "m"
end
- ruby_version_is "3.3" do
- it "calls #initialize" do
- data = DataSpecs::DataWithOverriddenInitialize.new(42, "m")
- ScratchPad.clear
+ it "calls #initialize" do
+ data = DataSpecs::DataWithOverriddenInitialize.new(42, "m")
+ ScratchPad.clear
- data.with(amount: 0)
+ data.with(amount: 0)
- ScratchPad.recorded.should == [:initialize, [], {amount: 0, unit: "m"}]
- end
+ ScratchPad.recorded.should == [:initialize, [], {amount: 0, unit: "m"}]
end
end