rubyless 0.4.0 → 0.5.0
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 +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
|