borx 0.0.1.alpha1

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/lib/borx.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Borx
2
+
3
+ end
4
+
5
+ require 'borx/environment'
6
+ require 'borx/rewriter'
data/lib/borx/code.rb ADDED
@@ -0,0 +1,22 @@
1
+ # A simple class to tell the difference between rewritten and raw code.
2
+ class Borx::Code
3
+
4
+ attr :code
5
+
6
+ def initialize(code)
7
+ @code = code
8
+ end
9
+
10
+ def hash
11
+ code.hash
12
+ end
13
+
14
+ def eql?(other)
15
+ code.eql?(other.code)
16
+ end
17
+
18
+ def inspect
19
+ "#<Borx::Code #{code.inspect}>"
20
+ end
21
+
22
+ end
@@ -0,0 +1,108 @@
1
+ base = Class.new do
2
+
3
+ def get_constant(binding, name, house = nil)
4
+ raise Borx::Environment::NotImplemented::GetConstant
5
+ end
6
+
7
+ def set_constant(binding, name, house = nil, value)
8
+ raise Borx::Environment::NotImplemented::SetConstant
9
+ end
10
+
11
+ def call_method(binding, receiver, name, *args, &block)
12
+ raise Borx::Environment::NotImplemented::CallMethod
13
+ end
14
+
15
+ def get_variable(binding, name)
16
+ raise Borx::Environment::NotImplemented::GetVariable
17
+ end
18
+
19
+ def set_variable(binding, name, value)
20
+ raise Borx::Environment::NotImplemented::SetVariable
21
+ end
22
+
23
+ end
24
+
25
+ class Borx::Environment < base
26
+
27
+ Base = superclass
28
+
29
+ module GetVariable
30
+
31
+ def get_variable(binding, name)
32
+ binding.eval(name)
33
+ end
34
+
35
+ end
36
+
37
+ module SetVariable
38
+
39
+ def set_variable(binding, name, value)
40
+ binding.eval("#{name} = nil ; lambda{|_x| #{name} = _x}").call(value)
41
+ end
42
+
43
+ end
44
+
45
+ module GetSetVariable
46
+ include GetVariable
47
+ include SetVariable
48
+ end
49
+
50
+ module CallPublicMethod
51
+ def call_method(binding, receiver, name ,*args, &block)
52
+ receiver.__send__(name, *args, &block)
53
+ end
54
+ end
55
+
56
+ module CallPrivateMethod
57
+ def call_private_method(binding, receiver, name ,*args, &block)
58
+ receiver.__send__(name, *args, &block)
59
+ end
60
+ end
61
+
62
+ module CallMethod
63
+ include CallPublicMethod
64
+ include CallPrivateMethod
65
+ end
66
+
67
+ module GetConstant
68
+ def get_constant(binding, name, house = Object)
69
+ house.const_get(name)
70
+ end
71
+ end
72
+
73
+ module GetSetConstant
74
+ include GetConstant
75
+ end
76
+
77
+ include GetSetVariable
78
+ include GetSetConstant
79
+ include CallMethod
80
+
81
+ class NotImplemented < StandardError
82
+ class GetConstant < self
83
+ end
84
+ class SetConstant < self
85
+ end
86
+ class CallMethod < self
87
+ end
88
+ class GetVariable < self
89
+ end
90
+ class SetVariable < self
91
+ end
92
+ end
93
+
94
+ def eval(code, bin_ding = nil)
95
+ bin_ding ||= proc{}.binding
96
+ eval_code( Borx::Rewriter.rewrite(code), bin_ding)
97
+ end
98
+
99
+ private
100
+ def eval_code(code, binding, file = '(eval)', line = 0)
101
+ old_borx, setter = binding.eval('__borx__ ||= nil ; [__borx__, lambda{|v| __borx__ = v}]')
102
+ setter.call(self)
103
+ binding.eval(code.code, file, line)
104
+ ensure
105
+ setter.call(old_borx) if setter
106
+ end
107
+
108
+ end
@@ -0,0 +1,193 @@
1
+ begin
2
+ require 'ripper'
3
+ rescue NotFound
4
+ raise NotFound, "Ripper extension not found. Please add it to your bundle."
5
+ end
6
+ require 'sorcerer'
7
+ require 'borx/code'
8
+ class Borx::Rewriter < Ripper::SexpBuilder
9
+
10
+ # @api private
11
+ ARGS_NEW = [:args_new].freeze
12
+
13
+ def self.rewrite(code, options = {})
14
+ tree = self.new(code).parse
15
+ code = Sorcerer.source(tree, options)
16
+ return Borx::Code.new(code)
17
+ end
18
+
19
+ private
20
+
21
+ def on_top_const_ref(*args)
22
+ ref = super
23
+ return call_borx('get_constant',
24
+ [:args_add,
25
+ [:args_add, ARGS_NEW, ident_to_string(ref[1])],
26
+ [:@const, "Object", [0,0]]])
27
+ end
28
+
29
+
30
+ def on_const_path_ref(*args)
31
+ path = super
32
+ return call_borx('get_constant',[:args_add, [:args_add, ARGS_NEW, ident_to_string(path[2])], path[1]])
33
+ end
34
+
35
+ def on_method_add_arg(*x)
36
+ name, call, args, block = super
37
+ inject_binding = true
38
+ if call[0] == :fcall
39
+ args = args[1][1] unless args == ARGS_NEW
40
+ brgs = push_args(args,[:var_ref, [:@kw, "self", [0, 0]]],ident_to_string(call[1]))
41
+ return call_borx('call_private_method', brgs)
42
+ elsif call[0] == :method_add_arg
43
+ # this case doesn't show up in the wild
44
+ # it exists because of the way we rewrite calls
45
+ inject_binding = false
46
+ brgs = push_args(args[1][1], call[2][1][1])
47
+ else
48
+ # :nocov:
49
+ raise "Unknown call type #{call}. This is bug. Please report it"
50
+ # :nocov:
51
+ end
52
+ return call_borx('call_method', brgs, binding: inject_binding)
53
+ end
54
+
55
+ def on_call(*args)
56
+ call = super
57
+ return call_borx('call_method',
58
+ [:args_add,
59
+ [:args_add,[:args_new],call[1]],
60
+ ident_to_string(call[3])])
61
+ end
62
+
63
+ def on_vcall(vcall)
64
+ s = super
65
+ return call_borx('get_variable', [:args_add, [:args_new], ident_to_string(s[1])])
66
+ end
67
+
68
+ def on_var_ref(x)
69
+ var = super
70
+ fun = case(var[1][0])
71
+ when :@const then 'get_constant'
72
+ when :@gvar then 'get_global_variable'
73
+ when :@cvar then 'get_class_variable'
74
+ when :@ivar then 'get_instance_variable'
75
+ when :@ident then 'get_variable'
76
+ when :@kw then 'get_magic'
77
+ # :nocov:
78
+ else
79
+ raise "Unknown var_ref type in #{var[1]}, this is a bug, please report it"
80
+ # :nocov:
81
+ end
82
+ return call_borx(fun, [:args_add, [:args_new], ident_to_string(var[1])])
83
+ end
84
+
85
+ def on_assign(field, value)
86
+ assign, field, value = super
87
+ if field[0] == :var_field
88
+ fun = case(field[1][0])
89
+ when :@const then 'set_constant'
90
+ when :@gvar then 'set_global_variable'
91
+ when :@cvar then 'set_class_variable'
92
+ when :@ivar then 'set_instance_variable'
93
+ when :@ident then 'set_variable'
94
+ # :nocov:
95
+ else
96
+ raise "Unknown var_field type in #{var[1]}, this is a bug, please report it"
97
+ # :nocov:
98
+ end
99
+ return call_borx(fun,
100
+ [:args_add,
101
+ [:args_add, [:args_new],
102
+ ident_to_string(field[1])], value])
103
+ elsif field[0] == :field
104
+ return call_borx('call_method',
105
+ [:args_add,
106
+ [:args_add,
107
+ [:args_add,[:args_new],field[1]],
108
+ [:string_literal,[:string_add,[:string_content],[:@tstring_content,field[3][1]+"=",field[3][2]]] ]
109
+ ], value
110
+ ])
111
+ elsif field[0] == :const_path_field
112
+ return call_borx('set_constant',
113
+ [:args_add,
114
+ [:args_add,
115
+ [:args_add,[:args_new],ident_to_string(field[2])],
116
+ field[1]
117
+ ], value
118
+ ])
119
+ elsif field[0] == :top_const_field
120
+ return call_borx('set_constant',
121
+ [:args_add,
122
+ [:args_add,
123
+ [:args_add, [:args_new],
124
+ ident_to_string(field[1])],
125
+ [:@const, 'Object',[0,0]]
126
+ ], value])
127
+ end
128
+ # :nocov:
129
+ raise "Unknown assign type #{field}. This is a bug. Please report it"
130
+ # :nocov:
131
+ end
132
+
133
+ def on_xstring_literal(x)
134
+ x = super
135
+ return call_borx('execute', [:args_add, [:args_new], xstring_to_string(x)])
136
+ end
137
+
138
+ def on_stmts_new
139
+ return [:stmts_add, [:stmts_new], [:assign,
140
+ [:var_field, [:@ident, "__borx_binding__", [1, 0]]],
141
+ [:vcall, [:@ident, "binding", [1, 17]]]]]
142
+ end
143
+
144
+ def xstring_to_string(x)
145
+ if x[0] == :xstring_literal
146
+ return [:string_literal, xstring_to_string(x[1]), *x[2..-1]]
147
+ elsif x[0] == :xstring_add
148
+ return [:string_add, xstring_to_string(x[1]), *x[2..-1]]
149
+ elsif x[0] == :xstring_new
150
+ return [:string_content]
151
+ else
152
+ # :nocov:
153
+ raise "Unknown xstring type #{x}. This is a bug. Please report it."
154
+ # :nocov:
155
+ end
156
+ end
157
+
158
+ def call_borx(name,args, options = {})
159
+ args = push_args(args, binding_args) unless options[:binding] == false
160
+ return [:method_add_arg,
161
+ [:call, [:var_ref, [:@ident, "__borx__", [0, 0]]], :'.', [:@ident, name, [0,0]]],
162
+ [:arg_paren,
163
+ [:args_add_block,
164
+ args,
165
+ options.fetch(:block, false)
166
+ ]
167
+ ]
168
+ ]
169
+ end
170
+
171
+ def binding_args
172
+ return [:var_field, [:@ident, "__borx_binding__", [1, 0]]]
173
+ end
174
+
175
+ def ident_to_string(ident)
176
+ [:string_literal,
177
+ [:string_add,[:string_content],
178
+ [:@tstring_content, ident[1], ident[2]]]]
179
+ end
180
+
181
+ def push_args(tree, *args)
182
+ if tree == [:args_new]
183
+ if args.any?
184
+ return [:args_add, push_args(tree, *args[0..-2]), args.last]
185
+ else
186
+ return tree
187
+ end
188
+ else
189
+ return [:args_add, push_args(tree[1], *args), tree[2]]
190
+ end
191
+ end
192
+
193
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: borx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.alpha1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Hannes Georg
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sorcerer
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.3.10
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.3.10
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: simplecov
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: |
63
+ This plugin parses a piece of ruby code with ripper, rewrites it a bit and converts it back to ruby code.
64
+ This lets a central object decide what the code is actually doing.
65
+ email: hannes.georg@googlemail.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - lib/borx/environment.rb
71
+ - lib/borx/code.rb
72
+ - lib/borx/rewriter.rb
73
+ - lib/borx.rb
74
+ homepage: https://github.com/hannesg/borx
75
+ licenses: []
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - '>'
90
+ - !ruby/object:Gem::Version
91
+ version: 1.3.1
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 1.8.25
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: rewrites ruby code so you can decide what it actually does
98
+ test_files: []
99
+ has_rdoc: