smtlaissezfaire-mix_master 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/MIT_LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009 Scott Taylor (smtlaissezfaire) <scott@railsnewbie.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,32 @@
1
+ = MixMaster
2
+
3
+ A pure ruby solution for mixing & unmix modules from classes.
4
+
5
+ module Foo
6
+ def foo
7
+ 'foo'
8
+ end
9
+ end
10
+
11
+ class Baz
12
+ mixin Foo
13
+ end
14
+
15
+ b = Baz.new
16
+ => #<Baz:0x6aa48c>
17
+ b.foo
18
+ => "foo"
19
+
20
+ Baz.mixout Foo
21
+ => [Foo]
22
+
23
+ Baz.new.foo
24
+ NoMethodError: undefined method `foo' for #<Baz:0x69a640>
25
+ from (irb):30
26
+ b.foo
27
+ NoMethodError: undefined method `foo' for #<Baz:0x6aa48c>
28
+ from (irb):31
29
+
30
+ == Inspiration
31
+
32
+ Both _why's and Dan Manges' libraries (mixico and mixology) have been of great inspiration.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+
2
+ Dir.glob(File.dirname(__FILE__) + "/rake_tasks/**/**").each do |file|
3
+ load file
4
+ end
5
+
6
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.3
data/lib/mix_master.rb ADDED
@@ -0,0 +1,23 @@
1
+ require "using"
2
+
3
+ module MixMaster
4
+ extend Using
5
+
6
+ using :Mixer
7
+ using :MixinTable
8
+ using :ModuleExtension
9
+ using :Version
10
+
11
+ class MixingError < StandardError; end
12
+ extend Mixer
13
+
14
+ class << self
15
+ def install
16
+ mixin MixMaster::ModuleExtension, Object
17
+ end
18
+
19
+ def uninstall
20
+ mixout MixMaster::ModuleExtension, Object
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module MixMaster
2
+ module Mixer
3
+ def mixin(mod, target)
4
+ mod_duplicate = mod.clone
5
+
6
+ target.instance_eval { include mod_duplicate }
7
+
8
+ mixin_table.set(mod, target, mod_duplicate)
9
+ end
10
+
11
+ def mixout(from, to)
12
+ check_mixin(from, to)
13
+
14
+ mixin_table.get(from, to).instance_eval do
15
+ methods = (public_instance_methods(false) + protected_instance_methods(false) + private_instance_methods(false))
16
+
17
+ methods.each do |m|
18
+ remove_method m
19
+ end
20
+ end
21
+
22
+ mixin_table.delete(from, to)
23
+ end
24
+
25
+ private
26
+
27
+ def check_mixin(from, to)
28
+ unless mixin_table.exists?(from, to)
29
+ raise MixMaster::MixingError, "#{from} was never mixed into #{to} with #mixin, or was already mixed out"
30
+ end
31
+ end
32
+
33
+ def mixin_table
34
+ @mixin_table ||= MixinTable.new
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ module MixMaster
2
+ class MixinTable
3
+ def initialize
4
+ @list = {}
5
+ end
6
+
7
+ def set(source, target, source_dup)
8
+ @list[source] ||= {}
9
+ @list[source][target] = source_dup
10
+ end
11
+
12
+ def exists?(from, to)
13
+ get(from, to) ? true : false
14
+ end
15
+
16
+ def get(from, to)
17
+ @list[from] && @list[from][to]
18
+ end
19
+
20
+ def delete(from, to)
21
+ @list[from] = nil
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ module MixMaster
2
+ module ModuleExtension
3
+ def mixin(*mods)
4
+ mods.each do |mod|
5
+ MixMaster.mixin(mod, self)
6
+ end
7
+ end
8
+
9
+ def mixout(*mods)
10
+ mods.each do |mod|
11
+ MixMaster.mixout(mod, self)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module MixMaster
2
+ module Version
3
+ STRING = File.read(File.expand_path(File.dirname(__FILE__) + "/../../VERSION")).strip
4
+
5
+ MAJOR,
6
+ MINOR,
7
+ TINY = STRING.split(",")
8
+ end
9
+ end
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mix_master}
8
+ s.version = "0.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Scott Taylor"]
12
+ s.date = %q{2009-09-05}
13
+ s.description = %q{Allows modules to be unmixed at runtime from classes, much as Mixology or Mixico do. Implemented in pure ruby with no C extensions.}
14
+ s.email = %q{scott@railsnewbie.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "BUGS.rdoc",
21
+ "GPL_LICENSE",
22
+ "MIT_LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/mix_master.rb",
27
+ "lib/mix_master/mixer.rb",
28
+ "lib/mix_master/mixin_table.rb",
29
+ "lib/mix_master/module_extension.rb",
30
+ "lib/mix_master/version.rb",
31
+ "mix_master.gemspec",
32
+ "rake_tasks/flog.rake",
33
+ "rake_tasks/gems.rake",
34
+ "rake_tasks/rdoc.rake",
35
+ "rake_tasks/rspec.rake",
36
+ "rake_tasks/sloc.rake",
37
+ "rake_tasks/tags.rake",
38
+ "spec/installation_spec.rb",
39
+ "spec/mix_master_spec.rb",
40
+ "spec/module_methods_spec.rb",
41
+ "spec/spec.opts",
42
+ "spec/spec_helper.rb",
43
+ "spec/version_spec.rb"
44
+ ]
45
+ s.has_rdoc = true
46
+ s.homepage = %q{http://github.com/smtlaissezfaire/mix_master}
47
+ s.rdoc_options = ["--charset=UTF-8"]
48
+ s.require_paths = ["lib"]
49
+ s.rubygems_version = %q{1.3.1}
50
+ s.summary = %q{Mix and unmix modules}
51
+ s.test_files = [
52
+ "spec/installation_spec.rb",
53
+ "spec/mix_master_spec.rb",
54
+ "spec/module_methods_spec.rb",
55
+ "spec/spec_helper.rb",
56
+ "spec/version_spec.rb"
57
+ ]
58
+
59
+ if s.respond_to? :specification_version then
60
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
61
+ s.specification_version = 2
62
+
63
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
64
+ s.add_runtime_dependency(%q<smtlaissezfaire-using>, [">= 0.1.5"])
65
+ else
66
+ s.add_dependency(%q<smtlaissezfaire-using>, [">= 0.1.5"])
67
+ end
68
+ else
69
+ s.add_dependency(%q<smtlaissezfaire-using>, [">= 0.1.5"])
70
+ end
71
+ end
@@ -0,0 +1,10 @@
1
+ desc "Feel the pain of my code, and submit a refactoring patch"
2
+ task :flog do
3
+ puts %x(find lib | grep ".rb$" | xargs flog)
4
+ end
5
+
6
+ task :flog_to_disk => :create_doc_directory do
7
+ puts "Flogging..."
8
+ %x(find lib | grep ".rb$" | xargs flog > doc/flog.txt)
9
+ puts "Done Flogging...\n"
10
+ end
@@ -0,0 +1,19 @@
1
+ begin
2
+ require 'jeweler'
3
+
4
+ Jeweler::Tasks.new do |gemspec|
5
+ gemspec.name = "mix_master"
6
+ gemspec.summary = "Mix and unmix modules"
7
+ gemspec.description = <<-DESC
8
+ Allows modules to be unmixed at runtime from classes,
9
+ much as Mixology or Mixico do. Implemented in pure ruby with
10
+ no C extensions.
11
+ DESC
12
+ gemspec.email = "scott@railsnewbie.com"
13
+ gemspec.homepage = "http://github.com/smtlaissezfaire/mix_master"
14
+ gemspec.authors = ["Scott Taylor"]
15
+ gemspec.add_dependency "smtlaissezfaire-using", ">= 0.1.5"
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
@@ -0,0 +1,16 @@
1
+ require 'rake'
2
+ require 'hanna/rdoctask'
3
+
4
+ DOC_DIRECTORY = File.dirname(__FILE__) + "/../doc"
5
+
6
+ Rake::RDocTask.new do |rdoc|
7
+ rdoc.rdoc_dir = DOC_DIRECTORY
8
+ rdoc.title = "mix_master"
9
+ rdoc.options << '--line-numbers' << '--inline-source'
10
+
11
+ rdoc.options << '--webcvs=http://github.com/mislav/will_paginate/tree/master/'
12
+
13
+ ["README.rdoc", "BUGS.rdoc", "GPL_LICENSE", "MIT_LICENSE", "lib/**/*.rb"].each do |file|
14
+ rdoc.rdoc_files.include(file)
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec/rake/spectask'
2
+ require 'spec/rake/verify_rcov'
3
+
4
+ desc 'Run the specs'
5
+ Spec::Rake::SpecTask.new do |t|
6
+ t.warning = false
7
+ t.spec_opts = ["--color"]
8
+ end
9
+
10
+ desc "Create the html specdoc"
11
+ Spec::Rake::SpecTask.new(:specdoc) do |t|
12
+ t.spec_opts = ["--format", "html:doc/specdoc.html"]
13
+ end
14
+
15
+ desc "Run all examples with RCov"
16
+ Spec::Rake::SpecTask.new(:rcov) do |t|
17
+ t.rcov = true
18
+ t.rcov_opts = ['--exclude', 'spec']
19
+ t.rcov_dir = "doc/rcov"
20
+ end
@@ -0,0 +1,16 @@
1
+ def sloc
2
+ `sloccount #{File.dirname(__FILE__)}/../lib #{File.dirname(__FILE__)}/../ext`
3
+ end
4
+
5
+ desc "Output sloccount report. You'll need sloccount installed."
6
+ task :sloc do
7
+ puts "Counting lines of code"
8
+ puts sloc
9
+ end
10
+
11
+ desc "Write sloccount report"
12
+ task :output_sloc => :create_doc_directory do
13
+ File.open(File.dirname(__FILE__) + "/doc/lines_of_code.txt", "w") do |f|
14
+ f << sloc
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ # Build the TAGS file for Emacs
2
+ # Taken with slight modifications from
3
+ # http://blog.lathi.net/articles/2007/11/07/navigating-your-projects-in-emacs
4
+ #
5
+ # Thanks Jim Weirich
6
+
7
+ module Emacs
8
+ module Tags
9
+ def self.ruby_files
10
+ @ruby_files ||= FileList['**/*.rb'].exclude("pkg")
11
+ end
12
+ end
13
+ end
14
+
15
+ namespace :tags do
16
+ task :emacs do
17
+ puts "Making Emacs TAGS file"
18
+ sh "ctags -e #{Emacs::Tags.ruby_files}", :verbose => false
19
+ end
20
+ end
21
+
22
+ desc "Build the emacs tags file"
23
+ task :tags => ["tags:emacs"]
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
2
+
3
+ describe MixMaster do
4
+ describe "installation" do
5
+ after do
6
+ MixMaster.uninstall
7
+ end
8
+
9
+ it "should have mixin + mixout in Object" do
10
+ MixMaster.install
11
+ Object.should respond_to(:mixin)
12
+ Object.should respond_to(:mixout)
13
+ end
14
+ end
15
+
16
+ describe "uninstall" do
17
+ before do
18
+ MixMaster.install
19
+ end
20
+
21
+ it "should uninstall mixin, mixout" do
22
+ MixMaster.uninstall
23
+
24
+ Object.should_not respond_to(:mixin)
25
+ Object.should_not respond_to(:mixout)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,179 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
2
+
3
+ describe MixMaster do
4
+ describe "mixing a module into a class" do
5
+ before do
6
+ @target = Class.new
7
+ @source = Module.new do
8
+ def something
9
+ end
10
+
11
+ def a_public_method
12
+ end
13
+
14
+ protected
15
+
16
+ def a_protected_method
17
+ end
18
+
19
+ private
20
+
21
+ def a_private_method
22
+ end
23
+ end
24
+ end
25
+
26
+ describe "mixing in" do
27
+ it "should add a public method" do
28
+ MixMaster.mixin(@source, @target)
29
+
30
+ @target.new.should respond_to(:something)
31
+ end
32
+
33
+ it "should add private methods" do
34
+ MixMaster.mixin(@source, @target)
35
+
36
+ @target.new.private_methods.should include("a_private_method")
37
+ end
38
+
39
+ it "should add protected methods" do
40
+ MixMaster.mixin(@source, @target)
41
+
42
+ @target.new.protected_methods.should include("a_protected_method")
43
+ end
44
+ end
45
+
46
+ describe "mixing out" do
47
+ it "should no longer respond to a public method" do
48
+ MixMaster.mixin(@source, @target)
49
+ MixMaster.mixout(@source, @target)
50
+
51
+ @target.new.should_not respond_to(:something)
52
+ end
53
+
54
+ it "should no longer respond to any public method" do
55
+ MixMaster.mixin(@source, @target)
56
+ MixMaster.mixout(@source, @target)
57
+
58
+ @target.new.should_not respond_to(:a_public_method)
59
+ end
60
+
61
+ it "should no longer have protected methods added" do
62
+ MixMaster.mixin(@source, @target)
63
+ MixMaster.mixout(@source, @target)
64
+
65
+ @target.new.protected_methods.should_not include("a_protected_method")
66
+ end
67
+
68
+ it "should no longer have private methods added" do
69
+ MixMaster.mixin(@source, @target)
70
+ MixMaster.mixout(@source, @target)
71
+
72
+ @target.new.private_methods.should_not include("a_private_method")
73
+ end
74
+
75
+ it "should not remove the methods from the module given" do
76
+ lambda {
77
+ MixMaster.mixin(@source, @target)
78
+ MixMaster.mixout(@source, @target)
79
+ }.should_not change { @source.instance_methods }
80
+ end
81
+
82
+ it "should have a method added after remixing" do
83
+ MixMaster.mixin(@source, @target)
84
+ MixMaster.mixout(@source, @target)
85
+ MixMaster.mixin(@source, @target)
86
+
87
+ @target.new.should respond_to(:something)
88
+ end
89
+
90
+ it "should raise an error if never mixed in" do
91
+ lambda {
92
+ MixMaster.mixout(@source, @target)
93
+ }.should raise_error(MixMaster::MixingError, "#{@source} was never mixed into #{@target} with #mixin, or was already mixed out")
94
+ end
95
+
96
+ it "should be able to mixout from a first module after mixing to a second one" do
97
+ one = Module.new do
98
+ def one; end
99
+ end
100
+
101
+ two = Module.new do
102
+ def two; end
103
+ end
104
+
105
+ MixMaster.mixin(one, @target)
106
+ MixMaster.mixin(two, @target)
107
+ MixMaster.mixout(one, @target)
108
+
109
+ @target.new.should_not respond_to(:one)
110
+ @target.new.should respond_to(:two)
111
+ end
112
+
113
+ it "should add methods in a module inside of a module" do
114
+ one = Module.new do
115
+ def one; end
116
+ end
117
+
118
+ two = Module.new do
119
+ include one
120
+ end
121
+
122
+ MixMaster.mixin(two, @target)
123
+
124
+ @target.new.should respond_to(:one)
125
+ end
126
+
127
+ it "should NOT remove methods from other parents (such as modules included into the module given)" do
128
+ one = Module.new do
129
+ def one; end
130
+ end
131
+
132
+ two = Module.new do
133
+ include one
134
+ end
135
+
136
+ MixMaster.mixin(two, @target)
137
+ MixMaster.mixout(two, @target)
138
+
139
+ @target.new.should respond_to(:one)
140
+ end
141
+
142
+ it "should not remove methods like to_s which will be defined on a regular object & a module (since the class is always first in the hierarchy chain)" do
143
+ one = Module.new do
144
+ end
145
+
146
+ MixMaster.mixin(one, @target)
147
+ MixMaster.mixout(one, @target)
148
+
149
+ @target.new.should respond_to(:inspect)
150
+ end
151
+
152
+ it "should allow calling of super in the hierarchy chain" do
153
+ one = Module.new do
154
+ def something
155
+ "from_mod"
156
+ end
157
+ end
158
+
159
+ two = Module.new do
160
+ def something
161
+ super
162
+ end
163
+ end
164
+
165
+ klass = Class.new do
166
+ def something
167
+ super
168
+ end
169
+ end
170
+
171
+ MixMaster.mixin(one, klass)
172
+ MixMaster.mixin(two, klass)
173
+ MixMaster.mixout(two, klass)
174
+
175
+ klass.new.something.should == "from_mod"
176
+ end
177
+ end
178
+ end
179
+ end