rubyless 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +15 -1
- data/README.rdoc +2 -2
- data/Rakefile +1 -1
- data/TODO +8 -0
- data/lib/ruby_less/basic_types.rb +13 -1
- data/lib/ruby_less/info.rb +1 -1
- data/lib/ruby_less/no_method_error.rb +3 -2
- data/lib/ruby_less/processor.rb +106 -34
- data/lib/ruby_less/safe_class.rb +19 -9
- data/lib/ruby_less/typed_method.rb +50 -0
- data/lib/ruby_less/typed_string.rb +56 -11
- data/lib/ruby_less.rb +17 -4
- data/rubyless.gemspec +16 -8
- data/test/RubyLess/basic.yml +51 -5
- data/test/RubyLess/errors.yml +17 -9
- data/test/RubyLess/hash.yml +19 -0
- data/test/RubyLess/string.yml +17 -0
- data/test/RubyLess_test.rb +42 -8
- data/test/mock/dummy_class.rb +2 -3
- data/test/mock/dummy_module.rb +1 -1
- data/test/typed_method_test.rb +47 -0
- data/test/typed_string_test.rb +19 -0
- metadata +14 -6
data/History.txt
CHANGED
@@ -1,6 +1,20 @@
|
|
1
|
+
== 0.5.0 2010-05-27
|
2
|
+
|
3
|
+
* Major enhancements
|
4
|
+
* Added 'accept_nil' option.
|
5
|
+
* Added 'append_hash' options.
|
6
|
+
* RubyLess activation is now enabled with 'include RubyLess' (no need for SafeClass).
|
7
|
+
* Added support for compile time literal args evaluation.
|
8
|
+
* Added support for compile time literal args evaluation on literal objects (strings for example).
|
9
|
+
* All method signatures that have an optional hash should accept calls without the last hash.
|
10
|
+
* Better error messages for missing methods.
|
11
|
+
* Added support for Array with Mixed array detection.
|
12
|
+
* Added support for pre_processing with helper method.
|
13
|
+
* Added support for method detection on NilClass.
|
14
|
+
|
1
15
|
== 0.4.0 2010-03-21
|
2
16
|
|
3
|
-
* 4 major
|
17
|
+
* 4 major enhancements
|
4
18
|
* Parsing inheritance tree to get safe_method_type.
|
5
19
|
* Instance variable (ivar) support (declared as safe_methods).
|
6
20
|
* Added support for prepend variables to tranform methods like link("foo") to link(@node, "foo").
|
data/README.rdoc
CHANGED
@@ -24,14 +24,14 @@ return 'nil' instead of the declared output, you need to wrap your final ruby 'e
|
|
24
24
|
|
25
25
|
# signature is made of [method, arg_class, arg_class, ...]
|
26
26
|
class Node
|
27
|
-
include RubyLess
|
27
|
+
include RubyLess
|
28
28
|
safe_method [:ancestor?, Node] => Boolean
|
29
29
|
end
|
30
30
|
|
31
31
|
# methods defined in helper
|
32
32
|
|
33
33
|
# global methods
|
34
|
-
include RubyLess
|
34
|
+
include RubyLess
|
35
35
|
safe_method :prev => {:class => Dummy, :method => 'previous', :nil => true}
|
36
36
|
safe_method :node => lambda {|h| {:class => h.context[:node_class], :method => h.context[:node]}}
|
37
37
|
safe_method [:strftime, Time, String] => String
|
data/Rakefile
CHANGED
@@ -16,7 +16,7 @@ begin
|
|
16
16
|
gem.add_dependency 'ruby_parser', '>= 2.0.4'
|
17
17
|
gem.add_dependency 'sexp_processor', '>= 3.0.1'
|
18
18
|
gem.add_development_dependency "shoulda", ">= 0"
|
19
|
-
gem.add_development_dependency "yamltest", ">= 0.
|
19
|
+
gem.add_development_dependency "yamltest", ">= 0.6.0"
|
20
20
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
21
21
|
end
|
22
22
|
Jeweler::GemcutterTasks.new
|
data/TODO
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
2010-03-28
|
2
|
+
|
3
|
+
1. Write shoulda tests for TypedMethod, TypedArray and TypedHash
|
4
|
+
2. Create sub-classes of TypedString: TypedMethod, TypedArray, TypedHash
|
5
|
+
A TypedMethod should have a TypedArray as arguments, eventually with a TypedHash as last argument
|
6
|
+
3. Move list code from TypedString into TypedArray
|
7
|
+
4. Move hash code from TypedString into TypedHash
|
8
|
+
|
@@ -1,15 +1,27 @@
|
|
1
1
|
require 'ruby_less/safe_class'
|
2
2
|
|
3
3
|
|
4
|
+
# Dummy classes
|
4
5
|
class Boolean
|
5
6
|
end
|
6
7
|
|
7
8
|
class Number
|
8
9
|
end
|
9
10
|
|
11
|
+
class StringDictionary
|
12
|
+
include RubyLess
|
13
|
+
safe_method ['[]', Symbol] => {:class => String, :nil => true}
|
14
|
+
disable_safe_read # ?
|
15
|
+
end
|
16
|
+
|
10
17
|
RubyLess::SafeClass.safe_literal_class Fixnum => Number, Float => Number, Symbol => Symbol, Regexp => Regexp
|
11
18
|
RubyLess::SafeClass.safe_method_for( Number,
|
12
19
|
[:==, Number] => Boolean, [:< , Number] => Boolean, [:> , Number] => Boolean,
|
13
20
|
[:<=, Number] => Boolean, [:>=, Number] => Boolean, [:- , Number] => Number,
|
14
21
|
[:+ , Number] => Number, [:* , Number] => Number, [:/ , Number] => Number,
|
15
|
-
[:% , Number] => Number, [:"-@"] => Number
|
22
|
+
[:% , Number] => Number, [:"-@"] => Number
|
23
|
+
)
|
24
|
+
|
25
|
+
RubyLess::SafeClass.safe_method_for( String,
|
26
|
+
[:==, String] => Boolean
|
27
|
+
)
|
data/lib/ruby_less/info.rb
CHANGED
@@ -9,7 +9,7 @@ module RubyLess
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def message
|
12
|
-
"#{
|
12
|
+
"#{error_message} '#{method_with_arguments}' for #{receiver_with_class}."
|
13
13
|
end
|
14
14
|
|
15
15
|
def error_message
|
@@ -21,7 +21,8 @@ module RubyLess
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def receiver_with_class
|
24
|
-
@
|
24
|
+
klass = @klass.kind_of?(Class) ? @klass : @klass.class
|
25
|
+
@receiver ? "'#{@receiver}' of type #{@klass}" : klass
|
25
26
|
end
|
26
27
|
|
27
28
|
def method_with_arguments
|
data/lib/ruby_less/processor.rb
CHANGED
@@ -12,6 +12,8 @@ module RubyLess
|
|
12
12
|
def self.translate(string, helper)
|
13
13
|
sexp = RubyParser.new.parse(string)
|
14
14
|
self.new(helper).process(sexp)
|
15
|
+
rescue Racc::ParseError => err
|
16
|
+
raise RubyLess::SyntaxError.new(err.message)
|
15
17
|
end
|
16
18
|
|
17
19
|
def initialize(helper)
|
@@ -86,8 +88,22 @@ module RubyLess
|
|
86
88
|
end
|
87
89
|
|
88
90
|
def process_array(exp)
|
89
|
-
|
90
|
-
|
91
|
+
literal = true
|
92
|
+
list = []
|
93
|
+
classes = []
|
94
|
+
while !exp.empty?
|
95
|
+
res = process(exp.shift)
|
96
|
+
content_class ||= res.opts[:class]
|
97
|
+
unless res.opts[:class] <= content_class
|
98
|
+
classes = list.map { content_class.name } + [res.opts[:class].name]
|
99
|
+
raise RubyLess::Error.new("Mixed Array not supported ([#{classes * ','}]).")
|
100
|
+
end
|
101
|
+
list << res
|
102
|
+
end
|
103
|
+
|
104
|
+
res.opts[:class] = Array
|
105
|
+
res.opts[:array_content_class] = content_class
|
106
|
+
t "[#{list * ','}]", res.opts.merge(:literal => nil)
|
91
107
|
end
|
92
108
|
|
93
109
|
def process_vcall(exp)
|
@@ -104,11 +120,12 @@ module RubyLess
|
|
104
120
|
|
105
121
|
def process_lit(exp)
|
106
122
|
lit = exp.shift
|
107
|
-
t lit.inspect, get_lit_class(lit
|
123
|
+
t lit.inspect, get_lit_class(lit)
|
108
124
|
end
|
109
125
|
|
110
126
|
def process_str(exp)
|
111
|
-
|
127
|
+
lit = exp.shift
|
128
|
+
t lit.inspect, :class => String, :literal => lit
|
112
129
|
end
|
113
130
|
|
114
131
|
def process_dstr(exp)
|
@@ -120,8 +137,7 @@ module RubyLess
|
|
120
137
|
end
|
121
138
|
|
122
139
|
def process_hash(exp)
|
123
|
-
result =
|
124
|
-
klass = {}
|
140
|
+
result = t "", String
|
125
141
|
until exp.empty?
|
126
142
|
key = exp.shift
|
127
143
|
if [:lit, :str].include?(key.first)
|
@@ -131,16 +147,14 @@ module RubyLess
|
|
131
147
|
type = rhs.first
|
132
148
|
rhs = process rhs
|
133
149
|
#rhs = "(#{rhs})" unless [:lit, :str].include? type # TODO: verify better!
|
134
|
-
|
135
|
-
result << "#{key.inspect} => #{rhs}"
|
136
|
-
klass[key] = rhs.klass
|
150
|
+
result.set_hash(key, rhs)
|
137
151
|
else
|
138
152
|
# ERROR: invalid key
|
139
153
|
raise RubyLess::SyntaxError.new("Invalid key type for hash (should be a literal value, was #{key.first.inspect})")
|
140
154
|
end
|
141
155
|
end
|
142
|
-
|
143
|
-
|
156
|
+
result.rebuild_hash
|
157
|
+
result
|
144
158
|
end
|
145
159
|
|
146
160
|
def process_ivar(exp)
|
@@ -198,30 +212,76 @@ module RubyLess
|
|
198
212
|
end
|
199
213
|
|
200
214
|
if receiver
|
201
|
-
if receiver.could_be_nil?
|
202
|
-
cond += receiver.cond
|
203
|
-
end
|
204
215
|
opts = get_method(receiver, signature)
|
216
|
+
method_call_with_receiver(receiver, args, opts, cond, signature)
|
217
|
+
else
|
218
|
+
opts = get_method(nil, signature)
|
205
219
|
method = opts[:method]
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
220
|
+
args = args_with_prepend(args, opts)
|
221
|
+
|
222
|
+
if (proc = opts[:pre_processor]) && !args.list.detect {|a| !a.literal}
|
223
|
+
if proc.kind_of?(Proc)
|
224
|
+
res = proc.call(*args.list.map(&:literal))
|
225
|
+
else
|
226
|
+
res = @helper.send(proc, *args.list.map(&:literal))
|
227
|
+
end
|
228
|
+
|
229
|
+
return res.kind_of?(TypedString) ? res : t(res.inspect, :class => String, :literal => res)
|
230
|
+
end
|
231
|
+
|
232
|
+
if opts[:accept_nil]
|
233
|
+
method_call_accepting_nil(method, args, opts)
|
214
234
|
else
|
215
|
-
args = args_with_prepend(args, opts)
|
216
235
|
args = "(#{args.raw})" if args
|
217
|
-
t_if cond, "#{
|
236
|
+
t_if cond, "#{method}#{args}", opts
|
218
237
|
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def method_call_accepting_nil(method, args, opts)
|
242
|
+
if args
|
243
|
+
args = args.list.map do |arg|
|
244
|
+
if !arg.could_be_nil? || arg.raw == arg.cond.to_s
|
245
|
+
arg.raw
|
246
|
+
else
|
247
|
+
"(#{arg.cond} ? #{arg.raw} : nil)"
|
248
|
+
end
|
249
|
+
end.join(', ')
|
250
|
+
|
251
|
+
t "#{method}(#{args})", opts
|
252
|
+
else
|
253
|
+
t method, opts
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def method_call_with_receiver(receiver, args, opts, cond, signature)
|
258
|
+
method = opts[:method]
|
259
|
+
arg_list = args ? args.list : []
|
260
|
+
|
261
|
+
if receiver.could_be_nil? && opts != SafeClass.safe_method_type_for(NilClass, signature)
|
262
|
+
# Do not add a condition if the method applies on nil
|
263
|
+
cond += receiver.cond
|
264
|
+
elsif receiver.literal && (proc = opts[:pre_processor]) && !arg_list.detect {|a| !a.literal}
|
265
|
+
if proc.kind_of?(Proc)
|
266
|
+
res = proc.call([receiver.literal] + arg_list.map(&:literal))
|
267
|
+
else
|
268
|
+
res = receiver.literal.send(*([method] + arg_list.map(&:literal)))
|
269
|
+
end
|
270
|
+
return res.kind_of?(TypedString) ? res : t(res.inspect, :class => String, :literal => res)
|
271
|
+
end
|
272
|
+
|
273
|
+
if method == '/'
|
274
|
+
t_if cond, "(#{receiver.raw}#{method}#{args.raw} rescue nil)", opts.merge(:nil => true)
|
275
|
+
elsif INFIX_OPERATOR.include?(method)
|
276
|
+
t_if cond, "(#{receiver.raw}#{method}#{args.raw})", opts
|
277
|
+
elsif PREFIX_OPERATOR.include?(method)
|
278
|
+
t_if cond, "#{method.to_s[0..0]}#{receiver.raw}", opts
|
279
|
+
elsif method == '[]'
|
280
|
+
t_if cond, "#{receiver.raw}[#{args.raw}]", opts
|
219
281
|
else
|
220
|
-
opts = get_method(nil, signature)
|
221
|
-
method = opts[:method]
|
222
282
|
args = args_with_prepend(args, opts)
|
223
283
|
args = "(#{args.raw})" if args
|
224
|
-
t_if cond, "#{method}#{args}", opts
|
284
|
+
t_if cond, "#{receiver.raw}.#{method}#{args}", opts
|
225
285
|
end
|
226
286
|
end
|
227
287
|
|
@@ -258,24 +318,36 @@ module RubyLess
|
|
258
318
|
type[:class].kind_of?(Proc) ? type[:class].call(@helper, signature) : type
|
259
319
|
end
|
260
320
|
|
261
|
-
def get_lit_class(
|
262
|
-
unless lit_class = RubyLess::SafeClass.literal_class_for(
|
321
|
+
def get_lit_class(lit)
|
322
|
+
unless lit_class = RubyLess::SafeClass.literal_class_for(lit.class)
|
263
323
|
raise RubyLess::SyntaxError.new("#{klass} literal not supported by RubyLess.")
|
264
324
|
end
|
265
|
-
lit_class
|
325
|
+
{:class => lit_class, :literal => lit}
|
266
326
|
end
|
267
327
|
|
268
328
|
def args_with_prepend(args, opts)
|
269
329
|
if prepend_args = opts[:prepend_args]
|
270
330
|
if args
|
271
331
|
prepend_args.append_argument(args)
|
272
|
-
prepend_args
|
332
|
+
args = prepend_args
|
273
333
|
else
|
274
|
-
prepend_args
|
334
|
+
args = prepend_args
|
275
335
|
end
|
276
|
-
else
|
277
|
-
args
|
278
336
|
end
|
337
|
+
|
338
|
+
if append_hash = opts[:append_hash]
|
339
|
+
last_arg = args.list.last
|
340
|
+
unless last_arg.klass.kind_of?(Hash)
|
341
|
+
last_arg = t "", String
|
342
|
+
args.append_argument(last_arg)
|
343
|
+
end
|
344
|
+
append_hash.each do |key, value|
|
345
|
+
last_arg.set_hash(key, value)
|
346
|
+
end
|
347
|
+
last_arg.rebuild_hash
|
348
|
+
args.rebuild_arguments
|
349
|
+
end
|
350
|
+
args
|
279
351
|
end
|
280
352
|
end
|
281
353
|
end
|
data/lib/ruby_less/safe_class.rb
CHANGED
@@ -26,9 +26,10 @@ module RubyLess
|
|
26
26
|
s
|
27
27
|
end
|
28
28
|
end
|
29
|
+
|
29
30
|
# Find safe method in all ancestry
|
30
31
|
klass.ancestors.each do |ancestor|
|
31
|
-
|
32
|
+
# FIXME: find a way to optimize this search !
|
32
33
|
if type = safe_method_with_hash_args(ancestor, signature, signature_args)
|
33
34
|
return type
|
34
35
|
end
|
@@ -53,13 +54,22 @@ module RubyLess
|
|
53
54
|
defaults = methods_hash.delete(:defaults) || {}
|
54
55
|
|
55
56
|
list = (@@_safe_methods[klass] ||= {})
|
56
|
-
methods_hash.each do |
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
57
|
+
methods_hash.each do |signature, type|
|
58
|
+
signature, hash_args = build_signature(signature)
|
59
|
+
type = {:class => type} unless type.kind_of?(Hash)
|
60
|
+
type = defaults.merge(type)
|
61
|
+
type[:method] = type[:method] ? type[:method].to_s : signature.first.to_s
|
62
|
+
if hash_args
|
63
|
+
type[:hash_args] = hash_args
|
64
|
+
list[signature] = type
|
65
|
+
if hash_args.last.kind_of?(Hash)
|
66
|
+
# Also build signature without last hash. This enables the common idiom
|
67
|
+
# method(arg, arg, opts = {})
|
68
|
+
list[signature[0..-2]] = type.dup
|
69
|
+
end
|
70
|
+
else
|
71
|
+
list[signature] = type
|
72
|
+
end
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
@@ -193,7 +203,7 @@ module RubyLess
|
|
193
203
|
end
|
194
204
|
|
195
205
|
# Safe attribute reader used when 'safe_readable?' could not be called because the class
|
196
|
-
# is not known during compile time.
|
206
|
+
# is not known during compile time. FIXME: Is this used anymore ?
|
197
207
|
def safe_read(key)
|
198
208
|
return "'#{key}' not readable" unless type = self.class.safe_method_type([key])
|
199
209
|
self.send(type[:method])
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module RubyLess
|
2
|
+
class TypedMethod
|
3
|
+
attr_accessor :name, :args
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
@name = args.shift
|
7
|
+
@args = args
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_argument(arg, klass_or_opts = nil)
|
11
|
+
if klass_or_opts
|
12
|
+
@args << TypedString.new(arg.to_s, klass_or_opts)
|
13
|
+
elsif arg.kind_of?(TypedString)
|
14
|
+
@arg << arg
|
15
|
+
else
|
16
|
+
raise Exception.new("Cannot add #{arg} to TypedMethod '#{name}' (no type and not a TypedString)")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_hash(key, value, klass_or_opts)
|
21
|
+
if last_is_hash?
|
22
|
+
hash = @args.last
|
23
|
+
else
|
24
|
+
hash = TypedString.new
|
25
|
+
@args << hash
|
26
|
+
end
|
27
|
+
|
28
|
+
hash.set_hash(key, TypedString.new(value, klass_or_opts))
|
29
|
+
end
|
30
|
+
|
31
|
+
def last_is_hash?
|
32
|
+
@args.last.kind_of?(TypedString) && !@args.last.hash.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
if @args.empty?
|
37
|
+
@name
|
38
|
+
elsif last_is_hash?
|
39
|
+
args = @args[0..-2].map(&:to_s)
|
40
|
+
hash = @args.last
|
41
|
+
hash.rebuild_hash
|
42
|
+
hash = hash.to_s[1..-2]
|
43
|
+
args += [hash]
|
44
|
+
"#{@name}(#{args.join(', ')})"
|
45
|
+
else
|
46
|
+
"#{@name}(#{@args.join(', ')})"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,29 +1,29 @@
|
|
1
1
|
module RubyLess
|
2
|
-
|
2
|
+
|
3
3
|
# This is a special kind of string containing ruby code that retains some information from the
|
4
4
|
# elements that compose it.
|
5
5
|
class TypedString < String
|
6
6
|
attr_reader :klass, :opts
|
7
7
|
|
8
|
-
def initialize(content = "",
|
9
|
-
|
8
|
+
def initialize(content = "", klass_or_opts = nil)
|
9
|
+
klass_or_opts ||= {:class => String}
|
10
10
|
replace(content)
|
11
|
-
@opts =
|
11
|
+
@opts = klass_or_opts.kind_of?(Hash) ? klass_or_opts.dup : {:class => klass_or_opts}
|
12
12
|
if could_be_nil? && !@opts[:cond]
|
13
13
|
@opts[:cond] = [self.to_s]
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
# Resulting class of the evaluated ruby code if it is not nil.
|
18
18
|
def klass
|
19
19
|
@opts[:class]
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
# Returns true if the evaluation of the ruby code represented by the string could be 'nil'.
|
23
23
|
def could_be_nil?
|
24
24
|
@opts[:nil]
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
# Condition that could yield a nil result in the whole expression.
|
28
28
|
# For example in the following expression:
|
29
29
|
# node.spouse.name == ''
|
@@ -31,15 +31,39 @@ module RubyLess
|
|
31
31
|
def cond
|
32
32
|
@opts[:cond]
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
|
+
# Return the literal value (string before inspect, number)
|
36
|
+
def literal
|
37
|
+
@opts[:literal]
|
38
|
+
end
|
39
|
+
|
40
|
+
# List of typed_strings that form the argument list. This is only used
|
41
|
+
# to resolve nil when the receiver of the arguments accepts nil values.
|
42
|
+
def list
|
43
|
+
@list ||= self.empty? ? [] : [self]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Hash arguments. This is only used to resolve parameter insertion with
|
47
|
+
# append_hash.
|
48
|
+
def hash
|
49
|
+
@hash ||= {}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Used to keep hash order (this is useful for testing).
|
53
|
+
def keys
|
54
|
+
@hash_keys ||= []
|
55
|
+
end
|
56
|
+
|
35
57
|
# raw result without nil checking:
|
36
58
|
# "node.spouse.name" instead of "(node.spouse ? node.spouse.name : nil)"
|
37
59
|
def raw
|
38
60
|
@opts[:raw] || self.to_s
|
39
61
|
end
|
40
|
-
|
62
|
+
|
41
63
|
# Append a typed string to build an argument list
|
42
64
|
def append_argument(typed_string)
|
65
|
+
self.list << typed_string
|
66
|
+
|
43
67
|
append_opts(typed_string)
|
44
68
|
if self.empty?
|
45
69
|
replace(typed_string.raw)
|
@@ -47,7 +71,28 @@ module RubyLess
|
|
47
71
|
replace("#{self.raw}, #{typed_string.raw}")
|
48
72
|
end
|
49
73
|
end
|
50
|
-
|
74
|
+
|
75
|
+
def rebuild_arguments
|
76
|
+
replace(list.map {|arg| arg.raw}.join(', ')) if @list
|
77
|
+
end
|
78
|
+
|
79
|
+
def set_hash(key, value)
|
80
|
+
self.hash[key] = value
|
81
|
+
self.keys << key unless self.keys.include?(key)
|
82
|
+
@opts[:class] = {} unless self.klass.kind_of?(Hash)
|
83
|
+
self.klass[key] = value.klass
|
84
|
+
end
|
85
|
+
|
86
|
+
def rebuild_hash
|
87
|
+
if @hash
|
88
|
+
result = []
|
89
|
+
@hash_keys.each do |k|
|
90
|
+
result << "#{k.inspect} => #{@hash[k]}"
|
91
|
+
end
|
92
|
+
replace "{#{result.join(', ')}}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
51
96
|
private
|
52
97
|
def append_opts(typed_string)
|
53
98
|
if self.empty?
|
@@ -61,7 +106,7 @@ module RubyLess
|
|
61
106
|
append_cond(typed_string.cond) if typed_string.could_be_nil?
|
62
107
|
end
|
63
108
|
end
|
64
|
-
|
109
|
+
|
65
110
|
def append_cond(condition)
|
66
111
|
@opts[:cond] ||= []
|
67
112
|
@opts[:cond] += [condition].flatten
|
data/lib/ruby_less.rb
CHANGED
@@ -1,27 +1,40 @@
|
|
1
1
|
=begin rdoc
|
2
2
|
=end
|
3
3
|
require 'ruby_less/info'
|
4
|
-
require 'ruby_less/basic_types'
|
5
4
|
require 'ruby_less/signature_hash'
|
6
5
|
require 'ruby_less/error'
|
7
6
|
require 'ruby_less/no_method_error'
|
8
7
|
require 'ruby_less/syntax_error'
|
9
8
|
require 'ruby_less/typed_string'
|
9
|
+
require 'ruby_less/typed_method'
|
10
10
|
require 'ruby_less/safe_class'
|
11
11
|
require 'ruby_less/processor'
|
12
12
|
|
13
13
|
module RubyLess
|
14
|
+
def self.included(base)
|
15
|
+
base.class_eval do
|
16
|
+
include SafeClass
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return method type (options) if the given signature is a safe method for the class.
|
21
|
+
def self.safe_method_type_for(klass, signature)
|
22
|
+
SafeClass.safe_method_type_for(klass, signature)
|
23
|
+
end
|
24
|
+
|
14
25
|
def self.translate(string, helper)
|
15
26
|
RubyLessProcessor.translate(string, helper)
|
16
27
|
end
|
17
28
|
|
18
29
|
def self.translate_string(string, helper)
|
19
30
|
if string =~ /\#\{/
|
20
|
-
|
31
|
+
translate("%Q{#{string}}", helper)
|
21
32
|
else
|
22
|
-
string.inspect
|
33
|
+
TypedString.new(string.inspect, :class => String, :literal => string)
|
23
34
|
end
|
24
35
|
rescue => err
|
25
|
-
raise
|
36
|
+
raise RubyLess::Error.new("Error parsing string \"#{string}\": #{err.message.strip}")
|
26
37
|
end
|
27
38
|
end
|
39
|
+
|
40
|
+
require 'ruby_less/basic_types'
|
data/rubyless.gemspec
CHANGED
@@ -5,21 +5,23 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rubyless}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
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{2010-
|
12
|
+
s.date = %q{2010-05-27}
|
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
|
-
"README.rdoc"
|
16
|
+
"README.rdoc",
|
17
|
+
"TODO"
|
17
18
|
]
|
18
19
|
s.files = [
|
19
20
|
".gitignore",
|
20
21
|
"History.txt",
|
21
22
|
"README.rdoc",
|
22
23
|
"Rakefile",
|
24
|
+
"TODO",
|
23
25
|
"lib/ruby_less.rb",
|
24
26
|
"lib/ruby_less/basic_types.rb",
|
25
27
|
"lib/ruby_less/error.rb",
|
@@ -29,6 +31,7 @@ Gem::Specification.new do |s|
|
|
29
31
|
"lib/ruby_less/safe_class.rb",
|
30
32
|
"lib/ruby_less/signature_hash.rb",
|
31
33
|
"lib/ruby_less/syntax_error.rb",
|
34
|
+
"lib/ruby_less/typed_method.rb",
|
32
35
|
"lib/ruby_less/typed_string.rb",
|
33
36
|
"lib/rubyless.rb",
|
34
37
|
"rails/init.rb",
|
@@ -36,6 +39,7 @@ Gem::Specification.new do |s|
|
|
36
39
|
"test/RubyLess/active_record.yml",
|
37
40
|
"test/RubyLess/basic.yml",
|
38
41
|
"test/RubyLess/errors.yml",
|
42
|
+
"test/RubyLess/hash.yml",
|
39
43
|
"test/RubyLess/string.yml",
|
40
44
|
"test/RubyLess_test.rb",
|
41
45
|
"test/mock/active_record_mock.rb",
|
@@ -44,7 +48,9 @@ Gem::Specification.new do |s|
|
|
44
48
|
"test/mock/property_column.rb",
|
45
49
|
"test/safe_class_test.rb",
|
46
50
|
"test/signature_hash_test.rb",
|
47
|
-
"test/test_helper.rb"
|
51
|
+
"test/test_helper.rb",
|
52
|
+
"test/typed_method_test.rb",
|
53
|
+
"test/typed_string_test.rb"
|
48
54
|
]
|
49
55
|
s.homepage = %q{http://zenadmin.org/546}
|
50
56
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -59,7 +65,9 @@ Gem::Specification.new do |s|
|
|
59
65
|
"test/RubyLess_test.rb",
|
60
66
|
"test/safe_class_test.rb",
|
61
67
|
"test/signature_hash_test.rb",
|
62
|
-
"test/test_helper.rb"
|
68
|
+
"test/test_helper.rb",
|
69
|
+
"test/typed_method_test.rb",
|
70
|
+
"test/typed_string_test.rb"
|
63
71
|
]
|
64
72
|
|
65
73
|
if s.respond_to? :specification_version then
|
@@ -70,18 +78,18 @@ Gem::Specification.new do |s|
|
|
70
78
|
s.add_runtime_dependency(%q<ruby_parser>, [">= 2.0.4"])
|
71
79
|
s.add_runtime_dependency(%q<sexp_processor>, [">= 3.0.1"])
|
72
80
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
73
|
-
s.add_development_dependency(%q<yamltest>, [">= 0.
|
81
|
+
s.add_development_dependency(%q<yamltest>, [">= 0.6.0"])
|
74
82
|
else
|
75
83
|
s.add_dependency(%q<ruby_parser>, [">= 2.0.4"])
|
76
84
|
s.add_dependency(%q<sexp_processor>, [">= 3.0.1"])
|
77
85
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
78
|
-
s.add_dependency(%q<yamltest>, [">= 0.
|
86
|
+
s.add_dependency(%q<yamltest>, [">= 0.6.0"])
|
79
87
|
end
|
80
88
|
else
|
81
89
|
s.add_dependency(%q<ruby_parser>, [">= 2.0.4"])
|
82
90
|
s.add_dependency(%q<sexp_processor>, [">= 3.0.1"])
|
83
91
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
84
|
-
s.add_dependency(%q<yamltest>, [">= 0.
|
92
|
+
s.add_dependency(%q<yamltest>, [">= 0.6.0"])
|
85
93
|
end
|
86
94
|
end
|
87
95
|
|
data/test/RubyLess/basic.yml
CHANGED
@@ -22,10 +22,6 @@ symbol:
|
|
22
22
|
src: ":foobar"
|
23
23
|
sxp: 's(:lit, :foobar)'
|
24
24
|
|
25
|
-
hash_access:
|
26
|
-
src: "dictionary[:key]"
|
27
|
-
tem: "get_dict[:key]"
|
28
|
-
|
29
25
|
rewrite_variables:
|
30
26
|
src: "!prev.ancestor?(main) && !node.ancestor?(main)"
|
31
27
|
tem: "(not previous.ancestor?(@node) and not node.ancestor?(@node))"
|
@@ -138,4 +134,54 @@ match_on_subclass:
|
|
138
134
|
|
139
135
|
optional_argument_subclass:
|
140
136
|
src: "width(:mode => str)"
|
141
|
-
tem: "node.width({:mode => str})"
|
137
|
+
tem: "node.width({:mode => str})"
|
138
|
+
|
139
|
+
simple_accept_nil:
|
140
|
+
src: "accept_nil(dictionary[:foo])"
|
141
|
+
tem: "accept_nil(get_dict[:foo])"
|
142
|
+
|
143
|
+
accept_nil_many_arguments:
|
144
|
+
src: "accept_nil(dictionary[:foo], dictionary[:bar])"
|
145
|
+
tem: "accept_nil(get_dict[:foo], get_dict[:bar])"
|
146
|
+
res: "[\"Foo\", nil]"
|
147
|
+
|
148
|
+
accept_nil_many_arguments_no_all_nil:
|
149
|
+
src: "accept_nil('hop', dictionary[:bar])"
|
150
|
+
tem: "accept_nil(\"hop\", get_dict[:bar])"
|
151
|
+
res: "[\"hop\", nil]"
|
152
|
+
|
153
|
+
no_nil_in_accept_nil:
|
154
|
+
src: "accept_nil(no_nil(dictionary[:foo]))"
|
155
|
+
tem: "accept_nil((get_dict[:foo] ? no_nil(get_dict[:foo]) : nil))"
|
156
|
+
res: "[\"Foo\", nil]"
|
157
|
+
|
158
|
+
two_no_nil_in_accept_nil:
|
159
|
+
src: "accept_nil(no_nil(dictionary[:foo]), no_nil(dictionary[:bar]))"
|
160
|
+
tem: "accept_nil((get_dict[:foo] ? no_nil(get_dict[:foo]) : nil), (get_dict[:bar] ? no_nil(get_dict[:bar]) : nil))"
|
161
|
+
res: "[\"Foo\", nil]"
|
162
|
+
|
163
|
+
accept_nil_in_no_nil:
|
164
|
+
src: "no_nil(accept_nil(dictionary[:foo]))"
|
165
|
+
tem: "no_nil(accept_nil(get_dict[:foo]))"
|
166
|
+
|
167
|
+
parse_array:
|
168
|
+
src: "[3, 6]"
|
169
|
+
tem: '[3,6]'
|
170
|
+
|
171
|
+
noop_method:
|
172
|
+
src: 'no_op("hello")'
|
173
|
+
tem: '("hello")'
|
174
|
+
res: 'hello'
|
175
|
+
|
176
|
+
noop_other_signature:
|
177
|
+
src: 'no_op(45)'
|
178
|
+
tem: 'transform(45)'
|
179
|
+
|
180
|
+
build_finder:
|
181
|
+
# This test shows a way to 'pre_process' literal content with a helper method that should return a TypedString
|
182
|
+
src: 'find("one two")'
|
183
|
+
tem: 'secure(Node) { Node.find("one two") }'
|
184
|
+
|
185
|
+
methods_on_nil:
|
186
|
+
src: 'dictionary[:foo].blank?'
|
187
|
+
tem: 'get_dict[:foo].blank?'
|
data/test/RubyLess/errors.yml
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
unknown_global_method:
|
2
2
|
src: "system('echo date')"
|
3
|
-
res: "
|
3
|
+
res: "unknown method 'system(String)' for RubyLessTest."
|
4
4
|
|
5
5
|
bad_argument_types:
|
6
6
|
src: "strftime(34,'ffoo')"
|
7
|
-
res: "
|
7
|
+
res: "unknown method 'strftime(Number, String)' for RubyLessTest."
|
8
8
|
|
9
9
|
zero_div:
|
10
10
|
src: "1/(id-10)"
|
@@ -17,23 +17,23 @@ looping:
|
|
17
17
|
|
18
18
|
add_two_strings:
|
19
19
|
src: "name + 14"
|
20
|
-
res: "
|
20
|
+
res: "unknown method '+(Number)' for 'node.name' of type String."
|
21
21
|
|
22
22
|
two_arguments_in_hash:
|
23
23
|
src: "dictionary[:one, :two]"
|
24
|
-
res: "
|
24
|
+
res: "unknown method '[](Symbol, Symbol)' for 'get_dict' of type StringDictionary."
|
25
25
|
|
26
26
|
number_argument:
|
27
27
|
src: "dictionary[43]"
|
28
|
-
res: "
|
28
|
+
res: "unknown method '[](Number)' for 'get_dict' of type StringDictionary."
|
29
29
|
|
30
30
|
string_argument:
|
31
31
|
src: "dictionary[spouse.name]"
|
32
|
-
res: "
|
32
|
+
res: "unknown method '[](String)' for 'get_dict' of type StringDictionary."
|
33
33
|
|
34
34
|
symbol_type_not_used_out_of_helper:
|
35
35
|
src: "node.foo"
|
36
|
-
tem: "
|
36
|
+
tem: "unknown method 'foo()' for 'node' of type Dummy."
|
37
37
|
|
38
38
|
optional_arguments_with_dynamic_string:
|
39
39
|
src: "spouse.width(\"nice#{spouse.name}\" => 'pv')"
|
@@ -42,8 +42,16 @@ optional_arguments_with_dynamic_string:
|
|
42
42
|
|
43
43
|
optional_arguments_bad_type:
|
44
44
|
src: "width(:mode => 12)"
|
45
|
-
res: "
|
45
|
+
res: "unknown method 'width(:mode=>Number)' for RubyLessTest."
|
46
46
|
|
47
47
|
optional_arguments_bad_argument:
|
48
48
|
src: "width(:xyz => 'pv')"
|
49
|
-
res: "
|
49
|
+
res: "unknown method 'width(:xyz=>String)' for RubyLessTest."
|
50
|
+
|
51
|
+
hash_arguments_wrong_type:
|
52
|
+
src: "hash_args('name' => 45)"
|
53
|
+
tem: "unknown method 'hash_args(\"name\"=>Number)' for RubyLessTest."
|
54
|
+
|
55
|
+
mixed_array:
|
56
|
+
src: "[3, '4']"
|
57
|
+
tem: 'Mixed Array not supported ([Number,String]).'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
parse_hash:
|
2
|
+
src: "{'one' => 1, 2 => 'two'}"
|
3
|
+
tem: "{\"one\" => 1, 2 => \"two\"}"
|
4
|
+
|
5
|
+
hash_access:
|
6
|
+
src: "dictionary[:key]"
|
7
|
+
tem: "get_dict[:key]"
|
8
|
+
|
9
|
+
append_hash:
|
10
|
+
src: "append_hash(5, 'foo' => 'Foo')"
|
11
|
+
tem: "add(5, {\"foo\" => \"Foo\", :xyz => bar})"
|
12
|
+
|
13
|
+
append_hash_on_empty:
|
14
|
+
src: "append_hash(5)"
|
15
|
+
tem: "add(5, {:xyz => bar})"
|
16
|
+
|
17
|
+
hash_arguments:
|
18
|
+
src: "hash_args('name' => str, 'age' => 13)"
|
19
|
+
tem: "hash_args({\"name\" => str, \"age\" => 13})"
|
data/test/RubyLess/string.yml
CHANGED
@@ -9,3 +9,20 @@ text:
|
|
9
9
|
dynamic_string:
|
10
10
|
str: 'one #{name} two'
|
11
11
|
tem: '"one #{node.name} two"'
|
12
|
+
|
13
|
+
concat_static:
|
14
|
+
src: "concat('hello ', 'World!')"
|
15
|
+
tem: '"hello World!"'
|
16
|
+
|
17
|
+
gsub_static:
|
18
|
+
src: "'one.one'.gsub(/\\./,';')"
|
19
|
+
tem: '"one;one"'
|
20
|
+
|
21
|
+
upcase_static:
|
22
|
+
src: "'one.one'.gsub(/\\./,';').upcase"
|
23
|
+
tem: '"ONE;ONE"'
|
24
|
+
|
25
|
+
equal:
|
26
|
+
src: "str == 'str'"
|
27
|
+
tem: '(str=="str")'
|
28
|
+
res: 'true'
|
data/test/RubyLess_test.rb
CHANGED
@@ -1,12 +1,6 @@
|
|
1
1
|
require 'date'
|
2
2
|
require 'test_helper'
|
3
3
|
|
4
|
-
class StringDictionary
|
5
|
-
include RubyLess::SafeClass
|
6
|
-
safe_method ['[]', Symbol] => {:class => String, :nil => true}
|
7
|
-
disable_safe_read
|
8
|
-
end
|
9
|
-
|
10
4
|
# Used to test sub-classes in optional arguments
|
11
5
|
class SubString < String
|
12
6
|
end
|
@@ -14,7 +8,7 @@ end
|
|
14
8
|
class RubyLessTest < Test::Unit::TestCase
|
15
9
|
attr_reader :context
|
16
10
|
yamltest :src_from_title => false
|
17
|
-
include RubyLess
|
11
|
+
include RubyLess
|
18
12
|
safe_method :prev => {:class => Dummy, :method => 'previous'}
|
19
13
|
safe_method :main => {:class => Dummy, :method => '@node'}
|
20
14
|
safe_method :node => lambda {|h, s| {:class => h.context[:node_class], :method => h.context[:node]}}
|
@@ -24,14 +18,34 @@ class RubyLessTest < Test::Unit::TestCase
|
|
24
18
|
safe_method [:vowel_count, String] => Number
|
25
19
|
safe_method [:log_info, Dummy, String] => String
|
26
20
|
safe_method :foo => :contextual_method, :bar => :contextual_method
|
21
|
+
|
27
22
|
safe_method_for String, [:==, String] => Boolean
|
28
23
|
safe_method_for String, [:to_s] => String
|
29
|
-
safe_method_for String, [:gsub, Regexp, String] => String
|
24
|
+
safe_method_for String, [:gsub, Regexp, String] => {:class => String, :pre_processor => Proc.new {|this, reg, str| this.gsub(reg, str)}}
|
25
|
+
safe_method_for String, :upcase => {:class => String, :pre_processor => true}
|
26
|
+
|
30
27
|
safe_method_for Time, [:strftime, String] => String
|
28
|
+
|
31
29
|
safe_method :@foo => {:class => Dummy, :method => "node"}
|
32
30
|
safe_method :sub => SubDummy
|
33
31
|
safe_method :str => SubString
|
34
32
|
|
33
|
+
safe_method [:accept_nil, String] => {:class => String, :accept_nil => true}
|
34
|
+
safe_method [:accept_nil, String, String] => {:class => String, :accept_nil => true}
|
35
|
+
safe_method [:no_nil, String] => String
|
36
|
+
|
37
|
+
safe_method [:no_op, String] => {:class => String, :method => ''}
|
38
|
+
safe_method [:no_op, Number] => {:class => String, :method => 'transform'}
|
39
|
+
|
40
|
+
safe_method [:hash_args, {'age' => Number, 'name' => String}] => String
|
41
|
+
safe_method [:append_hash, Number, {'foo' => String}] => :make_append_hash
|
42
|
+
|
43
|
+
safe_method [:concat, String, String] => {:class => String, :pre_processor => Proc.new{|a,b| a + b }}
|
44
|
+
safe_method [:find, String] => {:class => NilClass, :method => 'nil', :pre_processor => :build_finder}
|
45
|
+
|
46
|
+
# methods on nil
|
47
|
+
safe_method_for Object, :blank? => Boolean
|
48
|
+
|
35
49
|
# Example to dynamically rewrite method calls during compilation
|
36
50
|
def safe_method_type(signature)
|
37
51
|
unless res = super
|
@@ -46,6 +60,26 @@ class RubyLessTest < Test::Unit::TestCase
|
|
46
60
|
res
|
47
61
|
end
|
48
62
|
|
63
|
+
def make_append_hash(signature = {})
|
64
|
+
{:class => Number, :append_hash => {:xyz => RubyLess::TypedString.new('bar', :class => Dummy)}, :method => 'add'}
|
65
|
+
end
|
66
|
+
|
67
|
+
def build_finder(string)
|
68
|
+
TypedString.new("secure(Node) { Node.find(#{string.inspect}) }", :class => Dummy )
|
69
|
+
end
|
70
|
+
|
71
|
+
def accept_nil(foo, bar=nil)
|
72
|
+
[foo, bar].inspect
|
73
|
+
end
|
74
|
+
|
75
|
+
def no_nil(foo)
|
76
|
+
foo
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_dict
|
80
|
+
{:foo => 'Foo'}
|
81
|
+
end
|
82
|
+
|
49
83
|
def contextual_method(signature)
|
50
84
|
{:method => "contextual_#{signature[0]}", :class => String}
|
51
85
|
end
|
data/test/mock/dummy_class.rb
CHANGED
@@ -4,7 +4,7 @@ require File.dirname(__FILE__) + '/property_column'
|
|
4
4
|
|
5
5
|
class Dummy < RubyLess::ActiveRecordMock
|
6
6
|
include DummyModule
|
7
|
-
include RubyLess
|
7
|
+
include RubyLess
|
8
8
|
|
9
9
|
attr_reader :name
|
10
10
|
|
@@ -16,8 +16,7 @@ class Dummy < RubyLess::ActiveRecordMock
|
|
16
16
|
:id => {:class => Number, :method => :zip},
|
17
17
|
:name => String,
|
18
18
|
:foo => :bar,
|
19
|
-
[:width, {:mode => String, :type => String, 'nice' => Boolean}] => String
|
20
|
-
[:width] => String
|
19
|
+
[:width, {:mode => String, :type => String, 'nice' => Boolean}] => String
|
21
20
|
safe_context :spouse => 'Dummy',
|
22
21
|
:husband => {:class => 'Dummy', :context => {:clever => 'no'}}
|
23
22
|
|
data/test/mock/dummy_module.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TypedMethodTest < Test::Unit::TestCase
|
4
|
+
TypedMethod = RubyLess::TypedMethod
|
5
|
+
|
6
|
+
context 'A method' do
|
7
|
+
subject do
|
8
|
+
TypedMethod.new('foo')
|
9
|
+
end
|
10
|
+
|
11
|
+
should 'render with name' do
|
12
|
+
assert_equal 'foo', subject.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'accept new arguments with add_argument' do
|
16
|
+
assert_nothing_raised { subject.add_argument(1, Number) }
|
17
|
+
end
|
18
|
+
|
19
|
+
should 'accept new hash arguments' do
|
20
|
+
subject.set_hash(:foo, 'bar', Dummy)
|
21
|
+
assert_equal 'foo(:foo => bar)', subject.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with arguments' do
|
25
|
+
subject do
|
26
|
+
TypedMethod.new('foo', '1', '2')
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'render with arguments' do
|
30
|
+
assert_equal 'foo(1, 2)', subject.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with a hash as last argument' do
|
35
|
+
subject do
|
36
|
+
m = TypedMethod.new('foo')
|
37
|
+
m.add_argument('1', Number)
|
38
|
+
m.set_hash(:foo, 'bar', Dummy)
|
39
|
+
m
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'use ruby last hash syntax' do
|
43
|
+
assert_equal 'foo(1, :foo => bar)', subject.to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TypedStringTest < Test::Unit::TestCase
|
4
|
+
TypedString = RubyLess::TypedString
|
5
|
+
|
6
|
+
context 'A typed string' do
|
7
|
+
subject do
|
8
|
+
TypedString.new('foo', Number)
|
9
|
+
end
|
10
|
+
|
11
|
+
should 'render with name' do
|
12
|
+
assert_equal 'foo', subject.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'return class of content on klass' do
|
16
|
+
assert_equal Number, subject.klass
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 5
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.5.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Gaspard Bucher
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-05-27 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -66,9 +66,9 @@ dependencies:
|
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
segments:
|
68
68
|
- 0
|
69
|
-
-
|
70
|
-
-
|
71
|
-
version: 0.
|
69
|
+
- 6
|
70
|
+
- 0
|
71
|
+
version: 0.6.0
|
72
72
|
type: :development
|
73
73
|
version_requirements: *id004
|
74
74
|
description: 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.
|
@@ -79,11 +79,13 @@ extensions: []
|
|
79
79
|
|
80
80
|
extra_rdoc_files:
|
81
81
|
- README.rdoc
|
82
|
+
- TODO
|
82
83
|
files:
|
83
84
|
- .gitignore
|
84
85
|
- History.txt
|
85
86
|
- README.rdoc
|
86
87
|
- Rakefile
|
88
|
+
- TODO
|
87
89
|
- lib/ruby_less.rb
|
88
90
|
- lib/ruby_less/basic_types.rb
|
89
91
|
- lib/ruby_less/error.rb
|
@@ -93,6 +95,7 @@ files:
|
|
93
95
|
- lib/ruby_less/safe_class.rb
|
94
96
|
- lib/ruby_less/signature_hash.rb
|
95
97
|
- lib/ruby_less/syntax_error.rb
|
98
|
+
- lib/ruby_less/typed_method.rb
|
96
99
|
- lib/ruby_less/typed_string.rb
|
97
100
|
- lib/rubyless.rb
|
98
101
|
- rails/init.rb
|
@@ -100,6 +103,7 @@ files:
|
|
100
103
|
- test/RubyLess/active_record.yml
|
101
104
|
- test/RubyLess/basic.yml
|
102
105
|
- test/RubyLess/errors.yml
|
106
|
+
- test/RubyLess/hash.yml
|
103
107
|
- test/RubyLess/string.yml
|
104
108
|
- test/RubyLess_test.rb
|
105
109
|
- test/mock/active_record_mock.rb
|
@@ -109,6 +113,8 @@ files:
|
|
109
113
|
- test/safe_class_test.rb
|
110
114
|
- test/signature_hash_test.rb
|
111
115
|
- test/test_helper.rb
|
116
|
+
- test/typed_method_test.rb
|
117
|
+
- test/typed_string_test.rb
|
112
118
|
has_rdoc: true
|
113
119
|
homepage: http://zenadmin.org/546
|
114
120
|
licenses: []
|
@@ -148,3 +154,5 @@ test_files:
|
|
148
154
|
- test/safe_class_test.rb
|
149
155
|
- test/signature_hash_test.rb
|
150
156
|
- test/test_helper.rb
|
157
|
+
- test/typed_method_test.rb
|
158
|
+
- test/typed_string_test.rb
|