rbdi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ require_relative 'graphs'
2
+
3
+ module Rdi
4
+ module DependencyGraphBuilder
5
+
6
+ def self.build(context)
7
+ graph = Rdi::Graphs::DirectedGraph.new
8
+
9
+ context.beans.each do |bean_id, bean|
10
+ graph.add_vertex(bean_id)
11
+ end
12
+
13
+ context.beans.each do |bean_id, bean|
14
+ bean.constructor_args.each do |arg|
15
+ add_dependencies(bean_id, arg, graph)
16
+ end
17
+
18
+ bean.properties.each do |name, value|
19
+ add_dependencies(bean_id, value, graph)
20
+ end
21
+ end
22
+
23
+ graph
24
+ end
25
+
26
+ def self.add_dependencies(bean_id, value, graph)
27
+ if value.kind_of?(Rdi::Ref)
28
+ graph.add_edge(bean_id, value.bean_id)
29
+ elsif value.kind_of?(Hash)
30
+ add_dependencies_from_map(bean_id, value, graph)
31
+ elsif value.kind_of?(Array)
32
+ add_dependencies_from_collection(bean_id, value, graph)
33
+ end
34
+ end
35
+
36
+ def self.add_dependencies_from_collection(bean_id, collection, graph)
37
+ collection.each do |e|
38
+ add_dependencies(bean_id, e, graph)
39
+ end
40
+ end
41
+
42
+ def self.add_dependencies_from_map(bean_id, map, graph)
43
+ map.each do |key, value|
44
+ add_dependencies(bean_id, key, graph)
45
+ add_dependencies(bean_id, value, graph)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,116 @@
1
+ module Rdi
2
+ module Graphs
3
+
4
+ class DirectedGraph
5
+
6
+ attr_reader :vertices
7
+ attr_reader :adjacencies
8
+
9
+ def initialize
10
+ @vertices = {}
11
+ @adjacencies = {}
12
+ end
13
+
14
+ def add_vertex(label)
15
+ vertex = Vertex.new(label)
16
+ @vertices[label] = vertex
17
+ @adjacencies[vertex] = []
18
+ end
19
+
20
+ def add_edge(from, to)
21
+ @adjacencies[@vertices[from]] << @vertices[to]
22
+ end
23
+
24
+ def cycle?
25
+ cycle_vertices.any?
26
+ end
27
+
28
+
29
+ def path?(from, to)
30
+ _path?(Vertex.new(from), Vertex.new(to), @adjacencies.clone)
31
+ end
32
+
33
+ # Return the vertices that are part of a cycle.
34
+ #
35
+ # If there is no cycle, it returns an empty array.
36
+ #
37
+ # @return Array Vertices labels
38
+ def cycle_vertices
39
+ # Makes a copy of vertices and searches for ones with input grade of 0.
40
+ #
41
+ # Then it removes those vertices and searches again
42
+ # until there are not vertices with input grade of 0.
43
+ #
44
+ # At this point, if there are some vertices it implies that a cycle exists.
45
+
46
+ vertices = @vertices
47
+ adjacencies = @adjacencies
48
+
49
+ while vertices.any?
50
+ leafs = in0(vertices, adjacencies)
51
+
52
+ if leafs.empty?
53
+ return vertices.keys
54
+ else
55
+ vertices = vertices.reject { |k, v| leafs.include?(v) }
56
+ adjacencies = adjacencies.reject { |k, v| leafs.include?(k) }
57
+ end
58
+ end
59
+
60
+ []
61
+ end
62
+
63
+ private
64
+ # Returns the vertices from adjacencies that have an input grade of 0.
65
+ #
66
+ # @param [Hash<Object, Vertex>] vertices
67
+ # @param [Hash<Vertext, Array<Vertext>>] adjacencies
68
+ #
69
+ # @return [Array<Vertext>]
70
+ def in0(vertices, adjacencies)
71
+ vertices.values.select { |vertex|
72
+ adjacencies.count { |v, adjacents|
73
+ adjacents.count { |adjacent_vertext|
74
+ adjacent_vertext == vertex
75
+ } != 0
76
+ } == 0
77
+ }
78
+ end
79
+
80
+ def _path?(from, to, adjacencies)
81
+ adjacents = adjacencies.delete(from) { [] }
82
+
83
+ adjacents.any? do |adjacent|
84
+ if adjacent == to
85
+ true
86
+ else
87
+ _path?(adjacent, to, adjacencies)
88
+ end
89
+ end
90
+ end
91
+
92
+
93
+ end
94
+
95
+ class Vertex
96
+ attr_reader :label
97
+
98
+ def initialize(label)
99
+ @label = label
100
+ end
101
+
102
+ def ==(other)
103
+ eql?(other)
104
+ end
105
+
106
+ def eql?(other)
107
+ label == other.label
108
+ end
109
+
110
+ def hash
111
+ label.hash
112
+ end
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,7 @@
1
+ module Rdi
2
+ module Logger
3
+ def self.log(msg)
4
+ STDERR.puts Time.now.strftime("%H:%M:%S: ") + " #{msg}"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,103 @@
1
+ module Rdi
2
+ module ObjectBuilder
3
+
4
+ def self.create(bean, context)
5
+ args = bean.constructor_args.map do |arg|
6
+ if arg.kind_of?(Rdi::Ref)
7
+ context.get(arg.bean_id)
8
+ elsif arg.kind_of?(Rdi::Bean)
9
+ arg.evaluate
10
+
11
+ else
12
+ arg
13
+ end
14
+ end
15
+
16
+ obj = if bean.factory_method
17
+ bean.klass.send(bean.factory_method)
18
+ else
19
+ bean.klass.new(*args)
20
+ end
21
+
22
+ Rdi::ObjectBuilder.set_properties(obj, bean.properties, context)
23
+
24
+ if bean.init_method
25
+ obj.send bean.init_method
26
+ end
27
+
28
+ obj
29
+ end
30
+
31
+ def self.evaluate_collection(collection, context)
32
+ collection.map do |e|
33
+ if e.kind_of?(Rdi::Ref)
34
+ context.get(e.bean_id)
35
+
36
+ elsif e.kind_of?(Rdi::Bean)
37
+ e.evaluate
38
+
39
+ else
40
+ e
41
+ end
42
+ end
43
+ end
44
+
45
+ def self.evaluate_hash(hash, context)
46
+ value = {}
47
+
48
+ hash.each do |k, v|
49
+ if v.kind_of?(Rdi::Ref)
50
+ value[k] = context.get(v.bean_id)
51
+
52
+ elsif v.kind_of?(Rdi::Bean)
53
+ value[k] = v.evaluate
54
+
55
+ else
56
+ value[k] = v
57
+ end
58
+ end
59
+
60
+ value
61
+ end
62
+
63
+ def self.evaluate_proc(proc_, context)
64
+ params = proc_.parameters.map { |_, param| param }
65
+
66
+ values = params.map { |param| context.get(param) }
67
+
68
+ proc_.call(*values)
69
+ end
70
+
71
+ def self.set_properties(obj, properties, context)
72
+ properties.each do |name, value|
73
+ set_property(obj, name, value, context)
74
+ end
75
+ end
76
+
77
+ def self.set_property(obj, name, value, context)
78
+ value = evaluate(value, context)
79
+ obj.public_send "#{name}=", value
80
+ end
81
+
82
+ def self.evaluate(value, context)
83
+ if value.kind_of?(Rdi::Ref)
84
+ context.get(value.bean_id)
85
+
86
+ elsif value.kind_of?(Rdi::Bean)
87
+ value.evaluate
88
+
89
+ elsif value.kind_of?(Array) || value.kind_of?(Set)
90
+ evaluate_collection(value, context)
91
+
92
+ elsif value.kind_of?(Hash)
93
+ evaluate_hash(value, context)
94
+
95
+ elsif value.kind_of?(Proc) && !value.lambda?
96
+ p 'waa'
97
+ p evaluate_proc(value, context)
98
+ else
99
+ value
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,23 @@
1
+ module Rdi
2
+ class Ref
3
+
4
+ attr_reader :bean_id
5
+
6
+ def initialize(bean_id)
7
+ @bean_id = bean_id
8
+ end
9
+
10
+ def eql?(other)
11
+ if other.respond_to?(:bean_id)
12
+ bean_id.eql?(other.bean_id)
13
+ else
14
+ false
15
+ end
16
+ end
17
+
18
+ def hash
19
+ bean_id.hash
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,116 @@
1
+ module Rdi
2
+ module Utils
3
+
4
+ # Extracts values from a given array
5
+ #
6
+ #
7
+ # Suppose there is a method named 'm'
8
+ #
9
+ # def m(id, name, options); end
10
+ #
11
+ # Which is expected to be called as:
12
+ #
13
+ # m(1, 'foo', :x => true, :y => true)
14
+ #
15
+ # and also:
16
+ #
17
+ # m('foo')
18
+ #
19
+ # In the second case, 'foo' is expected to be pased as the 'name' parameter,
20
+ # and id parameter is expected to be nil
21
+ #
22
+ # It would be good to define m as:
23
+ #
24
+ # def m(id = nil, name, options = {})
25
+ #
26
+ # So, we start to implement m as:
27
+ #
28
+ # def m(*args)
29
+ #
30
+ # id = 0
31
+ # name = nil
32
+ # options = {}
33
+ #
34
+ # if args.empty?
35
+ # raise ArgumentError, 'wrong number of arguments (0 for 1..3)'
36
+ # end
37
+ #
38
+ # if args.length == 1
39
+ # name = args[0]
40
+ # elsif args.length == 2
41
+ # id = args[0]
42
+ # name = args[1]
43
+ # elsif args.length == 3
44
+ # id = args[0]
45
+ # name = args[1]
46
+ # options = args[3]
47
+ # else
48
+ # raise ArgumentError, "wrong number of arguments (#{args.length} for 1..3)"
49
+ # end
50
+ #
51
+ # # ...
52
+ # end
53
+ #
54
+ # By using extract_args, the above code could be refactored as follows:
55
+ #
56
+ # def m(*args)
57
+ # id, name, options = extract_args(args, 3, {:0 => nil, :2 => {}})
58
+ #
59
+ # // ...
60
+ # end
61
+ #
62
+ # @param [Array] args Arguments passed
63
+ # @param [Integer] args_number Maximum number of expected arguments
64
+ # @param [Hash<Integer, Object>] Default argument values.
65
+ # keys => index of the argument
66
+ # values => argument value
67
+ #
68
+ # @return [Array] Extracted arguments
69
+ #
70
+ # @raise [ArgumentError] if less params than expected are passed
71
+ def self.extract_args(args, args_number, defaults = {})
72
+ min_args_number = args_number - defaults.length
73
+
74
+ if args.length < min_args_number
75
+ raise ArgumentError, "wrong number of arguments (#{args.length} for #{min_args_number}..#{args_number}"
76
+ elsif args.length > args_number
77
+ raise ArgumentError, "wrong number of arguments (#{args.length} for #{min_args_number}..#{args_number}"
78
+ end
79
+
80
+ # extracted args are put here
81
+ values = Array.new(args_number)
82
+
83
+ default_indexes = defaults.keys.sort
84
+ non_default_indexes = (0...args_number).to_a - default_indexes
85
+
86
+ defaults.each do |index, default|
87
+ values[index] = default
88
+ end
89
+
90
+ # number of args that will override default args
91
+ extra_args = args.length - (args_number - defaults.length)
92
+
93
+ # TODO: make this more legible
94
+ args.each_with_index do |arg, index|
95
+ if extra_args > 0
96
+ possible_default_index = default_indexes.first || (args_number + 1)
97
+ possible_non_default_index = non_default_indexes.first || (args_number + 1)
98
+ if possible_default_index < possible_non_default_index
99
+ values[possible_default_index] = arg
100
+ default_indexes.delete_at(0)
101
+ extra_args -= 1
102
+ else
103
+ values[possible_non_default_index] = arg
104
+ non_default_indexes.delete_at(0)
105
+ end
106
+ else
107
+ possible_non_default_index = non_default_indexes.delete_at(0)
108
+ values[possible_non_default_index] = arg
109
+ end
110
+ end
111
+
112
+ values
113
+ end
114
+
115
+ end
116
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rbdi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gianfranco Zas
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.10.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - '='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.10.0
30
+ description: Based on spring core.
31
+ email: snmgian@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - lib/rdi/utils.rb
37
+ - lib/rdi/object_builder.rb
38
+ - lib/rdi/ref.rb
39
+ - lib/rdi/context_builder.rb
40
+ - lib/rdi/graphs.rb
41
+ - lib/rdi/context.rb
42
+ - lib/rdi/chain.rb
43
+ - lib/rdi/logger.rb
44
+ - lib/rdi/bean.rb
45
+ - lib/rdi/dependency_graph_builder.rb
46
+ - lib/rdi.rb
47
+ - COPYING
48
+ - COPYING.LESSER
49
+ - README.md
50
+ homepage: http://rubygems.org/gems/rbdi
51
+ licenses: []
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.23
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Brings dependency injection to ruby.
74
+ test_files: []
75
+ has_rdoc: