meta_programming 0.0.1
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 +6 -0
- data/LICENSE +20 -0
- data/README.rdoc +28 -0
- data/Rakefile +66 -0
- data/init.rb +1 -0
- data/lib/meta_programming.rb +8 -0
- data/lib/meta_programming/class.rb +17 -0
- data/lib/meta_programming/object.rb +48 -0
- data/spec/meta_programming_spec.rb +159 -0
- data/spec/spec.opts +4 -0
- metadata +73 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2006-2009 Jeff Patmon
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
= MetaProgramming
|
|
2
|
+
|
|
3
|
+
Various meta-programming methods and 'spells' for Ruby
|
|
4
|
+
|
|
5
|
+
== Resources
|
|
6
|
+
|
|
7
|
+
Install
|
|
8
|
+
|
|
9
|
+
* sudo gem install meta_programming
|
|
10
|
+
|
|
11
|
+
Use
|
|
12
|
+
|
|
13
|
+
* require 'meta_programming'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
== Description
|
|
17
|
+
|
|
18
|
+
Current Methods
|
|
19
|
+
* eigenclass (or metaclass)
|
|
20
|
+
* safe_alias_method_chain
|
|
21
|
+
* define_chained_method
|
|
22
|
+
* blank_slate
|
|
23
|
+
* clean_room
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
== Dependencies
|
|
27
|
+
|
|
28
|
+
none
|
data/Rakefile
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#require 'rake/testtask'
|
|
2
|
+
require 'rake/rdoctask'
|
|
3
|
+
require 'rake/gempackagetask'
|
|
4
|
+
gem 'gem_version', '>= 0.0.1'
|
|
5
|
+
require 'gem_version'
|
|
6
|
+
#require 'rake/contrib/sshpublisher'
|
|
7
|
+
|
|
8
|
+
spec = Gem::Specification.new do |s|
|
|
9
|
+
s.name = 'meta_programming'
|
|
10
|
+
s.version = GemVersion.next_version
|
|
11
|
+
s.platform = Gem::Platform::RUBY
|
|
12
|
+
s.required_ruby_version = '>= 1.8.7'
|
|
13
|
+
s.description = 'Collection of meta-programming methods for Ruby'
|
|
14
|
+
s.summary = 'Collection of meta-programming methods for Ruby'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
exclude_folders = '' # 'spec/rails/{doc,lib,log,nbproject,tmp,vendor,test}'
|
|
18
|
+
exclude_files = [] # FileList['**/*.log'] + FileList[exclude_folders+'/**/*'] + FileList[exclude_folders]
|
|
19
|
+
s.files = FileList['{lib,spec}/**/*'] + %w(init.rb LICENSE Rakefile README.rdoc .gitignore) - exclude_files
|
|
20
|
+
s.require_path = 'lib'
|
|
21
|
+
s.has_rdoc = true
|
|
22
|
+
s.test_files = Dir['spec/*_spec.rb']
|
|
23
|
+
|
|
24
|
+
s.author = 'Jeff Patmon'
|
|
25
|
+
s.email = 'jpatmon@gmail.com'
|
|
26
|
+
s.homepage = 'http://github.com/jeffp/meta_programming/tree/master'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
require 'spec/version'
|
|
30
|
+
require 'spec/rake/spectask'
|
|
31
|
+
|
|
32
|
+
desc "Run specs"
|
|
33
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
|
34
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
|
35
|
+
t.libs << 'lib' << 'spec'
|
|
36
|
+
t.rcov = false
|
|
37
|
+
t.spec_opts = ['--options', 'spec/spec.opts']
|
|
38
|
+
#t.rcov_dir = 'coverage'
|
|
39
|
+
#t.rcov_opts = ['--exclude', "kernel,load-diff-lcs\.rb,instance_exec\.rb,lib/spec.rb,lib/spec/runner.rb,^spec/*,bin/spec,examples,/gems,/Library/Ruby,\.autotest,#{ENV['GEM_HOME']}"]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc "Generate documentation for the #{spec.name} gem."
|
|
43
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
44
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
45
|
+
rdoc.title = spec.name
|
|
46
|
+
#rdoc.template = '../rdoc_template.rb'
|
|
47
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
|
48
|
+
rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'lib/**/*.rb')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
desc 'Generate a gemspec file.'
|
|
52
|
+
task :gemspec do
|
|
53
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
|
54
|
+
f.write spec.to_ruby
|
|
55
|
+
end
|
|
56
|
+
GemVersion.increment_version
|
|
57
|
+
GemVersion.commit_and_push
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
Rake::GemPackageTask.new(spec) do |p|
|
|
61
|
+
p.gem_spec = spec
|
|
62
|
+
p.need_tar = RUBY_PLATFORM =~ /mswin/ ? false : true
|
|
63
|
+
p.need_zip = true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
Dir['tasks/**/*.rake'].each {|rake| load rake}
|
data/init.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'arspy'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module MetaProgramming
|
|
2
|
+
module Class
|
|
3
|
+
def self.included(base)
|
|
4
|
+
raise 'This module may only be included in class Class' unless base.name == 'Class'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def blank_slate(opts={})
|
|
8
|
+
opts[:except] = opts[:except] ? (opts[:except].is_a?(Array) ? opts[:except] : [opts[:except]]) : []
|
|
9
|
+
exceptions = ['method_missing', 'respond_to\?', '^__'] + opts[:except].map{|ex| ex.to_s}
|
|
10
|
+
regexp = Regexp.new(exceptions.join('|'))
|
|
11
|
+
instance_methods.each do |m|
|
|
12
|
+
undef_method m unless regexp.match(m.to_s)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
alias_method :clean_room, :blank_slate
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module MetaProgramming
|
|
2
|
+
module Object
|
|
3
|
+
def self.included(base)
|
|
4
|
+
raise 'This module may only be included in class Object' unless base.name == 'Object'
|
|
5
|
+
base.extend(ClassMethods)
|
|
6
|
+
base.class_eval do
|
|
7
|
+
alias_method :metaclass, :eigenclass
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def eigenclass
|
|
12
|
+
class << self; self; end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module ClassMethods
|
|
16
|
+
def safe_alias_method_chain(method_name, ext)
|
|
17
|
+
class_eval do
|
|
18
|
+
stripped_method_name, punctuation = method_name.to_s.sub(/([?!=])$/, ''), $1
|
|
19
|
+
method_name_with_ext = "#{stripped_method_name}_with_#{ext}#{punctuation}".to_sym
|
|
20
|
+
method_name_without_ext = "#{stripped_method_name}_without_#{ext}#{punctuation}".to_sym
|
|
21
|
+
instance_variable = "#{stripped_method_name}_#{ext}_#{{'?'=>'questmark', '!'=>'bang', '='=>'equals'}[punctuation]}"
|
|
22
|
+
if (method_defined?(method_name_with_ext) && !(eigenclass.instance_variable_defined?("@#{instance_variable}")))
|
|
23
|
+
if method_defined?(method_name.to_sym)
|
|
24
|
+
#alias_method_chain(method_name.to_sym, ext.to_sym)
|
|
25
|
+
alias_method method_name_without_ext, method_name.to_sym
|
|
26
|
+
alias_method method_name.to_sym, method_name_with_ext
|
|
27
|
+
case
|
|
28
|
+
when public_method_defined?(method_name_without_ext) then public(method_name.to_sym)
|
|
29
|
+
when protected_method_defined?(method_name_without_ext) then protected(without_method.to_sym)
|
|
30
|
+
when private_method_defined?(method_name_without_ext) then private(without_method.to_sym)
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
alias_method method_name.to_sym, method_name_with_ext
|
|
34
|
+
define_method(method_name_without_ext) {|*args| }
|
|
35
|
+
end
|
|
36
|
+
eigenclass.instance_variable_set("@#{instance_variable}", true)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def define_chained_method(method_name, ext, &block)
|
|
42
|
+
define_method("#{method_name}_with_#{ext}".to_sym, block)
|
|
43
|
+
safe_alias_method_chain(method_name.to_sym, ext)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require 'lib/meta_programming'
|
|
2
|
+
|
|
3
|
+
describe "MetaProgramming" do
|
|
4
|
+
describe "eigenclass and metaclass methods" do
|
|
5
|
+
it "should return the eigen class of a class" do
|
|
6
|
+
class A
|
|
7
|
+
class << self; self; end.should == self.eigenclass
|
|
8
|
+
class << self; self; end.should == self.metaclass
|
|
9
|
+
end
|
|
10
|
+
class << A; self; end.should == A.eigenclass
|
|
11
|
+
class << A; self; end.should == A.metaclass
|
|
12
|
+
end
|
|
13
|
+
it "should return the eigen class of an object" do
|
|
14
|
+
class A; def return_eigenclass; eigenclass; end; end
|
|
15
|
+
a = A.new
|
|
16
|
+
class << a; self; end.should == a.eigenclass
|
|
17
|
+
class << a; self; end.should == a.metaclass
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
describe "define_chained_method" do
|
|
21
|
+
it "should complain if there is no block" do
|
|
22
|
+
lambda {
|
|
23
|
+
class B1
|
|
24
|
+
def target; end
|
|
25
|
+
define_chained_method(:target, :chain)
|
|
26
|
+
end
|
|
27
|
+
}.should raise_exception
|
|
28
|
+
end
|
|
29
|
+
it "should define and chain a method" do
|
|
30
|
+
class B2; def target(array); array << 'target'; end; end
|
|
31
|
+
B2.new.target(['init']).should == ['init', 'target']
|
|
32
|
+
class B2
|
|
33
|
+
define_chained_method(:target, :chain) do |array|
|
|
34
|
+
array << 'chain'
|
|
35
|
+
target_without_chain(array)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
B2.new.target(['init']).should == ['init', 'chain', 'target']
|
|
39
|
+
end
|
|
40
|
+
# it "should complain if block has wrong arity" do
|
|
41
|
+
# lambda {
|
|
42
|
+
# class B3
|
|
43
|
+
# def target(array); array << 'target'; end
|
|
44
|
+
# define_chained_method(:target, :chain) do
|
|
45
|
+
# target_without_chain(['chain'])
|
|
46
|
+
# end
|
|
47
|
+
# end
|
|
48
|
+
# B3.new.target(['init'])
|
|
49
|
+
# }.should raise_exception
|
|
50
|
+
# end
|
|
51
|
+
it "should define and chain a method safely if there is no target method" do
|
|
52
|
+
class B4
|
|
53
|
+
define_chained_method(:target, :chain) do |array|
|
|
54
|
+
array << 'chain'
|
|
55
|
+
target_without_chain(array)
|
|
56
|
+
end
|
|
57
|
+
B4.new.target(['init']).should == ['init', 'chain']
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
describe "safe_alias_method_chain method" do
|
|
62
|
+
it "should not chain for non-existent chaining method" do
|
|
63
|
+
class A
|
|
64
|
+
def primary(array); array << 'primary'; end
|
|
65
|
+
#do not define :primary_with_ext
|
|
66
|
+
safe_alias_method_chain :primary, :ext
|
|
67
|
+
end
|
|
68
|
+
a=A.new
|
|
69
|
+
a.primary(['init']).should == ['init', 'primary']
|
|
70
|
+
end
|
|
71
|
+
it "should not raise NoMethod error when chaining for non-existing primary method" do
|
|
72
|
+
lambda {
|
|
73
|
+
class A0
|
|
74
|
+
def primary_with_ext(array)
|
|
75
|
+
array << 'chaining'
|
|
76
|
+
primary_without_ext(array)
|
|
77
|
+
end
|
|
78
|
+
safe_alias_method_chain :primary, :ext
|
|
79
|
+
end
|
|
80
|
+
a=A0.new
|
|
81
|
+
a.primary(['init']).should == ['init', 'chaining']
|
|
82
|
+
}.should_not raise_exception(NoMethodError)
|
|
83
|
+
end
|
|
84
|
+
it "should chain for two methods" do
|
|
85
|
+
class A1
|
|
86
|
+
def primary(array)
|
|
87
|
+
array << 'primary'
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def primary_with_ext(array)
|
|
91
|
+
array << 'chaining_in'
|
|
92
|
+
primary_without_ext(array)
|
|
93
|
+
array << 'chaining_out'
|
|
94
|
+
end
|
|
95
|
+
safe_alias_method_chain :primary, :ext
|
|
96
|
+
end
|
|
97
|
+
A1.new.primary(['init']).should == ['init', 'chaining_in', 'primary', 'chaining_out']
|
|
98
|
+
end
|
|
99
|
+
it "should chain for three methods" do
|
|
100
|
+
class A2
|
|
101
|
+
def primary(array); array << 'primary'; end
|
|
102
|
+
|
|
103
|
+
def primary_with_one(array); array << 'one'; primary_without_one(array); end
|
|
104
|
+
|
|
105
|
+
def primary_with_two(array); array << 'two'; primary_without_two(array); end
|
|
106
|
+
|
|
107
|
+
safe_alias_method_chain :primary, :one
|
|
108
|
+
safe_alias_method_chain :primary, :two
|
|
109
|
+
end
|
|
110
|
+
A2.new.primary(['init']).should == ['init', 'two', 'one', 'primary']
|
|
111
|
+
end
|
|
112
|
+
it "should not cause endless loop when called twice" do
|
|
113
|
+
lambda {
|
|
114
|
+
class A3
|
|
115
|
+
def primary(array); array << 'primary'; end
|
|
116
|
+
def primary_with_one(array); array << 'one'; primary_without_one(array); end
|
|
117
|
+
|
|
118
|
+
safe_alias_method_chain :primary, :one
|
|
119
|
+
safe_alias_method_chain :primary, :one
|
|
120
|
+
end
|
|
121
|
+
A3.new.primary(['init']).should == ['init', 'one', 'primary']
|
|
122
|
+
}.should_not raise_exception
|
|
123
|
+
end
|
|
124
|
+
it "should chain with = punctuation" do
|
|
125
|
+
class A5
|
|
126
|
+
def primary=(array); array << 'primary'; end
|
|
127
|
+
def primary_with_one=(array); array << 'one'; self.primary_without_one=(array); end
|
|
128
|
+
|
|
129
|
+
safe_alias_method_chain :primary=, :one
|
|
130
|
+
end
|
|
131
|
+
lambda {
|
|
132
|
+
a = A5.new.primary=(['init'])
|
|
133
|
+
a.should == ['init', 'one', 'primary']
|
|
134
|
+
}.should_not raise_exception
|
|
135
|
+
end
|
|
136
|
+
it "should chain with ? punctuation" do
|
|
137
|
+
class A6
|
|
138
|
+
def primary?(array); array << 'primary'; end
|
|
139
|
+
def primary_with_one?(array); array << 'one'; primary_without_one?(array); end
|
|
140
|
+
|
|
141
|
+
safe_alias_method_chain :primary?, :one
|
|
142
|
+
end
|
|
143
|
+
lambda {
|
|
144
|
+
A6.new.primary?(['init']).should == ['init', 'one', 'primary']
|
|
145
|
+
}.should_not raise_exception
|
|
146
|
+
end
|
|
147
|
+
it "should chain with ! punctuation" do
|
|
148
|
+
class A7
|
|
149
|
+
def primary!(array); array << 'primary'; end
|
|
150
|
+
def primary_with_one!(array); array << 'one'; primary_without_one!(array); end
|
|
151
|
+
|
|
152
|
+
safe_alias_method_chain :primary!, :one
|
|
153
|
+
end
|
|
154
|
+
lambda {
|
|
155
|
+
A7.new.primary!(['init']).should == ['init', 'one', 'primary']
|
|
156
|
+
}.should_not raise_exception
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
data/spec/spec.opts
ADDED
metadata
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: meta_programming
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease: false
|
|
5
|
+
segments:
|
|
6
|
+
- 0
|
|
7
|
+
- 0
|
|
8
|
+
- 1
|
|
9
|
+
version: 0.0.1
|
|
10
|
+
platform: ruby
|
|
11
|
+
authors:
|
|
12
|
+
- Jeff Patmon
|
|
13
|
+
autorequire:
|
|
14
|
+
bindir: bin
|
|
15
|
+
cert_chain: []
|
|
16
|
+
|
|
17
|
+
date: 2010-04-29 00:00:00 -07:00
|
|
18
|
+
default_executable:
|
|
19
|
+
dependencies: []
|
|
20
|
+
|
|
21
|
+
description: Collection of meta-programming methods for Ruby
|
|
22
|
+
email: jpatmon@gmail.com
|
|
23
|
+
executables: []
|
|
24
|
+
|
|
25
|
+
extensions: []
|
|
26
|
+
|
|
27
|
+
extra_rdoc_files: []
|
|
28
|
+
|
|
29
|
+
files:
|
|
30
|
+
- lib/meta_programming/class.rb
|
|
31
|
+
- lib/meta_programming/object.rb
|
|
32
|
+
- lib/meta_programming.rb
|
|
33
|
+
- spec/meta_programming_spec.rb
|
|
34
|
+
- spec/spec.opts
|
|
35
|
+
- init.rb
|
|
36
|
+
- LICENSE
|
|
37
|
+
- Rakefile
|
|
38
|
+
- README.rdoc
|
|
39
|
+
- .gitignore
|
|
40
|
+
has_rdoc: true
|
|
41
|
+
homepage: http://github.com/jeffp/meta_programming/tree/master
|
|
42
|
+
licenses: []
|
|
43
|
+
|
|
44
|
+
post_install_message:
|
|
45
|
+
rdoc_options: []
|
|
46
|
+
|
|
47
|
+
require_paths:
|
|
48
|
+
- lib
|
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
segments:
|
|
54
|
+
- 1
|
|
55
|
+
- 8
|
|
56
|
+
- 7
|
|
57
|
+
version: 1.8.7
|
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
segments:
|
|
63
|
+
- 0
|
|
64
|
+
version: "0"
|
|
65
|
+
requirements: []
|
|
66
|
+
|
|
67
|
+
rubyforge_project:
|
|
68
|
+
rubygems_version: 1.3.6
|
|
69
|
+
signing_key:
|
|
70
|
+
specification_version: 3
|
|
71
|
+
summary: Collection of meta-programming methods for Ruby
|
|
72
|
+
test_files:
|
|
73
|
+
- spec/meta_programming_spec.rb
|