Class: Wirer::Dependency
- Inherits:
-
Object
- Object
- Wirer::Dependency
- Defined in:
- lib/wirer/dependency.rb
Overview
Models a dependency, and has class methods for interpreting dsl-like argument lists that declare dependencies.
dependency :foo, Class, :features => [:feature, :feature], :optional => true dependency :foo, Class, :features => [:feature, :feature], :multiple => true
dependency :foo, Class, :optional => true dependency :foo, :feature, :another_feature, :optional => true, :constructor => true
Constant Summary collapse
- OPTION_NAMES =
[:class, :module, :features, :prefer, :multiple, :optional, :factory]
- PREFER_DEFAULT =
By default, dependencies will :prefer => :default. This means if you want to force one factory to be preferred over another in a given situation, you can just add (or wrap it with) a provided feature name of :default.
:default
Instance Attribute Summary collapse
-
#preferred_features ⇒ Object
readonly
Returns the value of attribute preferred_features.
-
#required_features ⇒ Object
readonly
Returns the value of attribute required_features.
Class Method Summary collapse
- .new_from_arg_or_args_list(arg_or_args_list) ⇒ Object
- .new_from_args(*args) ⇒ Object
- .normalise_arg_or_args_list(arg_or_args_list) ⇒ Object
- .normalise_args(*args) ⇒ Object
Instance Method Summary collapse
- #===(factory) ⇒ Object
- #check_argument(argument_name, argument, strict_type_checks = false) ⇒ Object
-
#factory? ⇒ Boolean
Specifying :factory => true on a dependency means that you won’t be given an actual instance of the class you asked for, but rather you’ll be given a simple factory wrapper which allows you to construct your own instance(s), with any dependencies pre-supplied.
-
#initialize(options = {}) ⇒ Dependency
constructor
A new instance of Dependency.
- #inspect ⇒ Object
- #match_factories(available_factories) ⇒ Object
-
#matches_required_class(factory) ⇒ Object
if the required_class can’t be resolved (ie a class of that name doesn’t even exist) then nothing will match.
- #matches_required_features(factory) ⇒ Object
- #multiple? ⇒ Boolean
- #optional? ⇒ Boolean
-
#required_class ⇒ Object
A string class name may be supplied as the :class arg to the constructor, in which case we only attempt to resolve the actual class from it the first time .required_class is requested.
- #requirements_to_s ⇒ Object
- #with_options(options) ⇒ Object
Constructor Details
#initialize(options = {}) ⇒ Dependency
Returns a new instance of Dependency.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/wirer/dependency.rb', line 48 def initialize( = {}) required_class = [:class] || [:module] case required_class when Module @required_class = required_class when String @required_class_name = required_class when NilClass @required_class = nil else raise ArgumentError, "required :class for a Dependency must be a Module or Class, or a String name of a Module or Class" end @required_features = [:features] && [*[:features]] @multiple = [:multiple] || false @optional = [:optional] || false @factory = [:factory] || false if @multiple raise ArgumentError, "preferred features don't make sense for a :multiple depedency" if .has_key?(:prefer) else @preferred_features = [*.fetch(:prefer, PREFER_DEFAULT) || []] end end |
Instance Attribute Details
#preferred_features ⇒ Object (readonly)
Returns the value of attribute preferred_features.
72 73 74 |
# File 'lib/wirer/dependency.rb', line 72 def preferred_features @preferred_features end |
#required_features ⇒ Object (readonly)
Returns the value of attribute required_features.
72 73 74 |
# File 'lib/wirer/dependency.rb', line 72 def required_features @required_features end |
Class Method Details
.new_from_arg_or_args_list(arg_or_args_list) ⇒ Object
17 18 19 |
# File 'lib/wirer/dependency.rb', line 17 def self.new_from_arg_or_args_list(arg_or_args_list) new(normalise_arg_or_args_list(arg_or_args_list)) end |
.new_from_args(*args) ⇒ Object
13 14 15 |
# File 'lib/wirer/dependency.rb', line 13 def self.new_from_args(*args) new(normalise_args(*args)) end |
.normalise_arg_or_args_list(arg_or_args_list) ⇒ Object
21 22 23 24 25 26 27 |
# File 'lib/wirer/dependency.rb', line 21 def self.normalise_arg_or_args_list(arg_or_args_list) case arg_or_args_list when Hash then arg_or_args_list when Array then normalise_args(*arg_or_args_list) else normalise_args(arg_or_args_list) end end |
.normalise_args(*args) ⇒ Object
29 30 31 32 33 34 35 36 37 38 |
# File 'lib/wirer/dependency.rb', line 29 def self.normalise_args(*args) = args.last.is_a?(Hash) ? args.pop : {} args.each do |requirement| case requirement when Module then [:class] = requirement else ([:features] ||= []) << requirement end end end |
Instance Method Details
#===(factory) ⇒ Object
140 141 142 |
# File 'lib/wirer/dependency.rb', line 140 def ===(factory) factory.is_a?(Factory::Interface) && matches_required_class(factory) && matches_required_features(factory) end |
#check_argument(argument_name, argument, strict_type_checks = false) ⇒ Object
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/wirer/dependency.rb', line 144 def check_argument(argument_name, argument, strict_type_checks=false) if @multiple raise ArgumentError, "expected Array for argument #{argument_name}" unless argument.is_a?(Array) if argument.empty? if @optional then return else raise ArgumentError, "expected at least one value for argument #{argument_name}" end end argument.each do |value| if @factory unless value.respond_to?(:new) raise ArgumentError, "expected Array of factory-like objects which respond_to?(:new) for argument #{argument_name}" end elsif strict_type_checks && required_class && !value.is_a?(required_class) raise ArgumentError, "expected Array of #{required_class} for argument #{argument_name}" end end else if argument.nil? if @optional then return else raise ArgumentError, "expected argument #{argument_name}" end end if @factory unless argument.respond_to?(:new) raise ArgumentError, "expected factory-like object which respond_to?(:new) for argument #{argument_name}" end elsif strict_type_checks && required_class && !argument.is_a?(required_class) raise ArgumentError, "expected #{required_class} for argument #{argument_name}" end end end |
#factory? ⇒ Boolean
Specifying :factory => true on a dependency means that you won’t be given an actual instance of the class you asked for, but rather you’ll be given a simple factory wrapper which allows you to construct your own instance(s), with any dependencies pre-supplied. See Factory::CurriedDependencies
80 |
# File 'lib/wirer/dependency.rb', line 80 def factory?; @factory; end |
#inspect ⇒ Object
105 106 107 108 109 110 111 112 113 |
# File 'lib/wirer/dependency.rb', line 105 def inspect description = [ @factory ? "factory for #{requirements_to_s}" : requirements_to_s, ("optional" if @optional), ("multiple" if @multiple), ("preferring features #{@preferred_features.inspect}" if @preferred_features && !@preferred_features.empty?) ].compact.join(', ') "#<#{self.class} on #{description}>" end |
#match_factories(available_factories) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/wirer/dependency.rb', line 115 def match_factories(available_factories) candidates = available_factories.select {|f| self === f} if !@optional && candidates.length == 0 raise DependencyFindingError, "No available factories matching #{requirements_to_s}" end if @multiple candidates.map! {|c| yield c} if block_given?; candidates else candidate = if candidates.length > 1 if @preferred_features.empty? raise DependencyFindingError, "More than one factory available matching #{requirements_to_s}" else unique_preferred_factory(candidates) end else candidates.first end if block_given? yield candidate if candidate else candidate end end end |
#matches_required_class(factory) ⇒ Object
if the required_class can’t be resolved (ie a class of that name doesn’t even exist) then nothing will match.
178 179 180 181 182 183 184 |
# File 'lib/wirer/dependency.rb', line 178 def matches_required_class(factory) begin !required_class || factory.provides_class <= required_class rescue NameError false end end |
#matches_required_features(factory) ⇒ Object
186 187 188 |
# File 'lib/wirer/dependency.rb', line 186 def matches_required_features(factory) !@required_features || @required_features.all? {|feature| factory.provides_features.include?(feature)} end |
#multiple? ⇒ Boolean
73 |
# File 'lib/wirer/dependency.rb', line 73 def multiple?; @multiple; end |
#optional? ⇒ Boolean
74 |
# File 'lib/wirer/dependency.rb', line 74 def optional?; @optional; end |
#required_class ⇒ Object
A string class name may be supplied as the :class arg to the constructor, in which case we only attempt to resolve the actual class from it the first time .required_class is requested.
This helps avoid introducing undesired load order dependencies between classes using Wirer::Factory::ClassDSL.
86 87 88 89 |
# File 'lib/wirer/dependency.rb', line 86 def required_class return @required_class if defined?(@required_class) @required_class = @required_class_name.split("::").inject(Object, :const_get) end |
#requirements_to_s ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/wirer/dependency.rb', line 91 def requirements_to_s [ begin case required_class when ::Class then "class #{@required_class}" when ::Module then "module #{@required_class}" end rescue NameError "class or module name #{@required_class_name} (not currently loaded!)" end, @required_features && "features #{@required_features.inspect}" ].compact.join(" and ") end |
#with_options(options) ⇒ Object
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/wirer/dependency.rb', line 190 def () = { :multiple => @multiple, :optional => @optional, :class => required_class, :features => @required_features, :prefer => @preferred_features } new_required_class = [:class] and begin if required_class && !(new_required_class <= required_class) raise "Required class #{new_required_class} not compatible with existing requirement for #{required_class}" end [:class] = new_required_class end new_required_features = [:features] and begin [:features] ||= [] [:features] |= [*new_required_features] end new_preferred_features = [:prefer] and begin [:prefer] ||= [] [:prefer] |= [*new_preferred_features] end self.class.new() end |