trailblazer-endpoint 0.0.3 → 0.0.8

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 +27 -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 +50 -14
  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 +217 -1
  12. data/lib/trailblazer/endpoint/dsl.rb +8 -3
  13. data/lib/trailblazer/endpoint/options.rb +16 -69
  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 +102 -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 +25 -0
  22. data/test/docs/controller_test.rb +220 -73
  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 +174 -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 +46 -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 +254 -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 +27 -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 +152 -145
  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 +69 -24
  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,102 @@
1
+ module Trailblazer
2
+ class Endpoint::Protocol
3
+ # Deserialize incoming state.
4
+ # Serialize outgoing state.
5
+ # What else?
6
+ module Controller
7
+ module_function
8
+
9
+ def decrypt?(ctx, encrypted_resume_data:, **)
10
+ encrypted_resume_data
11
+ end
12
+
13
+ def deserialize_resume_data(ctx, decrypted_value:, **)
14
+ ctx[:resume_data] = JSON.parse(decrypted_value)
15
+ end
16
+
17
+ def deserialize_process_model?(ctx, process_model_from_resume_data:, **)
18
+ process_model_from_resume_data
19
+ end
20
+
21
+ def deserialize_process_model_id(ctx, resume_data:, **)
22
+ ctx[:process_model_id] = resume_data["id"] # DISCUSS: overriding {:process_model_id}?
23
+ end
24
+
25
+ def encrypt?(ctx, domain_ctx:, **)
26
+ ctx[:suspend_data] = domain_ctx[:suspend_data]
27
+ end
28
+
29
+ def serialize_suspend_data(ctx, suspend_data:, **)
30
+ ctx[:serialized_suspend_data] = JSON.dump(suspend_data)
31
+ end
32
+
33
+ def copy_suspend_data_to_endpoint_ctx(ctx, domain_ctx:, **)
34
+ ctx[:suspend_data] = domain_ctx[:suspend_data] # FIXME: use {#insert_copy_from_domain_ctx!}
35
+ end
36
+
37
+ # FIXME: use Model() mechanics.
38
+ def deserialize_process_model_id_from_resume_data(ctx, resume_data:, **)
39
+ # DISCUSS: should we warn when overriding an existing {process_model_id}?
40
+ ctx[:process_model_id] = resume_data["id"] # DISCUSS: overriding {:process_model_id}? # FIXME: stolen from Advance___::Controller
41
+ end
42
+
43
+ def insert_deserialize_steps!(activity, deserialize_before: :policy)
44
+ activity.module_eval do
45
+ step Controller.method(:decrypt?), id: :decrypt?, before: deserialize_before # error out if no serialized_resume_data given.
46
+ step Controller::Cipher.method(:decrypt_value), id: :decrypt,
47
+ input: {cipher_key: :cipher_key, encrypted_resume_data: :encrypted_value} , before: deserialize_before,
48
+ # Output(:failure) => Track(:success),
49
+ Output(:success) => Path(connect_to: Track(:success), track_color: :deserialize, before: deserialize_before) do # usually, Path goes into {policy}
50
+
51
+ step Controller.method(:deserialize_resume_data), id: :deserialize_resume_data
52
+ # DISCUSS: unmarshall?
53
+ # step Controller.method(:deserialize_process_model_id?), id: :deserialize_process_model_id?, activity.Output(Trailblazer::Activity::Left, :failure) => activity.Id(around_activity_id)
54
+ # step Controller.method(:deserialize_process_model_id), id: :deserialize_process_model_id
55
+
56
+ step ->(*) { true } # FIXME: otherwise we can't insert an element AFTER :deserialize_resume_data
57
+ end
58
+ end
59
+ end
60
+
61
+ def insert_serialize_steps!(activity, serialize_after: :domain_activity)
62
+ activity.module_eval do
63
+ # FIXME: reverse order for insertion
64
+ step Controller::Cipher.method(:encrypt_value), id: :encrypt , after: serialize_after,
65
+ input: {cipher_key: :cipher_key, serialized_suspend_data: :value}, output: {encrypted_value: :encrypted_suspend_data}
66
+ step Controller.method(:serialize_suspend_data), id: :serialize_suspend_data , after: serialize_after
67
+ pass Controller.method(:copy_suspend_data_to_endpoint_ctx), id: :copy_suspend_data_to_endpoint_ctx , after: serialize_after
68
+ end
69
+ end
70
+
71
+ # Insert the "experimental" {find_process_model} steps
72
+ def insert_find_process_model!(protocol, **options)
73
+ protocol.module_eval do
74
+ step Subprocess(FindProcessModel), Output(:failure) => End(:not_found),
75
+ id: :find_process_model,
76
+ **options
77
+ # after: :authenticate
78
+ end
79
+
80
+ insert_copy_to_domain_ctx!(protocol, :process_model => :model)
81
+ end
82
+
83
+ def insert_copy_to_domain_ctx!(protocol, variables, before: :domain_activity) # FIXME: `:before` untested!
84
+ variables.each do |original_name, domain_name|
85
+ protocol.module_eval do
86
+ pass ->(ctx, domain_ctx:, **) { domain_ctx[domain_name] = ctx[original_name] if ctx.key?(original_name) },
87
+ id: :"copy_[#{original_name.inspect}]_to_domain_ctx[#{domain_name.inspect}]", before: before
88
+ end
89
+ end
90
+ end
91
+
92
+ def insert_copy_from_domain_ctx!(protocol, variables, after: :domain_activity) # FIXME: `:after` untested!
93
+ variables.each do |domain_name, endpoint_name|
94
+ protocol.module_eval do
95
+ pass ->(ctx, domain_ctx:, **) { ctx[endpoint_name] = domain_ctx[domain_name] if domain_ctx.key?(domain_name) },
96
+ id: :"copy_[#{endpoint_name.inspect}]_from_domain_ctx[#{domain_name.inspect}]", after: after
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -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.3"
3
+ VERSION = "0.0.8"
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,53 @@ 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,
62
+ scope_domain_ctx: true
39
63
  end
40
64
 
41
-
42
65
  class HtmlController < ApplicationController
43
66
  private def endpoint_for(*)
44
67
  protocol = Class.new(Trailblazer::Endpoint::Protocol) do
@@ -50,46 +73,31 @@ class DocsControllerTest < Minitest::Spec
50
73
  domain_activity: Minitest::Spec.new(nil).activity, # FIXME
51
74
  protocol: protocol,
52
75
  adapter: Trailblazer::Endpoint::Adapter::Web,
53
- scope_domain_ctx: false,
76
+ scope_domain_ctx: true,
54
77
 
55
78
  ) do
56
79
  {Output(:not_found) => Track(:not_found)}
57
80
  end
58
81
  end
59
82
 
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,
83
+ def self.options_for_domain_ctx(ctx, seq:, controller:, **)
84
+ {
75
85
  current_user: "Yo",
76
- **@params,
77
- )
86
+ seq: seq,
87
+ **controller.instance_variable_get(:@params)[:params],
88
+ }
78
89
  end
79
90
 
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 }
91
+ directive :options_for_domain_ctx, method(:options_for_domain_ctx)
84
92
 
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}
93
+ private def _endpoint(action, seq: [], &block)
94
+ endpoint(action, seq: seq, &block)
86
95
  end
87
96
 
88
97
  # all standard routes are user-defined
89
98
  def view
90
99
  _endpoint "view?" do |ctx, seq:, **|
91
100
  render "success" + ctx[:current_user] + seq.inspect
92
-
93
101
  end.failure do |ctx, seq:, **|
94
102
  render "failure" + ctx[:current_user] + seq.inspect
95
103
 
@@ -114,16 +122,6 @@ class DocsControllerTest < Minitest::Spec
114
122
  end
115
123
  end
116
124
 
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
125
  end # HtmlController
128
126
 
129
127
  it "what" do
@@ -171,57 +169,206 @@ class DocsControllerTest < Minitest::Spec
171
169
  # from controller-default
172
170
  controller.process(:update, params: {authenticate: false}).must_equal [:authenticate, :protocol_failure_block]
173
171
  end
174
- end
175
172
 
176
- class ControllerOptionsTest < Minitest::Spec
177
- class Controller
178
- include Trailblazer::Endpoint::Controller
179
173
 
180
- def view
181
- endpoint "view?"
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
+ }
182
181
  end
183
182
 
184
- # we add {:options_for_domain_ctx} manually
185
- def download
186
- endpoint "download?", params: {id: params[:other_id]}, redis: "Redis"
183
+ def self.options_for_endpoint(ctx, controller:, **)
184
+ {
185
+ current_user: "Yogi",
186
+ process_model: Class,
187
+ }
187
188
  end
188
189
 
189
- # override some settings from {endpoint_options}:
190
- def new
191
- endpoint "new?", find_process_model: false
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
192
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]}
193
203
  end
194
204
 
195
- class ControllerThatDoesntInherit
196
- include Trailblazer::Endpoint::Controller
197
205
 
198
- def options_for_domain_ctx
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)
210
+ end
211
+
212
+ Activity = Class.new(Trailblazer::Activity::Railway) do
213
+ step :check
214
+
215
+ def check(ctx, current_user:, seq:, process_model:, **)
216
+ seq << :check
217
+ ctx[:message] = "#{current_user} / #{process_model}"
218
+ end
219
+ end
220
+
221
+ endpoint "view?", domain_activity: Activity
222
+ endpoint "show?", domain_activity: Activity
223
+
224
+
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:, **)
199
232
  {
200
- params: params
233
+ current_user: "Yogi",
234
+ process_model: Class,
235
+ something: true,
201
236
  }
202
237
  end
203
238
 
204
- def options_for_endpoint
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
205
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
206
261
  end
207
262
 
208
- def view
209
- endpoint "view?"
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
210
307
  end
308
+ end
309
+
310
+ it "what" do
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
211
322
 
212
- # we add {:options_for_domain_ctx} manually
213
- def download
214
- endpoint "download?", params: {id: params[:other_id]}, redis: "Redis"
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
215
332
  end
216
333
 
217
- # override some settings from {endpoint_options}:
218
- def new
219
- endpoint "new?", find_process_model: false
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
+ }
220
340
  end
341
+
342
+ directive :options_for_block_options, method(:options_for_block_options)
221
343
  end
222
344
 
223
- it "allows to get options without a bloody controller" do
224
- MemoController.bla(params: params)
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
365
+ end
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}
225
372
  end
226
373
  end
227
374