class_dependencies 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +1 -1
- data/VERSION +1 -1
- data/lib/class_dependencies.rb +92 -93
- data/spec/class_dependencies_spec.rb +4 -4
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -4,7 +4,7 @@ say you have a bunch of classes which have a dependency relationship
|
|
4
4
|
e.g. you have A, B and C and a relationship depends_on. you can express
|
5
5
|
this as follows :
|
6
6
|
|
7
|
-
module DependsOn ; include
|
7
|
+
module DependsOn ; include ClassDependencies ; end
|
8
8
|
class A ; include DependsOn ; depends_on :b ; end
|
9
9
|
class B ; include DependsOn ; depends_on :c ; end
|
10
10
|
class C ; include DependsOn ; end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.2
|
data/lib/class_dependencies.rb
CHANGED
@@ -2,14 +2,14 @@ require 'set'
|
|
2
2
|
require 'tsort'
|
3
3
|
require 'inflector'
|
4
4
|
|
5
|
-
# include
|
5
|
+
# include ClassDependencies onto a Module or Class
|
6
6
|
# then include that Module, or inherit from that Class,
|
7
7
|
# and declare dependencies amongst the descendants of
|
8
8
|
# that Module or Class, which can be queried on
|
9
9
|
# the Module or Class, and ordered by dependency
|
10
10
|
# e.g.
|
11
11
|
#
|
12
|
-
# module SomeDep ; include
|
12
|
+
# module SomeDep ; include ClassDependencies ; end
|
13
13
|
# class A ; include Base ; some_dep :b ; end
|
14
14
|
# class B ; include Base ; some_dep :c ; end
|
15
15
|
# class C ; include Base ; end
|
@@ -18,7 +18,7 @@ require 'inflector'
|
|
18
18
|
# SomeDep.ordered_dependent_classes
|
19
19
|
# => [C, B, A]
|
20
20
|
#
|
21
|
-
# class AnotherDep ; include
|
21
|
+
# class AnotherDep ; include ClassDependencies ; end
|
22
22
|
# class D < Top ; another_dep :e ; end
|
23
23
|
# class E < Top ; another_dep :f ; end
|
24
24
|
# class F < Top ; end
|
@@ -28,120 +28,119 @@ require 'inflector'
|
|
28
28
|
# => [F, E, D]
|
29
29
|
#
|
30
30
|
# *NOTE* if your class already has an inherited() or included() method
|
31
|
-
# make sure to include
|
31
|
+
# make sure to include ClassDependencies after that method is
|
32
32
|
# defined : Ruby has no after/before methods, so your method will
|
33
33
|
# overwrite the ClassDependencies versions, and dependency tracking
|
34
34
|
# will not work
|
35
35
|
|
36
|
-
module
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
fetch(node,[]).each(&block)
|
43
|
-
end
|
36
|
+
module ClassDependencies
|
37
|
+
class TSortHash < Hash
|
38
|
+
include TSort
|
39
|
+
alias tsort_each_node each_key
|
40
|
+
def tsort_each_child(node,&block)
|
41
|
+
fetch(node,[]).each(&block)
|
44
42
|
end
|
43
|
+
end
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
module ClassName
|
46
|
+
def class_to_sym(klass)
|
47
|
+
klass.to_s.underscore.to_sym
|
48
|
+
end
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
end
|
50
|
+
def sym_to_class(sym)
|
51
|
+
eval(sym.to_s.camelize)
|
54
52
|
end
|
53
|
+
end
|
55
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
# generates an inclusion method [suitable for included() or inherited() ] on the module
|
60
|
-
# we are included into,
|
61
|
-
# which generates value proxys for the class_dependencies and obj_descendants
|
62
|
-
def generate_inclusion_method(mod, method_name)
|
63
|
-
mc = mod.instance_eval{class << self ; self ; end}
|
64
|
-
|
65
|
-
# if there is already such a method, alias it
|
66
|
-
if mod.respond_to?(method_name)
|
67
|
-
aliased_method_name = "class_dependencies_#{method_name}"
|
68
|
-
mc.send(:alias_method, aliased_method_name, method_name)
|
69
|
-
end
|
55
|
+
class << self
|
56
|
+
include ClassDependencies::ClassName
|
70
57
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
dep_method_name = mod.relationship_name || class_to_sym(mod)
|
77
|
-
mc2.send(:define_method, dep_method_name){|*params| mod.add_dependency(mod2, *params)}
|
78
|
-
end
|
79
|
-
|
80
|
-
# call any aliased method. if only Ruby had :after advice etc
|
81
|
-
mod.send(aliased_method_name, mod2) if aliased_method_name
|
82
|
-
end
|
83
|
-
end
|
58
|
+
# generates an inclusion method [suitable for included() or inherited() ] on the module
|
59
|
+
# we are included into,
|
60
|
+
# which generates value proxys for the class_dependencies and obj_descendants
|
61
|
+
def generate_inclusion_method(mod, method_name)
|
62
|
+
mc = mod.instance_eval{class << self ; self ; end}
|
84
63
|
|
85
|
-
|
86
|
-
|
87
|
-
|
64
|
+
# if there is already such a method, alias it
|
65
|
+
if mod.respond_to?(method_name)
|
66
|
+
aliased_method_name = "class_dependencies_#{method_name}"
|
67
|
+
mc.send(:alias_method, aliased_method_name, method_name)
|
68
|
+
end
|
69
|
+
|
70
|
+
mc.send(:define_method, method_name) do |mod2|
|
71
|
+
raise "include #{mod.to_s} on a Class... doesn't work with intermediate modules" if ! mod2.is_a? Class
|
72
|
+
mod.descendants << class_to_sym(mod2)
|
73
|
+
mod2.instance_eval do
|
74
|
+
mc2 = class << self ; include ClassDependencies::ClassName ; self ; end
|
75
|
+
dep_method_name = mod.relationship_name || class_to_sym(mod)
|
76
|
+
mc2.send(:define_method, dep_method_name){|*params| mod.add_dependency(mod2, *params)}
|
88
77
|
end
|
89
|
-
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
# and if we are included into a Class, then generate an inherited method too
|
99
|
-
ClassDependencies::generate_inclusion_method(mod, :inherited) if mod.is_a? Class
|
78
|
+
|
79
|
+
# call any aliased method. if only Ruby had :after advice etc
|
80
|
+
mod.send(aliased_method_name, mod2) if aliased_method_name
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def included(mod)
|
85
|
+
mc = mod.instance_eval do
|
86
|
+
class << self ; include BaseModuleMethods ; self ; end
|
100
87
|
end
|
88
|
+
# generate the dependency list value and the descendants value accessors
|
89
|
+
# on first include : they return a closed over value
|
90
|
+
dependencies = TSortHash.new
|
91
|
+
descendants = []
|
92
|
+
mc.send(:define_method, :class_dependencies){dependencies}
|
93
|
+
mc.send(:define_method, :descendants){descendants}
|
94
|
+
|
95
|
+
# generate an included method if we are included into a module
|
96
|
+
ClassDependencies::generate_inclusion_method(mod, :included)
|
97
|
+
# and if we are included into a Class, then generate an inherited method too
|
98
|
+
ClassDependencies::generate_inclusion_method(mod, :inherited) if mod.is_a? Class
|
101
99
|
end
|
100
|
+
end
|
102
101
|
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
# methods for the base module, on which the dependency map and descendants list live
|
103
|
+
module BaseModuleMethods
|
104
|
+
include ClassDependencies::ClassName
|
106
105
|
|
107
|
-
|
106
|
+
attr_reader :relationship_name
|
108
107
|
|
109
|
-
|
110
|
-
|
111
|
-
|
108
|
+
def set_relationship_name(name)
|
109
|
+
@relationship_name = name
|
110
|
+
end
|
112
111
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
112
|
+
def add_dependency(from, to)
|
113
|
+
from_sym = class_to_sym(from)
|
114
|
+
to_sym = class_to_sym(to)
|
115
|
+
return if from_sym == to_sym
|
116
|
+
deps = (class_dependencies[from_sym] ||= [])
|
117
|
+
raise "circular dependency" if all_dependencies_of(to_sym).include?(from_sym)
|
118
|
+
deps << to_sym
|
119
|
+
nil
|
120
|
+
end
|
122
121
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
122
|
+
def all_dependencies_of(from)
|
123
|
+
from_sym = class_to_sym(from)
|
124
|
+
find_dependencies_of(from_sym, Set.new()).delete(from).to_a
|
125
|
+
end
|
127
126
|
|
128
|
-
|
129
|
-
|
130
|
-
|
127
|
+
def ordered_dependencies
|
128
|
+
class_dependencies.tsort
|
129
|
+
end
|
131
130
|
|
132
|
-
|
133
|
-
|
134
|
-
|
131
|
+
def ordered_dependent_classes
|
132
|
+
ordered_dependencies.map{|sym| sym_to_class(sym)}
|
133
|
+
end
|
135
134
|
|
136
|
-
|
135
|
+
private
|
137
136
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
end
|
143
|
-
deps
|
137
|
+
def find_dependencies_of(from, deps)
|
138
|
+
deps << from
|
139
|
+
(class_dependencies[from]||[]).each do |dep|
|
140
|
+
find_dependencies_of(dep, deps) if !deps.include?(dep)
|
144
141
|
end
|
142
|
+
deps
|
145
143
|
end
|
146
144
|
end
|
147
145
|
end
|
146
|
+
|
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
3
3
|
describe "ClassDependencies" do
|
4
4
|
|
5
5
|
class BaseDep
|
6
|
-
include
|
6
|
+
include ClassDependencies
|
7
7
|
end
|
8
8
|
|
9
9
|
class A < BaseDep
|
@@ -30,7 +30,7 @@ describe "ClassDependencies" do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
module AnotherDep
|
33
|
-
include
|
33
|
+
include ClassDependencies
|
34
34
|
|
35
35
|
set_relationship_name :depends_on
|
36
36
|
end
|
@@ -66,7 +66,7 @@ describe "ClassDependencies" do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
include
|
69
|
+
include ClassDependencies
|
70
70
|
set_relationship_name :depends_on
|
71
71
|
end
|
72
72
|
|
@@ -94,7 +94,7 @@ describe "ClassDependencies" do
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
-
include
|
97
|
+
include ClassDependencies
|
98
98
|
set_relationship_name :depends_on
|
99
99
|
end
|
100
100
|
|