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.
- checksums.yaml +4 -4
- data/.gitignore +16 -0
- data/Appraisals +5 -0
- data/CHANGES.md +27 -0
- data/README.md +40 -5
- data/Rakefile +7 -1
- data/gemfiles/rails_app.gemfile +12 -0
- data/lib/trailblazer/endpoint.rb +50 -14
- data/lib/trailblazer/endpoint/adapter.rb +30 -121
- data/lib/trailblazer/endpoint/builder.rb +1 -1
- data/lib/trailblazer/endpoint/controller.rb +217 -1
- data/lib/trailblazer/endpoint/dsl.rb +8 -3
- data/lib/trailblazer/endpoint/options.rb +16 -69
- data/lib/trailblazer/endpoint/protocol.rb +7 -11
- data/lib/trailblazer/endpoint/protocol/cipher.rb +27 -0
- data/lib/trailblazer/endpoint/protocol/controller.rb +102 -0
- data/lib/trailblazer/endpoint/protocol/find_process_model.rb +15 -0
- data/lib/trailblazer/endpoint/version.rb +1 -1
- data/test/adapter/api_test.rb +6 -11
- data/test/adapter/web_test.rb +2 -5
- data/test/config_test.rb +25 -0
- data/test/docs/controller_test.rb +220 -73
- data/test/endpoint_test.rb +1 -1
- data/test/rails-app/.gitignore +8 -2
- data/test/rails-app/.ruby-version +1 -0
- data/test/rails-app/Gemfile +21 -9
- data/test/rails-app/Gemfile.lock +174 -118
- data/test/rails-app/app/concepts/app/api/v1/representer/errors.rb +16 -0
- data/test/rails-app/app/concepts/auth/jwt.rb +35 -0
- data/test/rails-app/app/concepts/auth/operation/authenticate.rb +32 -0
- data/test/rails-app/app/concepts/auth/operation/policy.rb +9 -0
- data/test/rails-app/app/concepts/song/cell/create.rb +4 -0
- data/test/rails-app/app/concepts/song/cell/new.rb +4 -0
- data/test/rails-app/app/concepts/song/operation/create.rb +17 -0
- data/test/rails-app/app/concepts/song/operation/show.rb +10 -0
- data/test/rails-app/app/concepts/song/representer.rb +5 -0
- data/test/rails-app/app/concepts/song/view/create.erb +1 -0
- data/test/rails-app/app/concepts/song/view/new.erb +1 -0
- data/test/rails-app/app/controllers/api/v1/songs_controller.rb +41 -0
- data/test/rails-app/app/controllers/application_controller.rb +8 -1
- data/test/rails-app/app/controllers/application_controller/api.rb +107 -0
- data/test/rails-app/app/controllers/application_controller/web.rb +46 -0
- data/test/rails-app/app/controllers/auth_controller.rb +44 -0
- data/test/rails-app/app/controllers/home_controller.rb +5 -0
- data/test/rails-app/app/controllers/songs_controller.rb +254 -13
- data/test/rails-app/app/models/song.rb +6 -0
- data/test/rails-app/app/models/user.rb +7 -0
- data/test/rails-app/bin/bundle +114 -0
- data/test/rails-app/bin/rails +4 -0
- data/test/rails-app/bin/rake +4 -0
- data/test/rails-app/bin/setup +33 -0
- data/test/rails-app/config/application.rb +26 -3
- data/test/rails-app/config/credentials.yml.enc +1 -0
- data/test/rails-app/config/database.yml +2 -2
- data/test/rails-app/config/environments/development.rb +7 -17
- data/test/rails-app/config/environments/production.rb +28 -23
- data/test/rails-app/config/environments/test.rb +10 -12
- data/test/rails-app/config/initializers/application_controller_renderer.rb +6 -4
- data/test/rails-app/config/initializers/cors.rb +16 -0
- data/test/rails-app/config/initializers/trailblazer.rb +2 -0
- data/test/rails-app/config/locales/en.yml +11 -1
- data/test/rails-app/config/master.key +1 -0
- data/test/rails-app/config/routes.rb +27 -4
- data/test/rails-app/db/schema.rb +15 -0
- data/test/rails-app/test/controllers/api_songs_controller_test.rb +87 -0
- data/test/rails-app/test/controllers/songs_controller_test.rb +152 -145
- data/test/rails-app/test/test_helper.rb +7 -1
- data/test/test_helper.rb +0 -2
- data/trailblazer-endpoint.gemspec +2 -1
- metadata +69 -24
- data/test/rails-app/config/initializers/cookies_serializer.rb +0 -5
- data/test/rails-app/config/initializers/new_framework_defaults.rb +0 -24
- data/test/rails-app/config/initializers/session_store.rb +0 -3
- data/test/rails-app/config/secrets.yml +0 -22
- data/test/rails-app/test/helpers/.keep +0 -0
- data/test/rails-app/test/integration/.keep +0 -0
- data/test/rails-app/test/mailers/.keep +0 -0
- data/test/rails-app/vendor/assets/javascripts/.keep +0 -0
- 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
|
data/test/adapter/api_test.rb
CHANGED
@@ -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
|
-
|
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
|
-
)
|
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)
|
data/test/adapter/web_test.rb
CHANGED
@@ -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
|
-
|
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)
|
data/test/config_test.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
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:
|
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
|
-
|
61
|
-
|
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
|
-
|
77
|
-
|
86
|
+
seq: seq,
|
87
|
+
**controller.instance_variable_get(:@params)[:params],
|
88
|
+
}
|
78
89
|
end
|
79
90
|
|
80
|
-
|
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
|
-
|
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
|
-
|
181
|
-
|
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
|
-
|
185
|
-
|
186
|
-
|
183
|
+
def self.options_for_endpoint(ctx, controller:, **)
|
184
|
+
{
|
185
|
+
current_user: "Yogi",
|
186
|
+
process_model: Class,
|
187
|
+
}
|
187
188
|
end
|
188
189
|
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
-
|
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
|
-
|
233
|
+
current_user: "Yogi",
|
234
|
+
process_model: Class,
|
235
|
+
something: true,
|
201
236
|
}
|
202
237
|
end
|
203
238
|
|
204
|
-
|
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
|
-
|
209
|
-
|
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
|
-
|
213
|
-
|
214
|
-
|
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
|
-
|
218
|
-
|
219
|
-
|
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
|
-
|
224
|
-
|
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
|
|