Skip to content

Instantly share code, notes, and snippets.

@sevos
Last active December 28, 2015 16:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sevos/7530280 to your computer and use it in GitHub Desktop.
Save sevos/7530280 to your computer and use it in GitHub Desktop.
This is just an idea of value objects API and dirty implementation for Ruby. A frankenstein created from merge of Struct's and OpenStruct's APIs. It supoprts mandatory and optional fields. It raises exceptions if API of value object is misused. Feel free to comment and refactor the implementation!
# TODO: ValueObject should be immutable. Setter should return new instance.
class ValueObject < Struct
def self.new(*fields)
all_fields, optional_fields = if fields.last.is_a?(Hash)
optional_fields = fields.pop
[fields + optional_fields.keys, optional_fields]
else
[fields, {}]
end
super(*all_fields) do
define_method :initialize do |attributes|
unless (missing_fields = fields - (attributes.keys & fields)).empty?
raise ArgumentError.new("missing fields: #{missing_fields.join(", ")}")
end
optional_fields.merge(attributes).each do |field, value|
self.public_send("#{field}=", value)
end
end
end
end
end
Credentials = ValueObject.new(:user, :pass, secure: false)
Credentials.new(user: "Artur", pass: "abc")
# => #<struct Credentials user="Artur", pass="abc", secure=false>
Credentials.new(user: 'Artur', pass: 'qwerty', secure: true)
# => #<struct Credentials user="Artur", pass="qwerty", secure=true>
Credentials.new(user: "Artur", pass: 'qwerty', foo: 1) rescue $!
# => #<NoMethodError: undefined method `foo=' for test:Credentials>
Credentials.new(user: "Artur") rescue $!
# => #<ArgumentError: missing fields: pass>
Credentials.new(user: 'Artur', pass: 'qwerty').class.ancestors
# => [Credentials,
# ValueObject,
# Struct,
# Enumerable,
# Object,
# PP::ObjectMixin,
# Kernel,
# BasicObject]
Credentials.new(user: 'foo', pass: 'bar') == Credentials.new(user: 'foo', pass: 'bar') # => true
Credentials.new(user: 'foo', pass: 'bar') == Credentials.new(user: 'foo', pass: 'baz') # => false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment