Module: RubyLess::SafeClass

Defined in:
lib/ruby_less/safe_class.rb

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.all_safe_methodsObject



55
56
57
# File 'lib/ruby_less/safe_class.rb', line 55

def self.all_safe_methods
  @@_safe_methods
end

.build_safe_methods_list(klass) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/ruby_less/safe_class.rb', line 243

def self.build_safe_methods_list(klass)
  list = SignatureHash.new
  (@@_safe_methods[klass] || {}).map do |signature, return_value|
    if return_value.kind_of?(Hash)
      return_value[:class] = parse_class(return_value[:class])
    elsif return_value.kind_of?(Proc) || return_value.kind_of?(Symbol)
      # keep
    else
      return_value = {:class => return_value}
    end
    method = signature.shift
    signature = [method] + signature.map {|e| parse_class(e)}
    list[signature] = return_value.freeze
  end
  list
end

.build_signature(key) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/ruby_less/safe_class.rb', line 227

def self.build_signature(key)
  keys = key.kind_of?(Array) ? key : [key]
  keys[0] = keys[0].to_s
  hash_args = []
  signature = keys.map do |k|
    if k.kind_of?(Hash)
      hash_args << k
      Hash
    else
      hash_args << nil
      k
    end
  end
  [signature, (hash_args.compact.empty? ? nil : hash_args)]
end

.included(base) ⇒ Object

ClassMethods



196
197
198
# File 'lib/ruby_less/safe_class.rb', line 196

def self.included(base)
  base.extend ClassMethods
end

.literal_class_for(klass) ⇒ Object



47
48
49
# File 'lib/ruby_less/safe_class.rb', line 47

def self.literal_class_for(klass)
  @@_safe_literal_classes[klass]
end

.parse_class(klass) ⇒ Object



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/ruby_less/safe_class.rb', line 260

def self.parse_class(klass)
  if klass.kind_of?(Array)
    if klass[0].kind_of?(String)
      [Module::const_get(klass[0])]
    else
      klass
    end
  else
    if klass.kind_of?(String)
      Module::const_get(klass)
    else
      klass
    end
  end
end

.safe_literal_class(hash) ⇒ Object



51
52
53
# File 'lib/ruby_less/safe_class.rb', line 51

def self.safe_literal_class(hash)
  @@_safe_literal_classes.merge!(hash)
end

.safe_method_for(klass, methods_hash) ⇒ Object

Declare a safe method for a given class ( same as #safe_method)



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/ruby_less/safe_class.rb', line 60

def self.safe_method_for(klass, methods_hash)
  defaults = methods_hash.delete(:defaults) || {}

  list = (@@_safe_methods[klass] ||= {})
  methods_hash.each do |signature, type|
    signature, hash_args = build_signature(signature)
    type = {:class => type} unless type.kind_of?(Hash)
    type = defaults.merge(type)
    type[:method] = type[:method] ? type[:method].to_s : signature.first.to_s
    if hash_args
      type[:hash_args] = hash_args
      list[signature] = type
      if hash_args.last.kind_of?(Hash)
        # Also build signature without last hash. This enables the common idiom
        # method(arg, arg, opts = {})
        list[signature[0..-2]] = type.dup
      end
    else
      list[signature] = type
    end
  end
end

.safe_method_type_for(klass, signature) ⇒ Object

Return method type (options) if the given signature is a safe method for the class.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/ruby_less/safe_class.rb', line 14

def self.safe_method_type_for(klass, signature)
  if klass.kind_of?(Array)
    safe_method_type_for(Array, signature)
  elsif klass.kind_of?(Hash)
    nil # literal hash resolved in processor
    klass = Hash
  else
    # Signature might be ['name', {:mode => String, :type => Number}].
    # build signature arguments

    # Replace all hashes in signature by Hash class and check for arguments
    signature_args = []
    signature = signature.map do |s|
      if s.kind_of?(Hash)
        signature_args << s
        Hash
      else
        signature_args << nil
        s
      end
    end

    # Find safe method in all ancestry
    klass.ancestors.each do |ancestor|
      # FIXME: find a way to optimize this search !
      if type = safe_method_with_hash_args(ancestor, signature, signature_args)
        return type
      end
    end
    nil
  end
end

.safe_method_type_for_column(col, is_property = false) ⇒ Object

Return a safe type from a column



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ruby_less/safe_class.rb', line 84

def self.safe_method_type_for_column(col, is_property = false)
  opts = {}
  opts[:nil]   = col.default.nil?
  if col.number?
    opts[:class] = Number
  elsif col.text?
    opts[:class] = String
  else
    opts[:class] = col.klass
  end
  if is_property
    opts[:method] = "prop['#{col.name.gsub("'",'')}']"
  else
    opts[:method] = col.name
  end

  opts
end

.safe_method_with_hash_args(klass, signature, hash_args) ⇒ Object



276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/ruby_less/safe_class.rb', line 276

def self.safe_method_with_hash_args(klass, signature, hash_args)

  if type = safe_methods_for(klass)[signature]
    unless allowed_args = type[:hash_args]
      # All arguments allowed
      return type
    end

    # Verify arguments
    hash_args.each_with_index do |args, i|
      next unless args
      # verify for each position: ({:a => 3}, {:x => :y})
      return nil unless allowed_args_for_position = allowed_args[i]
      args.each do |k,v|
        return nil unless v.ancestors.include?(allowed_args_for_position[k])
      end
    end
    type
  else
    nil
  end
end

.safe_methods_for(klass) ⇒ Object

List of safe methods for a specific class.



8
9
10
11
# File 'lib/ruby_less/safe_class.rb', line 8

def self.safe_methods_for(klass)
  # Caching safe_methods_all is bad when modules are dynamically added / removed.
  @@_safe_methods_parsed[klass] ||= build_safe_methods_list(klass)
end

Instance Method Details

#safe_eval(code) ⇒ Object

Evaluate a RubyLess expression. This is just like ‘eval’ but with safe method checking and typing.



215
216
217
218
# File 'lib/ruby_less/safe_class.rb', line 215

def safe_eval(code)
  ruby = RubyLessProcessor.translate(self, code)
  eval(ruby)
end

#safe_eval_string(code) ⇒ Object

Evaluate a RubyLess expression. This is just like ‘eval’ but with safe method checking and typing.



221
222
223
224
# File 'lib/ruby_less/safe_class.rb', line 221

def safe_eval_string(code)
  ruby = RubyLess.translate_string(self, code)
  eval(ruby)
end

#safe_method_type(signature, receiver = nil) ⇒ Object

Return the type if the given signature corresponds to a safe method for the object’s class.



201
202
203
204
205
# File 'lib/ruby_less/safe_class.rb', line 201

def safe_method_type(signature, receiver = nil)
  if type = SafeClass.safe_method_type_for(self.class, signature)
    type[:class].kind_of?(Symbol) ? self.send(type[:class], signature) : type
  end
end

#safe_send(method) ⇒ Object

Safe dynamic method dispatching when the method is not known during compile time. Currently this only works for methods without arguments.



209
210
211
212
# File 'lib/ruby_less/safe_class.rb', line 209

def safe_send(method)
  return nil unless type = self.class.safe_method_type([method])
  self.send(type[:method])
end