dry-transaction 0.13.0 → 0.13.1

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +308 -0
  3. data/LICENSE +20 -0
  4. data/README.md +15 -41
  5. data/dry-transaction.gemspec +35 -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 +5 -0
  10. data/lib/dry/transaction/dsl.rb +2 -0
  11. data/lib/dry/transaction/errors.rb +2 -0
  12. data/lib/dry/transaction/instance_methods.rb +6 -4
  13. data/lib/dry/transaction/operation.rb +5 -3
  14. data/lib/dry/transaction/operation_resolver.rb +2 -0
  15. data/lib/dry/transaction/result_matcher.rb +3 -1
  16. data/lib/dry/transaction/stack.rb +2 -0
  17. data/lib/dry/transaction/step.rb +6 -4
  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 +2 -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 -88
  34. data/spec/integration/around_spec.rb +0 -120
  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 -573
  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 -139
@@ -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, with: :fetch_data
32
- map :process, with: :process
33
- try :validate, with: :validate, catch: Test::NotValidError
34
- tee :persist, with: :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, with: :call_outside
71
- map :fetch_data, with: :fetch_data
72
- map :process, with: :process
73
- try :validate, with: :validate, catch: Test::NotValidError
74
- tee :external_store, with: :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, with: :process
89
- try :validate, with: :validate, catch: Test::NotValidError
90
- tee :call_outside, with: :call_outside
91
- tee :external_store, with: :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
@@ -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