rubyless 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,12 @@
1
+ == 0.8.1 2011-01-15
2
+
3
+ * Major enhancements
4
+ * Added support for Array method on list types.
5
+ * Changed API for Proc types (we now send 'helper, this, args').
6
+ * Passing receiver on pre_processor resolution.
7
+ * Enable receiver rewrite.
8
+ * Added support for pre-processing on elements that can be nil.
9
+
1
10
  == 0.8.0 2010-11-15
2
11
 
3
12
  * Major enhancements
data/lib/ruby_less.rb CHANGED
@@ -28,6 +28,8 @@ module RubyLess
28
28
  if err.kind_of?(RubyLess::Error)
29
29
  raise err
30
30
  else
31
+ #puts err
32
+ #puts err.backtrace
31
33
  raise RubyLess::Error.new("Error parsing \"#{string}\": #{err.message.strip}")
32
34
  end
33
35
  end
@@ -35,3 +35,7 @@ RubyLess::SafeClass.safe_method_for( NilClass,
35
35
  [:==, String] => Boolean,
36
36
  [:==, Number] => Boolean
37
37
  )
38
+
39
+ RubyLess::SafeClass.safe_method_for( Array,
40
+ :size => Number
41
+ )
@@ -1,3 +1,3 @@
1
1
  module RubyLess
2
- VERSION = '0.8.0'
2
+ VERSION = '0.8.1'
3
3
  end
@@ -119,7 +119,7 @@ module RubyLess
119
119
  end
120
120
 
121
121
  res.opts[:class] = Array
122
- res.opts[:array_content_class] = content_class
122
+ res.opts[:elem] = content_class
123
123
  t "[#{list * ','}]", res.opts.merge(:literal => nil)
124
124
  end
125
125
 
@@ -229,17 +229,26 @@ module RubyLess
229
229
  cond = []
230
230
  end
231
231
 
232
+ opts = get_method(receiver, signature)
233
+
234
+ # method type can rewrite receiver
235
+ if opts[:receiver]
236
+ if receiver
237
+ receiver = "#{receiver}.#{opts[:receiver]}"
238
+ else
239
+ receiver = opts[:receiver]
240
+ end
241
+ end
242
+
232
243
  if receiver
233
- opts = get_method(receiver, signature)
234
244
  method_call_with_receiver(receiver, args, opts, cond, signature)
235
245
  else
236
- opts = get_method(nil, signature)
237
246
  method = opts[:method]
238
247
  args = args_with_prepend(args, opts)
239
248
 
240
249
  if (proc = opts[:pre_processor]) && !args.list.detect {|a| !a.literal}
241
250
  if proc.kind_of?(Proc)
242
- res = proc.call(*args.list.map(&:literal))
251
+ res = proc.call(*([receiver] + args.list.map(&:literal)))
243
252
  else
244
253
  res = @helper.send(proc, *args.list.map(&:literal))
245
254
  end
@@ -280,13 +289,27 @@ module RubyLess
280
289
  !(opts == SafeClass.safe_method_type_for(NilClass, signature) && receiver.cond == [receiver])
281
290
  # Do not add a condition if the method applies on nil
282
291
  cond += receiver.cond
283
- elsif receiver.literal && (proc = opts[:pre_processor]) && !arg_list.detect {|a| !a.literal}
292
+ if (proc = opts[:pre_processor]) && !arg_list.detect {|a| !a.literal}
293
+ # pre-processor on element that can be nil
294
+ if proc.kind_of?(Proc)
295
+ res = proc.call([receiver] + arg_list.map(&:literal))
296
+ return t_if cond, res, res.opts
297
+ end
298
+ end
299
+ elsif (proc = opts[:pre_processor]) && !arg_list.detect {|a| !a.literal}
284
300
  if proc.kind_of?(Proc)
285
- res = proc.call([receiver.literal] + arg_list.map(&:literal))
286
- else
301
+ res = proc.call([receiver] + arg_list.map(&:literal))
302
+ elsif receiver.literal
287
303
  res = receiver.literal.send(*([method] + arg_list.map(&:literal)))
288
304
  end
289
- return res.kind_of?(TypedString) ? res : t(res.inspect, :class => String, :literal => res)
305
+ if res
306
+ if res.opts.nil?
307
+ # This can happen if we use native methods on TypedString (like gsub) that return a new
308
+ # typedstring without calling initialize....
309
+ res.instance_variable_set(:@opts, :class => String, :literal => res)
310
+ end
311
+ return res.kind_of?(TypedString) ? res : t(res.inspect, :class => String, :literal => res)
312
+ end
290
313
  end
291
314
 
292
315
  if method == '/'
@@ -334,7 +357,7 @@ module RubyLess
334
357
  end
335
358
  raise RubyLess::NoMethodError.new(receiver, klass, signature) if !type || type[:class].kind_of?(Symbol) # we cannot send: no object.
336
359
 
337
- type[:class].kind_of?(Proc) ? type[:class].call(@helper, signature) : type
360
+ type[:class].kind_of?(Proc) ? type[:class].call(@helper, receiver ? receiver.klass : @helper, signature) : type
338
361
  end
339
362
 
340
363
  def get_lit_class(lit)
@@ -12,6 +12,10 @@ module RubyLess
12
12
 
13
13
  # Return method type (options) if the given signature is a safe method for the class.
14
14
  def self.safe_method_type_for(klass, signature)
15
+ if klass.kind_of?(Array)
16
+ return safe_method_type_for(Array, signature)
17
+ end
18
+
15
19
  # Signature might be ['name', {:mode => String, :type => Number}].
16
20
  # build signature arguments
17
21
 
@@ -26,7 +30,7 @@ module RubyLess
26
30
  s
27
31
  end
28
32
  end
29
-
33
+
30
34
  # Find safe method in all ancestry
31
35
  klass.ancestors.each do |ancestor|
32
36
  # FIXME: find a way to optimize this search !
@@ -267,7 +271,7 @@ module RubyLess
267
271
  end
268
272
 
269
273
  def self.safe_method_with_hash_args(klass, signature, hash_args)
270
-
274
+
271
275
  if type = safe_methods_for(klass)[signature]
272
276
  unless allowed_args = type[:hash_args]
273
277
  # All arguments allowed
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.0"
8
+ s.version = "0.8.1"
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-11-15}
12
+ s.date = %q{2011-01-15}
13
13
  s.description = %q{RubyLess is an interpreter for "safe ruby". The idea is to transform some "unsafe" ruby code into safe, type checked ruby, eventually rewriting some variables or methods.}
14
14
  s.email = %q{gaspard@teti.ch}
15
15
  s.extra_rdoc_files = [
@@ -206,3 +206,20 @@ class_map_as_string:
206
206
  proc_to_resolve_class:
207
207
  src: "author"
208
208
  tem: "node.author"
209
+
210
+ method_on_array:
211
+ src: "@foo.genitors.size"
212
+ tem: "node.genitors.size"
213
+
214
+ method_on_array_with_nil:
215
+ src: "@foo.children.size"
216
+ tem: "(node.children ? node.children.size : nil)"
217
+
218
+ map_allowed_method:
219
+ src: "%w{45 52}.map(:to_i)"
220
+ tem: "[\"45\",\"52\"].map(&:to_i).compact"
221
+ res: "4552"
222
+
223
+ map_forbidden_method:
224
+ src: "%w{45 52}.map(:foo)"
225
+ tem: "unknown method 'map(:foo)' for '[\"45\",\"52\"]' of type Array."
@@ -9,9 +9,31 @@ class RubyLessTest < Test::Unit::TestCase
9
9
  attr_reader :context
10
10
  yamltest :src_from_title => false
11
11
  include RubyLess
12
+
13
+ # Dynamic resolution of map
14
+ def self.map_proc
15
+ @@map_proc ||= Proc.new do |receiver, method|
16
+ if elem = receiver.opts[:elem] || receiver.klass.first
17
+ if type = RubyLess::safe_method_type_for(elem, [method.to_s])
18
+ if type[:method] =~ /\A\w+\Z/
19
+ res = "#{receiver.raw}.map(&#{type[:method].to_sym.inspect}).compact"
20
+ else
21
+ res = "#{receiver.raw}.map{|_map_obj| _map_obj.#{type[:method]}}.compact"
22
+ end
23
+ res = RubyLess::TypedString.new(res, :class => [type[:class]])
24
+ else
25
+ raise RubyLess::NoMethodError.new(receiver.raw, receiver.klass, ['map', method])
26
+ end
27
+ else
28
+ # should never happen
29
+ raise RubyLess::NoMethodError.new(receiver.raw, receiver.klass, ['map', method])
30
+ end
31
+ end
32
+ end
33
+
12
34
  safe_method :prev => {:class => Dummy, :method => 'previous'}
13
35
  safe_method :main => {:class => Dummy, :method => '@node'}
14
- safe_method :node => lambda {|h, s| {:class => h.context[:node_class], :method => h.context[:node]}}
36
+ safe_method :node => Proc.new {|h, r, s| {:class => h.context[:node_class], :method => h.context[:node]}}
15
37
  safe_method :now => {:class => Time, :method => "Time.now"}
16
38
  safe_method :birth => {:class => Time, :method => "Date.parse('2009-06-02 18:44')"}
17
39
  safe_method 'dictionary' => {:class => StringDictionary, :method => 'get_dict'}
@@ -21,7 +43,18 @@ class RubyLessTest < Test::Unit::TestCase
21
43
 
22
44
  safe_method_for String, [:==, String] => Boolean
23
45
  safe_method_for String, [:to_s] => String
24
- safe_method_for String, [:gsub, Regexp, String] => {:class => String, :pre_processor => Proc.new {|this, reg, str| this.gsub(reg, str)}}
46
+ safe_method_for String, [:to_i] => {:class => Number, :pre_processor => true}
47
+ safe_method_for String, [:gsub, Regexp, String] => {:class => String, :pre_processor => Proc.new {|this, reg, str|
48
+ # We have to test if 'this' is a literal
49
+ if literal = this.literal
50
+ this.gsub(reg, str)
51
+ else
52
+ # abort pre-processing
53
+ nil
54
+ end}}
55
+
56
+ safe_method_for Array, [:map, Symbol] => {:method => 'nil', :class => nil, :pre_processor => self.map_proc}
57
+
25
58
  safe_method_for String, :upcase => {:class => String, :pre_processor => true}
26
59
 
27
60
  safe_method_for Time, [:strftime, String] => String
@@ -42,7 +75,7 @@ class RubyLessTest < Test::Unit::TestCase
42
75
  safe_method [:hash_args, {'age' => Number, 'name' => String}] => String
43
76
  safe_method [:append_hash, Number, {'foo' => String}] => :make_append_hash
44
77
 
45
- safe_method [:concat, String, String] => {:class => String, :pre_processor => Proc.new{|a,b| a + b }}
78
+ safe_method [:concat, String, String] => {:class => String, :pre_processor => Proc.new{|this,a,b| a + b }}
46
79
  safe_method [:find, String] => {:class => NilClass, :method => 'nil', :pre_processor => :build_finder}
47
80
 
48
81
  # methods on nil
@@ -175,5 +208,12 @@ class RubyLessTest < Test::Unit::TestCase
175
208
  err.message
176
209
  end
177
210
 
211
+ def test_proc
212
+ x = RubyLess.translate(self, 'main.proc_test')
213
+ assert_equal self, x.opts[:h]
214
+ assert_equal Dummy, x.opts[:r]
215
+ assert_equal ['proc_test'], x.opts[:s]
216
+ end
217
+
178
218
  yt_make
179
219
  end
@@ -10,7 +10,8 @@ class Dummy < RubyLess::ActiveRecordMock
10
10
 
11
11
  safe_method [:ancestor?, Dummy] => Boolean
12
12
  safe_method :parent => {:class => 'Dummy', :special_option => 'foobar'},
13
- :children => ['Dummy'],
13
+ :genitors => ['Dummy'],
14
+ :children => {:class => ['Dummy'], :nil => true},
14
15
  :project => 'Dummy',
15
16
  :image => 'Dummy',
16
17
  :id => {:class => Number, :method => :zip},
@@ -19,9 +20,13 @@ class Dummy < RubyLess::ActiveRecordMock
19
20
  [:width, {:mode => String, :type => String, 'nice' => Boolean}] => String,
20
21
  [:kind_of?, Class] => Boolean,
21
22
  [:kind_of?, String] => {:method => 'is_like?', :class => Boolean},
22
- :author => (Proc.new do |h, s| {:method => 'author', :class => Dummy} end)
23
+ # helper, receiver, signature
24
+ :author => (Proc.new do |h, r, s| {:method => 'author', :class => Dummy} end)
23
25
  #:author => {:method => 'author', :class => Dummy}
24
26
 
27
+ # just to test Proc.call
28
+ safe_method :proc_test => Proc.new {|h, r, s| {:class => String, :method => '""', :h => h, :r => r, :s => s}}
29
+
25
30
  safe_context :spouse => 'Dummy',
26
31
  :husband => {:class => 'Dummy', :context => {:clever => 'no'}}
27
32
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyless
3
3
  version: !ruby/object:Gem::Version
4
- hash: 63
4
+ hash: 61
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 8
9
- - 0
10
- version: 0.8.0
9
+ - 1
10
+ version: 0.8.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Gaspard Bucher
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-15 00:00:00 +01:00
18
+ date: 2011-01-15 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency