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
methods
so that all arguments are filtered through #check_constraints. -
.with_arguments_constrained(*methods, &block) ⇒ Object
Wrap
methods
so that some arguments are filtered through #check_constraints. -
.without_constraints(*methods) ⇒ Object
Delegate
methods
to @constrained_value without taking any measures.
Instance Method Summary collapse
- #check_constrained_value ⇒ Object
-
#check_constraints(object) ⇒ Object
Check if
object
complies 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 |