class_dependencies 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,17 +1,18 @@
1
1
  = class_dependencies
2
2
 
3
- say you have a bunch of classes which have dependencies amongst them, e.g. you have
4
- A and B which both have a common parent, say Top, and they have a common operation
5
- but you have to do it to A before you do it to B. then :
6
-
7
- class Top ; include Sonar::ClassDependencies ; end
8
- class A < Top ; end
9
- class B < Top ; depends_on :a ; end
10
-
11
- Top.ordered_dependencies
12
- =>[:a, :b]
13
- Top.ordered_dependent_classes
14
- =>[A, B]
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
1
+ 0.2.0
@@ -1,56 +1,93 @@
1
1
  require 'set'
2
+ require 'inflector.rb'
2
3
 
3
- # when included, defines a class method on the module
4
- # which can be used to declare dependencies amongst descendants
5
- # of that module
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
- # Top.ordered_dependencies
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
- # Top.ordered_dependent_classes
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 a method from a closure returning the value
22
- def generate_closure_value_method(obj, method_name, value)
23
- obj.send(:define_method, method_name) do
24
- value
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
- def class_to_sym(klass)
45
- klass.to_s.underscore.to_sym
46
- end
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 sym_to_class(sym)
49
- eval(sym.to_s.camelize)
50
- end
51
-
52
- def depends_on(dep)
53
- add_dependency(self, dep)
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 Top
5
+ class BaseDep
35
6
  include Sonar::ClassDependencies
36
7
  end
37
8
 
38
- class A < Top
39
- depends_on :b
9
+ class A < BaseDep
10
+ base_dep :b
40
11
  end
41
12
 
42
- class B < Top
43
- depends_on :c
13
+ class B < BaseDep
14
+ base_dep :c
44
15
  end
45
16
 
46
- class C < Top
17
+ class C < BaseDep
47
18
  end
48
19
 
49
20
 
50
21
  it "should correctly order class dependences" do
51
- Top.ordered_dependencies.should == [:c, :b, :a]
22
+ BaseDep.ordered_dependencies.should == [:c, :b, :a]
52
23
  end
53
24
 
54
25
  it "should record dependency declarations" do
55
- Top.class_dependencies.should == {:a=>[:b], :b=>[:c]}
26
+ BaseDep.class_dependencies.should == {:a=>[:b], :b=>[:c]}
56
27
  end
57
28
 
58
29
  it "should record class inheritance" do
59
- Top.descendants == [:a, :b, :c]
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
- - 1
7
+ - 2
8
8
  - 0
9
- version: 0.1.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-22 00:00:00 +00:00
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