Class: SolidObjects::ComponentsController
- Inherits:
-
ActionController::Base
- Object
- ActionController::Base
- SolidObjects::ComponentsController
- Defined in:
- app/controllers/solid_objects/components_controller.rb,
sig/generated/controllers/solid_objects/components_controller.rbs
Constant Summary collapse
- BATCH_LIMIT =
50
Instance Method Summary collapse
-
#accepts_registrations?(callable) ⇒ Boolean
A lambda answers
parametersdirectly; a callable object answers it through itscallmethod. - #batch ⇒ void
- #callable_parameters(callable) ⇒ Array[[ Symbol, Symbol ]]
-
#component_authorization_context(registrations) ⇒ Object
Callbacks written before batching accept only
controller:. - #component_frame(registration, snapshot, rendered) ⇒ String
- #component_view_context ⇒ Object
- #newer_than_snapshot?(requested_revision, snapshot) ⇒ Boolean
- #refresh(payload) ⇒ void
- #refresh_batch(payload) ⇒ void
- #registration_payload(registration) ⇒ Hash[Symbol, untyped]
- #requested_revision_key ⇒ Array[Integer]
- #show ⇒ void
- #validate_single_batch!(registrations) ⇒ void
Instance Method Details
#accepts_registrations?(callable) ⇒ Boolean
A lambda answers parameters directly; a callable object answers it
through its call method.
149 150 151 152 153 |
# File 'app/controllers/solid_objects/components_controller.rb', line 149 def accepts_registrations?(callable) callable_parameters(callable).any? do |type, name| type == :keyrest || (%i[key keyreq].include?(type) && name == :registrations) end end |
#batch ⇒ void
This method returns an undefined value.
17 18 19 |
# File 'app/controllers/solid_objects/components_controller.rb', line 17 def batch SolidObjects.instrument(:"component.batch_refreshed") { |payload| refresh_batch(payload) } end |
#callable_parameters(callable) ⇒ Array[[ Symbol, Symbol ]]
156 157 158 159 160 161 |
# File 'app/controllers/solid_objects/components_controller.rb', line 156 def callable_parameters(callable) return callable.parameters if callable.respond_to?(:parameters) return callable.method(:call).parameters if callable.respond_to?(:call) [] end |
#component_authorization_context(registrations) ⇒ Object
Callbacks written before batching accept only controller:. Those keep
working; a callback that also accepts registrations: receives one
registration for a single refresh and all of them for a batch.
139 140 141 142 143 144 |
# File 'app/controllers/solid_objects/components_controller.rb', line 139 def (registrations) callable = SolidObjects.configuration. return callable.call(controller: self) unless accepts_registrations?(callable) callable.call(controller: self, registrations:) end |
#component_frame(registration, snapshot, rendered) ⇒ String
201 202 203 204 |
# File 'app/controllers/solid_objects/components_controller.rb', line 201 def component_frame(registration, snapshot, rendered) revision = "#{snapshot.instance_id}:#{snapshot.revision}" %(<turbo-frame id="#{registration.dom_id}" data-solid-objects-revision="#{revision}" data-solid-objects-refresh="#{registration.refresh_method}">#{rendered}</turbo-frame>).html_safe end |
#component_view_context ⇒ Object
190 191 192 193 194 195 196 197 198 |
# File 'app/controllers/solid_objects/components_controller.rb', line 190 def component_view_context if defined?(Rails) && Rails.application prepend_view_path(*Rails.application.paths["app/views"].existent) end view_context.tap do |context| context.extend(Rails.application.helpers) if defined?(Rails) && Rails.application end end |
#newer_than_snapshot?(requested_revision, snapshot) ⇒ Boolean
185 186 187 |
# File 'app/controllers/solid_objects/components_controller.rb', line 185 def newer_than_snapshot?(requested_revision, snapshot) (requested_revision <=> [ snapshot.instance_id, snapshot.revision ]) == 1 end |
#refresh(payload) ⇒ void
This method returns an undefined value.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'app/controllers/solid_objects/components_controller.rb', line 98 def refresh(payload) registration = ComponentRegistration.from_token( params.require(:token) ) payload.merge!(registration_payload(registration)) requested_revision = requested_revision_key snapshot = ActorSnapshot.new(registration.reference) payload[:instance_id] = snapshot.instance_id payload[:revision] = snapshot.revision if newer_than_snapshot?(requested_revision, snapshot) payload[:outcome] = "conflict" return head :conflict end = ([ registration ]) rendered = ComponentRenderer.new( snapshot:, registration:, view_context: component_view_context, authorization_context: ).call response.headers["Cache-Control"] = "private, no-store" payload[:outcome] = "rendered" render html: component_frame(registration, snapshot, rendered) rescue Unauthorized payload[:outcome] = "unauthorized" head :forbidden rescue UnknownComponent payload[:outcome] = "unknown_component" head :not_found rescue ActionController::ParameterMissing, ArgumentError, InvalidComponentToken payload[:outcome] = "invalid_token" head :bad_request end |
#refresh_batch(payload) ⇒ void
This method returns an undefined value.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'app/controllers/solid_objects/components_controller.rb', line 24 def refresh_batch(payload) tokens = Array(params.require(:tokens)) raise ActionController::ParameterMissing, :tokens if tokens.empty? raise ArgumentError if tokens.length > BATCH_LIMIT registrations = tokens.map { |token| ComponentRegistration.from_token(token) } validate_single_batch!(registrations) requested_revision = requested_revision_key snapshot = ActorSnapshot.new(registrations.first.reference) payload.merge!( actor_type: snapshot.reference.actor_type, actor_id: snapshot.reference.actor_id, batch: registrations.first.batch, components: registrations.map(&:component_name), instance_id: snapshot.instance_id, revision: snapshot.revision ) if newer_than_snapshot?(requested_revision, snapshot) payload[:outcome] = "conflict" return head :conflict end = (registrations) frames = registrations.map do |registration| rendered = ComponentRenderer.new( snapshot:, registration:, view_context: component_view_context, authorization_context: ).call { "target" => registration.dom_id, "revision" => "#{snapshot.instance_id}:#{snapshot.revision}", "refresh_method" => registration.refresh_method, "html" => component_frame(registration, snapshot, rendered) } end response.headers["Cache-Control"] = "private, no-store" payload[:outcome] = "rendered" render json: { "actor_type" => snapshot.reference.actor_type, "actor_id" => snapshot.reference.actor_id, "batch" => registrations.first.batch, "instance_id" => snapshot.instance_id, "revision" => snapshot.revision, "frames" => frames } rescue Unauthorized payload[:outcome] = "unauthorized" head :forbidden rescue UnknownComponent payload[:outcome] = "unknown_component" head :not_found rescue ActionController::ParameterMissing, ArgumentError, InvalidComponentToken payload[:outcome] = "invalid_token" head :bad_request end |
#registration_payload(registration) ⇒ Hash[Symbol, untyped]
164 165 166 167 168 169 170 171 172 173 |
# File 'app/controllers/solid_objects/components_controller.rb', line 164 def registration_payload(registration) { actor_type: registration.reference.actor_type, actor_id: registration.reference.actor_id, component_name: registration.component_name, component_key: registration.component_key, dependencies: registration.dependencies, refresh_method: registration.refresh_method } end |
#requested_revision_key ⇒ Array[Integer]
176 177 178 179 180 181 182 |
# File 'app/controllers/solid_objects/components_controller.rb', line 176 def requested_revision_key instance_id = Integer(params.fetch(:instance_id), 10) revision = Integer(params.fetch(:revision), 10) raise ArgumentError if instance_id.negative? || revision.negative? [ instance_id, revision ] end |
#show ⇒ void
This method returns an undefined value.
12 13 14 |
# File 'app/controllers/solid_objects/components_controller.rb', line 12 def show SolidObjects.instrument(:"component.refreshed") { |payload| refresh(payload) } end |
#validate_single_batch!(registrations) ⇒ void
This method returns an undefined value.
85 86 87 88 89 90 91 92 93 94 95 |
# File 'app/controllers/solid_objects/components_controller.rb', line 85 def validate_single_batch!(registrations) first = registrations.first raise ArgumentError unless first.batch return if registrations.all? do |registration| registration.batch == first.batch && registration.reference.actor_type == first.reference.actor_type && registration.reference.actor_id == first.reference.actor_id end raise ArgumentError end |