No bo jak coś jest prywatne to możesz się do tego odwołać tylko z wewnątrz klasy. Poza tą klasą, ta metoda nie istnieje. Dlaczego w ogóle chcesz żeby ten setter był prywatny?
Wiem i uzywam go tylko i wyłącznie wewnątrz klasy MathSet, więc nie wiem o co chodzi?
Potrzebujego bo muszę zrobić przypisania w metodzie - i * .
class MathSet
attr_reader :elements
def initialize(*e)
@elements = e.uniq;
end
def count
@elements.length
end
def +(e)
if e.is_a? MathSet
e.elements.each do |elem|
@elements << elem unless @elements.include?(elem)
end
self
else
@elements << e unless @elements.include?(e);
self
end
end
def -(e)
if e.is_a? MathSet
new_set = MathSet.new
new_set.elements = @elements.reject { |elem| e.elements.include?(elem)}
new_set
else
if @elements.include?(e)
@elements.delete(e)
self
else
raise 'Element does not exist in this set!'
end
end
end
def *(set)
if set.is_a? MathSet
new_set = MathSet.new
new_set.elements = @elements.select{ |elem| set.elements.include?(elem)}
new_set
else
raise ArgumentError ,'Argument is not a set!'
end
end
private
attr_writer :elements
end
Uściślając: jeśli coś jest prywatne, to możesz się do tego odwołać tylko z wewnątrz tej samej klasy oraz tylko w obrębie tego samego obiektu. Cytując podręcznik: Każdy obiekt danej klasy może wywoływać metody prywatne tylko na rzecz samego siebie.
W linijce 26 tworzysz nowy obiekt MathSet (new_set), i linijkę niżej próbujesz użyć jego prywatnego settera elements z zewnątrz (czyli z aktualnego obiektu): new_set.elements = @elements.reject { |elem| e.elements.include?(elem)}
Stąd błąd.
Masz dwa wyjścia:
Zamiast private, użyj protected. Ten modyfikator działa właśnie tak, że pozwala na dostęp do metody (tutaj: settera) wszystkim obiektom danej klasy.
Zamiast używać settera w linijce 27 (i analogicznie dla operatora *), utwórz nowy obiekt MathSet z nowymi elementami przekazanymi w konstruktorze:
Wiem i nie testuję, dlatego właśnie nie wiedziałem czemu tak się dzieje.
Mój spec:
require_relative '../math_set'
describe MathSet do
describe '#count' do
before(:all) do
@set1 = MathSet.new
@set2 = MathSet.new(1,2,3,3)
end
it 'returns 0 when empty set' do
expect(@set1.count).to eq 0
end
it 'returns set size' do
expect(@set2.count).to eq 3
end
end
describe '#+' do
before(:each) do
@num = 5
@num1 = 3
@num2 = 4
@set1 = MathSet.new
@set2 = MathSet.new(@num1,@num2)
end
it 'adds element to set' do
@set1 + @num
expect(@set1.elements).to include(@num)
end
it 'unites two sets' do
set3 = @set1 + @set2
expect(set3.elements).to include(@num1)
expect(set3.elements).to include(@num2)
end
it 'does not repeat same elements' do
num1 = 3
num2 = 5
set = MathSet.new(num1,num2)
set = set + @set2
expect(set.elements).to include(num2)
expect(set.count).to eq 3
end
end
describe '#-' do
before(:each) do
@set1 = MathSet.new(2,3)
@num1 = 3
@not_existing_num = 7
end
describe 'element' do
describe 'if element exists in set' do
it 'removes it' do
@set1 - @num1
expect(@set1.elements).to_not include(@num1)
end
end
describe 'if element does not exits in set' do
it 'raises an exception' do
expect{@set1 - @not_existing_num}.to raise_error('Element does not exist in this set!')
end
end
end
describe 'sets' do
before(:each) do
@num = [1,2,3,4]
@set1 = MathSet.new(@num[1],@num[3],@num[2])
@set2 = MathSet.new(@num[1],@num[0])
end
it 'substracts two sets' do
set3 = @set1 - @set2
expect(set3.elements).to include(@num[3])
expect(set3.elements).to include(@num[2])
expect(set3.elements).to_not include(@num[1])
end
end
end
describe '#*' do
before(:each) do
@num = [1,2,3,4,5,6]
@set1 = MathSet.new(@num[1],@num[3],@num[2],@num[5])
@set2 = MathSet.new(@num[1],@num[0],@num[5],@num[2],@num[4])
end
describe 'sets' do
it 'returns intersection of two sets' do
set = @set1 * @set2
expect(set.elements).to include(@num[1])
expect(set.elements).to include(@num[5])
expect(set.elements).to include(@num[2])
expect(set.count).to eq 3
end
end
describe 'sth different' do
it 'raises ArgumentError' do
expect{ @set1 * ""}.to raise_error('Argument is not a set!')
end
end
end
end
Ja bym w ogóle nie wchodził w let i let! w tych testach. NIe ma praktycznie żadnych przesłanek dla których @set1, @set2 i im podobne muszą być tworzone w ogólnym kontekście. To się wydaje wygodne. Ale potem jesteś w 70 lini pliku testowego i musisz szukać po kaskadach kontekstów, letów i beforów w jakim tak naprawdę jesteś stanie. W zamian tego możesz sobie stworzyć stan w teście i wtedy od razu patrząc na test wiesz dokładnie co się dzieje. Najlepiej jeszcze zdefiniować helper metody dzięki którym lepiej się czyta test.
Więc zamiast tego
it 'returns 0 whhen empty set' do
expect(@set1.count).to eq 0
end
I patrząc na to nie masz pojęcia co to jest @set1 i trzeba iść w górę i szukać w jakim jesteś kontekście, możesz zrobić:
it 'returns 0 when empty set' do
empty_set = build_math_set([])
expect(empty_set.count).to eq(0)
end