extensible 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rubocop.yml +10 -0
- data/.ruby-version +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +18 -0
- data/Guardfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +147 -0
- data/Rakefile +10 -0
- data/extensible.gemspec +26 -0
- data/lib/extensible/constants.rb +9 -0
- data/lib/extensible/extension_kernel.rb +23 -0
- data/lib/extensible/extension_kernel_template.rb +35 -0
- data/lib/extensible/version.rb +39 -0
- data/lib/extensible.rb +35 -0
- data/spec/.rubocop.yml +4 -0
- data/spec/lib/extensible/constants_spec.rb +7 -0
- data/spec/lib/extensible/version_spec.rb +5 -0
- data/spec/lib/extensible_spec.rb +195 -0
- data/spec/spec_helper.rb +21 -0
- data/tasks/console.rb +25 -0
- data/tasks/coverage.rb +5 -0
- metadata +77 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 77a0368abe54f48d7dce5ffbbea97d6b932578d3
|
4
|
+
data.tar.gz: f297fa02fd05537287e1473f5758f25041df0c85
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cc7f44628796c89d5ce517d0606542189681d6213232d1be546832da17b7c9a1cb869f48e1d948e40372442fa14c80a65ef9952a30a57b53d76efe2511d79b7a
|
7
|
+
data.tar.gz: d165a474bc6fa83f98a0b5691e028504b31bf764a498d5814359e39b8c500fc3863834ea959b0b34b6dfce7abb30600f36a6439e29c19bf21475c249b7237e51
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.0
|
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- ruby-head
|
4
|
+
- 2.2
|
5
|
+
- 2.1
|
6
|
+
- 2.0
|
7
|
+
- 1.9.3
|
8
|
+
- 1.9.2
|
9
|
+
- jruby-head
|
10
|
+
- jruby-1.7
|
11
|
+
- rbx
|
12
|
+
bundler_args: "--without development"
|
13
|
+
addons:
|
14
|
+
code_climate:
|
15
|
+
repo_token:
|
16
|
+
secure: E3bOUINqN9inAqw0EsziZGyTzwThdrVrPuiRN6LQYIc7tHWkJBY0UO+tNvpj0tQcF3ViBy8Lbi26W2Fy+y0FWR163AXxQ8wu6Tu21es+sIPUxxnr/WFbVc7CEsE68ExsfNUciHHa82xnHECYaSqpR7gLHeMW7wFksdQvcnNMr38=
|
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
gemspec
|
3
|
+
|
4
|
+
group :development do
|
5
|
+
gem "byebug", "~> 3.5.1"
|
6
|
+
gem "guard-rspec", "~> 4.5.0"
|
7
|
+
gem "guard-rubocop", "~> 1.2.0"
|
8
|
+
gem "libnotify", "~> 0.9.1"
|
9
|
+
gem "pry", "~> 0.10.1"
|
10
|
+
gem "rubocop", "~> 0.29.0"
|
11
|
+
end
|
12
|
+
|
13
|
+
group :development, :test do
|
14
|
+
gem "codeclimate-test-reporter", "~> 0.4.6"
|
15
|
+
gem "rake", "~> 10.4.2"
|
16
|
+
gem "rspec", "~> 3.2.0"
|
17
|
+
gem "simplecov", "~> 0.9.1"
|
18
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
guard :rubocop, all_on_start: false do
|
2
|
+
watch(/^.+\.rb$/)
|
3
|
+
watch(/^(?:.+\/)?\.rubocop\.yml$/) {|m| File.dirname(m[0]) }
|
4
|
+
watch(/^(?:.+\/)?.+\.gemspec$/)
|
5
|
+
watch(/^(?:.+\/)?(?:Gem|Rake)file$/)
|
6
|
+
end
|
7
|
+
|
8
|
+
guard :rspec, cmd: "bundle exec rspec -fd" do
|
9
|
+
watch(/^spec\/.+_spec\.rb$/)
|
10
|
+
watch(/^lib\/(.+)\.rb$/) {|m| "spec/lib/#{m[1]}_spec.rb" }
|
11
|
+
watch("spec/spec_helper.rb") { "spec" }
|
12
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Gabriel de Oliveira
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# Extensible
|
2
|
+
|
3
|
+
[![Gem Version](http://img.shields.io/gem/v/extensible.svg)][gem]
|
4
|
+
[![Build Status](http://img.shields.io/travis/gdeoliveira/extensible.svg)][travis]
|
5
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/gdeoliveira/extensible.svg)][codeclimate]
|
6
|
+
[![Test Coverage](http://img.shields.io/codeclimate/coverage/github/gdeoliveira/extensible.svg)][codeclimate]
|
7
|
+
[![Dependency Status](http://img.shields.io/gemnasium/gdeoliveira/extensible.svg)][gemnasium]
|
8
|
+
[![Inline docs](http://inch-ci.org/github/gdeoliveira/extensible.svg?branch=master)][inch-ci]
|
9
|
+
|
10
|
+
[gem]: https://rubygems.org/gems/extensible
|
11
|
+
[travis]: http://travis-ci.org/gdeoliveira/extensible
|
12
|
+
[codeclimate]: https://codeclimate.com/github/gdeoliveira/extensible
|
13
|
+
[gemnasium]: https://gemnasium.com/gdeoliveira/extensible#development-dependencies
|
14
|
+
[inch-ci]: http://inch-ci.org/github/gdeoliveira/extensible
|
15
|
+
|
16
|
+
Use Extensible on your custom extensions in order to get the following set of advantages over traditional extensions that override the `Module#extended` method directly:
|
17
|
+
|
18
|
+
- Calls to `super` are handled internally to ensure all your _extensible_ extensions are properly initialized.
|
19
|
+
- Your _extensible_ extensions will be automatically able to become the base of other, more specific extensions while proper initialization is maintained by simply including them.
|
20
|
+
- Bundle several _extensible_ extensions in a single module by including them and they will all be correctly initialized when extending the bundler module.
|
21
|
+
|
22
|
+
Specific examples for each of these scenarios can be found in the [usage](#usage) section.
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem "extensible"
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
$ bundle
|
35
|
+
|
36
|
+
Or install it yourself as:
|
37
|
+
|
38
|
+
$ gem install extensible
|
39
|
+
|
40
|
+
## When should I use Extensible?
|
41
|
+
|
42
|
+
You should use Extensible every time you're implementing a module that is intended to be used as an extension for a module or a class **and** that extension needs to have some initialization code.
|
43
|
+
|
44
|
+
In short: if you are going to override `Module#extended`, use Extensible instead.
|
45
|
+
|
46
|
+
## <a name="usage"></a>Usage
|
47
|
+
|
48
|
+
#### Basic
|
49
|
+
|
50
|
+
Creating an _extensible_ extension module is, arguably, simpler than creating a traditional extension that overrides `Module#extended`. Note that we do not need to call `super` within the code block since it will be called "under the hood" _before_ the code is executed:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
module MyExtension
|
54
|
+
extend Extensible
|
55
|
+
when_extended {|m| puts "#{self} has extended #{m}." }
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
As you would expect, this is what happens when you use your _extensible_ extension in a class (or module):
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
class MyClass
|
63
|
+
extend MyExtension
|
64
|
+
end #=> MyExtension has extended MyClass.
|
65
|
+
```
|
66
|
+
|
67
|
+
#### Extending extensions
|
68
|
+
|
69
|
+
The extensions you create using Extensible are "extensible" in the sense that you (or someone else) can use them as the base for other, more specific extensions. The best bit is you get this at no additional cost. Simply include them and extend away!
|
70
|
+
|
71
|
+
Suppose we have a base extension that sets an instance variable that holds the reversed name of the module or class extending it:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
module MyBaseExtension
|
75
|
+
extend Extensible
|
76
|
+
when_extended do |m|
|
77
|
+
m.instance_variable_set(:@reversed_name, m.to_s.reverse)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
Now we can use `MyBaseExtension` on its own, but we can also _extend_ it to, for example, create a reader method for the `@reversed_name` variable by including it on our more specific `MySubExtension`:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
module MySubExtension
|
86
|
+
include MyBaseExtension
|
87
|
+
attr_reader :reversed_name
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
We can now extend `MySubExtension` ensuring that the initialization routine of `MyBaseExtension` is executed as expected:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class MyClass
|
95
|
+
extend MySubExtension
|
96
|
+
end
|
97
|
+
|
98
|
+
MyClass.reversed_name #=> "ssalCyM"
|
99
|
+
```
|
100
|
+
|
101
|
+
Note: `MySubExtension` could have (if needed) extended Extensible to provide its own initialization routine. In this case both initialization routines (the one for `MyBaseExtension` and the one for `MySubExtension`) would have been executed when `MyClass` extended it.
|
102
|
+
|
103
|
+
#### Bundling extensions
|
104
|
+
|
105
|
+
Traditional extensions that override the `Module#extended` method work correctly as long as they are explicitly extended in the module or class that will ultimately use them. This can become really cumbersome really fast when you want to apply several extensions to a set of different modules or classes.
|
106
|
+
|
107
|
+
Using _extensible_ extensions you can bundle many of them within a single module, and then extend all of them at the same time by extending the bundler module.
|
108
|
+
|
109
|
+
Suppose you have two extensions (`A` and `B`) that you want to bundle together:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
module A
|
113
|
+
extend Extensible
|
114
|
+
when_extended { puts "A was extended!" }
|
115
|
+
end
|
116
|
+
|
117
|
+
module B
|
118
|
+
extend Extensible
|
119
|
+
when_extended { puts "B was extended!" }
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
Simply include them in your bundler module:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
module Bundle
|
127
|
+
include A
|
128
|
+
include B
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
And extend it!
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
module MyClass
|
136
|
+
extend Bundle
|
137
|
+
end #=> A was extended!
|
138
|
+
#=> B was extended!
|
139
|
+
```
|
140
|
+
|
141
|
+
## Contributing
|
142
|
+
|
143
|
+
1. Fork it ( https://github.com/gdeoliveira/extensible/fork )
|
144
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
145
|
+
3. Commit your changes (`git commit -am "Add some feature"`)
|
146
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
147
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/extensible.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "extensible/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "extensible"
|
8
|
+
spec.version = Extensible::VERSION
|
9
|
+
spec.authors = ["Gabriel de Oliveira"]
|
10
|
+
spec.email = ["deoliveira.gab@gmail.com"]
|
11
|
+
spec.summary = "Ruby extensions made easy."
|
12
|
+
spec.description = "Use Extensible on your custom extensions in order to get the following set of advantages over" \
|
13
|
+
"traditional extensions that override the `Module#extended` method directly:\n\n" \
|
14
|
+
"- Calls to `super` are handled internally to ensure all your _extensible_ extensions are" \
|
15
|
+
"properly initialized.\n" \
|
16
|
+
"- Your _extensible_ extensions will be automatically able to become the base of other, more" \
|
17
|
+
"specific extensions while proper initialization is maintained by simply including them.\n" \
|
18
|
+
"- Bundle several _extensible_ extensions in a single module by including them and they will all" \
|
19
|
+
"be correctly initialized when extending the bundler module."
|
20
|
+
spec.homepage = "https://github.com/gdeoliveira/extensible"
|
21
|
+
spec.license = "MIT"
|
22
|
+
spec.files = `git ls-files -z`.split("\x0")
|
23
|
+
spec.test_files = spec.files.grep(/^spec\//)
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
spec.rdoc_options << "--title=Extensible"
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "extensible/extension_kernel_template"
|
2
|
+
|
3
|
+
module Extensible # rubocop:disable Style/Documentation
|
4
|
+
##
|
5
|
+
# A custom ExtensionKernelTemplate clone that is extended by Extensible to make it, well, extensible.
|
6
|
+
ExtensionKernel = ExtensionKernelTemplate.clone.module_eval do
|
7
|
+
private # rubocop:disable Style/EmptyLinesAroundAccessModifier
|
8
|
+
|
9
|
+
def extended(submodule)
|
10
|
+
super
|
11
|
+
|
12
|
+
unless submodule.const_defined?(:ExtensionKernel, false)
|
13
|
+
kernel = ExtensionKernelTemplate.clone
|
14
|
+
submodule.const_set(:ExtensionKernel, kernel)
|
15
|
+
submodule.extend kernel
|
16
|
+
end
|
17
|
+
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Extensible
|
2
|
+
##
|
3
|
+
# ExtensionKernelTemplate serves as the initial template for all extension kernels that are used internally by
|
4
|
+
# Extensible. Each extensible module will extend a clone of ExtensionKernelTemplate that will allow it to execute
|
5
|
+
# its own initialization code.
|
6
|
+
module ExtensionKernelTemplate
|
7
|
+
class << self
|
8
|
+
private
|
9
|
+
|
10
|
+
##
|
11
|
+
# call-seq:
|
12
|
+
# initialize_copy(_source) => extension_kernel_template
|
13
|
+
#
|
14
|
+
# Passes +_source+ to +super+ and overrides the <tt>Module#included</tt> method in order to extend self on
|
15
|
+
# including modules.
|
16
|
+
#
|
17
|
+
# Returns self object.
|
18
|
+
def initialize_copy(_source)
|
19
|
+
super
|
20
|
+
|
21
|
+
this_kernel = self
|
22
|
+
|
23
|
+
define_method :included do |submodule|
|
24
|
+
super submodule
|
25
|
+
submodule.extend this_kernel
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
private :included
|
30
|
+
|
31
|
+
self
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
##
|
2
|
+
# Use Extensible on your custom extensions in order to get a set of advantages over traditional extensions that override
|
3
|
+
# the <tt>Module#extended</tt> method directly. When _extending_ Extensible you will be able to define your
|
4
|
+
# initialization code (similar to using <tt>Module#extended</tt>) via the when_extended method.
|
5
|
+
#
|
6
|
+
# module MyExtension
|
7
|
+
# extend Extensible
|
8
|
+
# when_extended {|m| puts "#{self} has extended #{m}." }
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# class MyClass
|
12
|
+
# extend MyExtension
|
13
|
+
# end #=> MyExtension has extended MyClass.
|
14
|
+
#
|
15
|
+
# Extensible is, itself, extensible. This means that you will be able to extend it further or bundle it together with
|
16
|
+
# other extensible modules by _including_ it in your module.
|
17
|
+
#
|
18
|
+
# module MyExtensibleExtension
|
19
|
+
# include Extensible
|
20
|
+
#
|
21
|
+
# def custom_when_extended(&block)
|
22
|
+
# puts "Defining initialization code..."
|
23
|
+
# when_extended &block
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# module MyExtension
|
28
|
+
# extend MyExtensibleExtension
|
29
|
+
# custom_when_extended {|m| puts "#{self} has extended #{m}." }
|
30
|
+
# end #=> Defining initialization code...
|
31
|
+
#
|
32
|
+
# class MyClass
|
33
|
+
# extend MyExtension
|
34
|
+
# end #=> MyExtension has extended MyClass.
|
35
|
+
module Extensible
|
36
|
+
##
|
37
|
+
# Current version of Extensible.
|
38
|
+
VERSION = "0.0.1".freeze
|
39
|
+
end
|
data/lib/extensible.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require "extensible/constants"
|
2
|
+
require "extensible/extension_kernel"
|
3
|
+
require "extensible/version"
|
4
|
+
|
5
|
+
module Extensible # rubocop:disable Style/Documentation
|
6
|
+
extend ExtensionKernel
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
##
|
11
|
+
# call-seq:
|
12
|
+
# when_extended() {|module| ... } => self
|
13
|
+
#
|
14
|
+
# The +when_extended+ method accepts a code block that will be executed every time this module is extended in a
|
15
|
+
# manner similar to <tt>Module#extended</tt>. The code block will be passed a +module+ parameter containing the module
|
16
|
+
# that is being extended. There is no need to make explicit calls to +super+ as those will be made internally by
|
17
|
+
# Extensible.
|
18
|
+
#
|
19
|
+
# Returns self object.
|
20
|
+
def when_extended(&b)
|
21
|
+
fail(ArgumentError, Error::BLOCK_NOT_GIVEN) unless block_given?
|
22
|
+
|
23
|
+
self::ExtensionKernel.module_eval do
|
24
|
+
define_method :extended do |submodule|
|
25
|
+
super submodule
|
26
|
+
b.call submodule
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
private :extended
|
31
|
+
end
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
end
|
data/spec/.rubocop.yml
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Extensible do
|
4
|
+
context "#when_extended" do
|
5
|
+
it "raises an error if code block is not provided" do
|
6
|
+
m = Module.new
|
7
|
+
|
8
|
+
expect do
|
9
|
+
m.module_eval do
|
10
|
+
extend Extensible
|
11
|
+
when_extended
|
12
|
+
end
|
13
|
+
end.to raise_error(ArgumentError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "hierarchy" do
|
18
|
+
let(:extensible_base_extension_1) do
|
19
|
+
Module.new.tap do |m|
|
20
|
+
m.module_eval do
|
21
|
+
include Extensible
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:extensible_base_extension_2) do
|
27
|
+
Module.new.tap do |m|
|
28
|
+
m.module_eval do
|
29
|
+
include Extensible
|
30
|
+
extend Extensible
|
31
|
+
when_extended do |sm|
|
32
|
+
sm.const_set(:EBE_2_CALLED, true)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:extensible_sub_extension) do
|
39
|
+
ebe_1 = extensible_base_extension_1
|
40
|
+
ebe_2 = extensible_base_extension_2
|
41
|
+
Module.new.tap do |m|
|
42
|
+
m.module_eval do
|
43
|
+
include ebe_1
|
44
|
+
include ebe_2
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
let(:base_extension_1) do
|
50
|
+
ese = extensible_sub_extension
|
51
|
+
Module.new.tap do |m|
|
52
|
+
m.module_eval do
|
53
|
+
extend ese
|
54
|
+
when_extended do |sm|
|
55
|
+
sm.const_set(:BE_1_CALLED, true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
let(:base_extension_2) do
|
62
|
+
ebe_1 = extensible_base_extension_1
|
63
|
+
Module.new.tap do |m|
|
64
|
+
m.module_eval do
|
65
|
+
extend ebe_1
|
66
|
+
when_extended do |sm|
|
67
|
+
sm.const_set(:BE_2_CALLED, true)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:sub_extension) do
|
74
|
+
be_1 = base_extension_1
|
75
|
+
be_2 = base_extension_2
|
76
|
+
Module.new.tap do |m|
|
77
|
+
m.module_eval do
|
78
|
+
include be_1
|
79
|
+
include be_2
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
subject do
|
85
|
+
se = sub_extension
|
86
|
+
Class.new.tap do |c|
|
87
|
+
c.class_eval do
|
88
|
+
extend se
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def extension_ids(mod)
|
94
|
+
mod.singleton_class.ancestors.map(&:object_id)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "adds the extensible extension kernel to all extensible extensions" do
|
98
|
+
expect(extension_ids(Extensible)).to include(Extensible::ExtensionKernel.object_id)
|
99
|
+
expect(extension_ids(extensible_base_extension_1)).to include(Extensible::ExtensionKernel.object_id)
|
100
|
+
expect(extension_ids(extensible_base_extension_2)).to include(Extensible::ExtensionKernel.object_id)
|
101
|
+
expect(extension_ids(extensible_sub_extension)).to include(Extensible::ExtensionKernel.object_id)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "does not add the extensible extension kernel to non-extensible extensions" do
|
105
|
+
expect(extension_ids(base_extension_1)).not_to include(Extensible::ExtensionKernel.object_id)
|
106
|
+
expect(extension_ids(base_extension_2)).not_to include(Extensible::ExtensionKernel.object_id)
|
107
|
+
expect(extension_ids(sub_extension)).not_to include(Extensible::ExtensionKernel.object_id)
|
108
|
+
expect(extension_ids(subject)).not_to include(Extensible::ExtensionKernel.object_id)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "adds the `extensible_base_extension_2` extension kernel to modules that include it" do
|
112
|
+
expect(extension_ids(extensible_base_extension_2)).to include(extensible_base_extension_2::ExtensionKernel.object_id)
|
113
|
+
expect(extension_ids(extensible_sub_extension)).to include(extensible_base_extension_2::ExtensionKernel.object_id)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "does not add the `extensible_base_extension_2` extension kernel to modules that do not include it" do
|
117
|
+
expect(extension_ids(Extensible)).not_to include(extensible_base_extension_2::ExtensionKernel.object_id)
|
118
|
+
expect(extension_ids(extensible_base_extension_1)).not_to include(extensible_base_extension_2::ExtensionKernel.object_id)
|
119
|
+
expect(extension_ids(base_extension_1)).not_to include(extensible_base_extension_2::ExtensionKernel.object_id)
|
120
|
+
expect(extension_ids(base_extension_2)).not_to include(extensible_base_extension_2::ExtensionKernel.object_id)
|
121
|
+
expect(extension_ids(sub_extension)).not_to include(extensible_base_extension_2::ExtensionKernel.object_id)
|
122
|
+
expect(extension_ids(subject)).not_to include(extensible_base_extension_2::ExtensionKernel.object_id)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "adds the `base_extension_1` extension kernel to modules that include it" do
|
126
|
+
expect(extension_ids(base_extension_1)).to include(base_extension_1::ExtensionKernel.object_id)
|
127
|
+
expect(extension_ids(sub_extension)).to include(base_extension_1::ExtensionKernel.object_id)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "does not add the `base_extension_1` extension kernel to modules that do not include it" do
|
131
|
+
expect(extension_ids(Extensible)).not_to include(base_extension_1::ExtensionKernel.object_id)
|
132
|
+
expect(extension_ids(extensible_base_extension_1)).not_to include(base_extension_1::ExtensionKernel.object_id)
|
133
|
+
expect(extension_ids(extensible_base_extension_2)).not_to include(base_extension_1::ExtensionKernel.object_id)
|
134
|
+
expect(extension_ids(extensible_sub_extension)).not_to include(base_extension_1::ExtensionKernel.object_id)
|
135
|
+
expect(extension_ids(base_extension_2)).not_to include(base_extension_1::ExtensionKernel.object_id)
|
136
|
+
expect(extension_ids(subject)).not_to include(base_extension_1::ExtensionKernel.object_id)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "adds the `base_extension_2` extension kernel to modules that include it" do
|
140
|
+
expect(extension_ids(base_extension_2)).to include(base_extension_2::ExtensionKernel.object_id)
|
141
|
+
expect(extension_ids(sub_extension)).to include(base_extension_2::ExtensionKernel.object_id)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "does not add the `base_extension_2` extension kernel to modules that do not include it" do
|
145
|
+
expect(extension_ids(Extensible)).not_to include(base_extension_2::ExtensionKernel.object_id)
|
146
|
+
expect(extension_ids(extensible_base_extension_1)).not_to include(base_extension_2::ExtensionKernel.object_id)
|
147
|
+
expect(extension_ids(extensible_base_extension_2)).not_to include(base_extension_2::ExtensionKernel.object_id)
|
148
|
+
expect(extension_ids(extensible_sub_extension)).not_to include(base_extension_2::ExtensionKernel.object_id)
|
149
|
+
expect(extension_ids(base_extension_1)).not_to include(base_extension_2::ExtensionKernel.object_id)
|
150
|
+
expect(extension_ids(subject)).not_to include(base_extension_2::ExtensionKernel.object_id)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "executes the `extensible_base_extension_2` extension code on modules that extend it" do
|
154
|
+
expect(base_extension_1.const_defined?(:EBE_2_CALLED, false)).to be(true)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "does not execute `extensible_base_extension_2` extension code on modules that do not extend it" do
|
158
|
+
expect(Extensible.const_defined?(:EBE_2_CALLED, false)).to be(false)
|
159
|
+
expect(extensible_base_extension_1.const_defined?(:EBE_2_CALLED, false)).to be(false)
|
160
|
+
expect(extensible_base_extension_2.const_defined?(:EBE_2_CALLED, false)).to be(false)
|
161
|
+
expect(extensible_sub_extension.const_defined?(:EBE_2_CALLED, false)).to be(false)
|
162
|
+
expect(base_extension_2.const_defined?(:EBE_2_CALLED, false)).to be(false)
|
163
|
+
expect(sub_extension.const_defined?(:EBE_2_CALLED, false)).to be(false)
|
164
|
+
expect(subject.const_defined?(:EBE_2_CALLED, false)).to be(false)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "executes the `base_extension_1` extension code on modules that extend it" do
|
168
|
+
expect(subject.const_defined?(:BE_1_CALLED, false)).to be(true)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "does not execute `base_extension_1` extension code on modules that do not extend it" do
|
172
|
+
expect(Extensible.const_defined?(:BE_1_CALLED, false)).to be(false)
|
173
|
+
expect(extensible_base_extension_1.const_defined?(:BE_1_CALLED, false)).to be(false)
|
174
|
+
expect(extensible_base_extension_2.const_defined?(:BE_1_CALLED, false)).to be(false)
|
175
|
+
expect(extensible_sub_extension.const_defined?(:BE_1_CALLED, false)).to be(false)
|
176
|
+
expect(base_extension_1.const_defined?(:BE_1_CALLED, false)).to be(false)
|
177
|
+
expect(base_extension_2.const_defined?(:BE_1_CALLED, false)).to be(false)
|
178
|
+
expect(sub_extension.const_defined?(:BE_1_CALLED, false)).to be(false)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "executes the `base_extension_2` extension code on modules that extend it" do
|
182
|
+
expect(subject.const_defined?(:BE_2_CALLED, false)).to be(true)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "does not execute `base_extension_2` extension code on modules that do not extend it" do
|
186
|
+
expect(Extensible.const_defined?(:BE_2_CALLED, false)).to be(false)
|
187
|
+
expect(extensible_base_extension_1.const_defined?(:BE_2_CALLED, false)).to be(false)
|
188
|
+
expect(extensible_base_extension_2.const_defined?(:BE_2_CALLED, false)).to be(false)
|
189
|
+
expect(extensible_sub_extension.const_defined?(:BE_2_CALLED, false)).to be(false)
|
190
|
+
expect(base_extension_1.const_defined?(:BE_2_CALLED, false)).to be(false)
|
191
|
+
expect(base_extension_2.const_defined?(:BE_2_CALLED, false)).to be(false)
|
192
|
+
expect(sub_extension.const_defined?(:BE_2_CALLED, false)).to be(false)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
unless ENV["COVERAGE"].nil?
|
2
|
+
require "codeclimate-test-reporter"
|
3
|
+
SimpleCov.start do
|
4
|
+
formatter SimpleCov::Formatter::MultiFormatter[
|
5
|
+
SimpleCov::Formatter::HTMLFormatter,
|
6
|
+
CodeClimate::TestReporter::Formatter
|
7
|
+
]
|
8
|
+
add_filter "/spec/"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
13
|
+
require "extensible"
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.color = true
|
17
|
+
config.order = :rand
|
18
|
+
config.expect_with :rspec do |c|
|
19
|
+
c.syntax = :expect
|
20
|
+
end
|
21
|
+
end
|
data/tasks/console.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
desc "Open a console with the #{Bundler::GemHelper.gemspec.name} gem loaded"
|
2
|
+
task :console do
|
3
|
+
require Bundler::GemHelper.gemspec.name
|
4
|
+
|
5
|
+
if RUBY_VERSION >= "2"
|
6
|
+
begin
|
7
|
+
require "byebug"
|
8
|
+
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
9
|
+
end
|
10
|
+
else
|
11
|
+
begin
|
12
|
+
require "debugger"
|
13
|
+
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
require "pry"
|
19
|
+
Pry.start
|
20
|
+
rescue LoadError
|
21
|
+
require "irb"
|
22
|
+
ARGV.clear
|
23
|
+
IRB.start
|
24
|
+
end
|
25
|
+
end
|
data/tasks/coverage.rb
ADDED
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: extensible
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gabriel de Oliveira
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |-
|
14
|
+
Use Extensible on your custom extensions in order to get the following set of advantages overtraditional extensions that override the `Module#extended` method directly:
|
15
|
+
|
16
|
+
- Calls to `super` are handled internally to ensure all your _extensible_ extensions areproperly initialized.
|
17
|
+
- Your _extensible_ extensions will be automatically able to become the base of other, morespecific extensions while proper initialization is maintained by simply including them.
|
18
|
+
- Bundle several _extensible_ extensions in a single module by including them and they will allbe correctly initialized when extending the bundler module.
|
19
|
+
email:
|
20
|
+
- deoliveira.gab@gmail.com
|
21
|
+
executables: []
|
22
|
+
extensions: []
|
23
|
+
extra_rdoc_files: []
|
24
|
+
files:
|
25
|
+
- ".gitignore"
|
26
|
+
- ".rubocop.yml"
|
27
|
+
- ".ruby-version"
|
28
|
+
- ".travis.yml"
|
29
|
+
- Gemfile
|
30
|
+
- Guardfile
|
31
|
+
- LICENSE.txt
|
32
|
+
- README.md
|
33
|
+
- Rakefile
|
34
|
+
- extensible.gemspec
|
35
|
+
- lib/extensible.rb
|
36
|
+
- lib/extensible/constants.rb
|
37
|
+
- lib/extensible/extension_kernel.rb
|
38
|
+
- lib/extensible/extension_kernel_template.rb
|
39
|
+
- lib/extensible/version.rb
|
40
|
+
- spec/.rubocop.yml
|
41
|
+
- spec/lib/extensible/constants_spec.rb
|
42
|
+
- spec/lib/extensible/version_spec.rb
|
43
|
+
- spec/lib/extensible_spec.rb
|
44
|
+
- spec/spec_helper.rb
|
45
|
+
- tasks/console.rb
|
46
|
+
- tasks/coverage.rb
|
47
|
+
homepage: https://github.com/gdeoliveira/extensible
|
48
|
+
licenses:
|
49
|
+
- MIT
|
50
|
+
metadata: {}
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options:
|
53
|
+
- "--title=Extensible"
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 2.4.5
|
69
|
+
signing_key:
|
70
|
+
specification_version: 4
|
71
|
+
summary: Ruby extensions made easy.
|
72
|
+
test_files:
|
73
|
+
- spec/.rubocop.yml
|
74
|
+
- spec/lib/extensible/constants_spec.rb
|
75
|
+
- spec/lib/extensible/version_spec.rb
|
76
|
+
- spec/lib/extensible_spec.rb
|
77
|
+
- spec/spec_helper.rb
|