Class: Wirer::Dependency
- Inherits:
-
Object
- Object
- Wirer::Dependency
- Defined in:
- lib/wirer/dependency.rb
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.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/wirer/dependency.rb', line 45 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.
69 70 71 |
# File 'lib/wirer/dependency.rb', line 69 def preferred_features @preferred_features end |
#required_features ⇒ Object (readonly)
Returns the value of attribute required_features.
69 70 71 |
# File 'lib/wirer/dependency.rb', line 69 def required_features @required_features end |
Class Method Details
.new_from_arg_or_args_list(arg_or_args_list) ⇒ Object
14 15 16 |
# File 'lib/wirer/dependency.rb', line 14 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
10 11 12 |
# File 'lib/wirer/dependency.rb', line 10 def self.new_from_args(*args) new(normalise_args(*args)) end |
.normalise_arg_or_args_list(arg_or_args_list) ⇒ Object
18 19 20 21 22 23 24 |
# File 'lib/wirer/dependency.rb', line 18 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
26 27 28 29 30 31 32 33 34 35 |
# File 'lib/wirer/dependency.rb', line 26 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
137 138 139 |
# File 'lib/wirer/dependency.rb', line 137 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
141 142 143 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 |
# File 'lib/wirer/dependency.rb', line 141 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
77 |
# File 'lib/wirer/dependency.rb', line 77 def factory?; @factory; end |
#inspect ⇒ Object
102 103 104 105 106 107 108 109 110 |
# File 'lib/wirer/dependency.rb', line 102 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
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/wirer/dependency.rb', line 112 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.
175 176 177 178 179 180 181 |
# File 'lib/wirer/dependency.rb', line 175 def matches_required_class(factory) begin !required_class || factory.provides_class <= required_class rescue NameError false end end |
#matches_required_features(factory) ⇒ Object
183 184 185 |
# File 'lib/wirer/dependency.rb', line 183 def matches_required_features(factory) !@required_features || @required_features.all? {|feature| factory.provides_features.include?(feature)} end |
#multiple? ⇒ Boolean
70 |
# File 'lib/wirer/dependency.rb', line 70 def multiple?; @multiple; end |
#optional? ⇒ Boolean
71 |
# File 'lib/wirer/dependency.rb', line 71 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.
83 84 85 86 |
# File 'lib/wirer/dependency.rb', line 83 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
88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/wirer/dependency.rb', line 88 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
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/wirer/dependency.rb', line 187 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 |