Class: MaquinaStream::Generators::StreamableGenerator

Inherits:
Rails::Generators::NamedBase
  • Object
show all
Includes:
Rails::Generators::Migration
Defined in:
lib/generators/maquina_stream/streamable/streamable_generator.rb

Overview

Makes one model streamable: the migration carrying the columns the contract requires, and the include plus macro in the model.

bin/rails generate maquina_stream:streamable Message

Per model, because a host may have several — an assistant message and a tool call are two streams, not one.

The columns come from MaquinaStream::Streamable itself rather than from a list copied out of the documentation, so a contract that gains a column gains it here too. A column the contract requires and this generator has no definition for raises rather than being quietly left out.

Constant Summary collapse

INITIALIZER =
"config/initializers/maquina_stream.rb"
FIND_STREAM_STUB =

The stub the install generator writes, and what replaces it.

/^(\s*)c\.find_stream = ->\(sid\) \{ raise NotImplementedError.*\}$/
COLUMN_DEFINITIONS =

How to create each column the contract asks for. Keyed by the column name Streamable declares, plus :buffer for the one the host names. Streamable::Generated::REQUIRED_COLUMNS is the source of truth for which columns; this is the source of truth for their shape.

{
  :buffer => {type: :text, default: "", null: false},
  Streamable::SEQUENCE_COLUMN => {type: :integer, default: 0, null: false},
  Streamable::STATUS_COLUMN => {type: :string, default: Streamable::OPEN_STATUS, null: false}
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.next_migration_number(dirname) ⇒ Object



50
51
52
# File 'lib/generators/maquina_stream/streamable/streamable_generator.rb', line 50

def self.next_migration_number(dirname)
  ActiveRecord::Migration.next_migration_number(current_migration_number(dirname) + 1)
end

Instance Method Details

#create_migration_file ⇒ Object



54
55
56
57
58
59
# File 'lib/generators/maquina_stream/streamable/streamable_generator.rb', line 54

def create_migration_file
  return say_status(:skip, "db/migrate: #{table_name} already has every contract column", :yellow) if missing_columns.empty?
  return say_status(:skip, "db/migrate: #{migration_name} already generated", :yellow) if migration_generated?

  migration_template "migration.rb.tt", "db/migrate/#{migration_name}.rb"
end

#create_or_update_model ⇒ Object



61
62
63
64
65
66
67
68
69
# File 'lib/generators/maquina_stream/streamable/streamable_generator.rb', line 61

def create_or_update_model
  if File.exist?(File.join(destination_root, model_path))
    return say_status(:skip, "#{model_path}: already streamable", :yellow) if model_source.include?("MaquinaStream::Streamable")

    inject_into_class model_path, class_name, model_macro
  else
    template "model.rb.tt", model_path
  end
end

#fill_in_find_stream ⇒ Object

The install generator leaves find_stream raising. Now there is a model to point it at — but only the generated stub is ever replaced, so a host that already wrote its own is never clobbered.



74
75
76
77
78
79
80
81
82
83
# File 'lib/generators/maquina_stream/streamable/streamable_generator.rb', line 74

def fill_in_find_stream
  return say_status(:skip, "#{INITIALIZER}: not found — set c.find_stream yourself", :yellow) unless File.exist?(File.join(destination_root, INITIALIZER))

  source = File.read(File.join(destination_root, INITIALIZER))
  unless source.match?(FIND_STREAM_STUB)
    return say_status(:skip, "#{INITIALIZER}: find_stream is not the generated stub", :yellow)
  end

  gsub_file INITIALIZER, FIND_STREAM_STUB, "\\1c.find_stream = ->(sid) { #{class_name}.find_by(id: sid) }"
end

#report_what_is_left ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/generators/maquina_stream/streamable/streamable_generator.rb', line 85

def report_what_is_left
  say ""
  say "#{class_name} streams. What is still yours:", :green
  say ""
  say "  - `stream_for:` in #{model_path} — the Turbo broadcast target."
  say "    Who may subscribe to a stream is your question, not the engine's."
  say "  - `c.authorize` in #{INITIALIZER}, which still denies everything."
  say ""
  say "  Assert the contract in your own suite, so a missing column fails at"
  say "  test time rather than mid-stream:"
  say ""
  say "      assert_empty #{class_name}.maquina_stream_contract_gaps"
  say ""
end