Module: OKF::MCP::Filters

Defined in:
lib/okf/mcp/filters.rb

Overview

The dir vocabulary, in one place.

It lived in two — one copy in the server, one in the memory backend, each commented "the CLI's one rule kept verbatim" — and they drifted: only one of them mapped "" and "/" onto the root, so catalog(dir: "/") reported an empty bundle root while search and dirs answered for it. Two copies of a rule is two answers waiting to disagree, so there is one.

There are genuinely two rules here, and the distinction is not drift:

#under_dir?  filters *concepts*. A dir names itself and everything
           beneath it; `.` is a prefix of nothing, so the root
           selects only what lives directly in it (the CLI's `--dir`).
#within?     scopes a *tree* of directory rows, where the root is the
           ancestor of every row (`dirs`/`index` walking down).

Class Method Summary collapse

Class Method Details

.dir_depth(dir) ⇒ Object



67
68
69
# File 'lib/okf/mcp/filters.rb', line 67

def dir_depth(dir)
  dir == "." ? 0 : dir.split("/").length
end

.fold(value) ⇒ Object



23
24
25
# File 'lib/okf/mcp/filters.rb', line 23

def fold(value)
  value.to_s.downcase
end

.known_dir?(dirs, wanted) ⇒ Boolean

Is wanted a directory this set actually has? Folded, and blind to a trailing slash, so it accepts the spelling the views print back.

Returns:

  • (Boolean)


73
74
75
76
77
78
# File 'lib/okf/mcp/filters.rb', line 73

def known_dir?(dirs, wanted)
  base = normalize_dir(wanted)
  return true if base == "."

  dirs.any? { |dir| fold(dir) == base }
end

.normalize_dir(value) ⇒ Object

A dir argument as the tools compare it: case-folded, trailing slashes gone, and both spellings of the bundle root — "." and "/" — normalized to ".".

A blank argument never reaches here as a filter. Every concept filter short-circuits on OKF.blank? first, because a client that fills each declared optional property with "" is doing something routine and means "no filter" by it — so "" and an omitted dir are the same ask, and neither is a synonym for the root. In the tree scoping the two coincide anyway: . is the ancestor of every row, so a blank normalizes to "." and selects the whole tree.

Deliberately not the CLI's fold_dir, which also folds the literal name "root". Its whole rationale there is that a shell needs no quoting for it; a JSON argument has no such problem, and no dir description on this surface ever advertised the spelling. Importing it cost a bundle with a real root/ directory the ability to name it — dir: "root" answered for the bundle root instead, and scoped_rows skips its existence check for the root, so not even the refusal fired.



46
47
48
49
# File 'lib/okf/mcp/filters.rb', line 46

def normalize_dir(value)
  folded = fold(value).sub(%r{/+\z}, "")
  folded.empty? || folded == "." ? "." : folded
end

.under_dir?(entry_dir, wanted) ⇒ Boolean

Concept filtering: dir names itself and everything beneath it.

Returns:

  • (Boolean)


52
53
54
55
56
# File 'lib/okf/mcp/filters.rb', line 52

def under_dir?(entry_dir, wanted)
  entry = fold(entry_dir)
  path = normalize_dir(wanted)
  entry == path || entry.start_with?("#{path}/")
end

.within?(entry_dir, base) ⇒ Boolean

Tree scoping: the root is the ancestor of every row, so . selects the whole tree rather than only what sits directly in it.

Returns:

  • (Boolean)


60
61
62
63
64
65
# File 'lib/okf/mcp/filters.rb', line 60

def within?(entry_dir, base)
  return true if base == "."

  entry = fold(entry_dir)
  entry == base || entry.start_with?("#{base}/")
end