#
tokens: 41419/50000 22/34 files (page 1/10)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 10. Use http://codebase.md/m-gonzalo/cosa-sai?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitignore
├── bun.lock
├── Dockerfile
├── package.json
├── prompts
│   └── default.txt
├── README.md
├── smithery.yaml
├── src
│   ├── db.ts
│   ├── gemini.ts
│   ├── index.ts
│   ├── logger.ts
│   └── types.ts
├── test
│   ├── docs
│   │   ├── ash-docs
│   │   │   ├── ash_admin.md
│   │   │   ├── ash_appsignal.md
│   │   │   ├── ash_archival.md
│   │   │   ├── ash_authentication_phoenix.md
│   │   │   ├── ash_authentication.md
│   │   │   ├── ash_cloak.md
│   │   │   ├── ash_csv.md
│   │   │   ├── ash_cubdb.md
│   │   │   ├── ash_double_entry.md
│   │   │   ├── ash_graphql.md
│   │   │   ├── ash_json_api.md
│   │   │   ├── ash_money.md
│   │   │   ├── ash_oban.md
│   │   │   ├── ash_phoenix.md
│   │   │   ├── ash_postgres.md
│   │   │   ├── ash_rbac.md
│   │   │   ├── ash_sqlite.md
│   │   │   ├── ash_state_machine.md
│   │   │   └── ash.md
│   │   ├── bun-elysia-docs
│   │   │   ├── bun.sh.md
│   │   │   └── elysiajs.com.md
│   │   ├── javascript-docs
│   │   │   └── sample.md
│   │   └── phoenix-docs
│   │       └── phx-docs.md
│   └── prompts
│       ├── ash-framework.txt
│       ├── bun-elysia.txt
│       ├── javascript.txt
│       └── phoenix.txt
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # dependencies (bun install)
 2 | node_modules
 3 | 
 4 | # output
 5 | out
 6 | dist
 7 | *.tgz
 8 | 
 9 | # code coverage
10 | coverage
11 | *.lcov
12 | 
13 | # logs
14 | logs
15 | *.log
16 | _.log
17 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
18 | 
19 | # dotenv environment variable files
20 | .env
21 | .env.development.local
22 | .env.test.local
23 | .env.production.local
24 | .env.local
25 | 
26 | # caches
27 | .eslintcache
28 | .cache
29 | *.tsbuildinfo
30 | 
31 | # IntelliJ based IDEs
32 | .idea
33 | 
34 | # Finder (MacOS) folder config
35 | .DS_Store
36 | 
37 | # Local DB
38 | gemini_docs.sqlite
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Gemini Docs MCP Server
  2 | 
  3 | [![smithery badge](https://smithery.ai/badge/@M-Gonzalo/cosa-sai)](https://smithery.ai/server/@M-Gonzalo/cosa-sai)
  4 | 
  5 | ## Description
  6 | 
  7 | This project implements an MCP server that enables access to documentation for various technologies using the Gemini API with its gigantic context window of 2M tokens. It should work for any client, but is targeted especially to the Roo/Cline environment.
  8 | 
  9 | This approach offers several advantages over simply browsing the web or using a search engine:
 10 | 
 11 | *   **Access to a curated knowledge base:** The LLM uses a specific set of documentation, avoiding junk results and false positives that can confuse the model.
 12 | *   **Overcomes context window limitations:** By providing the documentation directly, the LLM can access more information than would be possible with web search alone.
 13 | *   **Tailored and well-thought-out responses:** The LLM doesn't just provide snippets from the documentation, but crafts well-reasoned answers that take into consideration the entire specification for the technology in question. This allows for more complex questions like "what alternative ways of doing X are there?" or "is this snippet idiomatic?".
 14 | 
 15 | It also overcomes some problemmatic hurdles of traditional RAG systems:
 16 | 
 17 | *   **No need for chunking:** The LLM can access the entire documentation in one go, without needing to chunk it into smaller pieces, and having to painfully test and choose between all the possible ways of doing so.
 18 | *   **No need for a retriever:** The Gemini API itself serves as a powerful retriever that can access the entire documentation, so there's no need to implement a custom one.
 19 | *   **No vectorization, vector DBs, or other complex systems:** We work directly with plain text, and since we can see everything at once, we don't need vectors for similarity search. If it's relevant, we know about it.
 20 | 
 21 | There are some limitations, though:
 22 | 
 23 | *   **No real-time updates:** The documentation is static and won't be updated in real time. This means that the LLM might not know about the latest features or changes in the technology unless we manually update the documentation or provide an automated way of doing so.
 24 | *   **A lot of tokens is not the same as an infinite context window:** The LLM can only see about 2 million tokens at a time, so it might not be able to see the entire documentation for some technologies. This is especially true for large and complex stacks with copious amounts of documentation.
 25 | *   **It's not that fast:** We're using Gemini 1.5 Pro (not Flash), and we're loading it with a whole bunch of documentation, so it might take a while to get a response. This is especially true for the first query, as the server needs to upload the documentation to the API.
 26 | 
 27 | ## Features
 28 | 
 29 | *   Enables clients to take an "ask your docs" approach to learning and debugging for an arbitrary number of technologies, including some obscure or lesser-known ones.
 30 | *   Uses the Gemini API to answer questions about the documentation.
 31 | *   Supports multiple tools for querying the documentation:
 32 |     *   `can_x_be_done`: Check if a specific task can be done in a given technology.
 33 |     *   `hints_for_problem`: Get hints for solving a specific problem.
 34 |     *   `is_this_good_practice`: Check if a code snippet follows good practices.
 35 |     *   `how_to_do_x`: Get examples and alternative approaches for a specific task.
 36 | *   Provides a logging system for debugging (enabled with the `--verbose` flag).
 37 | 
 38 | ## Getting Started
 39 | 
 40 | ### Installing via Smithery
 41 | 
 42 | To install Gemini Docs Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@M-Gonzalo/cosa-sai):
 43 | 
 44 | ```bash
 45 | npx -y @smithery/cli install @M-Gonzalo/cosa-sai --client claude
 46 | ```
 47 | 
 48 | This MCP server is automatically started and managed by the client. To enable it, you need to configure it in your settings file (for example, `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`). There's usually a button for opening up the settings file in the client.
 49 | 
 50 | Here's the configuration for this server:
 51 | 
 52 | ```json
 53 | {
 54 |   "command": "bun",
 55 |   "args": [
 56 |     "--watch",
 57 |     "path/to/repo/cosa-sai-mcp/src/index.ts",
 58 |     "--verbose"
 59 |   ],
 60 |   "env": {
 61 |     "GEMINI_API_KEY": "<your_gemini_api_key>"
 62 |   },
 63 |   "disabled": false,
 64 |   "alwaysAllow": [
 65 |     "can_x_be_done",
 66 |     "hints_for_problem",
 67 |     "is_this_good_practice",
 68 |     "how_to_do_x"
 69 |   ],
 70 |   "timeout": 60 // in seconds
 71 | }
 72 | ```
 73 | 
 74 | ## Procuring and Sanitizing the Knowledge Base
 75 | 
 76 | This MCP server requires a knowledge base of documentation to answer questions. You must manually procure this knowledge base, either by downloading a public repository, scraping a website, or using other methods.
 77 | 
 78 | An optional sanitation process can be performed to clean up the original documentation from styling and other unnecessary content.
 79 | 
 80 | Here are some basic tools for doing so. Better solutions are encouraged:
 81 | 
 82 | **Naive Scrapper:**
 83 | 
 84 | ```bash
 85 | wget --mirror --convert-links --adjust-extension --page-requisites --no-parent --directory-prefix=./local_copy --no-verbose --show-progress $1
 86 | ```
 87 | 
 88 | **Quick and Dirty Conversor to Markdown-ish:**
 89 | 
 90 | ```bash
 91 | #!/bin/bash
 92 | 
 93 | directory="${1:-.}"  # Default to current directory if no argument is provided
 94 | output_file="${2:-concatenated.md}"  # Default output file name
 95 | 
 96 | echo "Concatenating files in '$directory' into '$output_file'..."
 97 | 
 98 | # Clear output file if it exists
 99 | truncate -s 0 "$output_file"
100 | 
101 | # Find all files (excluding directories) and process them
102 | find "$directory" -type f -name '*.html' | while IFS= read -r file; do
103 |     echo "=== ${file#./} ===" >> "$output_file"
104 |     cat "$file" \
105 |     | grep -v 'base64' \
106 |     | html2markdown >> "$output_file"
107 |     echo -e "\n" >> "$output_file"
108 | done
109 | 
110 | echo "Done! Output saved to '$output_file'"
111 | ```
112 | 
113 | ## Usage
114 | 
115 | This server provides the following tools:
116 | 
117 | *   **can\_x\_be\_done:** Checks if a specific task can be done in a given technology.
118 |     *   **Input:** `docs`, `prompt`, `x`, `technology`
119 |     *   **Output:** `success`, `data`
120 | *   **hints\_for\_problem:** Gets hints for solving a specific problem.
121 |     *   **Input:** `docs`, `prompt`, `problem`, `context`, `environment`
122 |     *   **Output:** `success`, `data`
123 | *   **is\_this\_good\_practice:** Checks if a code snippet follows good practices.
124 |     *   **Input:** `docs`, `prompt`, `snippet`, `context`
125 |     *   **Output:** `success`, `data`
126 | *   **how\_to\_do\_x:** Gets examples and alternative approaches for a specific task.
127 |     *   **Input:** `docs`, `prompt`, `x`, `technology`
128 |     *   **Output:** `success`, `data`
129 | 
130 | ## Contributing
131 | 
132 | Contributions are welcome! Please follow these guidelines:
133 | 
134 | 1.  Fork the repository.
135 | 2.  Create a new branch for your feature or bug fix.
136 | 3.  Make your changes and commit them with descriptive commit messages.
137 | 4.  Submit a pull request.
138 | 
139 | ## License
140 | 
141 | This project is licensed under the MIT License.
142 | 
143 | ## Disclaimer
144 | 
145 | This is a very early version of the project, and it's likely to have bugs and limitations. Please report any issues you find, and feel free to suggest improvements or new features.
```

--------------------------------------------------------------------------------
/test/prompts/javascript.txt:
--------------------------------------------------------------------------------

```
1 | You are an expert JavaScript programmer with extensive knowledge about JavaScript's core features, best practices, and modern development patterns.
```

--------------------------------------------------------------------------------
/test/prompts/phoenix.txt:
--------------------------------------------------------------------------------

```
1 | You are an expert Elixir programmer with deep knowledge about the Phoenix web framework. You understand the intricacies of building scalable, real-time web applications using Phoenix's features and OTP patterns.
```

--------------------------------------------------------------------------------
/test/prompts/bun-elysia.txt:
--------------------------------------------------------------------------------

```
1 | You are an expert programmer with deep knowledge about Bun and the Elysia web framework. You understand the intricacies of building high-performance web applications using Bun's runtime features and Elysia's type-safe approach.
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2021",
 4 |     "module": "ESNext",
 5 |     "moduleResolution": "bundler",
 6 |     "types": ["bun-types"],
 7 |     "allowJs": true,
 8 |     "strict": true,
 9 |     "noEmit": true,
10 |     "allowImportingTsExtensions": true,
11 |     "skipLibCheck": true,
12 |     "jsx": "react-jsx"
13 |   }
14 | }
15 | 
```

--------------------------------------------------------------------------------
/test/prompts/ash-framework.txt:
--------------------------------------------------------------------------------

```
1 | You are an expert Elixir programmer with extensive and detailed knowledge about the Ash application framework. You understand the intricacies of building sophisticated applications using Ash's resource-oriented approach, including domain modeling, data persistence, authorization, and real-time features.
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "gemini-docs-mcp",
 3 |   "module": "index.ts",
 4 |   "type": "module",
 5 |   "private": true,
 6 |   "devDependencies": {
 7 |     "@types/bun": "latest",
 8 |     "@types/js-yaml": "^4.0.9"
 9 |   },
10 |   "peerDependencies": {
11 |     "typescript": "^5"
12 |   },
13 |   "dependencies": {
14 |     "@google/generative-ai": "^0.22.0",
15 |     "@modelcontextprotocol/sdk": "^1.6.0",
16 |     "js-yaml": "^4.1.0",
17 |     "zod": "^3.24.2"
18 |   }
19 | }
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
 2 | FROM oven/bun:latest
 3 | 
 4 | WORKDIR /app
 5 | 
 6 | # Copy the entire repository into a subdirectory to match expected structure
 7 | COPY . /app/gemini-docs-mcp
 8 | 
 9 | # Install dependencies in the subdirectory
10 | WORKDIR /app/gemini-docs-mcp
11 | RUN bun install --ignore-scripts
12 | 
13 | # Set workdir back to /app for running the command
14 | WORKDIR /app
15 | 
16 | CMD ["bun", "/app/gemini-docs-mcp/src/index.ts"]
17 | 
```

--------------------------------------------------------------------------------
/src/logger.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { join } from 'path';
 2 | import type { FileSink } from 'bun';
 3 | 
 4 | export class Logger {
 5 |   private static writer: FileSink | null = null;
 6 |   private static enabled = false;
 7 | 
 8 |   static enable() {
 9 |     this.enabled = true;
10 |     this.writer = Bun.file(join(import.meta.dir, '..', 'debug.log')).writer();
11 |   }
12 | 
13 |   static log(message: string) {
14 |     if (!this.enabled) return;
15 | 
16 |     const timestamp = new Date().toISOString();
17 |     this.writer?.write(`[${timestamp}] ${message}\n`);
18 |     this.writer?.flush();
19 |   }
20 | 
21 |   static close() {
22 |     if (this.writer) {
23 |       this.writer.end();
24 |       this.writer = null;
25 |     }
26 |   }
27 | }
```

--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_appsignal.md:
--------------------------------------------------------------------------------

```markdown
 1 | [![ash_appsignal](assets/logo.png)](AshAppsignal.html)
 2 | 
 3 | [ash\_appsignal](AshAppsignal.html)
 4 | 
 5 | v0.1.3
 6 | 
 7 | - GUIDES
 8 | - Modules
 9 | 
10 | <!--THE END-->
11 | 
12 | <!--THE END-->
13 | 
14 | Search documentation of ash\_appsignal
15 | 
16 | Settings
17 | 
18 | # AshAppsignal (ash\_appsignal v0.1.3)
19 | 
20 | Documentation for [`AshAppsignal`](AshAppsignal.html#content).
21 | 
22 | [Hex Package](https://hex.pm/packages/ash_appsignal/0.1.3) [Hex Preview](https://preview.hex.pm/preview/ash_appsignal/0.1.3) Search HexDocs [Download ePub version](ash_appsignal.epub "ePub version")
23 | 
24 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
25 | 
```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
 2 | 
 3 | startCommand:
 4 |   type: stdio
 5 |   configSchema:
 6 |     # JSON Schema defining the configuration options for the MCP.
 7 |     type: object
 8 |     required:
 9 |       - geminiApiKey
10 |     properties:
11 |       geminiApiKey:
12 |         type: string
13 |         description: API key for authenticating with the Gemini API.
14 |     description: Configuration for Gemini Docs MCP server.
15 |   commandFunction:
16 |     # A JS function that produces the CLI command based on the given config to start the MCP on stdio.
17 |     |-
18 |     (config) => ({ command: 'bun', args: ['/app/gemini-docs-mcp/src/index.ts', '--verbose'], env: { GEMINI_API_KEY: config.geminiApiKey } })
19 |   exampleConfig:
20 |     geminiApiKey: dummy_api_key_here
21 | 
```

--------------------------------------------------------------------------------
/test/docs/javascript-docs/sample.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Sample Documentation
 2 | 
 3 | ## Functions
 4 | 
 5 | ### Array Operations
 6 | 
 7 | `map()` creates a new array populated with the results of calling a provided function on every element in the array.
 8 | 
 9 | ```javascript
10 | const numbers = [1, 2, 3];
11 | const doubled = numbers.map(x => x * 2);
12 | // doubled: [2, 4, 6]
13 | ```
14 | 
15 | `filter()` creates a new array with all elements that pass the test implemented by the provided function.
16 | 
17 | ```javascript
18 | const numbers = [1, 2, 3, 4, 5];
19 | const evens = numbers.filter(x => x % 2 === 0);
20 | // evens: [2, 4]
21 | ```
22 | 
23 | `reduce()` executes a reducer function on each element of the array, resulting in a single output value.
24 | 
25 | ```javascript
26 | const numbers = [1, 2, 3, 4];
27 | const sum = numbers.reduce((acc, curr) => acc + curr, 0);
28 | // sum: 10
29 | ```
30 | 
31 | ## Best Practices
32 | 
33 | ### Array Operations
34 | 
35 | 1. Use `map()` when you want to transform each element in an array
36 | 2. Use `filter()` when you want to select elements that meet certain criteria
37 | 3. Use `reduce()` when you want to combine all elements into a single value
38 | 4. Prefer array methods over loops for better readability and maintainability
39 | 5. Chain array methods for complex operations instead of nesting them
```

--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // Base file record types
 2 | export interface FileRecord {
 3 |   file_path: string;
 4 |   file_id: string;
 5 |   file_uri: string;
 6 | }
 7 | 
 8 | // Gemini API types for file handling
 9 | export interface GeminiFileData {
10 |   fileUri: string;
11 | }
12 | 
13 | export interface GeminiContentPart {
14 |   text?: string;
15 |   fileData?: GeminiFileData;
16 | }
17 | 
18 | export interface GeminiContent {
19 |   parts: GeminiContentPart[];
20 | }
21 | 
22 | // Response types for different queries
23 | export interface GeminiResponse {
24 |   success: boolean;
25 |   data: CanXBeResponse | HowToDoResponse | HintsProblemResponse | GoodPracticeResponse;
26 | }
27 | 
28 | export interface CanXBeResponse {
29 |   yes?: {
30 |     example: string;
31 |     betterApproach?: string;
32 |   };
33 |   no?: {
34 |     reason: string;
35 |     alternative: string;
36 |   };
37 | }
38 | 
39 | export interface HowToDoResponse {
40 |   examples: Array<{
41 |     content: string;
42 |     description: string;
43 |   }>;
44 |   alternativeApproaches: Array<{
45 |     description: string;
46 |     content: string;
47 |   }>;
48 | }
49 | 
50 | export interface HintsProblemResponse {
51 |   cantSayForSure?: {
52 |     topics: string[];
53 |   };
54 |   try?: Array<{
55 |     possibleReason: string;
56 |     possibleSolution: string;
57 |   }>;
58 |   seeAlso?: string[];
59 | }
60 | 
61 | export interface GoodPracticeResponse {
62 |   isGoodPractice: {
63 |     yes?: {
64 |       cosmeticImprovements?: string[];
65 |     };
66 |     no?: {
67 |       betterAlternatives: string[];
68 |     };
69 |   };
70 | }
71 | 
72 | // Tool parameter types
73 | export interface ToolParams {
74 |   docs: string;
75 |   prompt: string;
76 | }
77 | 
78 | export interface CanXBeDoneParams extends ToolParams {
79 |   x: string;
80 |   technology: string;
81 | }
82 | 
83 | export interface HowToDoXParams extends ToolParams {
84 |   x: string;
85 |   technology: string;
86 | }
87 | 
88 | export interface HintsForProblemParams extends ToolParams {
89 |   problem: string;
90 |   context: string;
91 |   environment: Record<string, unknown>;
92 | }
93 | 
94 | export interface IsThisGoodPracticeParams extends ToolParams {
95 |   snippet: string;
96 |   context: string;
97 | }
```

--------------------------------------------------------------------------------
/prompts/default.txt:
--------------------------------------------------------------------------------

```
 1 | When answering queries:
 2 | 
 3 | 1. Ground your responses in the provided documentation
 4 |    - Base your answers on the actual documentation content
 5 |    - Stay within the parameters of what the docs describe
 6 |    - If multiple approaches exist in the docs, present them all
 7 | 
 8 | 2. Be comprehensive and detailed
 9 |    - Provide complete, working examples
10 |    - Include explanations for key concepts
11 |    - Cover edge cases and considerations
12 |    - Explain the rationale behind recommendations
13 | 
14 | 3. Format responses in YAML matching the query type structure:
15 |    - can_x_be_done: {success, yes:{example, betterApproach} | no:{reason, alternative}}
16 |    - how_to_do_x: {success, examples[], alternativeApproaches[]}
17 |    - hints_for_problem: {success, cantSayForSure:{topics[]} | try:[{possibleReason, possibleSolution}], seeAlso[]}
18 |    - is_this_good_practice: {success, isGoodPractice:{yes:{cosmeticImprovements[]} | no:{betterAlternatives[]}}}
19 | 
20 | Format all responses as YAML with this structure for different query types:
21 | 
22 | For "can X be done":
23 | success: true
24 | data:
25 |   yes:
26 |     example: "<code example>"
27 |     betterApproach: "<optional better approach>"
28 |   # OR
29 |   no:
30 |     reason: "<why it can't be done>"
31 |     alternative: "<alternative solution>"
32 | 
33 | For "how to do X":
34 | success: true
35 | data:
36 |   examples: 
37 |     - content: "<code example 1>"
38 |       description: "<description 1>"
39 |     - content: "<code example 2>"
40 |       description: "<description 2>"
41 |   alternativeApproaches:
42 |     - description: "<approach description 1>"
43 |       content: "<approach code 1>"
44 |     - description: "<approach description 2>"
45 |       content: "<approach code 2>"
46 | 
47 | For "hints for problem":
48 | success: true
49 | data:
50 |   cantSayForSure:
51 |     topics:
52 |     - "<topic 1>"
53 |     - "<topic 2>"
54 |   # OR
55 |   try:
56 |   - possibleReason: "<reason 1>"
57 |     possibleSolution: "<solution 1>"
58 |   - possibleReason: "<reason 2>"
59 |     possibleSolution: "<solution 2>"
60 |   seeAlso:
61 |   - "<related topic 1>"
62 |   - "<related topic 2>"
63 | 
64 | For "is this good practice":
65 | success: true
66 | data:
67 |   isGoodPractice:
68 |     yes:
69 |       cosmeticImprovements:
70 |       - "<improvement 1>"
71 |       - "<improvement 2>"
72 |     # OR
73 |     no:
74 |       betterAlternatives:
75 |       - "<alternative 1>"
76 |       - "<alternative 2>"
77 | 
78 | IMPORTANT: All code snippets must be properly escaped strings in YAML. Do not use literal blocks or code fences.
79 | 
80 | Remember: Keep your response as a clean YAML document with no extra formatting.
```

--------------------------------------------------------------------------------
/src/db.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Database } from 'bun:sqlite';
  2 | import type { FileRecord } from './types';
  3 | import { join, resolve } from 'path';
  4 | import { Logger } from './logger';
  5 | 
  6 | export class GeminiDocsDB {
  7 |   private db: Database;
  8 |   private dbPath: string;
  9 | 
 10 |   constructor() {
 11 |     try {
 12 |       this.dbPath = resolve(join(import.meta.dir, '..', 'gemini_docs.sqlite'));
 13 |       this.db = new Database(this.dbPath, { create: true });
 14 |       this.init();
 15 |     } catch (error) {
 16 |       console.error('[DB] Failed to initialize database:', error);
 17 |       Logger.log(`[DBDebug] Failed to initialize database: ${error}`);
 18 |       throw error;
 19 |     }
 20 |   }
 21 | 
 22 |   private readonly TABLE_SQL = `
 23 |     CREATE TABLE gemini_files (
 24 |       file_id TEXT PRIMARY KEY,
 25 |       file_path TEXT NOT NULL,
 26 |       file_uri TEXT NOT NULL,
 27 |       created_at INTEGER NOT NULL DEFAULT (unixepoch()),
 28 |       UNIQUE(file_path)
 29 |     )
 30 |   `;
 31 | 
 32 |   private migrateSchema() {
 33 |     Logger.log('[DBDebug] Schema migration not implemented - using existing schema');
 34 |   }
 35 | 
 36 |   private init() {
 37 |     try {
 38 |       const stmt = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='gemini_files'");
 39 |       if (stmt.get()) {
 40 |         Logger.log('[DBDebug] Found existing table, migrating schema...');
 41 |         this.migrateSchema();
 42 |       } else {
 43 |         Logger.log('[DBDebug] Creating new database with latest schema...');
 44 |         this.db.run(this.TABLE_SQL);
 45 |       }
 46 |       Logger.log('[DBDebug] Database schema initialized/verified');
 47 |     } catch (error) {
 48 |       console.error('[DB] Error during initialization:', error);
 49 |       Logger.log(`[DBDebug] Error during initialization: ${error}`);
 50 |       throw error;
 51 |     }
 52 |   }
 53 | 
 54 |   async saveFileId(
 55 |     filePath: string, 
 56 |     fileId: string, 
 57 |     fileUri: string
 58 |   ): Promise<void> {
 59 |     try {
 60 |       const stmt = this.db.prepare(
 61 |         'INSERT OR REPLACE INTO gemini_files (file_id, file_path, file_uri) VALUES (?, ?, ?)'
 62 |       );
 63 |       stmt.run(fileId, filePath, fileUri);
 64 |       Logger.log(`[DBDebug] Saved/Updated file record:
 65 |         ID: ${fileId}
 66 |         Path: ${filePath}
 67 |         URI: ${fileUri}`);
 68 |     } catch (error) {
 69 |       Logger.log(`[DBDebug] Error saving file record:
 70 |         Error: ${error}`);
 71 |       console.error('[DB] Error saving file:', error);
 72 |       throw error;
 73 |     }
 74 |   }
 75 | 
 76 |   async getFile(fileHash: string): Promise<{
 77 |     fileUri: string;
 78 |   } | null> {
 79 |     try {
 80 |       const stmt = this.db.prepare(
 81 |         'SELECT file_uri FROM gemini_files WHERE file_id = ? LIMIT 1'
 82 |       );
 83 |       const result = stmt.get(fileHash) as {
 84 |         file_uri: string;
 85 |       } | null;
 86 |       
 87 |       if (result) {
 88 |         Logger.log(`[DBDebug] Retrieved file record for hash ${fileHash}:
 89 |           Hash: ${fileHash}
 90 |           URI: ${result.file_uri}`);
 91 |       }
 92 |       return result
 93 |         ? {
 94 |             fileUri: result.file_uri
 95 |           }
 96 |         : null;
 97 |     } catch (error) {
 98 |       Logger.log(`[DBDebug] Error retrieving file record for ${fileHash}: ${error}`);
 99 |       console.error('[DB] Error getting file:', error);
100 |       throw error;
101 |     }
102 |   }
103 | 
104 |   async getAllFiles(): Promise<FileRecord[]> {
105 |     try {
106 |       const stmt = this.db.prepare(
107 |         'SELECT file_path, file_id, file_uri FROM gemini_files ORDER BY file_id, file_path'
108 |       );
109 |       Logger.log('[DBDebug] Retrieved all file records from database');
110 |       return stmt.all() as FileRecord[];
111 |     } catch (error) {
112 |       console.error('[DB] Error getting files:', error);
113 |       Logger.log(`[DBDebug] Error retrieving all file records: ${error}`);
114 |       throw error;
115 |     }
116 |   }
117 | 
118 |   close(): void {
119 |     this.db.close();
120 |   }
121 | }
```

--------------------------------------------------------------------------------
/src/gemini.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { GoogleGenerativeAI } from '@google/generative-ai';
  2 | import { load } from 'js-yaml';
  3 | import { join } from 'path';
  4 | import { readFileSync } from 'fs';
  5 | import type { GeminiResponse, GeminiContent, GeminiContentPart, GeminiFileData } from './types';
  6 | 
  7 | // Error codes that should trigger a retry
  8 | const RETRYABLE_STATUS_CODES = new Set(['429', '503', '500', '502', '504']);
  9 | 
 10 | export class GeminiAPI {
 11 |   private gemini: GoogleGenerativeAI;
 12 |   private model: any; 
 13 |   private defaultPrompt: string;
 14 |   private rateLimitDelay = 1000;
 15 |   private maxRetries = 3;
 16 |   private lastRequestTime = 0;
 17 | 
 18 |   constructor() {
 19 |     const API_KEY = process.env.GEMINI_API_KEY;
 20 |     if (!API_KEY) {
 21 |       throw new Error('GEMINI_API_KEY environment variable is required');
 22 |     }
 23 | 
 24 |     try {
 25 |       this.gemini = new GoogleGenerativeAI(API_KEY);
 26 |       this.model = this.gemini.getGenerativeModel({ model: 'gemini-1.5-pro' });
 27 |       // Load default prompt using full path from project root
 28 |       const defaultPromptPath = join(process.cwd(), 'gemini-docs-mcp', 'prompts', 'default.txt');
 29 |       this.defaultPrompt = readFileSync(defaultPromptPath, 'utf-8');
 30 |     } catch (error) {
 31 |       console.error('[Gemini] Failed to initialize:', error);
 32 |       throw error;
 33 |     }
 34 |   }
 35 | 
 36 |   private async waitForRateLimit() {
 37 |     const now = Date.now();
 38 |     const timeSinceLastRequest = now - this.lastRequestTime;
 39 |     
 40 |     if (timeSinceLastRequest < this.rateLimitDelay) {
 41 |       await Bun.sleep(this.rateLimitDelay - timeSinceLastRequest);
 42 |     }
 43 |     
 44 |     this.lastRequestTime = Date.now();
 45 |   }
 46 | 
 47 |   private shouldRetry(error: any): boolean {
 48 |     // Check if error message includes status code
 49 |     for (const code of RETRYABLE_STATUS_CODES) {
 50 |       if (error.message?.includes(`[${code}`)) {
 51 |         return true;
 52 |       }
 53 |     }
 54 | 
 55 |     // Check for quota or availability keywords
 56 |     return (
 57 |       error.message?.includes('quota') ||
 58 |       error.message?.includes('unavailable') ||
 59 |       error.message?.includes('try again')
 60 |     );
 61 |   }
 62 | 
 63 |   private formatPrompt(
 64 |     query: string, 
 65 |     docRefs: GeminiFileData[], 
 66 |     context: Record<string, any> = {}, 
 67 |     promptFile?: string
 68 |   ): GeminiContent[] {
 69 |     let prompt = '';
 70 | 
 71 |     // Add custom prompt if provided
 72 |     if (promptFile) {
 73 |       prompt = readFileSync(promptFile, 'utf-8') + '\n\n';
 74 |     }
 75 | 
 76 |     // Add default prompt and query
 77 |     prompt += `${this.defaultPrompt}\n\nUSER QUERY: ${query}\nCONTEXT: ${JSON.stringify(context)}`;
 78 |     const parts: GeminiContentPart[] = [{ text: prompt.trim() }];
 79 | 
 80 |     for (const doc of docRefs) {
 81 |       parts.push({ fileData: doc });
 82 |     }
 83 | 
 84 |     return [{ parts }];
 85 |   }
 86 | 
 87 |   private async retryWithBackoff<T>(
 88 |     operation: () => Promise<T>,
 89 |     retries = this.maxRetries,
 90 |     initialDelay = this.rateLimitDelay
 91 |   ): Promise<T> {
 92 |     let lastError: Error;
 93 |     let delay = initialDelay;
 94 | 
 95 |     for (let i = 0; i < retries; i++) {
 96 |       try {
 97 |         await this.waitForRateLimit();
 98 |         return await operation();
 99 |       } catch (error: any) {
100 |         lastError = error;
101 |         
102 |         if (this.shouldRetry(error)) {
103 |           console.error(`[Gemini] Request failed (attempt ${i + 1}/${retries}), retrying in ${delay}ms:`, error.message);
104 |           await Bun.sleep(delay);
105 |           // Use more aggressive backoff for service issues
106 |           delay *= 3;
107 |           continue;
108 |         }
109 |         
110 |         throw error;
111 |       }
112 |     }
113 | 
114 |     throw lastError!;
115 |   }
116 | 
117 |   private async query(contents: GeminiContent[]): Promise<string> {
118 |     return await this.retryWithBackoff(async () => {
119 |       const response = await this.model.generateContent({ contents });
120 |       return response.response.text();
121 |     });
122 |   }
123 | 
124 |   private parseResponse(text: string): GeminiResponse {
125 |     try {
126 |       // Extract just the YAML portion by finding the first "success: true"
127 |       let yamlContent = text;
128 |       const successIndex = text.indexOf('success: true');
129 |       if (successIndex !== -1) {
130 |         yamlContent = text.slice(successIndex);
131 |       }
132 | 
133 |       // Further clean up the text
134 |       yamlContent = yamlContent
135 |         .split('\n')
136 |         .map(line => line.replace(/^```\s*ya?ml\s*$/, '').replace(/^```\s*$/, ''))
137 |         .join('\n')
138 |         .trim();
139 | 
140 |       const response = load(yamlContent) as GeminiResponse;
141 |       
142 |       if (!response.success || !response.data) {
143 |         throw new Error('Invalid response format');
144 |       }
145 | 
146 |       return response;
147 |     } catch (error) {
148 |       console.error('[Gemini] Failed to parse response:', error);
149 |       throw error;
150 |     }
151 |   }
152 | 
153 |   async canXBeDone(
154 |     docRefs: GeminiFileData[],
155 |     x: string,
156 |     technology: string,
157 |     prompt?: string
158 |   ): Promise<GeminiResponse> {
159 |     const contents = this.formatPrompt(
160 |       `Can I "${x}" in ${technology}? Show example if possible.`,
161 |       docRefs,
162 |       { technology },
163 |       prompt
164 |     );
165 | 
166 |     const result = await this.query(contents);
167 |     return this.parseResponse(result);
168 |   }
169 | 
170 |   async howToDoX(
171 |     docRefs: GeminiFileData[],
172 |     x: string,
173 |     technology: string,
174 |     prompt?: string
175 |   ): Promise<GeminiResponse> {
176 |     const contents = this.formatPrompt(
177 |       `How do I "${x}" in ${technology}? Show multiple examples and alternative approaches if available.`,
178 |       docRefs,
179 |       { technology },
180 |       prompt
181 |     );
182 | 
183 |     const result = await this.query(contents);
184 |     return this.parseResponse(result);
185 |   }
186 | 
187 |   async hintsForProblem(
188 |     docRefs: GeminiFileData[],
189 |     problem: string,
190 |     context: string,
191 |     environment: Record<string, unknown>,
192 |     prompt?: string
193 |   ): Promise<GeminiResponse> {
194 |     const contents = this.formatPrompt(
195 |       problem,
196 |       docRefs,
197 |       { context, environment },
198 |       prompt
199 |     );
200 | 
201 |     const result = await this.query(contents);
202 |     return this.parseResponse(result);
203 |   }
204 | 
205 |   async isThisGoodPractice(
206 |     docRefs: GeminiFileData[],
207 |     snippet: string,
208 |     context: string,
209 |     prompt?: string
210 |   ): Promise<GeminiResponse> {
211 |     const contents = this.formatPrompt(
212 |       `Is this a good practice:\n${snippet}\nContext: ${context}`,
213 |       docRefs,
214 |       { context },
215 |       prompt
216 |     );
217 | 
218 |     const result = await this.query(contents);
219 |     return this.parseResponse(result);
220 |   }
221 | }
```

--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_csv.md:
--------------------------------------------------------------------------------

```markdown
  1 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv)
  2 | 
  3 | [ash\_csv](https://github.com/ash-project/ash_csv)
  4 | 
  5 | v0.9.7
  6 | 
  7 | - Pages
  8 | - Modules
  9 | 
 10 | <!--THE END-->
 11 | 
 12 | <!--THE END-->
 13 | 
 14 | Search documentation of ash\_csv
 15 | 
 16 | Settings
 17 | 
 18 | # [View Source](https://github.com/ash-project/ash_csv "View Source") API Reference ash\_csv v0.9.7
 19 | 
 20 | ## [](api-reference.html#modules)Modules
 21 | 
 22 | [AshCsv](AshCsv.html)
 23 | 
 24 | A CSV datalayer for the Ash framework
 25 | 
 26 | [AshCsv.DataLayer](AshCsv.DataLayer.html)
 27 | 
 28 | The data layer implementation for AshCsv
 29 | 
 30 | [AshCsv.DataLayer.Info](AshCsv.DataLayer.Info.html)
 31 | 
 32 | Introspection helpers for AshCsv.DataLayer
 33 | 
 34 | [Next Page → Home](readme.html)
 35 | 
 36 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) Search HexDocs [Download ePub version](ash_csv.epub "ePub version")
 37 | 
 38 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 39 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv)
 40 | 
 41 | [ash\_csv](https://github.com/ash-project/ash_csv)
 42 | 
 43 | v0.9.7
 44 | 
 45 | - Pages
 46 | - Modules
 47 | 
 48 | <!--THE END-->
 49 | 
 50 | <!--THE END-->
 51 | 
 52 | Search documentation of ash\_csv
 53 | 
 54 | Settings
 55 | 
 56 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L1 "View Source") AshCsv.DataLayer (ash\_csv v0.9.7)
 57 | 
 58 | The data layer implementation for AshCsv
 59 | 
 60 | # [](AshCsv.DataLayer.html#summary)Summary
 61 | 
 62 | ## [Functions](AshCsv.DataLayer.html#functions)
 63 | 
 64 | [columns(resource)](AshCsv.DataLayer.html#columns/1) deprecated
 65 | 
 66 | See [`AshCsv.DataLayer.Info.columns/1`](AshCsv.DataLayer.Info.html#columns/1).
 67 | 
 68 | [create?(resource)](AshCsv.DataLayer.html#create?/1) deprecated
 69 | 
 70 | See [`AshCsv.DataLayer.Info.create?/1`](AshCsv.DataLayer.Info.html#create?/1).
 71 | 
 72 | [file(resource)](AshCsv.DataLayer.html#file/1) deprecated
 73 | 
 74 | See [`AshCsv.DataLayer.Info.file/1`](AshCsv.DataLayer.Info.html#file/1).
 75 | 
 76 | [filter\_matches(records, filter, domain)](AshCsv.DataLayer.html#filter_matches/3)
 77 | 
 78 | [header?(resource)](AshCsv.DataLayer.html#header?/1) deprecated
 79 | 
 80 | See [`AshCsv.DataLayer.Info.header?/1`](AshCsv.DataLayer.Info.html#header?/1).
 81 | 
 82 | [separator(resource)](AshCsv.DataLayer.html#separator/1) deprecated
 83 | 
 84 | See [`AshCsv.DataLayer.Info.separator/1`](AshCsv.DataLayer.Info.html#separator/1).
 85 | 
 86 | # [](AshCsv.DataLayer.html#functions)Functions
 87 | 
 88 | [Link to this function](AshCsv.DataLayer.html#columns/1 "Link to this function")
 89 | 
 90 | # columns(resource)
 91 | 
 92 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L73 "View Source")
 93 | 
 94 | This function is deprecated. See \`AshCsv.DataLayer.Info.columns/1.
 95 | 
 96 | See [`AshCsv.DataLayer.Info.columns/1`](AshCsv.DataLayer.Info.html#columns/1).
 97 | 
 98 | [Link to this function](AshCsv.DataLayer.html#create?/1 "Link to this function")
 99 | 
100 | # create?(resource)
101 | 
102 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L82 "View Source")
103 | 
104 | This function is deprecated. See \`AshCsv.DataLayer.Info.create?/1.
105 | 
106 | See [`AshCsv.DataLayer.Info.create?/1`](AshCsv.DataLayer.Info.html#create?/1).
107 | 
108 | [Link to this function](AshCsv.DataLayer.html#file/1 "Link to this function")
109 | 
110 | # file(resource)
111 | 
112 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L70 "View Source")
113 | 
114 | This function is deprecated. See \`AshCsv.DataLayer.Info.file/1.
115 | 
116 | See [`AshCsv.DataLayer.Info.file/1`](AshCsv.DataLayer.Info.html#file/1).
117 | 
118 | [Link to this function](AshCsv.DataLayer.html#filter_matches/3 "Link to this function")
119 | 
120 | # filter\_matches(records, filter, domain)
121 | 
122 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L397 "View Source")
123 | 
124 | [Link to this function](AshCsv.DataLayer.html#header?/1 "Link to this function")
125 | 
126 | # header?(resource)
127 | 
128 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L79 "View Source")
129 | 
130 | This function is deprecated. See \`AshCsv.DataLayer.Info.header?/1.
131 | 
132 | See [`AshCsv.DataLayer.Info.header?/1`](AshCsv.DataLayer.Info.html#header?/1).
133 | 
134 | [Link to this function](AshCsv.DataLayer.html#separator/1 "Link to this function")
135 | 
136 | # separator(resource)
137 | 
138 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L76 "View Source")
139 | 
140 | This function is deprecated. See \`AshCsv.DataLayer.Info.separator/1.
141 | 
142 | See [`AshCsv.DataLayer.Info.separator/1`](AshCsv.DataLayer.Info.html#separator/1).
143 | 
144 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) Search HexDocs [Download ePub version](ash_csv.epub "ePub version")
145 | 
146 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
147 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv)
148 | 
149 | [ash\_csv](https://github.com/ash-project/ash_csv)
150 | 
151 | v0.9.7
152 | 
153 | - Pages
154 | - Modules
155 | 
156 | <!--THE END-->
157 | 
158 | <!--THE END-->
159 | 
160 | Search documentation of ash\_csv
161 | 
162 | Settings
163 | 
164 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L1 "View Source") AshCsv.DataLayer.Info (ash\_csv v0.9.7)
165 | 
166 | Introspection helpers for AshCsv.DataLayer
167 | 
168 | # [](AshCsv.DataLayer.Info.html#summary)Summary
169 | 
170 | ## [Functions](AshCsv.DataLayer.Info.html#functions)
171 | 
172 | [columns(resource)](AshCsv.DataLayer.Info.html#columns/1)
173 | 
174 | [create?(resource)](AshCsv.DataLayer.Info.html#create?/1)
175 | 
176 | [file(resource)](AshCsv.DataLayer.Info.html#file/1)
177 | 
178 | [header?(resource)](AshCsv.DataLayer.Info.html#header?/1)
179 | 
180 | [separator(resource)](AshCsv.DataLayer.Info.html#separator/1)
181 | 
182 | # [](AshCsv.DataLayer.Info.html#functions)Functions
183 | 
184 | [Link to this function](AshCsv.DataLayer.Info.html#columns/1 "Link to this function")
185 | 
186 | # columns(resource)
187 | 
188 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L12 "View Source")
189 | 
190 | [Link to this function](AshCsv.DataLayer.Info.html#create?/1 "Link to this function")
191 | 
192 | # create?(resource)
193 | 
194 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L24 "View Source")
195 | 
196 | [Link to this function](AshCsv.DataLayer.Info.html#file/1 "Link to this function")
197 | 
198 | # file(resource)
199 | 
200 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L6 "View Source")
201 | 
202 | [Link to this function](AshCsv.DataLayer.Info.html#header?/1 "Link to this function")
203 | 
204 | # header?(resource)
205 | 
206 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L20 "View Source")
207 | 
208 | [Link to this function](AshCsv.DataLayer.Info.html#separator/1 "Link to this function")
209 | 
210 | # separator(resource)
211 | 
212 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L16 "View Source")
213 | 
214 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) Search HexDocs [Download ePub version](ash_csv.epub "ePub version")
215 | 
216 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
217 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv)
218 | 
219 | [ash\_csv](https://github.com/ash-project/ash_csv)
220 | 
221 | v0.9.7
222 | 
223 | - Pages
224 | - Modules
225 | 
226 | <!--THE END-->
227 | 
228 | <!--THE END-->
229 | 
230 | Search documentation of ash\_csv
231 | 
232 | Settings
233 | 
234 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv.ex#L1 "View Source") AshCsv (ash\_csv v0.9.7)
235 | 
236 | A CSV datalayer for the Ash framework
237 | 
238 | For DSL documentation, see [`AshCsv.DataLayer`](AshCsv.DataLayer.html)
239 | 
240 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) Search HexDocs [Download ePub version](ash_csv.epub "ePub version")
241 | 
242 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
243 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv)
244 | 
245 | [ash\_csv](https://github.com/ash-project/ash_csv)
246 | 
247 | v0.9.7
248 | 
249 | - Pages
250 | - Modules
251 | 
252 | <!--THE END-->
253 | 
254 | <!--THE END-->
255 | 
256 | Search documentation of ash\_csv
257 | 
258 | Settings
259 | 
260 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/documentation/dsls/DSL:-AshCsv.DataLayer.md#L1 "View Source") DSL: AshCsv.DataLayer
261 | 
262 | The data layer implementation for AshCsv
263 | 
264 | ## [](dsl-ashcsv-datalayer.html#csv)csv
265 | 
266 | ### [](dsl-ashcsv-datalayer.html#examples)Examples
267 | 
268 | ```
269 | csv do
270 |   file "priv/data/tags.csv"
271 |   create? true
272 |   header? true
273 |   separator '-'
274 |   columns [:id, :name]
275 | end
276 | ```
277 | 
278 | ### [](dsl-ashcsv-datalayer.html#options)Options
279 | 
280 | NameTypeDefaultDocs[`file`](dsl-ashcsv-datalayer.html#csv-file)`String.t`The file to read the data from[`create?`](dsl-ashcsv-datalayer.html#csv-create?)`boolean``false`Whether or not the file should be created if it does not exist (this will only happen on writes)[`header?`](dsl-ashcsv-datalayer.html#csv-header?)`boolean``false`If the csv file has a header that should be skipped[`separator`](dsl-ashcsv-datalayer.html#csv-separator)`any``44`The separator to use, defaults to a comma. Pass in a character (not a string).[`columns`](dsl-ashcsv-datalayer.html#csv-columns)`any`The order that the attributes appear in the columns of the CSV
281 | 
282 | [← Previous Page Getting Started with CSV](getting-started-with-ash-csv.html)
283 | 
284 | [Next Page → Change Log](changelog.html)
285 | 
286 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) ([current file](https://preview.hex.pm/preview/ash_csv/0.9.7/show/documentation/dsls/DSL:-AshCsv.DataLayer.md)) Search HexDocs [Download ePub version](ash_csv.epub "ePub version")
287 | 
288 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
289 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv)
290 | 
291 | [ash\_csv](https://github.com/ash-project/ash_csv)
292 | 
293 | v0.9.7
294 | 
295 | - Pages
296 | - Modules
297 | 
298 | <!--THE END-->
299 | 
300 | <!--THE END-->
301 | 
302 | Search documentation of ash\_csv
303 | 
304 | Settings
305 | 
306 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/documentation/tutorials/getting-started-with-ash-csv.md#L1 "View Source") Getting Started with CSV
307 | 
308 | AshCsv offers basic support for storing and reading resources from csv files.
309 | 
310 | ## [](getting-started-with-ash-csv.html#installation)Installation
311 | 
312 | Add `ash_csv` to your list of dependencies in `mix.exs`:
313 | 
314 | ```
315 | {:ash_csv, "~> 0.9.7-rc.0"}
316 | ```
317 | 
318 | For information on how to configure it, see the [DSL documentation.](dsl-ashcsv-datalayer.html)
319 | 
320 | [← Previous Page Home](readme.html)
321 | 
322 | [Next Page → DSL: AshCsv.DataLayer](dsl-ashcsv-datalayer.html)
323 | 
324 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) ([current file](https://preview.hex.pm/preview/ash_csv/0.9.7/show/documentation/tutorials/getting-started-with-ash-csv.md)) Search HexDocs [Download ePub version](ash_csv.epub "ePub version")
325 | 
326 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
327 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv)
328 | 
329 | [ash\_csv](https://github.com/ash-project/ash_csv)
330 | 
331 | v0.9.7
332 | 
333 | - Pages
334 | - Modules
335 | 
336 | <!--THE END-->
337 | 
338 | <!--THE END-->
339 | 
340 | Search documentation of ash\_csv
341 | 
342 | Settings
343 | 
344 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/README.md#L1 "View Source") Home
345 | 
346 | ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only)
347 | 
348 | ![Elixir CI](https://github.com/ash-project/ash_csv/workflows/CI/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Hex version badge](https://img.shields.io/hexpm/v/ash_csv.svg)](https://hex.pm/packages/ash_csv) [![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](../ash_csv.html)
349 | 
350 | # AshCsv
351 | 
352 | Welcome! This is the CSV Data Layer for [Ash Framework](../ash.html).
353 | 
354 | ## [](readme.html#tutorials)Tutorials
355 | 
356 | - [Getting Started with AshCsv](getting-started-with-ash-csv.html)
357 | 
358 | ## [](readme.html#reference)Reference
359 | 
360 | - [AshCsv.DataLayer DSL](dsl-ashcsv-datalayer.html)
361 | 
362 | [← Previous Page API Reference](api-reference.html)
363 | 
364 | [Next Page → Getting Started with CSV](getting-started-with-ash-csv.html)
365 | 
366 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) ([current file](https://preview.hex.pm/preview/ash_csv/0.9.7/show/README.md)) Search HexDocs [Download ePub version](ash_csv.epub "ePub version")
367 | 
368 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
369 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env bun
  2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  4 | import {  ListToolsRequestSchema, CallToolRequestSchema, Tool } from '@modelcontextprotocol/sdk/types.js';
  5 | import { GoogleAIFileManager } from '@google/generative-ai/server';
  6 | import { z } from 'zod';
  7 | import { readdirSync, readFileSync } from 'fs';
  8 | import { join, resolve } from 'path';
  9 | import { GeminiAPI } from './gemini';
 10 | import { GeminiDocsDB } from './db';
 11 | import { Logger } from './logger';
 12 | import type { CanXBeDoneParams, HowToDoXParams, HintsForProblemParams, IsThisGoodPracticeParams, GeminiFileData } from './types';
 13 | 
 14 | // Tool names as constants to ensure consistency
 15 | const TOOL_NAMES = {
 16 |   CAN_X_BE_DONE: 'can_x_be_done',
 17 |   HOW_TO_DO_X: 'how_to_do_x',
 18 |   HINTS_FOR_PROBLEM: 'hints_for_problem',
 19 |   IS_THIS_GOOD_PRACTICE: 'is_this_good_practice'
 20 | } as const;
 21 | 
 22 | const canXBeDoneSchema = z.object({
 23 |   docs: z.string().describe('Path to documentation directory'),
 24 |   prompt: z.string().describe('Path to prompt file'),
 25 |   x: z.string().describe('The task to check'),
 26 |   technology: z.string().describe('The technology (programming language/framework/tool)'),
 27 | });
 28 | 
 29 | const howToDoXSchema = z.object({
 30 |   docs: z.string().describe('Path to documentation directory'),
 31 |   prompt: z.string().describe('Path to prompt file'),
 32 |   x: z.string().describe('The task to implement'),
 33 |   technology: z.string().describe('The technology (programming language/framework/tool)'),
 34 | });
 35 | 
 36 | const hintsForProblemSchema = z.object({
 37 |   docs: z.string().describe('Path to documentation directory'),
 38 |   prompt: z.string().describe('Path to prompt file'),
 39 |   problem: z.string().describe('The problem description'),
 40 |   context: z.string().describe('The context of the problem'),
 41 |   environment: z.record(z.unknown()).describe('Environment information'),
 42 | });
 43 | 
 44 | const isThisGoodPracticeSchema = z.object({
 45 |   docs: z.string().describe('Path to documentation directory'),
 46 |   prompt: z.string().describe('Path to prompt file'),
 47 |   snippet: z.string().describe('The code snippet to check'),
 48 |   context: z.string().describe('The context in which the code is used'),
 49 | });
 50 | 
 51 | // Define Tool objects following the MCP specification
 52 | const TOOLS: Tool[] = [
 53 |   {
 54 |     name: TOOL_NAMES.CAN_X_BE_DONE,
 55 |     description: 'Check if a specific task can be done in a given language/framework/tool',
 56 |     inputSchema: {
 57 |       type: 'object',
 58 |       properties: {
 59 |         docs: { type: 'string', description: 'Path to documentation directory' },
 60 |         prompt: { type: 'string', description: 'Path to prompt file' },
 61 |         x: { type: 'string', description: 'The task to check' },
 62 |         technology: { type: 'string', description: 'The technology (programming language/framework/tool)' },
 63 |       },
 64 |       required: ['docs', 'x', 'technology'],
 65 |     },
 66 |   },
 67 |   {
 68 |     name: TOOL_NAMES.HOW_TO_DO_X,
 69 |     description: 'Get examples and alternative approaches for a specific task',
 70 |     inputSchema: {
 71 |       type: 'object',
 72 |       properties: {
 73 |         docs: { type: 'string', description: 'Path to documentation directory' },
 74 |         prompt: { type: 'string', description: 'Path to prompt file' },
 75 |         x: { type: 'string', description: 'The task to implement' },
 76 |         technology: { type: 'string', description: 'The technology (programming language/framework/tool)' },
 77 |       },
 78 |       required: ['docs', 'x', 'technology'],
 79 |     },
 80 |   },
 81 |   {
 82 |     name: TOOL_NAMES.HINTS_FOR_PROBLEM,
 83 |     description: 'Get hints for solving a specific problem',
 84 |     inputSchema: {
 85 |       type: 'object',
 86 |       properties: {
 87 |         docs: { type: 'string', description: 'Path to documentation directory' },
 88 |         prompt: { type: 'string', description: 'Path to prompt file' },
 89 |         problem: { type: 'string', description: 'The problem description' },
 90 |         context: { type: 'string', description: 'The context of the problem' },
 91 |         environment: { type: 'object', description: 'Environment information' },
 92 |       },
 93 |       required: ['docs', 'problem', 'context'],
 94 |     },
 95 |   },
 96 |   {
 97 |     name: TOOL_NAMES.IS_THIS_GOOD_PRACTICE,
 98 |     description: 'Check if a code snippet follows good practices',
 99 |     inputSchema: {
100 |       type: 'object',
101 |       properties: {
102 |         docs: { type: 'string', description: 'Path to documentation directory' },
103 |         prompt: { type: 'string', description: 'Path to prompt file' },
104 |         snippet: { type: 'string', description: 'The code snippet to check' },
105 |         context: { type: 'string', description: 'The context in which the code is used' },
106 |       },
107 |       required: ['docs', 'snippet', 'context'],
108 |     },
109 |   }
110 | ];
111 | 
112 | class GeminiDocsServer {
113 |   private server: Server;
114 |   private gemini: GeminiAPI;
115 |   private db: GeminiDocsDB;
116 |   private fileManager: GoogleAIFileManager;
117 | 
118 |   constructor() {
119 |     if (!process.env.GEMINI_API_KEY) {
120 |       throw new Error('GEMINI_API_KEY environment variable is required');
121 |     }
122 | 
123 |     const toolsEnabled = TOOLS.reduce((acc, tool) => {
124 |       acc[tool.name] = true;
125 |       return acc;
126 |     }, {} as Record<string, boolean>);
127 | 
128 |     this.server = new Server(
129 |       {
130 |         name: 'gemini-docs',
131 |         version: '0.1.0',
132 |       },
133 |       {
134 |         capabilities: {
135 |           tools: toolsEnabled,
136 |         },
137 |       }
138 |     );
139 | 
140 |     try {
141 |       this.db = new GeminiDocsDB();
142 |       this.fileManager = new GoogleAIFileManager(process.env.GEMINI_API_KEY);
143 |       this.gemini = new GeminiAPI();
144 |     } catch (error) {
145 |       console.error('[Server] Failed to initialize:', error);
146 |       Logger.log(`[Server] Failed to initialize: ${error}`);
147 |       throw error;
148 |     }
149 | 
150 |     this.setupTools();
151 |     this.setupErrorHandler();
152 |   }
153 | 
154 |   private setupErrorHandler() {
155 |     this.server.onerror = (error) => {
156 |       console.error('[MCP Error]', error);
157 |     };
158 |   }
159 | 
160 |   private generateFileId(content: string): string {
161 |     const hash = Bun.hash(content).toString(16);
162 |     return hash;
163 |   }
164 | 
165 |   private async uploadFileWithRetry(
166 |     fullPath: string,
167 |     displayName: string,
168 |     maxRetries: number = 3,
169 |     retryDelay: number = 1000
170 |   ): Promise<{ uri: string }> {
171 |     let lastError: Error;
172 |     
173 |     for (let attempt = 1; attempt <= maxRetries; attempt++) {
174 |       try {
175 |         const result = await this.fileManager.uploadFile(fullPath, {
176 |           mimeType: 'text/markdown',
177 |           displayName
178 |         });
179 |         return { uri: result.file.uri };
180 |       } catch (error: any) {
181 |         lastError = error;
182 |         if (attempt < maxRetries) {
183 |           Logger.log(`[FileURIDebug] Upload attempt ${attempt} failed for ${fullPath}, retrying in ${retryDelay}ms...`);
184 |           await new Promise(resolve => setTimeout(resolve, retryDelay * attempt));
185 |         }
186 |       }
187 |     }
188 |     throw lastError!;
189 |   }
190 | 
191 |   private async processFileBatch(
192 |     absPath: string,
193 |     files: string[],
194 |     batchSize: number = 10,
195 |     stats: { cached: number; uploaded: number; } = { cached: 0, uploaded: 0 }
196 |   ): Promise<GeminiFileData[]> {
197 |     const docRefs: GeminiFileData[] = [];
198 |     const batches = [];
199 | 
200 |     const logProgress = () => {
201 |       Logger.log(`[FileURIDebug] Progress Report:
202 |         Total files: ${files.length}
203 |         Processed: ${stats.cached + stats.uploaded}
204 |         Cached: ${stats.cached}
205 |         Uploaded: ${stats.uploaded}
206 |       `);
207 |     };
208 | 
209 |     // Split files into batches
210 |     for (let i = 0; i < files.length; i += batchSize) {
211 |       batches.push(files.slice(i, i + batchSize));
212 |     }
213 | 
214 |     Logger.log(`[FileURIDebug] Processing ${files.length} files in ${batches.length} batches of ${batchSize}`);
215 | 
216 |     // Process batches sequentially to avoid overwhelming the API
217 |     for (const batch of batches) {
218 |       Logger.log(`[FileURIDebug] Processing batch of ${batch.length} files...`);
219 |       const batchResults = await Promise.all(
220 |         batch.map(async (file) => {
221 |           const fullPath = join(absPath, file);
222 |           
223 |           try {
224 |             // Calculate content hash first
225 |             const content = readFileSync(fullPath, 'utf-8');
226 |             const fileHash = this.generateFileId(content);
227 |             
228 |             // Check if we have this content's URI
229 |             const existingFile = await this.db.getFile(fileHash);
230 |             
231 |             if (existingFile) {
232 |               Logger.log(`[FileURIDebug] Found existing content (hash: ${fileHash}) for ${fullPath}`);
233 |               stats.cached++;
234 |               logProgress();
235 |               return {
236 |                 fileUri: existingFile.fileUri
237 |               };
238 |             }
239 | 
240 |             // Upload new file to Gemini
241 |             Logger.log(`[FileURIDebug] Uploading file ${fullPath} (${file})`);
242 | 
243 |             const uploadResult = await this.uploadFileWithRetry(fullPath, file);
244 | 
245 |             // Store the Gemini file URI
246 |             await this.db.saveFileId(fullPath, fileHash, uploadResult.uri);
247 |             Logger.log(`[FileURIDebug] Successfully uploaded and saved ${fullPath}`);
248 |             stats.uploaded++;
249 |             logProgress();
250 |             return { fileUri: uploadResult.uri };
251 |           } catch (error) {
252 |             Logger.log(`[FileURIDebug] Error processing file ${fullPath}: ${error}`);
253 |             throw error;
254 |           }
255 |         })
256 |       );
257 |       docRefs.push(...batchResults);
258 |     }
259 |     return docRefs;
260 |   }
261 | 
262 |   private async readDocs(dirPath: string): Promise<GeminiFileData[]> {
263 |     try {
264 |       const absPath = resolve(dirPath);
265 |       const files = readdirSync(absPath, { recursive: true }) as string[];
266 |       const mdFiles = files.filter(file => file.endsWith('.md'));
267 |       Logger.log(`[FileURIDebug] Found ${mdFiles.length} markdown files to process`);
268 |       
269 |       // Track cached vs uploaded files
270 |       const stats = { cached: 0, uploaded: 0 };
271 |       const docRefs = await this.processFileBatch(absPath, mdFiles, 10, stats);
272 |       
273 |       Logger.log(`[FileURIDebug] Processing completed:
274 |         ${stats.cached} files loaded from cache
275 |         ${stats.uploaded} files uploaded to Gemini`);
276 |       return docRefs;
277 |     } catch (error) {
278 |       console.error('[Server] Error reading docs');
279 |       Logger.log(`[FileURIDebug] Error reading docs: ${error}`);
280 |       throw error;
281 |     }
282 |   }
283 | 
284 |   private setupTools() {
285 |     // List available tools
286 |     this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
287 |       tools: TOOLS,
288 |     }));
289 | 
290 |     // Handle tool calls
291 |     this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
292 |       const { name, arguments: args } = request.params;
293 |       
294 |       switch (name) {
295 |         case TOOL_NAMES.CAN_X_BE_DONE: {
296 |           const params = canXBeDoneSchema.parse(args) as CanXBeDoneParams;
297 |           const docs = await this.readDocs(params.docs);
298 |           const response = await this.gemini.canXBeDone(
299 |             docs,
300 |             params.x,
301 |             params.technology,
302 |             params.prompt
303 |           );
304 |           return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
305 |         }
306 |         case TOOL_NAMES.HOW_TO_DO_X: {
307 |           const params = howToDoXSchema.parse(args) as HowToDoXParams;
308 |           const docs = await this.readDocs(params.docs);
309 |           const response = await this.gemini.howToDoX(
310 |             docs,
311 |             params.x,
312 |             params.technology,
313 |             params.prompt
314 |           );
315 |           return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
316 |         }
317 |         case TOOL_NAMES.HINTS_FOR_PROBLEM: {
318 |           const params = hintsForProblemSchema.parse(args) as HintsForProblemParams;
319 |           const docs = await this.readDocs(params.docs);
320 |           const response = await this.gemini.hintsForProblem(
321 |             docs,
322 |             params.problem,
323 |             params.context,
324 |             params.environment,
325 |             params.prompt
326 |           );
327 |           return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
328 |         }
329 |         case TOOL_NAMES.IS_THIS_GOOD_PRACTICE: {
330 |           const params = isThisGoodPracticeSchema.parse(args) as IsThisGoodPracticeParams;
331 |           const docs = await this.readDocs(params.docs);
332 |           const response = await this.gemini.isThisGoodPractice(
333 |             docs,
334 |             params.snippet,
335 |             params.context,
336 |             params.prompt
337 |           );
338 |           return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
339 |         }
340 |         default:
341 |           throw new Error(`Unknown tool: ${name}`);
342 |       }
343 |     });
344 |   }
345 | 
346 |   async start() {
347 |     const transport = new StdioServerTransport();
348 |     await this.server.connect(transport);
349 |   }
350 | 
351 |   async stop() {
352 |     await this.server.close();
353 |     this.db.close();
354 |     Logger.close();
355 |   }
356 | }
357 | 
358 | // Handle graceful shutdown
359 | const server = new GeminiDocsServer();
360 | 
361 | // Parse command line arguments
362 | const args = process.argv.slice(2);
363 | if (args.includes('--verbose')) {
364 |   Logger.enable();
365 | }
366 | 
367 | process.on('SIGINT', async () => {
368 |   await server.stop();
369 |   process.exit(0);
370 | });
371 | 
372 | server.start().catch(error => {
373 |   console.error('Failed to start server:', error);
374 |   process.exit(1);
375 | });
```

--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_rbac.md:
--------------------------------------------------------------------------------

```markdown
  1 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
  2 | 
  3 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
  4 | 
  5 | v0.6.1
  6 | 
  7 | - Pages
  8 | - Modules
  9 | 
 10 | <!--THE END-->
 11 | 
 12 | <!--THE END-->
 13 | 
 14 | Search documentation of ash\_rbac
 15 | 
 16 | Settings
 17 | 
 18 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac "View Source") API Reference ash\_rbac v0.6.1
 19 | 
 20 | ## [](api-reference.html#modules)Modules
 21 | 
 22 | [AshRbac](AshRbac.html)
 23 | 
 24 | [AshRbac.Actions](AshRbac.Actions.html)
 25 | 
 26 | Adds the policies for actions to the dsl\_state
 27 | 
 28 | [AshRbac.Fields](AshRbac.Fields.html)
 29 | 
 30 | Adds the policies for fields to the dsl\_state
 31 | 
 32 | [AshRbac.HasRole](AshRbac.HasRole.html)
 33 | 
 34 | Check to determine if the actor has a specific role or if the actor has any of the roles in a list
 35 | 
 36 | [AshRbac.Info](AshRbac.Info.html)
 37 | 
 38 | Introspection functions for the Rbac Extension
 39 | 
 40 | [AshRbac.Policies](AshRbac.Policies.html)
 41 | 
 42 | Adds the configured policies to the resource
 43 | 
 44 | [AshRbac.Role](AshRbac.Role.html)
 45 | 
 46 | The Role entity for the DSL of the rbac extension
 47 | 
 48 | [Next Page → Getting Started](getting_started.html)
 49 | 
 50 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
 51 | 
 52 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 53 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
 54 | 
 55 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
 56 | 
 57 | v0.6.1
 58 | 
 59 | - Pages
 60 | - Modules
 61 | 
 62 | <!--THE END-->
 63 | 
 64 | <!--THE END-->
 65 | 
 66 | Search documentation of ash\_rbac
 67 | 
 68 | Settings
 69 | 
 70 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L1 "View Source") AshRbac.Actions (ash\_rbac v0.6.1)
 71 | 
 72 | Adds the policies for actions to the dsl\_state
 73 | 
 74 | # [](AshRbac.Actions.html#summary)Summary
 75 | 
 76 | ## [Functions](AshRbac.Actions.html#functions)
 77 | 
 78 | [after?(\_)](AshRbac.Actions.html#after?/1)
 79 | 
 80 | Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1).
 81 | 
 82 | [after\_compile?()](AshRbac.Actions.html#after_compile?/0)
 83 | 
 84 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
 85 | 
 86 | [before?(\_)](AshRbac.Actions.html#before?/1)
 87 | 
 88 | Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1).
 89 | 
 90 | # [](AshRbac.Actions.html#functions)Functions
 91 | 
 92 | [Link to this function](AshRbac.Actions.html#after?/1 "Link to this function")
 93 | 
 94 | # after?(\_)
 95 | 
 96 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L5 "View Source")
 97 | 
 98 | Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1).
 99 | 
100 | [Link to this function](AshRbac.Actions.html#after_compile?/0 "Link to this function")
101 | 
102 | # after\_compile?()
103 | 
104 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L5 "View Source")
105 | 
106 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
107 | 
108 | [Link to this function](AshRbac.Actions.html#before?/1 "Link to this function")
109 | 
110 | # before?(\_)
111 | 
112 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L5 "View Source")
113 | 
114 | Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1).
115 | 
116 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
117 | 
118 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
119 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
120 | 
121 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
122 | 
123 | v0.6.1
124 | 
125 | - Pages
126 | - Modules
127 | 
128 | <!--THE END-->
129 | 
130 | <!--THE END-->
131 | 
132 | Search documentation of ash\_rbac
133 | 
134 | Settings
135 | 
136 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L1 "View Source") AshRbac.Fields (ash\_rbac v0.6.1)
137 | 
138 | Adds the policies for fields to the dsl\_state
139 | 
140 | # [](AshRbac.Fields.html#summary)Summary
141 | 
142 | ## [Functions](AshRbac.Fields.html#functions)
143 | 
144 | [after?(\_)](AshRbac.Fields.html#after?/1)
145 | 
146 | Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1).
147 | 
148 | [after\_compile?()](AshRbac.Fields.html#after_compile?/0)
149 | 
150 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
151 | 
152 | [before?(\_)](AshRbac.Fields.html#before?/1)
153 | 
154 | Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1).
155 | 
156 | # [](AshRbac.Fields.html#functions)Functions
157 | 
158 | [Link to this function](AshRbac.Fields.html#after?/1 "Link to this function")
159 | 
160 | # after?(\_)
161 | 
162 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L6 "View Source")
163 | 
164 | Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1).
165 | 
166 | [Link to this function](AshRbac.Fields.html#after_compile?/0 "Link to this function")
167 | 
168 | # after\_compile?()
169 | 
170 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L6 "View Source")
171 | 
172 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
173 | 
174 | [Link to this function](AshRbac.Fields.html#before?/1 "Link to this function")
175 | 
176 | # before?(\_)
177 | 
178 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L6 "View Source")
179 | 
180 | Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1).
181 | 
182 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
183 | 
184 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
185 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
186 | 
187 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
188 | 
189 | v0.6.1
190 | 
191 | - Pages
192 | - Modules
193 | 
194 | <!--THE END-->
195 | 
196 | <!--THE END-->
197 | 
198 | Search documentation of ash\_rbac
199 | 
200 | Settings
201 | 
202 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L1 "View Source") AshRbac.HasRole (ash\_rbac v0.6.1)
203 | 
204 | Check to determine if the actor has a specific role or if the actor has any of the roles in a list
205 | 
206 | # [](AshRbac.HasRole.html#summary)Summary
207 | 
208 | ## [Functions](AshRbac.HasRole.html#functions)
209 | 
210 | [prefer\_expanded\_description?()](AshRbac.HasRole.html#prefer_expanded_description?/0)
211 | 
212 | Callback implementation for [`Ash.Policy.Check.prefer_expanded_description?/0`](../ash/3.4.21/Ash.Policy.Check.html#c:prefer_expanded_description?/0).
213 | 
214 | [requires\_original\_data?(\_, \_)](AshRbac.HasRole.html#requires_original_data?/2)
215 | 
216 | Callback implementation for [`Ash.Policy.Check.requires_original_data?/2`](../ash/3.4.21/Ash.Policy.Check.html#c:requires_original_data?/2).
217 | 
218 | [strict\_check(actor, context, opts)](AshRbac.HasRole.html#strict_check/3)
219 | 
220 | Callback implementation for [`Ash.Policy.Check.strict_check/3`](../ash/3.4.21/Ash.Policy.Check.html#c:strict_check/3).
221 | 
222 | [type()](AshRbac.HasRole.html#type/0)
223 | 
224 | Callback implementation for [`Ash.Policy.Check.type/0`](../ash/3.4.21/Ash.Policy.Check.html#c:type/0).
225 | 
226 | # [](AshRbac.HasRole.html#functions)Functions
227 | 
228 | [Link to this function](AshRbac.HasRole.html#prefer_expanded_description?/0 "Link to this function")
229 | 
230 | # prefer\_expanded\_description?()
231 | 
232 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source")
233 | 
234 | Callback implementation for [`Ash.Policy.Check.prefer_expanded_description?/0`](../ash/3.4.21/Ash.Policy.Check.html#c:prefer_expanded_description?/0).
235 | 
236 | [Link to this function](AshRbac.HasRole.html#requires_original_data?/2 "Link to this function")
237 | 
238 | # requires\_original\_data?(\_, \_)
239 | 
240 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source")
241 | 
242 | Callback implementation for [`Ash.Policy.Check.requires_original_data?/2`](../ash/3.4.21/Ash.Policy.Check.html#c:requires_original_data?/2).
243 | 
244 | [Link to this function](AshRbac.HasRole.html#strict_check/3 "Link to this function")
245 | 
246 | # strict\_check(actor, context, opts)
247 | 
248 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source")
249 | 
250 | Callback implementation for [`Ash.Policy.Check.strict_check/3`](../ash/3.4.21/Ash.Policy.Check.html#c:strict_check/3).
251 | 
252 | [Link to this function](AshRbac.HasRole.html#type/0 "Link to this function")
253 | 
254 | # type()
255 | 
256 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source")
257 | 
258 | Callback implementation for [`Ash.Policy.Check.type/0`](../ash/3.4.21/Ash.Policy.Check.html#c:type/0).
259 | 
260 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
261 | 
262 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
263 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
264 | 
265 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
266 | 
267 | v0.6.1
268 | 
269 | - Pages
270 | - Modules
271 | 
272 | <!--THE END-->
273 | 
274 | <!--THE END-->
275 | 
276 | Search documentation of ash\_rbac
277 | 
278 | Settings
279 | 
280 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L1 "View Source") AshRbac (ash\_rbac v0.6.1)
281 | 
282 | # [](AshRbac.html#summary)Summary
283 | 
284 | ## [Functions](AshRbac.html#functions)
285 | 
286 | [rbac(body)](AshRbac.html#rbac/1)
287 | 
288 | # [](AshRbac.html#functions)Functions
289 | 
290 | [Link to this macro](AshRbac.html#rbac/1 "Link to this macro")
291 | 
292 | # rbac(body)
293 | 
294 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L177 "View Source") (macro)
295 | 
296 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
297 | 
298 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
299 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
300 | 
301 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
302 | 
303 | v0.6.1
304 | 
305 | - Pages
306 | - Modules
307 | 
308 | <!--THE END-->
309 | 
310 | <!--THE END-->
311 | 
312 | Search documentation of ash\_rbac
313 | 
314 | Settings
315 | 
316 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L127 "View Source") AshRbac.Info (ash\_rbac v0.6.1)
317 | 
318 | Introspection functions for the Rbac Extension
319 | 
320 | # [](AshRbac.Info.html#summary)Summary
321 | 
322 | ## [Functions](AshRbac.Info.html#functions)
323 | 
324 | [bypass(resource)](AshRbac.Info.html#bypass/1)
325 | 
326 | [bypass\_roles\_field(resource)](AshRbac.Info.html#bypass_roles_field/1)
327 | 
328 | [public?(resource)](AshRbac.Info.html#public?/1)
329 | 
330 | [roles(resource)](AshRbac.Info.html#roles/1)
331 | 
332 | # [](AshRbac.Info.html#functions)Functions
333 | 
334 | [Link to this function](AshRbac.Info.html#bypass/1 "Link to this function")
335 | 
336 | # bypass(resource)
337 | 
338 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L133 "View Source")
339 | 
340 | [Link to this function](AshRbac.Info.html#bypass_roles_field/1 "Link to this function")
341 | 
342 | # bypass\_roles\_field(resource)
343 | 
344 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L137 "View Source")
345 | 
346 | [Link to this function](AshRbac.Info.html#public?/1 "Link to this function")
347 | 
348 | # public?(resource)
349 | 
350 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L141 "View Source")
351 | 
352 | [Link to this function](AshRbac.Info.html#roles/1 "Link to this function")
353 | 
354 | # roles(resource)
355 | 
356 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L145 "View Source")
357 | 
358 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
359 | 
360 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
361 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
362 | 
363 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
364 | 
365 | v0.6.1
366 | 
367 | - Pages
368 | - Modules
369 | 
370 | <!--THE END-->
371 | 
372 | <!--THE END-->
373 | 
374 | Search documentation of ash\_rbac
375 | 
376 | Settings
377 | 
378 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/policies.ex#L1 "View Source") AshRbac.Policies (ash\_rbac v0.6.1)
379 | 
380 | Adds the configured policies to the resource
381 | 
382 | # [](AshRbac.Policies.html#summary)Summary
383 | 
384 | ## [Functions](AshRbac.Policies.html#functions)
385 | 
386 | [after\_compile?()](AshRbac.Policies.html#after_compile?/0)
387 | 
388 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
389 | 
390 | # [](AshRbac.Policies.html#functions)Functions
391 | 
392 | [Link to this function](AshRbac.Policies.html#after_compile?/0 "Link to this function")
393 | 
394 | # after\_compile?()
395 | 
396 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/policies.ex#L6 "View Source")
397 | 
398 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
399 | 
400 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
401 | 
402 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
403 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
404 | 
405 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
406 | 
407 | v0.6.1
408 | 
409 | - Pages
410 | - Modules
411 | 
412 | <!--THE END-->
413 | 
414 | <!--THE END-->
415 | 
416 | Search documentation of ash\_rbac
417 | 
418 | Settings
419 | 
420 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/role.ex#L1 "View Source") AshRbac.Role (ash\_rbac v0.6.1)
421 | 
422 | The Role entity for the DSL of the rbac extension
423 | 
424 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
425 | 
426 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
427 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
428 | 
429 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
430 | 
431 | v0.6.1
432 | 
433 | - Pages
434 | - Modules
435 | 
436 | <!--THE END-->
437 | 
438 | <!--THE END-->
439 | 
440 | Search documentation of ash\_rbac
441 | 
442 | Settings
443 | 
444 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/documentation/getting_started.md#L1 "View Source") Getting Started
445 | 
446 | ## [](getting_started.html#installation)Installation
447 | 
448 | Add the ash\_rbac dependency to your mix.exs
449 | 
450 | ```
451 | defp deps do
452 |   [
453 |     {:ash_rbac, "~> 0.6.1"}
454 |   ]
455 | end
456 | ```
457 | 
458 | ## [](getting_started.html#adding-ashrbac-to-your-resource)Adding AshRbac to your resource
459 | 
460 | First, the authorizer and the extension need to be added.
461 | 
462 | ```
463 | defmodule RootResource do
464 |     @moduledoc false
465 |     use Ash.Resource,
466 |       data_layer: Ash.DataLayer.Ets,
467 |       authorizers: [Ash.Policy.Authorizer], # Add the authorizer
468 |       extensions: [AshRbac] # Add the extension
469 |   ...
470 | end
471 | ```
472 | 
473 | Afterwards, you can add a rbac block to your resource.
474 | 
475 | ```
476 |   rbac do
477 |     role :user do
478 |       fields [:name, :email]
479 |       actions [:read]
480 |     end
481 |   end
482 | ```
483 | 
484 | The options defined in the rbac block are transformed into policies during compile time.
485 | 
486 | The previous example will generate the following policies:
487 | 
488 | ```
489 | field_policies do
490 |   field_policy [:name, :email], [{AshRbac.HasRole, [role: [:user]]}] do
491 |     authorize_if always()
492 |   end
493 | 
494 |   # it also adds a policy for all other fields like this
495 |   field_policy [:other_fields, ...] do
496 |     forbid_if always()
497 |   end
498 | end
499 | 
500 | policies do
501 |   policy [action(:read), {AshRbac.HasRole, [role: [:user]]}] do
502 |     authorize_if always()
503 |   end
504 | end
505 | ```
506 | 
507 | It is possible to add extra conditions to fields and actions:
508 | 
509 | ```
510 |   rbac do
511 |     role :user do
512 |       fields [:name, {:email, actor_attribute_equals(:field, "value")}]
513 |       actions [{:read, accessing_from(RelatedResource, :path)}]
514 |     end
515 |   end
516 | ```
517 | 
518 | The conditions are added to the generated policies as well.
519 | 
520 | ```
521 | field_policies do
522 |   field_policy [:name], [{AshRbac.HasRole, [role: [:user]]}] do
523 |     authorize_if always()
524 |   end
525 | 
526 |   field_policy [:email], [{AshRbac.HasRole, [role: [:user], actor_attribute_equals(:field, "value")]}] do
527 |     authorize_if always()
528 |   end
529 | 
530 |   # it also adds a policy for all other fields like this
531 |   field_policy [:other_fields, ...] do
532 |     forbid_if always()
533 |   end
534 | end
535 | 
536 | policies do
537 |   policy [action(:read), {hAshRbac.HasRole, [role: [:user]]}, accessing_from(RelatedResource, :path)] do
538 |     authorize_if always()
539 |   end
540 | end
541 | ```
542 | 
543 | [← Previous Page API Reference](api-reference.html)
544 | 
545 | [Next Page → Relationships](relationships.html)
546 | 
547 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) ([current file](https://preview.hex.pm/preview/ash_rbac/0.6.1/show/documentation/getting_started.md)) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
548 | 
549 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
550 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
551 | 
552 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
553 | 
554 | v0.6.1
555 | 
556 | - Pages
557 | - Modules
558 | 
559 | <!--THE END-->
560 | 
561 | <!--THE END-->
562 | 
563 | Search documentation of ash\_rbac
564 | 
565 | Settings
566 | 
567 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/README.md#L1 "View Source") AshRbac
568 | 
569 | A small extension that allows for easier application of policies
570 | 
571 | ```
572 | rbac do
573 |   role :user do
574 |     fields [:fields, :user, :can, :see]
575 |     actions [:actions, :user, :can :use]
576 |   end
577 | end
578 | ```
579 | 
580 | ## [](readme.html#installation)Installation
581 | 
582 | The package can be installed by adding `ash_rbac` to your list of dependencies in `mix.exs`:
583 | 
584 | ```
585 | def deps do
586 |   [
587 |     {:ash_rbac, "~> 0.6.1"},
588 |   ]
589 | end
590 | ```
591 | 
592 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) and published on [HexDocs](../index.html). Once published, the docs can be found at [https://hexdocs.pm/ash\_rbac](../ash_rbac.html).
593 | 
594 | [← Previous Page Relationships](relationships.html)
595 | 
596 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) ([current file](https://preview.hex.pm/preview/ash_rbac/0.6.1/show/README.md)) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
597 | 
598 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
599 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac)
600 | 
601 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
602 | 
603 | v0.6.1
604 | 
605 | - Pages
606 | - Modules
607 | 
608 | <!--THE END-->
609 | 
610 | <!--THE END-->
611 | 
612 | Search documentation of ash\_rbac
613 | 
614 | Settings
615 | 
616 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/documentation/relationships.md#L1 "View Source") Relationships
617 | 
618 | As relationships are not part of field policies it is necessary to protect them with an action policy. This can be done by passing a custom condition to the action.
619 | 
620 | ```
621 | # only allow read access if accessed from a parent
622 | rbac do
623 |   role :user do
624 |     actions [
625 |       {:read, accessing_from(Parent, :child)}
626 |     ]
627 |   end
628 | end
629 | 
630 | # result
631 | policies do
632 |   policy [action(:read), accessing_from(Parent, :child)] do
633 |     authorize_if {AshRbac.HasRole, [role: :user]}
634 |   end
635 | end
636 | ```
637 | 
638 | [← Previous Page Getting Started](getting_started.html)
639 | 
640 | [Next Page → Readme](readme.html)
641 | 
642 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) ([current file](https://preview.hex.pm/preview/ash_rbac/0.6.1/show/documentation/relationships.md)) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version")
643 | 
644 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
645 | 
```

--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_money.md:
--------------------------------------------------------------------------------

```markdown
  1 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
  2 | 
  3 | [ash\_money](https://github.com/ash-project/ash_money)
  4 | 
  5 | v0.1.15
  6 | 
  7 | - GUIDES
  8 | - Modules
  9 | - Mix Tasks
 10 | 
 11 | <!--THE END-->
 12 | 
 13 | <!--THE END-->
 14 | 
 15 | <!--THE END-->
 16 | 
 17 | Search documentation of ash\_money
 18 | 
 19 | Settings
 20 | 
 21 | # [View Source](https://github.com/ash-project/ash_money "View Source") API Reference ash\_money v0.1.15
 22 | 
 23 | ## [](api-reference.html#modules)Modules
 24 | 
 25 | [AshMoney](AshMoney.html)
 26 | 
 27 | [`AshMoney`](AshMoney.html#content) provides a type for working with money in your Ash resources.
 28 | 
 29 | [AshMoney.AshPostgresExtension](AshMoney.AshPostgresExtension.html)
 30 | 
 31 | Installs the `money_with_currency` type and operators/functions for Postgres.
 32 | 
 33 | [AshMoney.Types.Money](AshMoney.Types.Money.html)
 34 | 
 35 | A money type for Ash that uses the `ex_money` library.
 36 | 
 37 | [Comparable.Type.Decimal.To.Money](Comparable.Type.Decimal.To.Money.html)
 38 | 
 39 | [Comparable.Type.Float.To.Money](Comparable.Type.Float.To.Money.html)
 40 | 
 41 | [Comparable.Type.Integer.To.Money](Comparable.Type.Integer.To.Money.html)
 42 | 
 43 | [Comparable.Type.Money.To.Decimal](Comparable.Type.Money.To.Decimal.html)
 44 | 
 45 | [Comparable.Type.Money.To.Float](Comparable.Type.Money.To.Float.html)
 46 | 
 47 | [Comparable.Type.Money.To.Integer](Comparable.Type.Money.To.Integer.html)
 48 | 
 49 | [Comparable.Type.Money.To.Money](Comparable.Type.Money.To.Money.html)
 50 | 
 51 | ## [](api-reference.html#mix-tasks)Mix Tasks
 52 | 
 53 | [mix ash\_money.add\_to\_ash\_postgres](Mix.Tasks.AshMoney.AddToAshPostgres.html)
 54 | 
 55 | Adds AshMoney.AshPostgresExtension to installed\_extensions and installs :ex\_money\_sql.
 56 | 
 57 | [mix ash\_money.install](Mix.Tasks.AshMoney.Install.html)
 58 | 
 59 | Installs AshMoney. Should be run with `mix igniter.install ash_money`
 60 | 
 61 | [Next Page → Home](readme.html)
 62 | 
 63 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
 64 | 
 65 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 66 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
 67 | 
 68 | [ash\_money](https://github.com/ash-project/ash_money)
 69 | 
 70 | v0.1.15
 71 | 
 72 | - GUIDES
 73 | - Modules
 74 | - Mix Tasks
 75 | 
 76 | <!--THE END-->
 77 | 
 78 | <!--THE END-->
 79 | 
 80 | <!--THE END-->
 81 | 
 82 | Search documentation of ash\_money
 83 | 
 84 | Settings
 85 | 
 86 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L2 "View Source") AshMoney.AshPostgresExtension (ash\_money v0.1.15)
 87 | 
 88 | Installs the `money_with_currency` type and operators/functions for Postgres.
 89 | 
 90 | # [](AshMoney.AshPostgresExtension.html#summary)Summary
 91 | 
 92 | ## [Functions](AshMoney.AshPostgresExtension.html#functions)
 93 | 
 94 | [extension()](AshMoney.AshPostgresExtension.html#extension/0)
 95 | 
 96 | [install(int)](AshMoney.AshPostgresExtension.html#install/1)
 97 | 
 98 | Callback implementation for [`AshPostgres.CustomExtension.install/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:install/1).
 99 | 
100 | [uninstall(v)](AshMoney.AshPostgresExtension.html#uninstall/1)
101 | 
102 | Callback implementation for [`AshPostgres.CustomExtension.uninstall/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:uninstall/1).
103 | 
104 | # [](AshMoney.AshPostgresExtension.html#functions)Functions
105 | 
106 | [Link to this function](AshMoney.AshPostgresExtension.html#extension/0 "Link to this function")
107 | 
108 | # extension()
109 | 
110 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L6 "View Source")
111 | 
112 | [Link to this function](AshMoney.AshPostgresExtension.html#install/1 "Link to this function")
113 | 
114 | # install(int)
115 | 
116 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L8 "View Source")
117 | 
118 | Callback implementation for [`AshPostgres.CustomExtension.install/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:install/1).
119 | 
120 | [Link to this function](AshMoney.AshPostgresExtension.html#uninstall/1 "Link to this function")
121 | 
122 | # uninstall(v)
123 | 
124 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L56 "View Source")
125 | 
126 | Callback implementation for [`AshPostgres.CustomExtension.uninstall/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:uninstall/1).
127 | 
128 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
129 | 
130 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
131 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
132 | 
133 | [ash\_money](https://github.com/ash-project/ash_money)
134 | 
135 | v0.1.15
136 | 
137 | - GUIDES
138 | - Modules
139 | - Mix Tasks
140 | 
141 | <!--THE END-->
142 | 
143 | <!--THE END-->
144 | 
145 | <!--THE END-->
146 | 
147 | Search documentation of ash\_money
148 | 
149 | Settings
150 | 
151 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money.ex#L1 "View Source") AshMoney (ash\_money v0.1.15)
152 | 
153 | [`AshMoney`](AshMoney.html#content) provides a type for working with money in your Ash resources.
154 | 
155 | It also provides an `AshPostgres.Extension` that can be used to add support for money types and operators to your Postgres database.
156 | 
157 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
158 | 
159 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
160 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
161 | 
162 | [ash\_money](https://github.com/ash-project/ash_money)
163 | 
164 | v0.1.15
165 | 
166 | - GUIDES
167 | - Modules
168 | - Mix Tasks
169 | 
170 | <!--THE END-->
171 | 
172 | <!--THE END-->
173 | 
174 | <!--THE END-->
175 | 
176 | Search documentation of ash\_money
177 | 
178 | Settings
179 | 
180 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/types/money.ex#L1 "View Source") AshMoney.Types.Money (ash\_money v0.1.15)
181 | 
182 | A money type for Ash that uses the `ex_money` library.
183 | 
184 | When constructing a composite type, use a tuple in the following structure:
185 | 
186 | `composite_type(%{currency: "USD", amount: Decimal.new("0")}}, AshMoney.Types.Money)`
187 | 
188 | If you've added a custom type, like `:money`:
189 | 
190 | ```
191 | composite_type(%{currency: "USD", amount: Decimal.new("0")}}, :money)
192 | ```
193 | 
194 | # [](AshMoney.Types.Money.html#summary)Summary
195 | 
196 | ## [Functions](AshMoney.Types.Money.html#functions)
197 | 
198 | [handle\_change?()](AshMoney.Types.Money.html#handle_change?/0)
199 | 
200 | [prepare\_change?()](AshMoney.Types.Money.html#prepare_change?/0)
201 | 
202 | # [](AshMoney.Types.Money.html#functions)Functions
203 | 
204 | [Link to this function](AshMoney.Types.Money.html#handle_change?/0 "Link to this function")
205 | 
206 | # handle\_change?()
207 | 
208 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/types/money.ex#L1 "View Source")
209 | 
210 | [Link to this function](AshMoney.Types.Money.html#prepare_change?/0 "Link to this function")
211 | 
212 | # prepare\_change?()
213 | 
214 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/types/money.ex#L1 "View Source")
215 | 
216 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
217 | 
218 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
219 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
220 | 
221 | [ash\_money](https://github.com/ash-project/ash_money)
222 | 
223 | v0.1.15
224 | 
225 | - GUIDES
226 | - Modules
227 | - Mix Tasks
228 | 
229 | <!--THE END-->
230 | 
231 | <!--THE END-->
232 | 
233 | <!--THE END-->
234 | 
235 | Search documentation of ash\_money
236 | 
237 | Settings
238 | 
239 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L11 "View Source") Comparable.Type.Decimal.To.Money (ash\_money v0.1.15)
240 | 
241 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
242 | 
243 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
244 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
245 | 
246 | [ash\_money](https://github.com/ash-project/ash_money)
247 | 
248 | v0.1.15
249 | 
250 | - GUIDES
251 | - Modules
252 | - Mix Tasks
253 | 
254 | <!--THE END-->
255 | 
256 | <!--THE END-->
257 | 
258 | <!--THE END-->
259 | 
260 | Search documentation of ash\_money
261 | 
262 | Settings
263 | 
264 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L15 "View Source") Comparable.Type.Float.To.Money (ash\_money v0.1.15)
265 | 
266 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
267 | 
268 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
269 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
270 | 
271 | [ash\_money](https://github.com/ash-project/ash_money)
272 | 
273 | v0.1.15
274 | 
275 | - GUIDES
276 | - Modules
277 | - Mix Tasks
278 | 
279 | <!--THE END-->
280 | 
281 | <!--THE END-->
282 | 
283 | <!--THE END-->
284 | 
285 | Search documentation of ash\_money
286 | 
287 | Settings
288 | 
289 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L3 "View Source") Comparable.Type.Integer.To.Money (ash\_money v0.1.15)
290 | 
291 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
292 | 
293 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
294 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
295 | 
296 | [ash\_money](https://github.com/ash-project/ash_money)
297 | 
298 | v0.1.15
299 | 
300 | - GUIDES
301 | - Modules
302 | - Mix Tasks
303 | 
304 | <!--THE END-->
305 | 
306 | <!--THE END-->
307 | 
308 | <!--THE END-->
309 | 
310 | Search documentation of ash\_money
311 | 
312 | Settings
313 | 
314 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L11 "View Source") Comparable.Type.Money.To.Decimal (ash\_money v0.1.15)
315 | 
316 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
317 | 
318 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
319 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
320 | 
321 | [ash\_money](https://github.com/ash-project/ash_money)
322 | 
323 | v0.1.15
324 | 
325 | - GUIDES
326 | - Modules
327 | - Mix Tasks
328 | 
329 | <!--THE END-->
330 | 
331 | <!--THE END-->
332 | 
333 | <!--THE END-->
334 | 
335 | Search documentation of ash\_money
336 | 
337 | Settings
338 | 
339 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L15 "View Source") Comparable.Type.Money.To.Float (ash\_money v0.1.15)
340 | 
341 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
342 | 
343 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
344 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
345 | 
346 | [ash\_money](https://github.com/ash-project/ash_money)
347 | 
348 | v0.1.15
349 | 
350 | - GUIDES
351 | - Modules
352 | - Mix Tasks
353 | 
354 | <!--THE END-->
355 | 
356 | <!--THE END-->
357 | 
358 | <!--THE END-->
359 | 
360 | Search documentation of ash\_money
361 | 
362 | Settings
363 | 
364 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L3 "View Source") Comparable.Type.Money.To.Integer (ash\_money v0.1.15)
365 | 
366 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
367 | 
368 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
369 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
370 | 
371 | [ash\_money](https://github.com/ash-project/ash_money)
372 | 
373 | v0.1.15
374 | 
375 | - GUIDES
376 | - Modules
377 | - Mix Tasks
378 | 
379 | <!--THE END-->
380 | 
381 | <!--THE END-->
382 | 
383 | <!--THE END-->
384 | 
385 | Search documentation of ash\_money
386 | 
387 | Settings
388 | 
389 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L7 "View Source") Comparable.Type.Money.To.Money (ash\_money v0.1.15)
390 | 
391 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
392 | 
393 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
394 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
395 | 
396 | [ash\_money](https://github.com/ash-project/ash_money)
397 | 
398 | v0.1.15
399 | 
400 | - GUIDES
401 | - Modules
402 | - Mix Tasks
403 | 
404 | <!--THE END-->
405 | 
406 | <!--THE END-->
407 | 
408 | <!--THE END-->
409 | 
410 | Search documentation of ash\_money
411 | 
412 | Settings
413 | 
414 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/documentation/tutorials/getting-started-with-ash-money.md#L1 "View Source") Getting Started With AshMoney
415 | 
416 | ## [](getting-started-with-ash-money.html#bring-in-the-ash_money-dependency)Bring in the ash\_money dependency
417 | 
418 | ```
419 | def deps()
420 |   [
421 |     ...
422 |     {:ash_money, "~> 0.1.15"}
423 |   ]
424 | end
425 | ```
426 | 
427 | ## [](getting-started-with-ash-money.html#setup)Setup
428 | 
429 | ### [](getting-started-with-ash-money.html#using-igniter-recommended)Using Igniter (recommended)
430 | 
431 | ```
432 | mix igniter.install ash_money
433 | ```
434 | 
435 | ### [](getting-started-with-ash-money.html#manual)Manual
436 | 
437 | The primary thing that AshMoney provides is [`AshMoney.Types.Money`](AshMoney.Types.Money.html). This is backed by `ex_money`. You can use it out of the box like so:
438 | 
439 | ```
440 | attribute :balance, AshMoney.Types.Money
441 | ```
442 | 
443 | ## [](getting-started-with-ash-money.html#add-to-known-types)Add to known types
444 | 
445 | To support money operations in runtime expressions, which use [`Ash`](../ash/3.4.48/Ash.html)'s operator overloading feature, we have to tell Ash about the `Ash.Type.Money` using the `known_type` configuration.
446 | 
447 | ```
448 | config :ash, :known_types, [AshMoney.Types.Money]
449 | ```
450 | 
451 | ## [](getting-started-with-ash-money.html#referencing-with-money)Referencing with `:money`
452 | 
453 | You can add it to your compile time list of types for easier reference:
454 | 
455 | ```
456 | config :ash, :custom_types, money: AshMoney.Types.Money
457 | ```
458 | 
459 | Then compile ash again, `mix deps.compile ash --force`, and refer to it like so:
460 | 
461 | ```
462 | attribute :balance, :money
463 | ```
464 | 
465 | ## [](getting-started-with-ash-money.html#adding-ashpostgres-support)Adding AshPostgres Support
466 | 
467 | First, add the `:ex_money_sql` dependency to your `mix.exs` file.
468 | 
469 | Then add [`AshMoney.AshPostgresExtension`](AshMoney.AshPostgresExtension.html) to your list of `installed_extensions` in your repo, and generate migrations.
470 | 
471 | ```
472 | defmodule YourRepo do
473 |   def installed_extensions do
474 |     [..., AshMoney.AshPostgresExtension]
475 |   end
476 | end
477 | ```
478 | 
479 | ## [](getting-started-with-ash-money.html#ashpostgres-support)AshPostgres Support
480 | 
481 | Thanks to `ex_money_sql`, there are excellent tools for lowering support for money into your postgres database. This allows for things like aggregates that sum amounts, and referencing money in expressions:
482 | 
483 | ```
484 | sum :sum_of_usd_balances, :accounts, :balance do
485 |   filter expr(balance[:currency_code] == "USD")
486 | end
487 | ```
488 | 
489 | ## [](getting-started-with-ash-money.html#ashgraphql-support)AshGraphql Support
490 | 
491 | Add the following to your schema file:
492 | 
493 | ```
494 |   object :money do
495 |     field(:amount, non_null(:decimal))
496 |     field(:currency, non_null(:string))
497 |   end
498 | 
499 |   input_object :money_input do
500 |     field(:amount, non_null(:decimal))
501 |     field(:currency, non_null(:string))
502 |   end
503 | ```
504 | 
505 | [← Previous Page Home](readme.html)
506 | 
507 | [Next Page → Change Log](changelog.html)
508 | 
509 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) ([current file](https://preview.hex.pm/preview/ash_money/0.1.15/show/documentation/tutorials/getting-started-with-ash-money.md)) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
510 | 
511 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
512 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
513 | 
514 | [ash\_money](https://github.com/ash-project/ash_money)
515 | 
516 | v0.1.15
517 | 
518 | - GUIDES
519 | - Modules
520 | - Mix Tasks
521 | 
522 | <!--THE END-->
523 | 
524 | <!--THE END-->
525 | 
526 | <!--THE END-->
527 | 
528 | Search documentation of ash\_money
529 | 
530 | Settings
531 | 
532 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/mix/tasks/ash_money.add_to_ash_postgres.ex#L2 "View Source") mix ash\_money.add\_to\_ash\_postgres (ash\_money v0.1.15)
533 | 
534 | Adds AshMoney.AshPostgresExtension to installed\_extensions and installs :ex\_money\_sql.
535 | 
536 | This is called automatically by `mix igniter.install ash_money` if [`AshPostgres`](../ash_postgres/2.4.18/AshPostgres.html) is installed at the time. This task is useful if you install `ash_postgres` *after* installing `ash_money`.
537 | 
538 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
539 | 
540 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
541 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
542 | 
543 | [ash\_money](https://github.com/ash-project/ash_money)
544 | 
545 | v0.1.15
546 | 
547 | - GUIDES
548 | - Modules
549 | - Mix Tasks
550 | 
551 | <!--THE END-->
552 | 
553 | <!--THE END-->
554 | 
555 | <!--THE END-->
556 | 
557 | Search documentation of ash\_money
558 | 
559 | Settings
560 | 
561 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/mix/tasks/ash_money.install.ex#L2 "View Source") mix ash\_money.install (ash\_money v0.1.15)
562 | 
563 | Installs AshMoney. Should be run with `mix igniter.install ash_money`
564 | 
565 | # [](Mix.Tasks.AshMoney.Install.html#summary)Summary
566 | 
567 | ## [Functions](Mix.Tasks.AshMoney.Install.html#functions)
568 | 
569 | [igniter(igniter, argv)](Mix.Tasks.AshMoney.Install.html#igniter/2)
570 | 
571 | Callback implementation for [`Igniter.Mix.Task.igniter/2`](../igniter/0.5.0/Igniter.Mix.Task.html#c:igniter/2).
572 | 
573 | # [](Mix.Tasks.AshMoney.Install.html#functions)Functions
574 | 
575 | [Link to this function](Mix.Tasks.AshMoney.Install.html#igniter/2 "Link to this function")
576 | 
577 | # igniter(igniter, argv)
578 | 
579 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/mix/tasks/ash_money.install.ex#L8 "View Source")
580 | 
581 | Callback implementation for [`Igniter.Mix.Task.igniter/2`](../igniter/0.5.0/Igniter.Mix.Task.html#c:igniter/2).
582 | 
583 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
584 | 
585 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
586 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money)
587 | 
588 | [ash\_money](https://github.com/ash-project/ash_money)
589 | 
590 | v0.1.15
591 | 
592 | - GUIDES
593 | - Modules
594 | - Mix Tasks
595 | 
596 | <!--THE END-->
597 | 
598 | <!--THE END-->
599 | 
600 | <!--THE END-->
601 | 
602 | Search documentation of ash\_money
603 | 
604 | Settings
605 | 
606 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/README.md#L1 "View Source") Home
607 | 
608 | ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only)
609 | 
610 | ![Elixir CI](https://github.com/ash-project/ash_money/workflows/CI/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Hex version badge](https://img.shields.io/hexpm/v/ash_money.svg)](https://hex.pm/packages/ash_money) [![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](../ash_money.html)
611 | 
612 | # AshMoney
613 | 
614 | Welcome! This is the extension for working with money types in [Ash](../ash.html). This is a thin wrapper around the very excellent [ex\_money](../ex_money.html). It provides:
615 | 
616 | - An [`Ash.Type`](../ash/3.4.48/Ash.Type.html) for representing [`Money`](../ex_money/5.18.0/Money.html)
617 | - An `AshPostgres.Extension` for supporting common money operations directly in the database
618 | - An implementation of [`Comp`](../ash/3.4.48/Comp.html) for `%Money{}`, allowing Ash to compare them.
619 | 
620 | ## [](readme.html#tutorials)Tutorials
621 | 
622 | - [Getting Started with AshMoney](getting-started-with-ash-money.html)
623 | 
624 | [← Previous Page API Reference](api-reference.html)
625 | 
626 | [Next Page → Getting Started With AshMoney](getting-started-with-ash-money.html)
627 | 
628 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) ([current file](https://preview.hex.pm/preview/ash_money/0.1.15/show/README.md)) Search HexDocs [Download ePub version](ash_money.epub "ePub version")
629 | 
630 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
631 | 
```

--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_cloak.md:
--------------------------------------------------------------------------------

```markdown
  1 | [ash\_cloak](https://github.com/ash-project/ash_cloak)
  2 | 
  3 | v0.1.2
  4 | 
  5 | - GUIDES
  6 | - Modules
  7 | 
  8 | <!--THE END-->
  9 | 
 10 | <!--THE END-->
 11 | 
 12 | Search documentation of ash\_cloak
 13 | 
 14 | Settings
 15 | 
 16 | # [View Source](https://github.com/ash-project/ash_cloak "View Source") API Reference ash\_cloak v0.1.2
 17 | 
 18 | ## [](api-reference.html#modules)Modules
 19 | 
 20 | [AshCloak](AshCloak.html)
 21 | 
 22 | An extension for encrypting attributes of a resource.
 23 | 
 24 | [AshCloak.Changes.Encrypt](AshCloak.Changes.Encrypt.html)
 25 | 
 26 | Takes an argument, and encrypts it into an attribute called `encrypted_{attribute}`
 27 | 
 28 | [AshCloak.Cloak.Options](AshCloak.Cloak.Options.html)
 29 | 
 30 | Mod Docs
 31 | 
 32 | [AshCloak.Info](AshCloak.Info.html)
 33 | 
 34 | Introspection functions for the [`AshCloak`](AshCloak.html) extension.
 35 | 
 36 | [Next Page → Home](readme.html)
 37 | 
 38 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version")
 39 | 
 40 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 41 | [ash\_cloak](https://github.com/ash-project/ash_cloak)
 42 | 
 43 | v0.1.2
 44 | 
 45 | - GUIDES
 46 | - Modules
 47 | 
 48 | <!--THE END-->
 49 | 
 50 | <!--THE END-->
 51 | 
 52 | Search documentation of ash\_cloak
 53 | 
 54 | Settings
 55 | 
 56 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/changes/encrypt.ex#L1 "View Source") AshCloak.Changes.Encrypt (ash\_cloak v0.1.2)
 57 | 
 58 | Takes an argument, and encrypts it into an attribute called `encrypted_{attribute}`
 59 | 
 60 | # [](AshCloak.Changes.Encrypt.html#summary)Summary
 61 | 
 62 | ## [Functions](AshCloak.Changes.Encrypt.html#functions)
 63 | 
 64 | [atomic(changeset, opts, \_)](AshCloak.Changes.Encrypt.html#atomic/3)
 65 | 
 66 | Callback implementation for [`Ash.Resource.Change.atomic/3`](../ash/3.4.1/Ash.Resource.Change.html#c:atomic/3).
 67 | 
 68 | [change(changeset, opts, \_)](AshCloak.Changes.Encrypt.html#change/3)
 69 | 
 70 | Callback implementation for [`Ash.Resource.Change.change/3`](../ash/3.4.1/Ash.Resource.Change.html#c:change/3).
 71 | 
 72 | # [](AshCloak.Changes.Encrypt.html#functions)Functions
 73 | 
 74 | [Link to this function](AshCloak.Changes.Encrypt.html#atomic/3 "Link to this function")
 75 | 
 76 | # atomic(changeset, opts, \_)
 77 | 
 78 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/changes/encrypt.ex#L19 "View Source")
 79 | 
 80 | Callback implementation for [`Ash.Resource.Change.atomic/3`](../ash/3.4.1/Ash.Resource.Change.html#c:atomic/3).
 81 | 
 82 | [Link to this function](AshCloak.Changes.Encrypt.html#change/3 "Link to this function")
 83 | 
 84 | # change(changeset, opts, \_)
 85 | 
 86 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/changes/encrypt.ex#L5 "View Source")
 87 | 
 88 | Callback implementation for [`Ash.Resource.Change.change/3`](../ash/3.4.1/Ash.Resource.Change.html#c:change/3).
 89 | 
 90 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version")
 91 | 
 92 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
 93 | [ash\_cloak](https://github.com/ash-project/ash_cloak)
 94 | 
 95 | v0.1.2
 96 | 
 97 | - GUIDES
 98 | - Modules
 99 | 
100 | <!--THE END-->
101 | 
102 | <!--THE END-->
103 | 
104 | Search documentation of ash\_cloak
105 | 
106 | Settings
107 | 
108 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") AshCloak.Cloak.Options (ash\_cloak v0.1.2)
109 | 
110 | Mod Docs
111 | 
112 | # [](AshCloak.Cloak.Options.html#summary)Summary
113 | 
114 | ## [Functions](AshCloak.Cloak.Options.html#functions)
115 | 
116 | [attributes(value)](AshCloak.Cloak.Options.html#attributes/1)
117 | 
118 | Hello 3
119 | 
120 | [decrypt\_by\_default(value)](AshCloak.Cloak.Options.html#decrypt_by_default/1)
121 | 
122 | Hello 3
123 | 
124 | [on\_decrypt(value)](AshCloak.Cloak.Options.html#on_decrypt/1)
125 | 
126 | Hello 3
127 | 
128 | [vault(value)](AshCloak.Cloak.Options.html#vault/1)
129 | 
130 | Hello 3
131 | 
132 | # [](AshCloak.Cloak.Options.html#functions)Functions
133 | 
134 | [Link to this macro](AshCloak.Cloak.Options.html#attributes/1 "Link to this macro")
135 | 
136 | # attributes(value)
137 | 
138 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro)
139 | 
140 | Hello 3
141 | 
142 | [Link to this macro](AshCloak.Cloak.Options.html#decrypt_by_default/1 "Link to this macro")
143 | 
144 | # decrypt\_by\_default(value)
145 | 
146 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro)
147 | 
148 | Hello 3
149 | 
150 | [Link to this macro](AshCloak.Cloak.Options.html#on_decrypt/1 "Link to this macro")
151 | 
152 | # on\_decrypt(value)
153 | 
154 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro)
155 | 
156 | Hello 3
157 | 
158 | [Link to this macro](AshCloak.Cloak.Options.html#vault/1 "Link to this macro")
159 | 
160 | # vault(value)
161 | 
162 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro)
163 | 
164 | Hello 3
165 | 
166 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version")
167 | 
168 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
169 | [ash\_cloak](https://github.com/ash-project/ash_cloak)
170 | 
171 | v0.1.2
172 | 
173 | - GUIDES
174 | - Modules
175 | 
176 | <!--THE END-->
177 | 
178 | <!--THE END-->
179 | 
180 | Search documentation of ash\_cloak
181 | 
182 | Settings
183 | 
184 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak.ex#L1 "View Source") AshCloak (ash\_cloak v0.1.2)
185 | 
186 | An extension for encrypting attributes of a resource.
187 | 
188 | See the getting started guide for more information.
189 | 
190 | # [](AshCloak.html#summary)Summary
191 | 
192 | ## [Functions](AshCloak.html#functions)
193 | 
194 | [cloak(body)](AshCloak.html#cloak/1)
195 | 
196 | Hello!
197 | 
198 | [encrypt\_and\_set(changeset, key, value)](AshCloak.html#encrypt_and_set/3)
199 | 
200 | Encrypts and writes to an encrypted attribute.
201 | 
202 | # [](AshCloak.html#functions)Functions
203 | 
204 | [Link to this macro](AshCloak.html#cloak/1 "Link to this macro")
205 | 
206 | # cloak(body)
207 | 
208 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak.ex#L41 "View Source") (macro)
209 | 
210 | Hello!
211 | 
212 | [Link to this function](AshCloak.html#encrypt_and_set/3 "Link to this function")
213 | 
214 | # encrypt\_and\_set(changeset, key, value)
215 | 
216 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak.ex#L50 "View Source")
217 | 
218 | ```
219 | @spec encrypt_and_set(Ash.Changeset.t(), attr :: atom(), term :: term()) ::
220 |   Ash.Changeset.t()
221 | ```
222 | 
223 | Encrypts and writes to an encrypted attribute.
224 | 
225 | If the changeset is pending (i.e not currently running the action), then it is added as a before\_action hook. Otherwise, it is run immediately
226 | 
227 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version")
228 | 
229 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
230 | [ash\_cloak](https://github.com/ash-project/ash_cloak)
231 | 
232 | v0.1.2
233 | 
234 | - GUIDES
235 | - Modules
236 | 
237 | <!--THE END-->
238 | 
239 | <!--THE END-->
240 | 
241 | Search documentation of ash\_cloak
242 | 
243 | Settings
244 | 
245 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L1 "View Source") AshCloak.Info (ash\_cloak v0.1.2)
246 | 
247 | Introspection functions for the [`AshCloak`](AshCloak.html) extension.
248 | 
249 | # [](AshCloak.Info.html#summary)Summary
250 | 
251 | ## [Functions](AshCloak.Info.html#functions)
252 | 
253 | [cloak\_attributes(dsl\_or\_extended)](AshCloak.Info.html#cloak_attributes/1)
254 | 
255 | The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.
256 | 
257 | [cloak\_attributes!(dsl\_or\_extended)](AshCloak.Info.html#cloak_attributes!/1)
258 | 
259 | The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.
260 | 
261 | [cloak\_decrypt\_by\_default(dsl\_or\_extended)](AshCloak.Info.html#cloak_decrypt_by_default/1)
262 | 
263 | A list of attributes that should be decrypted (their calculation should be loaded) by default.
264 | 
265 | [cloak\_decrypt\_by\_default!(dsl\_or\_extended)](AshCloak.Info.html#cloak_decrypt_by_default!/1)
266 | 
267 | A list of attributes that should be decrypted (their calculation should be loaded) by default.
268 | 
269 | [cloak\_on\_decrypt(dsl\_or\_extended)](AshCloak.Info.html#cloak_on_decrypt/1)
270 | 
271 | A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}`
272 | 
273 | [cloak\_on\_decrypt!(dsl\_or\_extended)](AshCloak.Info.html#cloak_on_decrypt!/1)
274 | 
275 | A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}`
276 | 
277 | [cloak\_options(dsl\_or\_extended)](AshCloak.Info.html#cloak_options/1)
278 | 
279 | cloak DSL options
280 | 
281 | [cloak\_vault(dsl\_or\_extended)](AshCloak.Info.html#cloak_vault/1)
282 | 
283 | The vault to use to encrypt &amp; decrypt the value
284 | 
285 | [cloak\_vault!(dsl\_or\_extended)](AshCloak.Info.html#cloak_vault!/1)
286 | 
287 | The vault to use to encrypt &amp; decrypt the value
288 | 
289 | # [](AshCloak.Info.html#functions)Functions
290 | 
291 | [Link to this function](AshCloak.Info.html#cloak_attributes/1 "Link to this function")
292 | 
293 | # cloak\_attributes(dsl\_or\_extended)
294 | 
295 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
296 | 
297 | ```
298 | @spec cloak_attributes(dsl_or_extended :: module() | map()) ::
299 |   {:ok, [atom()]} | :error
300 | ```
301 | 
302 | The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.
303 | 
304 | [Link to this function](AshCloak.Info.html#cloak_attributes!/1 "Link to this function")
305 | 
306 | # cloak\_attributes!(dsl\_or\_extended)
307 | 
308 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
309 | 
310 | ```
311 | @spec cloak_attributes!(dsl_or_extended :: module() | map()) :: [atom()] | no_return()
312 | ```
313 | 
314 | The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.
315 | 
316 | [Link to this function](AshCloak.Info.html#cloak_decrypt_by_default/1 "Link to this function")
317 | 
318 | # cloak\_decrypt\_by\_default(dsl\_or\_extended)
319 | 
320 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
321 | 
322 | ```
323 | @spec cloak_decrypt_by_default(dsl_or_extended :: module() | map()) ::
324 |   {:ok, [atom()]} | :error
325 | ```
326 | 
327 | A list of attributes that should be decrypted (their calculation should be loaded) by default.
328 | 
329 | [Link to this function](AshCloak.Info.html#cloak_decrypt_by_default!/1 "Link to this function")
330 | 
331 | # cloak\_decrypt\_by\_default!(dsl\_or\_extended)
332 | 
333 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
334 | 
335 | ```
336 | @spec cloak_decrypt_by_default!(dsl_or_extended :: module() | map()) ::
337 |   [atom()] | no_return()
338 | ```
339 | 
340 | A list of attributes that should be decrypted (their calculation should be loaded) by default.
341 | 
342 | [Link to this function](AshCloak.Info.html#cloak_on_decrypt/1 "Link to this function")
343 | 
344 | # cloak\_on\_decrypt(dsl\_or\_extended)
345 | 
346 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
347 | 
348 | ```
349 | @spec cloak_on_decrypt(dsl_or_extended :: module() | map()) ::
350 |   {:ok, (any(), any(), any(), any() -> any()) | mfa()} | :error
351 | ```
352 | 
353 | A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}`
354 | 
355 | [Link to this function](AshCloak.Info.html#cloak_on_decrypt!/1 "Link to this function")
356 | 
357 | # cloak\_on\_decrypt!(dsl\_or\_extended)
358 | 
359 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
360 | 
361 | ```
362 | @spec cloak_on_decrypt!(dsl_or_extended :: module() | map()) ::
363 |   ((any(), any(), any(), any() -> any()) | mfa()) | no_return()
364 | ```
365 | 
366 | A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}`
367 | 
368 | [Link to this function](AshCloak.Info.html#cloak_options/1 "Link to this function")
369 | 
370 | # cloak\_options(dsl\_or\_extended)
371 | 
372 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
373 | 
374 | ```
375 | @spec cloak_options(dsl_or_extended :: module() | map()) :: %{
376 |   required(atom()) => any()
377 | }
378 | ```
379 | 
380 | cloak DSL options
381 | 
382 | Returns a map containing the and any configured or default values.
383 | 
384 | [Link to this function](AshCloak.Info.html#cloak_vault/1 "Link to this function")
385 | 
386 | # cloak\_vault(dsl\_or\_extended)
387 | 
388 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
389 | 
390 | ```
391 | @spec cloak_vault(dsl_or_extended :: module() | map()) :: {:ok, module()} | :error
392 | ```
393 | 
394 | The vault to use to encrypt &amp; decrypt the value
395 | 
396 | [Link to this function](AshCloak.Info.html#cloak_vault!/1 "Link to this function")
397 | 
398 | # cloak\_vault!(dsl\_or\_extended)
399 | 
400 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
401 | 
402 | ```
403 | @spec cloak_vault!(dsl_or_extended :: module() | map()) :: module() | no_return()
404 | ```
405 | 
406 | The vault to use to encrypt &amp; decrypt the value
407 | 
408 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version")
409 | 
410 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
411 | [ash\_cloak](https://github.com/ash-project/ash_cloak)
412 | 
413 | v0.1.2
414 | 
415 | - GUIDES
416 | - Modules
417 | 
418 | <!--THE END-->
419 | 
420 | <!--THE END-->
421 | 
422 | Search documentation of ash\_cloak
423 | 
424 | Settings
425 | 
426 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/documentation/dsls/DSL:-AshCloak.md#L1 "View Source") DSL: AshCloak
427 | 
428 | An extension for encrypting attributes of a resource.
429 | 
430 | See the getting started guide for more information.
431 | 
432 | ## [](dsl-ashcloak.html#cloak)cloak
433 | 
434 | Encrypt attributes of a resource
435 | 
436 | ### [](dsl-ashcloak.html#options)Options
437 | 
438 | NameTypeDefaultDocs[`vault`](dsl-ashcloak.html#cloak-vault)`module`The vault to use to encrypt &amp; decrypt the value[`attributes`](dsl-ashcloak.html#cloak-attributes)`atom | list(atom)``[]`The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.[`decrypt_by_default`](dsl-ashcloak.html#cloak-decrypt_by_default)`atom | list(atom)``[]`A list of attributes that should be decrypted (their calculation should be loaded) by default.[`on_decrypt`](dsl-ashcloak.html#cloak-on_decrypt)`(any, any, any, any -> any) | mfa`A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}`
439 | 
440 | [← Previous Page How does AshCloak work?](how-does-ash-cloak-work.html)
441 | 
442 | [Next Page → Change Log](changelog.html)
443 | 
444 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) ([current file](https://preview.hex.pm/preview/ash_cloak/0.1.2/show/documentation/dsls/DSL:-AshCloak.md)) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version")
445 | 
446 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
447 | [ash\_cloak](https://github.com/ash-project/ash_cloak)
448 | 
449 | v0.1.2
450 | 
451 | - GUIDES
452 | - Modules
453 | 
454 | <!--THE END-->
455 | 
456 | <!--THE END-->
457 | 
458 | Search documentation of ash\_cloak
459 | 
460 | Settings
461 | 
462 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/documentation/tutorials/getting-started-with-ash-cloak.md#L1 "View Source") Get Started with AshCloak
463 | 
464 | ## [](getting-started-with-ash-cloak.html#installation)Installation
465 | 
466 | Add `ash_cloak` to your list of dependencies in `mix.exs`:
467 | 
468 | ```
469 | {:ash_cloak, "~> 0.1.2"}
470 | ```
471 | 
472 | Follow [the cloak getting started guide](../cloak/readme.html) to add `cloak` as a dependency, as AshCloak does not add a vault implementation for you. Note that you do not need `cloak_ecto` because your Ash data layer will take care of this.
473 | 
474 | Alternatively you could use your own vault module that implements `encrypt!` and `decrypt!`, but we recommend using `Cloak` to achieve that goal. See the [cloak vault guide](../cloak/install.html#create-a-vault)
475 | 
476 | ### [](getting-started-with-ash-cloak.html#add-the-ashcloak-extension-to-your-resource)Add the [`AshCloak`](AshCloak.html) extension to your resource
477 | 
478 | ```
479 | defmodule User do
480 |   use Ash.Resource, extensions: [AshCloak]
481 | 
482 |   cloak do
483 |     # the vault to use to encrypt them
484 |     vault MyApp.Vault
485 | 
486 |     # the attributes to encrypt
487 |     attributes [:address, :phone_number]
488 |     
489 |     # This is just equivalent to always providing `load: fields` on all calls
490 |     decrypt_by_default [:address]
491 |     
492 |     # An MFA or function to be invoked beforce any decryption
493 |     on_decrypt fn records, field, context ->
494 |       # Ash has policies that allow forbidding certain users to load data.
495 |       # You should generally use those for authorization rules, and
496 |       # only use this callback for auditing/logging.
497 |       Audit.user_accessed_encrypted_field(records, field, context)
498 | 
499 |       if context.user.name == "marty" do
500 |         {:error, "No martys at the party!"}
501 |       else
502 |         :ok
503 |       end
504 |     end
505 |   end
506 | end
507 | ```
508 | 
509 | [← Previous Page Home](readme.html)
510 | 
511 | [Next Page → How does AshCloak work?](how-does-ash-cloak-work.html)
512 | 
513 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) ([current file](https://preview.hex.pm/preview/ash_cloak/0.1.2/show/documentation/tutorials/getting-started-with-ash-cloak.md)) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version")
514 | 
515 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
516 | [ash\_cloak](https://github.com/ash-project/ash_cloak)
517 | 
518 | v0.1.2
519 | 
520 | - GUIDES
521 | - Modules
522 | 
523 | <!--THE END-->
524 | 
525 | <!--THE END-->
526 | 
527 | Search documentation of ash\_cloak
528 | 
529 | Settings
530 | 
531 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/documentation/topics/how-does-ash-cloak-work.md#L1 "View Source") How does AshCloak work?
532 | 
533 | ## [](how-does-ash-cloak-work.html#rewrite-attributes-to-calculations)Rewrite attributes to calculations
534 | 
535 | First, AshCloak changes the name of each cloaked attribute to `encrypted_<name>`, and sets `public?: false` and `sensitive?: true`.
536 | 
537 | Then it adds a *calculation* matching the original attribute that, when loaded, will decrypt the given attribute and call any configured `on_decrypt` callbacks.
538 | 
539 | ## [](how-does-ash-cloak-work.html#modify-actions)Modify Actions
540 | 
541 | AshCloak then goes through each action that accepts the attribute and removes the attribute from the accept list.
542 | 
543 | Then it adds an argument by the same name, and a `change` that encrypts the attribute value.
544 | 
545 | This `change` also deletes the argument from the arguments list and from the params. This is a small extra layer of security to prevent accidental leakage of the value.
546 | 
547 | ## [](how-does-ash-cloak-work.html#add-preparation-and-change)Add `preparation` and `change`
548 | 
549 | Finally, it add a `preparation` and a `change` that will automatically load the corresponding calculations for any attribute in the `decrypt_by_default` list.
550 | 
551 | ## [](how-does-ash-cloak-work.html#the-result)The result
552 | 
553 | The cloaked attribute will now seamlessly encrypt when writing and decrypt on request.
554 | 
555 | [← Previous Page Get Started with AshCloak](getting-started-with-ash-cloak.html)
556 | 
557 | [Next Page → DSL: AshCloak](dsl-ashcloak.html)
558 | 
559 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) ([current file](https://preview.hex.pm/preview/ash_cloak/0.1.2/show/documentation/topics/how-does-ash-cloak-work.md)) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version")
560 | 
561 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
562 | [ash\_cloak](https://github.com/ash-project/ash_cloak)
563 | 
564 | v0.1.2
565 | 
566 | - GUIDES
567 | - Modules
568 | 
569 | <!--THE END-->
570 | 
571 | <!--THE END-->
572 | 
573 | Search documentation of ash\_cloak
574 | 
575 | Settings
576 | 
577 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/README.md#L1 "View Source") Home
578 | 
579 | ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only)
580 | 
581 | ![Elixir CI](https://github.com/ash-project/ash_cloak/workflows/CI/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Hex version badge](https://img.shields.io/hexpm/v/ash_cloak.svg)](https://hex.pm/packages/ash_cloak) [![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](../ash_cloak.html)
582 | 
583 | # AshCloak
584 | 
585 | AshCloak is an [Ash](../ash.html) extension to seamlessly encrypt and decrypt attributes of your resources.
586 | 
587 | Encrypting attributes ensures that sensitive fields are not stored in the clear in your data layer.
588 | 
589 | AshCloak can be used with any Ash data layer.
590 | 
591 | It's recommended to use [Cloak](https://github.com/danielberkompas/cloak) as a vault implementation but you could also build your own.
592 | 
593 | ## [](readme.html#tutorials)Tutorials
594 | 
595 | - [Get Started with AshCloak](getting-started-with-ash-cloak.html)
596 | 
597 | ## [](readme.html#topics)Topics
598 | 
599 | - [How does AshCloak work?](how-does-ash-cloak-work.html)
600 | 
601 | ## [](readme.html#reference)Reference
602 | 
603 | - [AshCloak DSL](dsl-ashcloak.html)
604 | 
605 | [← Previous Page API Reference](api-reference.html)
606 | 
607 | [Next Page → Get Started with AshCloak](getting-started-with-ash-cloak.html)
608 | 
609 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) ([current file](https://preview.hex.pm/preview/ash_cloak/0.1.2/show/README.md)) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version")
610 | 
611 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
612 | 
```
Page 1/10FirstPrevNextLast