Module: RubyLess::SafeClass

Defined in:
lib/safe_class.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.build_safe_methods_list(klass) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/safe_class.rb', line 128

def self.build_safe_methods_list(klass)
  list = klass.superclass.respond_to?(:safe_methods) ? klass.superclass.safe_methods : {}
  (@@_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

.included(base) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/safe_class.rb', line 28

def self.included(base)
  base.class_eval do

    # Declare safe methods. By providing
    #
    # The methods hash has the following format:
    #  signature => return type
    # or
    #  signature => options
    # or
    #  signature => lambda {|h| ... }
    #
    # The lambda expression will be called with @helper as argument during compilation.
    #
    # The signature can be either a single symbol or an array containing the method name and type arguments like:
    #  [:strftime, Time, String]
    #
    # If the signature is :defaults, the options defined are used as defaults for the other elements defined in the
    # same call.
    #
    # The return type can be a string with the class name or a class.
    #
    # Options are:
    # :class  the return type (class name)
    # :nil    set this to true if the method could return nil
    def self.safe_method(hash)
      methods_hash = hash
      defaults = methods_hash.delete(:defaults) || {}

      list = (@@_safe_methods[self] ||= {})
      methods_hash.each do |k,v|
        k = [k] unless k.kind_of?(Array)
        k[0] = k[0].to_s
        if v.kind_of?(Hash)
          v = defaults.merge(v)
          v[:method] = v[:method] ? v[:method].to_s : k.first.to_s
        elsif v.kind_of?(Proc) || v.kind_of?(Symbol)
          # cannot merge defaults
        else
          v = defaults.merge(:class => v, :method => k.first.to_s)
        end
        list[k] = v
      end
    end

    # Declare a safe method to access a list of attributes.
    # This method should only be used when the class is linked with a database table and provides
    # proper introspection to detect types and the possibility of NULL values.
    def self.safe_attribute(*attributes)
      attributes.each do |att|
        if col = columns_hash[att.to_s]
          opts = {}
          opts[:nil]   = col.default.nil?
          if col.number?
            opts[:class] = Number
          elsif col.text?
            opts[:class] = String
          else
            opts[:class] = col.klass
          end
          safe_method att.to_sym => opts
        else
          puts "Warning: could not declare safe_attribute '#{att}' (No column with this name found in class #{self})"
        end
      end
    end

    # Declare a safe method for a given class
    def self.safe_method_for(klass, signature)
      SafeClass.safe_method_for(klass, signature)
    end

    # Hash of all safe methods defined for the class.
    def self.safe_methods
      SafeClass.safe_methods_for(self)
    end

    # Return the type if the given signature corresponds to a safe method for the class.
    def self.safe_method_type(signature)
      SafeClass.safe_method_type_for(self, signature)
    end
  end  # base.class_eval
end

.parse_class(klass) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/safe_class.rb', line 145

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_method_for(klass, hash) ⇒ Object

Declare a safe method for a given class



17
18
19
20
21
22
23
24
25
26
# File 'lib/safe_class.rb', line 17

def self.safe_method_for(klass, hash)
  list = (@@_safe_methods[klass] ||= {})
  hash.each do |k,v|
    k = [k] unless k.kind_of?(Array)
    k[0] = k[0].to_s
    v = {:class => v} unless v.kind_of?(Hash) || v.kind_of?(Proc)
    v[:method] = v[:method] ? v[:method].to_s : k.first.to_s
    list[k] = v
  end
end

.safe_method_type_for(klass, signature) ⇒ Object

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



12
13
14
# File 'lib/safe_class.rb', line 12

def self.safe_method_type_for(klass, signature)
  safe_methods_for(klass)[signature]
end

.safe_methods_for(klass) ⇒ Object

List of safe methods for a specific class.



7
8
9
# File 'lib/safe_class.rb', line 7

def self.safe_methods_for(klass)
  @@_safe_methods_all[klass] ||= build_safe_methods_list(klass)
end

Instance Method Details

#safe_method_type(signature) ⇒ Object

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



114
115
116
117
118
# File 'lib/safe_class.rb', line 114

def safe_method_type(signature)
  if type = self.class.safe_method_type(signature)
    type.kind_of?(Symbol) ? self.send(type, signature) : type
  end
end

#safe_read(key) ⇒ Object

Safe attribute reader used when ‘safe_readable?’ could not be called because the class is not known during compile time.



122
123
124
125
# File 'lib/safe_class.rb', line 122

def safe_read(key)
  return "'#{key}' not readable" unless type = self.class.safe_method_type([key])
  self.send(type[:method])
end