trailblazer-future 2.1.0.rc1

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 (161) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +6 -0
  4. data/CHANGES.md +4 -0
  5. data/LICENSE.txt +9 -0
  6. data/README.md +48 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/gems.rb +3 -0
  11. data/lib/trailblazer/future.rb +9 -0
  12. data/lib/trailblazer/future/version.rb +5 -0
  13. data/lib/trailblazer/v2_1/activity.rb +123 -0
  14. data/lib/trailblazer/v2_1/activity/config.rb +37 -0
  15. data/lib/trailblazer/v2_1/activity/dsl/add_task.rb +22 -0
  16. data/lib/trailblazer/v2_1/activity/dsl/helper.rb +68 -0
  17. data/lib/trailblazer/v2_1/activity/dsl/magnetic.rb +36 -0
  18. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder.rb +101 -0
  19. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/default_normalizer.rb +26 -0
  20. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/fast_track.rb +118 -0
  21. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/normalizer.rb +113 -0
  22. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/path.rb +105 -0
  23. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/railway.rb +97 -0
  24. data/lib/trailblazer/v2_1/activity/dsl/magnetic/builder/state.rb +58 -0
  25. data/lib/trailblazer/v2_1/activity/dsl/magnetic/finalizer.rb +51 -0
  26. data/lib/trailblazer/v2_1/activity/dsl/magnetic/generate.rb +62 -0
  27. data/lib/trailblazer/v2_1/activity/dsl/magnetic/merge.rb +16 -0
  28. data/lib/trailblazer/v2_1/activity/dsl/magnetic/process_options.rb +76 -0
  29. data/lib/trailblazer/v2_1/activity/dsl/magnetic/structure/alterations.rb +44 -0
  30. data/lib/trailblazer/v2_1/activity/dsl/magnetic/structure/plus_poles.rb +85 -0
  31. data/lib/trailblazer/v2_1/activity/dsl/magnetic/structure/polarization.rb +23 -0
  32. data/lib/trailblazer/v2_1/activity/dsl/record.rb +11 -0
  33. data/lib/trailblazer/v2_1/activity/dsl/schema/dependencies.rb +46 -0
  34. data/lib/trailblazer/v2_1/activity/dsl/schema/sequence.rb +46 -0
  35. data/lib/trailblazer/v2_1/activity/dsl/strategy/build_state.rb +32 -0
  36. data/lib/trailblazer/v2_1/activity/dsl/strategy/fast_track.rb +24 -0
  37. data/lib/trailblazer/v2_1/activity/dsl/strategy/path.rb +26 -0
  38. data/lib/trailblazer/v2_1/activity/dsl/strategy/plan.rb +36 -0
  39. data/lib/trailblazer/v2_1/activity/dsl/strategy/railway.rb +23 -0
  40. data/lib/trailblazer/v2_1/activity/interface.rb +16 -0
  41. data/lib/trailblazer/v2_1/activity/introspect.rb +167 -0
  42. data/lib/trailblazer/v2_1/activity/present.rb +95 -0
  43. data/lib/trailblazer/v2_1/activity/structures.rb +57 -0
  44. data/lib/trailblazer/v2_1/activity/task_builder.rb +41 -0
  45. data/lib/trailblazer/v2_1/activity/task_wrap.rb +40 -0
  46. data/lib/trailblazer/v2_1/activity/task_wrap/call_task.rb +19 -0
  47. data/lib/trailblazer/v2_1/activity/task_wrap/merge.rb +23 -0
  48. data/lib/trailblazer/v2_1/activity/task_wrap/runner.rb +62 -0
  49. data/lib/trailblazer/v2_1/activity/task_wrap/trace.rb +44 -0
  50. data/lib/trailblazer/v2_1/activity/task_wrap/variable_mapping.rb +162 -0
  51. data/lib/trailblazer/v2_1/activity/testing.rb +32 -0
  52. data/lib/trailblazer/v2_1/activity/trace.rb +97 -0
  53. data/lib/trailblazer/v2_1/circuit.rb +71 -0
  54. data/lib/trailblazer/v2_1/container_chain.rb +45 -0
  55. data/lib/trailblazer/v2_1/context.rb +79 -0
  56. data/lib/trailblazer/v2_1/deprecation/call.rb +46 -0
  57. data/lib/trailblazer/v2_1/deprecation/context.rb +43 -0
  58. data/lib/trailblazer/v2_1/dsl.rb +47 -0
  59. data/lib/trailblazer/v2_1/macro.rb +11 -0
  60. data/lib/trailblazer/v2_1/macro/contract.rb +5 -0
  61. data/lib/trailblazer/v2_1/operation.rb +91 -0
  62. data/lib/trailblazer/v2_1/operation/auto_inject.rb +47 -0
  63. data/lib/trailblazer/v2_1/operation/callable.rb +42 -0
  64. data/lib/trailblazer/v2_1/operation/class_dependencies.rb +25 -0
  65. data/lib/trailblazer/v2_1/operation/contract.rb +61 -0
  66. data/lib/trailblazer/v2_1/operation/deprecated_macro.rb +19 -0
  67. data/lib/trailblazer/v2_1/operation/deprecations.rb +21 -0
  68. data/lib/trailblazer/v2_1/operation/guard.rb +18 -0
  69. data/lib/trailblazer/v2_1/operation/heritage.rb +30 -0
  70. data/lib/trailblazer/v2_1/operation/inject.rb +36 -0
  71. data/lib/trailblazer/v2_1/operation/inspect.rb +79 -0
  72. data/lib/trailblazer/v2_1/operation/model.rb +50 -0
  73. data/lib/trailblazer/v2_1/operation/module.rb +29 -0
  74. data/lib/trailblazer/v2_1/operation/nested.rb +98 -0
  75. data/lib/trailblazer/v2_1/operation/persist.rb +14 -0
  76. data/lib/trailblazer/v2_1/operation/policy.rb +44 -0
  77. data/lib/trailblazer/v2_1/operation/public_call.rb +55 -0
  78. data/lib/trailblazer/v2_1/operation/pundit.rb +38 -0
  79. data/lib/trailblazer/v2_1/operation/railway.rb +32 -0
  80. data/lib/trailblazer/v2_1/operation/railway/fast_track.rb +13 -0
  81. data/lib/trailblazer/v2_1/operation/railway/macaroni.rb +23 -0
  82. data/lib/trailblazer/v2_1/operation/railway/normalizer.rb +58 -0
  83. data/lib/trailblazer/v2_1/operation/railway/task_builder.rb +37 -0
  84. data/lib/trailblazer/v2_1/operation/rescue.rb +42 -0
  85. data/lib/trailblazer/v2_1/operation/responder.rb +18 -0
  86. data/lib/trailblazer/v2_1/operation/result.rb +30 -0
  87. data/lib/trailblazer/v2_1/operation/test.rb +17 -0
  88. data/lib/trailblazer/v2_1/operation/trace.rb +46 -0
  89. data/lib/trailblazer/v2_1/operation/validate.rb +76 -0
  90. data/lib/trailblazer/v2_1/operation/wrap.rb +83 -0
  91. data/lib/trailblazer/v2_1/option.rb +78 -0
  92. data/lib/trailblazer/v2_1/rails.rb +12 -0
  93. data/lib/trailblazer/v2_1/rails/cell.rb +22 -0
  94. data/lib/trailblazer/v2_1/rails/controller.rb +66 -0
  95. data/lib/trailblazer/v2_1/rails/form.rb +21 -0
  96. data/lib/trailblazer/v2_1/rails/railtie.rb +31 -0
  97. data/lib/trailblazer/v2_1/rails/railtie/extend_application_controller.rb +28 -0
  98. data/lib/trailblazer/v2_1/rails/railtie/loader.rb +58 -0
  99. data/lib/trailblazer/v2_1/rails/test/integration.rb +6 -0
  100. data/lib/trailblazer/v2_1/versions.txt +7 -0
  101. data/test/rails5.0/.gitignore +17 -0
  102. data/test/rails5.0/Gemfile +21 -0
  103. data/test/rails5.0/Rakefile +3 -0
  104. data/test/rails5.0/app/concepts/artist/cell/dashboard.rb +7 -0
  105. data/test/rails5.0/app/concepts/artist/cell/show.rb +4 -0
  106. data/test/rails5.0/app/concepts/artist/view/dashboard.erb +4 -0
  107. data/test/rails5.0/app/concepts/artist/view/show.erb +1 -0
  108. data/test/rails5.0/app/concepts/params/operation/with_args.rb +5 -0
  109. data/test/rails5.0/app/concepts/song/contract/form.rb +6 -0
  110. data/test/rails5.0/app/concepts/song/operation/create.rb +15 -0
  111. data/test/rails5.0/app/concepts/song/operation/show.rb +3 -0
  112. data/test/rails5.0/app/concepts/song/operation/update.rb +15 -0
  113. data/test/rails5.0/app/controllers/application_controller.rb +46 -0
  114. data/test/rails5.0/app/controllers/args_controller.rb +5 -0
  115. data/test/rails5.0/app/controllers/artists_controller.rb +24 -0
  116. data/test/rails5.0/app/controllers/params_controller.rb +11 -0
  117. data/test/rails5.0/app/controllers/songs_controller.rb +35 -0
  118. data/test/rails5.0/app/models/artist.rb +2 -0
  119. data/test/rails5.0/app/models/song.rb +2 -0
  120. data/test/rails5.0/app/views/args/with_args.html.erb +1 -0
  121. data/test/rails5.0/app/views/layouts/application.html.erb +2 -0
  122. data/test/rails5.0/app/views/songs/edit.html.erb +4 -0
  123. data/test/rails5.0/app/views/songs/new.html.erb +5 -0
  124. data/test/rails5.0/app/views/songs/new_with_result.html.erb +3 -0
  125. data/test/rails5.0/app/views/songs/show.html.erb +3 -0
  126. data/test/rails5.0/bin/bundle +3 -0
  127. data/test/rails5.0/bin/rails +4 -0
  128. data/test/rails5.0/bin/rake +4 -0
  129. data/test/rails5.0/bin/setup +29 -0
  130. data/test/rails5.0/config.ru +4 -0
  131. data/test/rails5.0/config/application.rb +25 -0
  132. data/test/rails5.0/config/boot.rb +3 -0
  133. data/test/rails5.0/config/database.yml +21 -0
  134. data/test/rails5.0/config/environment.rb +5 -0
  135. data/test/rails5.0/config/environments/development.rb +41 -0
  136. data/test/rails5.0/config/environments/test.rb +42 -0
  137. data/test/rails5.0/config/initializers/assets.rb +11 -0
  138. data/test/rails5.0/config/initializers/backtrace_silencers.rb +7 -0
  139. data/test/rails5.0/config/initializers/cookies_serializer.rb +3 -0
  140. data/test/rails5.0/config/initializers/filter_parameter_logging.rb +4 -0
  141. data/test/rails5.0/config/initializers/inflections.rb +16 -0
  142. data/test/rails5.0/config/initializers/mime_types.rb +4 -0
  143. data/test/rails5.0/config/initializers/session_store.rb +3 -0
  144. data/test/rails5.0/config/initializers/wrap_parameters.rb +14 -0
  145. data/test/rails5.0/config/locales/en.yml +23 -0
  146. data/test/rails5.0/config/routes.rb +15 -0
  147. data/test/rails5.0/config/secrets.yml +17 -0
  148. data/test/rails5.0/db/migrate/20161122145124_create_songs.rb +8 -0
  149. data/test/rails5.0/db/schema.rb +19 -0
  150. data/test/rails5.0/log/.keep +0 -0
  151. data/test/rails5.0/test/concepts/song/operation/create_test.rb +11 -0
  152. data/test/rails5.0/test/concepts/song/operation/update_test.rb +34 -0
  153. data/test/rails5.0/test/integration/.keep +0 -0
  154. data/test/rails5.0/test/integration/args_controller_test.rb +8 -0
  155. data/test/rails5.0/test/integration/artists_controller_test.rb +28 -0
  156. data/test/rails5.0/test/integration/params_controller_test.rb +8 -0
  157. data/test/rails5.0/test/integration/songs_controller_test.rb +40 -0
  158. data/test/rails5.0/test/test_helper.rb +7 -0
  159. data/test/test_helper.rb +5 -0
  160. data/trailblazer-future.gemspec +25 -0
  161. metadata +246 -0
@@ -0,0 +1,32 @@
1
+ # DISCUSS: move to trailblazer-activity-test ?
2
+
3
+ # Helpers to quickly create steps and tasks.
4
+ module Trailblazer::V2_1::Activity::Testing
5
+ # Creates a module with one step method for each name.
6
+ #
7
+ # @example
8
+ # extend T.def_steps(:create, :save)
9
+ def self.def_steps(*names)
10
+ Module.new do
11
+ names.each do |name|
12
+ define_method(name) do | ctx, ** |
13
+ ctx[:seq] << name
14
+ ctx.key?(name) ? ctx[name] : true
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # Creates a method instance with a task interface.
21
+ #
22
+ # @example
23
+ # task task: T.def_task(:create)
24
+ def self.def_task(name)
25
+ Module.new do
26
+ define_singleton_method(name) do | (ctx, flow_options), ** |
27
+ ctx[:seq] << name
28
+ return Trailblazer::V2_1::Activity::Right, [ctx, flow_options]
29
+ end
30
+ end.method(name)
31
+ end
32
+ end
@@ -0,0 +1,97 @@
1
+ module Trailblazer::V2_1
2
+ class Activity < Module # Trace#call will call the activities and trace what steps are called, options passed,
3
+ # and the order and nesting.
4
+ #
5
+ # stack, _ = Trailblazer::V2_1::Activity::Trace.(activity, activity[:Start], { id: 1 })
6
+ # puts Trailblazer::V2_1::Activity::Present.tree(stack) # renders the trail.
7
+ #
8
+ # Hooks into the taskWrap.
9
+ module Trace
10
+ class << self
11
+ # {:argumenter} API
12
+ # FIXME: needs Introspect.arguments_for_call
13
+ # FIXME: needs TaskWrap.arguments_for_call
14
+ def arguments_for_call(activity, (options, flow_options), **circuit_options)
15
+ tracing_flow_options = {
16
+ stack: Trace::Stack.new,
17
+ }
18
+
19
+ tracing_circuit_options = {
20
+ wrap_runtime: ::Hash.new(Trace.wirings), # FIXME: this still overrides existing :wrap_runtime.
21
+ }
22
+
23
+ return activity, [ options, tracing_flow_options.merge(flow_options) ], circuit_options.merge(tracing_circuit_options)
24
+ end
25
+
26
+ def call(activity, (options, flow_options), circuit_options={})
27
+ activity, (options, flow_options), circuit_options = Trace.arguments_for_call( activity, [options, flow_options], circuit_options ) # only run once for the entire circuit!
28
+
29
+ last_signal, (options, flow_options) =
30
+ Activity::TaskWrap.invoke(activity, [options, flow_options], circuit_options)
31
+
32
+ return flow_options[:stack], last_signal, [options, flow_options]
33
+ end
34
+
35
+ alias_method :invoke, :call
36
+
37
+ # Insertions for the trace tasks that capture the arguments just before calling the task,
38
+ # and before the TaskWrap is finished.
39
+ #
40
+ # Note that the TaskWrap steps are implemented in Activity::TaskWrap::Trace.
41
+ #
42
+ # @private
43
+ def wirings
44
+ Module.new do
45
+ extend Activity::Path::Plan()
46
+
47
+ task TaskWrap::Trace.method(:capture_args), id: "task_wrap.capture_args", before: "task_wrap.call_task"
48
+ task TaskWrap::Trace.method(:capture_return), id: "task_wrap.capture_return", before: "End.success", group: :end
49
+ end
50
+ end
51
+ end
52
+
53
+ Entity = Struct.new(:task, :activity, :data)
54
+ class Entity::Input < Entity
55
+ end
56
+
57
+ class Entity::Output < Entity
58
+ end
59
+
60
+ class Task < Array
61
+ def inspect
62
+ %{<Task>#{super}}
63
+ end
64
+ end
65
+
66
+ # Mutable/stateful per design. We want a (global) stack!
67
+ class Stack
68
+ def initialize
69
+ @nested = []
70
+ @stack = [ @nested ]
71
+ end
72
+
73
+ def indent!
74
+ current << indented = Task.new
75
+ @stack << indented
76
+ end
77
+
78
+ def unindent!
79
+ @stack.pop
80
+ end
81
+
82
+ def <<(args)
83
+ current << args
84
+ end
85
+
86
+ def to_a
87
+ @nested
88
+ end
89
+
90
+ private
91
+ def current
92
+ @stack.last
93
+ end
94
+ end # Stack
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,71 @@
1
+ module Trailblazer::V2_1
2
+ # Running a Circuit instance will run all tasks sequentially depending on the former's result.
3
+ # Each task is called and retrieves the former task's return values.
4
+ #
5
+ # Note: Please use #Activity as a public circuit builder.
6
+ #
7
+ # @param map [Hash] Defines the wiring.
8
+ # @param stop_events [Array] Tasks that stop execution of the circuit.
9
+ #
10
+ # result = circuit.(start_at, *args)
11
+ #
12
+ # @see Activity
13
+ # @api semi-private
14
+ #
15
+ # This is the "pipeline operator"'s implementation.
16
+ class Circuit
17
+ def initialize(map, stop_events, start_task:, name: nil)
18
+ @map = map
19
+ @stop_events = stop_events
20
+ @name = name
21
+ @start_task = start_task
22
+ end
23
+
24
+ # @param args [Array] all arguments to be passed to the task's `call`
25
+ # @param task [callable] task to call
26
+ Run = ->(task, args, **circuit_options) { task.(args, **circuit_options) }
27
+
28
+ # Runs the circuit until we hit a stop event.
29
+ #
30
+ # This method throws exceptions when the returned value of a task doesn't match
31
+ # any wiring.
32
+ #
33
+ # @param task An event or task of this circuit from where to start
34
+ # @param options anything you want to pass to the first task
35
+ # @param flow_options Library-specific flow control data
36
+ # @return [last_signal, options, flow_options, *args]
37
+ #
38
+ # NOTE: returned circuit_options are discarded when calling the runner.
39
+ def call(args, start_task: @start_task, runner: Run, **circuit_options)
40
+ circuit_options = circuit_options.merge( runner: runner ).freeze # TODO: set the :runner option via arguments_for_call to save the merge?
41
+ task = start_task
42
+
43
+ loop do
44
+ last_signal, args, _discarded_circuit_options = runner.(
45
+ task,
46
+ args,
47
+ circuit_options
48
+ )
49
+
50
+ # Stop execution of the circuit when we hit a stop event (< End). This could be an task's End or Suspend.
51
+ return [ last_signal, args ] if @stop_events.include?(task) # DISCUSS: return circuit_options here?
52
+
53
+ task = next_for(task, last_signal) or raise IllegalSignalError.new("<#{@name}>[#{task}][ #{last_signal.inspect} ]")
54
+ end
55
+ end
56
+
57
+ # Returns the circuit's components.
58
+ def to_h
59
+ { map: @map, end_events: @stop_events, start_task: @start_task }
60
+ end
61
+
62
+ private
63
+ def next_for(last_task, signal)
64
+ outputs = @map[last_task]
65
+ outputs[signal]
66
+ end
67
+
68
+ class IllegalSignalError < RuntimeError
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,45 @@
1
+ # @private
2
+ class Trailblazer::V2_1::Context::ContainerChain # used to be called Resolver.
3
+ # Keeps a list of containers. When looking up a key/value, containers are traversed in
4
+ # the order they were added until key is found.
5
+ #
6
+ # Required Container interface: `#key?`, `#[]`.
7
+ #
8
+ # @note ContainerChain is an immutable data structure, it does not support writing.
9
+ # @param containers Array of <Container> objects (splatted)
10
+ def initialize(containers, to_hash: nil)
11
+ @containers = containers
12
+ @to_hash = to_hash
13
+ end
14
+
15
+ # @param name Symbol or String to lookup a value stored in one of the containers.
16
+ def [](name)
17
+ self.class.find(@containers, name)
18
+ end
19
+
20
+ # @private
21
+ def key?(name)
22
+ @containers.find { |container| container.key?(name) }
23
+ end
24
+
25
+ def self.find(containers, name)
26
+ containers.find { |container| container.key?(name) && (return container[name]) }
27
+ end
28
+
29
+ def keys
30
+ @containers.collect(&:keys).flatten
31
+ end
32
+
33
+ # @private
34
+ def to_hash
35
+ return @to_hash.(@containers) if @to_hash # FIXME: introduce pattern matching so we can have different "transformers" for each container type.
36
+ @containers.each_with_object({}) { |container, hash| hash.merge!(container.to_hash) }
37
+ end
38
+ end
39
+
40
+ # alternative implementation:
41
+ # containers.reverse.each do |container| @mutable_options.merge!(container) end
42
+ #
43
+ # benchmark, merging in #initialize vs. this resolver.
44
+ # merge 39.678k (± 9.1%) i/s - 198.700k in 5.056653s
45
+ # resolver 68.928k (± 6.4%) i/s - 342.836k in 5.001610s
@@ -0,0 +1,79 @@
1
+ # TODO: mark/make all but mutable_options as frozen.
2
+ # The idea of Skill is to have a generic, ordered read/write interface that
3
+ # collects mutable runtime-computed data while providing access to compile-time
4
+ # information.
5
+ # The runtime-data takes precedence over the class data.
6
+ #
7
+ # notes
8
+ # a context is a ContainerChain with two elements (when reading)
9
+ module Trailblazer::V2_1
10
+ # Holds local options (aka `mutable_options`) and "original" options from the "outer"
11
+ # activity (aka wrapped_options).
12
+
13
+ # only public creator: Build
14
+ class Context # :data object:
15
+ def initialize(wrapped_options, mutable_options)
16
+ @wrapped_options, @mutable_options = wrapped_options, mutable_options
17
+ # TODO: wrapped_options should be optimized for lookups here since it could also be a Context instance, but should be a ContainerChain.
18
+ end
19
+
20
+ def [](name)
21
+ # ContainerChain.find( [@mutable_options, @wrapped_options], name )
22
+
23
+ # in 99.9% or cases @mutable_options will be a Hash, and these are already optimized for lookups.
24
+ # it's up to the ContainerChain to optimize itself.
25
+ return @mutable_options[name] if @mutable_options.key?(name)
26
+ @wrapped_options[name]
27
+ end
28
+
29
+ # TODO: use ContainerChain.find here for a generic optimization
30
+ #
31
+ # the version here is about 4x faster for now.
32
+ def key?(name)
33
+ # ContainerChain.find( [@mutable_options, @wrapped_options], name )
34
+ @mutable_options.key?(name) || @wrapped_options.key?(name)
35
+ end
36
+
37
+ def []=(name, value)
38
+ @mutable_options[name] = value
39
+ end
40
+
41
+ # @private
42
+ #
43
+ # This method might be removed.
44
+ def merge(hash)
45
+ original, mutable_options = decompose
46
+
47
+ ctx = Trailblazer::V2_1::Context( original, mutable_options.merge(hash) )
48
+ end
49
+
50
+ # Return the Context's two components. Used when computing the new output for
51
+ # the next activity.
52
+ def decompose
53
+ [ @wrapped_options, @mutable_options ]
54
+ end
55
+
56
+ def keys
57
+ @mutable_options.keys + @wrapped_options.keys # FIXME.
58
+ end
59
+
60
+
61
+
62
+ # TODO: maybe we shouldn't allow to_hash from context?
63
+ # TODO: massive performance bottleneck. also, we could already "know" here what keys the
64
+ # transformation wants.
65
+ # FIXME: ToKeywordArguments()
66
+ def to_hash
67
+ {}.tap do |hash|
68
+ # the "key" here is to call to_hash on all containers.
69
+ [ @wrapped_options.to_hash, @mutable_options.to_hash ].each do |options|
70
+ options.each { |k, v| hash[k.to_sym] = v }
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ def self.Context(wrapped_options, mutable_options={})
77
+ Context.new(wrapped_options, mutable_options)
78
+ end
79
+ end # Trailblazer::V2_1
@@ -0,0 +1,46 @@
1
+ module Trailblazer::V2_1
2
+ module Deprecation
3
+ module Operation
4
+ # This is super hacky. Fix you call calls everywhere (shouldn't take too long) and never load this file, again.
5
+ module Call
6
+ def self.options_for_call(params, *containers)
7
+ if containers == []
8
+ if params.is_a?(Hash) # this means we assume everything is cool. Create.( {...} )
9
+
10
+ else # this means someone did Create.( #<WeirdParamsObject> )
11
+ deprecate_positional_params(params, *containers)
12
+ return { params: params }, *containers
13
+ end
14
+ else # Create.( params, "current_user" => ... )
15
+ options, containers = containers[0], (containers[1..-1] || [])
16
+ if options.is_a?(Hash) # old API
17
+ warn "[Trailblazer::V2_1] Please don't pass the `params` object as a positional argument into `Operation.()`, use the `:params` key and one hash for all: `Operation.( params: my_params, current_user: ... )` ."
18
+ return options.merge( params: params ), *containers
19
+ end
20
+ end
21
+
22
+ return params, *containers
23
+ end
24
+
25
+ def self.deprecate_positional_params(params, *containers)
26
+ warn "[Trailblazer::V2_1] Please don't pass the `params` object as a positional argument into `Operation.()`, use the `:params` key: `Operation.( params: my_params )` ."
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Trailblazer::V2_1::Operation.module_eval do
34
+ # this sucks:
35
+ def self.call(options={}, *containers)
36
+ options, *containers = Trailblazer::V2_1::Deprecation::Operation::Call.options_for_call(options, *containers)
37
+
38
+ ctx = Trailblazer::V2_1::Operation::PublicCall.options_for_public_call(options, *containers)
39
+
40
+ # call the activity.
41
+ last_signal, (options, flow_options) = __call__( [ctx, {}] ) # Railway::call # DISCUSS: this could be ::call_with_context.
42
+
43
+ # Result is successful if the activity ended with an End event derived from Railway::End::Success.
44
+ Trailblazer::V2_1::Operation::Railway::Result(last_signal, options, flow_options)
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ module Trailblazer::V2_1
2
+ module Deprecation
3
+ class ContextWithIndifferentAccess < Trailblazer::V2_1::Context
4
+ def [](key)
5
+ return super unless Trailblazer::V2_1::Operation::PublicCall.deprecatable?(key)
6
+ key, _ = Trailblazer::V2_1::Operation::PublicCall.deprecate_string(key, nil)
7
+ super(key)
8
+ end
9
+
10
+ def []=(key, value)
11
+ return super unless Trailblazer::V2_1::Operation::PublicCall.deprecatable?(key)
12
+ key, _ = Trailblazer::V2_1::Operation::PublicCall.deprecate_string(key, nil)
13
+ super(key, value)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ Trailblazer::V2_1::Operation::PublicCall.module_eval do
20
+ def self.options_for_public_call(options={}, *containers)
21
+ hash_transformer = ->(containers) { containers[0].to_hash } # FIXME: don't transform any containers into kw args.
22
+
23
+ options = deprecate_strings(options)
24
+
25
+ immutable_options = Trailblazer::V2_1::Context::ContainerChain.new( [options, *containers], to_hash: hash_transformer ) # Runtime options, immutable.
26
+
27
+ Trailblazer::V2_1::Deprecation::ContextWithIndifferentAccess.new(immutable_options, {})
28
+ end
29
+
30
+ def self.deprecatable?(key)
31
+ key.is_a?(String) && key.split(".").size == 1
32
+ end
33
+
34
+ def self.deprecate_strings(options)
35
+ ary = options.collect { |k,v| deprecatable?(k) ? deprecate_string(k, v) : [k,v] }
36
+ Hash[ary]
37
+ end
38
+
39
+ def self.deprecate_string(key, value)
40
+ warn "[Trailblazer::V2_1] Using a string key for non-namespaced keys is deprecated. Please use `:#{key}` instead of `#{key.inspect}`."
41
+ [ key.to_sym, value ]
42
+ end
43
+ end
@@ -0,0 +1,47 @@
1
+ module Trailblazer::V2_1
2
+ module DSL
3
+ # Boring DSL code that allows to set a skill class, or define it ad-hoc using a block.
4
+ # passing a constant always wipes out the existing class.
5
+ #
6
+ # Used in Contract, Representer, Callback, ..
7
+ class Build
8
+ # options[:prefix]
9
+ # options[:class]
10
+ # options[:container]
11
+
12
+ # Currently, adds .class only to classes. this could break builder instances?
13
+
14
+ def call(options, name=nil, constant=nil, dsl_block, &block)
15
+ # contract MyForm
16
+ if name.is_a?(Class)
17
+ constant = name
18
+ name = :default
19
+ end
20
+
21
+ is_instance = !(constant.kind_of?(Class) || dsl_block) # i don't like this magic too much, but since it's the only DSL method in TRB, it should be ok. # DISCUSS: options[:is_instance]
22
+
23
+ path = path_name(options[:prefix], name, is_instance ? nil : "class") # "contract.default.class"
24
+
25
+ if is_instance
26
+ skill = constant
27
+ else
28
+ extended = options[:container][path] # Operation["contract.default.class"]
29
+ extended = yield extended if extended && block_given?
30
+
31
+ # only extend an existing skill class when NO constant was passed.
32
+ constant = (extended || options[:class]) if constant.nil?# && block_given?
33
+
34
+ skill = Class.new(constant)
35
+ skill.class_eval(&dsl_block) if dsl_block
36
+ end
37
+
38
+ [path, skill]
39
+ end
40
+
41
+ private
42
+ def path_name(prefix, name, suffix)
43
+ [prefix, name, suffix].compact.join(".") # "contract.class" for default, otherwise "contract.params.class" etc.
44
+ end
45
+ end
46
+ end
47
+ end