From fd73a011b2d4dec97949f3549b21ab2c214cf176 Mon Sep 17 00:00:00 2001 From: kustosz Date: Mon, 8 Sep 2014 01:35:21 +0200 Subject: [PATCH 1/3] add outline of class injections feature --- features/class_injections.feature | 47 +++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 features/class_injections.feature diff --git a/features/class_injections.feature b/features/class_injections.feature new file mode 100644 index 0000000..c8e5f68 --- /dev/null +++ b/features/class_injections.feature @@ -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." + """ + From 2b229098946da9c126d085ae8c8d830870329d92 Mon Sep 17 00:00:00 2001 From: kustosz Date: Wed, 10 Sep 2014 01:32:28 +0200 Subject: [PATCH 2/3] basic functionality for class injecting --- features/class_injections.feature | 4 ++-- lib/dependor.rb | 8 ++++++++ lib/dependor/class_takes_ext.rb | 13 +++++++++++++ lib/dependor/injectable_class.rb | 8 ++++++++ lib/dependor/instantiator.rb | 5 +++++ lib/dependor/object_definition.rb | 5 ++++- lib/dependor/subclass_builder.rb | 15 +++++++++++++++ 7 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 lib/dependor/class_takes_ext.rb create mode 100644 lib/dependor/injectable_class.rb create mode 100644 lib/dependor/subclass_builder.rb diff --git a/features/class_injections.feature b/features/class_injections.feature index c8e5f68..020be2d 100644 --- a/features/class_injections.feature +++ b/features/class_injections.feature @@ -41,7 +41,7 @@ Feature: Class Injections """ Then the output should be: """ - "For the King!" - "Welcome to FarFarAway's army, fellow Sword Master." + For the King! + Welcome to FarFarAway's army, fellow Sword Master. """ diff --git a/lib/dependor.rb b/lib/dependor.rb index be4f0d2..945c342 100644 --- a/lib/dependor.rb +++ b/lib/dependor.rb @@ -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) @@ -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 diff --git a/lib/dependor/class_takes_ext.rb b/lib/dependor/class_takes_ext.rb new file mode 100644 index 0000000..bc4ca51 --- /dev/null +++ b/lib/dependor/class_takes_ext.rb @@ -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 diff --git a/lib/dependor/injectable_class.rb b/lib/dependor/injectable_class.rb new file mode 100644 index 0000000..7c59e7a --- /dev/null +++ b/lib/dependor/injectable_class.rb @@ -0,0 +1,8 @@ +module Dependor + module InjectableClass + attr_accessor :class_takes + def add_dependencies(*names) + @class_takes = names + end + end +end diff --git a/lib/dependor/instantiator.rb b/lib/dependor/instantiator.rb index 1eeb5dc..4f41333 100644 --- a/lib/dependor/instantiator.rb +++ b/lib/dependor/instantiator.rb @@ -20,6 +20,11 @@ def new(klass_name, overwrites = {}) klass.new(args) end + def get_class(klass_name) + klass = @class_lookup.lookup(klass_name) + SubclassBuilder.subclass(klass, @injector) + end + def method_missing(name, *args, &block) super if args.any? @injector[name] diff --git a/lib/dependor/object_definition.rb b/lib/dependor/object_definition.rb index 3d0f45b..1b26b40 100644 --- a/lib/dependor/object_definition.rb +++ b/lib/dependor/object_definition.rb @@ -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 diff --git a/lib/dependor/subclass_builder.rb b/lib/dependor/subclass_builder.rb new file mode 100644 index 0000000..4bbeb67 --- /dev/null +++ b/lib/dependor/subclass_builder.rb @@ -0,0 +1,15 @@ +module Dependor + class SubclassBuilder + def self.subclass(klass, injector) + return klass unless klass.respond_to?(:class_takes) + + Class.new(klass) do + klass.class_takes.each do |dependency| + define_singleton_method dependency do + injector[dependency] + end + end + end + end + end +end From 8bc7e2dd8656efc38ca827d8d6e929390782d454 Mon Sep 17 00:00:00 2001 From: kustosz Date: Fri, 12 Sep 2014 13:10:08 +0200 Subject: [PATCH 3/3] add overwrites for class injections --- lib/dependor/core_ext.rb | 1 + lib/dependor/instantiator.rb | 4 ++-- lib/dependor/subclass_builder.rb | 15 +++++++++++---- spec/features/automagic_injection_spec.rb | 13 +++++++++++++ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/dependor/core_ext.rb b/lib/dependor/core_ext.rb index 26bfb95..cd4f397 100644 --- a/lib/dependor/core_ext.rb +++ b/lib/dependor/core_ext.rb @@ -1,2 +1,3 @@ Object.extend Dependor::TakesExt +Object.extend Dependor::ClassTakesExt Transient = Dependor::Transient diff --git a/lib/dependor/instantiator.rb b/lib/dependor/instantiator.rb index 4f41333..18d399a 100644 --- a/lib/dependor/instantiator.rb +++ b/lib/dependor/instantiator.rb @@ -20,9 +20,9 @@ def new(klass_name, overwrites = {}) klass.new(args) end - def get_class(klass_name) + def get_class(klass_name, overwrites = {}) klass = @class_lookup.lookup(klass_name) - SubclassBuilder.subclass(klass, @injector) + SubclassBuilder.subclass(klass, @injector, overwrites) end def method_missing(name, *args, &block) diff --git a/lib/dependor/subclass_builder.rb b/lib/dependor/subclass_builder.rb index 4bbeb67..c152180 100644 --- a/lib/dependor/subclass_builder.rb +++ b/lib/dependor/subclass_builder.rb @@ -1,12 +1,19 @@ module Dependor class SubclassBuilder - def self.subclass(klass, injector) + 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 |dependency| - define_singleton_method dependency do - injector[dependency] + 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 diff --git a/spec/features/automagic_injection_spec.rb b/spec/features/automagic_injection_spec.rb index 7ab25bf..d9d0e12 100644 --- a/spec/features/automagic_injection_spec.rb +++ b/spec/features/automagic_injection_spec.rb @@ -33,6 +33,10 @@ class Crown class MagicalSword < Sword extend Takes(:name) end + + class Kingdom + extend ClassTakes(:king, :name) + end end end @@ -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 } @@ -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]