README.md in deadly_serious-0.12.0 vs README.md in deadly_serious-1.0.0.pre

- old
+ new

@@ -1,21 +1,29 @@ # DeadlySerious [![Gem Version](https://badge.fury.io/rb/deadly_serious.png)](http://badge.fury.io/rb/deadly_serious) +---- + +**Preparing version 1.0 - all interfaces are broken** + +---- + Flow Based Programming Maestro! This relies on [*named pipes*](http://linux.die.net/man/7/fifo) and *Linux processes* to create a program. Each component runs as a separate linux process and they exchange information through pipes. -**REQUIRES** Ruby 2.0 and a \*nix based OS (Operating System, tested on *Ubuntu* and *Arch Linux*) +That means it uses 'mechanical sympathy' with the Operating System, i.e., the S.O. is *part* of the program, it's not something *under* it. -Unlike [NoFlo](http://noflojs.org), this is not a real engine. It just "orchestrates" linux processes and pipes to create a flow based system. +Unlike [NoFlo](http://noflojs.org), this is not a real engine. It "orchestrates" linux processes and pipes to create flow based systems. +**REQUIRES** Ruby 2.1 and a \*nix based OS (Operating System, tested on *Ubuntu* and *Arch Linux*) + Overall, it's slower than a normal ruby program (the pipes add some overhead). However, there are 4 points where this approach is pretty interesting: 1. High modifiabilty: - * The interface between each component is tiny and very clear: it's just a stream of characteres. I usually use csv format or json when I need more structure than that. + * The interface between each component is tiny and very clear: it's just a stream of characteres. I usually use json format when I need more structure than that. * You can connect ruby process to anything that deals with STDIN, STDOUT or files (which includes shell commands, of course). 2. Cheap parallelism and distributed computation: * Each component runs as a separated process. The OS is in charge here (and it does an amazing work running things in parallel). * As any shell command can be used as a component, you can use a simple [ncat](http://nmap.org/ncat) (or something similar) to distribute jobs between different boxes. * It's really easy to avoid deadlocks and race conditions with the FBP paradigm. @@ -38,63 +46,62 @@ $ gem install deadly_serious ## Usage -### Basic pipeline +### Simple Usage Create a class that will orchestrate the pipeline: ```ruby #!/usr/bin/env ruby -# Assuming your are using RVM +require 'deadly_serious' +include DeadlySerious::Engine -class Pipeline < DeadlySerious::Engine::Spawner - def run_pipeline - # Here comes the code - end +pipeline = Pipeline.new do |p| + p.from_file('my_data_source.json') + # The command "spawn_lambda" assumes a JSON format + p.spawn_lambda { |a, b, writer:| writer << [b, a]} + p.spawn_lambda { |b, a, writer:| writer << [b.upcase, a.downcase]} + p.to_file('my_data_sink.json') end + # This line will alow you to run # it directly from the shell. -# -# Please, note that you fires the pipeline -# calling "run" not "run_pipeline". -Pipeline.new.run if __FILE__ == $0 +pipeline.run if __FILE__ == $0 ``` -You can spawn process the following way: +You can spawn shell commands: ```ruby -class Pipeline < DeadlySerious::Engine::Spawner - def run_pipeline +#!/usr/bin/env ruby +require 'deadly_serious' +include DeadlySerious::Engine - spawn_process(YourComponentClass, - readers: ['>an_awesome_text_file.txt'], # reads from a file - writers: ['your_first_output_pipe']) # outputs to a pipe - - spawn_process(YourOtherComponentClass, - readers: ['your_first_output_pipe'], - writers: ['more_pipe1', 'more_pipe2']) - - end +pipeline = Pipeline.new do |p| + p.from_file('my_data_source.txt') + p.spawn_command('grep something') + p.to_file('my_data_sink.txt') end + +pipeline.run if __FILE__ == $0 ``` -A component is any class with a "run" method and two named parameters "readers" and "writers": +You can spawn your own components: ```ruby -class EchoComponent - # "readers" and "writers" are both Array of IO objects. - def run(readers: [], writers: []) - reader = readers.first - writer = writers.first +#!/usr/bin/env ruby +require 'deadly_serious' +include DeadlySerious::Engine - reader.each_line do |line| - writer << line - end - end +pipeline = Pipeline.new do |p| + p.from_file('my_data_source.txt') + p.spawn_class(MyComponent) + p.to_file('my_data_sink.txt') end + +pipeline.run if __FILE__ == $0 ``` ### Pipes and files The parameters you receive in the "def run(readers: [], writers: [])" method are [**IO**](http://www.ruby-doc.org/core-2.0/IO.html) objects.