Module: ClassDependencies

Extended by:
ClassName
Defined in:
lib/class_dependencies.rb

Overview

include ClassDependencies onto a Module or Class then include that Module, or inherit from that Class, and declare dependencies amongst the descendants of that Module or Class, which can be queried on the Module or Class, and ordered by dependency e.g.

module SomeDep ; include ClassDependencies ; end class A ; include Base ; some_dep :b ; end class B ; include Base ; some_dep :c ; end class C ; include Base ; end SomeDep.ordered_dependencies

> [:c, :b, :a]

SomeDep.ordered_dependent_classes

> [C, B, A]

SomeDep.descendants

> [:a, :b, :c]

SomeDep.descendant_classes

> [A, B, C]

class AnotherDep ; include ClassDependencies ; end class D < Top ; another_dep :e ; end class E < Top ; another_dep :f ; end class F < Top ; end AnotherDep.ordered_dependencies

> [:f, :e, :d]

AnotherDep.ordered_dependent_classes

> [F, E, D]

NOTE if your class already has an inherited() or included() method make sure to include ClassDependencies after that method is defined : Ruby has no after/before methods, so your method will overwrite the ClassDependencies versions, and dependency tracking will not work

Defined Under Namespace

Modules: BaseModuleMethods, ClassName Classes: TSortHash

Class Method Summary collapse

Methods included from ClassName

class_to_sym, sym_to_class

Class Method Details

.generate_inclusion_method(mod, method_name) ⇒ Object

generates an inclusion method [suitable for included() or inherited() ] on the module we are included into, which generates value proxys for the class_dependencies and obj_descendants



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/class_dependencies.rb', line 70

def generate_inclusion_method(mod, method_name)
  mc = mod.instance_eval{class << self ; self ; end}
  
  # if there is already such a method, alias it
  if mod.respond_to?(method_name)
    aliased_method_name = "class_dependencies_#{method_name}"
    raise "can't include ClassDependencies twice" if mod.respond_to?(aliased_method_name)
    mc.send(:alias_method, aliased_method_name, method_name) 
  end

  mc.send(:define_method, method_name) do |mod2|
#        raise "include #{mod.to_s} on a Class... doesn't work with intermediate modules" if ! mod2.is_a? Class
    mod.descendants << class_to_sym(mod2)
    mod2.instance_eval do
      mc2 = class << self ; include ClassDependencies::ClassName ; self ; end
      dep_method_name = mod.relationship_name || class_to_sym(mod)
      mc2.send(:define_method, dep_method_name){|*params| mod.add_dependency(mod2, *params)}
    end

    # call any aliased method. if only Ruby had :after advice etc
    mod.send(aliased_method_name, mod2) if aliased_method_name
  end
end

.included(mod) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/class_dependencies.rb', line 94

def included(mod)
  mc = mod.instance_eval do
    class << self ; include BaseModuleMethods ; self ; end
  end
  # generate the dependency list value and the descendants value accessors
  # on first include : they return a closed over value
  dependencies = TSortHash.new(mod)
  descendants = []
  mc.send(:define_method, :class_dependencies){dependencies} if ! mc.instance_methods.include?("class_dependencies")
  mc.send(:define_method, :descendants){descendants} if ! mc.instance_methods.include?("descendants")

  # generate an included method if we are included into a module
  ClassDependencies::generate_inclusion_method(mod, :included)
  # and if we are included into a Class, then generate an inherited method too
  ClassDependencies::generate_inclusion_method(mod, :inherited) if mod.is_a? Class
end