Class: Karst::Spec::Catalog

Inherits:
Object
  • Object
show all
Defined in:
lib/karst/spec/catalog.rb

Overview

Read-only index over the JSON artifact Karst::Spec::Observer writes, answering "what scenarios have been observed for this controller/action" without requiring RSpec, Rails, or the host application's database to be loaded. This is pure data ingestion: JSON primitives in, immutable Scenario objects out. It never deserializes arbitrary Ruby objects, never constantizes a principal type, and never queries anything.

A Catalog is always in exactly one of three states:

  • :missing -- the artifact does not exist yet (the suite has never been run with the observer installed).
  • :invalid -- the artifact exists but could not be read as the expected JSON array (empty file, malformed JSON, or an incompatible top-level shape). error holds a human-readable reason.
  • :ready -- the artifact was a valid JSON array. scenarios may still be empty (a suite that observed zero browser-facing requests), which is deliberately distinct from :missing: "no scenarios cover this route" is not the same claim as "the catalog was never generated."

Malformed individual entries inside an otherwise valid array (a request missing its controller, an example with no requests at all) are skipped individually rather than invalidating the whole artifact. rubocop:disable Metrics/ClassLength

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(status, scenarios, error) ⇒ Catalog

Returns a new instance of Catalog.



163
164
165
166
167
168
169
# File 'lib/karst/spec/catalog.rb', line 163

def initialize(status, scenarios, error)
  @status = status
  @scenarios = scenarios
  @error = error
  @index = build_index(scenarios)
  freeze
end

Instance Attribute Details

#errorObject (readonly)

Returns the value of attribute error.



161
162
163
# File 'lib/karst/spec/catalog.rb', line 161

def error
  @error
end

#scenariosObject (readonly)

Returns the value of attribute scenarios.



161
162
163
# File 'lib/karst/spec/catalog.rb', line 161

def scenarios
  @scenarios
end

#statusObject (readonly)

Returns the value of attribute status.



161
162
163
# File 'lib/karst/spec/catalog.rb', line 161

def status
  @status
end

Class Method Details

.default_pathObject

Rails' own tmp/ convention when available, otherwise a plain relative path so this stays usable from a bare Ruby process (tests, a future CLI) with no Rails loaded.



46
47
48
49
50
# File 'lib/karst/spec/catalog.rb', line 46

def default_path
  return DEFAULT_RELATIVE_PATH unless defined?(Rails) && Rails.respond_to?(:root) && Rails.root

  File.join(Rails.root.to_s, DEFAULT_RELATIVE_PATH)
end

.load(path: default_path) ⇒ Object



52
53
54
# File 'lib/karst/spec/catalog.rb', line 52

def load(path: default_path)
  new(*read(path))
end

Instance Method Details

#ready?Boolean

Returns:

  • (Boolean)


171
172
173
# File 'lib/karst/spec/catalog.rb', line 171

def ready?
  status == :ready
end

#scenarios_for(controller:, action:, http_method: nil) ⇒ Object

Indexed primarily by controller/action -- Rails' own stable routing identity -- so "/things/1" and "/things/2" report as the same capability instead of fragmenting by dynamic id. http_method narrows further for the (uncommon) case where one controller/action legitimately answers more than one verb; omitted, every scenario for that controller/action is returned regardless of method.



181
182
183
184
185
186
187
# File 'lib/karst/spec/catalog.rb', line 181

def scenarios_for(controller:, action:, http_method: nil)
  matches = @index.fetch([controller, action], EMPTY)
  return matches if http_method.nil?

  normalized = http_method.to_s.upcase
  matches.select { |scenario| scenario.http_method == normalized }.freeze
end