Object Path Resolver

Ruby class representing an path through an object graph. Instance of the +ObjectPath+ class can be used to navigate through an object graph and the value(s) in the final step of the path are returned.

Installation

Add this line to your application's Gemfile:

gem 'object_path'

And then execute:

bundle install

Or install it yourself as:

gem install object_path

Usage

Paths are defined as a set of steps. These can be declared in a number of ways :-

  • As a +String+ with each step separated by a '/' delimiter.
  • As an array of +Strings+.
  • As an array of +Symbols+.
  • As an existing +ObjectPaths::ObjectPath+
require 'object_paths/object_path'

path = ObjectPaths::ObjectPath.new('address/street')
path = ObjectPaths::ObjectPath.new(['address', 'street'])
path = ObjectPaths::ObjectPath.new([:address, :street])
path = ObjectPaths::ObjectPath.new(ObjectPaths::ObjectPath.new('address/street'))

The path can then be used to navigate through an object graph :-

require 'object_paths/object_path'

class Address
  attr_accessor :street
end

class Person
  attr_accessor :address
end

address1 = Address.new
address1.street = '123 Main Street'

address2 = Address.new
address2.street = '456 High Street'

person1 = Person.new
person1.address = address1

person2 = Person.new
person2.address = address2

path.resolve(person1) # => '123 Main Street'
path.resolve(person2) # => '456 High Street'

If any step on the path is +Enumerable+ then the following steps with be resolved for each item and an +Array+ of results will be returned. If multiple steps are +Enumerable+ then the process will be repeated for each item. The resulting nested +Array+ will be flattened to a single +Array+. All +Array+ results are also comapected :-

require 'object_paths/object_path'

class Address
  attr_accessor :street
end

class Person
  attr_accessor :addresses
end

address1 = Address.new
address1.street = '123 Main Street'
address2 = Address.new
address2.street = '456 High Street'

person = Person.new
person.addresses = [address1, address2]

path = ObjectPaths::ObjectPath.new('addresses/street')
path.resolve(person) # => ['123 Main Street', '456 High Street']

String & Array Extensions

To ease the creating of new Object Paths the +String+ and +Array+ classes have been extended to allow them to be converted to Object Paths. In both cases the instance method +to_object_path+ can be called to perform the conversions.

require 'object_paths/object_path'

'address/street'.to_object_path
['address', 'street'].to_object_path
%i[address street].to_object_path

Class/Module Support

The +ObjectPaths::ModelSupport+ module can be included in a class or module to provide additional methods for working with Object Paths. This includes the +object_path+ class & instance methods which can be used to define a path witch a cleaner syntax than ObjectPaths::ObjectPath.new.

require 'object_paths/model_support'
class Address
  include ObjectPaths::ModelSupport
end

Address.object_path('street') # => ObjectPaths::ObjectPath
Address.new.object_path('street') # => ObjectPaths::ObjectPath

The resulting path object is not in anyway directly tied to the enclosing class. The methods are just syntactic sugar for creating a new Object Path.

The +object_path!+ method can be used to directly resolve the path against the current instance of the class or module. This is useful for quickly resolving a path without having to create a new Object Path instance.

require 'object_paths/model_support'

class Address
  include ObjectPaths::ModelSupport

  attr_accessor :street

  def initialize(street)
    @street = street
  end
end

address = Address.new('123 Main Street')
address.object_path!('street') # => '123 Main Street'