functional-light-service 6.2.0 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/functional-light-service.rb +6 -36
- metadata +14 -307
- data/.github/workflows/project-build.yml +0 -71
- data/.gitignore +0 -24
- data/.rspec +0 -3
- data/.rubocop.yml +0 -101
- data/.solargraph.yml +0 -11
- data/AUDIT-functional-light-service.md +0 -352
- data/Appraisals +0 -7
- data/CHANGELOG.md +0 -292
- data/CODE_OF_CONDUCT.md +0 -22
- data/Gemfile +0 -4
- data/LICENSE +0 -22
- data/README.md +0 -1663
- data/Rakefile +0 -12
- data/VERSION +0 -1
- data/audit/bench.rb +0 -99
- data/audit/verify_findings.rb +0 -172
- data/functional-light-service.gemspec +0 -35
- data/gemfiles/dry_inflector_0_2_1.gemfile +0 -5
- data/gemfiles/i18n_1_8_11.gemfile +0 -5
- data/lib/functional-light-service/action.rb +0 -145
- data/lib/functional-light-service/configuration.rb +0 -36
- data/lib/functional-light-service/context/key_verifier.rb +0 -124
- data/lib/functional-light-service/context.rb +0 -208
- data/lib/functional-light-service/deprecations.rb +0 -26
- data/lib/functional-light-service/errors.rb +0 -9
- data/lib/functional-light-service/functional/enum.rb +0 -286
- data/lib/functional-light-service/functional/maybe.rb +0 -21
- data/lib/functional-light-service/functional/monad.rb +0 -77
- data/lib/functional-light-service/functional/null.rb +0 -88
- data/lib/functional-light-service/functional/option.rb +0 -100
- data/lib/functional-light-service/functional/result.rb +0 -129
- data/lib/functional-light-service/functional/sequencer.rb +0 -144
- data/lib/functional-light-service/i18n/localization_adapter.rb +0 -50
- data/lib/functional-light-service/localization_adapter.rb +0 -39
- data/lib/functional-light-service/localization_map.rb +0 -9
- data/lib/functional-light-service/organizer/execute.rb +0 -16
- data/lib/functional-light-service/organizer/iterate.rb +0 -30
- data/lib/functional-light-service/organizer/reduce_case.rb +0 -50
- data/lib/functional-light-service/organizer/reduce_if.rb +0 -19
- data/lib/functional-light-service/organizer/reduce_if_else.rb +0 -23
- data/lib/functional-light-service/organizer/reduce_until.rb +0 -22
- data/lib/functional-light-service/organizer/reduce_while.rb +0 -31
- data/lib/functional-light-service/organizer/scoped_reducable.rb +0 -15
- data/lib/functional-light-service/organizer/with_callback.rb +0 -28
- data/lib/functional-light-service/organizer/with_reducer.rb +0 -81
- data/lib/functional-light-service/organizer/with_reducer_factory.rb +0 -20
- data/lib/functional-light-service/organizer/with_reducer_log_decorator.rb +0 -111
- data/lib/functional-light-service/organizer.rb +0 -129
- data/lib/functional-light-service/testing/context_factory.rb +0 -48
- data/lib/functional-light-service/testing.rb +0 -3
- data/lib/functional-light-service/version.rb +0 -5
- data/resources/fail_actions.png +0 -0
- data/resources/light-service.png +0 -0
- data/resources/organizer_and_actions.png +0 -0
- data/resources/skip_actions.png +0 -0
- data/spec/acceptance/add_numbers_spec.rb +0 -11
- data/spec/acceptance/after_actions_spec.rb +0 -87
- data/spec/acceptance/around_each_spec.rb +0 -19
- data/spec/acceptance/before_actions_spec.rb +0 -115
- data/spec/acceptance/custom_log_from_organizer_spec.rb +0 -61
- data/spec/acceptance/deprecation_warnings_spec.rb +0 -82
- data/spec/acceptance/fail_spec.rb +0 -52
- data/spec/acceptance/log_from_organizer_spec.rb +0 -154
- data/spec/acceptance/message_localization_spec.rb +0 -119
- data/spec/acceptance/organizer/add_aliases_spec.rb +0 -28
- data/spec/acceptance/organizer/add_to_context_spec.rb +0 -54
- data/spec/acceptance/organizer/around_each_with_reduce_if_spec.rb +0 -42
- data/spec/acceptance/organizer/context_failure_and_skipping_spec.rb +0 -90
- data/spec/acceptance/organizer/execute_spec.rb +0 -67
- data/spec/acceptance/organizer/iterate_spec.rb +0 -44
- data/spec/acceptance/organizer/reduce_case_spec.rb +0 -65
- data/spec/acceptance/organizer/reduce_if_else_spec.rb +0 -60
- data/spec/acceptance/organizer/reduce_if_spec.rb +0 -89
- data/spec/acceptance/organizer/reduce_until_spec.rb +0 -49
- data/spec/acceptance/organizer/reduce_while_spec.rb +0 -96
- data/spec/acceptance/organizer/with_callback_spec.rb +0 -113
- data/spec/acceptance/organizer_entry_point_spec.rb +0 -35
- data/spec/acceptance/rollback_spec.rb +0 -183
- data/spec/acceptance/skip_all_remaining_spec.rb +0 -139
- data/spec/acceptance/testing/context_factory_spec.rb +0 -54
- data/spec/action_expected_keys_spec.rb +0 -63
- data/spec/action_expects_and_promises_spec.rb +0 -97
- data/spec/action_optional_expected_keys_spec.rb +0 -107
- data/spec/action_promised_keys_spec.rb +0 -126
- data/spec/action_spec.rb +0 -97
- data/spec/context/inspect_spec.rb +0 -52
- data/spec/context_spec.rb +0 -301
- data/spec/examples/amount_spec.rb +0 -77
- data/spec/examples/controller_spec.rb +0 -63
- data/spec/examples/validate_address_spec.rb +0 -38
- data/spec/i18n_localization_adapter_spec.rb +0 -83
- data/spec/lib/deterministic/class_mixin_spec.rb +0 -24
- data/spec/lib/deterministic/currify_spec.rb +0 -90
- data/spec/lib/deterministic/monad_axioms.rb +0 -46
- data/spec/lib/deterministic/monad_spec.rb +0 -47
- data/spec/lib/deterministic/null_spec.rb +0 -65
- data/spec/lib/deterministic/option_spec.rb +0 -140
- data/spec/lib/deterministic/result/failure_spec.rb +0 -65
- data/spec/lib/deterministic/result/result_map_spec.rb +0 -155
- data/spec/lib/deterministic/result/result_shared.rb +0 -25
- data/spec/lib/deterministic/result/success_spec.rb +0 -41
- data/spec/lib/deterministic/result_spec.rb +0 -63
- data/spec/lib/deterministic/sequencer_spec.rb +0 -506
- data/spec/lib/edge_cases_spec.rb +0 -156
- data/spec/lib/enum_spec.rb +0 -114
- data/spec/lib/native_pattern_matching_spec.rb +0 -74
- data/spec/localization_adapter_spec.rb +0 -66
- data/spec/organizer/with_reducer_spec.rb +0 -56
- data/spec/organizer_key_aliases_spec.rb +0 -29
- data/spec/organizer_spec.rb +0 -115
- data/spec/readme_spec.rb +0 -45
- data/spec/sample/calculates_order_tax_action_spec.rb +0 -16
- data/spec/sample/calculates_tax_spec.rb +0 -30
- data/spec/sample/looks_up_tax_percentage_action_spec.rb +0 -55
- data/spec/sample/provides_free_shipping_action_spec.rb +0 -25
- data/spec/sample/tax/calculates_order_tax_action.rb +0 -10
- data/spec/sample/tax/calculates_tax.rb +0 -11
- data/spec/sample/tax/looks_up_tax_percentage_action.rb +0 -28
- data/spec/sample/tax/provides_free_shipping_action.rb +0 -11
- data/spec/spec_helper.rb +0 -32
- data/spec/support.rb +0 -1
- data/spec/test_doubles.rb +0 -656
- data/spec/testing/context_factory/iterate_spec.rb +0 -39
- data/spec/testing/context_factory/reduce_if_spec.rb +0 -40
- data/spec/testing/context_factory/reduce_until_spec.rb +0 -40
- data/spec/testing/context_factory/with_callback_spec.rb +0 -38
- data/spec/testing/context_factory_spec.rb +0 -76
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
module FunctionalLightService
|
|
4
|
-
module Procify
|
|
5
|
-
def py(m, *args)
|
|
6
|
-
args.any? ? method(m).to_proc.curry[*args] : method(m)
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
class BookingController
|
|
12
|
-
include FunctionalLightService::Prelude::Result
|
|
13
|
-
include FunctionalLightService::Procify
|
|
14
|
-
|
|
15
|
-
Context = Struct.new(:booking, :ability, :format)
|
|
16
|
-
|
|
17
|
-
def index(id, format = :html)
|
|
18
|
-
get_booking(id) << log(:booking) >>
|
|
19
|
-
py(:ability) << log(:ability) >>
|
|
20
|
-
py(:present, format) << log(:presenter) >>
|
|
21
|
-
py(:render) << log(:render)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def log(step)
|
|
25
|
-
->(data) { [step, data] }
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def ability(ctx)
|
|
29
|
-
ctx.ability = {} # Ability.new(@booking)
|
|
30
|
-
Success(ctx)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def present(format, ctx)
|
|
34
|
-
ctx.format = format
|
|
35
|
-
|
|
36
|
-
Success(ctx)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def render(ctx)
|
|
40
|
-
send(ctx.format, ctx)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def html(ctx)
|
|
44
|
-
Success(ctx)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def get_booking(id)
|
|
48
|
-
ctx = Context.new
|
|
49
|
-
ctx.booking = { :ref_anixe => id }
|
|
50
|
-
Success(ctx)
|
|
51
|
-
# @booking = @bms.booking_by_id(id)
|
|
52
|
-
# rescue BSON::InvalidObjectId => ex
|
|
53
|
-
# @booking = nil
|
|
54
|
-
# @ui.error(404, ex.message)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
describe BookingController do
|
|
59
|
-
it "does something" do
|
|
60
|
-
bc = BookingController.new
|
|
61
|
-
bc.index('1234', :html)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
# A Unit of Work for validating an address
|
|
4
|
-
module ValidateAddress
|
|
5
|
-
extend FunctionalLightService::Prelude::Result
|
|
6
|
-
|
|
7
|
-
def self.call(candidate)
|
|
8
|
-
errors = {}
|
|
9
|
-
errors[:street] = "Street cannot be empty" unless candidate.key? :street
|
|
10
|
-
errors[:city] = "Street cannot be empty" unless candidate.key? :city
|
|
11
|
-
errors[:postal] = "Street cannot be empty" unless candidate.key? :postal
|
|
12
|
-
|
|
13
|
-
errors.empty? ? Success(candidate) : Failure(errors)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
describe ValidateAddress do
|
|
18
|
-
include FunctionalLightService
|
|
19
|
-
|
|
20
|
-
subject { ValidateAddress.call(candidate) }
|
|
21
|
-
context 'sunny day' do
|
|
22
|
-
let(:candidate) do
|
|
23
|
-
{ :title => "Hobbiton",
|
|
24
|
-
:street => "501 Buckland Rd",
|
|
25
|
-
:city => "Matamata",
|
|
26
|
-
:postal => "3472",
|
|
27
|
-
:country => "nz" }
|
|
28
|
-
end
|
|
29
|
-
specify { expect(subject).to be_a FunctionalLightService::Result::Success }
|
|
30
|
-
specify { expect(subject.value).to eq candidate }
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
context 'empty data' do
|
|
34
|
-
let(:candidate) { {} }
|
|
35
|
-
specify { expect(subject).to be_a FunctionalLightService::Result::Failure }
|
|
36
|
-
specify { expect(subject.value).to include(:street, :city, :postal) }
|
|
37
|
-
end
|
|
38
|
-
end
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
require 'test_doubles'
|
|
3
|
-
|
|
4
|
-
describe FunctionalLightService::I18n::LocalizationAdapter do
|
|
5
|
-
let(:action_class) { TestDoubles::AnAction }
|
|
6
|
-
let(:adapter) { described_class.new }
|
|
7
|
-
|
|
8
|
-
describe "#failure" do
|
|
9
|
-
subject { adapter.failure(message_or_key, action_class) }
|
|
10
|
-
|
|
11
|
-
context "when provided a Symbol" do
|
|
12
|
-
let(:message_or_key) { :not_found }
|
|
13
|
-
|
|
14
|
-
it "translates the message" do
|
|
15
|
-
expected_scope = "test_doubles/an_action.light_service.failures"
|
|
16
|
-
|
|
17
|
-
expect(I18n).to receive(:t)
|
|
18
|
-
.with(message_or_key, :scope => expected_scope)
|
|
19
|
-
.and_return("message")
|
|
20
|
-
|
|
21
|
-
expect(subject).to eq("message")
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
it "allows passing interpolation options to I18n layer" do
|
|
25
|
-
expect(I18n).to receive(:t)
|
|
26
|
-
.with(message_or_key, hash_including(:i18n_variable => "value"))
|
|
27
|
-
.and_return("message")
|
|
28
|
-
|
|
29
|
-
subject = adapter.failure(message_or_key,
|
|
30
|
-
action_class,
|
|
31
|
-
:i18n_variable => "value")
|
|
32
|
-
|
|
33
|
-
expect(subject).to eq("message")
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
context "when provided a String" do
|
|
38
|
-
let(:message_or_key) { "action failed" }
|
|
39
|
-
|
|
40
|
-
it "returns the message" do
|
|
41
|
-
expect(subject).to eq(message_or_key)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
describe "#success" do
|
|
47
|
-
subject { adapter.success(message_or_key, action_class) }
|
|
48
|
-
|
|
49
|
-
context "when provided a Symbol" do
|
|
50
|
-
let(:message_or_key) { :not_found }
|
|
51
|
-
|
|
52
|
-
it "translates the message" do
|
|
53
|
-
expected_scope = "test_doubles/an_action.light_service.successes"
|
|
54
|
-
|
|
55
|
-
expect(I18n).to receive(:t)
|
|
56
|
-
.with(message_or_key, :scope => expected_scope)
|
|
57
|
-
.and_return("message")
|
|
58
|
-
|
|
59
|
-
expect(subject).to eq("message")
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it "allows passing interpolation options to I18n layer" do
|
|
63
|
-
expect(I18n).to receive(:t)
|
|
64
|
-
.with(message_or_key, hash_including(:i18n_variable => "value"))
|
|
65
|
-
.and_return("message")
|
|
66
|
-
|
|
67
|
-
subject = adapter.success(message_or_key,
|
|
68
|
-
action_class,
|
|
69
|
-
:i18n_variable => "value")
|
|
70
|
-
|
|
71
|
-
expect(subject).to eq("message")
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
context "when provided a String" do
|
|
76
|
-
let(:message_or_key) { "action failed" }
|
|
77
|
-
|
|
78
|
-
it "returns the message" do
|
|
79
|
-
expect(subject).to eq(message_or_key)
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe 'Class Mixin' do
|
|
4
|
-
describe 'try' do
|
|
5
|
-
module MyApp
|
|
6
|
-
class Thing
|
|
7
|
-
include FunctionalLightService::Prelude::Result
|
|
8
|
-
|
|
9
|
-
def run
|
|
10
|
-
Success(11) >> method(:double)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def double(num)
|
|
14
|
-
Success(num * 2)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it "cleanly mixes into a class" do
|
|
20
|
-
result = MyApp::Thing.new.run
|
|
21
|
-
expect(result).to eq FunctionalLightService::Result::Success.new(22)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'pry'
|
|
3
|
-
|
|
4
|
-
module FunctionalLightService
|
|
5
|
-
module Currify
|
|
6
|
-
module ClassMethods
|
|
7
|
-
def currify(*names)
|
|
8
|
-
names.each do |name|
|
|
9
|
-
unbound_method = instance_method(name)
|
|
10
|
-
|
|
11
|
-
define_method(name) do |*args|
|
|
12
|
-
curried_method = unbound_method.bind(self).to_proc.curry
|
|
13
|
-
curried_method[*args]
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def self.included(curried)
|
|
20
|
-
curried.extend ClassMethods
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
class Object
|
|
26
|
-
class Proc
|
|
27
|
-
def self.compose(f, g)
|
|
28
|
-
->(*args) { f[g[*args]] }
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# rubocop:disable Naming/BinaryOperatorParameterName
|
|
32
|
-
# Compose left to right
|
|
33
|
-
def |(g)
|
|
34
|
-
Proc.compose(g, self)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Compose right to left
|
|
38
|
-
def *(g)
|
|
39
|
-
Proc.compose(self, g)
|
|
40
|
-
end
|
|
41
|
-
# rubocop:enable Naming/BinaryOperatorParameterName
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
class Booking
|
|
46
|
-
include FunctionalLightService::Currify
|
|
47
|
-
include FunctionalLightService::Prelude::Result
|
|
48
|
-
|
|
49
|
-
def initialize(deps)
|
|
50
|
-
@deps = deps
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def build(id, format)
|
|
54
|
-
validate(id) | req | find | render(format)
|
|
55
|
-
|
|
56
|
-
validate(id) | request(id) | find
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def validate(id)
|
|
60
|
-
Success(id)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def req(a, id)
|
|
64
|
-
Success(:id => id + a)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def find(req)
|
|
68
|
-
Success(:found => req)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def render(format, req)
|
|
72
|
-
Success("rendered in #{format}: #{req[:found]}")
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
currify :find, :render, :req
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
describe "Pref" do
|
|
79
|
-
include FunctionalLightService::Prelude::Result
|
|
80
|
-
|
|
81
|
-
it "does something" do
|
|
82
|
-
b = Booking.new(1)
|
|
83
|
-
actual = b.validate(1) >> b.req(2) >> b.find >> b.render(:html)
|
|
84
|
-
|
|
85
|
-
# il formato di Hash#to_s cambia tra Ruby 3.3 e 3.4: costruiamo l'atteso dinamicamente
|
|
86
|
-
rendered_params = { :id => 3 }
|
|
87
|
-
expected = FunctionalLightService::Result::Success.new("rendered in html: #{rendered_params}")
|
|
88
|
-
expect(actual).to eq expected
|
|
89
|
-
end
|
|
90
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
shared_examples 'a Monad' do
|
|
2
|
-
describe 'axioms' do
|
|
3
|
-
it '1st monadic law: left-identity' do
|
|
4
|
-
f = ->(value) { monad.new(value + 1) }
|
|
5
|
-
expect(
|
|
6
|
-
monad.new(1).bind do |value|
|
|
7
|
-
f.call(value)
|
|
8
|
-
end
|
|
9
|
-
).to eq f.call(1)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
it '2nd monadic law: right-identy - new and bind do not change the value' do
|
|
13
|
-
expect(
|
|
14
|
-
monad.new(1).bind do |value|
|
|
15
|
-
monad.new(value)
|
|
16
|
-
end
|
|
17
|
-
).to eq monad.new(1)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it '3rd monadic law: associativity' do
|
|
21
|
-
f = ->(value) { monad.new(value + 1) }
|
|
22
|
-
g = ->(value) { monad.new(value + 100) }
|
|
23
|
-
|
|
24
|
-
id1 = monad.new(1).bind { |a| f.call(a) }.bind { |b| g.call(b) }
|
|
25
|
-
|
|
26
|
-
id2 = monad.new(1).bind do |a|
|
|
27
|
-
f.call(a).bind do |b|
|
|
28
|
-
g.call(b)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
expect(id1).to eq id2
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it '#bind must return a monad' do
|
|
36
|
-
expect(monad.new(1).bind { |v| monad.new(v) }).to eq monad.new(1)
|
|
37
|
-
# rubocop:disable Lint/EmptyBlock
|
|
38
|
-
expect { monad.new(1).bind {} }.to raise_error(FunctionalLightService::Monad::NotMonadError)
|
|
39
|
-
# rubocop:enable Lint/EmptyBlock
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it '#new must return a monad' do
|
|
43
|
-
expect(monad.new(1)).to be_a monad
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require_relative 'monad_axioms'
|
|
3
|
-
|
|
4
|
-
describe FunctionalLightService::Monad do
|
|
5
|
-
class Identity
|
|
6
|
-
include FunctionalLightService::Monad
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
let(:monad) { Identity }
|
|
10
|
-
it_behaves_like 'a Monad' do
|
|
11
|
-
# let(:monad) { monad }
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
specify { expect(Identity.new(1).inspect).to eq 'Identity(1)' }
|
|
15
|
-
specify { expect(Identity.new(1).to_s).to eq '1' }
|
|
16
|
-
specify { expect(Identity.new(nil).inspect).to eq 'Identity(nil)' }
|
|
17
|
-
specify { expect(Identity.new(nil).to_s).to eq '' }
|
|
18
|
-
specify { expect(Identity.new([1, 2]).fmap(&:to_s)).to eq Identity.new("[1, 2]") }
|
|
19
|
-
specify { expect(Identity.new(1).fmap { |v| v + 2 }).to eq Identity.new(3) }
|
|
20
|
-
specify { expect(Identity.new('foo').fmap(&:upcase)).to eq Identity.new('FOO') }
|
|
21
|
-
|
|
22
|
-
context '#bind' do
|
|
23
|
-
it "raises an error if the passed function does not return a monad of the same class" do
|
|
24
|
-
# rubocop:disable Lint/EmptyBlock
|
|
25
|
-
expect { Identity.new(1).bind {} }.to \
|
|
26
|
-
raise_error(FunctionalLightService::Monad::NotMonadError)
|
|
27
|
-
# rubocop:enable Lint/EmptyBlock
|
|
28
|
-
end
|
|
29
|
-
specify { expect(Identity.new(1).bind { |value| Identity.new(value) }).to eq Identity.new(1) }
|
|
30
|
-
|
|
31
|
-
it "passes the monad class, this is ruby-fu?!" do
|
|
32
|
-
Identity.new(1)
|
|
33
|
-
.bind do |v|
|
|
34
|
-
expect(monad).to eq Identity
|
|
35
|
-
monad.new(v)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
specify do
|
|
40
|
-
expect(
|
|
41
|
-
monad.new(1).bind { |value| monad.new(value + 1) }
|
|
42
|
-
).to eq Identity.new(2)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
specify { expect(Identity.new(Identity.new(1))).to eq Identity.new(1) }
|
|
46
|
-
end
|
|
47
|
-
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
|
|
3
|
-
describe Null do
|
|
4
|
-
it "Null is a Singleton" do
|
|
5
|
-
expect(Null.instance).to be_a Null
|
|
6
|
-
expect { Null.new }.to raise_error(NoMethodError, /private method [`']new'/)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
it "respond_to? accepts the standard two-argument form" do
|
|
10
|
-
expect(Null.instance.respond_to?(:anything, true)).to be(true)
|
|
11
|
-
expect(Null.instance.respond_to?(:anything)).to be(true)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it "explicit conversions" do
|
|
15
|
-
expect(Null.to_s).to eq 'Null'
|
|
16
|
-
expect(Null.inspect).to eq 'Null'
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it "answer if Null is null" do
|
|
20
|
-
expect(Null.null?).to eq true
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it "answer if Null is some" do
|
|
24
|
-
expect(Null.some?).to eq false
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it "compares to Null" do
|
|
28
|
-
# rubocop:disable Style/CaseEquality
|
|
29
|
-
expect(Null === Null.instance).to be_truthy
|
|
30
|
-
expect(Null.instance === Null).to be_truthy
|
|
31
|
-
expect(Null.instance).to eq Null
|
|
32
|
-
expect(Null).to eq Null.instance
|
|
33
|
-
expect(1).not_to be Null
|
|
34
|
-
expect(1).not_to be Null.instance
|
|
35
|
-
expect(Null.instance).not_to be 1
|
|
36
|
-
expect(Null).not_to be 1
|
|
37
|
-
expect(Null.instance).not_to be_nil
|
|
38
|
-
expect(Null).not_to be_nil
|
|
39
|
-
# rubocop:enable Style/CaseEquality
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it "implicit conversions" do
|
|
43
|
-
null = Null.instance
|
|
44
|
-
expect(null.to_str).to eq ""
|
|
45
|
-
expect(null.to_ary).to eq []
|
|
46
|
-
# rubocop:disable Style/StringConcatenation
|
|
47
|
-
expect("" + null).to eq ""
|
|
48
|
-
# rubocop:enable Style/StringConcatenation
|
|
49
|
-
|
|
50
|
-
a, b, c = null
|
|
51
|
-
expect(a).to be_nil
|
|
52
|
-
expect(b).to be_nil
|
|
53
|
-
expect(c).to be_nil
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it "mimicks other classes and returns Null for their public methods" do
|
|
57
|
-
class UnderMimickTest
|
|
58
|
-
def test; end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
mimick = Null.mimic(UnderMimickTest)
|
|
62
|
-
expect(mimick.test).to be_null
|
|
63
|
-
expect { mimick.i_dont_exist }.to raise_error(NoMethodError)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
include FunctionalLightService::Prelude::Option
|
|
4
|
-
|
|
5
|
-
describe FunctionalLightService::Option do
|
|
6
|
-
it "Some cannot wrap nil" do
|
|
7
|
-
expect { described_class::Some.new(nil) }
|
|
8
|
-
.to raise_error(ArgumentError, /use None instead/)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
specify { expect(described_class::Some.new(0)).to be_a described_class::Some }
|
|
12
|
-
specify { expect(described_class::Some.new(0)).to be_a described_class }
|
|
13
|
-
specify { expect(described_class::Some.new(0)).to eq Some(0) }
|
|
14
|
-
|
|
15
|
-
specify { expect(described_class::None.new).to eq described_class::None.new }
|
|
16
|
-
specify { expect(described_class::None.new).to be_a described_class::None }
|
|
17
|
-
specify { expect(described_class::None.new).to be_a described_class }
|
|
18
|
-
specify { expect(described_class::None.new).to eq None }
|
|
19
|
-
|
|
20
|
-
it "join" do
|
|
21
|
-
expect(Some(Some(1))).to eq Some(1)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
it "fmap" do
|
|
25
|
-
expect(Some(1).fmap { |n| n + 1 }).to eq Some(2)
|
|
26
|
-
expect(None.fmap { |n| n + 1 }).to eq None
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
it "map" do
|
|
30
|
-
expect(Some(1).map { |n| Some(n + 1) }).to eq Some(2)
|
|
31
|
-
expect(Some(1).map { |_n| None }).to eq None
|
|
32
|
-
expect(None.map { |n| Some(n + 1) }).to eq None
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it "some?" do
|
|
36
|
-
expect(Some(1).some?).to be_truthy
|
|
37
|
-
expect(None.some?).to be_falsey
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
it "none?" do
|
|
41
|
-
expect(None.none?).to be_truthy
|
|
42
|
-
expect(Some(1).none?).to be_falsey
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
it "value" do
|
|
46
|
-
expect(Some(1).value).to eq 1
|
|
47
|
-
expect { None.value }.to raise_error NoMethodError
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it "value_or" do
|
|
51
|
-
expect(Some(1).value_or(2)).to eq 1
|
|
52
|
-
expect(None.value_or(0)).to eq 0
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it "+" do
|
|
56
|
-
expect(Some([1]) + None).to eq Some([1])
|
|
57
|
-
expect(Some(1) + None + None).to eq Some(1)
|
|
58
|
-
expect(Some(1) + Some(1)).to eq Some(2)
|
|
59
|
-
expect(None + Some(1)).to eq Some(1)
|
|
60
|
-
expect(None + None + Some(1)).to eq Some(1)
|
|
61
|
-
expect(None + None + Some(1) + None).to eq Some(1)
|
|
62
|
-
expect(None + Some(:foo => 1)).to eq Some(:foo => 1)
|
|
63
|
-
expect(Some([1]) + Some([1])).to eq Some([1, 1])
|
|
64
|
-
expect { Some([1]) + Some(1) }.to raise_error TypeError
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
it "inspect" do
|
|
68
|
-
expect(Some(1).inspect).to eq "Some(1)"
|
|
69
|
-
expect(described_class::None.new.inspect).to eq "None"
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
it "to_s" do
|
|
73
|
-
expect(Some(1).to_s).to eq "1"
|
|
74
|
-
expect(described_class::None.new.to_s).to eq ""
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
it "match" do
|
|
78
|
-
expect(
|
|
79
|
-
# rubocop:disable Lint/UnusedBlockArgument
|
|
80
|
-
# rubocop:disable Lint/EmptyBlock
|
|
81
|
-
Some(0).match do
|
|
82
|
-
Some(where { s == 1 }) { |s| 99 }
|
|
83
|
-
Some(where { s == 0 }) { |s| s + 1 }
|
|
84
|
-
None() {}
|
|
85
|
-
end
|
|
86
|
-
).to eq 1
|
|
87
|
-
|
|
88
|
-
expect(
|
|
89
|
-
Some(1).match do
|
|
90
|
-
None() { 0 }
|
|
91
|
-
Some() { |s| 1 }
|
|
92
|
-
end
|
|
93
|
-
).to eq 1
|
|
94
|
-
|
|
95
|
-
expect(
|
|
96
|
-
Some(1).match do
|
|
97
|
-
None() { 0 }
|
|
98
|
-
Some(where { s.is_a? Integer }) { |s| 1 }
|
|
99
|
-
end
|
|
100
|
-
# rubocop:enable Lint/UnusedBlockArgument
|
|
101
|
-
# rubocop:enable Lint/EmptyBlock
|
|
102
|
-
).to eq 1
|
|
103
|
-
|
|
104
|
-
expect(
|
|
105
|
-
None.match do
|
|
106
|
-
None() { 0 }
|
|
107
|
-
Some() { 1 }
|
|
108
|
-
end
|
|
109
|
-
).to eq 0
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
it "nil?" do
|
|
113
|
-
expect(described_class.some?(nil)).to eq None
|
|
114
|
-
expect(described_class.some?(1)).to be_some
|
|
115
|
-
expect(described_class.some?(1)).to eq Some(1)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
it "any?" do
|
|
119
|
-
expect(described_class.any?(nil)).to be_none
|
|
120
|
-
expect(described_class.any?(None)).to be_none
|
|
121
|
-
expect(described_class.any?("")).to be_none
|
|
122
|
-
expect(described_class.any?([])).to be_none
|
|
123
|
-
expect(described_class.any?({})).to be_none
|
|
124
|
-
expect(described_class.any?([1])).to eq Some([1])
|
|
125
|
-
expect(described_class.any?(:foo => 1)).to eq Some(:foo => 1)
|
|
126
|
-
expect(described_class.any?(1)).to eq Some(1)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
it "try!" do
|
|
130
|
-
expect(described_class.try! { raise "error" }).to be_none
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
require_relative 'monad_axioms'
|
|
135
|
-
|
|
136
|
-
describe FunctionalLightService::Option::Some do
|
|
137
|
-
it_behaves_like 'a Monad' do
|
|
138
|
-
let(:monad) { described_class }
|
|
139
|
-
end
|
|
140
|
-
end
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require_relative '../monad_axioms'
|
|
3
|
-
require_relative 'result_shared'
|
|
4
|
-
|
|
5
|
-
describe FunctionalLightService::Result::Failure do
|
|
6
|
-
include FunctionalLightService::Prelude::Result
|
|
7
|
-
|
|
8
|
-
it_behaves_like 'a Monad' do
|
|
9
|
-
let(:monad) { described_class }
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
subject { described_class.new(1) }
|
|
13
|
-
|
|
14
|
-
specify { expect(subject).to be_an_instance_of described_class }
|
|
15
|
-
specify { expect(subject).to be_failure }
|
|
16
|
-
specify { expect(subject).not_to be_success }
|
|
17
|
-
specify { expect(subject.success?).to be_falsey }
|
|
18
|
-
specify { expect(subject.failure?).to be_truthy }
|
|
19
|
-
|
|
20
|
-
specify { expect(subject).to be_an_instance_of described_class }
|
|
21
|
-
specify { expect(subject).to eq(described_class.new(1)) }
|
|
22
|
-
specify { expect(subject.fmap { |v| v + 1 }).to eq Failure(2) }
|
|
23
|
-
specify { expect(subject.map { |v| Success(v + 1) }).to eq Failure(1) }
|
|
24
|
-
specify { expect(subject.map_err { |v| Success(v + 1) }).to eq Success(2) }
|
|
25
|
-
specify do
|
|
26
|
-
expect(subject.pipe { |v| raise RuntimeError unless v == Failure(1) }).to eq Failure(1)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
specify { expect(subject.or(Success(2))).to eq Success(2) }
|
|
30
|
-
specify { expect(subject.or(Failure(2))).to eq Failure(2) }
|
|
31
|
-
specify { expect(subject.or_else { Success(2) }).to eq Success(2) }
|
|
32
|
-
specify { expect(subject.or_else { Failure(2) }).to eq Failure(2) }
|
|
33
|
-
|
|
34
|
-
specify { expect(subject.and(Success(2))).to eq Failure(1) }
|
|
35
|
-
specify { expect(subject.and_then { Success(2) }).to eq Failure(1) }
|
|
36
|
-
|
|
37
|
-
it_behaves_like 'Result' do
|
|
38
|
-
let(:result) { described_class }
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it "#or" do
|
|
42
|
-
expect(Success(1).or(Failure(2))).to eq Success(1)
|
|
43
|
-
expect(Failure(1).or(Success(2))).to eq Success(2)
|
|
44
|
-
expect { Failure(1).or(2) }.to raise_error(FunctionalLightService::Monad::NotMonadError)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it "#or_else" do
|
|
48
|
-
expect(Success(1).or_else { Failure(2) }).to eq Success(1)
|
|
49
|
-
expect(Failure(1).or_else { |v| Success(v + 1) }).to eq Success(2)
|
|
50
|
-
expect { Failure(1).or_else { 2 } }.to raise_error(FunctionalLightService::Monad::NotMonadError)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
it "#and" do
|
|
54
|
-
expect(Success(1).and(Success(2))).to eq Success(2)
|
|
55
|
-
expect(Failure(1).and(Success(2))).to eq Failure(1)
|
|
56
|
-
expect { Success(1).and(2) }.to raise_error(FunctionalLightService::Monad::NotMonadError)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it "#and_then" do
|
|
60
|
-
expect(Success(1).and_then { Success(2) }).to eq Success(2)
|
|
61
|
-
expect(Failure(1).and_then { Success(2) }).to eq Failure(1)
|
|
62
|
-
expect { Success(1).and_then { 2 } }.to \
|
|
63
|
-
raise_error(FunctionalLightService::Monad::NotMonadError)
|
|
64
|
-
end
|
|
65
|
-
end
|