trailblazer-endpoint 0.0.2 → 0.0.7

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +16 -0
  3. data/Appraisals +5 -0
  4. data/CHANGES.md +28 -0
  5. data/README.md +40 -5
  6. data/Rakefile +7 -1
  7. data/gemfiles/rails_app.gemfile +12 -0
  8. data/lib/trailblazer/endpoint.rb +55 -16
  9. data/lib/trailblazer/endpoint/adapter.rb +30 -121
  10. data/lib/trailblazer/endpoint/builder.rb +1 -1
  11. data/lib/trailblazer/endpoint/controller.rb +223 -0
  12. data/lib/trailblazer/endpoint/dsl.rb +31 -0
  13. data/lib/trailblazer/endpoint/options.rb +92 -0
  14. data/lib/trailblazer/endpoint/protocol.rb +7 -11
  15. data/lib/trailblazer/endpoint/protocol/cipher.rb +27 -0
  16. data/lib/trailblazer/endpoint/protocol/controller.rb +93 -0
  17. data/lib/trailblazer/endpoint/protocol/find_process_model.rb +15 -0
  18. data/lib/trailblazer/endpoint/version.rb +1 -1
  19. data/test/adapter/api_test.rb +6 -11
  20. data/test/adapter/web_test.rb +2 -5
  21. data/test/config_test.rb +128 -0
  22. data/test/docs/controller_test.rb +340 -58
  23. data/test/endpoint_test.rb +1 -1
  24. data/test/rails-app/.gitignore +8 -2
  25. data/test/rails-app/.ruby-version +1 -0
  26. data/test/rails-app/Gemfile +21 -9
  27. data/test/rails-app/Gemfile.lock +173 -118
  28. data/test/rails-app/app/concepts/app/api/v1/representer/errors.rb +16 -0
  29. data/test/rails-app/app/concepts/auth/jwt.rb +35 -0
  30. data/test/rails-app/app/concepts/auth/operation/authenticate.rb +32 -0
  31. data/test/rails-app/app/concepts/auth/operation/policy.rb +9 -0
  32. data/test/rails-app/app/concepts/song/cell/create.rb +4 -0
  33. data/test/rails-app/app/concepts/song/cell/new.rb +4 -0
  34. data/test/rails-app/app/concepts/song/operation/create.rb +17 -0
  35. data/test/rails-app/app/concepts/song/operation/show.rb +10 -0
  36. data/test/rails-app/app/concepts/song/representer.rb +5 -0
  37. data/test/rails-app/app/concepts/song/view/create.erb +1 -0
  38. data/test/rails-app/app/concepts/song/view/new.erb +1 -0
  39. data/test/rails-app/app/controllers/api/v1/songs_controller.rb +41 -0
  40. data/test/rails-app/app/controllers/application_controller.rb +8 -1
  41. data/test/rails-app/app/controllers/application_controller/api.rb +107 -0
  42. data/test/rails-app/app/controllers/application_controller/web.rb +45 -0
  43. data/test/rails-app/app/controllers/auth_controller.rb +44 -0
  44. data/test/rails-app/app/controllers/home_controller.rb +5 -0
  45. data/test/rails-app/app/controllers/songs_controller.rb +245 -13
  46. data/test/rails-app/app/models/song.rb +6 -0
  47. data/test/rails-app/app/models/user.rb +7 -0
  48. data/test/rails-app/bin/bundle +114 -0
  49. data/test/rails-app/bin/rails +4 -0
  50. data/test/rails-app/bin/rake +4 -0
  51. data/test/rails-app/bin/setup +33 -0
  52. data/test/rails-app/config/application.rb +26 -3
  53. data/test/rails-app/config/credentials.yml.enc +1 -0
  54. data/test/rails-app/config/database.yml +2 -2
  55. data/test/rails-app/config/environments/development.rb +7 -17
  56. data/test/rails-app/config/environments/production.rb +28 -23
  57. data/test/rails-app/config/environments/test.rb +10 -12
  58. data/test/rails-app/config/initializers/application_controller_renderer.rb +6 -4
  59. data/test/rails-app/config/initializers/cors.rb +16 -0
  60. data/test/rails-app/config/initializers/trailblazer.rb +2 -0
  61. data/test/rails-app/config/locales/en.yml +11 -1
  62. data/test/rails-app/config/master.key +1 -0
  63. data/test/rails-app/config/routes.rb +26 -4
  64. data/test/rails-app/db/schema.rb +15 -0
  65. data/test/rails-app/test/controllers/api_songs_controller_test.rb +87 -0
  66. data/test/rails-app/test/controllers/songs_controller_test.rb +146 -144
  67. data/test/rails-app/test/test_helper.rb +7 -1
  68. data/test/test_helper.rb +0 -2
  69. data/trailblazer-endpoint.gemspec +2 -1
  70. metadata +73 -22
  71. data/test/rails-app/config/initializers/cookies_serializer.rb +0 -5
  72. data/test/rails-app/config/initializers/new_framework_defaults.rb +0 -24
  73. data/test/rails-app/config/initializers/session_store.rb +0 -3
  74. data/test/rails-app/config/secrets.yml +0 -22
  75. data/test/rails-app/test/helpers/.keep +0 -0
  76. data/test/rails-app/test/integration/.keep +0 -0
  77. data/test/rails-app/test/mailers/.keep +0 -0
  78. data/test/rails-app/vendor/assets/javascripts/.keep +0 -0
  79. data/test/rails-app/vendor/assets/stylesheets/.keep +0 -0
@@ -0,0 +1,15 @@
1
+ class Trailblazer::Endpoint::Protocol
2
+ class FindProcessModel < Trailblazer::Activity::Railway
3
+ # step :find_process_model?, Output(:failure) => Id("End.success")
4
+ step :find_process_model#, Output(:failure) => End(:not_found) # DISCUSS: currently, {End.failure} implies {not_found}.
5
+
6
+ # DISCUSS: should the implementation remain in {Activity}?
7
+ # def find_process_model?(ctx, find_process_model:, **)
8
+ # find_process_model
9
+ # end
10
+
11
+ def find_process_model(ctx, process_model_class:, process_model_id:, **)
12
+ ctx[:process_model] = process_model_class.find_by(id: process_model_id)
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Endpoint
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.7"
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)
@@ -0,0 +1,128 @@
1
+ require "test_helper"
2
+
3
+ require "trailblazer/endpoint/options"
4
+
5
+ class ConfigTest < Minitest::Spec
6
+ Controller = Struct.new(:params)
7
+
8
+ it "what" do
9
+ ApplicationController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true}}
10
+
11
+ # inherits endpoint options from ApplicationController
12
+ ApeController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true}}
13
+ # defines its own domain options, none in ApplicationController
14
+ ApeController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:current_user=>\"Yo\"}}
15
+
16
+ # 3-rd level, inherit everything from 2-nd level
17
+ ApeBabeController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true}}
18
+ ApeBabeController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:current_user=>\"Yo\"}}
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
+
24
+ BoringController.options_for(:options_for_endpoint, {}).inspect.must_equal %{{:find_process_model=>true, :request=>true, :xml=>"<XML"}}
25
+ BoringController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:policy=>\"Ehm\"}}
26
+
27
+ OverridingController.options_for(:options_for_domain_ctx, {}).inspect.must_equal %{{:redis=>\"Arrr\"}}
28
+ end
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
+
36
+ class ApplicationController
37
+ def self.options_for_endpoint(ctx, **)
38
+ {
39
+ find_process_model: true,
40
+ }
41
+ end
42
+
43
+ def self.request_options(ctx, **)
44
+ {
45
+ request: true,
46
+ }
47
+ end
48
+
49
+ extend Trailblazer::Endpoint::Controller
50
+ directive :options_for_endpoint, method(:options_for_endpoint), method(:request_options)
51
+ end
52
+
53
+ class ApeController < ApplicationController
54
+ def self.options_for_domain_ctx(ctx, **)
55
+ {
56
+ current_user: "Yo",
57
+ }
58
+ end
59
+
60
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
61
+ end
62
+
63
+ class ApeBabeController < ApeController
64
+ # def self.options_for_domain_ctx(ctx, **)
65
+ # {policy: "Ehm"}
66
+ # end
67
+
68
+ # directive :options_for_domain_ctx, method(:options_for_domain_ctx)
69
+ end
70
+
71
+ class ApeBabeKidController < ApeController
72
+
73
+ end
74
+
75
+ class BoringController < ApplicationController
76
+ def self.options_for_domain_ctx(ctx, **) {policy: "Ehm",} end
77
+ def self.options_for_endpoint(ctx, **) {xml: "<XML",} end
78
+
79
+ directive :options_for_endpoint, method(:options_for_endpoint) #, inherit: ApplicationController
80
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
81
+ end
82
+
83
+ class OverridingController < BoringController
84
+ def self.options_for_domain_ctx(ctx, **)
85
+ {
86
+ redis: "Arrr",
87
+ }
88
+ end
89
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx), inherit: false
90
+ end
91
+ end
92
+
93
+ class RuntimeOptionsTest < Minitest::Spec
94
+ class ApplicationController
95
+ def self.options_for_endpoint(ctx, controller:, **)
96
+ {
97
+ option: true,
98
+ params: controller[:params],
99
+ }
100
+ end
101
+
102
+ # You can access variables set prior to this options directive.
103
+ def self.request_options(ctx, controller:, params:, **)
104
+ {
105
+ my_params: params.inspect,
106
+ option: nil,
107
+ }
108
+ end
109
+
110
+ extend Trailblazer::Endpoint::Controller
111
+ directive :options_for_endpoint, method(:options_for_endpoint), method(:request_options)
112
+ end
113
+
114
+ it do
115
+ ApplicationController.options_for(:options_for_endpoint, controller: {params: {id: 1}}).inspect.must_equal %{{:option=>nil, :params=>{:id=>1}, :my_params=>\"{:id=>1}\"}}
116
+ end
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,92 +1,374 @@
1
1
  require "test_helper"
2
2
 
3
3
  class DocsControllerTest < Minitest::Spec
4
- it "what" do
5
- endpoint "view?" do |ctx|
6
- # 200, success
7
- return
4
+ class ApplicationController
5
+ def self.options_for_endpoint(ctx, controller:, **)
6
+ {
7
+ find_process_model: true,
8
+ **controller.instance_variable_get(:@params)[:params],
9
+ }
10
+ end
11
+
12
+ def self.request_options(ctx, **)
13
+ {
14
+ request: true,
15
+ }
16
+ end
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
+ }
8
29
  end
9
30
 
10
- # 422
11
- # but also 404 etc
31
+
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
+
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)
43
+
44
+ def process(action_name, **params)
45
+ @params = params
46
+ send_action(action_name)
47
+ @render
48
+ end
49
+
50
+ def render(text)
51
+ @render = text
52
+ end
53
+
54
+
55
+
56
+
57
+ Protocol = Class.new(Trailblazer::Endpoint::Protocol) do
58
+ include T.def_steps(:authenticate, :policy)
59
+ end
60
+
61
+ endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web,
62
+ scope_domain_ctx: true
12
63
  end
13
64
 
65
+ class HtmlController < ApplicationController
66
+ private def endpoint_for(*)
67
+ protocol = Class.new(Trailblazer::Endpoint::Protocol) do
68
+ include T.def_steps(:authenticate, :policy)
69
+ end
70
+
71
+ endpoint =
72
+ Trailblazer::Endpoint.build(
73
+ domain_activity: Minitest::Spec.new(nil).activity, # FIXME
74
+ protocol: protocol,
75
+ adapter: Trailblazer::Endpoint::Adapter::Web,
76
+ scope_domain_ctx: true,
77
+
78
+ ) do
79
+ {Output(:not_found) => Track(:not_found)}
80
+ end
81
+ end
82
+
83
+ def self.options_for_domain_ctx(ctx, seq:, controller:, **)
84
+ {
85
+ current_user: "Yo",
86
+ seq: seq,
87
+ **controller.instance_variable_get(:@params)[:params],
88
+ }
89
+ end
90
+
91
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
14
92
 
15
- class Controller
16
- def initialize(endpoint, activity)
17
- @___activity = activity
18
- @endpoint = endpoint
19
- @seq = []
93
+ private def _endpoint(action, seq: [], &block)
94
+ endpoint(action, seq: seq, &block)
20
95
  end
21
96
 
22
- def view(params)
23
- endpoint "view?", params do |ctx|
24
- @seq << :success
25
- # 200, success
26
- return
97
+ # all standard routes are user-defined
98
+ def view
99
+ _endpoint "view?" do |ctx, seq:, **|
100
+ render "success" + ctx[:current_user] + seq.inspect
101
+ end.failure do |ctx, seq:, **|
102
+ render "failure" + ctx[:current_user] + seq.inspect
103
+
104
+ end.protocol_failure do |ctx, seq:, **|
105
+ render "protocol_failure" + ctx[:current_user] + seq.inspect
27
106
  end
107
+ end
28
108
 
29
- @seq << :failure
30
- # 422
31
- # but also 404 etc
109
+ # standard use-case: only success
110
+ def show
111
+ _endpoint "view?" do |ctx, seq:, **|
112
+ render "success" + ctx[:current_user] + seq.inspect
113
+ end
32
114
  end
33
115
 
34
- def call(action, **params)
35
- send(action, **params)
36
- @seq
116
+ # standard use case: {success} and {failure}
117
+ def update
118
+ _endpoint "view?" do |ctx, seq:, **|
119
+ render "success" + ctx[:current_user] + seq.inspect
120
+ end.Or do |ctx, seq:, **|
121
+ render "Fail!" + ctx[:current_user] + seq.inspect
122
+ end
37
123
  end
38
124
 
39
- private def endpoint(action, params, &block)
40
- ctx = Trailblazer::Endpoint.advance_from_controller(@endpoint,
41
- event_name: "",
42
- success_block: block,
43
- failure_block: ->(*) { return },
44
- protocol_failure_block: ->(*) { @seq << 401 and return },
125
+ end # HtmlController
126
+
127
+ it "what" do
128
+ # success
129
+ controller = HtmlController.new
130
+ controller.process(:view, params: {}).must_equal %{successYo[:authenticate, :policy, :model, :validate]}
45
131
 
46
- collaboration: @___activity,
47
- domain_ctx: {},
48
- success_id: "fixme",
49
- flow_options: {},
132
+ # failure
133
+ controller = HtmlController.new
134
+ controller.process(:view, params: {validate: false}).must_equal %{failureYo[:authenticate, :policy, :model, :validate]}
50
135
 
51
- **params,
136
+ # protocol_failure
137
+ controller = HtmlController.new
138
+ controller.process(:view, params: {authenticate: false}).must_equal %{protocol_failureYo[:authenticate]}
139
+ end
52
140
 
53
- # DISCUSS: do we really like that fuzzy API? if yes, why do we need {additional_endpoint_options} or whatever it's called?
54
- seq: @seq,
55
- )
141
+ it "only success_block is user-defined" do
142
+ # success
143
+ controller = HtmlController.new
144
+ controller.process(:show, params: {}).must_equal %{successYo[:authenticate, :policy, :model, :validate]}
145
+
146
+ # failure
147
+ controller = HtmlController.new
148
+ # from controller-default
149
+ controller.process(:show, params: {validate: false}).must_equal [:authenticate, :policy, :model, :validate, :failure_block]
150
+
151
+ # protocol_failure
152
+ controller = HtmlController.new
153
+ # from controller-default
154
+ controller.process(:show, params: {authenticate: false}).must_equal [:authenticate, :protocol_failure_block]
155
+ end
156
+
157
+ it "success/Or" do
158
+ # success
159
+ controller = HtmlController.new
160
+ controller.process(:update, params: {}).must_equal %{successYo[:authenticate, :policy, :model, :validate]}
161
+
162
+ # failure
163
+ controller = HtmlController.new
164
+ # from controller-default
165
+ controller.process(:update, params: {validate: false}).must_equal %{Fail!Yo[:authenticate, :policy, :model, :validate]}
166
+
167
+ # protocol_failure
168
+ controller = HtmlController.new
169
+ # from controller-default
170
+ controller.process(:update, params: {authenticate: false}).must_equal [:authenticate, :protocol_failure_block]
171
+ end
172
+
173
+
174
+ # Test if {domain_ctx} is automatically wrapped via Context() so that we can use string-keys.
175
+ # TODO: test if aliases etc are properly passed.
176
+ class OptionsController < HtmlController
177
+ def self.options_for_domain_ctx(ctx, seq:, controller:, **)
178
+ {
179
+ "contract.params" => Object, # string-key should usually break if not wrapped
180
+ }
181
+ end
182
+
183
+ def self.options_for_endpoint(ctx, controller:, **)
184
+ {
185
+ current_user: "Yogi",
186
+ process_model: Class,
187
+ }
188
+ end
189
+
190
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
191
+ directive :options_for_endpoint, method(:options_for_endpoint), inherit: false
192
+
193
+ def view
194
+ _endpoint "view?" do |ctx, seq:, **|
195
+ render "success" + ctx["contract.params"].to_s + seq.inspect
196
+ end
56
197
  end
198
+ end # OptionsController
199
+
200
+ it "allows string keys in {domain_ctx} since it gets automatically Ctx()-wrapped" do
201
+ controller = OptionsController.new
202
+ controller.process(:view, params: {}).must_equal %{successObject[:authenticate, :policy, :model, :validate]}
57
203
  end
58
204
 
59
- it "injected {return} interrupts the controller action" do
60
- protocol = Class.new(Trailblazer::Endpoint::Protocol)do
61
- include T.def_steps(:authenticate, :policy)
205
+
206
+ # copy from {endpoint_ctx} to {domain_ctx}
207
+ class DomainContextController < ApplicationController
208
+ private def _endpoint(action, seq: [], **options, &block)
209
+ endpoint(action, seq: seq, **options, &block)
62
210
  end
63
211
 
64
- endpoint =
65
- Trailblazer::Endpoint.build(
66
- domain_activity: activity,
67
- protocol: protocol,
68
- adapter: Trailblazer::Endpoint::Adapter::Web,
69
- scope_domain_ctx: false,
212
+ Activity = Class.new(Trailblazer::Activity::Railway) do
213
+ step :check
70
214
 
71
- ) do
72
- {Output(:not_found) => Track(:not_found)}
215
+ def check(ctx, current_user:, seq:, process_model:, **)
216
+ seq << :check
217
+ ctx[:message] = "#{current_user} / #{process_model}"
218
+ end
73
219
  end
74
220
 
75
- # 200
76
- seq = Controller.new(endpoint, activity).call(:view)
77
- seq.must_equal [:authenticate, :policy, :model, :validate, :success] # the {return} works.
221
+ endpoint "view?", domain_activity: Activity
222
+ endpoint "show?", domain_activity: Activity
223
+
78
224
 
79
- # 401
80
- seq = Controller.new(endpoint, activity).call(:view, authenticate: false)
81
- seq.must_equal [:authenticate, :policy, :model, :validate, :success]
225
+ def self.options_for_domain_ctx(ctx, seq:, controller:, **)
226
+ {
227
+ seq: seq,
228
+ }
229
+ end
230
+
231
+ def self.options_for_endpoint(ctx, controller:, **)
232
+ {
233
+ current_user: "Yogi",
234
+ process_model: Class,
235
+ something: true,
236
+ }
237
+ end
238
+
239
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
240
+ directive :options_for_endpoint, method(:options_for_endpoint), inherit: false
241
+
242
+ def view
243
+ _endpoint "view?" do |ctx, seq:, **|
244
+ render "success" + ctx[:message].to_s + seq.inspect
245
+ end
246
+ end
247
+
248
+ def show
249
+ # override existing domain_ctx
250
+ # make options here available in steps
251
+ _endpoint "show?", options_for_domain_ctx: {params: {id: 1}, seq: []} do |ctx, seq:, params:, **|
252
+ render "success" + ctx[:message].to_s + seq.inspect + params.inspect
253
+ end
254
+ end
255
+
256
+ def create
257
+ # add endpoint_options
258
+ _endpoint "show?", policy: false do |ctx, seq:, params:, **|
259
+ render "success" + ctx[:message].to_s + seq.inspect + params.inspect
260
+ end
261
+ end
262
+
263
+ # todo: test overriding endp options
264
+ # _endpoint "show?", params: {id: 1}, process_model: "it's me!" do |ctx, seq:, params:, process_model:, **|
265
+ end # DomainContextController
266
+
267
+ it "{:current_user} and {:process_model} are made available in {domain_ctx}" do
268
+ controller = DomainContextController.new
269
+ controller.process(:view, params: {}).must_equal %{successYogi / Class[:authenticate, :policy, :check]}
270
+ end
271
+
272
+ it "{:seq} is overridden, {:params} made available, in {domain_ctx}" do
273
+ controller = DomainContextController.new
274
+ # note that {seq} is not shared anymore
275
+ controller.process(:show, params: {}).must_equal %{successYogi / Class[:check]{:id=>1}}
276
+ end
277
+
278
+ it "allows passing {endpoint_options} directly" do
279
+ controller = DomainContextController.new
280
+ controller.process(:create, params: {}).must_equal [:authenticate, :policy, :protocol_failure_block]
281
+ end
282
+
283
+
284
+ # Test without DSL
285
+ class BasicController
286
+ extend Trailblazer::Endpoint::Controller
287
+
288
+ directive :options_for_block_options, Trailblazer::Endpoint::Controller.method(:options_for_block_options)
289
+
290
+ def endpoint(name, &block)
291
+ action_options = {seq: []}
292
+
293
+ 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)
294
+ end
295
+
296
+ def head(status)
297
+ @status = status
298
+ end
299
+ end
300
+
301
+ class RodaController < BasicController
302
+ endpoint("show?", protocol: ApplicationController::Protocol, adapter: Trailblazer::Endpoint::Adapter::Web, domain_activity: Class.new(Trailblazer::Activity::Railway) { def save(*); true; end; step :save })
303
+
304
+ def show
305
+ endpoint "show?"
306
+ @status
307
+ end
82
308
  end
83
309
 
84
310
  it "what" do
85
- endpoint "view?" do |ctx|
86
- # 200, success
87
- return
88
- end.Or() do |ctx|
89
- # Only 422
311
+ RodaController.new.show.must_equal 200
312
+ end
313
+ end
314
+
315
+ class ControllerEndpointMethodTest < Minitest::Spec
316
+ # Test {Controller::endpoint}
317
+
318
+ class Protocol < Trailblazer::Endpoint::Protocol
319
+ def policy(*); true; end
320
+ def authenticate(*); true; end
321
+ end
322
+
323
+ class BasicController
324
+ include Trailblazer::Endpoint::Controller.module(api: true, application_controller: true)
325
+
326
+ directive :options_for_block_options, Trailblazer::Endpoint::Controller.method(:options_for_block_options)
327
+
328
+ endpoint protocol: Protocol, adapter: Trailblazer::Endpoint::Adapter::Web
329
+
330
+ def head(status)
331
+ @status = status
332
+ end
333
+
334
+ def self.options_for_block_options(ctx, controller:, **)
335
+ {
336
+ success_block: ->(ctx, endpoint_ctx:, **) { controller.head("#{ctx[:op]}") },
337
+ failure_block: ->(ctx, status:, **) { },
338
+ protocol_failure_block: ->(ctx, status:, **) { }
339
+ }
340
+ end
341
+
342
+ directive :options_for_block_options, method(:options_for_block_options)
343
+ end
344
+
345
+ class RodaController < BasicController
346
+ class Create < Trailblazer::Activity::Railway
347
+ def save(ctx, **); ctx[:op] = self.class; end;
348
+ step :save
349
+ end
350
+ class Update < Create
351
+ end
352
+
353
+ # {Controller::endpoint}: {:domain_activity} defaults to {name} when not given
354
+ endpoint Create # class {name}s are ok
355
+ endpoint :update, domain_activity: Update # symbol {name} is ok
356
+
357
+ def show
358
+ endpoint Create
359
+ @status
360
+ end
361
+
362
+ def update
363
+ endpoint :update
364
+ @status
90
365
  end
91
366
  end
367
+
368
+
369
+ it "what" do
370
+ RodaController.new.show.must_equal %{ControllerEndpointMethodTest::RodaController::Create}
371
+ RodaController.new.update.must_equal %{ControllerEndpointMethodTest::RodaController::Update}
372
+ end
92
373
  end
374
+