This topic is for the Sudoku Validator exercise in the Ruby Challenges trail. Post any questions, corrections, or pointers you have to share with other Upcase subscribers.
Continuing the discussion from New trail: Ruby Challenges:
Hi Philip!
I think you might be misreading the expectation.
It looks like this:
expect(result).to(
eq("This sudoku is invalid."),
"Expected #{fixture} to be invalid but it wasn't."
)
The second argument to to
is a string that provides a custom failure message when the expectation is not satisfied.
So, this code says āresult should be equal to āThis sudoku is invalid.ā, but if itās not, output the string 'Expected #{fixture_name} to be invalid but it wasnt.ā.
In other words, the Validator
does not know, and should not care about the name of the file.
Does that make sense?
More info on custom error messages: https://www.relishapp.com/rspec/rspec-expectations/v/3-2/docs/customized-message
You are, of course, quite correct. Thank you for the help.
I know several ways to āwrite a command line programā but none that could be constructed to fit this sample data:
$ sudoku-validator ./valid_complete.sudoku
This sudoku is valid.
$ sudoku-validator ./valid_incomplete.sudoku
This sudoku is valid, but incomplete.
$ sudoku-validator ./invalid_complete.sudoku
This sudoku is invalid.
$ sudoku-validator ./invalid_incomplete.sudoku
This sudoku is invalid.
This is what iām seeing. Iām in a directory we cloned called sudoku-validator:
git clone git@git.upcase.com:Midnightcoffee/sudoku-validator.git
cd sudoku-validator
bin/setup
Now from the output i should be able to run:
$ sudoku-validator ./valid_complete.sudoku
This sudoku is valid.
and see if the sudoku is valid or not. But sudoku-validator is the name of my directoryā¦ whats more ā./ā indicates an executable, which is odd sense valid_complete.sudoku is a .sudoku which from the directory contains the data about the sudokuā¦
ā sudoku-validator git:(master) ā cat spec/fixtures/invalid_due_to_column_dupe.sudoku
8 5 9 |6 1 2 |4 3 7
7 2 3 |8 5 4 |1 6 9
1 6 4 |3 7 9 |5 2 8
------+------+------
9 8 6 |1 4 7 |3 5 2
3 7 5 |8 6 2 |9 1 4
2 4 1 |5 9 3 |7 8 6
------+------+------
4 3 2 |9 8 1 |6 7 5
6 1 7 |4 2 5 |8 9 3
5 9 8 |7 3 6 |2 4 1
So there are several things that seem off to me. I get the problem, but not what the excecution is supposed to be, not at least from the descriptionā¦
Sorry about that! Thatās a typo in the instructions.
They should look like this:
$ ./sudoku-validator valid_complete.sudoku
Iāve updated them.
Thanks ben (is it ben?),
so inside the sudoku-validator folder i should create an executable sudoku-validator? Forgive me, iām still not a full fledged rubyiest yet. In python i would expect something like this:
./main.py some_data_main_acts_open.txt
with main.py containing something like
if __name__ == __main__:
file_name = args[1]
do_something_to_the_file(file_name)
what type do you expect sudoku-validator to be? does it not matter?
This file appears to be valid spec/fixtures/invalid_due_to_column_dupe.sudoku?
8 5 9 |6 1 2 |4 3 7
7 2 3 |8 5 4 |1 6 9
1 6 4 |3 7 9 |5 2 8
------Ā±-----Ā±-----
9 8 6 |1 4 7 |3 5 2
3 7 5 |8 6 2 |9 1 4
2 4 1 |5 9 3 |7 8 6
------Ā±-----Ā±-----
4 3 2 |9 8 1 |6 7 5
6 1 7 |4 2 5 |8 9 3
5 9 8 |7 3 6 |2 4 1
Am I missing something?
Thanks
Yep, the fourth column has two 8s in it.
Woopsā¦ (apparently your validator doesnāt like woopsā¦)
Hi all! Iām curious about the setup for this one. In particular, the approach to a class with one class method on it. The exercise started with the Validator
class with a class method like interface that instantiate a new object of that class.
class Validator
def initialize(puzzle_string)
@puzzle_string = puzzle_string
end
def self.validate(puzzle_string)
new(puzzle_string).validate
end
def validate
#code goes here
end
end
Is this pattern the common approach to the case where you need a single method interface? Iām familiar with the replace method with method object pattern, but why create an object? Why would you prefer this over something likeā¦
class Validator
def self.validate(puzzle_string)
#code goes here
end
end
I had a lot of fun with the exercise. Looking forward to chatting more about the approach!
Edit:
Interestingly, the exercise that follows this one, Ranking Poker Hands, uses the latter approach.
class HandEvaluator
def return_stronger_hand(left, right)
end
end
Hi, @timlombardo
I think you mean āclass methodā rather than āsingleton methodā? (See RubyMonk - Ruby Primer: Ascent - Singleton methods and metaclasses)
Regarding creating a new instance, the reason I usually do that is so that the object can have state, which makes it easier to simplify and refactor by extracting methods.
This post from Code Climate explains it well: Why Ruby Class Methods Resist Refactoring | Code Climate
Hey @andyw8 thanks for the response.
Yep, youāre correct! I meant class method, which I edited to prevent any more confusion later on.
Thanks for the insight and sharing the post. Sometimes with exercises the scope is narrow and fixed so I feel a bit of the YAGNI objection creeps in.
Iām really liking the object instance with convenience method pattern in this case.
@timlombardo you may also enjoy Meditations on a Class Method which talks about this exact pattern (class methods as syntactic sugar for creating an instance and calling a method on it)
Hi all! I have a design question regarding this exercise. Specifically, I would love to hear peopleās thoughts on if creating a SudokuPuzzle class, should an instance of that class have the ability to report on whether it is valid? and complete?
What Iām trying to get at is whether it is appropriate for a puzzle instance to be able to answer the question of whether it is valid or not, or complete or not. Or should that logic be better in a class that is specifically just for validating a puzzle?
If this is not clear, please let me know I can try to expand upon it.
Thank you!
Jonathan
I would say it depends on what other behaviour youāre planning to have in that SudukoPuzzle
class. Following the Single Responsibility Principle should help here.
I think there is a bug in the way the tests are set up, Iām getting
Failure/Error: result = Validator.validate(file)
Errno::ENOENT:
No such file or directory @ rb_sysopen - 8 5 9 |6 1 2 |4 3 7
7 2 3 |8 5 4 |1 6 9
1 6 4 |3 7 9 |5 2 8
------+------+------
9 8 6 |1 4 7 |3 5 2
3 7 5 |2 6 8 |9 1 4
2 4 1 |5 9 3 |7 8 6
------+------+------
4 3 2 |9 8 1 |6 7 5
6 1 7 |4 2 5 |8 9 3
5 9 8 |7 3 6 |2 4 1
# ./lib/validator.rb:3:in `initialize'
# ./lib/validator.rb:3:in `open'
# ./lib/validator.rb:3:in `initialize'
# ./lib/validator.rb:8:in `new'
# ./lib/validator.rb:8:in `validate'
# ./spec/end_to_end_integration_spec.rb:12:in `block (4 levels) in <top (required)>'
@askl56 I donāt think thereās a bug in the spec, but the naming of the variables is confusing:
file = File.read("spec/fixtures/valid_complete.sudoku")
result = Validator.validate(file)
File.read
returns a string rather than a File
instance, so file
should really be called something like file_contents
.
From the error output, it looks like the spec is passing a sudoku string to your implementation, but the implementation is expecting a file path.
I had a similar confusion, but managed to get the implementation requested in the challenge working properly using:
- a shebang in my ./sudoku-validator file to instruct the terminal to execute the contents of the file in ruby (i.e.
#!/usr/bin/env ruby
at the top of the file) - rubyās ARGV array in the ./sudoku-validator file. This is an array of any command line arguments given to a programme on execution. The string I pass to Validator.validate is therefore
file = File.read(ARGV[0])
-
puts
to get the string my validate method returns onto the command line
I was also careful to require the correct file(s) in the ./sudoku-validator file, and to use a valid path to a sudoku file when executing the programme (e.g../spec/fixtures/valid_complete.sudoku
)
Hope this helps!