trailblazer-endpoint 0.0.4 → 0.0.5

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +16 -0
  3. data/Appraisals +5 -0
  4. data/CHANGES.md +6 -0
  5. data/Rakefile +7 -1
  6. data/gemfiles/rails_app.gemfile +12 -0
  7. data/lib/trailblazer/endpoint.rb +20 -5
  8. data/lib/trailblazer/endpoint/adapter.rb +30 -121
  9. data/lib/trailblazer/endpoint/builder.rb +1 -1
  10. data/lib/trailblazer/endpoint/controller.rb +203 -1
  11. data/lib/trailblazer/endpoint/dsl.rb +6 -3
  12. data/lib/trailblazer/endpoint/options.rb +13 -44
  13. data/lib/trailblazer/endpoint/protocol.rb +5 -8
  14. data/lib/trailblazer/endpoint/version.rb +1 -1
  15. data/test/adapter/api_test.rb +6 -11
  16. data/test/adapter/web_test.rb +2 -5
  17. data/test/config_test.rb +25 -0
  18. data/test/docs/controller_test.rb +160 -73
  19. data/test/endpoint_test.rb +1 -1
  20. data/test/rails-app/.gitignore +8 -2
  21. data/test/rails-app/.ruby-version +1 -0
  22. data/test/rails-app/Gemfile +11 -9
  23. data/test/rails-app/Gemfile.lock +137 -121
  24. data/test/rails-app/app/concepts/app/api/v1/representer/errors.rb +16 -0
  25. data/test/rails-app/app/concepts/auth/jwt.rb +35 -0
  26. data/test/rails-app/app/concepts/auth/operation/authenticate.rb +32 -0
  27. data/test/rails-app/app/concepts/auth/operation/policy.rb +9 -0
  28. data/test/rails-app/app/concepts/song/operation/create.rb +13 -0
  29. data/test/rails-app/app/concepts/song/operation/show.rb +10 -0
  30. data/test/rails-app/app/concepts/song/representer.rb +5 -0
  31. data/test/rails-app/app/controllers/api/v1/songs_controller.rb +35 -0
  32. data/test/rails-app/app/controllers/application_controller.rb +6 -1
  33. data/test/rails-app/app/controllers/application_controller/api.rb +105 -0
  34. data/test/rails-app/app/controllers/application_controller/web.rb +30 -0
  35. data/test/rails-app/app/controllers/songs_controller.rb +15 -17
  36. data/test/rails-app/app/models/song.rb +3 -0
  37. data/test/rails-app/app/models/user.rb +5 -0
  38. data/test/rails-app/bin/bundle +114 -0
  39. data/test/rails-app/bin/rails +4 -0
  40. data/test/rails-app/bin/rake +4 -0
  41. data/test/rails-app/bin/setup +33 -0
  42. data/test/rails-app/config/application.rb +26 -3
  43. data/test/rails-app/config/credentials.yml.enc +1 -0
  44. data/test/rails-app/config/database.yml +2 -2
  45. data/test/rails-app/config/environments/development.rb +7 -17
  46. data/test/rails-app/config/environments/production.rb +28 -23
  47. data/test/rails-app/config/environments/test.rb +8 -12
  48. data/test/rails-app/config/initializers/application_controller_renderer.rb +6 -4
  49. data/test/rails-app/config/initializers/cors.rb +16 -0
  50. data/test/rails-app/config/initializers/trailblazer.rb +2 -0
  51. data/test/rails-app/config/locales/en.yml +11 -1
  52. data/test/rails-app/config/master.key +1 -0
  53. data/test/rails-app/config/routes.rb +8 -3
  54. data/test/rails-app/db/schema.rb +15 -0
  55. data/test/rails-app/test/controllers/api_songs_controller_test.rb +83 -0
  56. data/test/rails-app/test/controllers/songs_controller_test.rb +36 -144
  57. data/test/rails-app/test/test_helper.rb +7 -1
  58. data/test/test_helper.rb +0 -2
  59. data/trailblazer-endpoint.gemspec +1 -0
  60. metadata +52 -21
  61. data/test/rails-app/config/initializers/cookies_serializer.rb +0 -5
  62. data/test/rails-app/config/initializers/new_framework_defaults.rb +0 -24
  63. data/test/rails-app/config/initializers/session_store.rb +0 -3
  64. data/test/rails-app/config/secrets.yml +0 -22
  65. data/test/rails-app/test/helpers/.keep +0 -0
  66. data/test/rails-app/test/integration/.keep +0 -0
  67. data/test/rails-app/test/mailers/.keep +0 -0
  68. data/test/rails-app/vendor/assets/javascripts/.keep +0 -0
  69. data/test/rails-app/vendor/assets/stylesheets/.keep +0 -0
@@ -2,7 +2,7 @@ module Trailblazer
2
2
  class Endpoint
3
3
  module DSL
4
4
  # Run before the endpoint is invoked. This collects the blocks from the controller.
5
- class Runtime < Struct.new(:args, :success_block, :failure_block, :protocol_failure_block)
5
+ class Runtime < Struct.new(:options, :success_block, :failure_block, :protocol_failure_block)
6
6
 
7
7
  def failure(&block)
8
8
  self.failure_block = block
@@ -17,8 +17,11 @@ module Trailblazer
17
17
  end
18
18
 
19
19
  # #call
20
- def to_args
21
- return args, {success_block: success_block, failure_block: failure_block, protocol_failure_block: protocol_failure_block}
20
+ def to_args(default_block_options)
21
+ return options,
22
+ success_block: success_block || default_block_options[:success_block],
23
+ failure_block: failure_block || default_block_options[:failure_block],
24
+ protocol_failure_block: protocol_failure_block || default_block_options[:protocol_failure_block]
22
25
  end
23
26
  end
24
27
  end
@@ -1,28 +1,7 @@
1
- # TODO
1
+ # DISCUSS: the generic inheritance/Options logic might be extracted to trailblazer-config or something.
2
+ # it is completely independent and could be helpful for many other configurations.
2
3
  module Trailblazer
3
4
  class Endpoint
4
- def self.Normalizer(target:, options:)
5
- normalizer = Class.new(Trailblazer::Activity::Railway) do
6
- # inject an empty {} for all options.
7
- # options.collect do |(method_name => option)|
8
- # step Normalizer.DefaultToEmptyHash(config_name), id: :"default_#{config_name}"
9
- # end
10
- end
11
-
12
- Normalizer::State.new(normalizer, options)
13
- end
14
-
15
- def self.Normalizer___(options, base_class: Trailblazer::Activity::Path)
16
- normalizer = Class.new(base_class) do
17
- # inject an empty {} for all options.
18
- # options.collect do |(method_name => option)|
19
- # step Normalizer.DefaultToEmptyHash(config_name), id: :"default_#{config_name}"
20
- # end
21
- end
22
-
23
- Normalizer.add(normalizer, nil, options)
24
- end
25
-
26
5
  module Options
27
6
  module DSL
28
7
  def directive(directive_name, *callables, inherit: superclass)
@@ -35,6 +14,7 @@ module Trailblazer
35
14
  @normalizers[directive_name] = Trailblazer::Endpoint::Normalizer.Options(directive_name, *callables, **options) # DISCUSS: allow multiple calls?
36
15
  end
37
16
 
17
+ # Called in {Endpoint::Controller}.
38
18
  def self.extended(extended) # TODO: let's hope this is only called once per hierachy :)
39
19
  extended.instance_variable_set(:@normalizers, {})
40
20
  end
@@ -49,7 +29,7 @@ module Trailblazer
49
29
  end
50
30
 
51
31
  def options_for(directive_name, runtime_options)
52
- normalizer = @normalizers[directive_name]
32
+ normalizer = @normalizers.fetch(directive_name)
53
33
 
54
34
  ctx = Trailblazer::Context(runtime_options, {})
55
35
 
@@ -59,7 +39,15 @@ module Trailblazer
59
39
  _, options = ctx.decompose
60
40
  options
61
41
  end
62
- end
42
+
43
+
44
+ # Merge {merged} into {hash}, but only keys that exist in {hash}.
45
+ def self.merge_with(merged, hash)
46
+ keys = hash.keys
47
+ merged = keys.collect { |key| merged.key?(key) ? [key, merged[key]] : nil }.compact.to_h
48
+ hash.merge(merged)
49
+ end
50
+ end # Options
63
51
 
64
52
  module Normalizer
65
53
  def self.Options(directive_name, *callables, base_class: Trailblazer::Activity::Path)
@@ -110,16 +98,6 @@ module Trailblazer
110
98
  end
111
99
  end
112
100
 
113
- def self.CallDirectiveMethod(target, config_name)
114
- ->((ctx, flow_options), *) {
115
- config = target.send(config_name, ctx, **ctx) # e.g. ApplicationController.options_for_endpoint
116
-
117
- ctx[config_name] = ctx[config_name].merge(config)
118
-
119
- return Trailblazer::Activity::Right, [ctx, flow_options]
120
- }
121
- end
122
-
123
101
  def self.CallDirective(callable, option_name)
124
102
  ->((ctx, flow_options), *) {
125
103
  config = callable.(ctx, **ctx) # e.g. ApplicationController.options_for_endpoint
@@ -133,14 +111,5 @@ module Trailblazer
133
111
  }
134
112
  end
135
113
  end # Normalizer
136
-
137
- module Controller
138
- def self.extended(extended)
139
- extended.extend Trailblazer::Endpoint::Options::DSL
140
- extended.extend Trailblazer::Endpoint::Options::DSL::Inherit
141
- extended.extend Trailblazer::Endpoint::Options
142
- end
143
- end # Controller
144
-
145
114
  end
146
115
  end
@@ -1,3 +1,5 @@
1
+ require "trailblazer/activity/dsl/linear"
2
+
1
3
  module Trailblazer
2
4
  class Endpoint
3
5
  # The {Protocol} implements auth*, and calls the domain OP/WF.
@@ -16,13 +18,8 @@ module Trailblazer
16
18
  class Noop < Trailblazer::Activity::Railway
17
19
  end
18
20
 
19
- class Failure < Trailblazer::Activity::End # DISCUSS: move to Act::Railway?
20
- # class Authentication < Failure
21
- # end
22
- end
23
-
24
21
  def self._Path(semantic:, &block) # DISCUSS: the problem with Path currently is https://github.com/trailblazer/trailblazer-activity-dsl-linear/issues/27
25
- Path(track_color: semantic, end_id: "End.#{semantic}", end_task: Failure.new(semantic: semantic), &block)
22
+ Path(track_color: semantic, end_id: "End.#{semantic}", end_task: Activity::End.new(semantic: semantic), &block)
26
23
  end
27
24
 
28
25
  step :authenticate, Output(:failure) => _Path(semantic: :not_authenticated) do
@@ -43,8 +40,8 @@ module Trailblazer
43
40
  # termini for the Adapter this is the only way to get it working right now.
44
41
  # FIXME: is this really the only way to add an {End} to all this?
45
42
  @state.update_sequence do |sequence:, **|
46
- sequence = Activity::Path::DSL.append_end(sequence, task: Failure.new(semantic: :not_found), magnetic_to: :not_found, id: "End.not_found")
47
- sequence = Activity::Path::DSL.append_end(sequence, task: Failure.new(semantic: :invalid_data), magnetic_to: :invalid_data, id: "End.invalid_data")
43
+ sequence = Activity::Path::DSL.append_end(sequence, task: Activity::End.new(semantic: :not_found), magnetic_to: :not_found, id: "End.not_found")
44
+ sequence = Activity::Path::DSL.append_end(sequence, task: Activity::End.new(semantic: :invalid_data), magnetic_to: :invalid_data, id: "End.invalid_data")
48
45
 
49
46
  recompile_activity!(sequence)
50
47
 
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Endpoint
3
- VERSION = "0.0.4"
3
+ VERSION = "0.0.5"
4
4
  end
5
5
  end
@@ -12,14 +12,13 @@ class AdapterAPITest < Minitest::Spec
12
12
  protocol: protocol, # do we cover all usual routes?
13
13
  adapter: Trailblazer::Endpoint::Adapter::API,
14
14
  scope_domain_ctx: false,
15
- ) do
16
-
17
-
18
- {Output(:not_found) => Track(:not_found)}
19
- end
15
+ protocol_block: -> { {Output(:not_found) => Track(:not_found)} }
16
+ )
20
17
 
21
18
  # success
22
19
  assert_route endpoint, {}, :authenticate, :policy, :model, :validate, :success, status: 200
20
+ # created, when passing 201
21
+ assert_route endpoint, {success_status: 201}, :authenticate, :policy, :model, :validate, :success, status: 201
23
22
  # authentication error
24
23
  assert_route endpoint, {authenticate: false}, :authenticate, :fail_fast, status: 401 # fail_fast == protocol error
25
24
  # policy error
@@ -47,12 +46,8 @@ class AdapterAPITest < Minitest::Spec
47
46
  protocol: protocol, # do we cover all usual routes?
48
47
  adapter: adapter,
49
48
  scope_domain_ctx: false,
50
-
51
- ) do
52
-
53
-
54
- {Output(:not_found) => Track(:not_found)}
55
- end
49
+ protocol_block: -> { {Output(:not_found) => Track(:not_found)} }
50
+ )
56
51
 
57
52
  class TestErrors < Struct.new(:message)
58
53
  def ==(b)
@@ -19,11 +19,8 @@ class AdapterWebTest < Minitest::Spec
19
19
  protocol: protocol, # do we cover all usual routes?
20
20
  adapter: Trailblazer::Endpoint::Adapter::Web,
21
21
  scope_domain_ctx: false,
22
- ) do
23
-
24
-
25
- {Output(:not_found) => Track(:not_found)}
26
- end
22
+ protocol_block: -> { {Output(:not_found) => Track(:not_found)} }
23
+ )
27
24
 
28
25
  # success
29
26
  assert_route(endpoint, {}, :authenticate, :policy, :model, :validate, :success)
@@ -17,12 +17,22 @@ class ConfigTest < Minitest::Spec
17
17
  ApeBabeController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true}}
18
18
  ApeBabeController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:current_user=>\"Yo\"}}
19
19
 
20
+ # 4t-h level, also inherit everything from 2-nd level
21
+ ApeBabeKidController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true}}
22
+ ApeBabeKidController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:current_user=>\"Yo\"}}
23
+
20
24
  BoringController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true, :xml=>"<XML"}}
21
25
  BoringController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:policy=>\"Ehm\"}}
22
26
 
23
27
  OverridingController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:redis=>\"Arrr\"}}
24
28
  end
25
29
 
30
+ it "raises helpful exception with unknown directive" do
31
+ assert_raises KeyError do
32
+ ApplicationController.options_for(:unknown_options, {})
33
+ end
34
+ end
35
+
26
36
  class ApplicationController
27
37
  def self.options_for_endpoint(ctx, **)
28
38
  {
@@ -58,6 +68,10 @@ class ConfigTest < Minitest::Spec
58
68
  # directive :options_for_domain_ctx, method(:options_for_domain_ctx)
59
69
  end
60
70
 
71
+ class ApeBabeKidController < ApeController
72
+
73
+ end
74
+
61
75
  class BoringController < ApplicationController
62
76
  def self.options_for_domain_ctx(ctx, **) {policy: "Ehm",} end
63
77
  def self.options_for_endpoint(ctx, **) {xml: "<XML",} end
@@ -101,3 +115,14 @@ class RuntimeOptionsTest < Minitest::Spec
101
115
  ApplicationController.options_for(:options_for_endpoint, controller: {params: {id: 1}}).inspect.must_equal %{{:option=>nil, :params=>{:id=>1}, :my_params=>\"{:id=>1}\"}}
102
116
  end
103
117
  end
118
+
119
+ class OptionsTest < Minitest::Spec
120
+ # Options#merge_with
121
+ it "works with empty {merged}" do
122
+ Trailblazer::Endpoint::Options.merge_with({}, {a: 1, b: 2}).inspect.must_equal %{{:a=>1, :b=>2}}
123
+ end
124
+
125
+ it "keys in merged have precedence, but unknown {merged} keys are discarded" do
126
+ Trailblazer::Endpoint::Options.merge_with({a: 3, d: 4}, {a: 1, b: 2}).inspect.must_equal %{{:a=>3, :b=>2}}
127
+ end
128
+ end
@@ -1,11 +1,11 @@
1
1
  require "test_helper"
2
2
 
3
3
  class DocsControllerTest < Minitest::Spec
4
-
5
4
  class ApplicationController
6
- def self.options_for_endpoint(ctx, **)
5
+ def self.options_for_endpoint(ctx, controller:, **)
7
6
  {
8
7
  find_process_model: true,
8
+ **controller.instance_variable_get(:@params)[:params],
9
9
  }
10
10
  end
11
11
 
@@ -15,30 +15,52 @@ class DocsControllerTest < Minitest::Spec
15
15
  }
16
16
  end
17
17
 
18
+ def self.options_for_flow_options(ctx, **)
19
+ {
20
+ }
21
+ end
22
+
23
+ def self.options_for_block_options(ctx, controller:, **)
24
+ {
25
+ success_block: ->(ctx, seq:, **) { controller.instance_exec { render seq << :success_block } },
26
+ failure_block: ->(ctx, seq:, **) { controller.instance_exec { render seq << :failure_block } },
27
+ protocol_failure_block: ->(ctx, seq:, **) { controller.instance_exec { render seq << :protocol_failure_block } }
28
+ }
29
+ end
30
+
31
+
18
32
  extend Trailblazer::Endpoint::Controller
33
+
34
+ # include Trailblazer::Endpoint::Controller::InstanceMethods # {#endpoint_for}
35
+ include Trailblazer::Endpoint::Controller::InstanceMethods::DSL # {#endpoint}
36
+
37
+ include Trailblazer::Endpoint::Controller::Rails
38
+ include Trailblazer::Endpoint::Controller::Rails::Process
39
+
19
40
  directive :options_for_endpoint, method(:options_for_endpoint), method(:request_options)
41
+ directive :options_for_flow_options, method(:options_for_flow_options)
42
+ directive :options_for_block_options, method(:options_for_block_options)
20
43
 
21
- def process(action_name, params:)
44
+ def process(action_name, **params)
22
45
  @params = params
23
- send(action_name)
46
+ send_action(action_name)
47
+ @render
24
48
  end
25
49
 
26
50
  def render(text)
27
51
  @render = text
28
52
  end
29
- end
30
53
 
31
- class ApeController < ApplicationController
32
- def self.options_for_domain_ctx(ctx, **)
33
- {
34
- current_user: "Yo",
35
- }
54
+
55
+
56
+
57
+ Protocol = Class.new(Trailblazer::Endpoint::Protocol) do
58
+ include T.def_steps(:authenticate, :policy)
36
59
  end
37
60
 
38
- directive :options_for_domain_ctx, method(:options_for_domain_ctx)
61
+ endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web, domain_ctx_filter: Trailblazer::Endpoint.domain_ctx_filter([:current_user, :process_model]), scope_domain_ctx: true
39
62
  end
40
63
 
41
-
42
64
  class HtmlController < ApplicationController
43
65
  private def endpoint_for(*)
44
66
  protocol = Class.new(Trailblazer::Endpoint::Protocol) do
@@ -50,46 +72,31 @@ class DocsControllerTest < Minitest::Spec
50
72
  domain_activity: Minitest::Spec.new(nil).activity, # FIXME
51
73
  protocol: protocol,
52
74
  adapter: Trailblazer::Endpoint::Adapter::Web,
53
- scope_domain_ctx: false,
75
+ scope_domain_ctx: true,
54
76
 
55
77
  ) do
56
78
  {Output(:not_found) => Track(:not_found)}
57
79
  end
58
80
  end
59
81
 
60
- private def advance_endpoint(options, &block)
61
- endpoint = endpoint_for(options)
62
-
63
- ctx = Trailblazer::Endpoint.advance_from_controller(
64
-
65
- endpoint,
66
-
67
- # retrieve/compute options_for_domain_ctx and options_for_endpoint
68
-
69
- domain_ctx: {seq: seq=[], current_user: "Yo", **@params},
70
- flow_options: {},
71
-
72
- **options,
73
-
74
- seq: seq,
82
+ def self.options_for_domain_ctx(ctx, seq:, controller:, **)
83
+ {
75
84
  current_user: "Yo",
76
- **@params,
77
- )
85
+ seq: seq,
86
+ **controller.instance_variable_get(:@params)[:params],
87
+ }
78
88
  end
79
89
 
80
- private def _endpoint(action, params: {}, &block)
81
- success_block = ->(ctx, seq:, **) { render seq << :success_block }
82
- failure_block = ->(ctx, seq:, **) { render seq << :failure_block }
83
- protocol_failure_block = ->(ctx, seq:, **) { render seq << :protocol_failure_block }
90
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
84
91
 
85
- dsl = Trailblazer::Endpoint::DSL::Runtime.new({action: action, params: params}, block || success_block, failure_block, protocol_failure_block) # provides #Or etc, is returned to {Controller#call}
92
+ private def _endpoint(action, seq: [], &block)
93
+ endpoint(action, seq: seq, &block)
86
94
  end
87
95
 
88
96
  # all standard routes are user-defined
89
97
  def view
90
98
  _endpoint "view?" do |ctx, seq:, **|
91
99
  render "success" + ctx[:current_user] + seq.inspect
92
-
93
100
  end.failure do |ctx, seq:, **|
94
101
  render "failure" + ctx[:current_user] + seq.inspect
95
102
 
@@ -114,16 +121,6 @@ class DocsControllerTest < Minitest::Spec
114
121
  end
115
122
  end
116
123
 
117
- def process(*)
118
- dsl = super
119
-
120
- options, block_options = dsl.to_args
121
-
122
- advance_endpoint(**options, **block_options)
123
-
124
- @render
125
- end
126
-
127
124
  end # HtmlController
128
125
 
129
126
  it "what" do
@@ -171,57 +168,147 @@ class DocsControllerTest < Minitest::Spec
171
168
  # from controller-default
172
169
  controller.process(:update, params: {authenticate: false}).must_equal [:authenticate, :protocol_failure_block]
173
170
  end
174
- end
175
171
 
176
- class ControllerOptionsTest < Minitest::Spec
177
- class Controller
178
- include Trailblazer::Endpoint::Controller
179
172
 
180
- def view
181
- endpoint "view?"
173
+ # Test if {domain_ctx} is automatically wrapped via Context() so that we can use string-keys.
174
+ # TODO: test if aliases etc are properly passed.
175
+ class OptionsController < HtmlController
176
+ def self.options_for_domain_ctx(ctx, seq:, controller:, **)
177
+ {
178
+ "contract.params" => Object, # string-key should usually break if not wrapped
179
+ }
182
180
  end
183
181
 
184
- # we add {:options_for_domain_ctx} manually
185
- def download
186
- endpoint "download?", params: {id: params[:other_id]}, redis: "Redis"
182
+ def self.options_for_endpoint(ctx, controller:, **)
183
+ {
184
+ current_user: "Yogi",
185
+ process_model: Class,
186
+ }
187
187
  end
188
188
 
189
- # override some settings from {endpoint_options}:
190
- def new
191
- endpoint "new?", find_process_model: false
189
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
190
+ directive :options_for_endpoint, method(:options_for_endpoint), inherit: false
191
+
192
+ def view
193
+ _endpoint "view?" do |ctx, seq:, **|
194
+ render "success" + ctx["contract.params"].to_s + seq.inspect
195
+ end
192
196
  end
197
+ end # OptionsController
198
+
199
+ it "allows string keys in {domain_ctx} since it gets automatically Ctx()-wrapped" do
200
+ controller = OptionsController.new
201
+ controller.process(:view, params: {}).must_equal %{successObject[:authenticate, :policy, :model, :validate]}
193
202
  end
194
203
 
195
- class ControllerThatDoesntInherit
196
- include Trailblazer::Endpoint::Controller
197
204
 
198
- def options_for_domain_ctx
205
+ # copy from {endpoint_ctx} to {domain_ctx}
206
+ class DomainContextController < ApplicationController
207
+ private def _endpoint(action, seq: [], **options, &block)
208
+ endpoint(action, seq: seq, **options, &block)
209
+ end
210
+
211
+ Activity = Class.new(Trailblazer::Activity::Railway) do
212
+ step :check
213
+
214
+ def check(ctx, current_user:, seq:, process_model:, **)
215
+ seq << :check
216
+ ctx[:message] = "#{current_user} / #{process_model}"
217
+ end
218
+ end
219
+
220
+ endpoint "view?", domain_activity: Activity
221
+ endpoint "show?", domain_activity: Activity
222
+
223
+
224
+ def self.options_for_domain_ctx(ctx, seq:, controller:, **)
199
225
  {
200
- params: params
226
+ seq: seq,
201
227
  }
202
228
  end
203
229
 
204
- def options_for_endpoint
205
-
230
+ def self.options_for_endpoint(ctx, controller:, **)
231
+ {
232
+ current_user: "Yogi",
233
+ process_model: Class,
234
+ something: true,
235
+ }
206
236
  end
207
237
 
238
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
239
+ directive :options_for_endpoint, method(:options_for_endpoint), inherit: false
240
+
208
241
  def view
209
- endpoint "view?"
242
+ _endpoint "view?" do |ctx, seq:, **|
243
+ render "success" + ctx[:message].to_s + seq.inspect
244
+ end
210
245
  end
211
246
 
212
- # we add {:options_for_domain_ctx} manually
213
- def download
214
- endpoint "download?", params: {id: params[:other_id]}, redis: "Redis"
247
+ def show
248
+ # override existing domain_ctx
249
+ # make options here available in steps
250
+ _endpoint "show?", options_for_domain_ctx: {params: {id: 1}, seq: []} do |ctx, seq:, params:, **|
251
+ render "success" + ctx[:message].to_s + seq.inspect + params.inspect
252
+ end
215
253
  end
216
254
 
217
- # override some settings from {endpoint_options}:
218
- def new
219
- endpoint "new?", find_process_model: false
255
+ def create
256
+ # add endpoint_options
257
+ _endpoint "show?", policy: false do |ctx, seq:, params:, **|
258
+ render "success" + ctx[:message].to_s + seq.inspect + params.inspect
259
+ end
220
260
  end
261
+
262
+ # todo: test overriding endp options
263
+ # _endpoint "show?", params: {id: 1}, process_model: "it's me!" do |ctx, seq:, params:, process_model:, **|
264
+ end # DomainContextController
265
+
266
+ it "{:current_user} and {:process_model} are made available in {domain_ctx}" do
267
+ controller = DomainContextController.new
268
+ controller.process(:view, params: {}).must_equal %{successYogi / Class[:authenticate, :policy, :check]}
269
+ end
270
+
271
+ it "{:seq} is overridden, {:params} made available, in {domain_ctx}" do
272
+ controller = DomainContextController.new
273
+ # note that {seq} is not shared anymore
274
+ controller.process(:show, params: {}).must_equal %{successYogi / Class[:check]{:id=>1}}
221
275
  end
222
276
 
223
- it "allows to get options without a bloody controller" do
224
- MemoController.bla(params: params)
277
+ it "allows passing {endpoint_options} directly" do
278
+ controller = DomainContextController.new
279
+ controller.process(:create, params: {}).must_equal [:authenticate, :policy, :protocol_failure_block]
280
+ end
281
+
282
+
283
+ # Test without DSL
284
+ class BasicController
285
+ extend Trailblazer::Endpoint::Controller
286
+
287
+ directive :options_for_block_options, Trailblazer::Endpoint::Controller.method(:options_for_block_options)
288
+
289
+ def endpoint(name, &block)
290
+ action_options = {seq: []}
291
+
292
+ Trailblazer::Endpoint::Controller.advance_endpoint_for_controller(endpoint: endpoint_for(name), block_options: self.class.options_for(:options_for_block_options, {controller: self}), config_source: self.class, **action_options)
293
+ end
294
+
295
+ def head(status)
296
+ @status = status
297
+ end
298
+ end
299
+
300
+ class RodaController < BasicController
301
+ endpoint("show?", protocol: ApplicationController::Protocol, adapter: Trailblazer::Endpoint::Adapter::Web, domain_activity: Class.new(Trailblazer::Activity::Railway) { def save(*); true; end; step :save })
302
+
303
+ def show
304
+ endpoint "show?"
305
+ @status
306
+ end
307
+ end
308
+
309
+ it "what" do
310
+ RodaController.new.show.must_equal 200
225
311
  end
226
312
  end
227
313
 
314
+