rubyless 0.8.2 → 0.8.3
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/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