mixit 0.1.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/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.swp
6
+ *.rbc
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - rbx-2.0
6
+ - jruby
7
+ - ruby-head
8
+ - ree
9
+
10
+ notifications:
11
+ irc: "irc.freenode.org#flowof.info"
12
+ recipients:
13
+ - rob@flowof.info
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in inject.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ | Project | Mixit
2
+ |:----------------|:--------------------------------------------------
3
+ | Homepage | https://github.com/robgleeson/Mixit
4
+ | Documentation | http://rubydoc.info/gems/Mixit/frames
5
+ | Author | Rob Gleeson
6
+
7
+
8
+ __DESCRIPTION__
9
+
10
+ Mixit can be used to temporarily extend the calling scope of a block or object with instance methods from a module.
11
+ It is written to help Domain Specific Language(DSL) writers provide a DSL with their own methods
12
+ without losing the calling scope of the caller.
13
+
14
+ __WHY?__
15
+
16
+ Some Ruby DSL approaches already exist(maybe one suits you):
17
+
18
+ * `instance_eval`
19
+ The calling scope is lost, and internal state is exposed to the user of the DSL.
20
+
21
+ * `instance_eval` via "EmptyContext"
22
+ The calling scope is lost, but the object is void of any state.
23
+
24
+ * Pass `self` onto the calling block.
25
+ The cleanest approach but some of the beauty of the DSL can be lost.
26
+
27
+ All these approaches(but the last) lose the calling scope, and that's why I created 'Mixit'.
28
+ I wanted the user of the DSL to have access to the calling scope, but have access to my DSL as
29
+ implicit receivers of 'self' as well.
30
+
31
+ This approach is by no means 'perfect', or even 'good'.
32
+ If you use it wisely, though, it can be a cool way to build nice DSLs.
33
+
34
+
35
+
36
+ __EXAMPLES__
37
+
38
+ Mix 'Foo' onto the 'self' referenced by _block_:
39
+
40
+ module Foo
41
+ def bar
42
+ end
43
+ end
44
+
45
+ def DSL(&block)
46
+ Mixit.mix(Foo, block)
47
+ end
48
+
49
+ DSL do
50
+ p respond_to?(:bar) # => true
51
+ end
52
+
53
+ p respond_to?(:bar) # => false
54
+
55
+ __INSTALL__
56
+
57
+ gem install mixit
58
+
59
+ __LICENSE__
60
+
61
+
62
+ See LICENSE.txt
63
+
64
+
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.name = :test
6
+ t.test_files = FileList["test/setup.rb", "test/*.rb"]
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,3 @@
1
+ def Mix *args, &block
2
+ Mixit.mix(*args, &block)
3
+ end
@@ -0,0 +1,3 @@
1
+ class Mixit
2
+ VERSION = '0.1.0'
3
+ end
data/lib/mixit.rb ADDED
@@ -0,0 +1,60 @@
1
+ require "mixit/core_ext/object"
2
+ require "mixit/version"
3
+
4
+ class Mixit
5
+
6
+ class << self
7
+
8
+ def mix(*args, &block)
9
+ case args.last
10
+ when Proc
11
+ scope = args.pop
12
+ receiver = scope.binding.eval("self")
13
+ modules = args
14
+ else
15
+ receiver = args.pop
16
+ modules = args
17
+ end
18
+
19
+ new(modules).mix_onto(receiver, &block)
20
+ end
21
+ end
22
+
23
+ def initialize(modules)
24
+ @modules = modules.map(&:clone)
25
+ end
26
+
27
+ def mix_onto(receiver, &block)
28
+ if block.arity == 1
29
+ block.call extend!(receiver, true)
30
+ else
31
+ ivars = receiver.instance_variables
32
+ extend!(receiver, false).instance_eval(&block)
33
+
34
+ # Remove instance variables added by @modules, or &block from 'receiver'.
35
+ # We want to restore the calling scope to its previous state.
36
+ (receiver.instance_variables - ivars).each do |ivar|
37
+ receiver.send(:remove_instance_variable, ivar)
38
+ end
39
+
40
+ # Remove methods added by @modules from 'receiver'.
41
+ # We want to restore the calling scope to its previous state.
42
+ @modules.each do |mod|
43
+ mod.instance_methods(false).each do |meth|
44
+ mod.send(:remove_method, meth)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def extend!(receiver, to_be_cloned=false)
51
+ if to_be_cloned
52
+ receiver = receiver.clone
53
+ end
54
+
55
+ @modules.inject(receiver) { |obj, mod| obj.extend(mod) }
56
+ end
57
+ private :extend!
58
+
59
+ end
60
+
data/mixit.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mixit/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "mixit"
7
+ s.version = Mixit::VERSION
8
+ s.authors = ["Rob Gleeson"]
9
+ s.email = ["rob@flowof.info"]
10
+ s.homepage = ""
11
+ s.summary = %q{Temporarily extend the calling scope of a block with instance methods from a module.}
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = "mixit"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rake" , "~> 0.9.2"
22
+ s.add_development_dependency "minitest" , "~> 2.5"
23
+ end
data/test/setup.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'mixit'
4
+ require 'minitest/spec'
5
+ require 'minitest/autorun'
6
+
7
+ alias :context :describe
@@ -0,0 +1,109 @@
1
+ context Mixit do
2
+
3
+ context 'mix' do
4
+ context 'When receiving a block.' do
5
+ before do
6
+ @module = Module.new do
7
+ def callme
8
+ :ok
9
+ end
10
+
11
+ def i
12
+ @i = 'asdf'
13
+ end
14
+ end
15
+ end
16
+
17
+ context 'When extending the calling scope of a Proc.' do
18
+ context 'Without cloning the receiver.' do
19
+ it 'must provide access to mixed in module methoss' do
20
+ block = proc { }
21
+
22
+ Mixit.mix(@module, block) do
23
+ callme.must_equal(:ok)
24
+ end
25
+ end
26
+
27
+ it 'must provide access to surrounding locals.' do
28
+ block = proc { }
29
+ local = :ok
30
+
31
+ Mixit.mix(@module, block) do
32
+ local.must_equal(:ok)
33
+ end
34
+ end
35
+
36
+ it 'must provide access to surrounding methods.' do
37
+ def callme_() :ok end
38
+ block = proc { }
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
86
+ end
87
+ end
88
+
89
+ context 'With the receiver cloned.' do
90
+ it 'must provide access to mixed in module methods.' do
91
+ block = proc {}
92
+
93
+ Mixit.mix(@module, block) do |receiver|
94
+ receiver.callme.must_equal(:ok)
95
+ end
96
+ end
97
+
98
+ it 'must not extending the original receiver with mixed in module methods.' do
99
+ block = proc {}
100
+
101
+ Mixit.mix(@module, block) do |receiver|
102
+ respond_to?(:callme).must_equal(false)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mixit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rob Gleeson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70294493576860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.2
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70294493576860
25
+ - !ruby/object:Gem::Dependency
26
+ name: minitest
27
+ requirement: &70294493576360 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '2.5'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70294493576360
36
+ description: Temporarily extend the calling scope of a block with instance methods
37
+ from a module.
38
+ email:
39
+ - rob@flowof.info
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - .travis.yml
46
+ - Gemfile
47
+ - README.md
48
+ - Rakefile
49
+ - lib/mixit.rb
50
+ - lib/mixit/core_ext/object.rb
51
+ - lib/mixit/version.rb
52
+ - mixit.gemspec
53
+ - test/setup.rb
54
+ - test/test_mixit.rb
55
+ homepage: ''
56
+ licenses: []
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ segments:
68
+ - 0
69
+ hash: 4457976507324133179
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ segments:
77
+ - 0
78
+ hash: 4457976507324133179
79
+ requirements: []
80
+ rubyforge_project: mixit
81
+ rubygems_version: 1.8.11
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Temporarily extend the calling scope of a block with instance methods from
85
+ a module.
86
+ test_files:
87
+ - test/setup.rb
88
+ - test/test_mixit.rb