virtual_keywords 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ClassMirrorer' do
4
+ before :each do
5
+ @stub_parser = double 'parser'
6
+ @mirrorer = VirtualKeywords::ClassMirrorer.new :parser => @stub_parser
7
+ end
8
+
9
+ it 'mirrors given classes' do
10
+ @stub_parser.stub(:translate_instance_method).and_return :translated
11
+ result = @mirrorer.mirror Fizzbuzzer
12
+
13
+ class_and_method = VirtualKeywords::ClassAndMethodName.new(
14
+ Fizzbuzzer, :fizzbuzz)
15
+ result.keys.should include class_and_method
16
+ result[class_and_method].should eql :translated
17
+ end
18
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'micro SQL DSL rewriting' do
4
+ include TrackIfs, TrackAnds, TrackOrs, DoRewrite
5
+
6
+ before :each do
7
+ # Here, MicroSqlUser is the consumer of the DSL.
8
+ @user = MicroSqlUser.new
9
+
10
+ @methods = sexpify_instance_methods MicroSqlUser
11
+ @if_rewriter = VirtualKeywords::IfRewriter.new
12
+ @and_rewriter = VirtualKeywords::AndRewriter.new
13
+ @or_rewriter = VirtualKeywords::OrRewriter.new
14
+
15
+ @my_if_calls = 0
16
+ @my_and_calls = 0
17
+ @my_or_calls = 0
18
+
19
+ VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
20
+ @user, :if, my_if)
21
+ VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
22
+ @user, :and, my_and)
23
+ VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
24
+ @user, :or, my_or)
25
+ end
26
+
27
+ def rewriters
28
+ [@if_rewriter, @and_rewriter, @or_rewriter]
29
+ end
30
+
31
+ it 'rewrites postfix if in the SQL DSL' do
32
+ do_rewrite(:select_with_where, @user, verbose = false,
33
+ old_and_new_are_same = false)
34
+ @my_if_calls.should eql 1
35
+ end
36
+
37
+ it 'rewrites if with or in the SQL DSL' do
38
+ do_rewrite(:select_with_or, @user, verbose = false,
39
+ old_and_new_are_same = false)
40
+ @my_if_calls.should eql 1
41
+ @my_or_calls.should eql 1
42
+ end
43
+
44
+ it 'rewrites complex conditionals in the SQL DSL' do
45
+ do_rewrite(:select_complex, @user, verbose = false,
46
+ old_and_new_are_same = false)
47
+ @my_if_calls.should eql 1
48
+ @my_and_calls.should eql 1
49
+ @my_or_calls.should eql 1
50
+ end
51
+ end
52
+
53
+ describe 'micro SQL DSL virtualizing' do
54
+ before :each do
55
+ # Here, MicroSqlUser is the consumer of the DSL.
56
+ @user = MicroSqlUser.new
57
+ Sql.dslify @user
58
+ end
59
+
60
+ it 'generates basic select statements' do
61
+ result_sql = @user.simple_select
62
+ result_sql.should eql 'select (name,post_ids) from users'
63
+ end
64
+
65
+ it 'generates select-where statements' do
66
+ result_sql = @user.select_with_where
67
+
68
+ result_sql.should eql 'select (name,post_ids) from users where name="rahul"'
69
+ end
70
+
71
+ it 'generates select-where with "or" statements' do
72
+ result_sql = @user.select_with_or
73
+ result_sql.should eql 'select (name,post_ids) from users where ' +
74
+ 'name="rahul" or name="Rahul Rajagopalan"'
75
+ end
76
+ end
@@ -16,6 +16,7 @@ require 'virtualizees/operator_user'
16
16
  require 'virtualizees/while_user'
17
17
  require 'virtualizees/until_user'
18
18
  require 'virtualizees/case_when_user'
19
+ require 'virtualizees/micro_sql_dsl'
19
20
 
20
21
  require 'rspec'
21
22
 
@@ -120,10 +121,11 @@ module DoRewrite
120
121
  # Override this and return a list of rewriters, in order, so do_rewrite
121
122
  # can call them
122
123
  def rewriters
123
- raise Abstract
124
+ raise Abstract, 'Must provide rewriters!'
124
125
  end
125
126
 
126
- def do_rewrite(method_name, object, verbose = false)
127
+ def do_rewrite(method_name, object, verbose = false,
128
+ old_and_new_are_same = true)
127
129
  sexp = @methods[method_name]
128
130
  # Run all rewriters on the sexp
129
131
  result = rewriters.reduce(sexp) { |rewritee, rewriter|
@@ -141,11 +143,14 @@ module DoRewrite
141
143
  # old and new code should produce the same result,
142
144
  # except that @my_*_calls is incremented
143
145
  old_result = object.send method_name
144
- VirtualKeywords::ClassReflection.new.install_method_on_instance(
145
- object, code_result)
146
+ object.instance_eval code_result
147
+ #VirtualKeywords::ClassReflection.new.install_method_on_instance(
148
+ #object, code_result)
146
149
  new_result = object.send method_name
147
150
 
148
- new_result.should eql old_result
151
+ if old_and_new_are_same
152
+ new_result.should eql old_result
153
+ end
149
154
  end
150
155
  end
151
156
 
@@ -0,0 +1,110 @@
1
+ class Sql
2
+ def self.select(columns, options)
3
+ column_names = columns.join ','
4
+ table_name = options[:from]
5
+
6
+ "select (#{column_names}) from #{table_name}"
7
+ end
8
+
9
+ def self.dslify object
10
+ virtualizer = VirtualKeywords::Virtualizer.new :for_instances => [object]
11
+
12
+ virtualizer.virtual_if do |condition, then_do, else_do|
13
+ # In this DSL, all "if"s are postfix and have no else clause.
14
+ "#{then_do.call} where #{condition.call}"
15
+ end
16
+
17
+ virtualizer.virtual_or do |first, second|
18
+ "#{first.call} or #{second.call}"
19
+ end
20
+ end
21
+ end
22
+
23
+ class MicroSqlUser
24
+ # First version, just using built-in syntatic sugar: optional parentheses
25
+ # and braces around hashes.
26
+ def simple_select
27
+ # Limitation: the parser can't handle the new hash syntax
28
+ # (so here we use the old one)
29
+ Sql::select [:name, :post_ids], :from => :users
30
+ end
31
+
32
+ # Ok, now mix in some VirtualKeywords!
33
+ # Use postfix "if" to stand in for "where" clauses. It should look
34
+ # gramatically similar to SQL, just with "if" instead of "where".
35
+ #
36
+ # Calling virtual_if should turn this method into:
37
+ #
38
+ # name_is_rahul = 'name="rahul"'
39
+ # VirtualKeywords::REWRITTEN_KEYWORDS.call_if(
40
+ # self,
41
+ # lambda { Sql::select [:name, :post_ids], :from=> :users },
42
+ # lambda { name_is_rahul },
43
+ # lambda {}
44
+ # )
45
+ def select_with_where
46
+ # Limitation: 'name="rahul"' is in quotes (we can't actually get
47
+ # AST nodes because virtual_keywords hides them.)
48
+
49
+ # Using the string literal directly is valid Ruby, but produces
50
+ # an annoying warning.
51
+ name_is_rahul = 'name="rahul"'
52
+ Sql::select [:name, :post_ids], :from => :users if name_is_rahul
53
+ end
54
+
55
+ # Should turn into:
56
+ # def select_with_or
57
+ # name_is_rahul = "name=\"rahul\""
58
+ # is_full_name = "name=\"Rahul Rajagopalan\""
59
+ # VirtualKeywords::REWRITTEN_KEYWORDS.call_if(
60
+ # self,
61
+ # lambda do
62
+ # VirtualKeywords::REWRITTEN_KEYWORDS.call_or(
63
+ # self,
64
+ # lambda { name_is_rahul },
65
+ # lambda { is_full_name }
66
+ # )
67
+ # end,
68
+ # lambda { Sql.select([:name, :post_ids], :from => :users) },
69
+ # lambda { }
70
+ # )
71
+ # end
72
+ def select_with_or
73
+ name_is_rahul = 'name="rahul"'
74
+ is_full_name = 'name="Rahul Rajagopalan"'
75
+ Sql::select [:name, :post_ids], :from => :users if
76
+ name_is_rahul or is_full_name
77
+ end
78
+
79
+ # Should turn into:
80
+ # def select_complex
81
+ # name_is_rahul = "name=\"rahul\""
82
+ # right_id = "id=5"
83
+ # is_full_name = "name=\"Rahul Rajagopalan\""
84
+ # VirtualKeywords::REWRITTEN_KEYWORDS.call_if(
85
+ # self,
86
+ # lambda do
87
+ # VirtualKeywords::REWRITTEN_KEYWORDS.call_or(
88
+ # self,
89
+ # lambda do
90
+ # VirtualKeywords::REWRITTEN_KEYWORDS.call_and(
91
+ # self,
92
+ # lambda { name_is_rahul },
93
+ # lambda { right_id }
94
+ # )
95
+ # end,
96
+ # lambda { is_full_name }
97
+ # )
98
+ # end,
99
+ # lambda { Sql.select([:name, :post_ids], :from => :users) },
100
+ # lambda { }
101
+ # )
102
+ # end
103
+ def select_complex
104
+ name_is_rahul = 'name="rahul"'
105
+ right_id = 'id=5'
106
+ is_full_name = 'name="Rahul Rajagopalan"'
107
+ Sql::select [:name, :post_ids], :from => :users if
108
+ name_is_rahul and right_id or is_full_name
109
+ end
110
+ end
@@ -164,4 +164,20 @@ describe 'Virtualizer' do
164
164
  result = @inverter.run true
165
165
  result.should be_true
166
166
  end
167
+
168
+ it 'virtualizes multiple keywords in the same method' do
169
+ if_calls = 0
170
+ and_calls = 0
171
+ @virtualizer.virtual_if do |condition, then_do, else_do|
172
+ if_calls += 1
173
+ if condition.call then then_do.call else else_do.call end
174
+ end
175
+ @virtualizer.virtual_and do |first, second|
176
+ and_calls += 1
177
+ first.call and second.call
178
+ end
179
+ @my_class.foo
180
+ if_calls.should eql 1
181
+ and_calls.should eql 1
182
+ end
167
183
  end
@@ -12,6 +12,7 @@ require 'ruby2ruby'
12
12
 
13
13
  if RUBY_VERSION.start_with? '1.8'
14
14
  require 'virtual_keywords/deep_copy_array'
15
+ require 'virtual_keywords/class_mirrorer'
15
16
  require 'virtual_keywords/parser_strategy'
16
17
  require 'virtual_keywords/sexp_stringifier'
17
18
  require 'virtual_keywords/class_reflection'
@@ -20,6 +21,7 @@ if RUBY_VERSION.start_with? '1.8'
20
21
  require 'virtual_keywords/rewritten_keywords'
21
22
  else
22
23
  require_relative 'virtual_keywords/deep_copy_array'
24
+ require_relative 'virtual_keywords/class_mirrorer'
23
25
  require_relative 'virtual_keywords/parser_strategy'
24
26
  require_relative 'virtual_keywords/sexp_stringifier'
25
27
  require_relative 'virtual_keywords/class_reflection'
@@ -0,0 +1,37 @@
1
+ module VirtualKeywords
2
+ # Simple data object holding a Class and the name of one of its methods
3
+ ClassAndMethodName = Struct.new(:klass, :method_name)
4
+
5
+ # ClassMirrorer that uses ParseTree (Ruby 1.8)
6
+ class ClassMirrorer
7
+ # Initialize a ParseTreeClassMirrorer
8
+ #
9
+ # Arguments:
10
+ # An options Hash with the following key
11
+ # parser: (ParserStrategy) an object with a method translate, that takes
12
+ # a class and method name, and returns a syntax tree that can be
13
+ # sexpified (optional, the default is ParserStrategy.new)
14
+ def initialize options
15
+ @parser = options[:parser] || ParserStrategy.new
16
+ end
17
+
18
+ # Map ClassAndMethodNames to sexps
19
+ #
20
+ # Arguments:
21
+ # klass: (Class) the class to mirror.
22
+ #
23
+ # Returns:
24
+ # (Hash[ClassAndMethodName, Sexp]) a hash mapping every method of the
25
+ # class to parsed output.
26
+ def mirror klass
27
+ methods = {}
28
+ klass.instance_methods(false).each do |method_name|
29
+ key = ClassAndMethodName.new(klass, method_name)
30
+ translated = @parser.translate_instance_method(klass, method_name)
31
+ methods[key] = translated
32
+ end
33
+
34
+ methods
35
+ end
36
+ end
37
+ end
@@ -2,6 +2,10 @@ module VirtualKeywords
2
2
  # Object used to inspect the class hierarchy, and to view
3
3
  # and modify methods of classes.
4
4
  class ClassReflection
5
+ # Create a new ClassReflection
6
+ # Arguments:
7
+ # parser_strategy: (ParserStrategy) the strategy object to use to parse
8
+ # methods. (Optional, the default value is ParserStrategy.new)
5
9
  def initialize(parser_strategy = ParserStrategy.new)
6
10
  @parser_strategy = parser_strategy
7
11
  end
@@ -27,7 +31,7 @@ module VirtualKeywords
27
31
  #
28
32
  # Returns:
29
33
  # (Array) All classes that are subclasses of one of the classes in klasses,
30
- # in a flattened array.
34
+ # in a flattened array.
31
35
  def subclasses_of_classes(klasses)
32
36
  klasses.map { |klass|
33
37
  subclasses_of_class klass
@@ -114,12 +114,20 @@ module VirtualKeywords
114
114
  end
115
115
  end
116
116
 
117
+ # SexpProcessor subclass that rewrites "not" expressions.
117
118
  class NotRewriter < SexpProcessor
118
119
  def initialize
119
120
  super
120
121
  self.strict = false
121
122
  end
122
123
 
124
+ # Rewrite "not" expressions (automatically called by SexpProcessor#process)
125
+ #
126
+ # Arguments:
127
+ # expression: (Sexp) the :not sexp to rewrite.
128
+ #
129
+ # Returns:
130
+ # (Sexp): a sexp that instead calls REWRITTEN_KEYWORDS.call_not
123
131
  def rewrite_not(expression)
124
132
  value = expression[1]
125
133
 
@@ -136,15 +144,24 @@ module VirtualKeywords
136
144
  end
137
145
  end
138
146
 
147
+ # Raised if a rewriter encounters an unexpected sexp, indicating a bug.
139
148
  class UnexpectedSexp < StandardError
140
149
  end
141
150
 
151
+ # SexpProcessor subclass that rewrites "while" expressions.
142
152
  class WhileRewriter < SexpProcessor
143
153
  def initialize
144
154
  super
145
155
  self.strict = false
146
156
  end
147
157
 
158
+ # Rewrite "while" expressions (automatically called by SexpProcessor#process)
159
+ #
160
+ # Arguments:
161
+ # expression: (Sexp) the :while sexp to rewrite.
162
+ #
163
+ # Returns:
164
+ # (Sexp): a sexp that instead calls REWRITTEN_KEYWORDS.call_while
148
165
  def rewrite_while(expression)
149
166
  condition = expression[1]
150
167
  body = expression[2]
@@ -171,12 +188,20 @@ module VirtualKeywords
171
188
  end
172
189
  end
173
190
 
191
+ # SexpProcessor subclass that rewrites "until" expressions.
174
192
  class UntilRewriter < SexpProcessor
175
193
  def initialize
176
194
  super
177
195
  self.strict = false
178
196
  end
179
197
 
198
+ # Rewrite "until" expressions (automatically called by SexpProcessor#process)
199
+ #
200
+ # Arguments:
201
+ # expression: (Sexp) the :until sexp to rewrite.
202
+ #
203
+ # Returns:
204
+ # (Sexp): a sexp that instead calls REWRITTEN_KEYWORDS.call_until
180
205
  def rewrite_until(expression)
181
206
  condition = expression[1]
182
207
  body = expression[2]
@@ -10,6 +10,7 @@ module VirtualKeywords
10
10
  end
11
11
  end
12
12
  end
13
+
13
14
  # One problem that needs to be solved is that of converting source code form
14
15
  # files into sexps.
15
16
 
@@ -17,22 +18,48 @@ module VirtualKeywords
17
18
  # In Ruby 1.9, we use RubyParser.
18
19
  # Note that neither set of libraries seems to work in the other version.
19
20
  # Use the strategy pattern, and initialize whichever class is appropriate.
21
+
22
+ # Parser strategy that uses ParseTree
20
23
  class ParseTreeStrategy
24
+ # Initialize a ParseTreeStrategy
25
+ #
26
+ # Arguments:
27
+ # parse_tree: (Class) an instance of the ParseTree singleton, that
28
+ # translates code into ParseTree output.
29
+ # sexp_processor: (SexpProcessor) an object that translates ParseTree
30
+ # output into rewriteable sexps.
21
31
  def initialize(parse_tree, sexp_processor)
22
32
  @parse_tree = parse_tree
23
33
  @sexp_processor = sexp_processor
24
34
  end
25
35
 
36
+ # Translate an instance method of a class.
37
+ #
38
+ # Arguments:
39
+ # klass: (Class) the class.
40
+ # method_name: (String) the name of the method to translate.
41
+ #
42
+ # Returns:
43
+ # (Sexp) the method, turned into a sexp.
26
44
  def translate_instance_method(klass, method_name)
27
45
  @sexp_processor.process(@parse_tree.translate(klass, method_name))
28
46
  end
29
47
  end
30
48
 
49
+ # Parser strategy that uses ruby_parser
31
50
  class RubyParserStrategy
32
51
  def initialize ruby_parser
33
52
  @ruby_parser = ruby_parser
34
53
  end
35
54
 
55
+ # Translate an instance method of a class.
56
+ #
57
+ # Arguments:
58
+ # klass: (Class) the class.
59
+ # method_name: (String) the name of the method to translate.
60
+ #
61
+ # Returns:
62
+ # (Sexp) the method, turned into a sexp.
36
63
  def translate_instance_method(klass, method_name)
37
64
  @ruby_parser.parse(klass.instance_method(method_name).source)
38
65
  end
@@ -130,7 +130,7 @@ module VirtualKeywords
130
130
  or_lambda.call(first, second)
131
131
  end
132
132
 
133
- # Call a "while" virtual block in place of an "while" expression.
133
+ # Call a "while" virtual block in place of a "while" expression.
134
134
  #
135
135
  # Arguments:
136
136
  # caller_object: (Object) the object whose method this is being called in.
@@ -145,16 +145,35 @@ module VirtualKeywords
145
145
  while_lambda.call(condition, body)
146
146
  end
147
147
 
148
+ # Call an "until" virtual block in place of an "until" expression.
148
149
  # Unlike unless, until IS a node in the AST
149
150
  # (it doesn't turn into while not)
150
151
  # For now, I'm passing this inconsistency through to the client.
151
152
  # A later modification of this gem may fold while and until into one thing
152
153
  # for consistency.
154
+ #
155
+ # Arguments:
156
+ # caller_object: (Object) the object whose method this is being called in.
157
+ # condition: (Proc) The condition of the until expression.
158
+ # body: (Proc) The body of the until expression (which is normally
159
+ # executed repeatedly)
160
+ #
161
+ # Raises:
162
+ # RewriteLambdaNotProvided if no "until" lambda is available.
153
163
  def call_until(caller_object, condition, body)
154
164
  until_lambda = lambda_or_raise(caller_object, :until)
155
165
  until_lambda.call(condition, body)
156
166
  end
157
167
 
168
+ # Call a "not" virtual block in place of a "not" expression.
169
+ #
170
+ # Arguments:
171
+ # caller_object: (Object) the object whose method this is being called in.
172
+ # value: (Proc) The operand of the not operator, which would normally be
173
+ # inverted.
174
+ #
175
+ # Raises:
176
+ # RewriteLambdaNotProvided if no "not" lambda is available.
158
177
  def call_not(caller_object, value)
159
178
  not_lambda = lambda_or_raise(caller_object, :not)
160
179
  not_lambda.call value
@@ -13,6 +13,7 @@ module VirtualKeywords
13
13
  end
14
14
 
15
15
  # Turn a sexp into a string of Ruby code.
16
+ # Makes a copy first so the inputted sexp is not emptied.
16
17
  #
17
18
  # Arguments:
18
19
  # sexp: (Sexp) the sexp to be stringified.
@@ -20,7 +21,8 @@ module VirtualKeywords
20
21
  # Returns:
21
22
  # (String) Ruby code equivalent to the sexp.
22
23
  def stringify sexp
23
- @ruby2ruby.process(@unifier.process(sexp))
24
+ sexp_copy = VirtualKeywords.deep_copy_array sexp
25
+ @ruby2ruby.process(@unifier.process(sexp_copy))
24
26
  end
25
27
  end
26
28
  end
@@ -1,3 +1,3 @@
1
1
  module VirtualKeywords
2
- VERSION = '0.3.0'
2
+ VERSION = '0.3.1'
3
3
  end
@@ -1,11 +1,18 @@
1
1
  # Parent module containing all variables defined as part of virtual_keywords
2
2
  module VirtualKeywords
3
+
4
+ class NoSuchInstance < StandardError
5
+ end
6
+
7
+ class NoSuchClass < StandardError
8
+ end
9
+
3
10
  # Object that virtualizes keywords.
4
11
  class Virtualizer
5
12
  # Initialize a Virtualizer
6
13
  #
7
14
  # Arguments:
8
- # A Hash with the following arguments (all optional):
15
+ # A Hash with the following key-value pairs (all optional):
9
16
  # for_classes: (Array[Class]) an array of classes. All methods of objects
10
17
  # created from the given classes will be virtualized (optional, the
11
18
  # default is an empty Array).
@@ -54,6 +61,34 @@ module VirtualKeywords
54
61
  @rewritten_keywords =
55
62
  input_hash[:rewritten_keywords] || REWRITTEN_KEYWORDS
56
63
  @class_reflection = input_hash[:class_reflection] || ClassReflection.new
64
+ @class_mirrorer = input_hash[:class_mirrorer] || ClassMirrorer.new({})
65
+
66
+ @sexps_for_classes = {}
67
+ @sexps_for_instances = {}
68
+ # TODO Refactor out work in the constructor
69
+ # and maybe move the sexp storage elsewhere.
70
+ parse_all_the_classes
71
+ end
72
+
73
+ def parse_all_the_classes
74
+ # Well, not _all_, just the ones provided in the constructor..
75
+ @for_classes.each do |klass|
76
+ @sexps_for_classes[klass] = @class_mirrorer.mirror klass
77
+ end
78
+ @for_instances.each do |instance|
79
+ @sexps_for_instances[instance] = @class_mirrorer.mirror instance.class
80
+ end
81
+ @for_subclasses_of.each do |klass|
82
+ subclasses = @class_reflection.subclasses_of_class klass
83
+ subclasses.each do |subclass|
84
+ @sexps_for_classes[subclass] = @class_mirrorer.mirror subclass
85
+ end
86
+ end
87
+ end
88
+
89
+ def rewritten_sexp(sexp, rewriter)
90
+ sexp_copy = VirtualKeywords.deep_copy_array sexp
91
+ rewriter.process sexp_copy
57
92
  end
58
93
 
59
94
  # Helper method to rewrite code.
@@ -78,11 +113,20 @@ module VirtualKeywords
78
113
  def rewrite_all_methods_of_instance(instance, keyword, rewriter, block)
79
114
  @rewritten_keywords.register_lambda_for_object(instance, keyword, block)
80
115
 
81
- methods = @class_reflection.instance_methods_of instance.class
82
- methods.each do |name, sexp|
83
- new_code = rewritten_code(sexp, rewriter)
116
+ if not @sexps_for_instances.include? instance
117
+ raise NoSuchInstance, "Tried to rewrite methods of #{instance}," +
118
+ "but it's not there!"
119
+ end
120
+ mirror_hash = @sexps_for_instances[instance]
121
+ new_mirror_hash = {}
122
+ mirror_hash.each do |class_and_method_name, sexp|
123
+ new_sexp = rewritten_sexp(sexp, rewriter)
124
+ new_code = @sexp_stringifier.stringify new_sexp
84
125
  @class_reflection.install_method_on_instance(instance, new_code)
126
+ # Save the modified sexp for the next go
127
+ new_mirror_hash[class_and_method_name] = new_sexp
85
128
  end
129
+ @sexps_for_instances[instance] = new_mirror_hash
86
130
  end
87
131
 
88
132
  # Helper method to rewrite all methods of objects from a class.
@@ -95,11 +139,19 @@ module VirtualKeywords
95
139
  def rewrite_all_methods_of_class(klass, keyword, rewriter, block)
96
140
  @rewritten_keywords.register_lambda_for_class(klass, keyword, block)
97
141
 
98
- methods = @class_reflection.instance_methods_of klass
99
- methods.each do |name, sexp|
100
- new_code = rewritten_code(sexp, rewriter)
142
+ if not @sexps_for_classes.include? klass
143
+ raise NoSuchInstance, "Tried to rewrite methods of #{klass}," +
144
+ "but it's not there!"
145
+ end
146
+ mirror_hash = @sexps_for_classes[klass]
147
+ new_mirror_hash = {}
148
+ mirror_hash.each do |class_and_method_name, sexp|
149
+ new_sexp = rewritten_sexp(sexp, rewriter)
150
+ new_code = @sexp_stringifier.stringify new_sexp
101
151
  @class_reflection.install_method_on_class(klass, new_code)
152
+ new_mirror_hash[class_and_method_name] = new_sexp
102
153
  end
154
+ @sexps_for_classes[klass] = new_mirror_hash
103
155
  end
104
156
 
105
157
  # Helper method to virtualize a keyword (rewrite with the given block)
@@ -150,6 +202,15 @@ module VirtualKeywords
150
202
  virtualize_keyword(:or, @or_rewriter, block)
151
203
  end
152
204
 
205
+ # Rewrite "not" expressions.
206
+ #
207
+ # Arguments:
208
+ # &block: The block that will replace "not"s in the objects being
209
+ # virtualized
210
+ def virtual_not(&block)
211
+ virtualize_keyword(:not, @not_rewriter, block)
212
+ end
213
+
153
214
  # Rewrite "while" expressions.
154
215
  #
155
216
  # Arguments:
@@ -159,12 +220,13 @@ module VirtualKeywords
159
220
  virtualize_keyword(:while, @while_rewriter, block)
160
221
  end
161
222
 
223
+ # Rewrite "until" expressions.
224
+ #
225
+ # Arguments:
226
+ # &block: The block that will replace "until"s in the objects being
227
+ # virtualized
162
228
  def virtual_until(&block)
163
229
  virtualize_keyword(:until, @until_rewriter, block)
164
230
  end
165
-
166
- def virtual_not(&block)
167
- virtualize_keyword(:not, @not_rewriter, block)
168
- end
169
231
  end
170
232
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: virtual_keywords
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-04-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &4909200 !ruby/object:Gem::Requirement
16
+ requirement: &10217980 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *4909200
24
+ version_requirements: *10217980
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sexp_processor
27
- requirement: &4908420 !ruby/object:Gem::Requirement
27
+ requirement: &10216740 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *4908420
35
+ version_requirements: *10216740
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: ParseTree
38
- requirement: &4907600 !ruby/object:Gem::Requirement
38
+ requirement: &10216300 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *4907600
46
+ version_requirements: *10216300
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: ruby2ruby
49
- requirement: &4906840 !ruby/object:Gem::Requirement
49
+ requirement: &10215780 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *4906840
57
+ version_requirements: *10215780
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: method_source
60
- requirement: &4906180 !ruby/object:Gem::Requirement
60
+ requirement: &10215360 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *4906180
68
+ version_requirements: *10215360
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: ruby_parser
71
- requirement: &5415440 !ruby/object:Gem::Requirement
71
+ requirement: &10214920 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,13 +76,14 @@ dependencies:
76
76
  version: '0'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *5415440
79
+ version_requirements: *10214920
80
80
  description: Replace keyword implementations with your own functions, for DSLs
81
81
  email: rahulrajago@gmail.com
82
82
  executables: []
83
83
  extensions: []
84
84
  extra_rdoc_files: []
85
85
  files:
86
+ - lib/virtual_keywords/class_mirrorer.rb
86
87
  - lib/virtual_keywords/class_reflection.rb
87
88
  - lib/virtual_keywords/sexp_stringifier.rb
88
89
  - lib/virtual_keywords/rewritten_keywords.rb
@@ -137,11 +138,14 @@ files:
137
138
  - lib/spec/virtualizees/until_user.rb
138
139
  - lib/spec/virtualizees/not_user.rb
139
140
  - lib/spec/virtualizees/greeter.rb
141
+ - lib/spec/virtualizees/micro_sql_dsl.rb
140
142
  - lib/spec/not_rewriter_spec.rb
141
143
  - lib/spec/keyword_rewriter_spec.rb
144
+ - lib/spec/class_mirrorer_spec.rb
142
145
  - lib/spec/spec_helper.rb
143
146
  - lib/spec/class_reflection_spec.rb
144
147
  - lib/spec/or_rewriter_spec.rb
148
+ - lib/spec/micro_sql_dsl_spec.rb
145
149
  - lib/Rakefile
146
150
  homepage: http://github.com/rahulraj/virtual_keywords
147
151
  licenses: []