Top Level Namespace

Defined Under Namespace

Modules: Deject

Instance Method Summary collapse

Instance Method Details

#Deject(klass, *initial_dependencies) ⇒ Object



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
# File 'lib/deject.rb', line 30

def Deject(klass, *initial_dependencies)
  # Not a common way of writing code in Ruby, I know.
  # But I tried out several implementations and found this was the easiest to
  # work with within the constraints of the gem (that it doesn't leave traces
  # of itself all over your objects)

  uninitialized_error = lambda do |meth|
    raise Deject::UninitializedDependency, "#{meth} invoked before being defined"
  end

  define_instance_methods = lambda do |meth, default_block|
    # define the getter
    define_method meth do
      block = default_block || Deject.registered(meth)
      uninitialized_error[meth] unless block
      value = block.call self
      define_singleton_method(meth) { value }
      send meth
    end

    # define the override
    define_method :"with_#{meth}" do |value=nil, &block|

      # redefine getter if given a block
      if block
        define_singleton_method meth do
          value = block.call self
          define_singleton_method(meth) { value }
          send meth
        end

      # always return value if given a value
      else
        define_singleton_method(meth) { value }
      end

      self
    end
    self
  end

  has_dependency = lambda do |meth|
    instance_methods.include?(meth.intern) && instance_methods.include?(:"with_#{meth}")
  end


  # define klass.dependency
  klass.define_singleton_method :dependency do |meth, &default_block|
    if instance_exec meth, &has_dependency
      warn "Deprecation: Use .override instead of .dependency to override a dependency (#{meth})"
    end
    instance_exec meth, default_block, &define_instance_methods
  end

  # define klass.override
  klass.define_singleton_method :override do |meth, &override_block|
    if !override_block
      raise ArgumentError, "Cannot override #{meth} without an override block" unless override_block
    elsif !instance_exec(meth, &has_dependency)
      raise ArgumentError, "#{meth} is not a dependency of #{klass.inspect}"
    else
      instance_exec meth, override_block, &define_instance_methods
    end
  end

  # override multiple dependencies
  klass.send :define_method, :with_dependencies do |overrides|
    overrides.each { |meth, value| send "with_#{meth}", value }
    self
  end

  # add the initial dependencies
  initial_dependencies.each { |dependency| klass.dependency dependency }

  klass
end