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