dry-transaction 0.12.0 → 0.13.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +337 -0
  3. data/LICENSE +20 -0
  4. data/README.md +16 -43
  5. data/dry-transaction.gemspec +41 -0
  6. data/lib/dry-transaction.rb +2 -0
  7. data/lib/dry/transaction.rb +2 -0
  8. data/lib/dry/transaction/builder.rb +6 -4
  9. data/lib/dry/transaction/callable.rb +22 -2
  10. data/lib/dry/transaction/dsl.rb +9 -7
  11. data/lib/dry/transaction/errors.rb +2 -0
  12. data/lib/dry/transaction/instance_methods.rb +24 -20
  13. data/lib/dry/transaction/operation.rb +5 -3
  14. data/lib/dry/transaction/operation_resolver.rb +4 -2
  15. data/lib/dry/transaction/result_matcher.rb +4 -2
  16. data/lib/dry/transaction/stack.rb +2 -0
  17. data/lib/dry/transaction/step.rb +37 -24
  18. data/lib/dry/transaction/step_adapter.rb +6 -4
  19. data/lib/dry/transaction/step_adapters.rb +9 -7
  20. data/lib/dry/transaction/step_adapters/around.rb +4 -2
  21. data/lib/dry/transaction/step_adapters/check.rb +2 -0
  22. data/lib/dry/transaction/step_adapters/map.rb +2 -0
  23. data/lib/dry/transaction/step_adapters/raw.rb +5 -3
  24. data/lib/dry/transaction/step_adapters/tee.rb +2 -0
  25. data/lib/dry/transaction/step_adapters/try.rb +3 -1
  26. data/lib/dry/transaction/step_failure.rb +12 -0
  27. data/lib/dry/transaction/version.rb +3 -1
  28. metadata +14 -110
  29. data/Gemfile +0 -15
  30. data/Gemfile.lock +0 -97
  31. data/LICENSE.md +0 -9
  32. data/Rakefile +0 -6
  33. data/spec/examples.txt +0 -85
  34. data/spec/integration/around_spec.rb +0 -81
  35. data/spec/integration/auto_injection_spec.rb +0 -32
  36. data/spec/integration/custom_step_adapters_spec.rb +0 -41
  37. data/spec/integration/operation_spec.rb +0 -30
  38. data/spec/integration/passing_step_arguments_spec.rb +0 -51
  39. data/spec/integration/publishing_step_events_spec.rb +0 -119
  40. data/spec/integration/transaction_spec.rb +0 -566
  41. data/spec/integration/transaction_without_steps_spec.rb +0 -101
  42. data/spec/spec_helper.rb +0 -116
  43. data/spec/support/container.rb +0 -10
  44. data/spec/support/database.rb +0 -12
  45. data/spec/support/db_transactions.rb +0 -45
  46. data/spec/support/result_mixin.rb +0 -3
  47. data/spec/support/test_module_constants.rb +0 -11
  48. data/spec/unit/step_adapters/around_spec.rb +0 -46
  49. data/spec/unit/step_adapters/check_spec.rb +0 -43
  50. data/spec/unit/step_adapters/map_spec.rb +0 -16
  51. data/spec/unit/step_adapters/raw_spec.rb +0 -36
  52. data/spec/unit/step_adapters/tee_spec.rb +0 -17
  53. data/spec/unit/step_adapters/try_spec.rb +0 -89
  54. data/spec/unit/step_spec.rb +0 -131
@@ -1,101 +0,0 @@
1
- RSpec.describe "Transactions steps without arguments" do
2
- let(:dependencies) { {} }
3
-
4
- before do
5
- Test::NotValidError = Class.new(StandardError)
6
- Test::DB = [{"name" => "Jane", "email" => "jane@doe.com"}]
7
- Test::Http = Class.new do
8
- def self.get
9
- "pong"
10
- end
11
-
12
- def self.post(value)
13
- Test::DB << value
14
- end
15
- end
16
- class Test::Container
17
- extend Dry::Container::Mixin
18
- register :fetch_data, -> { Test::DB.delete_at(0) }, call: false
19
- register :call_outside, -> { Test::Http.get }, call: false
20
- register :external_store, -> input { Test::Http.post(input) }
21
- register :process, -> input { { name: input["name"], email: input["email"] } }
22
- register :validate, -> input { input[:email].nil? ? raise(Test::NotValidError, "email required") : input }
23
- register :persist, -> input { Test::DB << input and true }
24
- end
25
- end
26
-
27
- context "successful" do
28
- let(:transaction) {
29
- Class.new do
30
- include Dry::Transaction(container: Test::Container)
31
- map :fetch_data
32
- map :process
33
- try :validate, catch: Test::NotValidError
34
- tee :persist
35
- end.new(**dependencies)
36
- }
37
-
38
- it "calls the operations" do
39
- transaction.call
40
- expect(Test::DB).to include(name: "Jane", email: "jane@doe.com")
41
- end
42
-
43
- it "returns a success" do
44
- expect(transaction.call()).to be_a Dry::Monads::Result::Success
45
- end
46
-
47
- it "wraps the result of the final operation" do
48
- expect(transaction.call().value!).to eq(name: "Jane", email: "jane@doe.com")
49
- end
50
-
51
- it "supports matching on success" do
52
- results = []
53
-
54
- transaction.call() do |m|
55
- m.success do |value|
56
- results << "success for #{value[:email]}"
57
- end
58
-
59
- m.failure { }
60
- end
61
-
62
- expect(results.first).to eq "success for jane@doe.com"
63
- end
64
- end
65
-
66
- context "using multiple tee step operators" do
67
- let(:transaction) {
68
- Class.new do
69
- include Dry::Transaction(container: Test::Container)
70
- tee :call_outside
71
- map :fetch_data
72
- map :process
73
- try :validate, catch: Test::NotValidError
74
- tee :external_store
75
- end.new(**dependencies)
76
- }
77
-
78
- it "calls the operations" do
79
- transaction.call
80
- expect(Test::DB).to include(name: "Jane", email: "jane@doe.com")
81
- end
82
- end
83
-
84
- context "not needing arguments in the middle of the transaction" do
85
- let(:transaction) {
86
- Class.new do
87
- include Dry::Transaction(container: Test::Container)
88
- map :process
89
- try :validate, catch: Test::NotValidError
90
- tee :call_outside
91
- tee :external_store
92
- end.new(**dependencies)
93
- }
94
- let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} }
95
-
96
- it "calls the operations" do
97
- transaction.call(input)
98
- expect(Test::DB).to include(name: "Jane", email: "jane@doe.com")
99
- end
100
- end
101
- end
data/spec/spec_helper.rb DELETED
@@ -1,116 +0,0 @@
1
- if RUBY_ENGINE == 'ruby' && ENV['COVERAGE'] == 'true'
2
- require 'yaml'
3
- rubies = YAML.load(File.read(File.join(__dir__, '..', '.travis.yml')))['rvm']
4
- latest_mri = rubies.select { |v| v =~ /\A\d+\.\d+.\d+\z/ }.max
5
-
6
- if RUBY_VERSION == latest_mri
7
- require 'simplecov'
8
- SimpleCov.start do
9
- add_filter '/spec/'
10
- end
11
- end
12
- end
13
-
14
- begin
15
- require "pry"
16
- require "pry-byebug"
17
- rescue LoadError; end
18
-
19
- require "dry-transaction"
20
- require "dry-matcher"
21
- require "dry-monads"
22
- require "dry-container"
23
-
24
- # Requires supporting ruby files with custom matchers and macros, etc, in
25
- # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
26
- # run as spec files by default. This means that files in spec/support that end
27
- # in _spec.rb will both be required and run as specs, causing the specs to be
28
- # run twice. It is recommended that you do not name files matching this glob to
29
- # end with _spec.rb. You can configure this pattern with the --pattern
30
- # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
31
- #
32
- # The following line is provided for convenience purposes. It has the downside
33
- # of increasing the boot-up time by auto-requiring all files in the support
34
- # directory. Alternatively, in the individual `*_spec.rb` files, manually
35
- # require only the support files necessary.
36
- Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each do |f| require f end
37
-
38
- # This file was generated by the `rspec --init` command. Conventionally, all
39
- # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
40
- # The generated `.rspec` file contains `--require spec_helper` which will cause
41
- # this file to always be loaded, without a need to explicitly require it in any
42
- # files.
43
- #
44
- # Given that it is always loaded, you are encouraged to keep this file as
45
- # light-weight as possible. Requiring heavyweight dependencies from this file
46
- # will add to the boot time of your test suite on EVERY test run, even for an
47
- # individual file that may not need all of that loaded. Instead, consider making
48
- # a separate helper file that requires the additional dependencies and performs
49
- # the additional setup, and require it from the spec files that actually need
50
- # it.
51
- #
52
- # The `.rspec` file also contains a few flags that are not defaults but that
53
- # users commonly want.
54
- #
55
- # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
56
- RSpec.configure do |config|
57
- # rspec-expectations config goes here. You can use an alternate
58
- # assertion/expectation library such as wrong or the stdlib/minitest
59
- # assertions if you prefer.
60
- config.expect_with :rspec do |expectations|
61
- # This option will default to `true` in RSpec 4. It makes the `description`
62
- # and `failure_message` of custom matchers include text for helper methods
63
- # defined using `chain`, e.g.:
64
- # be_bigger_than(2).and_smaller_than(4).description
65
- # # => "be bigger than 2 and smaller than 4"
66
- # ...rather than:
67
- # # => "be bigger than 2"
68
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
69
- end
70
-
71
- # rspec-mocks config goes here. You can use an alternate test double
72
- # library (such as bogus or mocha) by changing the `mock_with` option here.
73
- config.mock_with :rspec do |mocks|
74
- # Prevents you from mocking or stubbing a method that does not exist on
75
- # a real object. This is generally recommended, and will default to
76
- # `true` in RSpec 4.
77
- mocks.verify_partial_doubles = true
78
- end
79
-
80
- # Allows RSpec to persist some state between runs in order to support
81
- # the `--only-failures` and `--next-failure` CLI options. We recommend
82
- # you configure your source control system to ignore this file.
83
- config.example_status_persistence_file_path = "spec/examples.txt"
84
-
85
- # Limits the available syntax to the non-monkey patched syntax that is
86
- # recommended.
87
- config.disable_monkey_patching!
88
-
89
- # This setting enables warnings. It's recommended, but in some cases may
90
- # be too noisy due to issues in dependencies.
91
- config.warnings = true
92
-
93
- # Many RSpec users commonly either run the entire suite or an individual
94
- # file, and it's useful to allow more verbose output when running an
95
- # individual spec file.
96
- if config.files_to_run.one?
97
- # Use the documentation formatter for detailed output,
98
- # unless a formatter has already been configured
99
- # (e.g. via a command-line flag).
100
- config.default_formatter = "doc"
101
- end
102
-
103
- # Run specs in random order to surface order dependencies. If you find an
104
- # order dependency and want to debug it, you can fix the order by providing
105
- # the seed, which is printed after each run.
106
- # --seed 1234
107
- config.order = :random
108
-
109
- # Seed global randomization in this process using the `--seed` CLI option.
110
- # Setting this allows you to use `--seed` to deterministically reproduce
111
- # test failures related to randomization by passing the same `--seed` value
112
- # as the one that triggered the failure.
113
- Kernel.srand config.seed
114
-
115
- config.include Dry::Monads::Result::Mixin, adapter: true
116
- end
@@ -1,10 +0,0 @@
1
- RSpec.shared_context "container" do
2
- before do
3
- class Test::Container
4
- extend Dry::Container::Mixin
5
- extend Dry::Monads::Result::Mixin
6
- end
7
- end
8
-
9
- let(:container) { Test::Container }
10
- end
@@ -1,12 +0,0 @@
1
- RSpec.shared_context "database" do
2
- include_context "container"
3
-
4
- before do
5
- Test::NotValidError = Class.new(StandardError)
6
- Test::DB = []
7
-
8
- Test::Container.register(:database, Test::DB)
9
- end
10
-
11
- let(:database) { Test::DB }
12
- end
@@ -1,45 +0,0 @@
1
- RSpec.shared_context "db transactions" do
2
- include_context "database"
3
-
4
- before do
5
- Test::Rollback = Class.new(StandardError)
6
-
7
- class << Test::DB
8
- attr_accessor :in_transaction, :rolled_back, :committed
9
- alias_method :in_transaction?, :in_transaction
10
- alias_method :rolled_back?, :rolled_back
11
- alias_method :committed?, :committed
12
-
13
- def transaction
14
- self.in_transaction = true
15
- self.rolled_back = false
16
- self.committed = false
17
-
18
- yield.tap do
19
- self.committed = true
20
- end
21
- rescue => error
22
- self.rolled_back = true
23
- clear
24
-
25
- raise error
26
- ensure
27
- self.in_transaction = false
28
- end
29
- end
30
-
31
- container.register(:transaction) do |input, &block|
32
- result = nil
33
-
34
- begin
35
- Test::DB.transaction do
36
- result = block.(Success(input))
37
- raise Test::Rollback if result.failure?
38
- result
39
- end
40
- rescue Test::Rollback
41
- result
42
- end
43
- end
44
- end
45
- end
@@ -1,3 +0,0 @@
1
- RSpec.configure do |config|
2
- config.include Dry::Monads::Result::Mixin
3
- end
@@ -1,11 +0,0 @@
1
- module Test
2
- def self.remove_constants
3
- constants.each(&method(:remove_const))
4
- end
5
- end
6
-
7
- RSpec.configure do |config|
8
- config.after do
9
- Test.remove_constants
10
- end
11
- end
@@ -1,46 +0,0 @@
1
- RSpec.describe Dry::Transaction::StepAdapters::Around, :adapter do
2
- subject { described_class.new }
3
-
4
- let(:operation) {
5
- -> (input, &block) { block.(Success(input.upcase)) }
6
- }
7
-
8
- let(:options) { { step_name: "unit" } }
9
-
10
- let(:continue) do
11
- -> (input) { input.fmap { |v| v + " terminated" } }
12
- end
13
-
14
- describe "#call" do
15
- context "when the result of the operation is NOT a Dry::Monads::Result" do
16
- let(:continue) do
17
- -> (input) { "plain string" }
18
- end
19
-
20
- it "raises an InvalidResultError" do
21
- expect {
22
- subject.(operation, options, ["input"], &continue)
23
- }.to raise_error(
24
- Dry::Transaction::InvalidResultError,
25
- "step +unit+ must return a Result object"
26
- )
27
- end
28
- end
29
-
30
- context "passing a block" do
31
- it "returns a Success value with result from block" do
32
- expect(subject.(operation, options, ["input"], &continue)).to eql(Success("INPUT terminated"))
33
- end
34
- end
35
-
36
- context "when the result of the operation is a Failure value" do
37
- let(:operation) {
38
- -> (input, &block) { block.(Failure(input.upcase)) }
39
- }
40
-
41
- it "return a Failure value" do
42
- expect(subject.(operation, options, ["input"], &continue)).to eql(Failure("INPUT"))
43
- end
44
- end
45
- end
46
- end
@@ -1,43 +0,0 @@
1
- RSpec.describe Dry::Transaction::StepAdapters::Check, :adapter do
2
-
3
- subject { described_class.new }
4
-
5
- let(:operation) {
6
- -> (input) { input == "right" }
7
- }
8
-
9
- let(:options) { { step_name: "unit" } }
10
-
11
- describe "#call" do
12
-
13
- it "returns the result of the operation as output" do
14
- expect(subject.(operation, options, ["right"])).to eql(Success("right"))
15
- end
16
-
17
- context "when check fails" do
18
- it "return a Failure" do
19
- expect(subject.(operation, options, ["wrong"])).to eql(Failure("wrong"))
20
- end
21
- end
22
-
23
- context "when operation return right monad" do
24
- let(:operation) {
25
- -> (input) { Success(true) }
26
- }
27
-
28
- it "return a Success" do
29
- expect(subject.(operation, options, ["input"])).to eql(Success("input"))
30
- end
31
- end
32
-
33
- context "when operation return failure monad" do
34
- let(:operation) {
35
- -> (input) { Failure(true) }
36
- }
37
-
38
- it "return a Failure" do
39
- expect(subject.(operation, options, ["input"])).to eql(Failure("input"))
40
- end
41
- end
42
- end
43
- end
@@ -1,16 +0,0 @@
1
- RSpec.describe Dry::Transaction::StepAdapters::Map, :adapter do
2
-
3
- subject { described_class.new }
4
-
5
- let(:options) { {} }
6
-
7
- let(:operation) {
8
- -> (input) { input.upcase }
9
- }
10
-
11
- describe "#call" do
12
- it "return a Success value" do
13
- expect(subject.(operation, options, 'input')).to eql(Success('INPUT'))
14
- end
15
- end
16
- end
@@ -1,36 +0,0 @@
1
- RSpec.describe Dry::Transaction::StepAdapters::Raw, adapter: true do
2
-
3
- subject { described_class.new }
4
-
5
- let(:options) { { step_name: "unit" } }
6
-
7
- describe "#call" do
8
-
9
- context "when the result of the operation is NOT a Dry::Monads::Result" do
10
-
11
- let(:operation) {
12
- -> (input) { input.upcase }
13
- }
14
-
15
- it "raises an InvalidResultError" do
16
- expect {
17
- subject.(operation, options, "input")
18
- }.to raise_error(
19
- Dry::Transaction::InvalidResultError,
20
- "step +unit+ must return a Result object"
21
- )
22
- end
23
- end
24
-
25
- context "when the result of the operation is a Success value" do
26
-
27
- let(:operation) {
28
- -> (input) { Success(input.upcase) }
29
- }
30
-
31
- it "return a Success value" do
32
- expect(subject.(operation, options, "input")).to eql(Success("INPUT"))
33
- end
34
- end
35
- end
36
- end