Class: TurboBoost::Commands::Command

Inherits:
Object
  • Object
show all
Includes:
CommandCallbacks
Defined in:
lib/turbo_boost/commands/command.rb

Overview

TurboBoost::Commands::Command superclass. All command classes should inherit from this class.

Commands are executed via a before_action in the Rails controller lifecycle. They have access to the following methods and properties.

  • controller .….….….….….. The Rails controller processing the HTTP request

  • convert_to_instance_variables … Converts a Hash to instance variables

  • css_id_selector .….….….…. Returns a CSS selector for an element ‘id` i.e. prefixes with `#`

  • dom_id .….….….….….….. The Rails dom_id helper

  • dom_id_selector .….….….…. Returns a CSS selector for a dom_id

  • element .….….….….….…. A struct that represents the DOM element that triggered the command

  • morph .….….….….….…… Appends a Turbo Stream to morph a DOM element

  • params .….….….….….….. Commands specific params (frame_id, element, etc.)

  • render .….….….….….….. Renders Rails templates, partials, etc. (doesn’t halt controller request handling)

  • renderer .….….….….….… An ActionController::Renderer

  • state .….….….….….…… An object that stores ephemeral ‘state`

  • transfer_instance_variables .…. Transfers all instance variables to another object

  • turbo_stream .….….….….… A Turbo Stream TagBuilder

  • turbo_streams .….….….…… A list of Turbo Streams to append to the response (also aliased as streams)

They also have access to the following class methods:

  • prevent_controller_action … Prevents the rails controller/action from running (i.e. the command handles the response entirely)

Constant Summary

Constants included from CommandCallbacks

TurboBoost::Commands::CommandCallbacks::NAME

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from CommandCallbacks

#abort_handler, #aborted?, #error_handler, #errored?, #perform_with_callbacks, #performed?, #performing?, #succeeded?

Constructor Details

#initialize(controller, state, params = {}) ⇒ Command

Returns a new instance of Command.



86
87
88
89
90
91
# File 'lib/turbo_boost/commands/command.rb', line 86

def initialize(controller, state, params = {})
  @controller = controller
  @state = state
  @params = params
  @turbo_streams = Set.new
end

Instance Attribute Details

#controllerObject (readonly)

Returns the value of attribute controller.



80
81
82
# File 'lib/turbo_boost/commands/command.rb', line 80

def controller
  @controller
end

#paramsObject (readonly)

Returns the value of attribute params.



80
81
82
# File 'lib/turbo_boost/commands/command.rb', line 80

def params
  @params
end

#stateObject (readonly)

Returns the value of attribute state.



80
81
82
# File 'lib/turbo_boost/commands/command.rb', line 80

def state
  @state
end

#turbo_streamsObject (readonly) Also known as: streams

Returns the value of attribute turbo_streams.



80
81
82
# File 'lib/turbo_boost/commands/command.rb', line 80

def turbo_streams
  @turbo_streams
end

Class Method Details

.css_id_selector(id) ⇒ Object



35
36
37
38
# File 'lib/turbo_boost/commands/command.rb', line 35

def css_id_selector(id)
  return id if id.to_s.start_with?("#")
  "##{id}"
end

.prevent_controller_action(options = {}) ⇒ Object



44
45
46
# File 'lib/turbo_boost/commands/command.rb', line 44

def prevent_controller_action(options = {})
  preventers << options.with_indifferent_access
end

.preventersObject



40
41
42
# File 'lib/turbo_boost/commands/command.rb', line 40

def preventers
  @preventers ||= Set.new
end

.should_prevent_controller_action?(command, method_name) ⇒ Boolean

Returns:

  • (Boolean)


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
# File 'lib/turbo_boost/commands/command.rb', line 48

def should_prevent_controller_action?(command, method_name)
  method_name = method_name.to_s
  match = preventers.find do |options|
    only = options[:only] || []
    only = [only] unless only.is_a?(Array)
    only.map!(&:to_s)

    except = options[:except] || []
    except = [except] unless except.is_a?(Array)
    except.map!(&:to_s)

    options.blank? || only.include?(method_name) || (except.present? && except.exclude?(method_name))
  end

  return false if match.nil?

  if match[:if].present?
    case match[:if]
    when Symbol then command.public_send(match[:if])
    when Proc then command.instance_exec { match[:if].call command }
    end
  elsif match[:unless].present?
    case match[:unless]
    when Symbol then !command.public_send(match[:unless])
    when Proc then !(command.instance_exec { match[:unless].call(command) })
    end
  else
    true
  end
end

Instance Method Details

#convert_to_instance_variables(hash = {}) ⇒ Object

Converts a Hash to instance variables



103
104
105
106
# File 'lib/turbo_boost/commands/command.rb', line 103

def convert_to_instance_variables(hash = {})
  return unless hash.present?
  hash.each { |key, value| instance_variable_set :"@#{key}", value }
end

#dom_id_selectorObject



115
116
117
# File 'lib/turbo_boost/commands/command.rb', line 115

def dom_id_selector(...)
  css_id_selector dom_id(...)
end

#elementObject



145
146
147
148
149
150
151
152
153
# File 'lib/turbo_boost/commands/command.rb', line 145

def element
  @element ||= begin
    attributes = params[:element_attributes].to_h
    TurboBoost::Commands::AttributeSet.new(attributes.merge(
      aria: TurboBoost::Commands::AttributeSet.new(attributes, prefix: "aria"),
      data: TurboBoost::Commands::AttributeSet.new(attributes, prefix: "data")
    ))
  end
end

#morph(html:, id: nil, selector: nil) ⇒ Object



140
141
142
143
# File 'lib/turbo_boost/commands/command.rb', line 140

def morph(html:, id: nil, selector: nil)
  selector ||= css_id_selector(id)
  turbo_streams << turbo_stream.invoke("morph", args: [html], selector: selector)
end

#performObject

Abstract ‘perform` method, override in subclassed commands

Raises:

  • (NotImplementedError)


98
99
100
# File 'lib/turbo_boost/commands/command.rb', line 98

def perform
  raise NotImplementedError, "#{self.class.name} must implement the `perform` method!"
end

#render(options = {}, locals = {}, &block) ⇒ Object

Same method signature as ActionView::Rendering#render (i.e. controller.view_context.render) Great for rendering partials with short-hand syntax sugar → ‘render “/path/to/partial”`



121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/turbo_boost/commands/command.rb', line 121

def render(options = {}, locals = {}, &block)
  return controller.view_context.render(options, locals, &block) unless options.is_a?(Hash)

  options = options.symbolize_keys

  ivars = options[:assigns]&.each_with_object({}) do |(key, value), memo|
    memo[key] = controller.instance_variable_get(:"@#{key}")
    controller.instance_variable_set :"@#{key}", value
  end

  controller.view_context.render(options.except(:assigns), locals, &block)
ensure
  ivars&.each { |key, value| controller.instance_variable_set :"@#{key}", value }
end

#resolve_state(client_state) ⇒ Object

Abstract method to resolve state (default noop), override in subclassed commands



94
95
# File 'lib/turbo_boost/commands/command.rb', line 94

def resolve_state(client_state)
end

#should_prevent_controller_action?(method_name) ⇒ Boolean

Returns:

  • (Boolean)


155
156
157
# File 'lib/turbo_boost/commands/command.rb', line 155

def should_prevent_controller_action?(method_name)
  self.class.should_prevent_controller_action? self, method_name
end

#transfer_instance_variables(object) ⇒ Object

Transfers all instance variables to another object



109
110
111
112
113
# File 'lib/turbo_boost/commands/command.rb', line 109

def transfer_instance_variables(object)
  instance_variables.each do |name|
    object.instance_variable_set name, instance_variable_get(name)
  end
end

#turbo_streamObject



136
137
138
# File 'lib/turbo_boost/commands/command.rb', line 136

def turbo_stream
  @turbo_stream ||= Turbo::Streams::TagBuilder.new(controller.view_context)
end