Module: RailsAiContext::Install::SelectionRecord

Defined in:
lib/rails_ai_context/install/selection_record.rb

Overview

Which AI tools the user picked, written and read the same way whichever entry point ran. Two records exist because they answer to two owners: the YAML file is the installer's own, and the initializer line is Rails config a user hand-edits. The initializer therefore wins on read.

Reading is a textual parse, never an eval or a boot, so the standalone CLI can answer the question with no Rails in the process.

Constant Summary collapse

YAML_FILE =
".rails-ai-context.yml"
INITIALIZER =
"config/initializers/rails_ai_context.rb"
YAML_KEY =
"ai_tools"
PERMITTED_YAML =

Dates and times because a hand-added generated_at: is ordinary in a config file, and refusing to load one used to cost the user the write.

[ Symbol, Date, Time ].freeze
SELECTION_LINE =

Matches the line the installer writes, and nothing else. Anchored past any leading whitespace but not past a #, so the commented-out default in the generated initializer is not mistaken for a selection.

/^[ \t]*config\.ai_tools\s*=\s*%i\[([^\]]*)\]/
ANY_ASSIGNMENT =

Any assignment to the key, in any shape. A hand-written config.ai_tools = [:claude] is not one this module rewrites, but inserting beside it would leave two assignments: the stale one wins at boot while read returns the fresh one.

/^[ \t]*config\.ai_tools\s*=/
CONFIGURE_BLOCK =

Where a selection line goes when the initializer has none yet.

/RailsAiContext\.configure do \|config\|\n/

Class Method Summary collapse

Class Method Details

.add(tool, root:) ⇒ Object

Adds one tool to whatever is already recorded, for the per-tool context tasks. Reads first so the addition lands in both files together rather than only in whichever one the caller happened to know about.



80
81
82
83
84
85
86
87
88
89
# File 'lib/rails_ai_context/install/selection_record.rb', line 80

def add(tool, root:)
  addition = normalize([ tool ])
  # `json` is a real context format and a real rake task but not an AI
  # tool. Recording the empty union would put `config.ai_tools = %i[]`
  # into the user's initializer and, worse, make the record look set,
  # so the first-run prompt never fires again.
  return { tools: [], yaml: :skipped, initializer: :skipped } if addition.empty?

  write((read(root: root) || []) | addition, root: root)
end

.initializer_line(tools) ⇒ Object



91
92
93
# File 'lib/rails_ai_context/install/selection_record.rb', line 91

def initializer_line(tools)
  "  config.ai_tools = %i[#{normalize(tools).join(' ')}]"
end

.messages(result) ⇒ Array<Array(Symbol, String)>

What a caller should tell the user about a write, as [level, text] pairs. The entries differ in voice - Thor say with a colour, puts with an emoji, $stderr.puts - not in what there is to say, and keeping that judgement here means a new outcome lands in one place rather than three.

Returns:

  • (Array<Array(Symbol, String)>)

    level is :ok, :muted or :warn



72
73
74
# File 'lib/rails_ai_context/install/selection_record.rb', line 72

def messages(result)
  [ yaml_message(result[:yaml]), initializer_message(result[:initializer]) ].compact
end

.read(root:) ⇒ Array<Symbol>?

Returns the recorded tools, or nil if none.

Returns:

  • (Array<Symbol>, nil)

    the recorded tools, or nil if none.



42
43
44
# File 'lib/rails_ai_context/install/selection_record.rb', line 42

def read(root:)
  from_initializer(root) || from_yaml(root)
end

.write(tools, root:, extra_yaml: {}, initializer: true) ⇒ Hash

Records the selection in both places and says what it did, because every entry prints its own "Created / Updated / unchanged" line and would otherwise keep its own copy of the writing just to know which.

Parameters:

  • initializer (Boolean) (defaults to: true)

    false leaves the user's Rails config untouched, for callers refreshing this gem's own YAML on a run the user did not ask to change their selection in.

Returns:

  • (Hash)

    { tools:, yaml: :created|:updated|:unchanged|:replaced|:failed, initializer: :updated|:inserted|:unchanged|:conflict|:absent|:skipped }



55
56
57
58
59
60
61
62
63
# File 'lib/rails_ai_context/install/selection_record.rb', line 55

def write(tools, root:, extra_yaml: {}, initializer: true)
  tools = normalize(tools)

  {
    tools: tools,
    yaml: write_yaml(tools, root, extra_yaml),
    initializer: initializer ? write_initializer(tools, root) : :skipped
  }
end