OPatch is ruby objects patcher in declarative way. By providing simple DSL it allows you write complex patch logic with ease
Let's say you have the folowing plain ruby classes:
class Person
attr_accessor :name, :address
def initialize(name:, address: nil)
@name = name
@address = address
end
end
class Address
attr_accessor :city, :country
def initialize(city:, country:)
@city = city
@country = country
end
end
And you have the following instance of the Person class:
person = Person.new(name: "John Smith", address: Address.new(city: "Kazan", country: "Russia"))
Now you want to update person's name and address fields, let's use OPatch for that. You need to call OPatch with attributes you want to update:
new_attributes = { name: "Jim White", address: { country: "USA", city: "New York" } }
OPatch.patch(person, new_attributes) do
field :name
object :address do
field :country
field :city
end
end
That's all! Now you have the person updated:
=> #<Person:0x007f8bc42487c0 @name="Jim White", @address=#<Address:0x007f8bc4248838 @city="New York", @country="USA">>
Opatch allows you to build nested objects if you specify build block. Lets see how it works if we want to build address when it wasn't created initialilly:
person = Person.new(name: "John Smith")
=> #<Person:0x007f8bc40239e0 @name="John Smith", @address=nil>
new_attributes = { address: { country: "USA", city: "New York" } }
OPatch.patch(person, new_attributes) do
field :name
object :address, build: proc { |person, attributes| person.address = Address.new(attributes) } do
field :country
field :city
end
end
=> #<Person:0x007f8bc4130590 @name="John Smith", @address=#<Address:0x007f8bc4049a28 @city="New York", @country="USA">>
If you provide nil for the nested object the object will be removed:
person = Person.new(name: "John Smith", address: Address.new(city: "Kazan", country: "Russia"))
OPatch.patch(person, name: 'Vasya', address: nil) do
field :name
object :address do
field :country
field :city
end
end
=> #<Person:0x007f8bc400a850 @name="Vasya", @address=nil>
Albert Gazizov, @deeper4k