diff options
Diffstat (limited to 'spec/ruby/core/data')
| -rw-r--r-- | spec/ruby/core/data/constants_spec.rb | 20 | ||||
| -rw-r--r-- | spec/ruby/core/data/deconstruct_keys_spec.rb | 130 | ||||
| -rw-r--r-- | spec/ruby/core/data/deconstruct_spec.rb | 8 | ||||
| -rw-r--r-- | spec/ruby/core/data/define_spec.rb | 48 | ||||
| -rw-r--r-- | spec/ruby/core/data/eql_spec.rb | 63 | ||||
| -rw-r--r-- | spec/ruby/core/data/equal_value_spec.rb | 63 | ||||
| -rw-r--r-- | spec/ruby/core/data/fixtures/classes.rb | 31 | ||||
| -rw-r--r-- | spec/ruby/core/data/hash_spec.rb | 25 | ||||
| -rw-r--r-- | spec/ruby/core/data/initialize_spec.rb | 142 | ||||
| -rw-r--r-- | spec/ruby/core/data/inspect_spec.rb | 6 | ||||
| -rw-r--r-- | spec/ruby/core/data/members_spec.rb | 21 | ||||
| -rw-r--r-- | spec/ruby/core/data/shared/inspect.rb | 62 | ||||
| -rw-r--r-- | spec/ruby/core/data/to_h_spec.rb | 63 | ||||
| -rw-r--r-- | spec/ruby/core/data/to_s_spec.rb | 6 | ||||
| -rw-r--r-- | spec/ruby/core/data/with_spec.rb | 57 |
15 files changed, 666 insertions, 79 deletions
diff --git a/spec/ruby/core/data/constants_spec.rb b/spec/ruby/core/data/constants_spec.rb index 2eb43d501e..ad0b1ddea7 100644 --- a/spec/ruby/core/data/constants_spec.rb +++ b/spec/ruby/core/data/constants_spec.rb @@ -1,21 +1,11 @@ require_relative '../../spec_helper' -ruby_version_is ''...'3.2' do - describe "Data" do - it "does not exist anymore" do - Object.should_not have_constant(:Data) - end +describe "Data" do + it "is a new constant" do + Data.superclass.should == Object end -end - -ruby_version_is '3.2' do - describe "Data" do - it "is a new constant" do - Data.superclass.should == Object - end - it "is not deprecated" do - -> { Data }.should_not complain - end + it "is not deprecated" do + -> { Data }.should_not complain end end diff --git a/spec/ruby/core/data/deconstruct_keys_spec.rb b/spec/ruby/core/data/deconstruct_keys_spec.rb new file mode 100644 index 0000000000..df378f8b98 --- /dev/null +++ b/spec/ruby/core/data/deconstruct_keys_spec.rb @@ -0,0 +1,130 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Data#deconstruct_keys" do + it "returns a hash of attributes" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) + + d.deconstruct_keys([:x, :y]).should == {x: 1, y: 2} + end + + it "requires one argument" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) + + -> { + d.deconstruct_keys + }.should raise_error(ArgumentError, /wrong number of arguments \(given 0, expected 1\)/) + end + + it "returns only specified keys" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) + + d.deconstruct_keys([:x, :y]).should == {x: 1, y: 2} + d.deconstruct_keys([:x] ).should == {x: 1} + d.deconstruct_keys([] ).should == {} + end + + it "accepts string attribute names" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) + + 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) + + d.deconstruct_keys([:x, :y, :x]).should == {} + end + + it "returns at first not existing attribute name" do + klass = Data.define(:x, :y) + d = klass.new(1, 2) + + d.deconstruct_keys([:a, :x]).should == {} + 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) + + 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) + + key = mock("to_int") + key.should_receive(:to_int).and_return(1) + + d.deconstruct_keys([key]).should == { key => 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) + + key = mock("to_int") + key.should_receive(:to_int).and_return("not an Integer") + + -> { + d.deconstruct_keys([key]) + }.should raise_error(TypeError, /can't convert MockObject to Integer/) + end + + 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) + + -> { + d.deconstruct_keys([0, []]) + }.should raise_error(TypeError, "no implicit conversion of Array into Integer") + 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/) + end +end diff --git a/spec/ruby/core/data/deconstruct_spec.rb b/spec/ruby/core/data/deconstruct_spec.rb new file mode 100644 index 0000000000..4ca0b87039 --- /dev/null +++ b/spec/ruby/core/data/deconstruct_spec.rb @@ -0,0 +1,8 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Data#deconstruct" do + it "returns an array of attribute values" do + DataSpecs::Measure.new(42, "km").deconstruct.should == [42, "km"] + end +end diff --git a/spec/ruby/core/data/define_spec.rb b/spec/ruby/core/data/define_spec.rb index 2aa2c50d4c..c0b4671e39 100644 --- a/spec/ruby/core/data/define_spec.rb +++ b/spec/ruby/core/data/define_spec.rb @@ -1,36 +1,34 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -ruby_version_is "3.2" do - describe "Data.define" do - it "accepts no arguments" do - empty_data = Data.define - empty_data.members.should == [] - end +describe "Data.define" do + it "accepts no arguments" do + empty_data = Data.define + empty_data.members.should == [] + end - it "accepts symbols" do - movie = Data.define(:title, :year) - movie.members.should == [:title, :year] - end + it "accepts symbols" do + movie = Data.define(:title, :year) + movie.members.should == [:title, :year] + end - it "accepts strings" do - movie = Data.define("title", "year") - movie.members.should == [:title, :year] - end + it "accepts strings" do + movie = Data.define("title", "year") + movie.members.should == [:title, :year] + end - it "accepts a mix of strings and symbols" do - movie = Data.define("title", :year, "genre") - movie.members.should == [:title, :year, :genre] - end + it "accepts a mix of strings and symbols" do + movie = Data.define("title", :year, "genre") + movie.members.should == [:title, :year, :genre] + end - it "accepts a block" do - movie = Data.define(:title, :year) do - def title_with_year - "#{title} (#{year})" - end + it "accepts a block" do + movie = Data.define(:title, :year) do + def title_with_year + "#{title} (#{year})" end - movie.members.should == [:title, :year] - movie.new("Matrix", 1999).title_with_year.should == "Matrix (1999)" end + movie.members.should == [:title, :year] + movie.new("Matrix", 1999).title_with_year.should == "Matrix (1999)" end end diff --git a/spec/ruby/core/data/eql_spec.rb b/spec/ruby/core/data/eql_spec.rb new file mode 100644 index 0000000000..6958d5de4a --- /dev/null +++ b/spec/ruby/core/data/eql_spec.rb @@ -0,0 +1,63 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Data#eql?" do + it "returns true if the other is the same object" do + a = DataSpecs::Measure.new(42, "km") + a.should.eql?(a) + end + + it "returns true if the other has all the same fields" do + a = DataSpecs::Measure.new(42, "km") + b = DataSpecs::Measure.new(42, "km") + a.should.eql?(b) + end + + it "returns false if the other is a different object or has different fields" do + a = DataSpecs::Measure.new(42, "km") + b = DataSpecs::Measure.new(42, "mi") + a.should_not.eql?(b) + end + + it "returns false if other is of a different class" do + a = DataSpecs::Measure.new(42, "km") + klass = Data.define(*DataSpecs::Measure.members) + b = klass.new(42, "km") + a.should_not.eql?(b) + end + + it "returns false if any corresponding elements are not equal with #eql?" do + a = DataSpecs::Measure.new(42, "km") + b = DataSpecs::Measure.new(42.0, "mi") + a.should_not.eql?(b) + end + + context "recursive structure" do + it "returns true the other is the same object" do + a = DataSpecs::Measure.allocate + a.send(:initialize, amount: 42, unit: a) + + a.should.eql?(a) + end + + it "returns true if the other has all the same fields" do + a = DataSpecs::Measure.allocate + a.send(:initialize, amount: 42, unit: a) + + b = DataSpecs::Measure.allocate + b.send(:initialize, amount: 42, unit: b) + + a.should.eql?(b) + end + + it "returns false if any corresponding elements are not equal with #eql?" do + a = DataSpecs::Measure.allocate + a.send(:initialize, amount: a, unit: "km") + + b = DataSpecs::Measure.allocate + b.send(:initialize, amount: b, unit: "mi") + + a.should_not.eql?(b) + end + end +end diff --git a/spec/ruby/core/data/equal_value_spec.rb b/spec/ruby/core/data/equal_value_spec.rb new file mode 100644 index 0000000000..d9a0dcff3e --- /dev/null +++ b/spec/ruby/core/data/equal_value_spec.rb @@ -0,0 +1,63 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Data#==" do + it "returns true if the other is the same object" do + a = DataSpecs::Measure.new(42, "km") + a.should == a + end + + it "returns true if the other has all the same fields" do + a = DataSpecs::Measure.new(42, "km") + b = DataSpecs::Measure.new(42, "km") + a.should == b + end + + it "returns false if the other is a different object or has different fields" do + a = DataSpecs::Measure.new(42, "km") + b = DataSpecs::Measure.new(42, "mi") + a.should_not == b + end + + it "returns false if other is of a different class" do + a = DataSpecs::Measure.new(42, "km") + klass = Data.define(*DataSpecs::Measure.members) + b = klass.new(42, "km") + a.should_not == b + end + + it "returns false if any corresponding elements are not equal with #==" do + a = DataSpecs::Measure.new(42, "km") + b = DataSpecs::Measure.new(42.0, "mi") + a.should_not == b + end + + context "recursive structure" do + it "returns true the other is the same object" do + a = DataSpecs::Measure.allocate + a.send(:initialize, amount: 42, unit: a) + + a.should == a + end + + it "returns true if the other has all the same fields" do + a = DataSpecs::Measure.allocate + a.send(:initialize, amount: 42, unit: a) + + b = DataSpecs::Measure.allocate + b.send(:initialize, amount: 42, unit: b) + + a.should == b + end + + it "returns false if any corresponding elements are not equal with #==" do + a = DataSpecs::Measure.allocate + a.send(:initialize, amount: a, unit: "km") + + b = DataSpecs::Measure.allocate + b.send(:initialize, amount: b, unit: "mi") + + a.should_not == b + end + end +end diff --git a/spec/ruby/core/data/fixtures/classes.rb b/spec/ruby/core/data/fixtures/classes.rb index 46a6b48bb2..650c0b2a62 100644 --- a/spec/ruby/core/data/fixtures/classes.rb +++ b/spec/ruby/core/data/fixtures/classes.rb @@ -1,5 +1,34 @@ module DataSpecs - guard -> { ruby_version_is "3.2" and Data.respond_to?(:define) } do + if Data.respond_to?(:define) Measure = Data.define(:amount, :unit) + + class MeasureWithOverriddenName < Measure + def self.name + "A" + end + end + + class DataSubclass < Data; end + + MeasureSubclass = Class.new(Measure) do + def initialize(amount:, unit:) + super + end + end + + Empty = Data.define() + + DataWithOverriddenInitialize = Data.define(:amount, :unit) do + def initialize(*rest, **kw) + super + 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 new file mode 100644 index 0000000000..c23f08a71d --- /dev/null +++ b/spec/ruby/core/data/hash_spec.rb @@ -0,0 +1,25 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Data#hash" do + it "returns the same integer for objects with the same content" 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) + end + + it "returns different hashes for objects with different values" do + a = DataSpecs::Measure.new(42, "km") + b = DataSpecs::Measure.new(42, "ml") + a.hash.should_not == b.hash + + a = DataSpecs::Measure.new(42, "km") + b = DataSpecs::Measure.new(13, "km") + a.hash.should_not == b.hash + end + + it "returns different hashes for different classes" do + Data.define(:x).new(1).hash.should != Data.define(:x).new(1).hash + end +end diff --git a/spec/ruby/core/data/initialize_spec.rb b/spec/ruby/core/data/initialize_spec.rb index 94470cd108..010c73b91b 100644 --- a/spec/ruby/core/data/initialize_spec.rb +++ b/spec/ruby/core/data/initialize_spec.rb @@ -1,58 +1,124 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' -ruby_version_is "3.2" do - describe "Data#initialize" do - it "accepts positional arguments" do - data = DataSpecs::Measure.new(42, "km") +describe "Data#initialize" do + it "accepts positional arguments" do + data = DataSpecs::Measure.new(42, "km") - data.amount.should == 42 - data.unit.should == "km" - end + data.amount.should == 42 + data.unit.should == "km" + end - it "accepts alternative positional arguments" do - data = DataSpecs::Measure[42, "km"] + it "accepts alternative positional arguments" do + data = DataSpecs::Measure[42, "km"] - data.amount.should == 42 - data.unit.should == "km" - end + data.amount.should == 42 + data.unit.should == "km" + end + + it "accepts keyword arguments" do + data = DataSpecs::Measure.new(amount: 42, unit: "km") + + data.amount.should == 42 + data.unit.should == "km" + end + + it "accepts alternative keyword arguments" do + data = DataSpecs::Measure[amount: 42, unit: "km"] + + data.amount.should == 42 + data.unit.should == "km" + end + + it "accepts String keyword arguments" do + data = DataSpecs::Measure.new("amount" => 42, "unit" => "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| + e.message.should.include?("missing keywords: :amount, :unit") + } + end + + it "raises ArgumentError if at least one argument is missing" do + -> { + DataSpecs::Measure.new(unit: "km") + }.should raise_error(ArgumentError) { |e| + e.message.should.include?("missing keyword: :amount") + } + end + + it "raises ArgumentError if unknown keyword is given" do + -> { + DataSpecs::Measure.new(amount: 42, unit: "km", system: "metric") + }.should raise_error(ArgumentError) { |e| + e.message.should.include?("unknown keyword: :system") + } + end + + it "supports super from a subclass" do + ms = DataSpecs::MeasureSubclass.new(amount: 1, unit: "km") + + ms.amount.should == 1 + ms.unit.should == "km" + end + + it "supports Data with no fields" do + -> { DataSpecs::Empty.new }.should_not raise_error + end - it "accepts keyword arguments" do - data = DataSpecs::Measure.new(amount: 42, unit: "km") + it "can be overridden" do + ScratchPad.record [] - data.amount.should == 42 - data.unit.should == "km" + measure_class = Data.define(:amount, :unit) do + def initialize(*, **) + super + ScratchPad << :initialize + end end - it "accepts alternative keyword arguments" do - data = DataSpecs::Measure[amount: 42, unit: "km"] + measure_class.new(42, "m") + ScratchPad.recorded.should == [:initialize] + end - data.amount.should == 42 - data.unit.should == "km" + context "when it is overridden" do + it "is called with keyword arguments when given positional arguments" do + ScratchPad.clear + DataSpecs::DataWithOverriddenInitialize.new(42, "m") + ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}] end - it "raises ArgumentError if no arguments are given" do - -> { - DataSpecs::Measure.new - }.should raise_error(ArgumentError) { |e| - e.message.should.include?("missing keywords: :amount, :unit") - } + it "is called with keyword arguments when given keyword arguments" do + ScratchPad.clear + DataSpecs::DataWithOverriddenInitialize.new(amount: 42, unit: "m") + ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}] end - it "raises ArgumentError if at least one argument is missing" do - -> { - DataSpecs::Measure.new(unit: "km") - }.should raise_error(ArgumentError) { |e| - e.message.should.include?("missing keyword: :amount") - } + it "is called with keyword arguments when given alternative positional arguments" do + ScratchPad.clear + DataSpecs::DataWithOverriddenInitialize[42, "m"] + ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}] end - it "raises ArgumentError if unknown keyword is given" do - -> { - DataSpecs::Measure.new(amount: 42, unit: "km", system: "metric") - }.should raise_error(ArgumentError) { |e| - e.message.should.include?("unknown keyword: :system") - } + it "is called with keyword arguments when given alternative keyword arguments" do + ScratchPad.clear + DataSpecs::DataWithOverriddenInitialize[amount: 42, unit: "m"] + ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}] + 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 new file mode 100644 index 0000000000..38642910a0 --- /dev/null +++ b/spec/ruby/core/data/inspect_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/inspect' + +describe "Data#inspect" do + it_behaves_like :data_inspect, :inspect +end diff --git a/spec/ruby/core/data/members_spec.rb b/spec/ruby/core/data/members_spec.rb new file mode 100644 index 0000000000..457a90a0d6 --- /dev/null +++ b/spec/ruby/core/data/members_spec.rb @@ -0,0 +1,21 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Data#members" do + it "returns an array of attribute names" do + measure = DataSpecs::Measure.new(amount: 42, unit: 'km') + measure.members.should == [:amount, :unit] + end +end + +describe "DataClass#members" do + it "returns an array of attribute names" do + DataSpecs::Measure.members.should == [:amount, :unit] + end + + context "class inheriting Data" do + it "isn't available in a subclass" do + DataSpecs::DataSubclass.should_not.respond_to?(:members) + end + end +end diff --git a/spec/ruby/core/data/shared/inspect.rb b/spec/ruby/core/data/shared/inspect.rb new file mode 100644 index 0000000000..6cd5664da7 --- /dev/null +++ b/spec/ruby/core/data/shared/inspect.rb @@ -0,0 +1,62 @@ +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 new file mode 100644 index 0000000000..64816b7251 --- /dev/null +++ b/spec/ruby/core/data/to_h_spec.rb @@ -0,0 +1,63 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Data#to_h" do + it "transforms the data object into a hash" do + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + data.to_h.should == { amount: 42, unit: 'km' } + end + + context "with block" do + it "transforms [key, value] pairs returned by the block into a hash" do + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + data.to_h { |key, value| [value, key] }.should == { 42 => :amount, 'km' => :unit } + end + + it "passes to a block each pair's key and value as separate arguments" do + ScratchPad.record [] + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + data.to_h { |k, v| ScratchPad << [k, v]; [k, v] } + ScratchPad.recorded.sort.should == [[:amount, 42], [:unit, 'km']] + + ScratchPad.record [] + data.to_h { |*args| ScratchPad << args; [args[0], args[1]] } + ScratchPad.recorded.sort.should == [[:amount, 42], [:unit, 'km']] + end + + it "raises ArgumentError if block returns longer or shorter array" 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/) + + -> do + data.to_h { |k, v| [k] } + end.should raise_error(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 + + it "coerces returned pair to Array with #to_ary" do + x = mock('x') + x.stub!(:to_ary).and_return([:b, 'b']) + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + + data.to_h { |k| x }.should == { :b => 'b' } + end + + it "does not coerce returned pair to Array with #to_a" do + x = mock('x') + x.stub!(:to_a).and_return([:b, 'b']) + data = DataSpecs::Measure.new(amount: 42, unit: 'km') + + -> do + data.to_h { |k| x } + end.should raise_error(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 new file mode 100644 index 0000000000..2b4a670e8e --- /dev/null +++ b/spec/ruby/core/data/to_s_spec.rb @@ -0,0 +1,6 @@ +require_relative '../../spec_helper' +require_relative 'shared/inspect' + +describe "Data#to_s" do + it_behaves_like :data_inspect, :to_s +end diff --git a/spec/ruby/core/data/with_spec.rb b/spec/ruby/core/data/with_spec.rb new file mode 100644 index 0000000000..fd0a99d1fa --- /dev/null +++ b/spec/ruby/core/data/with_spec.rb @@ -0,0 +1,57 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Data#with" do + it "returns self if given no arguments" do + data = DataSpecs::Measure.new(amount: 42, unit: "km") + data = data.with.should.equal?(data) + end + + it "accepts keyword arguments" do + data = DataSpecs::Measure.new(amount: 42, unit: "km") + data = data.with(amount: 4, unit: "m") + + data.amount.should == 4 + data.unit.should == "m" + end + + it "accepts String keyword arguments" do + data = DataSpecs::Measure.new(amount: 42, unit: "km") + data = data.with("amount" => 4, "unit" => "m") + + data.amount.should == 4 + data.unit.should == "m" + end + + it "raises ArgumentError if no keyword arguments are given" do + data = DataSpecs::Measure.new(amount: 42, unit: "km") + + -> { + data.with(4, "m") + }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)") + end + + it "does not depend on the Data.new method" do + subclass = Class.new(DataSpecs::Measure) + data = subclass.new(amount: 42, unit: "km") + + def subclass.new(*) + raise "Data.new is called" + end + + data_copy = data.with(unit: "m") + data_copy.amount.should == 42 + data_copy.unit.should == "m" + end + + ruby_version_is "3.3" do + it "calls #initialize" do + data = DataSpecs::DataWithOverriddenInitialize.new(42, "m") + ScratchPad.clear + + data.with(amount: 0) + + ScratchPad.recorded.should == [:initialize, [], {amount: 0, unit: "m"}] + end + end +end |
