mixit 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.yardopts +8 -0
- data/LICENSE.txt +20 -0
- data/README.md +20 -19
- data/lib/mixit.rb +61 -8
- data/lib/mixit/version.rb +1 -1
- data/test/test_mixit.rb +86 -90
- metadata +10 -8
data/.yardopts
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (C) 2011 by Robert Gleeson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
data/README.md
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
| Project | Mixit
|
2
2
|
|:----------------|:--------------------------------------------------
|
3
3
|
| Homepage | https://github.com/robgleeson/Mixit
|
4
|
-
| Documentation | http://rubydoc.info/gems/
|
5
|
-
| Author | Rob Gleeson
|
4
|
+
| Documentation | http://rubydoc.info/gems/mixit/frames
|
5
|
+
| Author | Rob Gleeson, Renato Riccieri Santos Zannon
|
6
6
|
|
7
7
|
|
8
8
|
__DESCRIPTION__
|
9
9
|
|
10
|
-
Mixit can be used to temporarily extend the calling scope of a block or
|
11
|
-
|
12
|
-
|
10
|
+
Mixit can be used to temporarily extend the calling scope of a block or
|
11
|
+
object with instance methods from a module.
|
12
|
+
It is written to help Domain Specific Language(DSL) writers provide a DSL
|
13
|
+
with their own methods without losing the calling scope of the caller.
|
13
14
|
|
14
15
|
__WHY?__
|
15
16
|
|
16
17
|
Some Ruby DSL approaches already exist(maybe one suits you):
|
17
18
|
|
18
19
|
* `instance_eval`
|
19
|
-
The calling scope is lost, and internal state is exposed to the user of
|
20
|
+
The calling scope is lost, and internal state is exposed to the user of
|
21
|
+
the DSL.
|
20
22
|
|
21
23
|
* `instance_eval` via "EmptyContext"
|
22
24
|
The calling scope is lost, but the object is void of any state.
|
@@ -24,32 +26,31 @@ __WHY?__
|
|
24
26
|
* Pass `self` onto the calling block.
|
25
27
|
The cleanest approach but some of the beauty of the DSL can be lost.
|
26
28
|
|
27
|
-
All these approaches(but the last) lose the calling scope, and that's why I
|
28
|
-
|
29
|
-
|
29
|
+
All these approaches(but the last) lose the calling scope, and that's why I
|
30
|
+
created 'Mixit'.
|
31
|
+
I wanted the user of the DSL to have access to the calling scope, but have
|
32
|
+
access to my DSL as implicit receivers of 'self' as well.
|
30
33
|
|
31
34
|
This approach is by no means 'perfect', or even 'good'.
|
32
35
|
If you use it wisely, though, it can be a cool way to build nice DSLs.
|
33
36
|
|
34
37
|
|
35
|
-
|
36
38
|
__EXAMPLES__
|
37
39
|
|
38
|
-
|
40
|
+
Temporarily mix 'Foo' onto the 'self' referenced by _block_:
|
39
41
|
|
40
|
-
module Foo
|
41
|
-
def bar
|
42
|
-
end
|
42
|
+
module Foo
|
43
|
+
def bar() end
|
43
44
|
end
|
44
45
|
|
45
|
-
|
46
|
-
Mixit.mix(Foo, block)
|
47
|
-
end
|
46
|
+
block = proc { }
|
48
47
|
|
49
|
-
|
50
|
-
p
|
48
|
+
Mixit.temporarily(Foo, block) do
|
49
|
+
p self.class # => Object
|
50
|
+
p respond_to?(:bar) # => true
|
51
51
|
end
|
52
52
|
|
53
|
+
p self.class # => Object
|
53
54
|
p respond_to?(:bar) # => false
|
54
55
|
|
55
56
|
__INSTALL__
|
data/lib/mixit.rb
CHANGED
@@ -4,7 +4,24 @@ require "mixit/version"
|
|
4
4
|
class Mixit
|
5
5
|
|
6
6
|
class << self
|
7
|
-
|
7
|
+
#
|
8
|
+
# @overload mix(*modules, receiver, &block)
|
9
|
+
# Temporarily extends the __receiver__ with methods from __modules__.
|
10
|
+
#
|
11
|
+
# @param [Module] modules
|
12
|
+
# A variable number of modules to extend the receiver with.
|
13
|
+
#
|
14
|
+
# @param receiver
|
15
|
+
# (see Mixit#mix_onto)
|
16
|
+
#
|
17
|
+
# @param block
|
18
|
+
# (see Mixit#mix_onto)
|
19
|
+
#
|
20
|
+
# @yieldparam object
|
21
|
+
# (see Mixit#mix_onto)
|
22
|
+
#
|
23
|
+
# @return [void]
|
24
|
+
#
|
8
25
|
def mix(*args, &block)
|
9
26
|
case args.last
|
10
27
|
when Proc
|
@@ -18,12 +35,39 @@ class Mixit
|
|
18
35
|
|
19
36
|
new(modules).mix_onto(receiver, &block)
|
20
37
|
end
|
38
|
+
alias_method :temporarily, :mix
|
21
39
|
end
|
22
40
|
|
41
|
+
|
42
|
+
#
|
43
|
+
# @return [Array<Module>]
|
44
|
+
# An Array of Module objects.
|
45
|
+
#
|
46
|
+
attr_reader :modules
|
47
|
+
|
48
|
+
#
|
49
|
+
# @param [Array<Module>] modules
|
50
|
+
# An Array of Module objects.
|
51
|
+
#
|
23
52
|
def initialize(modules)
|
24
|
-
@modules = modules
|
53
|
+
@modules = modules
|
54
|
+
@clones = []
|
25
55
|
end
|
26
56
|
|
57
|
+
#
|
58
|
+
# Temporarily extends __receiver__ with methods from {#modules modules}.
|
59
|
+
#
|
60
|
+
# @param [Object] receiver
|
61
|
+
# An object to temporarily extend.
|
62
|
+
#
|
63
|
+
# @param [Proc] block
|
64
|
+
# A block executed while the object is extended.
|
65
|
+
#
|
66
|
+
# @yieldparam [Object] object
|
67
|
+
# Yields a clone of the extended object (Optional).
|
68
|
+
#
|
69
|
+
# @return [void]
|
70
|
+
#
|
27
71
|
def mix_onto(receiver, &block)
|
28
72
|
if block.arity == 1
|
29
73
|
block.call extend!(receiver, true)
|
@@ -32,15 +76,11 @@ class Mixit
|
|
32
76
|
ivars = receiver.instance_variables
|
33
77
|
extend!(receiver, false).instance_eval(&block)
|
34
78
|
ensure
|
35
|
-
# Remove instance variables added by @modules, or &block from 'receiver'.
|
36
|
-
# We want to restore the calling scope to its previous state.
|
37
79
|
(receiver.instance_variables - ivars).each do |ivar|
|
38
80
|
receiver.send(:remove_instance_variable, ivar)
|
39
81
|
end
|
40
82
|
|
41
|
-
|
42
|
-
# We want to restore the calling scope to its previous state.
|
43
|
-
@modules.each do |mod|
|
83
|
+
@clones.each do |mod|
|
44
84
|
mod.instance_methods(false).each do |meth|
|
45
85
|
mod.send(:remove_method, meth)
|
46
86
|
end
|
@@ -49,12 +89,25 @@ class Mixit
|
|
49
89
|
end
|
50
90
|
end
|
51
91
|
|
92
|
+
#
|
93
|
+
# Extends __receiver__ with the methods from {#modules modules}.
|
94
|
+
#
|
95
|
+
# @param receiver
|
96
|
+
# (see Mixit#mix_onto)
|
97
|
+
#
|
98
|
+
# @param [Boolean] to_be_cloned
|
99
|
+
# If true, a clone of the receiver is extended.
|
100
|
+
#
|
101
|
+
# @return [Object]
|
102
|
+
# Returns the extended object.
|
103
|
+
#
|
52
104
|
def extend!(receiver, to_be_cloned=false)
|
53
105
|
if to_be_cloned
|
54
106
|
receiver = receiver.clone
|
55
107
|
end
|
56
108
|
|
57
|
-
@
|
109
|
+
@clones = @modules.map(&:clone)
|
110
|
+
@clones.inject(receiver) { |obj, mod| obj.extend(mod) }
|
58
111
|
end
|
59
112
|
private :extend!
|
60
113
|
|
data/lib/mixit/version.rb
CHANGED
data/test/test_mixit.rb
CHANGED
@@ -1,109 +1,105 @@
|
|
1
1
|
context Mixit do
|
2
2
|
|
3
3
|
context 'mix' do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
before do
|
5
|
+
@module = Module.new do
|
6
|
+
def callme
|
7
|
+
:ok
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_ivar
|
11
|
+
@set_ivar = 'asdf'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'When receiving a block with no param.' do
|
17
|
+
it 'must provide access to mixed-in methods' do
|
18
|
+
block = proc { }
|
19
|
+
|
20
|
+
Mixit.mix(@module, block) do
|
21
|
+
callme.must_equal(:ok)
|
14
22
|
end
|
15
23
|
end
|
16
24
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
Mixit.mix(@module, block) do
|
41
|
-
callme_.must_equal(:ok)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'must remove instance variables added by the passed block.' do
|
46
|
-
block = proc { }
|
47
|
-
|
48
|
-
Mixit.mix(@module, block) do
|
49
|
-
@ok = :no
|
50
|
-
end
|
51
|
-
|
52
|
-
instance_variable_defined?(:@ok).must_equal(false)
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'must remove instance variables added by the mixed in modules.' do
|
56
|
-
block = proc { }
|
57
|
-
|
58
|
-
Mixit.mix(@module, block) do
|
59
|
-
i
|
60
|
-
instance_variable_defined?(:@i).must_equal(true)
|
61
|
-
end
|
62
|
-
|
63
|
-
instance_variable_defined?(:@i).must_equal(false)
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'must not remove instance variables set before the scope is extended.' do
|
67
|
-
block = proc {}
|
68
|
-
@ok = :ok
|
69
|
-
|
70
|
-
Mixit.mix(@module, block) do
|
71
|
-
nil
|
72
|
-
end
|
73
|
-
|
74
|
-
instance_variable_defined?(:@ok).must_equal(true)
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'must remove methods added by mixed in modules.' do
|
78
|
-
block = proc {}
|
79
|
-
|
80
|
-
Mixit.mix(@module, block) do
|
81
|
-
nil
|
82
|
-
end
|
83
|
-
|
84
|
-
respond_to?(:callme).must_equal(false)
|
85
|
-
end
|
25
|
+
it 'must provide access to surrounding locals.' do
|
26
|
+
block = proc { }
|
27
|
+
local = :ok
|
28
|
+
|
29
|
+
Mixit.mix(@module, block) do
|
30
|
+
local.must_equal(:ok)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'must provide access to surrounding methods.' do
|
35
|
+
def callme_() :ok end
|
36
|
+
block = proc { }
|
37
|
+
|
38
|
+
Mixit.mix(@module, block) do
|
39
|
+
callme_.must_equal(:ok)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'must remove instance variables added by the calling block.' do
|
44
|
+
block = proc { }
|
45
|
+
|
46
|
+
Mixit.mix(@module, block) do
|
47
|
+
@ok = :no
|
86
48
|
end
|
49
|
+
|
50
|
+
instance_variable_defined?(:@ok).must_equal(false)
|
87
51
|
end
|
88
52
|
|
89
|
-
|
90
|
-
|
91
|
-
block = proc {}
|
53
|
+
it 'must remove instance variables added by mixed-in modules.' do
|
54
|
+
block = proc { }
|
92
55
|
|
93
|
-
|
94
|
-
|
95
|
-
|
56
|
+
Mixit.mix(@module, block) do
|
57
|
+
set_ivar
|
58
|
+
instance_variable_defined?(:@set_ivar).must_equal(true)
|
96
59
|
end
|
97
60
|
|
98
|
-
|
99
|
-
|
61
|
+
instance_variable_defined?(:@set_ivar).must_equal(false)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'must not remove pre-existing instance variables.' do
|
65
|
+
block = proc {}
|
66
|
+
@ok = :ok
|
67
|
+
|
68
|
+
Mixit.mix(@module, block) do
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
instance_variable_defined?(:@ok).must_equal(true)
|
73
|
+
end
|
100
74
|
|
101
|
-
|
102
|
-
|
103
|
-
|
75
|
+
it 'must remove methods added by mixed-in modules.' do
|
76
|
+
block = proc {}
|
77
|
+
|
78
|
+
Mixit.mix(@module, block) do
|
79
|
+
nil
|
104
80
|
end
|
81
|
+
|
82
|
+
respond_to?(:callme).must_equal(false)
|
105
83
|
end
|
106
84
|
end
|
85
|
+
|
86
|
+
context 'When receiving a block with a extended object as a param.' do
|
87
|
+
it 'must provide access to mixed-in methods.' do
|
88
|
+
block = proc {}
|
89
|
+
|
90
|
+
Mixit.mix(@module, block) do |receiver|
|
91
|
+
receiver.callme.must_equal(:ok)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'must not extend the original receiver with mixed-in methods.' do
|
96
|
+
block = proc {}
|
97
|
+
|
98
|
+
Mixit.mix(@module, block) do |receiver|
|
99
|
+
respond_to?(:callme).must_equal(false)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
107
103
|
end
|
108
104
|
|
109
105
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-11-
|
12
|
+
date: 2011-11-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &70339179578900 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.9.2
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70339179578900
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: minitest
|
27
|
-
requirement: &
|
27
|
+
requirement: &70339179578400 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '2.5'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70339179578400
|
36
36
|
description: Temporarily extend the calling scope of a block with instance methods
|
37
37
|
from a module.
|
38
38
|
email:
|
@@ -43,7 +43,9 @@ extra_rdoc_files: []
|
|
43
43
|
files:
|
44
44
|
- .gitignore
|
45
45
|
- .travis.yml
|
46
|
+
- .yardopts
|
46
47
|
- Gemfile
|
48
|
+
- LICENSE.txt
|
47
49
|
- README.md
|
48
50
|
- Rakefile
|
49
51
|
- lib/mixit.rb
|
@@ -66,7 +68,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
68
|
version: '0'
|
67
69
|
segments:
|
68
70
|
- 0
|
69
|
-
hash:
|
71
|
+
hash: 2621427566551580532
|
70
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
73
|
none: false
|
72
74
|
requirements:
|
@@ -75,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
77
|
version: '0'
|
76
78
|
segments:
|
77
79
|
- 0
|
78
|
-
hash:
|
80
|
+
hash: 2621427566551580532
|
79
81
|
requirements: []
|
80
82
|
rubyforge_project: mixit
|
81
83
|
rubygems_version: 1.8.11
|