rubyless 0.3.4 → 0.3.5

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 CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.3.5 2009-11-08
2
+
3
+ * 1 major enhancement
4
+ * added support for hash in signature: ['img', {'mode' => String, 'class' => String}]
5
+
6
+ * 1 minor enhancement
7
+ * added 'disable_safe_read' method
8
+
1
9
  == 0.3.4 2009-11-05
2
10
 
3
11
  * 1 minor enhancement
data/lib/processor.rb CHANGED
@@ -113,6 +113,30 @@ module RubyLess
113
113
  exp.empty? ? t('', String) : process(exp.shift)
114
114
  end
115
115
 
116
+ def process_hash(exp)
117
+ result = []
118
+ klass = {}
119
+ until exp.empty?
120
+ key = exp.shift
121
+ if [:lit, :str].include?(key.first)
122
+ key = key[1]
123
+
124
+ rhs = exp.shift
125
+ type = rhs.first
126
+ rhs = process rhs
127
+ #rhs = "(#{rhs})" unless [:lit, :str].include? type # TODO: verify better!
128
+
129
+ result << "#{key.inspect} => #{rhs}"
130
+ klass[key] = rhs.klass
131
+ else
132
+ # ERROR: invalid key
133
+ raise "Invalid key type for hash (should be a literal value, was #{key.first.inspect})"
134
+ end
135
+ end
136
+
137
+ t "{#{result.join(', ')}}", :class => klass
138
+ end
139
+
116
140
  private
117
141
  def t(content, opts = nil)
118
142
  if opts.nil?
@@ -209,9 +233,10 @@ module RubyLess
209
233
  end
210
234
 
211
235
  def get_method(signature, receiver, is_method = true)
212
- res = receiver.respond_to?(:safe_method_type) ? receiver.safe_method_type(signature) : SafeClass.safe_method_type_for(receiver, signature)
213
- res = res.call(@helper, signature) if res.kind_of?(Proc)
214
- res.kind_of?(Symbol) ? nil : res # Symbols not allowed here (should be resolved in receiver.safe_method_type)
236
+ type = receiver.respond_to?(:safe_method_type) ? receiver.safe_method_type(signature) : SafeClass.safe_method_type_for(receiver, signature)
237
+ return nil if !type || type[:class].kind_of?(Symbol) # we cannot send: no object.
238
+
239
+ type[:class].kind_of?(Proc) ? type[:class].call(@helper, signature) : type
215
240
  end
216
241
  end
217
242
  end
data/lib/rubyless.rb CHANGED
@@ -6,7 +6,7 @@ require 'processor'
6
6
  =begin rdoc
7
7
  =end
8
8
  module RubyLess
9
- VERSION = '0.3.4'
9
+ VERSION = '0.3.5'
10
10
 
11
11
  def self.translate(string, helper)
12
12
  RubyLessProcessor.translate(string, helper)
data/lib/safe_class.rb CHANGED
@@ -10,17 +10,56 @@ module RubyLess
10
10
 
11
11
  # Return method type (options) if the given signature is a safe method for the class.
12
12
  def self.safe_method_type_for(klass, signature)
13
- safe_methods_for(klass)[signature]
13
+ if type = safe_methods_for(klass)[signature]
14
+ type
15
+ else
16
+ # Signature might be ['name', {:mode => String, :type => Number}].
17
+
18
+ # Replace all hashes in signature by Hash class and check for arguments
19
+ signature_args = []
20
+ signature = signature.map do |s|
21
+ if s.kind_of?(Hash)
22
+ signature_args << s
23
+ Hash
24
+ else
25
+ signature_args << nil
26
+ s
27
+ end
28
+ end
29
+
30
+ if type = safe_methods_for(klass)[signature]
31
+ unless allowed_args = type[:hash_args]
32
+ # All arguments allowed
33
+ return type
34
+ end
35
+
36
+ # Verify arguments
37
+ signature_args.each_with_index do |args, i|
38
+ next unless args
39
+ # verify for each position: ({:a => 3}, {:x => :y})
40
+ return nil unless allowed_args_for_position = allowed_args[i]
41
+ args.each do |k,v|
42
+ return nil unless allowed_args_for_position[k] == v
43
+ end
44
+ end
45
+ type
46
+ else
47
+ nil
48
+ end
49
+ end
14
50
  end
15
51
 
16
- # Declare a safe method for a given class
17
- def self.safe_method_for(klass, hash)
52
+ # Declare a safe method for a given class ( same as #safe_method)
53
+ def self.safe_method_for(klass, methods_hash)
54
+ defaults = methods_hash.delete(:defaults) || {}
55
+
18
56
  list = (@@_safe_methods[klass] ||= {})
19
- hash.each do |k,v|
20
- k = [k] unless k.kind_of?(Array)
21
- k[0] = k[0].to_s
22
- v = {:class => v} unless v.kind_of?(Hash) || v.kind_of?(Proc)
57
+ methods_hash.each do |k,v|
58
+ k, hash_args = build_signature(k)
59
+ v = {:class => v} unless v.kind_of?(Hash)
60
+ v = defaults.merge(v)
23
61
  v[:method] = v[:method] ? v[:method].to_s : k.first.to_s
62
+ v[:hash_args] = hash_args if hash_args
24
63
  list[k] = v
25
64
  end
26
65
  end
@@ -42,6 +81,11 @@ module RubyLess
42
81
  # The signature can be either a single symbol or an array containing the method name and type arguments like:
43
82
  # [:strftime, Time, String]
44
83
  #
84
+ # If your method accepts variable arguments through a Hash, you should declare it with:
85
+ # [:img, String, {:mode => String, :max_size => Number}]
86
+ #
87
+ # Make sure your literal values are of the right type: +:mode+ and +'mode'+ are not the same here.
88
+ #
45
89
  # If the signature is :defaults, the options defined are used as defaults for the other elements defined in the
46
90
  # same call.
47
91
  #
@@ -51,22 +95,7 @@ module RubyLess
51
95
  # :class the return type (class name)
52
96
  # :nil set this to true if the method could return nil
53
97
  def self.safe_method(methods_hash)
54
- defaults = methods_hash.delete(:defaults) || {}
55
-
56
- list = (@@_safe_methods[self] ||= {})
57
- methods_hash.each do |k,v|
58
- k = [k] unless k.kind_of?(Array)
59
- k[0] = k[0].to_s
60
- if v.kind_of?(Hash)
61
- v = defaults.merge(v)
62
- v[:method] = v[:method] ? v[:method].to_s : k.first.to_s
63
- elsif v.kind_of?(Proc) || v.kind_of?(Symbol)
64
- # cannot merge defaults
65
- else
66
- v = defaults.merge(:class => v, :method => k.first.to_s)
67
- end
68
- list[k] = v
69
- end
98
+ RubyLess::SafeClass.safe_method_for(self, methods_hash)
70
99
  end
71
100
 
72
101
  # A safe context is simply a safe method that can return nil in some situations. The rest of the
@@ -114,6 +143,17 @@ module RubyLess
114
143
  def self.safe_method_type(signature)
115
144
  SafeClass.safe_method_type_for(self, signature)
116
145
  end
146
+
147
+ # Return true if the class is safe (we can call safe_read on its instances)
148
+ def self.safe_class?
149
+ true
150
+ end
151
+
152
+ # Use this if you want to disable 'safe_read'. This is useful if you provided
153
+ # a mock as return type signature.
154
+ def self.disable_safe_read
155
+ undef_method(:safe_read)
156
+ end
117
157
  end # base.class_eval
118
158
  end # included
119
159
 
@@ -121,7 +161,7 @@ module RubyLess
121
161
  # Return the type if the given signature corresponds to a safe method for the object's class.
122
162
  def safe_method_type(signature)
123
163
  if type = self.class.safe_method_type(signature)
124
- type.kind_of?(Symbol) ? self.send(type, signature) : type
164
+ type[:class].kind_of?(Symbol) ? self.send(type[:class], signature) : type
125
165
  end
126
166
  end
127
167
 
@@ -133,6 +173,22 @@ module RubyLess
133
173
  end
134
174
 
135
175
  private
176
+ def self.build_signature(key)
177
+ keys = key.kind_of?(Array) ? key : [key]
178
+ keys[0] = keys[0].to_s
179
+ hash_args = []
180
+ signature = keys.map do |k|
181
+ if k.kind_of?(Hash)
182
+ hash_args << k
183
+ Hash
184
+ else
185
+ hash_args << nil
186
+ k
187
+ end
188
+ end
189
+ [signature, (hash_args.compact.empty? ? nil : hash_args)]
190
+ end
191
+
136
192
  def self.build_safe_methods_list(klass)
137
193
  list = klass.superclass.respond_to?(:safe_methods) ? klass.superclass.safe_methods : {}
138
194
  (@@_safe_methods[klass] || {}).map do |signature, return_value|
data/rubyless.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{rubyless}
5
- s.version = "0.3.4"
5
+ s.version = "0.3.5"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Gaspard Bucher"]
9
- s.date = %q{2009-11-05}
9
+ s.date = %q{2009-11-08}
10
10
  s.description = %q{RubyLess is an interpreter for "safe ruby". The idea is to transform some "unsafe" ruby code into safe, type checked
11
11
  ruby, eventually rewriting some variables or methods.}
12
12
  s.email = %q{gaspard@teti.ch}
@@ -91,3 +91,13 @@ literal_argument_for_method:
91
91
  safe_method_defined_as_symbol:
92
92
  src: "foo"
93
93
  tem: "contextual_foo"
94
+
95
+ optional_arguments:
96
+ src: "width(:mode => 'pv')"
97
+ tem: "var1.width({:mode => \"pv\"})"
98
+ res: "mode: pv, type: none"
99
+
100
+ optional_arguments_string:
101
+ src: "width('nice' => 1 == 1)"
102
+ tem: "var1.width({\"nice\" => (1==1)})"
103
+ res: "nice!"
@@ -33,4 +33,17 @@ string_argument:
33
33
 
34
34
  symbol_type_not_used_out_of_helper:
35
35
  src: "node.foo"
36
- res: "'var1' does not respond to 'foo()'."
36
+ tem: "'var1' does not respond to 'foo()'."
37
+
38
+ optional_arguments_with_dynamic_string:
39
+ src: "spouse.width(\"nice#{spouse.name}\" => 'pv')"
40
+ tem: "Invalid key type for hash (should be a literal value, was :dstr)"
41
+
42
+
43
+ optional_arguments_bad_type:
44
+ src: "width(:mode => 12)"
45
+ res: "Unknown method 'width({:mode => 12})'."
46
+
47
+ optional_arguments_bad_argument:
48
+ src: "width(:xyz => 'pv')"
49
+ res: "Unknown method 'width({:xyz => \"pv\"})'."
@@ -4,9 +4,10 @@ require File.dirname(__FILE__) + '/test_helper.rb'
4
4
  class StringDictionary
5
5
  include RubyLess::SafeClass
6
6
  safe_method ['[]', Symbol] => {:class => String, :nil => true}
7
+ disable_safe_read
7
8
  end
8
9
 
9
- class SimpleHelper < Test::Unit::TestCase
10
+ class RubyLessTest < Test::Unit::TestCase
10
11
  attr_reader :context
11
12
  yamltest :src_from_title => false
12
13
  include RubyLess::SafeClass
@@ -56,6 +57,17 @@ class SimpleHelper < Test::Unit::TestCase
56
57
  assert_equal "'rm' not readable", Dummy.new.safe_read('rm')
57
58
  end
58
59
 
60
+ def test_disable_safe_read
61
+ assert Dummy.instance_methods.include?('safe_read')
62
+ assert !StringDictionary.instance_methods.include?('safe_read')
63
+ end
64
+
65
+ def test_safe_method_type
66
+ type = Dummy.safe_method_type(['husband'])
67
+ type_should_be = {:class => Dummy, :method => 'husband', :nil => true, :context => {:clever => 'no'}}
68
+ assert_equal type_should_be, type
69
+ end
70
+
59
71
  def yt_do_test(file, test, context = yt_get('context',file,test))
60
72
  @@test_strings[file][test].keys.each do |key|
61
73
  next if ['src', 'context'].include?(key)
@@ -78,9 +90,9 @@ class SimpleHelper < Test::Unit::TestCase
78
90
  "Unknown key '#{key}'. Should be 'tem' or 'res'."
79
91
  end
80
92
  rescue => err
81
- # puts "\n\n#{err.message}"
82
- # puts err.backtrace
83
- err.message
93
+ #puts "\n\n#{err.message}"
94
+ #puts err.backtrace
95
+ err.message
84
96
  end
85
97
 
86
98
  yt_make
@@ -8,11 +8,13 @@ class Dummy < RubyLess::ActiveRecordMock
8
8
  safe_method :parent => {:class => 'Dummy', :special_option => 'foobar'},
9
9
  :children => ['Dummy'],
10
10
  :project => 'Dummy',
11
+ :image => 'Dummy',
11
12
  :id => {:class => Number, :method => :zip},
12
13
  :name => String,
13
- :foo => :bar
14
+ :foo => :bar,
15
+ [:width, {:mode => String, :type => String, 'nice' => Boolean}] => String
14
16
  safe_context :spouse => 'Dummy',
15
- :husband => {:class => 'Dummy'}
17
+ :husband => {:class => 'Dummy', :context => {:clever => 'no'}}
16
18
 
17
19
  safe_attribute :age, :friend_id, :log_at, :format
18
20
 
@@ -20,11 +22,20 @@ class Dummy < RubyLess::ActiveRecordMock
20
22
  @name = name
21
23
  end
22
24
 
25
+ def width(opts = {})
26
+ return 'nice!' if opts['nice']
27
+ "mode: #{(opts[:mode] || 'none')}, type: #{(opts[:type] || 'none')}"
28
+ end
29
+
23
30
  # This method returns pseudo-nil and does not need to be declared with :nil => true
24
31
  def project
25
32
  Dummy.new('project')
26
33
  end
27
34
 
35
+ def image
36
+ Dummy.new('image')
37
+ end
38
+
28
39
  # This method can return nil and must be declared with :nil => true
29
40
  def spouse
30
41
  nil
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gaspard Bucher
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-05 00:00:00 +01:00
12
+ date: 2009-11-08 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency