delegate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6cd16ea3c730a702d06a1721e2213829db7c2ccc0109aa267fa62201f9f5b910
4
+ data.tar.gz: 7e46d4c7174ff100079239050e33e41589c9d1a553d8497ab0fb763b22939835
5
+ SHA512:
6
+ metadata.gz: 9779b0aa3f86278c7ddef289936be20530e6056c22cfca97039740495a80e9cd9f77d22cb51ff6bc02e9b43e7dfdb3917dd22bd0fb93bf6e38c8858d6080e174
7
+ data.tar.gz: 7d1ce7b641a66022ac9ef56f46dbcfd0999fdd4f6834fbdfb4142d0fbb39fa689a4007395657704f174181709ca251e5f78965e9cbead70b8a8f9cb0f0d8bc3a
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.3
7
+ before_install: gem install bundler -v 2.0.2
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem "bundler"
7
+ gem "rake"
8
+ gem "test-unit"
9
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
@@ -0,0 +1,59 @@
1
+ # Delegator
2
+
3
+ This library provides three different ways to delegate method calls to an
4
+ object. The easiest to use is SimpleDelegator. Pass an object to the
5
+ constructor and all methods supported by the object will be delegated. This
6
+ object can be changed later.
7
+
8
+ Going a step further, the top level DelegateClass method allows you to easily
9
+ setup delegation through class inheritance. This is considerably more
10
+ flexible and thus probably the most common use for this library.
11
+
12
+ Finally, if you need full control over the delegation scheme, you can inherit
13
+ from the abstract class Delegator and customize as needed. (If you find
14
+ yourself needing this control, have a look at Forwardable which is also in
15
+ the standard library. It may suit your needs better.)
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'delegate'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install delegate
32
+
33
+ ## Usage
34
+
35
+ SimpleDelegator's implementation serves as a nice example of the use of
36
+ Delegator:
37
+
38
+ ```ruby
39
+ class SimpleDelegator < Delegator
40
+ def __getobj__
41
+ @delegate_sd_obj # return object we are delegating to, required
42
+ end
43
+
44
+ def __setobj__(obj)
45
+ @delegate_sd_obj = obj # change delegation object,
46
+ # a feature we're providing
47
+ end
48
+ end
49
+ ```
50
+
51
+ ## Development
52
+
53
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
54
+
55
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
56
+
57
+ ## Contributing
58
+
59
+ Bug reports and pull requests are welcome on GitHub at https://github.com/hsbt/delegate.
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test/lib"
6
+ t.ruby_opts << "-rhelper"
7
+ t.test_files = FileList["test/**/test_*.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "delegate"
5
+
6
+ require "irb"
7
+ IRB.start(__FILE__)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,25 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "delegate/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "delegate"
7
+ spec.version = Delegator::VERSION
8
+ spec.authors = ["Hiroshi SHIBATA"]
9
+ spec.email = ["hsbt@ruby-lang.org"]
10
+
11
+ spec.summary = %q{Provides three abilities to delegate method calls to an object.}
12
+ spec.description = %q{Provides three abilities to delegate method calls to an object.}
13
+ spec.homepage = "https://github.com/ruby/delegate"
14
+ spec.license = "BSD-2-Clause"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,429 @@
1
+ # frozen_string_literal: true
2
+ # = delegate -- Support for the Delegation Pattern
3
+ #
4
+ # Documentation by James Edward Gray II and Gavin Sinclair
5
+
6
+ ##
7
+ # This library provides three different ways to delegate method calls to an
8
+ # object. The easiest to use is SimpleDelegator. Pass an object to the
9
+ # constructor and all methods supported by the object will be delegated. This
10
+ # object can be changed later.
11
+ #
12
+ # Going a step further, the top level DelegateClass method allows you to easily
13
+ # setup delegation through class inheritance. This is considerably more
14
+ # flexible and thus probably the most common use for this library.
15
+ #
16
+ # Finally, if you need full control over the delegation scheme, you can inherit
17
+ # from the abstract class Delegator and customize as needed. (If you find
18
+ # yourself needing this control, have a look at Forwardable which is also in
19
+ # the standard library. It may suit your needs better.)
20
+ #
21
+ # SimpleDelegator's implementation serves as a nice example of the use of
22
+ # Delegator:
23
+ #
24
+ # class SimpleDelegator < Delegator
25
+ # def __getobj__
26
+ # @delegate_sd_obj # return object we are delegating to, required
27
+ # end
28
+ #
29
+ # def __setobj__(obj)
30
+ # @delegate_sd_obj = obj # change delegation object,
31
+ # # a feature we're providing
32
+ # end
33
+ # end
34
+ #
35
+ # == Notes
36
+ #
37
+ # Be advised, RDoc will not detect delegated methods.
38
+ #
39
+ class Delegator < BasicObject
40
+ kernel = ::Kernel.dup
41
+ kernel.class_eval do
42
+ alias __raise__ raise
43
+ [:to_s, :inspect, :=~, :!~, :===, :<=>, :hash].each do |m|
44
+ undef_method m
45
+ end
46
+ private_instance_methods.each do |m|
47
+ if /\Ablock_given\?\z|\Aiterator\?\z|\A__.*__\z/ =~ m
48
+ next
49
+ end
50
+ undef_method m
51
+ end
52
+ end
53
+ include kernel
54
+
55
+ # :stopdoc:
56
+ def self.const_missing(n)
57
+ ::Object.const_get(n)
58
+ end
59
+ # :startdoc:
60
+
61
+ ##
62
+ # :method: raise
63
+ # Use #__raise__ if your Delegator does not have a object to delegate the
64
+ # #raise method call.
65
+ #
66
+
67
+ #
68
+ # Pass in the _obj_ to delegate method calls to. All methods supported by
69
+ # _obj_ will be delegated to.
70
+ #
71
+ def initialize(obj)
72
+ __setobj__(obj)
73
+ end
74
+
75
+ #
76
+ # Handles the magic of delegation through \_\_getobj\_\_.
77
+ #
78
+ def method_missing(m, *args, &block)
79
+ r = true
80
+ target = self.__getobj__ {r = false}
81
+
82
+ if r && target.respond_to?(m)
83
+ target.__send__(m, *args, &block)
84
+ elsif ::Kernel.method_defined?(m) || ::Kernel.private_method_defined?(m)
85
+ ::Kernel.instance_method(m).bind(self).(*args, &block)
86
+ else
87
+ super(m, *args, &block)
88
+ end
89
+ end
90
+
91
+ #
92
+ # Checks for a method provided by this the delegate object by forwarding the
93
+ # call through \_\_getobj\_\_.
94
+ #
95
+ def respond_to_missing?(m, include_private)
96
+ r = true
97
+ target = self.__getobj__ {r = false}
98
+ r &&= target.respond_to?(m, include_private)
99
+ if r && include_private && !target.respond_to?(m, false)
100
+ warn "delegator does not forward private method \##{m}", uplevel: 3
101
+ return false
102
+ end
103
+ r
104
+ end
105
+
106
+ #
107
+ # Returns the methods available to this delegate object as the union
108
+ # of this object's and \_\_getobj\_\_ methods.
109
+ #
110
+ def methods(all=true)
111
+ __getobj__.methods(all) | super
112
+ end
113
+
114
+ #
115
+ # Returns the methods available to this delegate object as the union
116
+ # of this object's and \_\_getobj\_\_ public methods.
117
+ #
118
+ def public_methods(all=true)
119
+ __getobj__.public_methods(all) | super
120
+ end
121
+
122
+ #
123
+ # Returns the methods available to this delegate object as the union
124
+ # of this object's and \_\_getobj\_\_ protected methods.
125
+ #
126
+ def protected_methods(all=true)
127
+ __getobj__.protected_methods(all) | super
128
+ end
129
+
130
+ # Note: no need to specialize private_methods, since they are not forwarded
131
+
132
+ #
133
+ # Returns true if two objects are considered of equal value.
134
+ #
135
+ def ==(obj)
136
+ return true if obj.equal?(self)
137
+ self.__getobj__ == obj
138
+ end
139
+
140
+ #
141
+ # Returns true if two objects are not considered of equal value.
142
+ #
143
+ def !=(obj)
144
+ return false if obj.equal?(self)
145
+ __getobj__ != obj
146
+ end
147
+
148
+ #
149
+ # Returns true if two objects are considered of equal value.
150
+ #
151
+ def eql?(obj)
152
+ return true if obj.equal?(self)
153
+ obj.eql?(__getobj__)
154
+ end
155
+
156
+ #
157
+ # Delegates ! to the \_\_getobj\_\_
158
+ #
159
+ def !
160
+ !__getobj__
161
+ end
162
+
163
+ #
164
+ # This method must be overridden by subclasses and should return the object
165
+ # method calls are being delegated to.
166
+ #
167
+ def __getobj__
168
+ __raise__ ::NotImplementedError, "need to define `__getobj__'"
169
+ end
170
+
171
+ #
172
+ # This method must be overridden by subclasses and change the object delegate
173
+ # to _obj_.
174
+ #
175
+ def __setobj__(obj)
176
+ __raise__ ::NotImplementedError, "need to define `__setobj__'"
177
+ end
178
+
179
+ #
180
+ # Serialization support for the object returned by \_\_getobj\_\_.
181
+ #
182
+ def marshal_dump
183
+ ivars = instance_variables.reject {|var| /\A@delegate_/ =~ var}
184
+ [
185
+ :__v2__,
186
+ ivars, ivars.map {|var| instance_variable_get(var)},
187
+ __getobj__
188
+ ]
189
+ end
190
+
191
+ #
192
+ # Reinitializes delegation from a serialized object.
193
+ #
194
+ def marshal_load(data)
195
+ version, vars, values, obj = data
196
+ if version == :__v2__
197
+ vars.each_with_index {|var, i| instance_variable_set(var, values[i])}
198
+ __setobj__(obj)
199
+ else
200
+ __setobj__(data)
201
+ end
202
+ end
203
+
204
+ def initialize_clone(obj) # :nodoc:
205
+ self.__setobj__(obj.__getobj__.clone)
206
+ end
207
+ def initialize_dup(obj) # :nodoc:
208
+ self.__setobj__(obj.__getobj__.dup)
209
+ end
210
+ private :initialize_clone, :initialize_dup
211
+
212
+ ##
213
+ # :method: trust
214
+ # Trust both the object returned by \_\_getobj\_\_ and self.
215
+ #
216
+
217
+ ##
218
+ # :method: untrust
219
+ # Untrust both the object returned by \_\_getobj\_\_ and self.
220
+ #
221
+
222
+ ##
223
+ # :method: taint
224
+ # Taint both the object returned by \_\_getobj\_\_ and self.
225
+ #
226
+
227
+ ##
228
+ # :method: untaint
229
+ # Untaint both the object returned by \_\_getobj\_\_ and self.
230
+ #
231
+
232
+ ##
233
+ # :method: freeze
234
+ # Freeze both the object returned by \_\_getobj\_\_ and self.
235
+ #
236
+
237
+ [:trust, :untrust, :taint, :untaint, :freeze].each do |method|
238
+ define_method method do
239
+ __getobj__.send(method)
240
+ super()
241
+ end
242
+ end
243
+
244
+ @delegator_api = self.public_instance_methods
245
+ def self.public_api # :nodoc:
246
+ @delegator_api
247
+ end
248
+ end
249
+
250
+ ##
251
+ # A concrete implementation of Delegator, this class provides the means to
252
+ # delegate all supported method calls to the object passed into the constructor
253
+ # and even to change the object being delegated to at a later time with
254
+ # #__setobj__.
255
+ #
256
+ # class User
257
+ # def born_on
258
+ # Date.new(1989, 9, 10)
259
+ # end
260
+ # end
261
+ #
262
+ # class UserDecorator < SimpleDelegator
263
+ # def birth_year
264
+ # born_on.year
265
+ # end
266
+ # end
267
+ #
268
+ # decorated_user = UserDecorator.new(User.new)
269
+ # decorated_user.birth_year #=> 1989
270
+ # decorated_user.__getobj__ #=> #<User: ...>
271
+ #
272
+ # A SimpleDelegator instance can take advantage of the fact that SimpleDelegator
273
+ # is a subclass of +Delegator+ to call <tt>super</tt> to have methods called on
274
+ # the object being delegated to.
275
+ #
276
+ # class SuperArray < SimpleDelegator
277
+ # def [](*args)
278
+ # super + 1
279
+ # end
280
+ # end
281
+ #
282
+ # SuperArray.new([1])[0] #=> 2
283
+ #
284
+ # Here's a simple example that takes advantage of the fact that
285
+ # SimpleDelegator's delegation object can be changed at any time.
286
+ #
287
+ # class Stats
288
+ # def initialize
289
+ # @source = SimpleDelegator.new([])
290
+ # end
291
+ #
292
+ # def stats(records)
293
+ # @source.__setobj__(records)
294
+ #
295
+ # "Elements: #{@source.size}\n" +
296
+ # " Non-Nil: #{@source.compact.size}\n" +
297
+ # " Unique: #{@source.uniq.size}\n"
298
+ # end
299
+ # end
300
+ #
301
+ # s = Stats.new
302
+ # puts s.stats(%w{James Edward Gray II})
303
+ # puts
304
+ # puts s.stats([1, 2, 3, nil, 4, 5, 1, 2])
305
+ #
306
+ # Prints:
307
+ #
308
+ # Elements: 4
309
+ # Non-Nil: 4
310
+ # Unique: 4
311
+ #
312
+ # Elements: 8
313
+ # Non-Nil: 7
314
+ # Unique: 6
315
+ #
316
+ class SimpleDelegator < Delegator
317
+ # Returns the current object method calls are being delegated to.
318
+ def __getobj__
319
+ unless defined?(@delegate_sd_obj)
320
+ return yield if block_given?
321
+ __raise__ ::ArgumentError, "not delegated"
322
+ end
323
+ @delegate_sd_obj
324
+ end
325
+
326
+ #
327
+ # Changes the delegate object to _obj_.
328
+ #
329
+ # It's important to note that this does *not* cause SimpleDelegator's methods
330
+ # to change. Because of this, you probably only want to change delegation
331
+ # to objects of the same type as the original delegate.
332
+ #
333
+ # Here's an example of changing the delegation object.
334
+ #
335
+ # names = SimpleDelegator.new(%w{James Edward Gray II})
336
+ # puts names[1] # => Edward
337
+ # names.__setobj__(%w{Gavin Sinclair})
338
+ # puts names[1] # => Sinclair
339
+ #
340
+ def __setobj__(obj)
341
+ __raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj)
342
+ @delegate_sd_obj = obj
343
+ end
344
+ end
345
+
346
+ def Delegator.delegating_block(mid) # :nodoc:
347
+ lambda do |*args, &block|
348
+ target = self.__getobj__
349
+ target.__send__(mid, *args, &block)
350
+ end
351
+ end
352
+
353
+ #
354
+ # The primary interface to this library. Use to setup delegation when defining
355
+ # your class.
356
+ #
357
+ # class MyClass < DelegateClass(ClassToDelegateTo) # Step 1
358
+ # def initialize
359
+ # super(obj_of_ClassToDelegateTo) # Step 2
360
+ # end
361
+ # end
362
+ #
363
+ # or:
364
+ #
365
+ # MyClass = DelegateClass(ClassToDelegateTo) do # Step 1
366
+ # def initialize
367
+ # super(obj_of_ClassToDelegateTo) # Step 2
368
+ # end
369
+ # end
370
+ #
371
+ # Here's a sample of use from Tempfile which is really a File object with a
372
+ # few special rules about storage location and when the File should be
373
+ # deleted. That makes for an almost textbook perfect example of how to use
374
+ # delegation.
375
+ #
376
+ # class Tempfile < DelegateClass(File)
377
+ # # constant and class member data initialization...
378
+ #
379
+ # def initialize(basename, tmpdir=Dir::tmpdir)
380
+ # # build up file path/name in var tmpname...
381
+ #
382
+ # @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
383
+ #
384
+ # # ...
385
+ #
386
+ # super(@tmpfile)
387
+ #
388
+ # # below this point, all methods of File are supported...
389
+ # end
390
+ #
391
+ # # ...
392
+ # end
393
+ #
394
+ def DelegateClass(superclass, &block)
395
+ klass = Class.new(Delegator)
396
+ ignores = [*::Delegator.public_api, :to_s, :inspect, :=~, :!~, :===]
397
+ protected_instance_methods = superclass.protected_instance_methods
398
+ protected_instance_methods -= ignores
399
+ public_instance_methods = superclass.public_instance_methods
400
+ public_instance_methods -= ignores
401
+ klass.module_eval do
402
+ def __getobj__ # :nodoc:
403
+ unless defined?(@delegate_dc_obj)
404
+ return yield if block_given?
405
+ __raise__ ::ArgumentError, "not delegated"
406
+ end
407
+ @delegate_dc_obj
408
+ end
409
+ def __setobj__(obj) # :nodoc:
410
+ __raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj)
411
+ @delegate_dc_obj = obj
412
+ end
413
+ protected_instance_methods.each do |method|
414
+ define_method(method, Delegator.delegating_block(method))
415
+ protected method
416
+ end
417
+ public_instance_methods.each do |method|
418
+ define_method(method, Delegator.delegating_block(method))
419
+ end
420
+ end
421
+ klass.define_singleton_method :public_instance_methods do |all=true|
422
+ super(all) | superclass.public_instance_methods
423
+ end
424
+ klass.define_singleton_method :protected_instance_methods do |all=true|
425
+ super(all) | superclass.protected_instance_methods
426
+ end
427
+ klass.module_eval(&block) if block
428
+ return klass
429
+ end
@@ -0,0 +1,3 @@
1
+ class Delegator
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delegate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hiroshi SHIBATA
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-11-06 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Provides three abilities to delegate method calls to an object.
14
+ email:
15
+ - hsbt@ruby-lang.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - ".travis.yml"
22
+ - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - bin/console
27
+ - bin/setup
28
+ - delegate.gemspec
29
+ - lib/delegate.rb
30
+ - lib/delegate/version.rb
31
+ homepage: https://github.com/ruby/delegate
32
+ licenses:
33
+ - BSD-2-Clause
34
+ metadata:
35
+ homepage_uri: https://github.com/ruby/delegate
36
+ source_code_uri: https://github.com/ruby/delegate
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubygems_version: 3.0.3
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Provides three abilities to delegate method calls to an object.
56
+ test_files: []