virtual_keywords 0.3.0 → 0.3.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.
@@ -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: []