![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
A result encapsulation class, like the Either
type in Haskell.
Given
require "lab42/result/autoimport" # equivalent to: the following lines:
# require "lab42/result"
# Result = Lab42::Result
let(:ok) { Result.ok(42) }
let(:error) { Result.error("oh no!") }
Then it's ok (I guess):
expect(ok).to be_ok
And its value might be of interest
expect(ok.value).to eq(42)
And it will execute the block passed to the if_ok
method
x = nil
expect(ok.if_ok {x = 42}).to eq(42)
expect(x).to eq(42)
And the value is passed in
expect(ok.if_ok{it/2}).to eq(21)
But not the one passed to the if_error
method
x = nil
expect(ok.if_error {x = 42}).to be_nil
expect(x).to be_nil
And will not raise any error
expect{ok.raise!}.not_to raise_error
But you must not access the error method
expect{ ok.error }.to raise_error(Lab42::Result::IllegalMonitorState, /must not invoke the error method on an ok result/)
And the same holds for the exception method
expect{ ok.exception }.to raise_error(Lab42::Result::IllegalMonitorState, /must not invoke the exception method on an ok result/)
But it is not an error
expect(error).not_to be_ok
And its value cannot be accessed anymore
expect{ error.value }.to raise_error(Lab42::Result::IllegalMonitorState, /must not invoke the value method on an error result/)
But of course now we can call the error methods #error
and #exception
expect(error.error).to eq("oh no!")
expect(error.exception).to eq(RuntimeError)
And it will certainly raise this time
expect{ error.raise! }.to raise_error(RuntimeError, "oh no!")
And as often times you will match on an error case only and raise a custom exception the following shortcut comes in handy
expect{ error.raise!(KeyError) }.to raise_error(KeyError, "oh no!")
And also you might like to have access to the original message
expect{ error.raise!(KeyError) { "key not found #{it}"} }
.to raise_error(KeyError, "key not found oh no!")
And it will execute the block passed to the if_error
method
x = nil
expect(error.if_error {x = 42}).to eq(42)
expect(x).to eq(42)
And the error and message are passed in
expect(error.if_error {[_1, _2]}).to eq([RuntimeError, "oh no!"])
But not the one passed to the if_ok
method
x = nil
expect(error.if_ok {x = 42}).to be_nil
expect(x).to be_nil
Given some code which might raise exceptions
class MyError < StandardError; end
def maybe_raise(answer)
raise MyError, "Not the correct answer" unless answer == 42
"Correct answer!"
end
Then you can rescue from the exception with
error = Result.from_rescue{maybe_raise(73)}
expect(error).not_to be_ok
And you can decostruct the error
Result.from_rescue{maybe_raise(73)} => {ok: false, error:}
expect(error).to eq("Not the correct answer")
But if you get the correct answer
Result.from_rescue{maybe_raise(42)} => {ok: true, value:}
expect(value).to eq("Correct answer!")
Given an ok result with the default value
let(:default_ok) { Result.ok }
Then we still have an ok result
expect(default_ok).to be_ok
But its value is just nil
expect(default_ok.value).to be_nil
Given an error with an explicit exception
let(:argument_error) { Result.error("do not do that", exception: ArgumentError) }
Then we will get that exception back
expect{ argument_error.raise! }
.to raise_error(ArgumentError, "do not do that")
While the Result
objectr is very strict on what methods can be called depending on its status (ok?, !ok?)
A more laissez-faire approach can be achieved via Pattern Matching and Conversions
We can deconstruct a result into a hash or an array, and the deconstruction into
a hash is identical to matching the result of #to_h
Given two results
let(:my_error) { Result.error("my bad") }
let(:my_success) { Result.ok("my good") }
let(:error_hash) { my_error.to_h }
let(:success_hash) { my_success.to_h }
Then we can convert them into hashes
expect(error_hash).to eq(ok: false, value: nil, error: "my bad", exception: RuntimeError)
expect(success_hash).to eq(ok: true, value: "my good", error: nil, exception: nil)
Then we can desconstruct the error as a hash (as seen above) ```ruby my_error in {ok: false, error: message, exception: RuntimeError} expect(message).to eq("my bad")
The deconstruction into an array however will yield two differently shaped patterns
And therefore...
```ruby
my_error in [false, message, RuntimeError]
expect(message).to eq("my bad")
And the same holds for the ok result
my_success in [true, value]
expect(value).to eq("my good")
And last but not least, to assure that all instances of Result
are frozen we have removed the
default constructor (we have not - yet - shadowed Object#allocate
though)
Given results
# Placeholder needed for speculate v1.0.6
Then we do not have a default constructor
expect{ Result.new }.to raise_error(NoMethodError)
Although the Result class has its merits for error handling it also encourages the usage of a pattern that I call Ok iff errors.empty?
This pattern is implemented by the following module
module OkIffErrorsEmpty
def errors = ( @__errors__ ||= [])
def ok? = errors.empty?
end
As simple as that.
Now Result allows for a seamingless integration with this pattern, first of all it exposes a module implementing it, but with a (more) reasonable name Errors
While this makes for a fatter interface, if well used, can make the workflow in your class more elegant
Given a class including Errors (pun intended)
require 'lab42/result/errors'
class MyErrors
include Lab42::Result::Errors
end
let(:my_errors) { MyErrors.new }
Then an instance of MyErrors is just ok
expect(my_errors).to be_ok
And has no errors
expect(my_errors.errors).to be_empty
And we can extract a result out of it
result = my_errors.to_result
expect(result).to be_ok
And the value of that result is (per default) my_errors
my_errors.to_result => {value: my_errors}
But if there are errors, we get
my_errors.errors << :error1
my_errors.errors << :error2
my_errors.to_result => [false, errors, _]
expect(errors).to eq([:error1, :error2])
And eventually we get a helper to add many errors at once (always an optimist)
my_errors.add_errors(:error3, :error4)
my_errors.to_result => [false, errors, _]
expect(errors).to eq([:error3, :error4])
But I kept my favorite at the end (bad misstake, the audience is sleeping already)
great = MyErrors.new
output = []
great.if_ok { output << "I was completly fine" }
great.if_error { raise "Does not happen" }
great.add_errors("I got sick")
great.if_ok { raise "Still not happening" }
great.if_error { output << "But then, caught something" }
expect(output).to eq(["I was completly fine" , "But then, caught something"])
...which allow to have an acceptance test provided test coverage and show the detailed behavior of this library can be found here
Copyright 2025 Robert Dober robert.dober@gmail.com
AGPL-3.0-or-later c.f LICENSE
FAQs
Unknown package
We found that lab42_result demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.