Class: NRSER::Types::Type
Direct Known Subclasses
AnyType, AttrsType, Bounded, Combinator, Is, IsA, Maybe, Not, Respond, Shape, When, Where
Display Instance Methods collapse
-
#explain ⇒ String
A string that gives our best concise description of the type’s logic, in particular exposing any composite types that it’s made up of.
-
#name ⇒ String
What this type likes to be called (and displayed as by default).
Validation Instance Methods collapse
-
#check(*args, &block) ⇒ Object
Old name for #check! without the bang.
-
#check!(value, &details) ⇒ Object
Check that a ‘value` satisfies the type.
- #test(value) ⇒ Boolean deprecated Deprecated.
-
#test?(value) ⇒ Boolean
See if a value satisfies the type.
Loading Values Instance Methods collapse
- #from_data(data) ⇒ Object
-
#from_s(string) ⇒ Object
Load a value of this type from a string representation by passing ‘string` to the @from_s Proc.
- #has_from_data? ⇒ Boolean
-
#has_from_s? ⇒ Boolean
Test if the type knows how to load values from strings.
Dumping Values Instance Methods collapse
-
#has_to_data? ⇒ Boolean
Test if the type has custom information about how to convert it’s values into “data” - structures and values suitable for transportation and storage (JSON, etc.).
-
#to_data(value) ⇒ Object
Dumps a value of this type to “data” - structures and values suitable for transport and storage, such as dumping to JSON or YAML, etc.
Language Integration Instance Methods collapse
-
#===(value) ⇒ Boolean
Hook into Ruby’s *case subsumption* operator to allow usage in ‘case` statements! Forwards to #test?.
-
#builtin_inspect ⇒ Object
Inspecting.
- #inspect ⇒ Object
-
#respond_to?(name, include_all = false) ⇒ Boolean
Overridden to customize behavior for the #from_s, #from_data and #to_data methods - those methods are always defined, but we have #respond_to? return ‘false` if they lack the underlying instance variables needed to execute.
-
#to_s ⇒ String
Proxies to #name.
Derivation Instance Methods collapse
-
#intersection(*others) ⇒ NRSER::Types::Intersection
(also: #&, #and)
Return an intersection type satisfied by values that satisfy both ‘self` and all of `others`.
-
#not ⇒ NRSER::Types::Not
(also: #~)
Return a “negation” type satisfied by all values that do not satisfy ‘self`.
-
#union(*others) ⇒ NRSER::Types::Union
(also: #|, #or)
Return a union type satisfied by values that satisfy either ‘self` or and of `others`.
-
#xor(*others) ⇒ NRSER::Types::Intersection
(also: #^)
Return an *exclusive or* type satisfied by values that satisfy either ‘self` or `other` *but not both*.
Instance Method Summary collapse
-
#initialize(name: nil, from_s: nil, to_data: nil, from_data: nil) ⇒ Type
constructor
Instantiate a new ‘NRSER::Types::Type`.
Constructor Details
#initialize(name: nil, from_s: nil, to_data: nil, from_data: nil) ⇒ Type
Instantiate a new ‘NRSER::Types::Type`.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/nrser/types/type.rb', line 48 def initialize name: nil, from_s: nil, to_data: nil, from_data: nil @name = name @from_s = from_s @to_data = if to_data.nil? nil elsif to_data.respond_to?( :call ) to_data elsif to_data.respond_to?( :to_proc ) to_data.to_proc else raise TypeError.new binding.erb <<-ERB `to_data:` keyword arg must be `nil`, respond to `#call` or respond to `#to_proc`. Found value: <%= to_data.pretty_inspect %> (type <%= to_data.class %>) ERB end @from_data = if from_data.nil? nil elsif from_data.respond_to?( :call ) from_data elsif from_data.respond_to?( :to_proc ) from_data.to_proc else raise TypeError.new binding.erb <<-ERB `to_data:` keyword arg must be `nil`, respond to `#call` or respond to `#to_proc`. Found value: <%= from_data.pretty_inspect %> (type <%= from_data.class %>) ERB end end |
Instance Method Details
#===(value) ⇒ Boolean
Hook into Ruby’s *case subsumption* operator to allow usage in ‘case` statements! Forwards to #test?.
398 399 400 |
# File 'lib/nrser/types/type.rb', line 398 def === value test? value end |
#builtin_inspect ⇒ Object
Inspecting
Due to their combinatoric nature, types can quickly become large data hierarchies, and the built-in #inspect will produce a massive dump that’s distracting and hard to decipher.
#inspect is readily used in tools like ‘pry` and `rspec`, significantly impacting their usefulness when working with types.
As a solution, we alias the built-in ‘#inspect` as #builtin_inspect, so it’s available in situations where you really want all those gory details, and point #inspect to #explain.
453 |
# File 'lib/nrser/types/type.rb', line 453 alias_method :builtin_inspect, :inspect |
#check(*args, &block) ⇒ Object
Old name for #check! without the bang.
201 |
# File 'lib/nrser/types/type.rb', line 201 def check *args, █ check! *args, █ end |
#check!(value, &details) ⇒ Object
Check that a ‘value` satisfies the type.
190 191 192 193 194 195 196 197 198 |
# File 'lib/nrser/types/type.rb', line 190 def check! value, &details # success case return value if test? value raise NRSER::Types::CheckError.new \ value: value, type: self, details: details end |
#explain ⇒ String
A string that gives our best concise description of the type’s logic, in particular exposing any composite types that it’s made up of.
Used as the #name when a custom one is not provided.
Meant for inline display, so the result *should not* contain newlines.
Realizing subclasses should override this method, as this implementation only returns the class’ name (and just the last segment, for brevity’s sake).
139 140 141 |
# File 'lib/nrser/types/type.rb', line 139 def explain self.class.demod_name end |
#from_data(data) ⇒ Object
329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/nrser/types/type.rb', line 329 def from_data data unless has_from_data? raise NoMethodError, "#from_data not defined" end value = if @from_data @from_data.call data else custom_from_data data end check! value end |
#from_s(string) ⇒ Object
Load a value of this type from a string representation by passing ‘string` to the @from_s Proc.
Checks the value @from_s returns with #check! before returning it, so you know it satisfies this type.
Realizing classes **should not** need to override this - they can define a ‘#custom_from_s` instance method for it to use, allowing individual types to still override that by providing a `from_s:` proc keyword arg at construction. This also lets them avoid checking the returned value, since we do so here.
287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/nrser/types/type.rb', line 287 def from_s string unless has_from_s? raise NoMethodError, "#from_s not defined for type #{ name }" end value = if @from_s @from_s.call string else custom_from_s string end check! value end |
#has_from_data? ⇒ Boolean
310 311 312 313 314 |
# File 'lib/nrser/types/type.rb', line 310 def has_from_data? !@from_data.nil? || # Need the `true` second arg to include protected methods respond_to?( :custom_from_data, true ) end |
#has_from_s? ⇒ Boolean
When this method returns ‘true` it simply indicates that some method of loading from strings exists - the load itself can of course still fail.
Test if the type knows how to load values from strings.
Looks for the ‘@from_s` instance variable or a `#custom_from_s` method.
Realizing classes should only need to override this method to limited or expand the scope relative to parameterized types.
247 248 249 250 251 |
# File 'lib/nrser/types/type.rb', line 247 def has_from_s? !@from_s.nil? || # Need the `true` second arg to include protected methods respond_to?( :custom_from_s, true ) end |
#has_to_data? ⇒ Boolean
Test if the type has custom information about how to convert it’s values into “data” - structures and values suitable for transportation and storage (JSON, etc.).
If this method returns ‘true` then #to_data should succeed.
357 358 359 |
# File 'lib/nrser/types/type.rb', line 357 def has_to_data? ! @to_data.nil? end |
#inspect ⇒ Object
454 455 456 457 458 459 460 461 462 463 |
# File 'lib/nrser/types/type.rb', line 454 def inspect name = self.name explain = self.explain if name == explain explain else "#{ name } := #{ explain }" end end |
#intersection(*others) ⇒ NRSER::Types::Intersection Also known as: &, and
Return an intersection type satisfied by values that satisfy both ‘self` and all of `others`.
500 501 502 503 504 |
# File 'lib/nrser/types/type.rb', line 500 def intersection *others require_relative './combinators' NRSER::Types.intersection self, *others end |
#name ⇒ String
What this type likes to be called (and displayed as by default).
Custom names can be provided when constructing most types via the ‘name:` keyword, which allows thinking about composite and complicated types in simpler and application-specific terms.
Realizing subclasses **should not** override this method - they should pass a ‘name:` keyword up to #initialize, which sets the `@name` instance variable that is then used here.
If no name is provided to #initialize, this method will fall back to #explain.
115 116 117 |
# File 'lib/nrser/types/type.rb', line 115 def name @name || explain end |
#not ⇒ NRSER::Types::Not Also known as: ~
Return a “negation” type satisfied by all values that do not satisfy ‘self`.
532 533 534 535 536 |
# File 'lib/nrser/types/type.rb', line 532 def not require_relative './not' NRSER::Types.not self end |
#respond_to?(name, include_all = false) ⇒ Boolean
Overridden to customize behavior for the #from_s, #from_data and #to_data methods - those methods are always defined, but we have #respond_to? return ‘false` if they lack the underlying instance variables needed to execute.
425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/nrser/types/type.rb', line 425 def respond_to? name, include_all = false case name.to_sym when :from_s has_from_s? when :from_data has_from_data? when :to_data has_to_data? else super name, include_all end end |
#test(value) ⇒ Boolean
Old name for #test?.
177 |
# File 'lib/nrser/types/type.rb', line 177 def test value; test? value; end |
#test?(value) ⇒ Boolean
See if a value satisfies the type.
Realizing classes must implement this method.
This implementation just defines the API; it always raises AbstractMethodError.
165 166 167 |
# File 'lib/nrser/types/type.rb', line 165 def test? value raise NRSER::AbstractMethodError.new( self, __method__ ) end |
#to_data(value) ⇒ Object
Dumps a value of this type to “data” - structures and values suitable for transport and storage, such as dumping to JSON or YAML, etc.
371 372 373 374 375 376 377 |
# File 'lib/nrser/types/type.rb', line 371 def to_data value if @to_data.nil? raise NoMethodError, "#to_data not defined" end @to_data.call value end |
#union(*others) ⇒ NRSER::Types::Union Also known as: |, or
Return a union type satisfied by values that satisfy either ‘self` or and of `others`.
482 483 484 485 486 |
# File 'lib/nrser/types/type.rb', line 482 def union *others require_relative './combinators' NRSER::Types.union self, *others end |
#xor(*others) ⇒ NRSER::Types::Intersection Also known as: ^
Return an *exclusive or* type satisfied by values that satisfy either ‘self` or `other` *but not both*.
518 519 520 521 522 |
# File 'lib/nrser/types/type.rb', line 518 def xor *others require_relative './combinators' NRSER::Types.xor self, *others end |