Backward Compatibility in Practice with Ruby

Backward Compatibility: Practice

Welcome back! Today, we'll master what we learned about backward compatibility in practice. Get prepared to apply all the knowledge on practice tasks, but first, let's look at two examples and analyze them.

Task 1: Enhancing a Complex Data Processing Method with Default Parameters

Let's say that initially, we have a complex data processing method designed to operate on an array of hashes, applying a transformation that converts all string values within the hashes to uppercase. Here's the initial version:

Ruby
def process_data(items)
  processed_items = items.map do |item|
    item.transform_values { |value| value.is_a?(String) ? value.upcase : value }
  end
  processed_items.first(3).each { |item| puts "Processed Item: #{item}" } # Display the first 3 items for brevity
end

We intend to expand this method, adding capabilities to filter the items based on a condition and allow for custom transformations. The aim is to retain backward compatibility while introducing these enhancements. Here's the updated approach:

def process_data(items, condition: ->(x) { true }, transform: nil)
  processed_items = items.select(&condition).map do |item|
    if transform
      transform.call(item)
    else
      # Default transformation: Convert string values to uppercase
      item.transform_values { |value| value.is_a?(String) ? value.upcase : value }
    end
  end
  processed_items.first(3).each { |item| puts "Processed Item: #{item}" } # Display the first 3 items for brevity
end

# Default behavior - convert string values to uppercase
process_data([{ "name" => "apple", "quantity" => 10 }, { "name" => "orange", "quantity" => 5 }])

# Custom filter - select items with a quantity greater than 5
process_data([{ "name" => "apple", "quantity" => 10 }, { "name" => "orange", "quantity" => 5 }], condition: ->(x) { x["quantity"] > 5 })

# Custom transformation - convert names to uppercase and multiply the quantity by 2
custom_transform = ->(item) { item.transform_keys { |key| item[key].upcase! if key == "name"; key }.transform_values { |value| value.is_a?(Numeric) ? value * 2 : value } }
process_data([{ "name" => "apple", "quantity" => 10 }, { "name" => "orange", "quantity" => 5 }], transform: custom_transform)

In this evolved version, we've introduced two optional parameters: condition, a Proc to filter the input array based on a given condition, and transform, a Proc for custom transformations of the filtered items. The default behavior processes all items, converting string values to uppercase, which ensures that the original method's behavior is maintained for existing code paths. This robust enhancement strategy facilitates adding new features to a method with significant complexity while preserving backward compatibility, showcasing an advanced application of evolving software capabilities responsively and responsibly.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal