Module: Delegates
- Defined in:
- lib/delegates.rb
Overview
Provides a #delegate class method to define methods whose calls are delegated to nested objects
Examples:
“‘ruby class Greeter
def hello
'hello'
end
def goodbye
'goodbye'
end
end
class Foo
def greeter
Greeter.new
end
extend Delegates
delegate :hello, to: :greeter
end
Foo.new.hello # => “hello” Foo.new.goodbye # => NoMethodError: undefined method ‘goodbye’ for #<Foo:0x1af30c> “‘
Methods can be delegated to instance variables, class variables, or constants by providing them as a symbols:
“‘ruby class Foo
CONSTANT_ARRAY = [0,1,2,3]
@@class_array = [4,5,6,7]
def initialize
@instance_array = [8,9,10,11]
end
delegate :sum, to: :CONSTANT_ARRAY
delegate :min, to: :@@class_array
delegate :max, to: :@instance_array
end
Foo.new.sum # => 6 Foo.new.min # => 4 Foo.new.max # => 11 “‘
See #delegate docs for available params.
The target method must be public, otherwise it will raise ‘NoMethodError`.
Defined Under Namespace
Classes: DelegationError
Constant Summary collapse
- RUBY_RESERVED_KEYWORDS =
%w[alias and BEGIN begin break case class def defined? do else elsif END end ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield].freeze
- DELEGATION_RESERVED_KEYWORDS =
%w[_ arg args block].freeze
- DELEGATION_RESERVED_METHOD_NAMES =
Set.new( RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS ).freeze
- ALLOW_NIL_PATTERN =
<<~RUBY def %{method_name}(%{definition}) _ = %{to} if !_.nil? || nil.respond_to?(:%{method}) _.%{method}(%{definition}) end end RUBY
- NO_NIL_PATTERN =
<<~RUBY def %{method_name}(%{definition}) _ = %{to} _.%{method}(%{definition}) rescue NoMethodError => e if _.nil? && e.name == :%{method} raise DelegationError, "%{module}#%{method_name} delegated to %{to}.%{method}, but %{to} is nil: \#{self.inspect}" else raise end end RUBY
Instance Method Summary collapse
-
#delegate(*methods, to:, prefix: nil, allow_nil: false, private: false) ⇒ Array<Symbol>
Defines methods specified in ‘methods` to delegate their calls to `to`.
Instance Method Details
#delegate(*methods, to:, prefix: nil, allow_nil: false, private: false) ⇒ Array<Symbol>
Defines methods specified in ‘methods` to delegate their calls to `to`.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/delegates.rb', line 119 def delegate(*methods, to:, prefix: nil, allow_nil: false, private: false) if prefix == true && /^[^a-z_]/.match?(to) raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.' end method_prefix = if prefix "#{prefix == true ? to : prefix}_" else '' end location = caller_locations(1, 1).first file, line = location.path, location.lineno to = to.to_s to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to) method_defs = [] method_names = [] methods.map(&:to_s).map do |method| method_name = "#{method_prefix}#{method}" method_names << method_name.to_sym # Attribute writer methods only accept one argument. Makes sure []= # methods still accept two arguments. definition = if method.match?(/[^\]]=$/) 'arg' elsif RUBY_VERSION >= '2.7' '...' else '*args, &block' end method_defs << if allow_nil ALLOW_NIL_PATTERN % { method_name: method_name, definition: definition, method: method, to: to } else NO_NIL_PATTERN % { module: self, method_name: method_name, definition: definition, method: method, to: to } end end module_eval(method_defs.join(';').gsub(/ *\n */m, ';'), file, line) private(*method_names) if private method_names end |