trailblazer-endpoint 0.0.1 → 0.0.6

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +16 -0
  3. data/Appraisals +5 -0
  4. data/CHANGES.md +26 -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 +29 -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 +223 -0
  12. data/lib/trailblazer/endpoint/dsl.rb +29 -0
  13. data/lib/trailblazer/endpoint/options.rb +92 -0
  14. data/lib/trailblazer/endpoint/protocol.rb +5 -8
  15. data/lib/trailblazer/endpoint/version.rb +1 -1
  16. data/test/adapter/api_test.rb +6 -11
  17. data/test/adapter/web_test.rb +2 -5
  18. data/test/config_test.rb +128 -0
  19. data/test/docs/controller_test.rb +339 -58
  20. data/test/endpoint_test.rb +1 -1
  21. data/test/rails-app/.gitignore +8 -2
  22. data/test/rails-app/.ruby-version +1 -0
  23. data/test/rails-app/Gemfile +15 -9
  24. data/test/rails-app/Gemfile.lock +162 -121
  25. data/test/rails-app/app/concepts/app/api/v1/representer/errors.rb +16 -0
  26. data/test/rails-app/app/concepts/auth/jwt.rb +35 -0
  27. data/test/rails-app/app/concepts/auth/operation/authenticate.rb +32 -0
  28. data/test/rails-app/app/concepts/auth/operation/policy.rb +9 -0
  29. data/test/rails-app/app/concepts/song/cell/create.rb +4 -0
  30. data/test/rails-app/app/concepts/song/cell/new.rb +4 -0
  31. data/test/rails-app/app/concepts/song/operation/create.rb +17 -0
  32. data/test/rails-app/app/concepts/song/operation/show.rb +10 -0
  33. data/test/rails-app/app/concepts/song/representer.rb +5 -0
  34. data/test/rails-app/app/concepts/song/view/create.erb +1 -0
  35. data/test/rails-app/app/concepts/song/view/new.erb +1 -0
  36. data/test/rails-app/app/controllers/api/v1/songs_controller.rb +41 -0
  37. data/test/rails-app/app/controllers/application_controller.rb +8 -1
  38. data/test/rails-app/app/controllers/application_controller/api.rb +107 -0
  39. data/test/rails-app/app/controllers/application_controller/web.rb +44 -0
  40. data/test/rails-app/app/controllers/auth_controller.rb +44 -0
  41. data/test/rails-app/app/controllers/home_controller.rb +5 -0
  42. data/test/rails-app/app/controllers/songs_controller.rb +71 -13
  43. data/test/rails-app/app/models/song.rb +3 -0
  44. data/test/rails-app/app/models/user.rb +7 -0
  45. data/test/rails-app/bin/bundle +114 -0
  46. data/test/rails-app/bin/rails +4 -0
  47. data/test/rails-app/bin/rake +4 -0
  48. data/test/rails-app/bin/setup +33 -0
  49. data/test/rails-app/config/application.rb +26 -3
  50. data/test/rails-app/config/credentials.yml.enc +1 -0
  51. data/test/rails-app/config/database.yml +2 -2
  52. data/test/rails-app/config/environments/development.rb +7 -17
  53. data/test/rails-app/config/environments/production.rb +28 -23
  54. data/test/rails-app/config/environments/test.rb +8 -12
  55. data/test/rails-app/config/initializers/application_controller_renderer.rb +6 -4
  56. data/test/rails-app/config/initializers/cors.rb +16 -0
  57. data/test/rails-app/config/initializers/trailblazer.rb +2 -0
  58. data/test/rails-app/config/locales/en.yml +11 -1
  59. data/test/rails-app/config/master.key +1 -0
  60. data/test/rails-app/config/routes.rb +16 -4
  61. data/test/rails-app/db/schema.rb +15 -0
  62. data/test/rails-app/test/controllers/api_songs_controller_test.rb +87 -0
  63. data/test/rails-app/test/controllers/songs_controller_test.rb +80 -147
  64. data/test/rails-app/test/test_helper.rb +7 -1
  65. data/test/test_helper.rb +0 -2
  66. data/trailblazer-endpoint.gemspec +2 -1
  67. metadata +70 -22
  68. data/test/rails-app/config/initializers/cookies_serializer.rb +0 -5
  69. data/test/rails-app/config/initializers/new_framework_defaults.rb +0 -24
  70. data/test/rails-app/config/initializers/session_store.rb +0 -3
  71. data/test/rails-app/config/secrets.yml +0 -22
  72. data/test/rails-app/test/helpers/.keep +0 -0
  73. data/test/rails-app/test/integration/.keep +0 -0
  74. data/test/rails-app/test/mailers/.keep +0 -0
  75. data/test/rails-app/vendor/assets/javascripts/.keep +0 -0
  76. data/test/rails-app/vendor/assets/stylesheets/.keep +0 -0
@@ -0,0 +1,223 @@
1
+ module Trailblazer
2
+ class Endpoint
3
+ module Controller
4
+ def self.extended(extended)
5
+ extended.extend Trailblazer::Endpoint::Options::DSL # ::directive
6
+ extended.extend Trailblazer::Endpoint::Options::DSL::Inherit
7
+ extended.extend Trailblazer::Endpoint::Options # ::options_for
8
+ extended.extend DSL::Endpoint
9
+
10
+ extended.include InstanceMethods # {#endpoint_for}
11
+
12
+ # DISCUSS: hmm
13
+ extended.directive :generic_options, ->(*) { Hash.new } # for Controller::endpoint
14
+ extended.directive :options_for_flow_options, ->(*) { Hash.new }
15
+ extended.directive :options_for_endpoint, ->(*) { Hash.new }
16
+ extended.directive :options_for_domain_ctx, ->(*) { Hash.new }
17
+ end
18
+
19
+ # @experimental
20
+ # TODO: test application_controller with and without dsl/api
21
+
22
+ def self.module(framework: :rails, api: false, dsl: false, application_controller: false)
23
+ if application_controller && !api && !dsl # FIXME: not tested! this is useful for an actual AppController with block_options or flow_options settings, "globally"
24
+ Module.new do
25
+ def self.included(includer)
26
+ includer.extend(Controller) # only ::directive and friends.
27
+ end
28
+ end
29
+ elsif api
30
+ Module.new do
31
+ @application_controller = application_controller
32
+ def self.included(includer)
33
+ if @application_controller
34
+ includer.extend Controller
35
+ end
36
+ includer.include(InstanceMethods::API)
37
+ end
38
+ end
39
+ elsif dsl
40
+ Module.new do
41
+ @application_controller = application_controller
42
+ def self.included(includer)
43
+ if @application_controller
44
+ includer.extend Controller
45
+ end
46
+ includer.include Trailblazer::Endpoint::Controller::InstanceMethods::DSL
47
+ includer.include Trailblazer::Endpoint::Controller::Rails
48
+ includer.extend Trailblazer::Endpoint::Controller::Rails::DefaultBlocks
49
+ includer.extend Trailblazer::Endpoint::Controller::Rails::DefaultParams
50
+ includer.include Trailblazer::Endpoint::Controller::Rails::Process
51
+ end
52
+ end # Module
53
+ else
54
+ raise
55
+ end
56
+ end
57
+
58
+ module Rails
59
+ module Process
60
+ def send_action(action_name)
61
+ puts "@@@@@>>>>>>> #{action_name.inspect}"
62
+
63
+ dsl = send(action_name) # call the actual controller action.
64
+
65
+ options, block_options = dsl.to_args(self.class.options_for(:options_for_block_options, controller: self)) # {success_block:, failure_block:, protocol_failure_block:}
66
+ # now we know the authorative blocks
67
+
68
+ Controller.advance_endpoint_for_controller(**options, block_options: block_options, config_source: self.class, controller: self)
69
+ end
70
+
71
+ end # Process
72
+
73
+ # The three default handlers for {Endpoint::with_or_etc}
74
+ # @experimental
75
+ module DefaultBlocks
76
+ def self.extended(extended)
77
+ extended.directive :options_for_block_options, Controller.method(:options_for_block_options)
78
+ end
79
+ end
80
+ # @experimental
81
+ module DefaultParams
82
+ def self.extended(extended)
83
+ extended.directive :options_for_domain_ctx, ->(ctx, controller:, **) { {params: controller.params} }
84
+ end
85
+ end
86
+
87
+ end # Rails
88
+
89
+ module DSL
90
+ module Endpoint
91
+ def self.extended(extended)
92
+ extended.directive(:endpoints, ->(*) { {} })
93
+ end
94
+
95
+ def endpoint(name, **options, &block)
96
+ options = options.merge(protocol_block: block) if block_given?
97
+
98
+ return generic_endpoint_config(**name, **options) if name.is_a?(Hash)
99
+ endpoint_config(name, **options)
100
+ end
101
+
102
+ def generic_endpoint_config(protocol:, adapter:, **options)
103
+ self.singleton_class.define_method :generic_options do |ctx,**|
104
+ {
105
+ protocol: protocol,
106
+ adapter: adapter,
107
+ **options
108
+ }
109
+ end
110
+
111
+ directive :generic_options, method(:generic_options) # FIXME: do we need this?
112
+ end
113
+
114
+ def endpoint_config(name, domain_activity: name, **options)
115
+ build_options = options_for(:generic_options, {}).merge(domain_activity: domain_activity, **options) # DISCUSS: why don't we add this as another directive option/step?
116
+
117
+ endpoint = Trailblazer::Endpoint.build(build_options)
118
+
119
+ directive :endpoints, ->(*) { {name.to_s => endpoint} }
120
+ end
121
+
122
+ end
123
+ end
124
+
125
+ module InstanceMethods
126
+
127
+ def endpoint_for(name, config_source: self.class)
128
+ config_source.options_for(:endpoints, {}).fetch(name.to_s) # TODO: test non-existant endpoint
129
+ end
130
+
131
+ module DSL
132
+ def endpoint(name, **action_options, &block)
133
+ action_options = {controller: self}.merge(action_options) # FIXME: redundant with {API#endpoint}
134
+
135
+ endpoint = endpoint_for(name)
136
+
137
+ invoke_endpoint_with_dsl(endpoint: endpoint, **action_options, &block)
138
+ end
139
+
140
+ def invoke_endpoint_with_dsl(options, &block)
141
+ _dsl = Trailblazer::Endpoint::DSL::Runtime.new(options, block) # provides #Or etc, is returned to {Controller#call}
142
+ end
143
+ end
144
+
145
+ module API
146
+ def endpoint(name, config_source: self.class, **action_options)
147
+ endpoint = endpoint_for(name, config_source: config_source)
148
+
149
+ action_options = {controller: self}.merge(action_options) # FIXME: redundant with {InstanceMethods#endpoint}
150
+
151
+ block_options = config_source.options_for(:options_for_block_options, **action_options)
152
+ block_options = Trailblazer::Endpoint::Options.merge_with(action_options, block_options)
153
+
154
+ signal, (ctx, _) = Trailblazer::Endpoint::Controller.advance_endpoint_for_controller(
155
+ endpoint: endpoint,
156
+ block_options: block_options,
157
+ config_source: config_source,
158
+ **action_options
159
+ )
160
+
161
+ ctx
162
+ end
163
+ end # API
164
+ end
165
+
166
+
167
+ def self.advance_endpoint_for_controller(endpoint:, block_options:, **action_options)
168
+ domain_ctx, endpoint_options, flow_options = compile_options_for_controller(**action_options) # controller-specific, get from directives.
169
+
170
+ endpoint_options = endpoint_options.merge(action_options) # DISCUSS
171
+
172
+ Endpoint::Controller.advance_endpoint(
173
+ endpoint: endpoint,
174
+ block_options: block_options,
175
+
176
+ domain_ctx: domain_ctx,
177
+ endpoint_options: endpoint_options,
178
+ flow_options: flow_options,
179
+ )
180
+ end
181
+
182
+ def self.compile_options_for_controller(options_for_domain_ctx: nil, config_source:, **action_options)
183
+ flow_options = config_source.options_for(:options_for_flow_options, **action_options)
184
+ endpoint_options = config_source.options_for(:options_for_endpoint, **action_options) # "class level"
185
+ domain_ctx = options_for_domain_ctx || config_source.options_for(:options_for_domain_ctx, **action_options)
186
+
187
+ return domain_ctx, endpoint_options, flow_options
188
+ end
189
+
190
+ # Ultimate low-level entry point.
191
+ # Remember that you don't _have_ to use Endpoint.with_or_etc to invoke an endpoint.
192
+ def self.advance_endpoint(endpoint:, block_options:, domain_ctx:, endpoint_options:, flow_options:)
193
+
194
+ # build Context(ctx),
195
+ args, _ = Trailblazer::Endpoint.arguments_for(
196
+ domain_ctx: domain_ctx,
197
+ flow_options: flow_options,
198
+ **endpoint_options,
199
+ )
200
+
201
+ signal, (ctx, _ ) = Trailblazer::Endpoint.with_or_etc(
202
+ endpoint,
203
+ args, # [ctx, flow_options]
204
+
205
+ **block_options,
206
+ # success_block: success_block,
207
+ # failure_block: failure_block,
208
+ # protocol_failure_block: protocol_failure_block,
209
+ )
210
+ end
211
+
212
+ # Default blocks for the {Adapter}.
213
+ def self.options_for_block_options(ctx, controller:, **)
214
+ {
215
+ success_block: ->(ctx, endpoint_ctx:, **) { controller.head 200 },
216
+ failure_block: ->(ctx, **) { controller.head 422 },
217
+ protocol_failure_block: ->(ctx, endpoint_ctx:, **) { controller.head endpoint_ctx[:status] }
218
+ }
219
+ end
220
+
221
+ end # Controller
222
+ end
223
+ end
@@ -0,0 +1,29 @@
1
+ module Trailblazer
2
+ class Endpoint
3
+ module DSL
4
+ # Run before the endpoint is invoked. This collects the blocks from the controller.
5
+ class Runtime < Struct.new(:options, :success_block, :failure_block, :protocol_failure_block)
6
+
7
+ def failure(&block)
8
+ self.failure_block = block
9
+ self
10
+ end
11
+
12
+ alias_method :Or, :failure
13
+
14
+ def protocol_failure(&block)
15
+ self.protocol_failure_block = block
16
+ self
17
+ end
18
+
19
+ # #call
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]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,92 @@
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.
3
+ module Trailblazer
4
+ class Endpoint
5
+ module Options
6
+ module DSL
7
+ def directive(directive_name, *callables, inherit: superclass)
8
+ options = {}
9
+
10
+ if inherit
11
+ options[:base_class] = instance_variable_get(:@normalizers)[directive_name] || Trailblazer::Activity::Path # FIXME
12
+ end
13
+
14
+ @normalizers[directive_name] = Trailblazer::Endpoint::Normalizer.Options(directive_name, *callables, **options) # DISCUSS: allow multiple calls?
15
+ end
16
+
17
+ # Called in {Endpoint::Controller}.
18
+ def self.extended(extended) # TODO: let's hope this is only called once per hierachy :)
19
+ extended.instance_variable_set(:@normalizers, {})
20
+ end
21
+
22
+ module Inherit
23
+ def inherited(subclass)
24
+ super
25
+
26
+ subclass.instance_variable_set(:@normalizers, @normalizers.dup)
27
+ end
28
+ end
29
+ end
30
+
31
+ def options_for(directive_name, runtime_options)
32
+ normalizer = @normalizers.fetch(directive_name)
33
+
34
+ ctx = Trailblazer::Context(runtime_options, {})
35
+
36
+ # signal, (ctx, ) = Trailblazer::Developer.wtf?(normalizer, [ctx])
37
+ signal, (ctx, ) = Trailblazer::Activity::TaskWrap.invoke(normalizer, [ctx])
38
+
39
+ _, options = ctx.decompose
40
+ options
41
+ 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
51
+
52
+ module Normalizer
53
+ def self.Options(directive_name, *callables, base_class: Trailblazer::Activity::Path)
54
+ normalizer = Class.new(base_class) do
55
+ end
56
+
57
+ Normalizer.add(normalizer, directive_name, callables)
58
+ end
59
+
60
+ def self.DefaultToEmptyHash(config_name)
61
+ -> (ctx, **) { ctx[config_name] ||= {} }
62
+ end
63
+
64
+ def self.add_normalizer!(target, normalizer, config)
65
+ normalizer = Normalizer.add(normalizer, target, config) # add configure steps for {subclass} to the _new_ normalizer.
66
+ target.instance_variable_set(:@normalizer, normalizer)
67
+ target.instance_variable_set(:@config, config)
68
+ end
69
+
70
+ def self.add(normalizer, directive_name, options)
71
+ Class.new(normalizer) do
72
+ options.collect do |callable|
73
+ step task: Normalizer.CallDirective(callable, directive_name), id: "#{directive_name}=>#{callable}"
74
+ end
75
+ end
76
+ end
77
+
78
+ def self.CallDirective(callable, option_name)
79
+ ->((ctx, flow_options), *) {
80
+ config = callable.(ctx, **ctx) # e.g. ApplicationController.options_for_endpoint
81
+
82
+ # ctx[option_name] = ctx[option_name].merge(config)
83
+ config.each do |k, v|
84
+ ctx[k] = v
85
+ end
86
+
87
+ return Trailblazer::Activity::Right, [ctx, flow_options]
88
+ }
89
+ end
90
+ end # Normalizer
91
+ end
92
+ 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.1"
3
+ VERSION = "0.0.6"
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