conject 0.0.1 → 0.0.3
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/.gitignore +2 -0
- data/Gemfile.lock +1 -1
- data/lib/conject.rb +15 -17
- data/lib/conject/class_finder.rb +9 -7
- data/lib/conject/composition_error.rb +26 -24
- data/lib/conject/dependency_resolver.rb +15 -13
- data/lib/conject/object_context.rb +49 -49
- data/lib/conject/object_definition.rb +7 -6
- data/lib/conject/object_factory.rb +21 -19
- data/lib/conject/utilities.rb +7 -5
- data/lib/conject/version.rb +1 -1
- data/spec/acceptance/regression/inject_object_context_spec.rb +28 -0
- data/spec/test_data/basic_composition/master_of_puppets.rb +5 -0
- metadata +12 -8
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/lib/conject.rb
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
require "conject/version"
|
2
2
|
|
3
|
+
require 'conject/object_definition'
|
4
|
+
require 'conject/extended_metaid'
|
5
|
+
require 'conject/class_ext_construct_with'
|
6
|
+
require 'conject/object_context'
|
7
|
+
require 'conject/object_factory'
|
8
|
+
require 'conject/class_finder'
|
9
|
+
require 'conject/dependency_resolver'
|
10
|
+
require 'conject/utilities'
|
11
|
+
require 'conject/composition_error'
|
12
|
+
require 'conject/borrowed_active_support_inflector'
|
13
|
+
|
3
14
|
module Conject
|
4
15
|
#
|
5
16
|
# Provide access to the default ObjectContext.
|
@@ -11,30 +22,17 @@ module Conject
|
|
11
22
|
end
|
12
23
|
|
13
24
|
def self.default_object_factory
|
14
|
-
@default_object_factory ||=
|
15
|
-
:class_finder =>
|
16
|
-
:dependency_resolver =>
|
25
|
+
@default_object_factory ||= ObjectFactory.new(
|
26
|
+
:class_finder => ClassFinder.new,
|
27
|
+
:dependency_resolver => DependencyResolver.new
|
17
28
|
)
|
18
29
|
end
|
19
30
|
|
20
31
|
def self.create_object_context(parent_context, object_factory=nil)
|
21
32
|
object_factory ||= default_object_factory
|
22
|
-
|
33
|
+
ObjectContext.new(
|
23
34
|
:parent_context => parent_context,
|
24
35
|
:object_factory => object_factory
|
25
36
|
)
|
26
37
|
end
|
27
38
|
end
|
28
|
-
|
29
|
-
# The rest of the libraries namespace themselves under Conject so
|
30
|
-
# they must be required AFTER the initial definition of Conject.
|
31
|
-
require 'conject/object_definition'
|
32
|
-
require 'conject/extended_metaid'
|
33
|
-
require 'conject/class_ext_construct_with'
|
34
|
-
require 'conject/object_context'
|
35
|
-
require 'conject/object_factory'
|
36
|
-
require 'conject/class_finder'
|
37
|
-
require 'conject/dependency_resolver'
|
38
|
-
require 'conject/utilities'
|
39
|
-
require 'conject/composition_error'
|
40
|
-
require 'conject/borrowed_active_support_inflector'
|
data/lib/conject/class_finder.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
Object.
|
7
|
-
|
8
|
-
|
2
|
+
module Conject
|
3
|
+
class ClassFinder
|
4
|
+
def find_class(name)
|
5
|
+
cname = name.to_s.camelize
|
6
|
+
if Object.const_defined?(cname)
|
7
|
+
Object.const_get(cname)
|
8
|
+
else
|
9
|
+
raise "Could not find class for #{name}"
|
10
|
+
end
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
@@ -1,33 +1,35 @@
|
|
1
1
|
require 'set'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
opts
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
msg = "Unexpected CompositionError"
|
12
|
-
|
13
|
-
if object_def.nil?
|
14
|
-
msg = "Failed to construct... something."
|
15
|
-
if provided and !provided.empty?
|
16
|
-
msg << " Provided objects were: #{provided.inspect}"
|
17
|
-
end
|
3
|
+
module Conject
|
4
|
+
class CompositionError < ArgumentError
|
5
|
+
def initialize(opts=nil)
|
6
|
+
opts ||= {}
|
7
|
+
object_def = opts[:object_definition]
|
8
|
+
required = nil
|
9
|
+
required = object_def.component_names if object_def
|
10
|
+
provided = opts[:provided] || []
|
18
11
|
|
19
|
-
|
20
|
-
owner = object_def.owner || "object"
|
21
|
-
msg = "Wrong components when building new #{owner}."
|
12
|
+
msg = "Unexpected CompositionError"
|
22
13
|
|
23
|
-
|
24
|
-
|
14
|
+
if object_def.nil?
|
15
|
+
msg = "Failed to construct... something."
|
16
|
+
if provided and !provided.empty?
|
17
|
+
msg << " Provided objects were: #{provided.inspect}"
|
18
|
+
end
|
25
19
|
|
26
|
-
|
27
|
-
|
20
|
+
elsif object_def and required and provided
|
21
|
+
owner = object_def.owner || "object"
|
22
|
+
msg = "Wrong components when building new #{owner}."
|
28
23
|
|
29
|
-
|
24
|
+
missing = required - provided
|
25
|
+
msg << " Missing required object(s) #{missing.to_a.inspect}." unless missing.empty?
|
26
|
+
|
27
|
+
unexpected = provided - required
|
28
|
+
msg << " Unexpected object(s) provided #{unexpected.to_a.inspect}." unless unexpected.empty?
|
30
29
|
|
31
|
-
|
30
|
+
end
|
31
|
+
|
32
|
+
super msg
|
33
|
+
end
|
32
34
|
end
|
33
35
|
end
|
@@ -1,16 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
klass
|
12
|
-
obj_map
|
13
|
-
|
1
|
+
module Conject
|
2
|
+
class DependencyResolver
|
3
|
+
#
|
4
|
+
# Given a Class, generate a map of dependencies needed to construct a new
|
5
|
+
# instance of that class. Dependencies are looked up (and/or instantiated, as
|
6
|
+
# determined within the ObjectContext) via the provided ObjectContext.
|
7
|
+
#
|
8
|
+
# This method assumes the Class has_object_defintion? (Client code should
|
9
|
+
# determine that before invoking this method.)
|
10
|
+
#
|
11
|
+
def resolve_for_class(klass, object_context)
|
12
|
+
klass.object_definition.component_names.inject({}) do |obj_map, name|
|
13
|
+
obj_map[name] = object_context.get(name)
|
14
|
+
obj_map
|
15
|
+
end
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -1,61 +1,61 @@
|
|
1
|
-
|
1
|
+
module Conject
|
2
|
+
class ObjectContext
|
2
3
|
|
3
|
-
|
4
|
+
construct_with :parent_context, :object_factory
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# Inject a named object into this context
|
10
|
-
def put(name, object)
|
11
|
-
@cache[name.to_sym] = object
|
12
|
-
end
|
6
|
+
def initialize
|
7
|
+
@cache = { :this_object_context => self }
|
8
|
+
end
|
13
9
|
|
14
|
-
|
10
|
+
# Inject a named object into this context
|
11
|
+
def put(name, object)
|
12
|
+
@cache[name.to_sym] = object
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
15
|
+
alias_method :[]=, :put
|
16
|
+
|
17
|
+
# Retrieve a named object from this context.
|
18
|
+
# If the object is already existant in this context, return it.
|
19
|
+
# If we have a parent context and it contains the requested object, get and return object from parent context. (Recursive upward search)
|
20
|
+
# If the object exists nowhere in this or a super context: construct, cache and return a new instance of the requested object using the object factory.
|
21
|
+
def get(name)
|
22
|
+
name = name.to_sym
|
23
|
+
object = @cache[name]
|
24
|
+
return @cache[name] if @cache.keys.include?(name)
|
25
|
+
|
26
|
+
if parent_context and parent_context.has?(name)
|
27
|
+
return parent_context.get(name)
|
28
|
+
else
|
29
|
+
object = object_factory.construct_new(name,self)
|
30
|
+
@cache[name] = object
|
31
|
+
return object
|
32
|
+
end
|
31
33
|
end
|
32
|
-
end
|
33
34
|
|
34
|
-
|
35
|
+
alias_method :[], :get
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
# Indicates if this context, or any parent context, contains the requested object in its cache.
|
38
|
+
def has?(name)
|
39
|
+
return true if directly_has?(name)
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
# Ask parent (if i have a parent) if I don't have the object:
|
42
|
+
if !parent_context.nil?
|
43
|
+
return parent_context.has?(name)
|
44
|
+
else
|
45
|
+
# I don't have it, and neither do my ancestors.
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Indicates if this context has the requested object in its own personal cache.
|
51
|
+
# (Does not consult any parent contexts.)
|
52
|
+
def directly_has?(name)
|
53
|
+
@cache.keys.include?(name.to_sym)
|
46
54
|
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Indicates if this context has the requested object in its own personal cache.
|
50
|
-
# (Does not consult any parent contexts.)
|
51
|
-
def directly_has?(name)
|
52
|
-
@cache.keys.include?(name.to_sym)
|
53
|
-
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
def in_subcontext
|
57
|
+
yield Conject.create_object_context(self) if block_given?
|
58
|
+
end
|
58
59
|
|
60
|
+
end
|
59
61
|
end
|
60
|
-
|
61
|
-
|
@@ -1,10 +1,11 @@
|
|
1
|
+
module Conject
|
1
2
|
|
2
|
-
class
|
3
|
-
|
3
|
+
class ObjectDefinition
|
4
|
+
attr_reader :component_names, :owner
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
def initialize(opts={})
|
7
|
+
@owner = opts[:owner]
|
8
|
+
@component_names = opts[:component_names] || []
|
9
|
+
end
|
8
10
|
end
|
9
11
|
end
|
10
|
-
|
@@ -1,28 +1,30 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
2
|
+
module Conject
|
3
|
+
class ObjectFactory
|
4
|
+
construct_with :class_finder, :dependency_resolver
|
4
5
|
|
5
|
-
|
6
|
+
def construct_new(name, object_context)
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
#
|
9
|
+
# This implementation is what I'm loosely calling "type 1" or "regular" object creation:
|
10
|
+
# - Assume we're looking for a class to create an instance with
|
11
|
+
# - it may or may not have a declared list of named objects it needs to be constructed with
|
12
|
+
#
|
12
13
|
|
13
|
-
|
14
|
+
klass = class_finder.find_class(name)
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
if klass.has_object_definition?
|
17
|
+
object_map = dependency_resolver.resolve_for_class(klass, object_context)
|
18
|
+
return klass.new(object_map)
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
elsif Utilities.has_zero_arg_constructor?(klass)
|
21
|
+
# Default construction
|
22
|
+
return klass.new
|
23
|
+
else
|
24
|
+
# Oops, out of ideas on how to build.
|
25
|
+
raise ArgumentError.new("Class #{klass} has no special component needs, but neither does it have a zero-argument constructor.");
|
26
|
+
end
|
26
27
|
|
28
|
+
end
|
27
29
|
end
|
28
30
|
end
|
data/lib/conject/utilities.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
module Conject
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Conject
|
2
|
+
module Utilities
|
3
|
+
class << self
|
4
|
+
def has_zero_arg_constructor?(klass)
|
5
|
+
init_arity = klass.instance_method(:initialize).arity
|
6
|
+
init_arity == 0 or (RUBY_VERSION <= "1.9.2" and init_arity == -1)
|
7
|
+
end
|
6
8
|
end
|
7
9
|
end
|
8
10
|
end
|
data/lib/conject/version.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
|
2
|
+
|
3
|
+
describe "object referencing its own context" do
|
4
|
+
subject { Conject.default_object_context }
|
5
|
+
|
6
|
+
before do
|
7
|
+
append_test_load_path "basic_composition"
|
8
|
+
require 'master_of_puppets'
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
restore_load_path
|
13
|
+
end
|
14
|
+
|
15
|
+
it "ObjectContext caches a reference to itself using the name :this_object_context" do
|
16
|
+
subject[:this_object_context].should == subject
|
17
|
+
end
|
18
|
+
|
19
|
+
it "an object can inject :this_object_context as a reference to its constructing ObjectContext" do
|
20
|
+
master = subject.get('master_of_puppets')
|
21
|
+
master.this_object_context.should == subject
|
22
|
+
|
23
|
+
master.this_object_context.get('master_of_puppets').should == master
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
28
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: conject
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &2152364960 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2152364960
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &2152364540 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2152364540
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: simplecov
|
38
|
-
requirement: &
|
38
|
+
requirement: &2152364060 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *2152364060
|
47
47
|
description: Enable Guice-like dependency injection and contextual object interactions.
|
48
48
|
email:
|
49
49
|
- david.crosby@atomicobject.com
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- spec/acceptance/regression/README
|
78
78
|
- spec/acceptance/regression/basic_composition_spec.rb
|
79
79
|
- spec/acceptance/regression/basic_object_creation_spec.rb
|
80
|
+
- spec/acceptance/regression/inject_object_context_spec.rb
|
80
81
|
- spec/acceptance/regression/nested_contexts_spec.rb
|
81
82
|
- spec/conject/borrowed_active_support_inflector_spec.rb
|
82
83
|
- spec/conject/class_ext_construct_with_spec.rb
|
@@ -96,6 +97,7 @@ files:
|
|
96
97
|
- spec/test_data/basic_composition/grass.rb
|
97
98
|
- spec/test_data/basic_composition/guest.rb
|
98
99
|
- spec/test_data/basic_composition/lobby.rb
|
100
|
+
- spec/test_data/basic_composition/master_of_puppets.rb
|
99
101
|
- spec/test_data/basic_composition/nails.rb
|
100
102
|
- spec/test_data/basic_composition/tv.rb
|
101
103
|
- spec/test_data/basic_composition/wood.rb
|
@@ -140,6 +142,7 @@ test_files:
|
|
140
142
|
- spec/acceptance/regression/README
|
141
143
|
- spec/acceptance/regression/basic_composition_spec.rb
|
142
144
|
- spec/acceptance/regression/basic_object_creation_spec.rb
|
145
|
+
- spec/acceptance/regression/inject_object_context_spec.rb
|
143
146
|
- spec/acceptance/regression/nested_contexts_spec.rb
|
144
147
|
- spec/conject/borrowed_active_support_inflector_spec.rb
|
145
148
|
- spec/conject/class_ext_construct_with_spec.rb
|
@@ -159,6 +162,7 @@ test_files:
|
|
159
162
|
- spec/test_data/basic_composition/grass.rb
|
160
163
|
- spec/test_data/basic_composition/guest.rb
|
161
164
|
- spec/test_data/basic_composition/lobby.rb
|
165
|
+
- spec/test_data/basic_composition/master_of_puppets.rb
|
162
166
|
- spec/test_data/basic_composition/nails.rb
|
163
167
|
- spec/test_data/basic_composition/tv.rb
|
164
168
|
- spec/test_data/basic_composition/wood.rb
|