trailblazer-context 0.1.4 → 0.3.3
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/.gitignore +1 -0
- data/.travis.yml +9 -5
- data/CHANGES.md +31 -0
- data/LICENSE +1 -1
- data/Rakefile +5 -2
- data/lib/trailblazer-context.rb +1 -0
- data/lib/trailblazer/container_chain.rb +0 -2
- data/lib/trailblazer/context.rb +13 -72
- data/lib/trailblazer/context/container.rb +100 -0
- data/lib/trailblazer/context/container/with_aliases.rb +102 -0
- data/lib/trailblazer/context/store/indifferent_access.rb +36 -0
- data/lib/trailblazer/context/version.rb +2 -2
- data/lib/trailblazer/option.rb +18 -19
- data/test/benchmark/benchmark_helper.rb +32 -0
- data/test/benchmark/indifferent_access_test.rb +89 -0
- data/test/benchmark/indifferent_access_with_aliasing_test.rb +73 -0
- data/test/context_test.rb +239 -41
- data/test/option_test.rb +6 -6
- data/test/test_helper.rb +0 -1
- data/trailblazer-context.gemspec +3 -2
- metadata +20 -15
- data/.rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml +0 -136
- data/.rubocop.yml +0 -40
- data/lib/trailblazer/context/indifferent_access.rb +0 -18
    
        data/lib/trailblazer/option.rb
    CHANGED
    
    | @@ -1,22 +1,5 @@ | |
| 1 1 | 
             
            module Trailblazer
         | 
| 2 | 
            -
              # @note This might go to trailblazer-args along with `Context` at some point.
         | 
| 3 | 
            -
              def self.Option(proc)
         | 
| 4 | 
            -
                Option.build(Option, proc)
         | 
| 5 | 
            -
              end
         | 
| 6 | 
            -
             | 
| 7 2 | 
             
              class Option
         | 
| 8 | 
            -
                # Generic builder for a callable "option".
         | 
| 9 | 
            -
                # @param call_implementation [Class, Module] implements the process of calling the proc
         | 
| 10 | 
            -
                #   while passing arguments/options to it in a specific style (e.g. kw args, step interface).
         | 
| 11 | 
            -
                # @return [Proc] when called, this proc will evaluate its option (at run-time).
         | 
| 12 | 
            -
                def self.build(call_implementation, proc)
         | 
| 13 | 
            -
                  if proc.is_a? Symbol
         | 
| 14 | 
            -
                    ->(*args, &block) { call_implementation.evaluate_method(proc, *args, &block) }
         | 
| 15 | 
            -
                  else
         | 
| 16 | 
            -
                    ->(*args, &block) { call_implementation.evaluate_callable(proc, *args, &block) }
         | 
| 17 | 
            -
                  end
         | 
| 18 | 
            -
                end
         | 
| 19 | 
            -
             | 
| 20 3 | 
             
                # A call implementation invoking `proc.(*args)` and plainly forwarding all arguments.
         | 
| 21 4 | 
             
                # Override this for your own step strategy (see KW#call!).
         | 
| 22 5 | 
             
                # @private
         | 
| @@ -27,16 +10,28 @@ module Trailblazer | |
| 27 10 | 
             
                # Note that both #evaluate_callable and #evaluate_method drop most of the args.
         | 
| 28 11 | 
             
                # If you need those, override this class.
         | 
| 29 12 | 
             
                # @private
         | 
| 30 | 
            -
                def self.evaluate_callable(proc, *args,  | 
| 13 | 
            +
                def self.evaluate_callable(proc, *args, **, &block)
         | 
| 31 14 | 
             
                  call!(proc, *args, &block)
         | 
| 32 15 | 
             
                end
         | 
| 33 16 |  | 
| 34 17 | 
             
                # Make the context's instance method a "lambda" and reuse #call!.
         | 
| 35 18 | 
             
                # @private
         | 
| 36 | 
            -
                def self.evaluate_method(proc, *args, exec_context: raise("No :exec_context given."),  | 
| 19 | 
            +
                def self.evaluate_method(proc, *args, exec_context: raise("No :exec_context given."), **, &block)
         | 
| 37 20 | 
             
                  call!(exec_context.method(proc), *args, &block)
         | 
| 38 21 | 
             
                end
         | 
| 39 22 |  | 
| 23 | 
            +
                # Generic builder for a callable "option".
         | 
| 24 | 
            +
                # @param call_implementation [Class, Module] implements the process of calling the proc
         | 
| 25 | 
            +
                #   while passing arguments/options to it in a specific style (e.g. kw args, step interface).
         | 
| 26 | 
            +
                # @return [Proc] when called, this proc will evaluate its option (at run-time).
         | 
| 27 | 
            +
                def self.build(call_implementation, proc)
         | 
| 28 | 
            +
                  if proc.is_a? Symbol
         | 
| 29 | 
            +
                    ->(*args, &block) { call_implementation.evaluate_method(proc, *args, &block) }
         | 
| 30 | 
            +
                  else
         | 
| 31 | 
            +
                    ->(*args, &block) { call_implementation.evaluate_callable(proc, *args, &block) }
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 40 35 | 
             
                # Returns a {Proc} that, when called, invokes the `proc` argument with keyword arguments.
         | 
| 41 36 | 
             
                # This is known as "step (call) interface".
         | 
| 42 37 | 
             
                #
         | 
| @@ -75,4 +70,8 @@ module Trailblazer | |
| 75 70 | 
             
                  end
         | 
| 76 71 | 
             
                end
         | 
| 77 72 | 
             
              end
         | 
| 73 | 
            +
              # @note This might go to trailblazer-args along with `Context` at some point.
         | 
| 74 | 
            +
              def self.Option(proc)
         | 
| 75 | 
            +
                Option.build(Option, proc)
         | 
| 76 | 
            +
              end
         | 
| 78 77 | 
             
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            require 'test_helper'
         | 
| 2 | 
            +
            require 'benchmark/ips'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            BenchmarkRepresenter = Struct.new(:benchmark, :times_slower)
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Minitest::Spec.class_eval do
         | 
| 7 | 
            +
              def benchmark_ips(records, time: 1, warmup: 1)
         | 
| 8 | 
            +
                base    = records[:base]
         | 
| 9 | 
            +
                target  = records[:target]
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                benchmark = Benchmark.ips do |x|
         | 
| 12 | 
            +
                  x.config(time: time, warmup: warmup)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  x.report(base[:label], &base[:block])
         | 
| 15 | 
            +
                  x.report(target[:label], &target[:block])
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  x.compare!
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                times_slower = benchmark.data[0][:ips] / benchmark.data[1][:ips]
         | 
| 21 | 
            +
                BenchmarkRepresenter.new(benchmark, times_slower)
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def assert_times_slower(result, threshold)
         | 
| 25 | 
            +
                base    = result.benchmark.data[0]
         | 
| 26 | 
            +
                target  = result.benchmark.data[1]
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                msg = "Expected #{target[:name]} to be slower by at most #{threshold} times than #{base[:name]}, but got #{result.times_slower}"
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                assert result.times_slower < threshold, msg
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,89 @@ | |
| 1 | 
            +
            require_relative "benchmark_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe "Context::IndifferentAccess Performance" do
         | 
| 4 | 
            +
              wrapped_options = { model: Object, policy: Hash, representer: String }
         | 
| 5 | 
            +
              mutable_options = { write: String, read: Integer, delete: Float, merge: Symbol }
         | 
| 6 | 
            +
              context_options = {
         | 
| 7 | 
            +
                container_class: Trailblazer::Context::Container,
         | 
| 8 | 
            +
                replica_class: Trailblazer::Context::Store::IndifferentAccess,
         | 
| 9 | 
            +
              }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              default_hash      = Hash(**wrapped_options, **mutable_options)
         | 
| 12 | 
            +
              indifferent_hash  = Trailblazer::Context.build(wrapped_options, mutable_options, context_options)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              it "initialize" do
         | 
| 15 | 
            +
                result = benchmark_ips(
         | 
| 16 | 
            +
                  base: { label: :initialize_default_hash, block: ->{
         | 
| 17 | 
            +
                    Hash(**wrapped_options, **mutable_options)
         | 
| 18 | 
            +
                  }},
         | 
| 19 | 
            +
                  target: { label: :initialize_indifferent_hash, block: ->{
         | 
| 20 | 
            +
                    Trailblazer::Context.build(wrapped_options, mutable_options, context_options)
         | 
| 21 | 
            +
                  }},
         | 
| 22 | 
            +
                )
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                assert_times_slower result, 3
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              it "read" do
         | 
| 28 | 
            +
                result = benchmark_ips(
         | 
| 29 | 
            +
                  base: { label: :read_from_default_hash, block: ->{ default_hash[:read] } },
         | 
| 30 | 
            +
                  target: { label: :read_from_indifferent_hash, block: ->{ indifferent_hash[:read] } },
         | 
| 31 | 
            +
                )
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                assert_times_slower result, 1.4
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              it "unknown read" do
         | 
| 37 | 
            +
                result = benchmark_ips(
         | 
| 38 | 
            +
                  base: { label: :unknown_read_from_default_hash, block: ->{ default_hash[:unknown] } },
         | 
| 39 | 
            +
                  target: { label: :unknown_read_from_indifferent_hash, block: ->{ indifferent_hash[:unknown] } },
         | 
| 40 | 
            +
                )
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                assert_times_slower result, 3.5
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              it "write" do
         | 
| 46 | 
            +
                result = benchmark_ips(
         | 
| 47 | 
            +
                  base: { label: :write_to_default_hash, block: ->{ default_hash[:write] = "" } },
         | 
| 48 | 
            +
                  target: { label: :write_to_indifferent_hash, block: ->{ indifferent_hash[:write] = "SKU-1" } },
         | 
| 49 | 
            +
                )
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                assert_times_slower result, 2.3
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              it "delete" do
         | 
| 55 | 
            +
                result = benchmark_ips(
         | 
| 56 | 
            +
                  base: { label: :delete_from_default_hash, block: ->{ default_hash.delete(:delete) } },
         | 
| 57 | 
            +
                  target: { label: :delete_from_indifferent_hash, block: ->{ indifferent_hash.delete(:delete) } },
         | 
| 58 | 
            +
                )
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                assert_times_slower result, 2.4
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              it "merge" do
         | 
| 64 | 
            +
                result = benchmark_ips(
         | 
| 65 | 
            +
                  base: { label: :merge_from_default_hash, block: ->{ default_hash.merge(merge: :object_id) } },
         | 
| 66 | 
            +
                  target: { label: :merge_from_indifferent_hash, block: ->{ indifferent_hash.merge(merge: :object_id) } },
         | 
| 67 | 
            +
                )
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                assert_times_slower result, 5.55
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              it "to_hash" do
         | 
| 73 | 
            +
                result = benchmark_ips(
         | 
| 74 | 
            +
                  base: { label: :default_to_hash, block: ->{ default_hash.to_hash } },
         | 
| 75 | 
            +
                  target: { label: :indifferent_to_hash, block: ->{ indifferent_hash.to_hash } },
         | 
| 76 | 
            +
                )
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                assert_times_slower result, 1.3
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              it "decompose" do
         | 
| 82 | 
            +
                result = benchmark_ips(
         | 
| 83 | 
            +
                  base: { label: :dup_default_hash, block: ->{ default_hash.to_hash } },
         | 
| 84 | 
            +
                  target: { label: :decompose, block: ->{ indifferent_hash.decompose } },
         | 
| 85 | 
            +
                )
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                assert_times_slower result, 1.55
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
            end
         | 
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            require_relative "benchmark_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe "Context::Aliasing Performance" do
         | 
| 4 | 
            +
              wrapped_options = { model: Object, policy: Hash, representer: String }
         | 
| 5 | 
            +
              mutable_options = { write: String, read: Integer, delete: Float, merge: Symbol }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              context_options = {
         | 
| 8 | 
            +
                container_class: Trailblazer::Context::Container::WithAliases,
         | 
| 9 | 
            +
                replica_class: Trailblazer::Context::Store::IndifferentAccess,
         | 
| 10 | 
            +
                aliases: { read: :reader }
         | 
| 11 | 
            +
              }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              default_hash  = Hash(**wrapped_options, **mutable_options)
         | 
| 14 | 
            +
              aliased_hash  = Trailblazer::Context.build(wrapped_options, mutable_options, context_options)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              it "initialize" do
         | 
| 17 | 
            +
                result = benchmark_ips(
         | 
| 18 | 
            +
                  base: { label: :initialize_default_hash, block: ->{
         | 
| 19 | 
            +
                    Hash(**wrapped_options, **mutable_options)
         | 
| 20 | 
            +
                  }},
         | 
| 21 | 
            +
                  target: { label: :initialize_aliased_hash, block: ->{
         | 
| 22 | 
            +
                    Trailblazer::Context.build(wrapped_options, mutable_options, context_options)
         | 
| 23 | 
            +
                  }},
         | 
| 24 | 
            +
                )
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                assert_times_slower result, 8
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              it "write" do
         | 
| 30 | 
            +
                result = benchmark_ips(
         | 
| 31 | 
            +
                  base: { label: :write_to_default_hash, block: ->{ default_hash[:write] = "" } },
         | 
| 32 | 
            +
                  target: { label: :write_to_aliased_hash, block: ->{ aliased_hash[:write] = "" } },
         | 
| 33 | 
            +
                )
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                assert_times_slower result, 3.66
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              it "read" do
         | 
| 39 | 
            +
                result = benchmark_ips(
         | 
| 40 | 
            +
                  base: { label: :read_from_default_hash, block: ->{ default_hash[:read] } },
         | 
| 41 | 
            +
                  target: { label: :read_from_aliased_hash, block: ->{ aliased_hash[:reader] } },
         | 
| 42 | 
            +
                )
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                assert_times_slower result, 1.5
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              it "delete" do
         | 
| 48 | 
            +
                result = benchmark_ips(
         | 
| 49 | 
            +
                  base: { label: :delete_from_default_hash, block: ->{ default_hash.delete(:delete) } },
         | 
| 50 | 
            +
                  target: { label: :delete_from_aliased_hash, block: ->{ aliased_hash.delete(:delete) } },
         | 
| 51 | 
            +
                )
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                assert_times_slower result, 4
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              it "merge" do
         | 
| 57 | 
            +
                result = benchmark_ips(
         | 
| 58 | 
            +
                  base: { label: :merge_from_default_hash, block: ->{ default_hash.merge(merge: :object_id) } },
         | 
| 59 | 
            +
                  target: { label: :merge_from_aliased_hash, block: ->{ aliased_hash.merge(merge: :object_id) } },
         | 
| 60 | 
            +
                )
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                assert_times_slower result, 8.5
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              it "to_hash" do
         | 
| 66 | 
            +
                result = benchmark_ips(
         | 
| 67 | 
            +
                  base: { label: :default_to_hash, block: ->{ default_hash.to_hash } },
         | 
| 68 | 
            +
                  target: { label: :aliased_to_hash, block: ->{ aliased_hash.to_hash } },
         | 
| 69 | 
            +
                )
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                assert_times_slower result, 1.5
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
            end
         | 
    
        data/test/context_test.rb
    CHANGED
    
    | @@ -2,8 +2,6 @@ require "test_helper" | |
| 2 2 | 
             
            require "trailblazer/container_chain"
         | 
| 3 3 |  | 
| 4 4 | 
             
            class ArgsTest < Minitest::Spec
         | 
| 5 | 
            -
              Context = Trailblazer::Context
         | 
| 6 | 
            -
             | 
| 7 5 | 
             
              let(:immutable) { {repository: "User"} }
         | 
| 8 6 |  | 
| 9 7 | 
             
              let(:ctx) { Trailblazer::Context(immutable) }
         | 
| @@ -16,16 +14,17 @@ class ArgsTest < Minitest::Spec | |
| 16 14 | 
             
                # options[] and options[]=
         | 
| 17 15 | 
             
                ctx[:model]    = Module
         | 
| 18 16 | 
             
                ctx[:contract] = Integer
         | 
| 19 | 
            -
                ctx[:model]   .must_equal Module
         | 
| 20 | 
            -
                ctx[:contract].must_equal Integer
         | 
| 17 | 
            +
                _(ctx[:model])   .must_equal Module
         | 
| 18 | 
            +
                _(ctx[:contract]).must_equal Integer
         | 
| 21 19 |  | 
| 22 20 | 
             
                # it {  }
         | 
| 23 | 
            -
                immutable.inspect.must_equal %({:repository=>\"User\"})
         | 
| 21 | 
            +
                _(immutable.inspect).must_equal %({:repository=>\"User\"})
         | 
| 22 | 
            +
                _(ctx.inspect).must_equal %{#<Trailblazer::Context::Container wrapped_options={:repository=>\"User\"} mutable_options={:model=>Module, :contract=>Integer}>}
         | 
| 24 23 | 
             
              end
         | 
| 25 24 |  | 
| 26 25 | 
             
              it "allows false/nil values" do
         | 
| 27 26 | 
             
                ctx["x"] = false
         | 
| 28 | 
            -
                ctx["x"].must_equal false
         | 
| 27 | 
            +
                _(ctx["x"]).must_equal false
         | 
| 29 28 |  | 
| 30 29 | 
             
                ctx["x"] = nil
         | 
| 31 30 | 
             
                assert_nil ctx["x"]
         | 
| @@ -36,7 +35,7 @@ class ArgsTest < Minitest::Spec | |
| 36 35 | 
             
                ctx = Trailblazer::Context(immutable)
         | 
| 37 36 |  | 
| 38 37 | 
             
                # it {  }
         | 
| 39 | 
            -
                ctx.to_hash.must_equal(repository: "User")
         | 
| 38 | 
            +
                _(ctx.to_hash).must_equal(repository: "User")
         | 
| 40 39 |  | 
| 41 40 | 
             
                # last added has precedence.
         | 
| 42 41 | 
             
                # only symbol keys.
         | 
| @@ -44,7 +43,7 @@ class ArgsTest < Minitest::Spec | |
| 44 43 | 
             
                ctx[:a] = Symbol
         | 
| 45 44 | 
             
                ctx["a"] = String
         | 
| 46 45 |  | 
| 47 | 
            -
                ctx.to_hash.must_equal(repository: "User", a: String)
         | 
| 46 | 
            +
                _(ctx.to_hash).must_equal(repository: "User", a: String)
         | 
| 48 47 | 
             
              end
         | 
| 49 48 |  | 
| 50 49 | 
             
              describe "#merge" do
         | 
| @@ -53,72 +52,271 @@ class ArgsTest < Minitest::Spec | |
| 53 52 |  | 
| 54 53 | 
             
                  merged = ctx.merge(current_user: Module)
         | 
| 55 54 |  | 
| 56 | 
            -
                  merged. | 
| 57 | 
            -
                   | 
| 55 | 
            +
                  _(merged.class).must_equal(Trailblazer::Context::Container)
         | 
| 56 | 
            +
                  _(merged.to_hash).must_equal(repository: "User", current_user: Module)
         | 
| 57 | 
            +
                  _(ctx.to_hash).must_equal(repository: "User")
         | 
| 58 58 | 
             
                end
         | 
| 59 59 | 
             
              end
         | 
| 60 60 |  | 
| 61 | 
            -
               | 
| 61 | 
            +
              describe "Enumerable behaviour" do
         | 
| 62 | 
            +
                it { _(ctx.each.to_a).must_equal [[:repository, "User"]] }
         | 
| 63 | 
            +
                it { _(ctx.find{ |k, _| k == :repository }).must_equal [:repository, "User"] }
         | 
| 64 | 
            +
                it { _(ctx.inject([]){ |r, (k, _)| r << k}).must_equal [:repository] }
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              #- #decompose
         | 
| 62 68 | 
             
              it do
         | 
| 63 69 | 
             
                immutable = {repository: "User", model: Module, current_user: Class}
         | 
| 70 | 
            +
                mutable   = {error: RuntimeError}
         | 
| 64 71 |  | 
| 65 | 
            -
                Trailblazer::Context(immutable | 
| 66 | 
            -
                  mutable
         | 
| 67 | 
            -
                end
         | 
| 72 | 
            +
                _([immutable, mutable]).must_equal Trailblazer::Context(immutable, mutable).decompose
         | 
| 68 73 | 
             
              end
         | 
| 69 74 | 
             
            end
         | 
| 70 75 |  | 
| 71 76 | 
             
            class ContextWithIndifferentAccessTest < Minitest::Spec
         | 
| 72 77 | 
             
              it do
         | 
| 73 | 
            -
                flow_options    = { | 
| 78 | 
            +
                flow_options    = {
         | 
| 79 | 
            +
                  context_options: {
         | 
| 80 | 
            +
                    container_class: Trailblazer::Context::Container,
         | 
| 81 | 
            +
                    replica_class: Trailblazer::Context::Store::IndifferentAccess
         | 
| 82 | 
            +
                  }
         | 
| 83 | 
            +
                }
         | 
| 84 | 
            +
             | 
| 74 85 | 
             
                circuit_options = {}
         | 
| 75 86 |  | 
| 76 87 | 
             
                immutable       = {model: Object, "policy" => Hash}
         | 
| 77 88 |  | 
| 78 | 
            -
                ctx = Trailblazer::Context. | 
| 89 | 
            +
                ctx = Trailblazer::Context.for_circuit(immutable, {}, [immutable, flow_options], **circuit_options)
         | 
| 79 90 |  | 
| 80 | 
            -
                ctx[:model].must_equal Object
         | 
| 81 | 
            -
                ctx["model"].must_equal Object
         | 
| 82 | 
            -
                ctx[:policy].must_equal Hash
         | 
| 83 | 
            -
                ctx["policy"].must_equal Hash
         | 
| 91 | 
            +
                _(ctx[:model]).must_equal Object
         | 
| 92 | 
            +
                _(ctx["model"]).must_equal Object
         | 
| 93 | 
            +
                _(ctx[:policy]).must_equal Hash
         | 
| 94 | 
            +
                _(ctx["policy"]).must_equal Hash
         | 
| 84 95 |  | 
| 85 96 | 
             
                ctx["contract.default"] = Module
         | 
| 86 | 
            -
                ctx["contract.default"].must_equal Module
         | 
| 87 | 
            -
                ctx[:"contract.default"].must_equal Module
         | 
| 97 | 
            +
                _(ctx["contract.default"]).must_equal Module
         | 
| 98 | 
            +
                _(ctx[:"contract.default"]).must_equal Module
         | 
| 88 99 |  | 
| 89 100 | 
             
            # key?
         | 
| 90 | 
            -
                ctx.key?("____contract.default").must_equal false
         | 
| 91 | 
            -
                ctx.key?("contract.default").must_equal true
         | 
| 92 | 
            -
                ctx.key?(:"contract.default").must_equal true
         | 
| 101 | 
            +
                _(ctx.key?("____contract.default")).must_equal false
         | 
| 102 | 
            +
                _(ctx.key?("contract.default")).must_equal true
         | 
| 103 | 
            +
                _(ctx.key?(:"contract.default")).must_equal true
         | 
| 93 104 |  | 
| 94 105 | 
             
            # context in context
         | 
| 95 | 
            -
                ctx2 = Trailblazer::Context. | 
| 106 | 
            +
                ctx2 = Trailblazer::Context.for_circuit(ctx, {}, [ctx, flow_options], **circuit_options)
         | 
| 96 107 |  | 
| 97 | 
            -
                ctx2[:model].must_equal Object
         | 
| 98 | 
            -
                ctx2["model"].must_equal Object
         | 
| 108 | 
            +
                _(ctx2[:model]).must_equal Object
         | 
| 109 | 
            +
                _(ctx2["model"]).must_equal Object
         | 
| 99 110 |  | 
| 100 111 | 
             
                ctx2["contract.default"] = Class
         | 
| 101 | 
            -
                ctx2["contract.default"].must_equal Class
         | 
| 102 | 
            -
                ctx2[:"contract.default"].must_equal Class
         | 
| 112 | 
            +
                _(ctx2["contract.default"]).must_equal Class
         | 
| 113 | 
            +
                _(ctx2[:"contract.default"]).must_equal Class
         | 
| 103 114 |  | 
| 104 115 | 
             
            # key?
         | 
| 105 | 
            -
                ctx2.key?("contract.default").must_equal true
         | 
| 106 | 
            -
                ctx2.key?(:"contract.default").must_equal true
         | 
| 107 | 
            -
                ctx2.key?("model").must_equal true
         | 
| 116 | 
            +
                _(ctx2.key?("contract.default")).must_equal true
         | 
| 117 | 
            +
                _(ctx2.key?(:"contract.default")).must_equal true
         | 
| 118 | 
            +
                _(ctx2.key?("model")).must_equal true
         | 
| 108 119 |  | 
| 109 120 | 
             
            # wrapped ctx doesn't change
         | 
| 110 | 
            -
                ctx["contract.default"].must_equal Module
         | 
| 111 | 
            -
                ctx[:"contract.default"].must_equal Module
         | 
| 121 | 
            +
                _(ctx["contract.default"]).must_equal Module
         | 
| 122 | 
            +
                _(ctx[:"contract.default"]).must_equal Module
         | 
| 112 123 |  | 
| 124 | 
            +
            # delete
         | 
| 125 | 
            +
                ctx[:model] = Object
         | 
| 126 | 
            +
                ctx.delete 'model'
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                _(ctx.key?(:model)).must_equal false
         | 
| 129 | 
            +
                _(ctx.key?("model")).must_equal false
         | 
| 113 130 |  | 
| 114 131 | 
             
                ctx3 = ctx.merge("result" => false)
         | 
| 115 132 |  | 
| 116 | 
            -
                ctx3["contract.default"].must_equal Module
         | 
| 117 | 
            -
                ctx3[:"contract.default"].must_equal Module
         | 
| 118 | 
            -
                ctx3["result"].must_equal false
         | 
| 119 | 
            -
                ctx3[:result].must_equal false
         | 
| 120 | 
            -
                ctx3.key?("result").must_equal true
         | 
| 121 | 
            -
                ctx3.key?(:result).must_equal true
         | 
| 133 | 
            +
                _(ctx3["contract.default"]).must_equal Module
         | 
| 134 | 
            +
                _(ctx3[:"contract.default"]).must_equal Module
         | 
| 135 | 
            +
                _(ctx3["result"]).must_equal false
         | 
| 136 | 
            +
                _(ctx3[:result]).must_equal false
         | 
| 137 | 
            +
                _(ctx3.key?("result")).must_equal true
         | 
| 138 | 
            +
                _(ctx3.key?(:result)).must_equal true
         | 
| 139 | 
            +
              end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
              it "Aliasable" do
         | 
| 142 | 
            +
                flow_options    = {
         | 
| 143 | 
            +
                  context_options: {
         | 
| 144 | 
            +
                    container_class: Trailblazer::Context::Container::WithAliases,
         | 
| 145 | 
            +
                    replica_class: Trailblazer::Context::Store::IndifferentAccess,
         | 
| 146 | 
            +
                    aliases: { "contract.default" => :contract, "result.default"=>:result, "trace.stack" => :stack }
         | 
| 147 | 
            +
                  }
         | 
| 148 | 
            +
                }
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                circuit_options = {}
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                immutable       = {model: Object, "policy" => Hash}
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                ctx = Trailblazer::Context.for_circuit(immutable, {}, [immutable, flow_options], **circuit_options)
         | 
| 155 | 
            +
                _(ctx.class).must_equal(Trailblazer::Context::Container::WithAliases)
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                _(ctx.inspect).must_equal %{#<Trailblazer::Context::Container::WithAliases wrapped_options={:model=>Object, \"policy\"=>Hash} mutable_options={} aliases={\"contract.default\"=>:contract, \"result.default\"=>:result, \"trace.stack\"=>:stack}>}
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                _(ctx.to_hash).must_equal(:model=>Object, :policy=>Hash)
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                _(ctx[:model]).must_equal Object
         | 
| 162 | 
            +
                _(ctx["model"]).must_equal Object
         | 
| 163 | 
            +
                _(ctx[:policy]).must_equal Hash
         | 
| 164 | 
            +
                _(ctx["policy"]).must_equal Hash
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                ctx["contract.default"] = Module
         | 
| 167 | 
            +
                _(ctx["contract.default"]).must_equal Module
         | 
| 168 | 
            +
                _(ctx[:"contract.default"]).must_equal Module
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                # alias
         | 
| 171 | 
            +
                assert_nil ctx[:result]
         | 
| 172 | 
            +
                assert_nil ctx["result"]
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                _(ctx[:contract]).must_equal Module
         | 
| 175 | 
            +
                _(ctx['contract']).must_equal Module
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                assert_nil ctx[:stack]
         | 
| 178 | 
            +
                assert_nil ctx['stack']
         | 
| 179 | 
            +
             | 
| 180 | 
            +
              # Set an aliased property via setter
         | 
| 181 | 
            +
                ctx["trace.stack"] = Object
         | 
| 182 | 
            +
                _(ctx[:stack]).must_equal Object
         | 
| 183 | 
            +
                _(ctx["stack"]).must_equal Object
         | 
| 184 | 
            +
                _(ctx["trace.stack"]).must_equal Object
         | 
| 185 | 
            +
             | 
| 186 | 
            +
              # Set an aliased property with merge
         | 
| 187 | 
            +
                ctx["trace.stack"] = String
         | 
| 188 | 
            +
                merged = ctx.merge(stack: Integer)
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                _(merged.class).must_equal(Trailblazer::Context::Container::WithAliases)
         | 
| 191 | 
            +
                _(merged.to_hash).must_equal(:model=>Object, :policy=>Hash, :contract=>Module, :"contract.default"=>Module, :stack=>Integer, :"trace.stack"=>Integer)
         | 
| 192 | 
            +
             | 
| 193 | 
            +
            # key?
         | 
| 194 | 
            +
                _(ctx.key?("____contract.default")).must_equal false
         | 
| 195 | 
            +
                _(ctx.key?("contract.default")).must_equal true
         | 
| 196 | 
            +
                _(ctx.key?(:"contract.default")).must_equal true
         | 
| 197 | 
            +
                _(ctx.key?(:contract)).must_equal true
         | 
| 198 | 
            +
                _(ctx.key?(:result)).must_equal false
         | 
| 199 | 
            +
                _(ctx.key?(:stack)).must_equal true
         | 
| 200 | 
            +
                _(ctx.key?("trace.stack")).must_equal true
         | 
| 201 | 
            +
                _(ctx.key?(:"trace.stack")).must_equal true
         | 
| 202 | 
            +
             | 
| 203 | 
            +
            # delete
         | 
| 204 | 
            +
                ctx[:result] = Object
         | 
| 205 | 
            +
                ctx.delete :result
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                _(ctx.key?(:result)).must_equal false
         | 
| 208 | 
            +
                _(ctx.key?("result")).must_equal false
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                _(ctx.key?(:"result.default")).must_equal false
         | 
| 211 | 
            +
                _(ctx.key?("result.default")).must_equal false
         | 
| 212 | 
            +
             | 
| 213 | 
            +
             | 
| 214 | 
            +
            # to_hash
         | 
| 215 | 
            +
                _(ctx.to_hash).must_equal(:model=>Object, :policy=>Hash, :contract=>Module, :"contract.default"=>Module, :stack=>String, :"trace.stack"=>String)
         | 
| 216 | 
            +
             | 
| 217 | 
            +
            # context in context
         | 
| 218 | 
            +
                ctx2 = Trailblazer::Context.for_circuit(ctx, {}, [ctx, flow_options], **circuit_options)
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                _(ctx2.key?("____contract.default")).must_equal false
         | 
| 221 | 
            +
                _(ctx2.key?("contract.default")).must_equal true
         | 
| 222 | 
            +
                _(ctx2.key?(:"contract.default")).must_equal true
         | 
| 223 | 
            +
                _(ctx2.key?(:contract)).must_equal true
         | 
| 224 | 
            +
                _(ctx2.key?(:result)).must_equal false
         | 
| 225 | 
            +
                _(ctx2.key?("result.default")).must_equal false
         | 
| 226 | 
            +
                _(ctx2.key?(:stack)).must_equal true
         | 
| 227 | 
            +
                _(ctx2.key?("trace.stack")).must_equal true
         | 
| 228 | 
            +
                _(ctx2.key?(:"trace.stack")).must_equal true
         | 
| 229 | 
            +
             | 
| 230 | 
            +
              # Set aliased in new context via setter
         | 
| 231 | 
            +
                ctx2["result.default"] = Class
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                _(ctx2[:result]).must_equal Class
         | 
| 234 | 
            +
                _(ctx2[:"result.default"]).must_equal Class
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                _(ctx2.key?("result.default")).must_equal true
         | 
| 237 | 
            +
                _(ctx2.key?(:"result.default")).must_equal true
         | 
| 238 | 
            +
                _(ctx2.key?(:result)).must_equal true
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                # todo: TEST flow_options={context_class: SomethingElse}
         | 
| 241 | 
            +
              end
         | 
| 242 | 
            +
             | 
| 243 | 
            +
              it ".build accepts custom container class" do
         | 
| 244 | 
            +
                MyContainer = Class.new(Trailblazer::Context::Container) do
         | 
| 245 | 
            +
                  def inspect
         | 
| 246 | 
            +
                    %{#<MyContainer wrapped=#{@wrapped_options} mutable=#{@mutable_options}>}
         | 
| 247 | 
            +
                  end
         | 
| 248 | 
            +
                end
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                immutable = { model: Object }
         | 
| 251 | 
            +
                options   = { container_class: MyContainer, replica_class: Trailblazer::Context::Store::IndifferentAccess }
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                ctx = Trailblazer::Context.build(immutable, {}, options)
         | 
| 254 | 
            +
                _(ctx.class).must_equal(MyContainer)
         | 
| 255 | 
            +
                _(ctx.inspect).must_equal("#<MyContainer wrapped=#{immutable} mutable={}>")
         | 
| 256 | 
            +
             | 
| 257 | 
            +
                _(ctx.to_hash).must_equal({ model: Object })
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                ctx[:integer] = Integer
         | 
| 260 | 
            +
                _(ctx.to_hash).must_equal({ model: Object, integer: Integer })
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                ctx2 = ctx.merge(float: Float)
         | 
| 263 | 
            +
                _(ctx2.class).must_equal(MyContainer)
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                _(ctx2.to_hash).must_equal({ model: Object, integer: Integer, float: Float })
         | 
| 266 | 
            +
              end
         | 
| 267 | 
            +
             | 
| 268 | 
            +
              it ".build accepts custom replica class (For example, To opt out from indifferent access)" do
         | 
| 269 | 
            +
                MyReplica = Class.new(Hash) do
         | 
| 270 | 
            +
                  def initialize(*containers)
         | 
| 271 | 
            +
                    containers.each do |container|
         | 
| 272 | 
            +
                      container.each{ |key, value| self[key] = value }
         | 
| 273 | 
            +
                    end
         | 
| 274 | 
            +
                  end
         | 
| 275 | 
            +
                end
         | 
| 276 | 
            +
             | 
| 277 | 
            +
                immutable = { model: Object }
         | 
| 278 | 
            +
                options   = { container_class: Trailblazer::Context::Container, replica_class: MyReplica }
         | 
| 279 | 
            +
             | 
| 280 | 
            +
                ctx = Trailblazer::Context.build(immutable, {}, options)
         | 
| 281 | 
            +
                ctx[:integer] = Integer
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                _(ctx[:integer]).must_equal(Integer)
         | 
| 284 | 
            +
                _(ctx['integer']).must_be_nil
         | 
| 285 | 
            +
              end
         | 
| 286 | 
            +
             | 
| 287 | 
            +
              it "Context() provides default args" do
         | 
| 288 | 
            +
                immutable = {model: Object, "policy.default" => Hash}
         | 
| 289 | 
            +
                options   = {
         | 
| 290 | 
            +
                  container_class: Trailblazer::Context::Container::WithAliases,
         | 
| 291 | 
            +
                  aliases: { "policy.default" => :policy }
         | 
| 292 | 
            +
                }
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                ctx = Trailblazer::Context(immutable, {}, options)
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                _(ctx[:model]).must_equal Object
         | 
| 297 | 
            +
                _(ctx["model"]).must_equal Object
         | 
| 298 | 
            +
                _(ctx[:policy]).must_equal Hash
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                ctx2 = ctx.merge(result: :success)
         | 
| 301 | 
            +
             | 
| 302 | 
            +
             | 
| 303 | 
            +
                _(ctx2[:model]).must_equal Object
         | 
| 304 | 
            +
                _(ctx2["model"]).must_equal Object
         | 
| 305 | 
            +
                _(ctx2[:policy]).must_equal Hash
         | 
| 306 | 
            +
                _(ctx2[:result]).must_equal :success
         | 
| 307 | 
            +
              end
         | 
| 308 | 
            +
             | 
| 309 | 
            +
              it "Context() throws RuntimeError if aliases are passed but container_class doesn't support it" do
         | 
| 310 | 
            +
                immutable = {model: Object, "policy.default" => Hash}
         | 
| 311 | 
            +
                options   = {
         | 
| 312 | 
            +
                  aliases: { "policy.default" => :policy }
         | 
| 313 | 
            +
                }
         | 
| 314 | 
            +
             | 
| 315 | 
            +
                exception = assert_raises Trailblazer::Context::Container::UseWithAliases do
         | 
| 316 | 
            +
                  Trailblazer::Context(immutable, {}, options)
         | 
| 317 | 
            +
                end
         | 
| 318 | 
            +
             | 
| 319 | 
            +
                _(exception.message).must_equal %{Pass `Trailblazer::Context::Container::WithAliases` as `container_class` while defining `aliases`}
         | 
| 122 320 | 
             
              end
         | 
| 123 321 | 
             
            end
         | 
| 124 322 |  |