Class: Perfume::SuperObject

Inherits:
Object
  • Object
show all
Defined in:
lib/perfume/super_object.rb

Overview

Public: Premise is tha using method arguments sucks because you need to remember the order, sucks even more when you might have lot of them (like structure/service initializers).

It’s often seen in ruby to declare classes that inherit from Struct. There we have problem of arguments order. Maybe I wanna specify defaults? Maybe I wanna pass only one argument from the middle of the list? For such cases people use OpenStruct or third-party stuff like Hashie::Dash. Not a good idea either because those classes are bloated with useless methods.

Here’s how this super object works:

class Women < SuperObject(:name, :surname)
  args :married

  def title
    @married ? 'Mrs.' : 'Miss'
  end

  def to_s
    [ title, name, surname ].join(' ')
  end
end

ada = Women.new(name: "Ada", surname: "Lovelace", married: true)
ada.name                        # => "Ada"
ada.surname                     # => "Lovelace"
ada.to_s                        # => "Mrs. Ada Lovelace"

mary = Women.new(name: "mary", surname: "Cassat", married: false)
mary.to_s                       # => "Miss Mary Cassat"
mary.married                    # => NoMethodError

Women.new(unknown: 'Something') # => ArgumentError

By default ‘args` method defines only instance variables. Then we have `args_accessor`, which is called for all class parameters, `args_reader` and `args_writer` if you need to expose some stuff.

Direct Known Subclasses

Console, Service

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = {}) ⇒ SuperObject

Returns a new instance of SuperObject.

Raises:

  • (ArgumentError)


70
71
72
73
74
75
76
77
# File 'lib/perfume/super_object.rb', line 70

def initialize(args = {})
  args.symbolize_keys!
  unknown_args = args.keys - self.class.init_args
  raise ArgumentError, "Unknown arguments: #{unknown_args.map(&:inspect).join(', ')}" unless unknown_args.empty?
  args = defaults.symbolize_keys.merge(args)
  self.class.init_args.each { |name| instance_variable_set("@#{name}", args[name]) }
  init
end

Class Method Details

.args(*names) ⇒ Object

Public: Defines unaccessible arguments.



47
48
49
# File 'lib/perfume/super_object.rb', line 47

def self.args(*names)
  init_args.concat(names)
end

.args_accessor(*names) ⇒ Object

Public: Defines given init arguments alongside with accessor methods.



52
53
54
55
56
# File 'lib/perfume/super_object.rb', line 52

def self.args_accessor(*names)
  names = names.map(&:to_sym)
  args(*names)
  attr_accessor(*names)
end

.args_reader(*names) ⇒ Object

Public: Defines given init arguments alongside with reader method.



59
60
61
62
# File 'lib/perfume/super_object.rb', line 59

def self.args_reader(*names)
  args(*names)
  attr_reader(*names)
end

.args_writer(*names) ⇒ Object

Public: Defines given init arguments alongside with writer method.



65
66
67
68
# File 'lib/perfume/super_object.rb', line 65

def self.args_writer(*names)
  args(*names)
  attr_writer(*names)
end

.init_argsObject

Public: Returns list of defined arguments. Note that superclass arguments are inherited by child class.



42
43
44
# File 'lib/perfume/super_object.rb', line 42

def self.init_args
  @init_args ||= superclass.respond_to?(:init_args) ? superclass.init_args.dup : []
end

Instance Method Details

#defaultsObject

Public: Override it with your own default arguments.



84
85
86
# File 'lib/perfume/super_object.rb', line 84

def defaults
  {}
end

#initObject

Public: Extra initialization.



80
81
# File 'lib/perfume/super_object.rb', line 80

def init
end