{"id":28509117,"url":"https://github.com/atomicobject/piece_pipe","last_synced_at":"2026-03-08T08:32:54.211Z","repository":{"id":3446745,"uuid":"4499823","full_name":"atomicobject/piece_pipe","owner":"atomicobject","description":"PiecePipe helps you break your code into small interesting pieces and provides the glue for pipelining them together to provide elegant, readable code. ","archived":false,"fork":false,"pushed_at":"2021-04-11T14:48:27.000Z","size":31,"stargazers_count":53,"open_issues_count":0,"forks_count":4,"subscribers_count":31,"default_branch":"master","last_synced_at":"2025-06-08T22:08:04.201Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/atomicobject.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-05-30T20:20:12.000Z","updated_at":"2023-10-03T00:42:29.000Z","dependencies_parsed_at":"2022-09-07T07:10:38.297Z","dependency_job_id":null,"html_url":"https://github.com/atomicobject/piece_pipe","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/atomicobject/piece_pipe","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicobject%2Fpiece_pipe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicobject%2Fpiece_pipe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicobject%2Fpiece_pipe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicobject%2Fpiece_pipe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atomicobject","download_url":"https://codeload.github.com/atomicobject/piece_pipe/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicobject%2Fpiece_pipe/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261280386,"owners_count":23134906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-08T22:07:28.796Z","updated_at":"2026-03-08T08:32:54.206Z","avatar_url":"https://github.com/atomicobject.png","language":"Ruby","readme":"# PiecePipe\n\nPiecePipe is about breaking your problem into its smallest, most interesting pieces, solving those pieces and not spending time on the glue code between them.\n\n## Motivation\n\nThough this style of programming is similar to sequence processing using map, select and inject, PiecePipe\ngives us power to:\n\n* Improve expressiveness -- our high-level pipeline definition reads like a book and encompasses a comprehensible algorithm.\n* Address and test each of the interesting little steps.\n* Decouple ourselves from the implementation of map, select etc, which assume you're processing a collection, and have preconceptions on how they should be iterated over.\n\nFor sufficiently interesting algorithms we often talk in circles about how to break the problem down such that\nno one piece is too complicated, then glue it all together.  We end up with a tree of strangely named calculator\nobjects and an almost arbitrary sprinkling of looping/mapping constructs.  We got sick of trying to \norganize the glue and decided to see if we could just boil it down to \"what are the interesting operations, \nand what are the smallest pieces we can operate on?\"\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n    gem 'piece_pipe'\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install piece_pipe\n\n## Usage\n\n```ruby\nclass NuclearPowerPlantHealthSummaryGenerator\n  def generate(region)\n    PiecePipe::Pipeline.new.\n      source([{region: region}]).\n      step(FetchPowerPlantsByRegion).\n      step(FindWorstReactor).\n      step(DetermineStatusClass).\n      step(BuildPlantHealthSummary).\n      step(SortByRadiationLevelsDescending).\n      collect(:plant_health_summary).\n      to_enum\n  end\n\n  # Custom Step, overriding #generate_sequence to produce a sequence of power plants for a given region.\n  # Each power plant is \"produced\" as a Hash with one key (so far) heading down the pipeline.\n  class FetchPowerPlantsByRegion \u003c PiecePipe::Step\n    def generate_sequence(inputs)\n      # The expected interface of any Step's soure is that it supports #to_enum.  \n      source.to_enum.each do |inputs|\n        inputs[:region].power_plants.those_still_open.each do |power_plant|\n          produce power_plant: power_plant  # Each call to #produce sends another object down the pipe\n        end\n      end\n    enb\n  end\n\n  # For any given power plant, determine the worst reactor.\n  # Implemented as an AssemblyStep that analyzes inputs[:power_plant] from the prior Step,\n  # and installs a new key/val pair for :worst_reactor.  \n  class FindWorstReactor \u003c PiecePipe::AssemblyStep\n    def receive(inputs)\n      # Figure out which reactor has the highest radiation levels.\n      # \"install\" works a lot like \"produce\", but rather than take responsibility for the totality\n      # of the produced object, we're just saying \"add :worst_reactor to whatever's there and pass it on\".\n      install worst_reactor: inputs[:power_plant].reactors.reject(\u0026:offline?).max_by(:\u0026radiation)\n    end\n  end\n\n  # Figure out which CSS class corresonds to the radiation from the worst reactor.\n  # (At this point, the inputs Hash has keys :region, :power_plant,  and :worst_reactor.)\n  class DetermineStatusClass \u003c PiecePipe::AssemblyStep\n    def receive(inputs)\n      install highlight_css_class: StatusFormatters.determine_css_class(inputs[:worst_reactor].radiation)\n    end\n  end\n\n  # Composite our details into a line-item structure for our report.\n  # Even though we consume most of the interesting values that arrive in the inputs Hash,\n  # we're letting them ride as we simply install one more key, :plant_health_summary.\n  # (This comes in handy, as we intend to sort these structures in a later step, using values\n  # that are present in our transient input Hash, but NOT actually available in the \n  # report structure.)\n  class BuildPlantHealthSummary \u003c PiecePipe::AssemblyStep\n    def receive(inputs)\n      power_plant = inputs[:power_plant]\n      worst_reactor = inputs[:worst_reactor]\n      install plant_health_summary: PlantHealthSummary.new(\n        power_plant_id: power_plant.id,\n        supervisor: power_plant.supervisor,\n        reactor_name: worst_reactor.name,\n        radiation: StatusFormatters.format_radiation(worst_reactor.radiation),\n        css_class: inputs[:highlight_css_class]\n      )\n    end\n  end\n\n  # Sort all the values that come through the pipe based on the radiation of the worst reactor\n  # in each power_plant.\n  # Notice this is not an AssemblyStep, and we're overriding #generate_sequence again, this time\n  # because we're implementing a sink.  The resulting downstream objects have the same structure\n  # they arrived with.\n  class SortByRadiationLevelsDescending \u003c PiecePipe::Step\n    def generate_sequence\n      source.to_enum.sort_by do |inputs|\n        inputs[:worst_reactor].radiation\n      end.each do |inputs|\n        produce inputs\n      end\n    end\n  end\n\n  #... and that's it. Recall that the pipeline terminates with .collect(:plant_health_summary), which\n  # is shorthand for a special Step that accepts Hashes and uses #produce to spit out only the specified\n  # objects.  Downstream of our #collect, only the PlantHealthSummary remains.\n\nend\n```\n\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Added some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatomicobject%2Fpiece_pipe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatomicobject%2Fpiece_pipe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatomicobject%2Fpiece_pipe/lists"}