Shifty is a Ruby framework designed for building data processing pipelines. It utilizes a system of cooperatively multitasking "workers" that operate in a single thread. Each worker performs a specific task and can pass data to the next worker in a chain, allowing for the creation of complex data flows in a manageable and sequential manner.
Shifty is well-suited for a variety of scenarios where data needs to be processed in a step-by-step fashion. Consider using Shifty for:
- Building Data Processing Pipelines: When your task can be broken down into a series of sequential steps, where each step transforms or enriches the data. Shifty allows you to encapsulate each step within a dedicated worker.
- ETL-like Workflows: For scenarios that involve extracting data from a source, transforming it according to certain rules, and then loading it elsewhere. While Shifty is primarily focused on the in-application transformation aspects, it can be a valuable part of a larger ETL process within a Ruby application.
- Cooperative Multitasking Scenarios: When you have multiple tasks that can effectively "take turns" executing. This is particularly useful if tasks involve waiting for non-blocking operations (though the current version of Shifty operates synchronously) or involve complex stateful interactions that are simpler to manage cooperatively rather than with preemptive threading.
- Simplifying Complex Sequential Logic: If you have a long, monolithic process with many stages, Shifty can help by breaking it down into a chain of smaller, focused, and more understandable workers. This improves modularity and maintainability.
- Creating Reusable Components: Shifty encourages the design of workers that perform specific, well-defined tasks. These workers can then be reused and recombined in different pipelines for various purposes, promoting code reuse.
Here are a few conceptual examples of how Shifty could be applied:
-
Log Processing: Imagine a pipeline for processing application logs:
- A
FileReaderWorkerreads log lines from a file. - A
LogParserWorkerparses each line into a structured format (e.g., timestamp, level, message). - A
ErrorFilterWorkerchecks the parsed log data and only passes on entries marked as "ERROR" or "CRITICAL". - A
ReportFormatterWorkerformats these error entries into a human-readable report. - A
FileWriterWorkerorEmailNotifierWorkeroutputs the report.
- A
-
Data Transformation Chain: A simple pipeline demonstrating data manipulation:
- A
NumberSourceWorkergenerates a sequence of numbers (e.g., 1, 2, 3, ...). - A
MultiplierWorkerreceives each number and multiplies it by 2. - A
StringFormatterWorkerconverts the multiplied number into a string, perhaps adding a prefix (e.g., "RESULT: 4"). - A
ConsoleOutputWorkerprints the final formatted string to the console.
- A
-
Batch Processing: Accumulating data into batches before further processing:
- An
ItemStreamWorkerproduces individual items (e.g., from a database query or an incoming data stream). - A
BatchWorker(Shifty provides abatch_workerfor this purpose) accumulates these items. It passes the accumulated batch to the next worker once a certain number of items are collected or a timeout occurs. - A
BatchProcessorWorkerthen processes the entire batch of items at once (e.g., bulk database insert, writing to a file).
- An
The examples above pass data from one worker to the next. Because Shifty runs
each value through every worker before starting the next value, workers must
treat a handed-off value as read-only and express changes as new values
(arr + [x], hash.merge(...), value.with(...)) rather than mutating in
place (arr <<, hash[k] =, map!). As of 0.6.0 this is enforced: values
are deeply frozen at every handoff by default, and a task that mutates its
input raises Shifty::PolicyViolation at the offending worker. Workers that
genuinely need a private scratch copy can declare policy: :isolated; workers
that need a shared mutable reference can declare policy: :shared. See the
wiki's Handoff Policies
and Coding Idioms Under :frozen
pages, and the Migration Guide.
Shifty aims to provide an intuitive and straightforward way to construct data processing systems within Ruby applications. By breaking down complex tasks into manageable, cooperatively multitasking workers, it helps in building modular, maintainable, and easy-to-understand data pipelines.