class_dependencies 0.1.0 → 0.2.0
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 +13 -12
- data/VERSION +1 -1
- data/lib/class_dependencies.rb +71 -44
- data/lib/inflector.rb +32 -0
- data/spec/class_dependencies_spec.rb +35 -38
- metadata +4 -3
data/README.rdoc
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
= class_dependencies
|
2
2
|
|
3
|
-
say you have a bunch of classes which have
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
class A
|
9
|
-
class B
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
say you have a bunch of classes which have a dependency relationship
|
4
|
+
e.g. you have A, B and C and a relationship depends_on. you can express
|
5
|
+
this as follows :
|
6
|
+
|
7
|
+
module DependsOn ; include Sonar::ClassDependencies ; end
|
8
|
+
class A ; include DependsOn ; depends_on :b ; end
|
9
|
+
class B ; include DependsOn ; depends_on :c ; end
|
10
|
+
class C ; include DependsOn ; end
|
11
|
+
|
12
|
+
DependsOn.ordered_dependencies
|
13
|
+
=>[:c, :b, :a]
|
14
|
+
DependsOn.ordered_dependent_classes
|
15
|
+
=>[C, B, A]
|
15
16
|
|
16
17
|
== Install
|
17
18
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/class_dependencies.rb
CHANGED
@@ -1,56 +1,93 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'inflector.rb'
|
2
3
|
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
4
|
+
# include Sonar::ClassDependencies onto a Module or Class
|
5
|
+
# then include that Module, or inherit from that Class,
|
6
|
+
# and declare dependencies amongst the descendants of
|
7
|
+
# that Module or Class, which can be queried on
|
8
|
+
# the Module or Class, and ordered by dependency
|
6
9
|
# e.g.
|
7
|
-
# class Top ; include Sonar::ClassDependencies ; end
|
8
|
-
# class A < Top ; depends_on :b ; end
|
9
|
-
# class B < Top ; depends_on :c ; end
|
10
|
-
# class C < Top ; end
|
11
10
|
#
|
12
|
-
#
|
11
|
+
# module SomeDep ; include Sonar::ClassDependencies ; end
|
12
|
+
# class A ; include Base ; some_dep :b ; end
|
13
|
+
# class B ; include Base ; some_dep :c ; end
|
14
|
+
# class C ; include Base ; end
|
15
|
+
# SomeDep.ordered_dependencies
|
13
16
|
# => [:c, :b, :a]
|
14
|
-
#
|
17
|
+
# SomeDep.ordered_dependent_classes
|
15
18
|
# => [C, B, A]
|
16
19
|
#
|
20
|
+
# class AnotherDep ; include Sonar::ClassDependencies ; end
|
21
|
+
# class D < Top ; another_dep :e ; end
|
22
|
+
# class E < Top ; another_dep :f ; end
|
23
|
+
# class F < Top ; end
|
24
|
+
# AnotherDep.ordered_dependencies
|
25
|
+
# => [:f, :e, :d]
|
26
|
+
# AnotherDep.ordered_dependent_classes
|
27
|
+
# => [F, E, D]
|
28
|
+
|
17
29
|
module Sonar
|
18
30
|
module ClassDependencies
|
31
|
+
module ClassName
|
32
|
+
def class_to_sym(klass)
|
33
|
+
klass.to_s.underscore.to_sym
|
34
|
+
end
|
35
|
+
|
36
|
+
def sym_to_class(sym)
|
37
|
+
eval(sym.to_s.camelize)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
19
41
|
class << self
|
42
|
+
include Sonar::ClassDependencies::ClassName
|
20
43
|
|
21
|
-
# generates
|
22
|
-
|
23
|
-
|
24
|
-
|
44
|
+
# generates an inclusion method [suitable for included() or inherited() ] on the module
|
45
|
+
# we are included into,
|
46
|
+
# which generates value proxys for the class_dependencies and obj_descendants
|
47
|
+
def generate_inclusion_method(mod, method_name)
|
48
|
+
mc = mod.instance_eval{class << self ; self ; end}
|
49
|
+
|
50
|
+
mc.send(:define_method, method_name) do |mod2|
|
51
|
+
raise "include #{mod.to_s} on a Class... doesn't work with intermediate modules" if ! mod2.is_a? Class
|
52
|
+
mod.descendants << class_to_sym(mod2)
|
53
|
+
dep_method_name = class_to_sym(mod)
|
54
|
+
mod2.instance_eval do
|
55
|
+
mc2 = class << self ; self ; end
|
56
|
+
mc2.send(:define_method, dep_method_name){|*params| mod.add_dependency(mod2, *params)}
|
57
|
+
end
|
25
58
|
end
|
26
59
|
end
|
27
|
-
|
60
|
+
|
28
61
|
def included(mod)
|
29
|
-
mod.instance_eval do
|
30
|
-
class << self
|
31
|
-
ClassDependencies::generate_closure_value_method(self, :class_dependencies, {})
|
32
|
-
ClassDependencies::generate_closure_value_method(self, :descendants, [])
|
33
|
-
include( ClassMethods )
|
34
|
-
end
|
62
|
+
mc = mod.instance_eval do
|
63
|
+
class << self ; include BaseModuleMethods ; self ; end
|
35
64
|
end
|
65
|
+
# generate the dependency list value and the descendants value accessors
|
66
|
+
# on first include : they return a closed over value
|
67
|
+
dependencies = {}
|
68
|
+
descendants = []
|
69
|
+
mc.send(:define_method, :class_dependencies){dependencies}
|
70
|
+
mc.send(:define_method, :descendants){descendants}
|
71
|
+
|
72
|
+
# generate an included method if we are included into a module
|
73
|
+
ClassDependencies::generate_inclusion_method(mod, :included)
|
74
|
+
# and if we are included into a Class, then generate an inherited method too
|
75
|
+
ClassDependencies::generate_inclusion_method(mod, :inherited) if mod.is_a? Class
|
36
76
|
end
|
37
77
|
end
|
38
|
-
|
39
|
-
module ClassMethods
|
40
|
-
def inherited(subclass)
|
41
|
-
descendants << subclass.to_s.underscore.to_sym
|
42
|
-
end
|
43
78
|
|
44
|
-
|
45
|
-
|
46
|
-
|
79
|
+
# methods for the base module, on which the dependency map and descendants list live
|
80
|
+
module BaseModuleMethods
|
81
|
+
include Sonar::ClassDependencies::ClassName
|
47
82
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
83
|
+
def add_dependency(from, to)
|
84
|
+
from_sym = class_to_sym(from)
|
85
|
+
to_sym = class_to_sym(to)
|
86
|
+
return if from_sym == to_sym
|
87
|
+
deps = (class_dependencies[from_sym] ||= [])
|
88
|
+
raise "circular dependency" if all_dependencies_of(to_sym).include?(from_sym)
|
89
|
+
deps << to_sym
|
90
|
+
nil
|
54
91
|
end
|
55
92
|
|
56
93
|
def all_dependencies_of(from)
|
@@ -76,16 +113,6 @@ module Sonar
|
|
76
113
|
|
77
114
|
private
|
78
115
|
|
79
|
-
def add_dependency(from, to)
|
80
|
-
from_sym = class_to_sym(from)
|
81
|
-
to_sym = class_to_sym(to)
|
82
|
-
return if from_sym == to_sym
|
83
|
-
deps = (class_dependencies[from_sym] ||= [])
|
84
|
-
raise "circular dependency" if all_dependencies_of(to_sym).include?(from_sym)
|
85
|
-
deps << to_sym
|
86
|
-
nil
|
87
|
-
end
|
88
|
-
|
89
116
|
def find_dependencies_of(from, deps)
|
90
117
|
deps << from
|
91
118
|
(class_dependencies[from]||[]).each do |dep|
|
data/lib/inflector.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# camelize and underscore taken from ActiveSupport
|
2
|
+
|
3
|
+
if !defined? Inflector
|
4
|
+
module Inflector
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
8
|
+
if first_letter_in_uppercase
|
9
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
10
|
+
else
|
11
|
+
lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def underscore(camel_cased_word)
|
16
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
17
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
18
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
19
|
+
tr("-", "_").
|
20
|
+
downcase
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class String
|
25
|
+
def camelize
|
26
|
+
Inflector::camelize(self)
|
27
|
+
end
|
28
|
+
def underscore
|
29
|
+
Inflector::underscore(self)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,62 +1,59 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
# camelize and underscore take from ActiveSupport
|
4
|
-
module Inflector
|
5
|
-
extend self
|
6
|
-
|
7
|
-
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
8
|
-
if first_letter_in_uppercase
|
9
|
-
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
10
|
-
else
|
11
|
-
lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def underscore(camel_cased_word)
|
16
|
-
camel_cased_word.to_s.gsub(/::/, '/').
|
17
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
18
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
19
|
-
tr("-", "_").
|
20
|
-
downcase
|
21
|
-
end
|
22
|
-
end
|
23
|
-
class String
|
24
|
-
def camelize
|
25
|
-
Inflector::camelize(self)
|
26
|
-
end
|
27
|
-
def underscore
|
28
|
-
Inflector::underscore(self)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
3
|
describe "ClassDependencies" do
|
33
4
|
|
34
|
-
class
|
5
|
+
class BaseDep
|
35
6
|
include Sonar::ClassDependencies
|
36
7
|
end
|
37
8
|
|
38
|
-
class A <
|
39
|
-
|
9
|
+
class A < BaseDep
|
10
|
+
base_dep :b
|
40
11
|
end
|
41
12
|
|
42
|
-
class B <
|
43
|
-
|
13
|
+
class B < BaseDep
|
14
|
+
base_dep :c
|
44
15
|
end
|
45
16
|
|
46
|
-
class C <
|
17
|
+
class C < BaseDep
|
47
18
|
end
|
48
19
|
|
49
20
|
|
50
21
|
it "should correctly order class dependences" do
|
51
|
-
|
22
|
+
BaseDep.ordered_dependencies.should == [:c, :b, :a]
|
52
23
|
end
|
53
24
|
|
54
25
|
it "should record dependency declarations" do
|
55
|
-
|
26
|
+
BaseDep.class_dependencies.should == {:a=>[:b], :b=>[:c]}
|
56
27
|
end
|
57
28
|
|
58
29
|
it "should record class inheritance" do
|
59
|
-
|
30
|
+
BaseDep.descendants == [:a, :b, :c]
|
31
|
+
end
|
32
|
+
|
33
|
+
module AnotherDep
|
34
|
+
include Sonar::ClassDependencies
|
35
|
+
end
|
36
|
+
|
37
|
+
class D
|
38
|
+
include AnotherDep
|
39
|
+
another_dep :e
|
40
|
+
end
|
41
|
+
|
42
|
+
class E
|
43
|
+
include AnotherDep
|
44
|
+
another_dep :f
|
45
|
+
end
|
46
|
+
|
47
|
+
class F
|
48
|
+
include AnotherDep
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should correctly order class dependencies for module include" do
|
52
|
+
AnotherDep.ordered_dependencies.should == [:f, :e, :d]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should record dependency declarations for module include" do
|
56
|
+
AnotherDep.class_dependencies.should == {:d=>[:e], :e=>[:f]}
|
60
57
|
end
|
61
58
|
|
62
59
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- mccraig mccraig of the clan mccraig
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-03-
|
17
|
+
date: 2010-03-23 00:00:00 +00:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -48,6 +48,7 @@ files:
|
|
48
48
|
- Rakefile
|
49
49
|
- VERSION
|
50
50
|
- lib/class_dependencies.rb
|
51
|
+
- lib/inflector.rb
|
51
52
|
- spec/class_dependencies_spec.rb
|
52
53
|
- spec/spec.opts
|
53
54
|
- spec/spec_helper.rb
|