Class: Silas::Generators::ChannelGenerator

Inherits:
Rails::Generators::NamedBase
  • Object
show all
Defined in:
lib/generators/silas/channel/channel_generator.rb

Overview

rails g silas:channel whatsapp

A channel is two halves that must agree on one name, and hand-rolling them is where the mistakes live: inbound needs signature verification and a stable thread key, outbound needs the approval link to reach an operator. This scaffolds both, wired together, with the security decisions already made.

Instance Method Summary collapse

Instance Method Details

#add_routeObject



45
46
47
# File 'lib/generators/silas/channel/channel_generator.rb', line 45

def add_route
  route %(post "/agent/channels/#{file_name}", to: "agent/channels/#{file_name}#create")
end

#create_channelObject

Templates are .rb.tt (Rails' own convention): the .tt keeps ERB-bearing files out of the linter and off Zeitwerk's radar.



34
35
36
# File 'lib/generators/silas/channel/channel_generator.rb', line 34

def create_channel
  template "channel.rb.tt", "app/agent/channels/#{file_name}.rb"
end

#create_controllerObject

The webhook lives in the HOST app, not the engine: only the host knows the vendor's signature scheme and payload shape. The engine ships routes for Slack alone because it also ships Slack's verification.



41
42
43
# File 'lib/generators/silas/channel/channel_generator.rb', line 41

def create_controller
  template "controller.rb.tt", "app/controllers/agent/channels/#{file_name}_controller.rb"
end

#show_next_stepsObject



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/generators/silas/channel/channel_generator.rb', line 49

def show_next_steps
  say <<~MSG, :green

    Channel "#{file_name}" scaffolded:
      app/agent/channels/#{file_name}.rb                        (outbound: answers + approvals)
      app/controllers/agent/channels/#{file_name}_controller.rb (inbound: webhook)
      config/routes.rb                                        POST /agent/channels/#{file_name}

    Next:
      1. Set the signing secret:
         bin/rails credentials:edit  ->  silas:
                                           #{file_name}:
                                             signing_secret: ...
      2. Fill in the three TODOs — the vendor's signature scheme, how a
         message maps to a thread key, and how to post a message back.
      3. Point the vendor's webhook at https://<your-host>/agent/channels/#{file_name}
      4. Restart: app/agent/ registers at boot.

    Full contract and a worked example: docs/channels.md
  MSG
end

#validate_nameObject

Channel identity is the filename (app/agent/channels/whatsapp.rb -> Agent::Channels::Whatsapp), and Registry#channels resolves it with camelize. A filename that isn't a snake_case identifier produces a constant Zeitwerk can't define, and the channel then fails at boot rather than here — so refuse it here, where the message is useful. (Rails' usual normalisation still applies first: MsTeams and ms_teams both land on ms_teams.)

Raises:

  • (Thor::Error)


24
25
26
27
28
29
30
# File 'lib/generators/silas/channel/channel_generator.rb', line 24

def validate_name
  return if file_name.match?(/\A[a-z_][a-z0-9_]*\z/)

  raise Thor::Error, "#{file_name.inspect} is not a valid channel name — " \
                     "it must be lowercase words separated by underscores, " \
                     "starting with a letter (e.g. whatsapp, ms_teams)."
end