rbdi 0.0.1

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.
@@ -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: