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 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