capataz 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +89 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/capataz.gemspec +25 -0
- data/lib/capataz.rb +207 -0
- data/lib/capataz/core_ext.rb +24 -0
- data/lib/capataz/proxy.rb +76 -0
- data/lib/capataz/rewriter.rb +165 -0
- data/lib/capataz/source_rewriter.rb +18 -0
- data/lib/capataz/version.rb +3 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0c29c01bcf811102a7b55b2cb078411516f6e5ae
|
4
|
+
data.tar.gz: 366911255ca5df4e56264b9d5855bf81a470fd38
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b24a102e5f3cca7f062b3f963a58fefbb0df89ff9c3140f3410b8b6f3475e21671a3326b1087642169e8289abc7916a3cf9e1d4114e1528d921dad6a9e207992
|
7
|
+
data.tar.gz: 287a8b838427b32b8cd829865609b37c2a2e988762ce4e69856bbe78f6c87832cc4c038c408198303a8a7ca08afb5239541de172888475ed64b202b9ba6393f6
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Maikel Arcia
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Capataz
|
2
|
+
|
3
|
+
Provides Ruby code execution control by defining rules for syntax and runtime behavior.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'capataz'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install capataz
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Take for example the following Ruby code:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
class A
|
27
|
+
attr_reader :value, :secret_value
|
28
|
+
def initialize
|
29
|
+
@value = rand(10)
|
30
|
+
@secret_value = rand(value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class B < A; end
|
35
|
+
```
|
36
|
+
|
37
|
+
So you can configure your code syntax and runtime rules
|
38
|
+
```ruby
|
39
|
+
Capataz.config do
|
40
|
+
|
41
|
+
deny_declarations_of :module, :class
|
42
|
+
|
43
|
+
deny_invoke_of :constantize
|
44
|
+
|
45
|
+
allowed_constants A
|
46
|
+
|
47
|
+
deny_for A, :secret_value
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
And then you can control your Ruby code execution by evaluating it with `Capataz`
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
Capataz.eval <<-RUBY
|
55
|
+
class C
|
56
|
+
end
|
57
|
+
RUBY #ERROR: can not define classes
|
58
|
+
|
59
|
+
Capataz.eval <<-RUBY
|
60
|
+
B.new
|
61
|
+
RUBY #ERROR: Illegal access to constant B
|
62
|
+
|
63
|
+
Capataz.eval <<-RUBY
|
64
|
+
'B'.constantize.new
|
65
|
+
RUBY #ERROR: invoking method constantize is not allowed
|
66
|
+
|
67
|
+
Capataz.eval <<-RUBY
|
68
|
+
A.new.value
|
69
|
+
RUBY # 5 (a random value)
|
70
|
+
|
71
|
+
Capataz.eval <<-RUBY
|
72
|
+
A.new.secret_value
|
73
|
+
RUBY #ERROR: undefined method secret_value for #<A:0x000000022e7960>
|
74
|
+
```
|
75
|
+
|
76
|
+
|
77
|
+
## Development
|
78
|
+
|
79
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
80
|
+
|
81
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
1. Fork it ( https://github.com/macarci/capataz/fork )
|
86
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
87
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
88
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
89
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'capataz'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/capataz.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'capataz/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'capataz'
|
8
|
+
spec.version = Capataz::VERSION
|
9
|
+
spec.authors = ['Maikel Arcia']
|
10
|
+
spec.email = ['macarci@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = %q{Provides Ruby code execution control by defining rules for syntax and runtime behavior.}
|
13
|
+
spec.homepage = 'https://github.com/macarci/capataz'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = 'exe'
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.8'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'parser'
|
25
|
+
end
|
data/lib/capataz.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'capataz/version'
|
2
|
+
require 'parser/current'
|
3
|
+
require 'capataz/core_ext'
|
4
|
+
require 'capataz/proxy'
|
5
|
+
require 'capataz/rewriter'
|
6
|
+
|
7
|
+
module Capataz
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def eval(string, *binding_filename_lineno)
|
12
|
+
string = rewrite(string)
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def disable(*args)
|
17
|
+
@disable = args.first.to_s.to_i.to_b
|
18
|
+
end
|
19
|
+
|
20
|
+
def disable?
|
21
|
+
@disable
|
22
|
+
end
|
23
|
+
|
24
|
+
def deny_declarations_of(*symbols)
|
25
|
+
symbol_array_store(:denied_declarations, symbols)
|
26
|
+
end
|
27
|
+
|
28
|
+
def deny_invoke_of(*methods)
|
29
|
+
symbol_array_store(:denied_methods, methods)
|
30
|
+
end
|
31
|
+
|
32
|
+
def allowed_constants(*constants)
|
33
|
+
set = @config[:allowed_constants]
|
34
|
+
constants = [constants] unless constants.is_a?(Enumerable)
|
35
|
+
constants << Capataz unless constants.include?(Capataz)
|
36
|
+
constants.each { |constant| set << constant }
|
37
|
+
end
|
38
|
+
|
39
|
+
def allow_on(objs, *methods)
|
40
|
+
store_options(:instances, :allow, objs, methods)
|
41
|
+
end
|
42
|
+
|
43
|
+
def deny_on(objs, *methods)
|
44
|
+
store_options(:instances, :deny, objs, methods)
|
45
|
+
end
|
46
|
+
|
47
|
+
def allow_for(types, *options)
|
48
|
+
store_options(:modules, :allow, types, options) { |type| type.is_a?(Module) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def deny_for(types, *options)
|
52
|
+
store_options(:modules, :deny, types, options) { |type| type.is_a?(Module) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def config(&block)
|
56
|
+
@config ||=
|
57
|
+
{
|
58
|
+
denied_declarations: Set.new,
|
59
|
+
allowed_constants: Set.new,
|
60
|
+
denied_methods: Set.new,
|
61
|
+
instances: {},
|
62
|
+
modules: {}
|
63
|
+
}
|
64
|
+
class_eval(&block) if block
|
65
|
+
@config
|
66
|
+
end
|
67
|
+
|
68
|
+
def instance_response_to?(instance, *args)
|
69
|
+
fail ArgumentError if args.length == 0
|
70
|
+
method = args[0].is_a?(Symbol) ? args[0] : args[0].to_s.to_sym
|
71
|
+
return false if @config[:denied_methods].include?(method)
|
72
|
+
if (options = @config[:instances][instance])
|
73
|
+
return false unless allowed_method?(options, instance, method)
|
74
|
+
else
|
75
|
+
@config[:modules].each do |type, opts|
|
76
|
+
if instance.is_a?(type)
|
77
|
+
return false unless allowed_method?(opts, instance, method)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
instance.respond_to?(*args)
|
82
|
+
end
|
83
|
+
|
84
|
+
def allows_invocation_of(method)
|
85
|
+
method = method.to_s.to_sym unless method.is_a?(Symbol)
|
86
|
+
return false if @config[:denied_methods].include?(method)
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
def allowed_constant?(const)
|
91
|
+
(set = @config[:allowed_constants]) && set.include?(const)
|
92
|
+
end
|
93
|
+
|
94
|
+
def can_declare?(symbol)
|
95
|
+
(set = @config[:denied_declarations]).empty? || !set.include?(symbol)
|
96
|
+
end
|
97
|
+
|
98
|
+
def validate(code)
|
99
|
+
errors = []
|
100
|
+
begin
|
101
|
+
buffer = Parser::Source::Buffer.new('code')
|
102
|
+
buffer.source = code
|
103
|
+
Capataz::Rewriter.new(errors: errors).rewrite(buffer, Parser::CurrentRuby.new.parse(buffer))
|
104
|
+
rescue => ex
|
105
|
+
errors << ex.message
|
106
|
+
end
|
107
|
+
errors
|
108
|
+
end
|
109
|
+
|
110
|
+
def rewrite(code, options = {})
|
111
|
+
return code if Capataz.disable?
|
112
|
+
options ||= {}
|
113
|
+
options[:halt_on_error] = true if options[:halt_on_error].nil?
|
114
|
+
if (locals = options[:locals])
|
115
|
+
locals = [locals] unless locals.is_a?(Enumerable)
|
116
|
+
locals.each { |local| code = "#{local} ||= nil\r\n" + code }
|
117
|
+
end
|
118
|
+
buffer = Parser::Source::Buffer.new('code')
|
119
|
+
buffer.source = code
|
120
|
+
begin
|
121
|
+
Capataz::Rewriter.new(options).rewrite(buffer, Parser::CurrentRuby.new.parse(buffer))
|
122
|
+
rescue Exception => ex
|
123
|
+
if (logs = options[:logs]).is_a?(Hash) &&
|
124
|
+
(errors = (logs[:errors] ||= [])).is_a?(Array)
|
125
|
+
errors << "syntax error: #{ex.message}"
|
126
|
+
else
|
127
|
+
raise ex
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def handle(obj, options = {})
|
133
|
+
if obj.capataz_proxy? || [NilClass, Fixnum, Symbol, String, TrueClass, FalseClass].any? { |type| obj.is_a?(type) }
|
134
|
+
obj
|
135
|
+
elsif obj.is_a?(Hash)
|
136
|
+
Capataz::HashProxy.new(obj)
|
137
|
+
else
|
138
|
+
Capataz::Proxy.new(obj, options)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def allow_method_overrides
|
145
|
+
true #TODO Setup on config
|
146
|
+
end
|
147
|
+
|
148
|
+
def allowed_method?(options, instance, method)
|
149
|
+
if (allow = options[:allow])
|
150
|
+
if allow.is_a?(Proc)
|
151
|
+
return false unless allow.call(instance, method)
|
152
|
+
else
|
153
|
+
return false unless allow.include?(method)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
if (deny = options[:deny])
|
157
|
+
if deny.is_a?(Proc)
|
158
|
+
return false if deny.call(instance, method)
|
159
|
+
else
|
160
|
+
return false if deny.include?(method)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
true
|
164
|
+
end
|
165
|
+
|
166
|
+
def symbol_array_store(key, symbols)
|
167
|
+
set = @config[key]
|
168
|
+
symbols = [symbols] unless symbols.is_a?(Enumerable)
|
169
|
+
symbols.each { |symbol| set << (symbol.is_a?(Symbol) ? symbol : symbol.to_s.to_sym) }
|
170
|
+
end
|
171
|
+
|
172
|
+
def instances_store(key, objs, methods)
|
173
|
+
methods = [methods] unless methods.is_a?(Enumerable)
|
174
|
+
methods = methods.to_a.collect { |method| method.is_a?(Symbol) ? method : method.to_s.to_sym }
|
175
|
+
objs = [objs] unless objs.is_a?(Enumerable)
|
176
|
+
instances = @config[:instances]
|
177
|
+
objs.each do |obj|
|
178
|
+
(instances[obj] ||= {}).merge!(key => methods)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def store_options(entry_key, access_key, objs, options)
|
183
|
+
if options.is_a?(Enumerable)
|
184
|
+
options = options.flatten if options.is_a?(Array)
|
185
|
+
else
|
186
|
+
options = [options]
|
187
|
+
end
|
188
|
+
options =
|
189
|
+
if options.length == 1 && options[0].is_a?(Proc)
|
190
|
+
options[0]
|
191
|
+
else
|
192
|
+
options.collect { |option| option.is_a?(Symbol) ? option : option.to_s.to_sym }
|
193
|
+
end
|
194
|
+
objs = [objs] unless objs.is_a?(Enumerable)
|
195
|
+
entry = @config[entry_key]
|
196
|
+
objs.each do |obj|
|
197
|
+
if block_given?
|
198
|
+
fail "Illegal object #{obj}" unless yield(obj)
|
199
|
+
end
|
200
|
+
(entry[obj] ||= {}).merge!(access_key => options)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
Capataz.config
|
207
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class BasicObject
|
2
|
+
def capataz_proxy?
|
3
|
+
false
|
4
|
+
end
|
5
|
+
|
6
|
+
def capataz_slave
|
7
|
+
self
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Parser
|
12
|
+
module Source
|
13
|
+
class Range
|
14
|
+
|
15
|
+
def eql?(obj)
|
16
|
+
self == obj
|
17
|
+
end
|
18
|
+
|
19
|
+
def hash
|
20
|
+
begin_pos + 83 * end_pos
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
|
2
|
+
module Capataz
|
3
|
+
|
4
|
+
class Proxy
|
5
|
+
|
6
|
+
instance_methods.each do |m|
|
7
|
+
if m !~ /^(__|instance_eval|object_id)/
|
8
|
+
class_eval("
|
9
|
+
def #{m}(*args, &block)
|
10
|
+
method_missing(:#{m}, *args, &block)
|
11
|
+
end")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(obj, options = {})
|
16
|
+
@obj = obj
|
17
|
+
if (@options = options || {})[:constant] && !Capataz.allowed_constant?(obj)
|
18
|
+
fail "Illegal access to constant #{obj}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def method(sym)
|
23
|
+
if Capataz.instance_response_to?(@obj, symbol)
|
24
|
+
@obj.method(sym)
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(symbol, *args, &block)
|
31
|
+
if Capataz.instance_response_to?(@obj, symbol)
|
32
|
+
@obj.send(symbol, *args, &block)
|
33
|
+
else
|
34
|
+
fail NoMethodError, "undefined method #{symbol} for #{@obj}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def is_a?(type)
|
39
|
+
if type.capataz_proxy?
|
40
|
+
@obj.is_a?(type.capataz_slave)
|
41
|
+
else
|
42
|
+
@obj.is_a?(type)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def capataz_proxy?
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def capataz_slave
|
51
|
+
@obj
|
52
|
+
end
|
53
|
+
|
54
|
+
def class
|
55
|
+
@obj.class
|
56
|
+
end
|
57
|
+
|
58
|
+
def respond_to?(*args)
|
59
|
+
Capataz.instance_response_to?(@obj, *args)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class HashProxy < Proxy
|
64
|
+
|
65
|
+
def []=(key, value)
|
66
|
+
key = key.capataz_slave if key.capataz_proxy?
|
67
|
+
@obj[key] = value
|
68
|
+
end
|
69
|
+
|
70
|
+
def [](key)
|
71
|
+
key = key.capataz_slave if key.capataz_proxy?
|
72
|
+
value = @obj[key]
|
73
|
+
value.capataz_proxy? ? value.capataz_slave : value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'capataz/source_rewriter'
|
2
|
+
|
3
|
+
module Capataz
|
4
|
+
|
5
|
+
class Rewriter < Parser::Rewriter
|
6
|
+
|
7
|
+
attr_reader :logs
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@options = options || {}
|
11
|
+
@logs = options[:logs] || {}
|
12
|
+
@self_linker = options[:self_linker]
|
13
|
+
@self_send_prefixer = options[:self_send_prefixer]
|
14
|
+
@capatized_nodes = Set.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def rewrite(source_buffer, ast)
|
18
|
+
@logs.clear
|
19
|
+
@capatized_nodes.clear
|
20
|
+
@source_rewriter = Capataz::SourceRewriter.new(source_buffer)
|
21
|
+
|
22
|
+
process(ast)
|
23
|
+
|
24
|
+
@source_rewriter.preprocess
|
25
|
+
@source_rewriter.process
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_array(node)
|
29
|
+
super
|
30
|
+
node.children.each { |child| decapatize(child) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
super
|
35
|
+
unless Capataz.allows_invocation_of(method_name = node.children[1])
|
36
|
+
report_error("invoking method #{method_name} is not allowed")
|
37
|
+
end
|
38
|
+
if (left = node.children[0])
|
39
|
+
capatize(left)
|
40
|
+
elsif node.type == :send
|
41
|
+
unless @self_linker.link?(method_name)
|
42
|
+
report_error("error linking #{method_name}")
|
43
|
+
end if @self_linker
|
44
|
+
(@logs[:self_sends] ||= Set.new) << method_name
|
45
|
+
prefix = @self_send_prefixer ? @self_send_prefixer.prefix(method_name, @self_linker) : ''
|
46
|
+
insert_before(node.location.expression, "::Capataz.handle(self).#{prefix}")
|
47
|
+
end
|
48
|
+
i = 2
|
49
|
+
while i < node.children.length
|
50
|
+
decapatize(node.children[i])
|
51
|
+
i += 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_return(node)
|
56
|
+
report_error('can not use return') unless Capataz.can_declare?(:return)
|
57
|
+
super
|
58
|
+
capatize(node.children[2])
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_casgn(node)
|
62
|
+
report_error('can not define (or override) constants') unless Capataz.can_declare?(:constant)
|
63
|
+
super
|
64
|
+
capatize(node.children[2])
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_lvasgn(node)
|
68
|
+
super
|
69
|
+
capatize(node.children[1])
|
70
|
+
end
|
71
|
+
|
72
|
+
def on_class(node)
|
73
|
+
report_error('can not define classes') unless Capataz.can_declare?(:class)
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
77
|
+
def on_module(node)
|
78
|
+
report_error('can not define modules') unless Capataz.can_declare?(:module)
|
79
|
+
super
|
80
|
+
end
|
81
|
+
|
82
|
+
def on_def(node)
|
83
|
+
report_error('can not define methods') unless Capataz.can_declare?(:def)
|
84
|
+
super
|
85
|
+
insert_before(node.location.expression, ";::Capataz.denied_override_of(self, :#{node.children[0]});") unless Capataz.allow_method_overrides
|
86
|
+
end
|
87
|
+
|
88
|
+
def on_self(_)
|
89
|
+
report_error('can not access to self') unless Capataz.can_declare?(:self)
|
90
|
+
end
|
91
|
+
|
92
|
+
def on_yield(node)
|
93
|
+
report_error('can not make yield calls') unless Capataz.can_declare?(:yield)
|
94
|
+
super
|
95
|
+
end
|
96
|
+
|
97
|
+
def on_ivasgn(_)
|
98
|
+
report_error('can not access instance variables') unless Capataz.can_declare?(:ivar)
|
99
|
+
super
|
100
|
+
end
|
101
|
+
|
102
|
+
def on_ivar(_)
|
103
|
+
report_error('can not access instance variables') unless Capataz.can_declare?(:ivar)
|
104
|
+
super
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_cvasgn(_)
|
108
|
+
report_error('can not access class variables') unless Capataz.can_declare?(:cvar)
|
109
|
+
super
|
110
|
+
end
|
111
|
+
|
112
|
+
def on_cvar(_)
|
113
|
+
report_error('can not access class variables') unless Capataz.can_declare?(:cvar)
|
114
|
+
super
|
115
|
+
end
|
116
|
+
|
117
|
+
def on_gvar(_)
|
118
|
+
report_error('can not access global variables') unless Capataz.can_declare?(:gvar)
|
119
|
+
super
|
120
|
+
end
|
121
|
+
|
122
|
+
def on_gvasgn(_)
|
123
|
+
report_error('can not access global variables') unless Capataz.can_declare?(:gvar)
|
124
|
+
super
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def report_error(message)
|
130
|
+
if @options[:halt_on_error]
|
131
|
+
fail message
|
132
|
+
else
|
133
|
+
(logs[:errors] ||= []) << message
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def capatize(node, options = {})
|
138
|
+
if node && !@capatized_nodes.include?(node)
|
139
|
+
@capatized_nodes << node
|
140
|
+
options[:constant] = true if node.type == :const
|
141
|
+
@source_rewriter.insert_before_multi(node.location.expression, '::Capataz.handle(')
|
142
|
+
if !options.empty?
|
143
|
+
@source_rewriter.insert_after_multi(node.location.expression, ", #{options.to_a.collect { |item| "#{item[0]}: #{item[1]}" }.join(',')})")
|
144
|
+
else
|
145
|
+
@source_rewriter.insert_after_multi(node.location.expression, ')')
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def decapatize(node)
|
151
|
+
unless node.type == :hash
|
152
|
+
@source_rewriter.insert_before_multi(node.location.expression, '(')
|
153
|
+
@source_rewriter.insert_after_multi(node.location.expression, ').capataz_slave')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def const_from(node)
|
158
|
+
if node
|
159
|
+
const_from(node.children[0]) + '::' + node.children[1].to_s
|
160
|
+
else
|
161
|
+
''
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module Capataz
|
3
|
+
|
4
|
+
class SourceRewriter < Parser::Source::Rewriter
|
5
|
+
|
6
|
+
def preprocess
|
7
|
+
hash = {}
|
8
|
+
@queue.each do |action|
|
9
|
+
if a = hash[action.range]
|
10
|
+
hash[action.range] = Parser::Source::Rewriter::Action.new(a.range, action.replacement + a.replacement)
|
11
|
+
else
|
12
|
+
hash[action.range] = action
|
13
|
+
end
|
14
|
+
end
|
15
|
+
@queue = hash.values.to_a
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: capataz
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maikel Arcia
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: parser
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- macarci@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- CODE_OF_CONDUCT.md
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- bin/console
|
68
|
+
- bin/setup
|
69
|
+
- capataz.gemspec
|
70
|
+
- lib/capataz.rb
|
71
|
+
- lib/capataz/core_ext.rb
|
72
|
+
- lib/capataz/proxy.rb
|
73
|
+
- lib/capataz/rewriter.rb
|
74
|
+
- lib/capataz/source_rewriter.rb
|
75
|
+
- lib/capataz/version.rb
|
76
|
+
homepage: https://github.com/macarci/capataz
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.4.6
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Provides Ruby code execution control by defining rules for syntax and runtime
|
100
|
+
behavior.
|
101
|
+
test_files: []
|