rubyless 0.8.2 → 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +11 -0
- data/lib/ruby_less/basic_types.rb +6 -2
- data/lib/ruby_less/info.rb +1 -1
- data/lib/ruby_less/processor.rb +34 -7
- data/lib/ruby_less/safe_class.rb +25 -22
- data/rubyless.gemspec +48 -51
- data/test/RubyLess/basic.yml +21 -5
- data/test/RubyLess/hash.yml +8 -0
- data/test/RubyLess_test.rb +19 -1
- metadata +9 -10
- data/.gitignore +0 -4
data/History.txt
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
== 0.8.3
|
2
|
+
|
3
|
+
* Major enhancements
|
4
|
+
* Better support for literal Arrays.
|
5
|
+
* Support for Hash methods and Hash literals.
|
6
|
+
|
7
|
+
* Minor enhancements
|
8
|
+
* Added some methods for basic types.
|
9
|
+
* Fixed a bug where evaluated string would be altered.
|
10
|
+
* Added support for true and false classes.
|
11
|
+
|
1
12
|
== 0.8.2 2011-01-15
|
2
13
|
|
3
14
|
* Minor enhancements
|
@@ -18,7 +18,9 @@ RubyLess::SafeClass.safe_method_for( Number,
|
|
18
18
|
[:==, Number] => Boolean, [:< , Number] => Boolean, [:> , Number] => Boolean,
|
19
19
|
[:<=, Number] => Boolean, [:>=, Number] => Boolean, [:- , Number] => Number,
|
20
20
|
[:+ , Number] => Number, [:* , Number] => Number, [:/ , Number] => Number,
|
21
|
-
[:% , Number] => Number, [:"-@"] => Number
|
21
|
+
[:% , Number] => Number, [:"-@"] => Number,
|
22
|
+
:to_f => Number,
|
23
|
+
:to_i => Number
|
22
24
|
)
|
23
25
|
|
24
26
|
RubyLess::SafeClass.safe_method_for( Time,
|
@@ -33,7 +35,9 @@ RubyLess::SafeClass.safe_method_for( String,
|
|
33
35
|
|
34
36
|
RubyLess::SafeClass.safe_method_for( NilClass,
|
35
37
|
[:==, String] => Boolean,
|
36
|
-
[:==, Number] => Boolean
|
38
|
+
[:==, Number] => Boolean,
|
39
|
+
:to_f => Number,
|
40
|
+
:to_i => Number
|
37
41
|
)
|
38
42
|
|
39
43
|
RubyLess::SafeClass.safe_method_for( Array,
|
data/lib/ruby_less/info.rb
CHANGED
data/lib/ruby_less/processor.rb
CHANGED
@@ -11,7 +11,11 @@ module RubyLess
|
|
11
11
|
|
12
12
|
def self.translate(receiver, string)
|
13
13
|
if sexp = RubyParser.new.parse(string)
|
14
|
-
self.new(receiver).process(sexp)
|
14
|
+
res = self.new(receiver).process(sexp)
|
15
|
+
if res.klass.kind_of?(Hash)
|
16
|
+
res.opts[:class] = Hash
|
17
|
+
end
|
18
|
+
res
|
15
19
|
elsif string.size == 0
|
16
20
|
''
|
17
21
|
else
|
@@ -52,6 +56,14 @@ module RubyLess
|
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
59
|
+
def process_true(*args)
|
60
|
+
t 'true', {:class => Boolean, :literal => true}
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_false(*args)
|
64
|
+
t 'false', {:class => Boolean, :literal => false}
|
65
|
+
end
|
66
|
+
|
55
67
|
def process_and(exp)
|
56
68
|
t "(#{process(exp.shift)} and #{process(exp.shift)})", Boolean
|
57
69
|
end
|
@@ -118,7 +130,7 @@ module RubyLess
|
|
118
130
|
list << res
|
119
131
|
end
|
120
132
|
|
121
|
-
res.opts[:class] = Array
|
133
|
+
res.opts[:class] = [content_class] # Array
|
122
134
|
res.opts[:elem] = content_class
|
123
135
|
t "[#{list * ','}]", res.opts.merge(:literal => nil)
|
124
136
|
end
|
@@ -229,7 +241,18 @@ module RubyLess
|
|
229
241
|
cond = []
|
230
242
|
end
|
231
243
|
|
232
|
-
|
244
|
+
if receiver && receiver.klass.kind_of?(Hash)
|
245
|
+
# resolve now
|
246
|
+
if signature.first == '[]' && klass = receiver.klass[args.literal]
|
247
|
+
return receiver.hash[args.literal]
|
248
|
+
else
|
249
|
+
# safe_method_type on Hash... ?
|
250
|
+
receiver = TypedString.new(receiver, Hash)
|
251
|
+
opts = get_method(receiver, signature)
|
252
|
+
end
|
253
|
+
else
|
254
|
+
opts = get_method(receiver, signature)
|
255
|
+
end
|
233
256
|
|
234
257
|
# method type can rewrite receiver
|
235
258
|
if opts[:receiver]
|
@@ -354,11 +377,15 @@ module RubyLess
|
|
354
377
|
def get_method(receiver, signature)
|
355
378
|
klass = receiver ? receiver.klass : @helper
|
356
379
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
380
|
+
if klass.respond_to?(:safe_method_type)
|
381
|
+
type = klass.safe_method_type(signature, receiver)
|
382
|
+
elsif klass.kind_of?(Array)
|
383
|
+
unless type = SafeClass.safe_method_type_for(Array, signature)
|
384
|
+
raise RubyLess::NoMethodError.new(receiver, "[#{klass}]", signature)
|
385
|
+
end
|
386
|
+
elsif type = SafeClass.safe_method_type_for(klass, signature)
|
361
387
|
end
|
388
|
+
|
362
389
|
raise RubyLess::NoMethodError.new(receiver, klass, signature) if !type || type[:class].kind_of?(Symbol) # we cannot send: no object.
|
363
390
|
|
364
391
|
type[:class].kind_of?(Proc) ? type[:class].call(@helper, receiver ? receiver.klass : @helper, signature) : type
|
data/lib/ruby_less/safe_class.rb
CHANGED
@@ -13,32 +13,35 @@ module RubyLess
|
|
13
13
|
# Return method type (options) if the given signature is a safe method for the class.
|
14
14
|
def self.safe_method_type_for(klass, signature)
|
15
15
|
if klass.kind_of?(Array)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
Hash
|
28
|
-
|
29
|
-
|
30
|
-
|
16
|
+
safe_method_type_for(Array, signature)
|
17
|
+
elsif klass.kind_of?(Hash)
|
18
|
+
nil # literal hash resolved in processor
|
19
|
+
klass = Hash
|
20
|
+
else
|
21
|
+
# Signature might be ['name', {:mode => String, :type => Number}].
|
22
|
+
# build signature arguments
|
23
|
+
|
24
|
+
# Replace all hashes in signature by Hash class and check for arguments
|
25
|
+
signature_args = []
|
26
|
+
signature = signature.map do |s|
|
27
|
+
if s.kind_of?(Hash)
|
28
|
+
signature_args << s
|
29
|
+
Hash
|
30
|
+
else
|
31
|
+
signature_args << nil
|
32
|
+
s
|
33
|
+
end
|
31
34
|
end
|
32
|
-
end
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
# Find safe method in all ancestry
|
37
|
+
klass.ancestors.each do |ancestor|
|
38
|
+
# FIXME: find a way to optimize this search !
|
39
|
+
if type = safe_method_with_hash_args(ancestor, signature, signature_args)
|
40
|
+
return type
|
41
|
+
end
|
39
42
|
end
|
43
|
+
nil
|
40
44
|
end
|
41
|
-
nil
|
42
45
|
end
|
43
46
|
|
44
47
|
def self.literal_class_for(klass)
|
data/rubyless.gemspec
CHANGED
@@ -1,78 +1,75 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rubyless}
|
8
|
-
s.version = "0.8.
|
8
|
+
s.version = "0.8.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Gaspard Bucher"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-06-15}
|
13
13
|
s.description = %q{RubyLess is an interpreter for "safe ruby". The idea is to transform some "unsafe" ruby code into safe, type checked ruby, eventually rewriting some variables or methods.}
|
14
14
|
s.email = %q{gaspard@teti.ch}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"README.rdoc",
|
17
|
-
|
17
|
+
"TODO"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
|
-
".
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
"test/typed_string_test.rb"
|
20
|
+
"History.txt",
|
21
|
+
"README.rdoc",
|
22
|
+
"Rakefile",
|
23
|
+
"TODO",
|
24
|
+
"lib/ruby_less.rb",
|
25
|
+
"lib/ruby_less/basic_types.rb",
|
26
|
+
"lib/ruby_less/error.rb",
|
27
|
+
"lib/ruby_less/info.rb",
|
28
|
+
"lib/ruby_less/no_method_error.rb",
|
29
|
+
"lib/ruby_less/processor.rb",
|
30
|
+
"lib/ruby_less/safe_class.rb",
|
31
|
+
"lib/ruby_less/signature_hash.rb",
|
32
|
+
"lib/ruby_less/syntax_error.rb",
|
33
|
+
"lib/ruby_less/typed_method.rb",
|
34
|
+
"lib/ruby_less/typed_string.rb",
|
35
|
+
"lib/rubyless.rb",
|
36
|
+
"rails/init.rb",
|
37
|
+
"rubyless.gemspec",
|
38
|
+
"test/RubyLess/active_record.yml",
|
39
|
+
"test/RubyLess/basic.yml",
|
40
|
+
"test/RubyLess/errors.yml",
|
41
|
+
"test/RubyLess/hash.yml",
|
42
|
+
"test/RubyLess/string.yml",
|
43
|
+
"test/RubyLess/time.yml",
|
44
|
+
"test/RubyLess_test.rb",
|
45
|
+
"test/mock/active_record_mock.rb",
|
46
|
+
"test/mock/dummy_class.rb",
|
47
|
+
"test/mock/dummy_module.rb",
|
48
|
+
"test/mock/property_column.rb",
|
49
|
+
"test/safe_class_test.rb",
|
50
|
+
"test/signature_hash_test.rb",
|
51
|
+
"test/test_helper.rb",
|
52
|
+
"test/typed_method_test.rb",
|
53
|
+
"test/typed_string_test.rb"
|
55
54
|
]
|
56
55
|
s.homepage = %q{http://zenadmin.org/546}
|
57
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
58
56
|
s.require_paths = ["lib"]
|
59
|
-
s.rubygems_version = %q{1.
|
57
|
+
s.rubygems_version = %q{1.6.1}
|
60
58
|
s.summary = %q{RubyLess is an interpreter for "safe ruby"}
|
61
59
|
s.test_files = [
|
60
|
+
"test/RubyLess_test.rb",
|
62
61
|
"test/mock/active_record_mock.rb",
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
"test/typed_string_test.rb"
|
62
|
+
"test/mock/dummy_class.rb",
|
63
|
+
"test/mock/dummy_module.rb",
|
64
|
+
"test/mock/property_column.rb",
|
65
|
+
"test/safe_class_test.rb",
|
66
|
+
"test/signature_hash_test.rb",
|
67
|
+
"test/test_helper.rb",
|
68
|
+
"test/typed_method_test.rb",
|
69
|
+
"test/typed_string_test.rb"
|
72
70
|
]
|
73
71
|
|
74
72
|
if s.respond_to? :specification_version then
|
75
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
76
73
|
s.specification_version = 3
|
77
74
|
|
78
75
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
data/test/RubyLess/basic.yml
CHANGED
@@ -164,10 +164,6 @@ accept_nil_in_no_nil:
|
|
164
164
|
src: "no_nil(accept_nil(dictionary[:foo]))"
|
165
165
|
tem: "no_nil(accept_nil(get_dict[:foo]))"
|
166
166
|
|
167
|
-
parse_array:
|
168
|
-
src: "[3, 6]"
|
169
|
-
tem: '[3,6]'
|
170
|
-
|
171
167
|
noop_method:
|
172
168
|
src: 'no_op("hello")'
|
173
169
|
tem: '("hello")'
|
@@ -222,4 +218,24 @@ map_allowed_method:
|
|
222
218
|
|
223
219
|
map_forbidden_method:
|
224
220
|
src: "%w{45 52}.map(:foo)"
|
225
|
-
tem: "unknown method 'map(:foo)' for '[\"45\",\"52\"]' of type
|
221
|
+
tem: "unknown method 'map(:foo)' for '[\"45\",\"52\"]' of type [String]."
|
222
|
+
|
223
|
+
eval_true:
|
224
|
+
src: "true"
|
225
|
+
tem: "true"
|
226
|
+
|
227
|
+
eval_false:
|
228
|
+
src: "false"
|
229
|
+
tem: "false"
|
230
|
+
|
231
|
+
number_array:
|
232
|
+
src: "[3, 6]"
|
233
|
+
tem: '[3,6]'
|
234
|
+
|
235
|
+
string_array:
|
236
|
+
src: "%w{foo bar}.join(',')"
|
237
|
+
res: 'foo,bar'
|
238
|
+
|
239
|
+
array_bad_method:
|
240
|
+
src: "%w{foo bar}.plop(',')"
|
241
|
+
res: "unknown method 'plop(String)' for '[\"foo\",\"bar\"]' of type [String]."
|
data/test/RubyLess/hash.yml
CHANGED
@@ -2,6 +2,14 @@ parse_hash:
|
|
2
2
|
src: "{'one' => 1, 2 => 'two'}"
|
3
3
|
tem: "{\"one\" => 1, 2 => \"two\"}"
|
4
4
|
|
5
|
+
literal_on_hash:
|
6
|
+
src: "{'one' => 1, 2 => 'two'}['one'] + 2"
|
7
|
+
tem: "(1+2)"
|
8
|
+
|
9
|
+
method_on_hash:
|
10
|
+
src: "{'one' => 1, 2 => 'two'}.to_param"
|
11
|
+
tem: "{\"one\" => 1, 2 => \"two\"}.to_param"
|
12
|
+
|
5
13
|
hash_access:
|
6
14
|
src: "dictionary[:key]"
|
7
15
|
tem: "get_dict[:key]"
|
data/test/RubyLess_test.rb
CHANGED
@@ -22,7 +22,7 @@ class RubyLessTest < Test::Unit::TestCase
|
|
22
22
|
end
|
23
23
|
res = RubyLess::TypedString.new(res, :class => [type[:class]])
|
24
24
|
else
|
25
|
-
raise RubyLess::NoMethodError.new(receiver.raw, receiver.klass, ['map', method])
|
25
|
+
raise RubyLess::NoMethodError.new(receiver.raw, "[#{receiver.klass}]", ['map', method])
|
26
26
|
end
|
27
27
|
else
|
28
28
|
# should never happen
|
@@ -55,6 +55,10 @@ class RubyLessTest < Test::Unit::TestCase
|
|
55
55
|
|
56
56
|
safe_method_for Array, [:map, Symbol] => {:method => 'nil', :class => nil, :pre_processor => self.map_proc}
|
57
57
|
|
58
|
+
safe_method_for Array, [:join, String] => {:method => 'join', :class => String, :pre_processor => true}
|
59
|
+
|
60
|
+
safe_method_for Hash, :to_param => String
|
61
|
+
|
58
62
|
safe_method_for String, :upcase => {:class => String, :pre_processor => true}
|
59
63
|
|
60
64
|
safe_method_for Time, [:strftime, String] => String
|
@@ -169,6 +173,20 @@ class RubyLessTest < Test::Unit::TestCase
|
|
169
173
|
assert_equal "marsupilami.says('Hello')", RubyLess.translate(typed_string, 'talk')
|
170
174
|
end
|
171
175
|
|
176
|
+
def test_literal_hash_type
|
177
|
+
# Out of RubyLess::Processor, type is Hash, not {:foo => TypedString}.
|
178
|
+
typed_string = RubyLess.translate(self, %q{{:foo => 'bar'}})
|
179
|
+
assert_equal Hash, typed_string.klass
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_should_not_alter_input_string
|
183
|
+
orig_str = 'contact where id #{params[:foo]} in site'
|
184
|
+
str = orig_str.dup
|
185
|
+
RubyLess.translate(self, str)
|
186
|
+
rescue RubyLess::Error => err
|
187
|
+
assert_equal orig_str, str
|
188
|
+
end
|
189
|
+
|
172
190
|
def yt_do_test(file, test, context = yt_get('context',file,test))
|
173
191
|
@@test_strings[file][test].keys.each do |key|
|
174
192
|
next if ['src', 'context', 'str'].include?(key)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyless
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 57
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 8
|
9
|
-
-
|
10
|
-
version: 0.8.
|
9
|
+
- 3
|
10
|
+
version: 0.8.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Gaspard Bucher
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-06-15 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -90,7 +90,6 @@ extra_rdoc_files:
|
|
90
90
|
- README.rdoc
|
91
91
|
- TODO
|
92
92
|
files:
|
93
|
-
- .gitignore
|
94
93
|
- History.txt
|
95
94
|
- README.rdoc
|
96
95
|
- Rakefile
|
@@ -130,8 +129,8 @@ homepage: http://zenadmin.org/546
|
|
130
129
|
licenses: []
|
131
130
|
|
132
131
|
post_install_message:
|
133
|
-
rdoc_options:
|
134
|
-
|
132
|
+
rdoc_options: []
|
133
|
+
|
135
134
|
require_paths:
|
136
135
|
- lib
|
137
136
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -155,16 +154,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
154
|
requirements: []
|
156
155
|
|
157
156
|
rubyforge_project:
|
158
|
-
rubygems_version: 1.
|
157
|
+
rubygems_version: 1.6.1
|
159
158
|
signing_key:
|
160
159
|
specification_version: 3
|
161
160
|
summary: RubyLess is an interpreter for "safe ruby"
|
162
161
|
test_files:
|
162
|
+
- test/RubyLess_test.rb
|
163
163
|
- test/mock/active_record_mock.rb
|
164
164
|
- test/mock/dummy_class.rb
|
165
165
|
- test/mock/dummy_module.rb
|
166
166
|
- test/mock/property_column.rb
|
167
|
-
- test/RubyLess_test.rb
|
168
167
|
- test/safe_class_test.rb
|
169
168
|
- test/signature_hash_test.rb
|
170
169
|
- test/test_helper.rb
|
data/.gitignore
DELETED