Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions features/class_injections.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@todo
Feature: Class Injections

Dependor allows to inject classes and class properties

@ignore
Scenario: Injecting class properties
Given dependor is required with core extensions
And a class is defined:
"""ruby
class Knight
extend Dependor.takes(:king)

def embark_on_a_quest
puts "For the King!"
king.greet_knight
end
end
"""
And a class is defined:
"""ruby
class King
extend Dependor.class_takes(:monarchy_name)

def self.greet_knight
puts "Welcome to #{monarchy_name}'s army, fellow Sword Master."
end
end
"""
When I run:
"""ruby
registry = Dependor.registry do
autoinject(Object)
king(as: :class)
monarchy_name { "FarFarAway" }
end

knight = registry[:knight]

knight.embark_on_a_quest
"""
Then the output should be:
"""
For the King!
Welcome to FarFarAway's army, fellow Sword Master.
"""

8 changes: 8 additions & 0 deletions lib/dependor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
require "dependor/registry"
require "dependor/lookup_chain"

require "dependor/injectable_class"
require "dependor/class_takes_ext"
require "dependor/subclass_builder"

module Dependor
def self.registry(&block)
Registry.build(&block)
Expand All @@ -22,4 +26,8 @@ def self.registry(&block)
def self.takes(*dependency_names)
TakesExt.Takes(*dependency_names)
end

def self.class_takes(*dependency_names)
ClassTakesExt.ClassTakes(*dependency_names)
end
end
13 changes: 13 additions & 0 deletions lib/dependor/class_takes_ext.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Dependor
module ClassTakesExt
module_function
def ClassTakes(*names)
Module.new do
define_singleton_method :extended do |klass|
klass.send(:extend, Dependor::InjectableClass)
klass.add_dependencies(*names)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/dependor/core_ext.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Object.extend Dependor::TakesExt
Object.extend Dependor::ClassTakesExt
Transient = Dependor::Transient
8 changes: 8 additions & 0 deletions lib/dependor/injectable_class.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Dependor
module InjectableClass
attr_accessor :class_takes
def add_dependencies(*names)
@class_takes = names
end
end
end
5 changes: 5 additions & 0 deletions lib/dependor/instantiator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ def new(klass_name, overwrites = {})
klass.new(args)
end

def get_class(klass_name, overwrites = {})
klass = @class_lookup.lookup(klass_name)
SubclassBuilder.subclass(klass, @injector, overwrites)
end

def method_missing(name, *args, &block)
super if args.any?
@injector[name]
Expand Down
5 changes: 4 additions & 1 deletion lib/dependor/object_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ def self.default_for(klass)
new(klass.name.to_sym, opts, proc{ new(klass) })
end

def self.build(name, transient: false, &block)
def self.build(name, transient: false, as: :instance, &block)
if as == :class
block ||= proc{ get_class(name) }
end
block ||= proc{ new(name) }
new(name, {transient: transient}, block)
end
Expand Down
22 changes: 22 additions & 0 deletions lib/dependor/subclass_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Dependor
class SubclassBuilder
def self.get_dependency(name, injector, overwrites)
return overwrites.fetch(name) { @injector[name] }
end

def self.subclass(klass, injector, overwrites = {})
return klass unless klass.respond_to?(:class_takes)

Class.new(klass) do
klass.class_takes.each do |name|
define_singleton_method name do
unless instance_variable_get("@#{name}")
instance_variable_set("@#{name}", overwrites.fetch(name) { injector[name] })
end
instance_variable_get("@#{name}")
end
end
end
end
end
end
13 changes: 13 additions & 0 deletions spec/features/automagic_injection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class Crown
class MagicalSword < Sword
extend Takes(:name)
end

class Kingdom
extend ClassTakes(:king, :name)
end
end
end

Expand Down Expand Up @@ -61,6 +65,7 @@ class MagicalSword < Sword
excalibur { new(:MagicalSword, name: "Excalibur") }
king { arthur }
knights { [lancelot, galahad] }
kingdom { get_class(:Kingdom, name: "Far Far Away") }
end
}

Expand All @@ -76,6 +81,14 @@ class MagicalSword < Sword
expect(registry[:king]).to equal(registry[:arthur])
end

it "allows class injections" do
expect(registry[:kingdom]).to be_an_instance_of(Class)
end

it "allows for overrides when injecting classes" do
expect(registry[:kingdom].name).to eq("Far Far Away")
end

it "makes objects singletons by default" do
first_camelot = registry[:camelot]
second_camelot = registry[:camelot]
Expand Down