Class: Module

Inherits:
Object show all
Defined in:
lib/must_be/attr_typed.rb

Instance Method Summary collapse

Instance Method Details

#attr_typed(symbol, *types, &test) ⇒ Object

Raises:

  • (TypeError)


2
3
4
5
6
7
8
9
10
11
12
13
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/must_be/attr_typed.rb', line 2

def attr_typed(symbol, *types, &test)
  raise TypeError, "#{symbol} is not a symbol" if symbol.is_a? Fixnum
  
  types.each do |type|
    raise TypeError, "class or module required" unless type.is_a? Module
  end
  
  attr_reader symbol
  name = symbol.to_sym.id2name
  check_method_name = "attr_typed__check_#{name}"
  
  unless types.empty?
    types_message = types.size == 1 ? types[0] :
      types.size == 2 ? "#{types[0]} or #{types[1]}" :
      "one of #{types.inspect}"
          
    type_check = lambda do |value|
      if types.none?{|type| value.is_a? type }
        must_notify("attribute `#{name}' must be a #{types_message},"\
          " but value #{value.inspect} is a #{value.class}")
      end
    end
  end
  
  if test
    test_check = lambda do |value|
      unless test[value]
        must_notify("attribute `#{name}' cannot be #{value.inspect}")
      end
    end
  end
  
  define_method(check_method_name, &(
    if types.empty?
      if test
        test_check
      else
        lambda do |value|
          if value.nil?
            must_notify("attribute `#{name}' cannot be nil")
          end
        end
      end
    else
      if test
        lambda do |value|
          type_check[value]
          test_check[value]
        end
      else
        type_check
      end
    end
  ))
  
  module_eval(<<-END, __FILE__, __LINE__ + 1)
    def #{name}=(value)
      #{check_method_name}(value)
      @#{name} = value
    end
  END
end