rbdi 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +674 -0
- data/COPYING.LESSER +165 -0
- data/README.md +0 -0
- data/lib/rdi.rb +12 -0
- data/lib/rdi/bean.rb +123 -0
- data/lib/rdi/chain.rb +71 -0
- data/lib/rdi/context.rb +57 -0
- data/lib/rdi/context_builder.rb +18 -0
- data/lib/rdi/dependency_graph_builder.rb +50 -0
- data/lib/rdi/graphs.rb +116 -0
- data/lib/rdi/logger.rb +7 -0
- data/lib/rdi/object_builder.rb +103 -0
- data/lib/rdi/ref.rb +23 -0
- data/lib/rdi/utils.rb +116 -0
- metadata +75 -0
@@ -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
|
data/lib/rdi/graphs.rb
ADDED
@@ -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
|
data/lib/rdi/logger.rb
ADDED
@@ -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
|
data/lib/rdi/ref.rb
ADDED
@@ -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
|
data/lib/rdi/utils.rb
ADDED
@@ -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:
|