rubyless 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +11 -0
- data/lib/ruby_less/basic_types.rb +11 -0
- data/lib/ruby_less/info.rb +1 -1
- data/lib/ruby_less/processor.rb +25 -5
- data/lib/ruby_less/safe_class.rb +121 -106
- data/lib/ruby_less.rb +12 -2
- data/rubyless.gemspec +3 -2
- data/test/RubyLess/basic.yml +19 -2
- data/test/RubyLess/errors.yml +20 -0
- data/test/RubyLess/time.yml +15 -0
- data/test/RubyLess_test.rb +13 -1
- data/test/mock/active_record_mock.rb +8 -4
- data/test/mock/dummy_class.rb +4 -1
- data/test/safe_class_test.rb +28 -1
- metadata +4 -3
data/History.txt
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
== 0.6.0 2010-07-22
|
2
|
+
|
3
|
+
* Major enhancements
|
4
|
+
* Added 'safe_eval' method.
|
5
|
+
* Added 'safe_eval_string' method.
|
6
|
+
* Better handling of syntax errors.
|
7
|
+
* Support for constants.
|
8
|
+
|
9
|
+
* Minor enhancements
|
10
|
+
* Added safe methods on Time (compare, add/subtract number).
|
11
|
+
|
1
12
|
== 0.5.0 2010-05-27
|
2
13
|
|
3
14
|
* Major enhancements
|
@@ -22,6 +22,17 @@ RubyLess::SafeClass.safe_method_for( Number,
|
|
22
22
|
[:% , Number] => Number, [:"-@"] => Number
|
23
23
|
)
|
24
24
|
|
25
|
+
RubyLess::SafeClass.safe_method_for( Time,
|
26
|
+
[:==, Time] => Boolean, [:< , Time] => Boolean, [:> , Time] => Boolean,
|
27
|
+
[:<=, Time] => Boolean, [:>=, Time] => Boolean,
|
28
|
+
[:- , Number] => Time, [:+ , Number] => Time
|
29
|
+
)
|
30
|
+
|
25
31
|
RubyLess::SafeClass.safe_method_for( String,
|
26
32
|
[:==, String] => Boolean
|
27
33
|
)
|
34
|
+
|
35
|
+
RubyLess::SafeClass.safe_method_for( NilClass,
|
36
|
+
[:==, String] => Boolean,
|
37
|
+
[:==, Number] => Boolean
|
38
|
+
)
|
data/lib/ruby_less/info.rb
CHANGED
data/lib/ruby_less/processor.rb
CHANGED
@@ -10,8 +10,13 @@ module RubyLess
|
|
10
10
|
PREFIX_OPERATOR = ['-@']
|
11
11
|
|
12
12
|
def self.translate(string, helper)
|
13
|
-
sexp = RubyParser.new.parse(string)
|
14
|
-
|
13
|
+
if sexp = RubyParser.new.parse(string)
|
14
|
+
self.new(helper).process(sexp)
|
15
|
+
elsif string.size == 0
|
16
|
+
''
|
17
|
+
else
|
18
|
+
raise RubyLess::SyntaxError.new("Syntax error")
|
19
|
+
end
|
15
20
|
rescue Racc::ParseError => err
|
16
21
|
raise RubyLess::SyntaxError.new(err.message)
|
17
22
|
end
|
@@ -38,12 +43,26 @@ module RubyLess
|
|
38
43
|
# send("process_#{method}", exp)
|
39
44
|
end
|
40
45
|
|
46
|
+
def process_const(exp)
|
47
|
+
const_name = exp.pop.to_s
|
48
|
+
if opts = @helper.respond_to?(:safe_const_type) ? @helper.safe_const_type(const_name) : nil
|
49
|
+
t opts[:method], opts
|
50
|
+
else
|
51
|
+
raise RubyLess::Error.new("Unknown constant '#{const_name}'.")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
41
55
|
def process_and(exp)
|
42
56
|
t "(#{process(exp.shift)} and #{process(exp.shift)})", Boolean
|
43
57
|
end
|
44
58
|
|
45
59
|
def process_or(exp)
|
46
|
-
|
60
|
+
left, right = process(exp.shift), process(exp.shift)
|
61
|
+
if left.klass == right.klass
|
62
|
+
t "(#{left} or #{right})", :class => right.klass, :nil => right.could_be_nil?
|
63
|
+
else
|
64
|
+
t "(#{left} or #{right})", Boolean
|
65
|
+
end
|
47
66
|
end
|
48
67
|
|
49
68
|
def process_not(exp)
|
@@ -109,7 +128,7 @@ module RubyLess
|
|
109
128
|
def process_vcall(exp)
|
110
129
|
var_name = exp.shift
|
111
130
|
unless opts = get_method([var_name], @helper, false)
|
112
|
-
raise RubyLess::
|
131
|
+
raise RubyLess::Error.new("Unknown variable or method '#{var_name}'.")
|
113
132
|
end
|
114
133
|
method = opts[:method]
|
115
134
|
if args = opts[:prepend_args]
|
@@ -258,7 +277,8 @@ module RubyLess
|
|
258
277
|
method = opts[:method]
|
259
278
|
arg_list = args ? args.list : []
|
260
279
|
|
261
|
-
if receiver.could_be_nil? &&
|
280
|
+
if receiver.could_be_nil? &&
|
281
|
+
!(opts == SafeClass.safe_method_type_for(NilClass, signature) && receiver.cond == [receiver])
|
262
282
|
# Do not add a condition if the method applies on nil
|
263
283
|
cond += receiver.cond
|
264
284
|
elsif receiver.literal && (proc = opts[:pre_processor]) && !arg_list.detect {|a| !a.literal}
|
data/lib/ruby_less/safe_class.rb
CHANGED
@@ -73,125 +73,127 @@ module RubyLess
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
#
|
93
|
-
# If your method accepts variable arguments through a Hash, you should declare it with:
|
94
|
-
# [:img, String, {:mode => String, :max_size => Number}]
|
95
|
-
#
|
96
|
-
# Make sure your literal values are of the right type: +:mode+ and +'mode'+ are not the same here.
|
97
|
-
#
|
98
|
-
# If the signature is :defaults, the options defined are used as defaults for the other elements defined in the
|
99
|
-
# same call.
|
100
|
-
#
|
101
|
-
# The return type can be a string with the class name or a class.
|
102
|
-
#
|
103
|
-
# Options are:
|
104
|
-
# :class the return type (class name)
|
105
|
-
# :nil set this to true if the method could return nil
|
106
|
-
def self.safe_method(methods_hash)
|
107
|
-
RubyLess::SafeClass.safe_method_for(self, methods_hash)
|
108
|
-
end
|
76
|
+
# Return a safe type from a column
|
77
|
+
def self.safe_method_type_for_column(col, is_property = false)
|
78
|
+
opts = {}
|
79
|
+
opts[:nil] = col.default.nil?
|
80
|
+
if col.number?
|
81
|
+
opts[:class] = Number
|
82
|
+
elsif col.text?
|
83
|
+
opts[:class] = String
|
84
|
+
else
|
85
|
+
opts[:class] = col.klass
|
86
|
+
end
|
87
|
+
if is_property
|
88
|
+
opts[:method] = "prop['#{col.name.gsub("'",'')}']"
|
89
|
+
else
|
90
|
+
opts[:method] = col.name
|
91
|
+
end
|
109
92
|
|
110
|
-
|
111
|
-
|
112
|
-
# as: if var = my_context(...) ---> enter context.
|
113
|
-
def self.safe_context(methods_hash)
|
114
|
-
methods_hash[:defaults] ||= {}
|
115
|
-
methods_hash[:defaults][:nil] = true
|
116
|
-
safe_method(methods_hash)
|
117
|
-
end
|
93
|
+
opts
|
94
|
+
end
|
118
95
|
|
119
|
-
|
120
|
-
RubyLess::SafeClass.safe_literal_class(hash)
|
121
|
-
end
|
96
|
+
module ClassMethods
|
122
97
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
98
|
+
# Declare safe methods. By providing
|
99
|
+
#
|
100
|
+
# The methods hash has the following format:
|
101
|
+
# signature => return type
|
102
|
+
# or
|
103
|
+
# signature => options
|
104
|
+
# or
|
105
|
+
# signature => lambda {|h| ... }
|
106
|
+
#
|
107
|
+
# The lambda expression will be called with @helper as argument during compilation.
|
108
|
+
#
|
109
|
+
# The signature can be either a single symbol or an array containing the method name and type arguments like:
|
110
|
+
# [:strftime, Time, String]
|
111
|
+
#
|
112
|
+
# If your method accepts variable arguments through a Hash, you should declare it with:
|
113
|
+
# [:img, String, {:mode => String, :max_size => Number}]
|
114
|
+
#
|
115
|
+
# Make sure your literal values are of the right type: +:mode+ and +'mode'+ are not the same here.
|
116
|
+
#
|
117
|
+
# If the signature is :defaults, the options defined are used as defaults for the other elements defined in the
|
118
|
+
# same call.
|
119
|
+
#
|
120
|
+
# The return type can be a string with the class name or a class.
|
121
|
+
#
|
122
|
+
# Options are:
|
123
|
+
# :class the return type (class name)
|
124
|
+
# :nil set this to true if the method could return nil
|
125
|
+
def safe_method(methods_hash)
|
126
|
+
RubyLess::SafeClass.safe_method_for(self, methods_hash)
|
127
|
+
end
|
128
|
+
|
129
|
+
# A safe context is simply a safe method that can return nil in some situations. The rest of the
|
130
|
+
# syntax is the same as #safe_method. We call it a safe context because it enables syntaxes such
|
131
|
+
# as: if var = my_context(...) ---> enter context.
|
132
|
+
def safe_context(methods_hash)
|
133
|
+
methods_hash[:defaults] ||= {}
|
134
|
+
methods_hash[:defaults][:nil] = true
|
135
|
+
safe_method(methods_hash)
|
136
|
+
end
|
137
|
+
|
138
|
+
def safe_literal_class(hash)
|
139
|
+
RubyLess::SafeClass.safe_literal_class(hash)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Declare a safe method to access a list of attributes.
|
143
|
+
# This method should only be used when the class is linked with a database table and provides
|
144
|
+
# proper introspection to detect types and the possibility of NULL values.
|
145
|
+
def safe_attribute(*attributes)
|
146
|
+
attributes.each do |att|
|
147
|
+
if col = columns_hash[att.to_s]
|
148
|
+
safe_method att.to_sym => SafeClass.safe_method_type_for_column(col)
|
149
|
+
else
|
150
|
+
puts "Warning: could not declare safe_attribute '#{att}' (No column with this name found in class #{self})"
|
142
151
|
end
|
143
152
|
end
|
153
|
+
end
|
144
154
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
opts[:class] = Number
|
155
|
-
elsif col.text?
|
156
|
-
opts[:class] = String
|
157
|
-
else
|
158
|
-
opts[:class] = col.klass
|
159
|
-
end
|
160
|
-
opts[:method] = "prop['#{att.to_s.gsub("'",'')}']"
|
161
|
-
safe_method att.to_sym => opts
|
162
|
-
else
|
163
|
-
puts "Warning: could not declare safe_property '#{att}' (No property column with this name found in class #{self})"
|
164
|
-
end
|
155
|
+
# Declare a safe method to access a list of properties.
|
156
|
+
# This method should only be used in conjunction with the Property gem.
|
157
|
+
def safe_property(*properties)
|
158
|
+
columns = schema.columns
|
159
|
+
properties.each do |att|
|
160
|
+
if col = columns[att.to_s]
|
161
|
+
safe_method att.to_sym => SafeClass.safe_method_type_for_column(col, true)
|
162
|
+
else
|
163
|
+
puts "Warning: could not declare safe_property '#{att}' (No property column with this name found in class #{self})"
|
165
164
|
end
|
166
165
|
end
|
166
|
+
end
|
167
167
|
|
168
|
+
# Declare a safe method for a given class
|
169
|
+
def safe_method_for(klass, signature)
|
170
|
+
SafeClass.safe_method_for(klass, signature)
|
171
|
+
end
|
168
172
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
+
# Hash of all safe methods defined for the class.
|
174
|
+
def safe_methods
|
175
|
+
SafeClass.safe_methods_for(self)
|
176
|
+
end
|
173
177
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
+
# Return the type if the given signature corresponds to a safe method for the class.
|
179
|
+
def safe_method_type(signature)
|
180
|
+
SafeClass.safe_method_type_for(self, signature)
|
181
|
+
end
|
178
182
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
+
# Return true if the class is safe (we can call safe_read on its instances)
|
184
|
+
def safe_class?
|
185
|
+
true
|
186
|
+
end
|
183
187
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
+
# Use this if you want to disable 'safe_read'. This is useful if you provided
|
189
|
+
# a mock as return type signature.
|
190
|
+
def disable_safe_read
|
191
|
+
undef_method(:safe_read)
|
192
|
+
end
|
193
|
+
end # ClassMethods
|
188
194
|
|
189
|
-
|
190
|
-
|
191
|
-
def self.disable_safe_read
|
192
|
-
undef_method(:safe_read)
|
193
|
-
end
|
194
|
-
end # base.class_eval
|
195
|
+
def self.included(base)
|
196
|
+
base.extend ClassMethods
|
195
197
|
end # included
|
196
198
|
|
197
199
|
|
@@ -203,12 +205,25 @@ module RubyLess
|
|
203
205
|
end
|
204
206
|
|
205
207
|
# Safe attribute reader used when 'safe_readable?' could not be called because the class
|
206
|
-
# is not known during compile time.
|
208
|
+
# is not known during compile time.
|
209
|
+
# FIXME: Is this used anymore ?
|
207
210
|
def safe_read(key)
|
208
211
|
return "'#{key}' not readable" unless type = self.class.safe_method_type([key])
|
209
212
|
self.send(type[:method])
|
210
213
|
end
|
211
214
|
|
215
|
+
# Evaluate a RubyLess expression. This is just like 'eval' but with safe method checking and typing.
|
216
|
+
def safe_eval(code)
|
217
|
+
ruby = RubyLessProcessor.translate(code, self)
|
218
|
+
eval(ruby)
|
219
|
+
end
|
220
|
+
|
221
|
+
# Evaluate a RubyLess expression. This is just like 'eval' but with safe method checking and typing.
|
222
|
+
def safe_eval_string(code)
|
223
|
+
ruby = RubyLess.translate_string(code, self)
|
224
|
+
eval(ruby)
|
225
|
+
end
|
226
|
+
|
212
227
|
private
|
213
228
|
def self.build_signature(key)
|
214
229
|
keys = key.kind_of?(Array) ? key : [key]
|
data/lib/ruby_less.rb
CHANGED
@@ -24,6 +24,12 @@ module RubyLess
|
|
24
24
|
|
25
25
|
def self.translate(string, helper)
|
26
26
|
RubyLessProcessor.translate(string, helper)
|
27
|
+
rescue Exception => err
|
28
|
+
if err.kind_of?(RubyLess::Error)
|
29
|
+
raise err
|
30
|
+
else
|
31
|
+
raise RubyLess::Error.new("Error parsing \"#{string}\": #{err.message.strip}")
|
32
|
+
end
|
27
33
|
end
|
28
34
|
|
29
35
|
def self.translate_string(string, helper)
|
@@ -32,8 +38,12 @@ module RubyLess
|
|
32
38
|
else
|
33
39
|
TypedString.new(string.inspect, :class => String, :literal => string)
|
34
40
|
end
|
35
|
-
rescue => err
|
36
|
-
|
41
|
+
rescue Exception => err
|
42
|
+
if err.kind_of?(RubyLess::Error)
|
43
|
+
raise err
|
44
|
+
else
|
45
|
+
raise RubyLess::Error.new("Error parsing string \"#{string}\": #{err.message.strip}")
|
46
|
+
end
|
37
47
|
end
|
38
48
|
end
|
39
49
|
|
data/rubyless.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rubyless}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.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-07-22}
|
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 = [
|
@@ -41,6 +41,7 @@ Gem::Specification.new do |s|
|
|
41
41
|
"test/RubyLess/errors.yml",
|
42
42
|
"test/RubyLess/hash.yml",
|
43
43
|
"test/RubyLess/string.yml",
|
44
|
+
"test/RubyLess/time.yml",
|
44
45
|
"test/RubyLess_test.rb",
|
45
46
|
"test/mock/active_record_mock.rb",
|
46
47
|
"test/mock/dummy_class.rb",
|
data/test/RubyLess/basic.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
empty:
|
2
2
|
src: ""
|
3
|
-
tem:
|
3
|
+
tem: ""
|
4
4
|
|
5
5
|
numbers:
|
6
6
|
src: "id > 45 and (3 > -id or 3+3)"
|
@@ -184,4 +184,21 @@ build_finder:
|
|
184
184
|
|
185
185
|
methods_on_nil:
|
186
186
|
src: 'dictionary[:foo].blank?'
|
187
|
-
tem: 'get_dict[:foo].blank?'
|
187
|
+
tem: 'get_dict[:foo].blank?'
|
188
|
+
|
189
|
+
equality_on_nil:
|
190
|
+
src: 'dictionary[:foo] == "yo"'
|
191
|
+
tem: '(get_dict[:foo]=="yo")'
|
192
|
+
|
193
|
+
or_same_class:
|
194
|
+
src: "vowel_count(dictionary[:bar] || 'aeiou')"
|
195
|
+
tem: 'vowel_count((get_dict[:bar] or "aeiou"))'
|
196
|
+
res: '5'
|
197
|
+
|
198
|
+
class:
|
199
|
+
src: "@foo.kind_of?(Page)"
|
200
|
+
tem: "node.kind_of?(Page)"
|
201
|
+
|
202
|
+
class_map_as_string:
|
203
|
+
src: "@foo.kind_of?(Document)"
|
204
|
+
tem: "node.is_like?(\"DOC\")"
|
data/test/RubyLess/errors.yml
CHANGED
@@ -55,3 +55,23 @@ hash_arguments_wrong_type:
|
|
55
55
|
mixed_array:
|
56
56
|
src: "[3, '4']"
|
57
57
|
tem: 'Mixed Array not supported ([Number,String]).'
|
58
|
+
|
59
|
+
hash_odd_keys_syntax_error:
|
60
|
+
src: '{text}'
|
61
|
+
tem: 'Syntax error'
|
62
|
+
|
63
|
+
syntax_error:
|
64
|
+
src: "3 * / * 5"
|
65
|
+
tem: "/nterminated string meets end of file/"
|
66
|
+
|
67
|
+
no_lasagna_in_rubyless:
|
68
|
+
src: "foo = system"
|
69
|
+
tem: "'lasgn' not available in RubyLess."
|
70
|
+
|
71
|
+
call_on_class:
|
72
|
+
src: "Page.name"
|
73
|
+
tem: "unknown method 'name()' for 'Page' of type Class."
|
74
|
+
|
75
|
+
unknown_constant:
|
76
|
+
src: "RubyLess"
|
77
|
+
tem: "Unknown constant 'RubyLess'."
|
data/test/RubyLess_test.rb
CHANGED
@@ -26,6 +26,8 @@ class RubyLessTest < Test::Unit::TestCase
|
|
26
26
|
|
27
27
|
safe_method_for Time, [:strftime, String] => String
|
28
28
|
|
29
|
+
safe_method :now => {:method => 'Time.now', :class => Time}
|
30
|
+
|
29
31
|
safe_method :@foo => {:class => Dummy, :method => "node"}
|
30
32
|
safe_method :sub => SubDummy
|
31
33
|
safe_method :str => SubString
|
@@ -46,12 +48,22 @@ class RubyLessTest < Test::Unit::TestCase
|
|
46
48
|
# methods on nil
|
47
49
|
safe_method_for Object, :blank? => Boolean
|
48
50
|
|
51
|
+
def safe_const_type(constant)
|
52
|
+
if constant == 'Page'
|
53
|
+
{:method => 'Page', :class => Class}
|
54
|
+
elsif constant =~ /^D/
|
55
|
+
{:method => constant[0..2].upcase.inspect, :class => String, :literal => constant}
|
56
|
+
else
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
49
61
|
# Example to dynamically rewrite method calls during compilation
|
50
62
|
def safe_method_type(signature)
|
51
63
|
unless res = super
|
52
64
|
if signature == ['prepend_test', Number]
|
53
65
|
res ={:class => Number, :prepend_args => RubyLess::TypedString.new('10', :class => Number), :method => 'add'}
|
54
|
-
elsif res = context[:node_class].safe_method_type(signature)
|
66
|
+
elsif context && res = context[:node_class].safe_method_type(signature)
|
55
67
|
# try to execute method in the current var "var.method"
|
56
68
|
res = res.call(self, signature) if res.kind_of?(Proc)
|
57
69
|
res = res.merge(:method => "#{context[:node]}.#{res[:method] || signature[0]}")
|
@@ -4,6 +4,10 @@ module RubyLess
|
|
4
4
|
@opts = opts
|
5
5
|
end
|
6
6
|
|
7
|
+
def name
|
8
|
+
@opts[:name]
|
9
|
+
end
|
10
|
+
|
7
11
|
def default
|
8
12
|
@opts[:default]
|
9
13
|
end
|
@@ -42,10 +46,10 @@ module RubyLess
|
|
42
46
|
|
43
47
|
class ActiveRecordMock
|
44
48
|
COLUMNS = {
|
45
|
-
'format' => ColumnMock.new(:default => '%d.%m.%Y', :type => :text),
|
46
|
-
'age' => ColumnMock.new(:default => 5, :type => :float),
|
47
|
-
'friend_id' => ColumnMock.new(:type => :integer),
|
48
|
-
'log_at' => ColumnMock.new(:type => :datetime),
|
49
|
+
'format' => ColumnMock.new(:default => '%d.%m.%Y', :type => :text, :name => 'format'),
|
50
|
+
'age' => ColumnMock.new(:default => 5, :type => :float, :name => 'age'),
|
51
|
+
'friend_id' => ColumnMock.new(:type => :integer, :name => 'friend_id'),
|
52
|
+
'log_at' => ColumnMock.new(:type => :datetime, :name => 'log_at'),
|
49
53
|
}
|
50
54
|
def self.columns_hash
|
51
55
|
COLUMNS
|
data/test/mock/dummy_class.rb
CHANGED
@@ -16,7 +16,10 @@ 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
|
19
|
+
[:width, {:mode => String, :type => String, 'nice' => Boolean}] => String,
|
20
|
+
[:kind_of?, Class] => Boolean,
|
21
|
+
[:kind_of?, String] => {:method => 'is_like?', :class => Boolean}
|
22
|
+
|
20
23
|
safe_context :spouse => 'Dummy',
|
21
24
|
:husband => {:class => 'Dummy', :context => {:clever => 'no'}}
|
22
25
|
|
data/test/safe_class_test.rb
CHANGED
@@ -12,5 +12,32 @@ class SafeClassTest < Test::Unit::TestCase
|
|
12
12
|
should 'have an associated SignatureHash for safe methods' do
|
13
13
|
assert_kind_of RubyLess::SignatureHash, Dummy.safe_methods
|
14
14
|
end
|
15
|
-
end
|
15
|
+
end # A safe model
|
16
|
+
|
17
|
+
context 'An instance of a safe model' do
|
18
|
+
subject do
|
19
|
+
Dummy.new
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'on safe_eval' do
|
23
|
+
should 'evaluate RubyLess' do
|
24
|
+
assert_equal 'Biscotte', subject.safe_eval("dog_name")
|
25
|
+
end
|
26
|
+
|
27
|
+
should 'raise NoMethodError on missing method' do
|
28
|
+
assert_raise(RubyLess::NoMethodError) { subject.safe_eval("bad_method('Bp Oil Spill')") }
|
29
|
+
end
|
30
|
+
end # on safe_eval
|
31
|
+
|
32
|
+
context 'on safe_eval_string' do
|
33
|
+
should 'evaluate RubyLess as dstring' do
|
34
|
+
assert_equal 'my Biscotte', subject.safe_eval_string('my #{dog_name}')
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'raise NoMethodError on missing method' do
|
38
|
+
assert_raise(RubyLess::NoMethodError) { subject.safe_eval_string("their \#{bad_method('Bp Oil Spill')}") }
|
39
|
+
end
|
40
|
+
end # on safe_eval
|
41
|
+
|
42
|
+
end # An instance of a safe model
|
16
43
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 6
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.6.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-07-22 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- test/RubyLess/errors.yml
|
106
106
|
- test/RubyLess/hash.yml
|
107
107
|
- test/RubyLess/string.yml
|
108
|
+
- test/RubyLess/time.yml
|
108
109
|
- test/RubyLess_test.rb
|
109
110
|
- test/mock/active_record_mock.rb
|
110
111
|
- test/mock/dummy_class.rb
|