virtual_keywords 0.0.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.
@@ -0,0 +1,241 @@
1
+ # Parent module containing all variables defined as part of virtual_keywords
2
+ module VirtualKeywords
3
+
4
+ # Utility functions used to inspect the class hierarchy, and to view
5
+ # and modify methods of classes.
6
+ class ClassReflection
7
+ # Get the subclasses of a given class.
8
+ #
9
+ # Arguments:
10
+ # parent: (Class) the class whose subclasses to find.
11
+ #
12
+ # Returns:
13
+ # (Array) all classes which are subclasses of parent.
14
+ def self.subclasses_of_class(parent)
15
+ ObjectSpace.each_object(Class).select { |klass|
16
+ klass < parent
17
+ }
18
+ end
19
+
20
+ # Given an array of base classes, return a flat array of all their
21
+ # subclasses.
22
+ #
23
+ # Arguments:
24
+ # klasses: (Array[Class]) an array of classes
25
+ #
26
+ # Returns:
27
+ # (Array) All classes that are subclasses of one of the classes in klasses,
28
+ # in a flattened array.
29
+ def self.subclasses_of_classes(klasses)
30
+ klasses.map { |klass|
31
+ subclasses_of_class klass
32
+ }.flatten
33
+ end
34
+
35
+ # Get the instance_methods of a class.
36
+ #
37
+ # Arguments:
38
+ # klass: (Class) the class.
39
+ #
40
+ # Returns:
41
+ # (Hash[Symbol, Array]) A hash, mapping method names to the results of
42
+ # ParseTree.translate.
43
+ def self.instance_methods_of(klass)
44
+ methods = {}
45
+ klass.instance_methods(false).each do |method_name|
46
+ translated = ParseTree.translate(klass, method_name)
47
+ methods[method_name] = translated
48
+ end
49
+
50
+ methods
51
+ end
52
+
53
+ # Install a method on a class. When object.method_name is called
54
+ # (for objects in the class), have them run the given code.
55
+ # TODO Should it be possible to recover the old method?
56
+ # How would that API look?
57
+ #
58
+ # Arguments:
59
+ # klass: (Class) the class which should be modified.
60
+ # method_code: (String) the code for the method to install, of the format:
61
+ # def method_name(args)
62
+ # ...
63
+ # end
64
+ def self.install_method_on_class(klass, method_code)
65
+ klass.class_eval method_code
66
+ end
67
+
68
+ # Install a method on an object. When object.method_name is called,
69
+ # runs the given code.
70
+ #
71
+ # This function can also be used for classmethods. For example, if you want
72
+ # to rewrite Klass.method_name (a method on Klass, a singleton Class),
73
+ # call this method (NOT install_method_on_class, that will modifiy objects
74
+ # created through Klass.new!)
75
+ #
76
+ # Arguments:
77
+ # object: (Object) the object instance that should be modified.
78
+ # method_code: (String) the code for the method to install, of the format:
79
+ # def method_name(args)
80
+ # ...
81
+ # end
82
+ def self.install_method_on_instance(object, method_code)
83
+ object.instance_eval method_code
84
+ end
85
+ end
86
+
87
+ # Deeply copy an array.
88
+ #
89
+ # Arguments:
90
+ # array: (Array[A]) the array to copy. A is any arbitrary type.
91
+ #
92
+ # Returns:
93
+ # (Array[A]) a deep copy of the original array.
94
+ def self.deep_copy_array(array)
95
+ Marshal.load(Marshal.dump(array))
96
+ end
97
+
98
+ # Object that virtualizes keywords.
99
+ class Virtualizer
100
+ # Initialize a Virtualizer
101
+ #
102
+ # Arguments:
103
+ # A Hash with the following arguments (all optional):
104
+ # for_classes: (Array[Class]) an array of classes. All methods of objects
105
+ # created from the given classes will be virtualized
106
+ # (optional, the default is an empty Array).
107
+ # for_instances: (Array[Object]) an array of object. All of these objects'
108
+ # methods will be virtualized
109
+ # (optional, the default is an empty Array).
110
+ # for subclasses_of: (Array[Class]) an array of classes. All methods of
111
+ # objects created from the given classes' subclasses
112
+ # (but NOT those from the given classes) will be
113
+ # virtualized.
114
+ # if_rewriter: (IfRewriter) the SexpProcessor descendant that
115
+ # rewrites "if"s in methods (optional, the default is
116
+ # IfRewriter.new).
117
+ # and_rewriter: (AndRewriter) the SexpProcessor descendant that
118
+ # rewrites "and"s in methods (optional, the default is
119
+ # AndRewriter.new).
120
+ # or_rewriter: (OrRewriter) the SexpProcessor descendant that
121
+ # rewrites "or"s in methods (optional, the default is
122
+ # OrRewriter.new).
123
+ # sexp_processor: (SexpProcessor) the sexp_processor that can turn
124
+ # ParseTree results into sexps (optional, the default is
125
+ # SexpProcessor.new).
126
+ # sexp_stringifier: (SexpStringifier) an object that can turn sexps
127
+ # back into Ruby code (optional, the default is
128
+ # SexpStringifier.new).
129
+ # rewritten_keywords: (RewrittenKeywords) a repository for keyword
130
+ # replacement lambdas (optional, the default is
131
+ # REWRITTEN_KEYWORDS).
132
+ def initialize(input_hash)
133
+ @for_classes = input_hash[:for_classes] || []
134
+ @for_instances = input_hash[:for_instances] || []
135
+ @for_subclasses_of = input_hash[:for_subclasses_of] || []
136
+ @if_rewriter = input_hash[:if_rewriter] || IfRewriter.new
137
+ @and_rewriter = input_hash[:and_rewriter] || AndRewriter.new
138
+ @or_rewriter = input_hash[:or_rewriter] || OrRewriter.new
139
+ @sexp_processor = input_hash[:sexp_processor] || SexpProcessor.new
140
+ @sexp_stringifier = input_hash[:sexp_stringifier] || SexpStringifier.new
141
+ @rewritten_keywords =
142
+ input_hash[:rewritten_keywords] || REWRITTEN_KEYWORDS
143
+ end
144
+
145
+ # Helper method to rewrite code.
146
+ #
147
+ # Arguments:
148
+ # translated: (Array) the output of ParseTree.translate on the original
149
+ # code
150
+ # rewriter: (SexpProcessor) the object that will rewrite the sexp, to
151
+ # virtualize the keywords.
152
+ def rewritten_code(translated, rewriter)
153
+ sexp = @sexp_processor.process(
154
+ VirtualKeywords.deep_copy_array(translated))
155
+ new_code = @sexp_stringifier.stringify(
156
+ rewriter.process(sexp))
157
+ end
158
+
159
+ # Helper method to rewrite all methods of an object.
160
+ #
161
+ # Arguments:
162
+ # instance: (Object) the object whose methods will be rewritten.
163
+ # keyword: (Symbol) the keyword to virtualize.
164
+ # rewriter: (SexpProcessor) the object that will do the rewriting.
165
+ # block: (Proc) the lambda that will replace the keyword.
166
+ def rewrite_methods_of_instance(instance, keyword, rewriter, block)
167
+ @rewritten_keywords.register_lambda_for_object(instance, keyword, block)
168
+
169
+ methods = ClassReflection.instance_methods_of instance.class
170
+ methods.each do |name, translated|
171
+ new_code = rewritten_code(translated, rewriter)
172
+ ClassReflection.install_method_on_instance(instance, new_code)
173
+ end
174
+ end
175
+
176
+ # Helper method to rewrite all methods of objects from a class.
177
+ #
178
+ # Arguments:
179
+ # klass: (Class) the class whose methods will be rewritten.
180
+ # keyword: (Symbol) the keyword to virtualize.
181
+ # rewriter: (SexpProcessor) the object that will do the rewriting.
182
+ # block: (Proc) the lambda that will replace the keyword.
183
+ def rewrite_methods_of_class(klass, keyword, rewriter, block)
184
+ @rewritten_keywords.register_lambda_for_class(klass, keyword, block)
185
+
186
+ methods = ClassReflection.instance_methods_of klass
187
+ methods.each do |name, translated|
188
+ new_code = rewritten_code(translated, rewriter)
189
+ ClassReflection.install_method_on_class(klass, new_code)
190
+ end
191
+ end
192
+
193
+ # Helper method to virtualize a keyword (rewrite with the given block)
194
+ #
195
+ # Arguments:
196
+ # keyword: (Symbol) the keyword to virtualize.
197
+ # rewriter: (SexpProcessor) the object that will do the rewriting.
198
+ # block: (Proc) the lambda that will replace the keyword.
199
+ def virtualize_keyword(keyword, rewriter, block)
200
+ @for_instances.each do |instance|
201
+ rewrite_methods_of_instance(instance, keyword, rewriter, block)
202
+ end
203
+
204
+ @for_classes.each do |klass|
205
+ rewrite_methods_of_class(klass, keyword, rewriter, block)
206
+ end
207
+
208
+ subclasses = ClassReflection.subclasses_of_classes @for_subclasses_of
209
+ subclasses.each do |subclass|
210
+ rewrite_methods_of_class(subclass, keyword, rewriter, block)
211
+ end
212
+ end
213
+
214
+ # Rewrite "if" expressions.
215
+ #
216
+ # Arguments:
217
+ # &block: The block that will replace "if"s in the objects being
218
+ # virtualized
219
+ def virtual_if(&block)
220
+ virtualize_keyword(:if, @if_rewriter, block)
221
+ end
222
+
223
+ # Rewrite "and" expressions.
224
+ #
225
+ # Arguments:
226
+ # &block: The block that will replace "and"s in the objects being
227
+ # virtualized
228
+ def virtual_and(&block)
229
+ virtualize_keyword(:and, @and_rewriter, block)
230
+ end
231
+
232
+ # Rewrite "or" expressions.
233
+ #
234
+ # Arguments:
235
+ # &block: The block that will replace "or"s in the objects being
236
+ # virtualized
237
+ def virtual_or(&block)
238
+ virtualize_keyword(:or, @or_rewriter, block)
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,33 @@
1
+ require 'virtual_keywords/sexp_stringifier'
2
+ require 'virtual_keywords/class_mirrorer'
3
+ require 'virtual_keywords/virtualizer'
4
+ require 'virtual_keywords/keyword_rewriter'
5
+ require 'virtual_keywords/rewritten_keywords'
6
+
7
+ module VirtualKeywords
8
+ class Foo
9
+ def hi
10
+ if true
11
+ :hi
12
+ else
13
+ :bye
14
+ end
15
+ end
16
+ end
17
+
18
+ def self.sanity_test
19
+ virtualizer = Virtualizer.new(
20
+ :for_classes => [Foo]
21
+ )
22
+ virtualizer.virtual_if do |condition, then_do, else_do|
23
+ :pwned
24
+ end
25
+
26
+ foo = Foo.new
27
+ if foo.hi == :pwned
28
+ :success
29
+ else
30
+ :failure
31
+ end
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: virtual_keywords
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 0
10
+ version: 0.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Rahul Rajagopalan
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-21 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: sexp_processor
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: ParseTree
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: ruby2ruby
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ type: :runtime
75
+ version_requirements: *id004
76
+ description: Replace keyword implementations with your own functions, for DSLs
77
+ email: rahulrajago@gmail.com
78
+ executables: []
79
+
80
+ extensions: []
81
+
82
+ extra_rdoc_files: []
83
+
84
+ files:
85
+ - lib/virtual_keywords/class_mirrorer.rb
86
+ - lib/virtual_keywords/sexp_stringifier.rb
87
+ - lib/virtual_keywords/rewritten_keywords.rb
88
+ - lib/virtual_keywords/keyword_rewriter.rb
89
+ - lib/virtual_keywords/version.rb
90
+ - lib/virtual_keywords/virtualizer.rb
91
+ - lib/sexps/count_to_ten_sexp.txt
92
+ - lib/sexps/sexps_and.txt
93
+ - lib/sexps/sexps_symbolic_and.txt
94
+ - lib/sexps/sexps_rewritten_keywords.txt
95
+ - lib/sexps/sexps_greet.txt
96
+ - lib/virtual_keywords.rb
97
+ - lib/spec/virtualizer_spec.rb
98
+ - lib/spec/rewritten_keywords_spec.rb
99
+ - lib/spec/keyword_rewriter_spec.rb
100
+ - lib/spec/class_mirrorer_spec.rb
101
+ - lib/spec/spec_helper.rb
102
+ - lib/Rakefile
103
+ homepage: http://github.com/rahulraj/virtual_keywords
104
+ licenses: []
105
+
106
+ post_install_message:
107
+ rdoc_options: []
108
+
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ hash: 3
117
+ segments:
118
+ - 0
119
+ version: "0"
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ hash: 3
126
+ segments:
127
+ - 0
128
+ version: "0"
129
+ requirements: []
130
+
131
+ rubyforge_project:
132
+ rubygems_version: 1.8.23
133
+ signing_key:
134
+ specification_version: 3
135
+ summary: Virtualize keywords, like "if", "and", and "or"
136
+ test_files: []
137
+