[](https://travis-ci.org/kvokka/shrek) [](https://www.codacy.com/app/kvokka/shrek?utm_source=github.com&utm_medium=referral&utm_content=kvokka/shrek&utm_campaign=Badge_Grade) # Shrek [](https://www.youtube.com/watch?v=GZpcwKEIRCI) Minimalistic variation of nested builder pattern. The most popular pattern implementation is in `Rack::Middleware`. Extremely useful for organizing heavy processing and encapsulate every piece of logic. * Hint: perfect for achieving understanding, how Middleware works ## Installation Add this line to your application's Gemfile: ```ruby gem 'shrek' ``` And then execute: $ bundle Or install it yourself as: $ gem install shrek ## Usage Basic structure is `Layer`. Layer is self-sufficient piece of logic, which in ideal circumstances does not depend on any other Layer. Each `Layer` must have method `#call(*args)`, which must return an array, `args` structure in one domain scope does not have to be identical, but it is highly recommended. ``` class MyAwesomeLayer < Shrek::Layers def call(bilder, *args, **options) # Argument structure is up to you. # do something before next layer args = [bilder, *args, options] next_layer.call(*args) # Run next execution Layer # some post execution logic. If you add it, keep in mind, that you have to # return args for the next Layer rescue SomeError => e # We can handle the error here and re-raise it for futher Layers # in this order we can split messy error handling and keep everything clean next_layer.skip(2).call(*args) # Skip 2 layers and run next_layer.skip!(42).call(*args) # Laught version. Will Raise if stack is # not enough end end ``` You can see more [examples](https://github.com/kvokka/shrek/blob/master/spec/shrek/runner_acceptance_spec.rb) Then you will be able to use desired collection of Layers with `Shrek[MyAwesomeLayer, AnotherOne].call(*initial_arguments)` may be used in own classes ``` class Paint include Shrek end Paint.new.use_layers FindPaints, SelectWall, PrepareBrushes, self_return: ->(result) { a } ``` We also are able to make some tuning with returning value with `self_return` option. It use something, which respond_to :call. Let's summarize procs and cons of this concept: ##### Procs * Maximum encapsulation * Easy reordering * Each peace of logic handles only own part of the error * Readable high level interface with minimal noise * Standardize interface for one peace of domain logic * Static analyzers will be happy * Simple tests * Well documented by someone else ;) ##### Cons * One more abstraction layer * Can be over-engineering * Requires more memory * More code, more files ### Why do I need it? Lets try to put this little snippet of a tale into a code . Given: > Shrek wants to eat something, and he decides to do a slug soup. He got > slugs and then went to get some wood for the fireplace. When he came back > he put the wood into the fireplace and tried to make fire. But he did not find > matches, so he had to move the wood back. He then made shashimi from the > slugs and ate as much as he could.He put the rest of the food into the fridge. It is a simple business story, but think for a while, how will you solve it? And keep in mind, that in the real world a lot of things can go wrong and you want to be able to extend this story, when author decides, that Shrek also has to try to find the lighter or add some swamp spices for better flavor (or onion) The simplest way is ``` class ServiceKlass def initialize(*) # ... end def call # ... end end ``` But you will have to keep explicit error handler, and resolve all sort of errors there. You will not be able to share one error between parts of the service class, so you will do new error names or generalize errors (again, this pattern is for big and heavy tasks). So the error handler will grow (May be you also extract it ). At the end you will find yourself with a mess. Do not believe me? Ok, take default `Rails::Middleware` and try to rewrite it in procedural style this weekend (and do not plan anything else, you will have lot's of fun ;) Another way to handle this sort of problem is `composite` pattern (the first example of this pattern, which came to me is `ActiveRecord::Migration`). It will aim at the same goal, but also you will get DSL of the `composite`. Also you will not be able to have the ability of error splitting. Usually it is suitable. ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem to your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/shrek. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). ## Code of Conduct Everyone interacting in the Shrek project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/shrek/blob/master/CODE_OF_CONDUCT.md).