Single Responsibility Principle Discussion
Also with some design patterns that follow along with this principles.
Chef/Ansible/Puppet/Salt/Terraform are all added DSLs/other things to learn. If all you need is a basic automation tool, and you're not going to need more, than doing it in Rails will be fine.
Looking forward to seeing your videos on those other tools for how you would do it.
Your second mistake was thinking that the application mattered, when clearly it just serves as a vehicle for illustrating the SRP.
Your final mistake is in looking at the result and seeing Rails, when one of the major outcomes by the end of the episode is that the provisioning code is now decoupled from Rails.
It seems like you've completely misunderstood the point of this video and point of hatchbox.io. (without meaning to sound harsh of course).
To answer your question, it is perfectly appropriate to use rails when building something like hatchbox.io when you're a rails developer. In fact it would be a strange decision to build it in anything else, do you not agree?
To say this flies in the face of the Single Responsibility Principle suggests you've not yet grasped it but keep trying, we're all here to learn! Good luck with your development!
Some people might misunderstand what happened here and end up with the (unfortunately common) misunderstanding that SRP means "one method per class".
What Chris did is refactor a set of domain-specific behaviours from being collected in one class, to each being classes in their own right. The next step might be to compose them back together as necessary, without over-engineering the infrastructure that does so.
The example I personally like to give is extracting business rules implemented as "just code" into Rule classes, and then delivering new and useful outcomes by re-ordering rule objects or using subclasses and variants.
For the Rails programmer this is hopefully all in contrast to using Concerns which are basically just a way to chop up fat models into separate files and don't lead to new and interesting runtime structures.
Interesting. I'm having trouble knowing how small to make my classes. I've seen different articles saying you should only have one public method per class.
Should I create one PriceCalculator service where I can ask it for things like sale_price, material_cost, fixed cost, variable_cost, production_cost...
Or should I make independant classes for each of these? That way sale_price would be one calling the other classes to get to the final price.
My biggest concern separating them all into classes is that material_cost, for example, is is called in different classes, repeting calculations that I have already done.
Under one big SalePrice class, I avoided calculating the same thing multiple times because I would set class variables that would be accessible by all methods.
I usually anticipate having more than one such calculator, because very often, pricing is a classic case for the Strategy pattern. This immediately informs the granularity because each strategy has a name, and the methods are the things that might vary from one namable strategy to the next.
A typical scenario is a tax calculator. A given invoice has a specific tax treatment depending on the customer or the location. The calculator object is instantiated for the invoice and embodies all the expertise necessary to be responsible for answering tax-related questions such as rate per item, handles special rates, due dates, can list the available rates, and calculates various answers as required.
That is one responsibility: it represents the domain knowledge of a specific tax treatment.
One-method-per-class is a naive rule for SRP that takes a mechanical, not domain-centric view of software and I disregard any such advice. Its a slippery slope from there into service objects and other antipatterns.
Chris's objects in the screencast naturally gravitate to one method, not due to the SRP, but because he's also implementing the command pattern for which the fundamental method is usually #perform. Notwithstanding which, command pattern implementations often have a bunch of other methods because they may also participate in a framework with logging, transactions, progress reporting, undo etc.
NB: for calculators, I'd suggest at most one instantiation parameter, usually a domain entity, and avoid internal state except for that entity object and maybe some value caching, because responses from calculators like should be consistent and nullipotent, or at least idempotent.
I have two questions:
1) What do you think about turning the Server::SSH class into a module, and mixing it in into the two other sever classes (instead of using inheritance).
2) Using service objects to do stuff is a similar technique right ?
I agree with you on the first point. Assuming Server::SSH class represents an SSH connection, the two other server classes are not really "types" of an SSH connection. So it's probably better to use a module to extract the common start_ssh behavior and then include that module in the server classes.