Class: Constraint::Shell
- Inherits:
-
Object
- Object
- Constraint::Shell
- Extended by:
- Helper, Forwardable
- Includes:
- Helper
- Defined in:
- lib/constraint.rb
Overview
If CONSTRAINT_DISABLE is set to true before loading this library, constraint checking is turned off.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#constrained_value ⇒ Object
readonly
The actual value that is covered by the Shell.
Attributes included from Helper
#constaint_descriptions, #constaint_handlers, #constraint_attributes, #constraints
Class Method Summary collapse
- .delegate_to_constrained_value(meth, alias_name) ⇒ Object
- .inherited(subclass) ⇒ Object
-
.with_all_arguments_constrained(*methods) ⇒ Object
Wrap
methodsso that all arguments are filtered through #check_constraints. -
.with_arguments_constrained(*methods, &block) ⇒ Object
Wrap
methodsso that some arguments are filtered through #check_constraints. -
.without_constraints(*methods) ⇒ Object
Delegate
methodsto @constrained_value without taking any measures.
Instance Method Summary collapse
- #check_constrained_value ⇒ Object
-
#check_constraints(object) ⇒ Object
Check if
objectcomplies with @constraints. - #clone ⇒ Object
-
#collect_constrained_values ⇒ Object
Collect all values.
-
#collect_constrained_values_until(&block) ⇒ Object
Return an array of constrained values starting from the current value until a value for which block is true.
-
#collect_constrained_values_while(&block) ⇒ Object
Return an array of constrained values starting from the current value until a value for which block is true.
- #dup ⇒ Object
-
#generate_constrained_value(value) ⇒ Object
Generate the next value for #next_constrained_value.
-
#handle_constraint_violation(name, object) ⇒ Object
Handle constraint violations.
-
#initialize(core = nil, predecessor = nil, &block) ⇒ Shell
constructor
def ===(other) if other.kind_of?(Shell) super else @constrained_value === other end end.
-
#method_missing(method, *args, &block) ⇒ Object
Delegate unknown methods to @constrained_value and filter the return value through #check_constraints.
-
#new_constrained ⇒ Object
Construct a new instance.
-
#next_constrained_value(value = nil) ⇒ Object
Return an object with an increased value (see #generate_constrained_value).
-
#next_constrained_value!(value = nil) ⇒ Object
“Increase” the current value to the next valid one (see #generate_constrained_value).
-
#process_constrained_value ⇒ Object
Filters @constrained_value through the supplied block (usually a call to #check_constraints) and collects the output in @constrained_value).
-
#respond_to?(*args) ⇒ Boolean
Forward to @constrained_value.
-
#with_constraints(new_value) ⇒ Object
Maintain object identity according to the return value of
block.
Methods included from Helper
and_constraint, constraint_attr, log_constraint_exception, on_constraint_violation, or_constraint, replicate_constraints
Constructor Details
#initialize(core = nil, predecessor = nil, &block) ⇒ Shell
282 283 284 285 286 287 288 289 |
# File 'lib/constraint.rb', line 282 def initialize(core=nil, predecessor=nil, &block) predecessor ||= self.class replicate_constraints(predecessor) @constrained_value = core || new_constrained unless CONSTRAINT_DISABLE check_constrained_value end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
Delegate unknown methods to @constrained_value and filter the return value through #check_constraints.
See also the notes on #with_constraints and Shell.with_arguments_constrained.
462 463 464 |
# File 'lib/constraint.rb', line 462 def method_missing(method, *args, &block) with_constraints(@constrained_value.send(method, *args, &block)) end |
Instance Attribute Details
#constrained_value ⇒ Object (readonly)
The actual value that is covered by the Shell.
190 191 192 |
# File 'lib/constraint.rb', line 190 def constrained_value @constrained_value end |
Class Method Details
.delegate_to_constrained_value(meth, alias_name) ⇒ Object
201 202 203 204 |
# File 'lib/constraint.rb', line 201 def delegate_to_constrained_value(meth, alias_name) class_eval %{alias #{alias_name} #{meth}} def_delegator(:@constrained_value, meth) end |
.inherited(subclass) ⇒ Object
206 207 208 209 210 211 |
# File 'lib/constraint.rb', line 206 def inherited(subclass) parent = self subclass.class_eval do replicate_constraints(parent) end end |
.with_all_arguments_constrained(*methods) ⇒ Object
Wrap methods so that all arguments are filtered through #check_constraints.
- methods
-
Method names as symbols
216 217 218 219 220 |
# File 'lib/constraint.rb', line 216 def with_all_arguments_constrained(*methods) with_arguments_constrained(*methods) do |checker, *args| args.collect {|e| checker.call(e)} end end |
.with_arguments_constrained(*methods, &block) ⇒ Object
Wrap methods so that some arguments are filtered through #check_constraints. This is useful in two situations:
-
if filtering the return value would be inefficient – e.g. with methods performing trivial Array operations where the return value will comply with the constraints if its argument do.
-
if the return value of the method call is not an instance of the actual value’s class (only then will the value’s integrity be checked)
See also the notes on Shell#with_constraints.
- methods
-
Method names as symbols
- block
-
Select the arguments that should be filtered
238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/constraint.rb', line 238 def with_arguments_constrained(*methods, &block) unless CONSTRAINT_DISABLE methods.each do |method| define_method(method) do |*args| unless args.empty? checker = lambda {|a| check_constraints(a)} args = block.call(checker, *args) end with_constraints(@constrained_value.send(method, *args)) end end end end |
.without_constraints(*methods) ⇒ Object
Delegate methods to @constrained_value without taking any measures.
254 255 256 257 258 259 260 |
# File 'lib/constraint.rb', line 254 def without_constraints(*methods) methods.each do |method| define_method(method) do |*args| with_constraints(@constrained_value.send(method, *args)) end end end |
Instance Method Details
#check_constrained_value ⇒ Object
387 388 389 |
# File 'lib/constraint.rb', line 387 def check_constrained_value process_constrained_value {|e| check_constraints(e)} end |
#check_constraints(object) ⇒ Object
Check if object complies with @constraints.
397 398 399 |
# File 'lib/constraint.rb', line 397 def check_constraints(object) object end |
#clone ⇒ Object
295 296 297 |
# File 'lib/constraint.rb', line 295 def clone replicate_constraint_shell(super) end |
#collect_constrained_values ⇒ Object
Collect all values. Shell#generate_constrained_value must provide a means to decide when no more values should be generated.
371 372 373 374 375 376 377 |
# File 'lib/constraint.rb', line 371 def collect_constrained_values collect_constrained_values_control do |val, acc| acc << self.dup next_constrained_value! true end end |
#collect_constrained_values_until(&block) ⇒ Object
Return an array of constrained values starting from the current value until a value for which block is true. Requires #generate_constrained_value to be defined.
358 359 360 361 362 363 364 365 366 |
# File 'lib/constraint.rb', line 358 def collect_constrained_values_until(&block) collect_constrained_values_control do |val, acc| if (rv = !block.call(val)) acc << self.dup next_constrained_value! end rv end end |
#collect_constrained_values_while(&block) ⇒ Object
Return an array of constrained values starting from the current value until a value for which block is true. Requires #generate_constrained_value to be defined.
345 346 347 348 349 350 351 352 353 |
# File 'lib/constraint.rb', line 345 def collect_constrained_values_while(&block) collect_constrained_values_control do |val, acc| if (rv = block.call(val)) acc << self.dup next_constrained_value! end rv end end |
#dup ⇒ Object
291 292 293 |
# File 'lib/constraint.rb', line 291 def dup replicate_constraint_shell(super) end |
#generate_constrained_value(value) ⇒ Object
Generate the next value for #next_constrained_value.
307 308 309 |
# File 'lib/constraint.rb', line 307 def generate_constrained_value(value) raise Constraint::NoMoreValues, "No more values: #{value}" end |
#handle_constraint_violation(name, object) ⇒ Object
Handle constraint violations. Can be overwritten by subclasses in order to rescue the constraint. The return value will replace the original object.
- name
-
The constraint’s name that didn’t succeed
- object
-
The object that caused the violation
438 439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/constraint.rb', line 438 def handle_constraint_violation(name, object) handlers = @constaint_handlers[name] if handlers for h in handlers begin return h.call(self, object) rescue Constraint::Violation next end end end raise_constraint_violation(name, object) end |
#new_constrained ⇒ Object
Construct a new instance. By default, this method calls generate_constrained_value(nil).
301 302 303 304 |
# File 'lib/constraint.rb', line 301 def new_constrained generate_constrained_value(nil) # raise 'Subclass responsibility' end |
#next_constrained_value(value = nil) ⇒ Object
Return an object with an increased value (see #generate_constrained_value).
338 339 340 |
# File 'lib/constraint.rb', line 338 def next_constrained_value(value=nil) self.clone.next_constrained_value!(value) end |
#next_constrained_value!(value = nil) ⇒ Object
“Increase” the current value to the next valid one (see #generate_constrained_value). Modify the object in-place.
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/constraint.rb', line 313 def next_constrained_value!(value=nil) begin cv = @constrained_value if value @constrained_value = value end begin while (@constrained_value = generate_constrained_value(@constrained_value)) check_constrained_value return self end rescue Constraint::NoMoreValues => e raise e rescue Constraint::Violation => e retry end raise_constraint_violation(nil, value, "No more values: #{value}") rescue Exception => e @constrained_value = cv raise e end end |
#process_constrained_value ⇒ Object
Filters @constrained_value through the supplied block (usually a call to #check_constraints) and collects the output in @constrained_value).
- block
-
The filter
383 384 385 |
# File 'lib/constraint.rb', line 383 def process_constrained_value @constrained_value = yield @constrained_value end |
#respond_to?(*args) ⇒ Boolean
Forward to @constrained_value.
471 472 473 |
# File 'lib/constraint.rb', line 471 def respond_to?(*args) super or @constrained_value.respond_to?(*args) end |
#with_constraints(new_value) ⇒ Object
Maintain object identity according to the return value of block. This method can be used when overwriting specific methods and if you don’t want to use the provided wrapper methods (e.g., Shell.with_arguments_constrained).
If the return value is of the same kind as @constrained_value and if block can’t be trusted, then the return value is filtered through #check_constraints.
In order to filter the arguments of methods that don’t return an object of the same kind as @constrained_value, you have to define an explicit delegator using Shell.with_arguments_constrained and friends.
- new_value
-
The result from the method call
- block
-
Optional, called @constrained_value is
new_value
422 423 424 425 426 427 428 429 430 431 |
# File 'lib/constraint.rb', line 422 def with_constraints(new_value) if new_value.equal?(@constrained_value) yield if block_given? return self elsif new_value.instance_of?(@constrained_value.class) return self.class.new(new_value, self) else return new_value end end |