closure_forwardable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 03d368bdd7202ed2a0baa6c274fd6c3e71dd3cab
4
+ data.tar.gz: ecab750d0c1349c2c4ec78bee71b8efa5785ee63
5
+ SHA512:
6
+ metadata.gz: 80c789834c633f08a852ba36d6aac3f9d2558b9279131519c3234148983dc46f8c9bb1e8ac17a566e36a8544ba8f61b4cdf16baa1252349d97d00614f3a2975c
7
+ data.tar.gz: abe4820384c00b091cad011ed38c8496133191b6d3845059ee334fb3787f66f07240a34cfe649aca7a3a86e685afcf911bff9ee437bf2477a92867ec593d5ee3
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ # We rescue from Exception on purpose in order to filter the backtrace
2
+ # We re-raise the exception in any case
3
+ Lint/RescueException:
4
+ Exclude:
5
+ - 'lib/closure_forwardable.rb'
6
+
7
+ Metrics/MethodLength:
8
+ Max: 15
9
+
10
+ # We don't want to document the top-level modules multiple times
11
+ # And we don't want to use namespace modules as they can break with unusual
12
+ # load-order. Thus, we simply disable this check.
13
+ Style/Documentation:
14
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,22 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+
5
+ matrix:
6
+ include:
7
+ - rvm: 2.0
8
+ env: COMMAND=rspec
9
+ - rvm: 2.1
10
+ env: COMMAND=rspec
11
+ - rvm: 2.2
12
+ env: COMMAND=rspec
13
+ - rvm: jruby
14
+ env: COMMAND=rspec
15
+ - rvm: 2.2
16
+ env: COMMAND=rubocop
17
+ allow_failures:
18
+ - rvm: 2.2
19
+ env: COMMAND=rubocop
20
+
21
+ before_install: gem install bundler -v 1.10.6
22
+ script: bundle exec $COMMAND
data/.yardopts ADDED
@@ -0,0 +1,8 @@
1
+ --protected
2
+ --no-private
3
+ --markup markdown
4
+ -
5
+ README.md
6
+ CHANGELOG.md
7
+ CODE_OF_CONDUCT.md
8
+ LICENSE.txt
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.1.0 / Unreleased
2
+
3
+ * Initial release
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in closure_forwardable.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Holger Just
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # ClosureForwardable
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/closure_forwardable.svg)](https://rubygems.org/gems/closure_forwardable)
4
+ [![Build Status](https://travis-ci.org/meineerde/closure_forwardable.svg?branch=master)](http://travis-ci.org/meineerde/closure_forwardable)
5
+
6
+ The `ClosureForwardable` module provides delegation of specified methods to a designated object, using the methods `delegate`, `def_delegator`, and `def_delegators`. It allows to define methods in a class or module which will be forwarded to any arbitrary object. This allows the user of this module to build meta-objects which provide a unified access to other objects which can be of great help when trying to adhere to the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter).
7
+
8
+ This module is intended to be used very similar to the [`Forwardable`](http://ruby-doc.org/stdlib-2.2.2/libdoc/forwardable/rdoc/Forwardable.html) module in the Ruby standard library. In fact, it provides almost the same interface and can be used in its place.
9
+
10
+ Generally, you should use the simple `Forwardable` module if possible as method calls will be slightly faster while providing the same functionality.
11
+
12
+ Use `ClosureForwardable` if you need to forward methods to a receiver that is not available by the including module itself. Using `ClosureForwardable`, you can forward methods to arbitrary objects.
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'closure_forwardable'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install closure_forwardable
29
+
30
+ ## Usage
31
+
32
+ The `closure_forwardable` gem provides a single module called `ClosureForwardable`. You can use it to delegate methods on to arbitrary other objects.
33
+
34
+ In the following example, we are going to define two classes, `Consumer` and `Producer` whose instances share a single queue object.
35
+
36
+ ```ruby
37
+ class Consumer
38
+ extend ClosureForwardable
39
+
40
+ def consume!
41
+ return unless any?
42
+ element = get_element
43
+ puts element.inspect
44
+ element
45
+ end
46
+ end
47
+
48
+ class Producer
49
+ extend ClosureForwardable
50
+ end
51
+
52
+ queue = []
53
+ # Add the methods any? to the Consumer class which simply calls any on our
54
+ # single queue object.
55
+ Consumer.def_delegator queue, :any?
56
+ # We also add a get_element method to the Consumer which in turn calls shift
57
+ # on the queue.
58
+ Consumer.def_delegator queue, :shift, :get_element
59
+
60
+ # Finally, we add the queue! method to the Producer class which calls the
61
+ # push method of the queue array.
62
+ Producer.def_delegator queue, :push, :queue!
63
+
64
+ producer = Producer.new
65
+ producer.queue! :job1
66
+ producer.queue! :job2
67
+
68
+ queue
69
+ # => [:job1, job2]
70
+
71
+ consumer = Consumer.new
72
+ consumer.consume!
73
+ # => job1
74
+ consumer.consume!
75
+ # => job2
76
+ consumer.consume!
77
+ # => nil
78
+
79
+ queue
80
+ # => []
81
+ ```
82
+
83
+ Note that all instances of the classes share the same single `queue` object. Also note that the queue is not added in any way to the classes. There are no instance variables or accessors being defined on the classes. The queue is just made available to the delegation methods and is effectively invisible to the rest of the class.
84
+
85
+ This is the main difference to the `Forwardable` module of the Ruby standard library: for Forwardable to be able to delegate the methods, it needs to be able to ge access to the delegation object from
86
+ the class instance. Typically, this is achieved using accessors or instance variables. When using `ClosureForwardable`, this is no longer necessary as we can delegate methods to any arbitrary object.
87
+
88
+ ## Why does this work?
89
+
90
+ The delegated object is in fact made available to the generated method. However, it is only visible though a block closure. With this mechanism which is provided by the block implementation of Ruby, when a block is executed, it can access all local variables which where available at the time the block was defined. This allows us to "hide" the delegation object inside a block's closure which forms the body of the method we define on our class.
91
+
92
+ Note that because of this, Ruby's garbage collector is intelligent enough to not destroy the object we delegate to as long as it is part of any block's closure. That means, when using `ClosureForwardable`, we strongly tie the forwarded object to the class. The delegation object will continue to be live as long as the class which delegates to it exists (thus typically forever).
93
+
94
+ ## Development
95
+
96
+ 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.
97
+
98
+ 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).
99
+
100
+ ## Contributing
101
+
102
+ Bug reports and pull requests are welcome on GitHub at https://github.com/meineerde/closure_forwardable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
103
+
104
+
105
+ ## License
106
+
107
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task test: :spec
7
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'closure_forwardable'
5
+
6
+ require 'irb'
7
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'closure_forwardable/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'closure_forwardable'
8
+ spec.version = ClosureForwardable::VERSION
9
+ spec.authors = ['Holger Just']
10
+ spec.email = ['hello@holgerjust.de']
11
+
12
+ spec.summary = <<-EOF.gsub(/^\s+/, '').gsub(/\s*\n/, ' ').strip
13
+ A variant of the Forwardable module in the Ruby Standards Library. Instead
14
+ of requiring the delegation object to be available on the class, it can
15
+ be any object.
16
+ EOF
17
+ spec.homepage = 'https://github.com/meineerde/closure_forwardable'
18
+ spec.license = 'MIT'
19
+
20
+ files = `git ls-files -z`.split("\x0")
21
+ spec.files = files.reject { |f| f.match(%r{^(test|spec|feature)/}) }
22
+ spec.bindir = 'exe'
23
+ spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ['lib']
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.10' # license: MIT
27
+ spec.add_development_dependency 'rake', '~> 10.0' # license: MIT
28
+ spec.add_development_dependency 'rspec', '~> 3.2' # license: MIT
29
+ spec.add_development_dependency 'rubocop', '~> 0.32' # license: MIT
30
+ spec.add_development_dependency 'yard', '~> 0.8.6' # license: MIT
31
+ end
@@ -0,0 +1,33 @@
1
+ module ClosureForwardable
2
+ # Version information about ClosureForwardable.
3
+ # We follow semantic versioning
4
+ module Version
5
+ # MAJOR version. It is incremented after incompatible API changes
6
+ MAJOR = 0
7
+ # MINOR version. It is incremented after adding functionality in a
8
+ # backwards-compatible manner
9
+ MINOR = 1
10
+ # PATCH version. It is incremented when making backwards-compatible
11
+ # bug-fixes.
12
+ PATCH = 0
13
+
14
+ # A standard string representation of the version parts
15
+ STRING = [MAJOR, MINOR, PATCH].compact.join('.').freeze
16
+
17
+ # @return [Gem::Version] the version of the currently loaded
18
+ # ClosureForwardable as a `Gem::Version`
19
+ def self.gem_version
20
+ Gem::Version.new to_s
21
+ end
22
+
23
+ # @return [String] the ClosureForwardable version as a semver-compliant
24
+ # string
25
+ def self.to_s
26
+ STRING
27
+ end
28
+ end
29
+
30
+ # The ClosureForwardable version as a semver-compliant string
31
+ # @see Version::STRING
32
+ VERSION = Version::STRING
33
+ end
@@ -0,0 +1,116 @@
1
+ require 'closure_forwardable/version'
2
+
3
+ require 'English'
4
+
5
+ # The {ClosureForwardable} module provides delegation of specified methods to a
6
+ # designated object, using the methods {#def_delegator} and {#def_delegators}.
7
+ #
8
+ # This module is intended to be used very similar to the `Forwardable` module in
9
+ # the Ruby standard library. For basic usage guidelines, see there. Generally,
10
+ # you should use the simple `Forwardable` module if possible as method calls
11
+ # will be slightly faster while providing the same functionality.
12
+ #
13
+ # Use {ClosureForwardable} if you need to forward methods to a receiver that
14
+ # is not available by the including module itself. Using {ClosureForwardable},
15
+ # you can forward methods to arbitrary objects.
16
+ module ClosureForwardable
17
+ # A regular expression matching the current file so that we can filter
18
+ # backtraces
19
+ FILE_REGEXP = Regexp.new(Regexp.escape(__FILE__))
20
+
21
+ @debug = nil
22
+ class << self
23
+ # If true, `__FILE__` will remain in the backtrace in the event an exception
24
+ # is raised.
25
+ attr_accessor :debug
26
+ end
27
+
28
+ # Takes a hash as its argument. The key is a symbol or an array of symbols.
29
+ # These symbols correspond to method names. The value is the receiver to which
30
+ # the methods will be delegated.
31
+ #
32
+ # @param [Hash<Symbol,String,Array<Symbol, String> => Object>] hash
33
+ # @return [void]
34
+ def closure_delegate(hash)
35
+ hash.each do |methods, receiver|
36
+ methods = [methods] unless methods.respond_to?(:each)
37
+ methods.each do |method|
38
+ def_closure_delegator(receiver, method)
39
+ end
40
+ end
41
+ end
42
+
43
+ # Shortcut for defining multiple delegator methods, but with no provision for
44
+ # using a different name.
45
+ #
46
+ # @example
47
+ # # This definition
48
+ # def_delegators records, :size, :<<, :map
49
+ #
50
+ # # is exactly the same as this:
51
+ # def_delegator records, :size
52
+ # def_delegator records, :<<
53
+ # def_delegator records, :map
54
+ #
55
+ # @param [Object] receiver the object which will be the receiver of all
56
+ # delegated methods calls
57
+ # @param [Array<Symbol, String>] methods Any number of methods to delegate
58
+ # @return [void]
59
+ def def_closure_delegators(receiver, *methods)
60
+ excluded_methods = ['__send__'.freeze, '__id__'.freeze]
61
+ methods.each do |method|
62
+ next if excluded_methods.include?(method.to_s)
63
+ def_closure_delegator(receiver, method)
64
+ end
65
+ end
66
+
67
+ # Define `method` as delegator instance method with an optional alias name
68
+ # `ali`. Method calls to `ali` will be delegated to `receiver.method`.
69
+ #
70
+ # @example
71
+ # class MyQueue
72
+ # extend InheritedSettings::ClosureForwardable
73
+ #
74
+ # attr_reader :internal
75
+ # def initialize
76
+ # @internal = []
77
+ # end
78
+ # end
79
+ #
80
+ # external = []
81
+ # MyQueue.def_delegator external, :push, :<<
82
+ # queue = MyQueue.new
83
+ #
84
+ # queue << 42
85
+ # external #=> [42]
86
+ #
87
+ # queue.internal << 23
88
+ # queue.internal #=> [23]
89
+ # external #=> [42]
90
+ #
91
+ # @param [Object] receiver the object which will be the receiver of all
92
+ # delegated methods calls
93
+ # @param [Symbol, String] method the name of the method on the `receiver`
94
+ # @param [Symbol, String] method_alias The method name created in the
95
+ # current module, by default, we use the same name as the method name on the
96
+ # `receiver`
97
+ # @return [void]
98
+ def def_closure_delegator(receiver, method, method_alias = method)
99
+ define_method(method_alias) do |*args, &block|
100
+ begin
101
+ receiver.__send__(method, *args, &block)
102
+ rescue Exception
103
+ unless ::ClosureForwardable.debug
104
+ $ERROR_POSITION.delete_if do |error_line|
105
+ ::ClosureForwardable::FILE_REGEXP =~ error_line
106
+ end
107
+ end
108
+ ::Kernel.raise
109
+ end
110
+ end
111
+ end
112
+
113
+ alias_method :delegate, :closure_delegate
114
+ alias_method :def_delegators, :def_closure_delegators
115
+ alias_method :def_delegator, :def_closure_delegator
116
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: closure_forwardable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Holger Just
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-08-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '3.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.32'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0.32'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.8.6
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.8.6
83
+ description:
84
+ email:
85
+ - hello@holgerjust.de
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .rspec
92
+ - .rubocop.yml
93
+ - .travis.yml
94
+ - .yardopts
95
+ - CHANGELOG.md
96
+ - CODE_OF_CONDUCT.md
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - bin/console
102
+ - bin/setup
103
+ - closure_forwardable.gemspec
104
+ - lib/closure_forwardable.rb
105
+ - lib/closure_forwardable/version.rb
106
+ homepage: https://github.com/meineerde/closure_forwardable
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.4.6
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: A variant of the Forwardable module in the Ruby Standards Library. Instead
130
+ of requiring the delegation object to be available on the class, it can be any object.
131
+ test_files: []
132
+ has_rdoc: