Runtime assertions for Ruby literal.fun
ruby
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add `Literal::Value`

+92
+47
README.md
··· 61 61 end 62 62 ``` 63 63 64 + ## `Literal::Value` 65 + 66 + `Literal::Value` is like a `Literal::Data`, but specifically designed to enrich a single value. You could wrap a `String` as an `EmailAddress` or an `Integer` as a `UserId`. 67 + 68 + ```ruby 69 + EmailAddress = Literal::Value.define(String) 70 + UserID = Literal::Value.define(Integer) 71 + ``` 72 + 73 + We can create a new `UserID` like this: 74 + 75 + ```ruby 76 + user_id = UserID.new(123) 77 + ``` 78 + 79 + The input will be type-checked. If it's not frozen already, it will be duplicated and frozen. 80 + 81 + You can access the value by calling `value` on the object: 82 + 83 + ```ruby 84 + user_id.value # => 123 85 + ``` 86 + 87 + Because these value types are defined with an underlying type — in this case, `Integer` — the value objects also implement specific coercion methods. With an `Integer` value, you can call `to_i` to get the underlying value: 88 + 89 + ```ruby 90 + user_id.to_i # => 123 91 + ``` 92 + 93 + With the `EmailAddress`, we could call `to_s` or `to_str`. 94 + 95 + These value objects are designed to help you add extra type safety to your application. Let's say we have an operation that sends an email to a user. We could define the operation like this. 96 + 97 + ```ruby 98 + class EmailUser 99 + include Literal::Attributes 100 + 101 + attribute :user_id, UserID 102 + 103 + def call 104 + # ... 105 + end 106 + end 107 + ``` 108 + 109 + Now, if we try to call the operation with an `Integer` that isn't a `UserID`, we get a type error. 110 + 64 111 ## `Literal::Types` 65 112 66 113 `Literal::Attributes`, `Literal::Struct`, and `Literal::Data` all extend `Literal::Types`, which provide some advanced types including some generic-like collection types.
+45
lib/literal/value.rb
··· 1 + class Literal::Value 2 + class << self 3 + attr_reader :__type__ 4 + 5 + def define(type, &block) 6 + value_class = Class.new(self) do 7 + @__type__ = type 8 + 9 + case type 10 + when Literal::Types::_Class(String) 11 + alias_method :to_s, :value 12 + alias_method :to_str, :value 13 + when Literal::Types::_Class(Symbol) 14 + alias_method :to_sym, :value 15 + when Literal::Types::_Class(Integer) 16 + alias_method :to_i, :value 17 + when Literal::Types::_Class(Float) 18 + alias_method :to_f, :value 19 + when Literal::Types::_Class(Set) 20 + alias_method :to_set, :value 21 + when Literal::Types::_Class(Array) 22 + alias_method :to_a, :value 23 + alias_method :to_ary, :value 24 + when Literal::Types::_Class(Hash) 25 + alias_method :to_h, :value 26 + when Literal::Types::_Class(Proc) 27 + alias_method :to_proc, :value 28 + end 29 + end 30 + 31 + value_class.class_exec(&block) if block 32 + value_class.freeze 33 + value_class 34 + end 35 + end 36 + 37 + def initialize(value) 38 + type = self.class.__type__ 39 + raise Literal::TypeError, "Expected value: `#{value.inspect}` to be: `#{type.inspect}`." unless type === value 40 + @value = value.frozen? ? value : value.dup.freeze 41 + freeze 42 + end 43 + 44 + attr_reader :value 45 + end