This is page 1 of 9. Use http://codebase.md/m-gonzalo/cosa-sai?lines=false&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:
--------------------------------------------------------------------------------
```
# dependencies (bun install)
node_modules
# output
out
dist
*.tgz
# code coverage
coverage
*.lcov
# logs
logs
*.log
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# caches
.eslintcache
.cache
*.tsbuildinfo
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store
# Local DB
gemini_docs.sqlite
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# Gemini Docs MCP Server
[](https://smithery.ai/server/@M-Gonzalo/cosa-sai)
## Description
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.
This approach offers several advantages over simply browsing the web or using a search engine:
* **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.
* **Overcomes context window limitations:** By providing the documentation directly, the LLM can access more information than would be possible with web search alone.
* **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?".
It also overcomes some problemmatic hurdles of traditional RAG systems:
* **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.
* **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.
* **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.
There are some limitations, though:
* **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.
* **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.
* **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.
## Features
* 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.
* Uses the Gemini API to answer questions about the documentation.
* Supports multiple tools for querying the documentation:
* `can_x_be_done`: Check if a specific task can be done in a given technology.
* `hints_for_problem`: Get hints for solving a specific problem.
* `is_this_good_practice`: Check if a code snippet follows good practices.
* `how_to_do_x`: Get examples and alternative approaches for a specific task.
* Provides a logging system for debugging (enabled with the `--verbose` flag).
## Getting Started
### Installing via Smithery
To install Gemini Docs Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@M-Gonzalo/cosa-sai):
```bash
npx -y @smithery/cli install @M-Gonzalo/cosa-sai --client claude
```
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.
Here's the configuration for this server:
```json
{
"command": "bun",
"args": [
"--watch",
"path/to/repo/cosa-sai-mcp/src/index.ts",
"--verbose"
],
"env": {
"GEMINI_API_KEY": "<your_gemini_api_key>"
},
"disabled": false,
"alwaysAllow": [
"can_x_be_done",
"hints_for_problem",
"is_this_good_practice",
"how_to_do_x"
],
"timeout": 60 // in seconds
}
```
## Procuring and Sanitizing the Knowledge Base
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.
An optional sanitation process can be performed to clean up the original documentation from styling and other unnecessary content.
Here are some basic tools for doing so. Better solutions are encouraged:
**Naive Scrapper:**
```bash
wget --mirror --convert-links --adjust-extension --page-requisites --no-parent --directory-prefix=./local_copy --no-verbose --show-progress $1
```
**Quick and Dirty Conversor to Markdown-ish:**
```bash
#!/bin/bash
directory="${1:-.}" # Default to current directory if no argument is provided
output_file="${2:-concatenated.md}" # Default output file name
echo "Concatenating files in '$directory' into '$output_file'..."
# Clear output file if it exists
truncate -s 0 "$output_file"
# Find all files (excluding directories) and process them
find "$directory" -type f -name '*.html' | while IFS= read -r file; do
echo "=== ${file#./} ===" >> "$output_file"
cat "$file" \
| grep -v 'base64' \
| html2markdown >> "$output_file"
echo -e "\n" >> "$output_file"
done
echo "Done! Output saved to '$output_file'"
```
## Usage
This server provides the following tools:
* **can\_x\_be\_done:** Checks if a specific task can be done in a given technology.
* **Input:** `docs`, `prompt`, `x`, `technology`
* **Output:** `success`, `data`
* **hints\_for\_problem:** Gets hints for solving a specific problem.
* **Input:** `docs`, `prompt`, `problem`, `context`, `environment`
* **Output:** `success`, `data`
* **is\_this\_good\_practice:** Checks if a code snippet follows good practices.
* **Input:** `docs`, `prompt`, `snippet`, `context`
* **Output:** `success`, `data`
* **how\_to\_do\_x:** Gets examples and alternative approaches for a specific task.
* **Input:** `docs`, `prompt`, `x`, `technology`
* **Output:** `success`, `data`
## Contributing
Contributions are welcome! Please follow these guidelines:
1. Fork the repository.
2. Create a new branch for your feature or bug fix.
3. Make your changes and commit them with descriptive commit messages.
4. Submit a pull request.
## License
This project is licensed under the MIT License.
## Disclaimer
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:
--------------------------------------------------------------------------------
```
You are an expert JavaScript programmer with extensive knowledge about JavaScript's core features, best practices, and modern development patterns.
```
--------------------------------------------------------------------------------
/test/prompts/phoenix.txt:
--------------------------------------------------------------------------------
```
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:
--------------------------------------------------------------------------------
```
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
{
"compilerOptions": {
"target": "ES2021",
"module": "ESNext",
"moduleResolution": "bundler",
"types": ["bun-types"],
"allowJs": true,
"strict": true,
"noEmit": true,
"allowImportingTsExtensions": true,
"skipLibCheck": true,
"jsx": "react-jsx"
}
}
```
--------------------------------------------------------------------------------
/test/prompts/ash-framework.txt:
--------------------------------------------------------------------------------
```
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
{
"name": "gemini-docs-mcp",
"module": "index.ts",
"type": "module",
"private": true,
"devDependencies": {
"@types/bun": "latest",
"@types/js-yaml": "^4.0.9"
},
"peerDependencies": {
"typescript": "^5"
},
"dependencies": {
"@google/generative-ai": "^0.22.0",
"@modelcontextprotocol/sdk": "^1.6.0",
"js-yaml": "^4.1.0",
"zod": "^3.24.2"
}
}
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
FROM oven/bun:latest
WORKDIR /app
# Copy the entire repository into a subdirectory to match expected structure
COPY . /app/gemini-docs-mcp
# Install dependencies in the subdirectory
WORKDIR /app/gemini-docs-mcp
RUN bun install --ignore-scripts
# Set workdir back to /app for running the command
WORKDIR /app
CMD ["bun", "/app/gemini-docs-mcp/src/index.ts"]
```
--------------------------------------------------------------------------------
/src/logger.ts:
--------------------------------------------------------------------------------
```typescript
import { join } from 'path';
import type { FileSink } from 'bun';
export class Logger {
private static writer: FileSink | null = null;
private static enabled = false;
static enable() {
this.enabled = true;
this.writer = Bun.file(join(import.meta.dir, '..', 'debug.log')).writer();
}
static log(message: string) {
if (!this.enabled) return;
const timestamp = new Date().toISOString();
this.writer?.write(`[${timestamp}] ${message}\n`);
this.writer?.flush();
}
static close() {
if (this.writer) {
this.writer.end();
this.writer = null;
}
}
}
```
--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_appsignal.md:
--------------------------------------------------------------------------------
```markdown
[](AshAppsignal.html)
[ash\_appsignal](AshAppsignal.html)
v0.1.3
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_appsignal
Settings
# AshAppsignal (ash\_appsignal v0.1.3)
Documentation for [`AshAppsignal`](AshAppsignal.html#content).
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
startCommand:
type: stdio
configSchema:
# JSON Schema defining the configuration options for the MCP.
type: object
required:
- geminiApiKey
properties:
geminiApiKey:
type: string
description: API key for authenticating with the Gemini API.
description: Configuration for Gemini Docs MCP server.
commandFunction:
# A JS function that produces the CLI command based on the given config to start the MCP on stdio.
|-
(config) => ({ command: 'bun', args: ['/app/gemini-docs-mcp/src/index.ts', '--verbose'], env: { GEMINI_API_KEY: config.geminiApiKey } })
exampleConfig:
geminiApiKey: dummy_api_key_here
```
--------------------------------------------------------------------------------
/test/docs/javascript-docs/sample.md:
--------------------------------------------------------------------------------
```markdown
# Sample Documentation
## Functions
### Array Operations
`map()` creates a new array populated with the results of calling a provided function on every element in the array.
```javascript
const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2);
// doubled: [2, 4, 6]
```
`filter()` creates a new array with all elements that pass the test implemented by the provided function.
```javascript
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(x => x % 2 === 0);
// evens: [2, 4]
```
`reduce()` executes a reducer function on each element of the array, resulting in a single output value.
```javascript
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
// sum: 10
```
## Best Practices
### Array Operations
1. Use `map()` when you want to transform each element in an array
2. Use `filter()` when you want to select elements that meet certain criteria
3. Use `reduce()` when you want to combine all elements into a single value
4. Prefer array methods over loops for better readability and maintainability
5. Chain array methods for complex operations instead of nesting them
```
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
```typescript
// Base file record types
export interface FileRecord {
file_path: string;
file_id: string;
file_uri: string;
}
// Gemini API types for file handling
export interface GeminiFileData {
fileUri: string;
}
export interface GeminiContentPart {
text?: string;
fileData?: GeminiFileData;
}
export interface GeminiContent {
parts: GeminiContentPart[];
}
// Response types for different queries
export interface GeminiResponse {
success: boolean;
data: CanXBeResponse | HowToDoResponse | HintsProblemResponse | GoodPracticeResponse;
}
export interface CanXBeResponse {
yes?: {
example: string;
betterApproach?: string;
};
no?: {
reason: string;
alternative: string;
};
}
export interface HowToDoResponse {
examples: Array<{
content: string;
description: string;
}>;
alternativeApproaches: Array<{
description: string;
content: string;
}>;
}
export interface HintsProblemResponse {
cantSayForSure?: {
topics: string[];
};
try?: Array<{
possibleReason: string;
possibleSolution: string;
}>;
seeAlso?: string[];
}
export interface GoodPracticeResponse {
isGoodPractice: {
yes?: {
cosmeticImprovements?: string[];
};
no?: {
betterAlternatives: string[];
};
};
}
// Tool parameter types
export interface ToolParams {
docs: string;
prompt: string;
}
export interface CanXBeDoneParams extends ToolParams {
x: string;
technology: string;
}
export interface HowToDoXParams extends ToolParams {
x: string;
technology: string;
}
export interface HintsForProblemParams extends ToolParams {
problem: string;
context: string;
environment: Record<string, unknown>;
}
export interface IsThisGoodPracticeParams extends ToolParams {
snippet: string;
context: string;
}
```
--------------------------------------------------------------------------------
/prompts/default.txt:
--------------------------------------------------------------------------------
```
When answering queries:
1. Ground your responses in the provided documentation
- Base your answers on the actual documentation content
- Stay within the parameters of what the docs describe
- If multiple approaches exist in the docs, present them all
2. Be comprehensive and detailed
- Provide complete, working examples
- Include explanations for key concepts
- Cover edge cases and considerations
- Explain the rationale behind recommendations
3. Format responses in YAML matching the query type structure:
- can_x_be_done: {success, yes:{example, betterApproach} | no:{reason, alternative}}
- how_to_do_x: {success, examples[], alternativeApproaches[]}
- hints_for_problem: {success, cantSayForSure:{topics[]} | try:[{possibleReason, possibleSolution}], seeAlso[]}
- is_this_good_practice: {success, isGoodPractice:{yes:{cosmeticImprovements[]} | no:{betterAlternatives[]}}}
Format all responses as YAML with this structure for different query types:
For "can X be done":
success: true
data:
yes:
example: "<code example>"
betterApproach: "<optional better approach>"
# OR
no:
reason: "<why it can't be done>"
alternative: "<alternative solution>"
For "how to do X":
success: true
data:
examples:
- content: "<code example 1>"
description: "<description 1>"
- content: "<code example 2>"
description: "<description 2>"
alternativeApproaches:
- description: "<approach description 1>"
content: "<approach code 1>"
- description: "<approach description 2>"
content: "<approach code 2>"
For "hints for problem":
success: true
data:
cantSayForSure:
topics:
- "<topic 1>"
- "<topic 2>"
# OR
try:
- possibleReason: "<reason 1>"
possibleSolution: "<solution 1>"
- possibleReason: "<reason 2>"
possibleSolution: "<solution 2>"
seeAlso:
- "<related topic 1>"
- "<related topic 2>"
For "is this good practice":
success: true
data:
isGoodPractice:
yes:
cosmeticImprovements:
- "<improvement 1>"
- "<improvement 2>"
# OR
no:
betterAlternatives:
- "<alternative 1>"
- "<alternative 2>"
IMPORTANT: All code snippets must be properly escaped strings in YAML. Do not use literal blocks or code fences.
Remember: Keep your response as a clean YAML document with no extra formatting.
```
--------------------------------------------------------------------------------
/src/db.ts:
--------------------------------------------------------------------------------
```typescript
import { Database } from 'bun:sqlite';
import type { FileRecord } from './types';
import { join, resolve } from 'path';
import { Logger } from './logger';
export class GeminiDocsDB {
private db: Database;
private dbPath: string;
constructor() {
try {
this.dbPath = resolve(join(import.meta.dir, '..', 'gemini_docs.sqlite'));
this.db = new Database(this.dbPath, { create: true });
this.init();
} catch (error) {
console.error('[DB] Failed to initialize database:', error);
Logger.log(`[DBDebug] Failed to initialize database: ${error}`);
throw error;
}
}
private readonly TABLE_SQL = `
CREATE TABLE gemini_files (
file_id TEXT PRIMARY KEY,
file_path TEXT NOT NULL,
file_uri TEXT NOT NULL,
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
UNIQUE(file_path)
)
`;
private migrateSchema() {
Logger.log('[DBDebug] Schema migration not implemented - using existing schema');
}
private init() {
try {
const stmt = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='gemini_files'");
if (stmt.get()) {
Logger.log('[DBDebug] Found existing table, migrating schema...');
this.migrateSchema();
} else {
Logger.log('[DBDebug] Creating new database with latest schema...');
this.db.run(this.TABLE_SQL);
}
Logger.log('[DBDebug] Database schema initialized/verified');
} catch (error) {
console.error('[DB] Error during initialization:', error);
Logger.log(`[DBDebug] Error during initialization: ${error}`);
throw error;
}
}
async saveFileId(
filePath: string,
fileId: string,
fileUri: string
): Promise<void> {
try {
const stmt = this.db.prepare(
'INSERT OR REPLACE INTO gemini_files (file_id, file_path, file_uri) VALUES (?, ?, ?)'
);
stmt.run(fileId, filePath, fileUri);
Logger.log(`[DBDebug] Saved/Updated file record:
ID: ${fileId}
Path: ${filePath}
URI: ${fileUri}`);
} catch (error) {
Logger.log(`[DBDebug] Error saving file record:
Error: ${error}`);
console.error('[DB] Error saving file:', error);
throw error;
}
}
async getFile(fileHash: string): Promise<{
fileUri: string;
} | null> {
try {
const stmt = this.db.prepare(
'SELECT file_uri FROM gemini_files WHERE file_id = ? LIMIT 1'
);
const result = stmt.get(fileHash) as {
file_uri: string;
} | null;
if (result) {
Logger.log(`[DBDebug] Retrieved file record for hash ${fileHash}:
Hash: ${fileHash}
URI: ${result.file_uri}`);
}
return result
? {
fileUri: result.file_uri
}
: null;
} catch (error) {
Logger.log(`[DBDebug] Error retrieving file record for ${fileHash}: ${error}`);
console.error('[DB] Error getting file:', error);
throw error;
}
}
async getAllFiles(): Promise<FileRecord[]> {
try {
const stmt = this.db.prepare(
'SELECT file_path, file_id, file_uri FROM gemini_files ORDER BY file_id, file_path'
);
Logger.log('[DBDebug] Retrieved all file records from database');
return stmt.all() as FileRecord[];
} catch (error) {
console.error('[DB] Error getting files:', error);
Logger.log(`[DBDebug] Error retrieving all file records: ${error}`);
throw error;
}
}
close(): void {
this.db.close();
}
}
```
--------------------------------------------------------------------------------
/src/gemini.ts:
--------------------------------------------------------------------------------
```typescript
import { GoogleGenerativeAI } from '@google/generative-ai';
import { load } from 'js-yaml';
import { join } from 'path';
import { readFileSync } from 'fs';
import type { GeminiResponse, GeminiContent, GeminiContentPart, GeminiFileData } from './types';
// Error codes that should trigger a retry
const RETRYABLE_STATUS_CODES = new Set(['429', '503', '500', '502', '504']);
export class GeminiAPI {
private gemini: GoogleGenerativeAI;
private model: any;
private defaultPrompt: string;
private rateLimitDelay = 1000;
private maxRetries = 3;
private lastRequestTime = 0;
constructor() {
const API_KEY = process.env.GEMINI_API_KEY;
if (!API_KEY) {
throw new Error('GEMINI_API_KEY environment variable is required');
}
try {
this.gemini = new GoogleGenerativeAI(API_KEY);
this.model = this.gemini.getGenerativeModel({ model: 'gemini-1.5-pro' });
// Load default prompt using full path from project root
const defaultPromptPath = join(process.cwd(), 'gemini-docs-mcp', 'prompts', 'default.txt');
this.defaultPrompt = readFileSync(defaultPromptPath, 'utf-8');
} catch (error) {
console.error('[Gemini] Failed to initialize:', error);
throw error;
}
}
private async waitForRateLimit() {
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequestTime;
if (timeSinceLastRequest < this.rateLimitDelay) {
await Bun.sleep(this.rateLimitDelay - timeSinceLastRequest);
}
this.lastRequestTime = Date.now();
}
private shouldRetry(error: any): boolean {
// Check if error message includes status code
for (const code of RETRYABLE_STATUS_CODES) {
if (error.message?.includes(`[${code}`)) {
return true;
}
}
// Check for quota or availability keywords
return (
error.message?.includes('quota') ||
error.message?.includes('unavailable') ||
error.message?.includes('try again')
);
}
private formatPrompt(
query: string,
docRefs: GeminiFileData[],
context: Record<string, any> = {},
promptFile?: string
): GeminiContent[] {
let prompt = '';
// Add custom prompt if provided
if (promptFile) {
prompt = readFileSync(promptFile, 'utf-8') + '\n\n';
}
// Add default prompt and query
prompt += `${this.defaultPrompt}\n\nUSER QUERY: ${query}\nCONTEXT: ${JSON.stringify(context)}`;
const parts: GeminiContentPart[] = [{ text: prompt.trim() }];
for (const doc of docRefs) {
parts.push({ fileData: doc });
}
return [{ parts }];
}
private async retryWithBackoff<T>(
operation: () => Promise<T>,
retries = this.maxRetries,
initialDelay = this.rateLimitDelay
): Promise<T> {
let lastError: Error;
let delay = initialDelay;
for (let i = 0; i < retries; i++) {
try {
await this.waitForRateLimit();
return await operation();
} catch (error: any) {
lastError = error;
if (this.shouldRetry(error)) {
console.error(`[Gemini] Request failed (attempt ${i + 1}/${retries}), retrying in ${delay}ms:`, error.message);
await Bun.sleep(delay);
// Use more aggressive backoff for service issues
delay *= 3;
continue;
}
throw error;
}
}
throw lastError!;
}
private async query(contents: GeminiContent[]): Promise<string> {
return await this.retryWithBackoff(async () => {
const response = await this.model.generateContent({ contents });
return response.response.text();
});
}
private parseResponse(text: string): GeminiResponse {
try {
// Extract just the YAML portion by finding the first "success: true"
let yamlContent = text;
const successIndex = text.indexOf('success: true');
if (successIndex !== -1) {
yamlContent = text.slice(successIndex);
}
// Further clean up the text
yamlContent = yamlContent
.split('\n')
.map(line => line.replace(/^```\s*ya?ml\s*$/, '').replace(/^```\s*$/, ''))
.join('\n')
.trim();
const response = load(yamlContent) as GeminiResponse;
if (!response.success || !response.data) {
throw new Error('Invalid response format');
}
return response;
} catch (error) {
console.error('[Gemini] Failed to parse response:', error);
throw error;
}
}
async canXBeDone(
docRefs: GeminiFileData[],
x: string,
technology: string,
prompt?: string
): Promise<GeminiResponse> {
const contents = this.formatPrompt(
`Can I "${x}" in ${technology}? Show example if possible.`,
docRefs,
{ technology },
prompt
);
const result = await this.query(contents);
return this.parseResponse(result);
}
async howToDoX(
docRefs: GeminiFileData[],
x: string,
technology: string,
prompt?: string
): Promise<GeminiResponse> {
const contents = this.formatPrompt(
`How do I "${x}" in ${technology}? Show multiple examples and alternative approaches if available.`,
docRefs,
{ technology },
prompt
);
const result = await this.query(contents);
return this.parseResponse(result);
}
async hintsForProblem(
docRefs: GeminiFileData[],
problem: string,
context: string,
environment: Record<string, unknown>,
prompt?: string
): Promise<GeminiResponse> {
const contents = this.formatPrompt(
problem,
docRefs,
{ context, environment },
prompt
);
const result = await this.query(contents);
return this.parseResponse(result);
}
async isThisGoodPractice(
docRefs: GeminiFileData[],
snippet: string,
context: string,
prompt?: string
): Promise<GeminiResponse> {
const contents = this.formatPrompt(
`Is this a good practice:\n${snippet}\nContext: ${context}`,
docRefs,
{ context },
prompt
);
const result = await this.query(contents);
return this.parseResponse(result);
}
}
```
--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_csv.md:
--------------------------------------------------------------------------------
```markdown
[](https://github.com/ash-project/ash_csv)
[ash\_csv](https://github.com/ash-project/ash_csv)
v0.9.7
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_csv
Settings
# [View Source](https://github.com/ash-project/ash_csv "View Source") API Reference ash\_csv v0.9.7
## [](api-reference.html#modules)Modules
[AshCsv](AshCsv.html)
A CSV datalayer for the Ash framework
[AshCsv.DataLayer](AshCsv.DataLayer.html)
The data layer implementation for AshCsv
[AshCsv.DataLayer.Info](AshCsv.DataLayer.Info.html)
Introspection helpers for AshCsv.DataLayer
[Next Page → Home](readme.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_csv)
[ash\_csv](https://github.com/ash-project/ash_csv)
v0.9.7
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_csv
Settings
# [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)
The data layer implementation for AshCsv
# [](AshCsv.DataLayer.html#summary)Summary
## [Functions](AshCsv.DataLayer.html#functions)
[columns(resource)](AshCsv.DataLayer.html#columns/1) deprecated
See [`AshCsv.DataLayer.Info.columns/1`](AshCsv.DataLayer.Info.html#columns/1).
[create?(resource)](AshCsv.DataLayer.html#create?/1) deprecated
See [`AshCsv.DataLayer.Info.create?/1`](AshCsv.DataLayer.Info.html#create?/1).
[file(resource)](AshCsv.DataLayer.html#file/1) deprecated
See [`AshCsv.DataLayer.Info.file/1`](AshCsv.DataLayer.Info.html#file/1).
[filter\_matches(records, filter, domain)](AshCsv.DataLayer.html#filter_matches/3)
[header?(resource)](AshCsv.DataLayer.html#header?/1) deprecated
See [`AshCsv.DataLayer.Info.header?/1`](AshCsv.DataLayer.Info.html#header?/1).
[separator(resource)](AshCsv.DataLayer.html#separator/1) deprecated
See [`AshCsv.DataLayer.Info.separator/1`](AshCsv.DataLayer.Info.html#separator/1).
# [](AshCsv.DataLayer.html#functions)Functions
[Link to this function](AshCsv.DataLayer.html#columns/1 "Link to this function")
# columns(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L73 "View Source")
This function is deprecated. See \`AshCsv.DataLayer.Info.columns/1.
See [`AshCsv.DataLayer.Info.columns/1`](AshCsv.DataLayer.Info.html#columns/1).
[Link to this function](AshCsv.DataLayer.html#create?/1 "Link to this function")
# create?(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L82 "View Source")
This function is deprecated. See \`AshCsv.DataLayer.Info.create?/1.
See [`AshCsv.DataLayer.Info.create?/1`](AshCsv.DataLayer.Info.html#create?/1).
[Link to this function](AshCsv.DataLayer.html#file/1 "Link to this function")
# file(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L70 "View Source")
This function is deprecated. See \`AshCsv.DataLayer.Info.file/1.
See [`AshCsv.DataLayer.Info.file/1`](AshCsv.DataLayer.Info.html#file/1).
[Link to this function](AshCsv.DataLayer.html#filter_matches/3 "Link to this function")
# filter\_matches(records, filter, domain)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L397 "View Source")
[Link to this function](AshCsv.DataLayer.html#header?/1 "Link to this function")
# header?(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L79 "View Source")
This function is deprecated. See \`AshCsv.DataLayer.Info.header?/1.
See [`AshCsv.DataLayer.Info.header?/1`](AshCsv.DataLayer.Info.html#header?/1).
[Link to this function](AshCsv.DataLayer.html#separator/1 "Link to this function")
# separator(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L76 "View Source")
This function is deprecated. See \`AshCsv.DataLayer.Info.separator/1.
See [`AshCsv.DataLayer.Info.separator/1`](AshCsv.DataLayer.Info.html#separator/1).
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_csv)
[ash\_csv](https://github.com/ash-project/ash_csv)
v0.9.7
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_csv
Settings
# [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)
Introspection helpers for AshCsv.DataLayer
# [](AshCsv.DataLayer.Info.html#summary)Summary
## [Functions](AshCsv.DataLayer.Info.html#functions)
[columns(resource)](AshCsv.DataLayer.Info.html#columns/1)
[create?(resource)](AshCsv.DataLayer.Info.html#create?/1)
[file(resource)](AshCsv.DataLayer.Info.html#file/1)
[header?(resource)](AshCsv.DataLayer.Info.html#header?/1)
[separator(resource)](AshCsv.DataLayer.Info.html#separator/1)
# [](AshCsv.DataLayer.Info.html#functions)Functions
[Link to this function](AshCsv.DataLayer.Info.html#columns/1 "Link to this function")
# columns(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L12 "View Source")
[Link to this function](AshCsv.DataLayer.Info.html#create?/1 "Link to this function")
# create?(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L24 "View Source")
[Link to this function](AshCsv.DataLayer.Info.html#file/1 "Link to this function")
# file(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L6 "View Source")
[Link to this function](AshCsv.DataLayer.Info.html#header?/1 "Link to this function")
# header?(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L20 "View Source")
[Link to this function](AshCsv.DataLayer.Info.html#separator/1 "Link to this function")
# separator(resource)
[View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L16 "View Source")
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_csv)
[ash\_csv](https://github.com/ash-project/ash_csv)
v0.9.7
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_csv
Settings
# [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)
A CSV datalayer for the Ash framework
For DSL documentation, see [`AshCsv.DataLayer`](AshCsv.DataLayer.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_csv)
[ash\_csv](https://github.com/ash-project/ash_csv)
v0.9.7
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_csv
Settings
# [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
The data layer implementation for AshCsv
## [](dsl-ashcsv-datalayer.html#csv)csv
### [](dsl-ashcsv-datalayer.html#examples)Examples
```
csv do
file "priv/data/tags.csv"
create? true
header? true
separator '-'
columns [:id, :name]
end
```
### [](dsl-ashcsv-datalayer.html#options)Options
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
[← Previous Page Getting Started with CSV](getting-started-with-ash-csv.html)
[Next Page → Change Log](changelog.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_csv)
[ash\_csv](https://github.com/ash-project/ash_csv)
v0.9.7
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_csv
Settings
# [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
AshCsv offers basic support for storing and reading resources from csv files.
## [](getting-started-with-ash-csv.html#installation)Installation
Add `ash_csv` to your list of dependencies in `mix.exs`:
```
{:ash_csv, "~> 0.9.7-rc.0"}
```
For information on how to configure it, see the [DSL documentation.](dsl-ashcsv-datalayer.html)
[← Previous Page Home](readme.html)
[Next Page → DSL: AshCsv.DataLayer](dsl-ashcsv-datalayer.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_csv)
[ash\_csv](https://github.com/ash-project/ash_csv)
v0.9.7
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_csv
Settings
# [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/README.md#L1 "View Source") Home
 
 [](https://opensource.org/licenses/MIT) [](https://hex.pm/packages/ash_csv) [](../ash_csv.html)
# AshCsv
Welcome! This is the CSV Data Layer for [Ash Framework](../ash.html).
## [](readme.html#tutorials)Tutorials
- [Getting Started with AshCsv](getting-started-with-ash-csv.html)
## [](readme.html#reference)Reference
- [AshCsv.DataLayer DSL](dsl-ashcsv-datalayer.html)
[← Previous Page API Reference](api-reference.html)
[Next Page → Getting Started with CSV](getting-started-with-ash-csv.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { ListToolsRequestSchema, CallToolRequestSchema, Tool } from '@modelcontextprotocol/sdk/types.js';
import { GoogleAIFileManager } from '@google/generative-ai/server';
import { z } from 'zod';
import { readdirSync, readFileSync } from 'fs';
import { join, resolve } from 'path';
import { GeminiAPI } from './gemini';
import { GeminiDocsDB } from './db';
import { Logger } from './logger';
import type { CanXBeDoneParams, HowToDoXParams, HintsForProblemParams, IsThisGoodPracticeParams, GeminiFileData } from './types';
// Tool names as constants to ensure consistency
const TOOL_NAMES = {
CAN_X_BE_DONE: 'can_x_be_done',
HOW_TO_DO_X: 'how_to_do_x',
HINTS_FOR_PROBLEM: 'hints_for_problem',
IS_THIS_GOOD_PRACTICE: 'is_this_good_practice'
} as const;
const canXBeDoneSchema = z.object({
docs: z.string().describe('Path to documentation directory'),
prompt: z.string().describe('Path to prompt file'),
x: z.string().describe('The task to check'),
technology: z.string().describe('The technology (programming language/framework/tool)'),
});
const howToDoXSchema = z.object({
docs: z.string().describe('Path to documentation directory'),
prompt: z.string().describe('Path to prompt file'),
x: z.string().describe('The task to implement'),
technology: z.string().describe('The technology (programming language/framework/tool)'),
});
const hintsForProblemSchema = z.object({
docs: z.string().describe('Path to documentation directory'),
prompt: z.string().describe('Path to prompt file'),
problem: z.string().describe('The problem description'),
context: z.string().describe('The context of the problem'),
environment: z.record(z.unknown()).describe('Environment information'),
});
const isThisGoodPracticeSchema = z.object({
docs: z.string().describe('Path to documentation directory'),
prompt: z.string().describe('Path to prompt file'),
snippet: z.string().describe('The code snippet to check'),
context: z.string().describe('The context in which the code is used'),
});
// Define Tool objects following the MCP specification
const TOOLS: Tool[] = [
{
name: TOOL_NAMES.CAN_X_BE_DONE,
description: 'Check if a specific task can be done in a given language/framework/tool',
inputSchema: {
type: 'object',
properties: {
docs: { type: 'string', description: 'Path to documentation directory' },
prompt: { type: 'string', description: 'Path to prompt file' },
x: { type: 'string', description: 'The task to check' },
technology: { type: 'string', description: 'The technology (programming language/framework/tool)' },
},
required: ['docs', 'x', 'technology'],
},
},
{
name: TOOL_NAMES.HOW_TO_DO_X,
description: 'Get examples and alternative approaches for a specific task',
inputSchema: {
type: 'object',
properties: {
docs: { type: 'string', description: 'Path to documentation directory' },
prompt: { type: 'string', description: 'Path to prompt file' },
x: { type: 'string', description: 'The task to implement' },
technology: { type: 'string', description: 'The technology (programming language/framework/tool)' },
},
required: ['docs', 'x', 'technology'],
},
},
{
name: TOOL_NAMES.HINTS_FOR_PROBLEM,
description: 'Get hints for solving a specific problem',
inputSchema: {
type: 'object',
properties: {
docs: { type: 'string', description: 'Path to documentation directory' },
prompt: { type: 'string', description: 'Path to prompt file' },
problem: { type: 'string', description: 'The problem description' },
context: { type: 'string', description: 'The context of the problem' },
environment: { type: 'object', description: 'Environment information' },
},
required: ['docs', 'problem', 'context'],
},
},
{
name: TOOL_NAMES.IS_THIS_GOOD_PRACTICE,
description: 'Check if a code snippet follows good practices',
inputSchema: {
type: 'object',
properties: {
docs: { type: 'string', description: 'Path to documentation directory' },
prompt: { type: 'string', description: 'Path to prompt file' },
snippet: { type: 'string', description: 'The code snippet to check' },
context: { type: 'string', description: 'The context in which the code is used' },
},
required: ['docs', 'snippet', 'context'],
},
}
];
class GeminiDocsServer {
private server: Server;
private gemini: GeminiAPI;
private db: GeminiDocsDB;
private fileManager: GoogleAIFileManager;
constructor() {
if (!process.env.GEMINI_API_KEY) {
throw new Error('GEMINI_API_KEY environment variable is required');
}
const toolsEnabled = TOOLS.reduce((acc, tool) => {
acc[tool.name] = true;
return acc;
}, {} as Record<string, boolean>);
this.server = new Server(
{
name: 'gemini-docs',
version: '0.1.0',
},
{
capabilities: {
tools: toolsEnabled,
},
}
);
try {
this.db = new GeminiDocsDB();
this.fileManager = new GoogleAIFileManager(process.env.GEMINI_API_KEY);
this.gemini = new GeminiAPI();
} catch (error) {
console.error('[Server] Failed to initialize:', error);
Logger.log(`[Server] Failed to initialize: ${error}`);
throw error;
}
this.setupTools();
this.setupErrorHandler();
}
private setupErrorHandler() {
this.server.onerror = (error) => {
console.error('[MCP Error]', error);
};
}
private generateFileId(content: string): string {
const hash = Bun.hash(content).toString(16);
return hash;
}
private async uploadFileWithRetry(
fullPath: string,
displayName: string,
maxRetries: number = 3,
retryDelay: number = 1000
): Promise<{ uri: string }> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await this.fileManager.uploadFile(fullPath, {
mimeType: 'text/markdown',
displayName
});
return { uri: result.file.uri };
} catch (error: any) {
lastError = error;
if (attempt < maxRetries) {
Logger.log(`[FileURIDebug] Upload attempt ${attempt} failed for ${fullPath}, retrying in ${retryDelay}ms...`);
await new Promise(resolve => setTimeout(resolve, retryDelay * attempt));
}
}
}
throw lastError!;
}
private async processFileBatch(
absPath: string,
files: string[],
batchSize: number = 10,
stats: { cached: number; uploaded: number; } = { cached: 0, uploaded: 0 }
): Promise<GeminiFileData[]> {
const docRefs: GeminiFileData[] = [];
const batches = [];
const logProgress = () => {
Logger.log(`[FileURIDebug] Progress Report:
Total files: ${files.length}
Processed: ${stats.cached + stats.uploaded}
Cached: ${stats.cached}
Uploaded: ${stats.uploaded}
`);
};
// Split files into batches
for (let i = 0; i < files.length; i += batchSize) {
batches.push(files.slice(i, i + batchSize));
}
Logger.log(`[FileURIDebug] Processing ${files.length} files in ${batches.length} batches of ${batchSize}`);
// Process batches sequentially to avoid overwhelming the API
for (const batch of batches) {
Logger.log(`[FileURIDebug] Processing batch of ${batch.length} files...`);
const batchResults = await Promise.all(
batch.map(async (file) => {
const fullPath = join(absPath, file);
try {
// Calculate content hash first
const content = readFileSync(fullPath, 'utf-8');
const fileHash = this.generateFileId(content);
// Check if we have this content's URI
const existingFile = await this.db.getFile(fileHash);
if (existingFile) {
Logger.log(`[FileURIDebug] Found existing content (hash: ${fileHash}) for ${fullPath}`);
stats.cached++;
logProgress();
return {
fileUri: existingFile.fileUri
};
}
// Upload new file to Gemini
Logger.log(`[FileURIDebug] Uploading file ${fullPath} (${file})`);
const uploadResult = await this.uploadFileWithRetry(fullPath, file);
// Store the Gemini file URI
await this.db.saveFileId(fullPath, fileHash, uploadResult.uri);
Logger.log(`[FileURIDebug] Successfully uploaded and saved ${fullPath}`);
stats.uploaded++;
logProgress();
return { fileUri: uploadResult.uri };
} catch (error) {
Logger.log(`[FileURIDebug] Error processing file ${fullPath}: ${error}`);
throw error;
}
})
);
docRefs.push(...batchResults);
}
return docRefs;
}
private async readDocs(dirPath: string): Promise<GeminiFileData[]> {
try {
const absPath = resolve(dirPath);
const files = readdirSync(absPath, { recursive: true }) as string[];
const mdFiles = files.filter(file => file.endsWith('.md'));
Logger.log(`[FileURIDebug] Found ${mdFiles.length} markdown files to process`);
// Track cached vs uploaded files
const stats = { cached: 0, uploaded: 0 };
const docRefs = await this.processFileBatch(absPath, mdFiles, 10, stats);
Logger.log(`[FileURIDebug] Processing completed:
${stats.cached} files loaded from cache
${stats.uploaded} files uploaded to Gemini`);
return docRefs;
} catch (error) {
console.error('[Server] Error reading docs');
Logger.log(`[FileURIDebug] Error reading docs: ${error}`);
throw error;
}
}
private setupTools() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: TOOLS,
}));
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case TOOL_NAMES.CAN_X_BE_DONE: {
const params = canXBeDoneSchema.parse(args) as CanXBeDoneParams;
const docs = await this.readDocs(params.docs);
const response = await this.gemini.canXBeDone(
docs,
params.x,
params.technology,
params.prompt
);
return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
}
case TOOL_NAMES.HOW_TO_DO_X: {
const params = howToDoXSchema.parse(args) as HowToDoXParams;
const docs = await this.readDocs(params.docs);
const response = await this.gemini.howToDoX(
docs,
params.x,
params.technology,
params.prompt
);
return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
}
case TOOL_NAMES.HINTS_FOR_PROBLEM: {
const params = hintsForProblemSchema.parse(args) as HintsForProblemParams;
const docs = await this.readDocs(params.docs);
const response = await this.gemini.hintsForProblem(
docs,
params.problem,
params.context,
params.environment,
params.prompt
);
return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
}
case TOOL_NAMES.IS_THIS_GOOD_PRACTICE: {
const params = isThisGoodPracticeSchema.parse(args) as IsThisGoodPracticeParams;
const docs = await this.readDocs(params.docs);
const response = await this.gemini.isThisGoodPractice(
docs,
params.snippet,
params.context,
params.prompt
);
return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
}
default:
throw new Error(`Unknown tool: ${name}`);
}
});
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
}
async stop() {
await this.server.close();
this.db.close();
Logger.close();
}
}
// Handle graceful shutdown
const server = new GeminiDocsServer();
// Parse command line arguments
const args = process.argv.slice(2);
if (args.includes('--verbose')) {
Logger.enable();
}
process.on('SIGINT', async () => {
await server.stop();
process.exit(0);
});
server.start().catch(error => {
console.error('Failed to start server:', error);
process.exit(1);
});
```
--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_rbac.md:
--------------------------------------------------------------------------------
```markdown
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [View Source](https://github.com/traveltechdeluxe/ash-rbac "View Source") API Reference ash\_rbac v0.6.1
## [](api-reference.html#modules)Modules
[AshRbac](AshRbac.html)
[AshRbac.Actions](AshRbac.Actions.html)
Adds the policies for actions to the dsl\_state
[AshRbac.Fields](AshRbac.Fields.html)
Adds the policies for fields to the dsl\_state
[AshRbac.HasRole](AshRbac.HasRole.html)
Check to determine if the actor has a specific role or if the actor has any of the roles in a list
[AshRbac.Info](AshRbac.Info.html)
Introspection functions for the Rbac Extension
[AshRbac.Policies](AshRbac.Policies.html)
Adds the configured policies to the resource
[AshRbac.Role](AshRbac.Role.html)
The Role entity for the DSL of the rbac extension
[Next Page → Getting Started](getting_started.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [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)
Adds the policies for actions to the dsl\_state
# [](AshRbac.Actions.html#summary)Summary
## [Functions](AshRbac.Actions.html#functions)
[after?(\_)](AshRbac.Actions.html#after?/1)
Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1).
[after\_compile?()](AshRbac.Actions.html#after_compile?/0)
Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
[before?(\_)](AshRbac.Actions.html#before?/1)
Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1).
# [](AshRbac.Actions.html#functions)Functions
[Link to this function](AshRbac.Actions.html#after?/1 "Link to this function")
# after?(\_)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L5 "View Source")
Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1).
[Link to this function](AshRbac.Actions.html#after_compile?/0 "Link to this function")
# after\_compile?()
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L5 "View Source")
Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
[Link to this function](AshRbac.Actions.html#before?/1 "Link to this function")
# before?(\_)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L5 "View Source")
Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1).
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [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)
Adds the policies for fields to the dsl\_state
# [](AshRbac.Fields.html#summary)Summary
## [Functions](AshRbac.Fields.html#functions)
[after?(\_)](AshRbac.Fields.html#after?/1)
Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1).
[after\_compile?()](AshRbac.Fields.html#after_compile?/0)
Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
[before?(\_)](AshRbac.Fields.html#before?/1)
Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1).
# [](AshRbac.Fields.html#functions)Functions
[Link to this function](AshRbac.Fields.html#after?/1 "Link to this function")
# after?(\_)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L6 "View Source")
Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1).
[Link to this function](AshRbac.Fields.html#after_compile?/0 "Link to this function")
# after\_compile?()
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L6 "View Source")
Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
[Link to this function](AshRbac.Fields.html#before?/1 "Link to this function")
# before?(\_)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L6 "View Source")
Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1).
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [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)
Check to determine if the actor has a specific role or if the actor has any of the roles in a list
# [](AshRbac.HasRole.html#summary)Summary
## [Functions](AshRbac.HasRole.html#functions)
[prefer\_expanded\_description?()](AshRbac.HasRole.html#prefer_expanded_description?/0)
Callback implementation for [`Ash.Policy.Check.prefer_expanded_description?/0`](../ash/3.4.21/Ash.Policy.Check.html#c:prefer_expanded_description?/0).
[requires\_original\_data?(\_, \_)](AshRbac.HasRole.html#requires_original_data?/2)
Callback implementation for [`Ash.Policy.Check.requires_original_data?/2`](../ash/3.4.21/Ash.Policy.Check.html#c:requires_original_data?/2).
[strict\_check(actor, context, opts)](AshRbac.HasRole.html#strict_check/3)
Callback implementation for [`Ash.Policy.Check.strict_check/3`](../ash/3.4.21/Ash.Policy.Check.html#c:strict_check/3).
[type()](AshRbac.HasRole.html#type/0)
Callback implementation for [`Ash.Policy.Check.type/0`](../ash/3.4.21/Ash.Policy.Check.html#c:type/0).
# [](AshRbac.HasRole.html#functions)Functions
[Link to this function](AshRbac.HasRole.html#prefer_expanded_description?/0 "Link to this function")
# prefer\_expanded\_description?()
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source")
Callback implementation for [`Ash.Policy.Check.prefer_expanded_description?/0`](../ash/3.4.21/Ash.Policy.Check.html#c:prefer_expanded_description?/0).
[Link to this function](AshRbac.HasRole.html#requires_original_data?/2 "Link to this function")
# requires\_original\_data?(\_, \_)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source")
Callback implementation for [`Ash.Policy.Check.requires_original_data?/2`](../ash/3.4.21/Ash.Policy.Check.html#c:requires_original_data?/2).
[Link to this function](AshRbac.HasRole.html#strict_check/3 "Link to this function")
# strict\_check(actor, context, opts)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source")
Callback implementation for [`Ash.Policy.Check.strict_check/3`](../ash/3.4.21/Ash.Policy.Check.html#c:strict_check/3).
[Link to this function](AshRbac.HasRole.html#type/0 "Link to this function")
# type()
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source")
Callback implementation for [`Ash.Policy.Check.type/0`](../ash/3.4.21/Ash.Policy.Check.html#c:type/0).
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L1 "View Source") AshRbac (ash\_rbac v0.6.1)
# [](AshRbac.html#summary)Summary
## [Functions](AshRbac.html#functions)
[rbac(body)](AshRbac.html#rbac/1)
# [](AshRbac.html#functions)Functions
[Link to this macro](AshRbac.html#rbac/1 "Link to this macro")
# rbac(body)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L177 "View Source") (macro)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L127 "View Source") AshRbac.Info (ash\_rbac v0.6.1)
Introspection functions for the Rbac Extension
# [](AshRbac.Info.html#summary)Summary
## [Functions](AshRbac.Info.html#functions)
[bypass(resource)](AshRbac.Info.html#bypass/1)
[bypass\_roles\_field(resource)](AshRbac.Info.html#bypass_roles_field/1)
[public?(resource)](AshRbac.Info.html#public?/1)
[roles(resource)](AshRbac.Info.html#roles/1)
# [](AshRbac.Info.html#functions)Functions
[Link to this function](AshRbac.Info.html#bypass/1 "Link to this function")
# bypass(resource)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L133 "View Source")
[Link to this function](AshRbac.Info.html#bypass_roles_field/1 "Link to this function")
# bypass\_roles\_field(resource)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L137 "View Source")
[Link to this function](AshRbac.Info.html#public?/1 "Link to this function")
# public?(resource)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L141 "View Source")
[Link to this function](AshRbac.Info.html#roles/1 "Link to this function")
# roles(resource)
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L145 "View Source")
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [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)
Adds the configured policies to the resource
# [](AshRbac.Policies.html#summary)Summary
## [Functions](AshRbac.Policies.html#functions)
[after\_compile?()](AshRbac.Policies.html#after_compile?/0)
Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
# [](AshRbac.Policies.html#functions)Functions
[Link to this function](AshRbac.Policies.html#after_compile?/0 "Link to this function")
# after\_compile?()
[View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/policies.ex#L6 "View Source")
Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0).
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [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)
The Role entity for the DSL of the rbac extension
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/documentation/getting_started.md#L1 "View Source") Getting Started
## [](getting_started.html#installation)Installation
Add the ash\_rbac dependency to your mix.exs
```
defp deps do
[
{:ash_rbac, "~> 0.6.1"}
]
end
```
## [](getting_started.html#adding-ashrbac-to-your-resource)Adding AshRbac to your resource
First, the authorizer and the extension need to be added.
```
defmodule RootResource do
@moduledoc false
use Ash.Resource,
data_layer: Ash.DataLayer.Ets,
authorizers: [Ash.Policy.Authorizer], # Add the authorizer
extensions: [AshRbac] # Add the extension
...
end
```
Afterwards, you can add a rbac block to your resource.
```
rbac do
role :user do
fields [:name, :email]
actions [:read]
end
end
```
The options defined in the rbac block are transformed into policies during compile time.
The previous example will generate the following policies:
```
field_policies do
field_policy [:name, :email], [{AshRbac.HasRole, [role: [:user]]}] do
authorize_if always()
end
# it also adds a policy for all other fields like this
field_policy [:other_fields, ...] do
forbid_if always()
end
end
policies do
policy [action(:read), {AshRbac.HasRole, [role: [:user]]}] do
authorize_if always()
end
end
```
It is possible to add extra conditions to fields and actions:
```
rbac do
role :user do
fields [:name, {:email, actor_attribute_equals(:field, "value")}]
actions [{:read, accessing_from(RelatedResource, :path)}]
end
end
```
The conditions are added to the generated policies as well.
```
field_policies do
field_policy [:name], [{AshRbac.HasRole, [role: [:user]]}] do
authorize_if always()
end
field_policy [:email], [{AshRbac.HasRole, [role: [:user], actor_attribute_equals(:field, "value")]}] do
authorize_if always()
end
# it also adds a policy for all other fields like this
field_policy [:other_fields, ...] do
forbid_if always()
end
end
policies do
policy [action(:read), {hAshRbac.HasRole, [role: [:user]]}, accessing_from(RelatedResource, :path)] do
authorize_if always()
end
end
```
[← Previous Page API Reference](api-reference.html)
[Next Page → Relationships](relationships.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/README.md#L1 "View Source") AshRbac
A small extension that allows for easier application of policies
```
rbac do
role :user do
fields [:fields, :user, :can, :see]
actions [:actions, :user, :can :use]
end
end
```
## [](readme.html#installation)Installation
The package can be installed by adding `ash_rbac` to your list of dependencies in `mix.exs`:
```
def deps do
[
{:ash_rbac, "~> 0.6.1"},
]
end
```
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).
[← Previous Page Relationships](relationships.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/traveltechdeluxe/ash-rbac)
[ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac)
v0.6.1
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_rbac
Settings
# [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/documentation/relationships.md#L1 "View Source") Relationships
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.
```
# only allow read access if accessed from a parent
rbac do
role :user do
actions [
{:read, accessing_from(Parent, :child)}
]
end
end
# result
policies do
policy [action(:read), accessing_from(Parent, :child)] do
authorize_if {AshRbac.HasRole, [role: :user]}
end
end
```
[← Previous Page Getting Started](getting_started.html)
[Next Page → Readme](readme.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
```
--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_money.md:
--------------------------------------------------------------------------------
```markdown
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [View Source](https://github.com/ash-project/ash_money "View Source") API Reference ash\_money v0.1.15
## [](api-reference.html#modules)Modules
[AshMoney](AshMoney.html)
[`AshMoney`](AshMoney.html#content) provides a type for working with money in your Ash resources.
[AshMoney.AshPostgresExtension](AshMoney.AshPostgresExtension.html)
Installs the `money_with_currency` type and operators/functions for Postgres.
[AshMoney.Types.Money](AshMoney.Types.Money.html)
A money type for Ash that uses the `ex_money` library.
[Comparable.Type.Decimal.To.Money](Comparable.Type.Decimal.To.Money.html)
[Comparable.Type.Float.To.Money](Comparable.Type.Float.To.Money.html)
[Comparable.Type.Integer.To.Money](Comparable.Type.Integer.To.Money.html)
[Comparable.Type.Money.To.Decimal](Comparable.Type.Money.To.Decimal.html)
[Comparable.Type.Money.To.Float](Comparable.Type.Money.To.Float.html)
[Comparable.Type.Money.To.Integer](Comparable.Type.Money.To.Integer.html)
[Comparable.Type.Money.To.Money](Comparable.Type.Money.To.Money.html)
## [](api-reference.html#mix-tasks)Mix Tasks
[mix ash\_money.add\_to\_ash\_postgres](Mix.Tasks.AshMoney.AddToAshPostgres.html)
Adds AshMoney.AshPostgresExtension to installed\_extensions and installs :ex\_money\_sql.
[mix ash\_money.install](Mix.Tasks.AshMoney.Install.html)
Installs AshMoney. Should be run with `mix igniter.install ash_money`
[Next Page → Home](readme.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
Installs the `money_with_currency` type and operators/functions for Postgres.
# [](AshMoney.AshPostgresExtension.html#summary)Summary
## [Functions](AshMoney.AshPostgresExtension.html#functions)
[extension()](AshMoney.AshPostgresExtension.html#extension/0)
[install(int)](AshMoney.AshPostgresExtension.html#install/1)
Callback implementation for [`AshPostgres.CustomExtension.install/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:install/1).
[uninstall(v)](AshMoney.AshPostgresExtension.html#uninstall/1)
Callback implementation for [`AshPostgres.CustomExtension.uninstall/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:uninstall/1).
# [](AshMoney.AshPostgresExtension.html#functions)Functions
[Link to this function](AshMoney.AshPostgresExtension.html#extension/0 "Link to this function")
# extension()
[View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L6 "View Source")
[Link to this function](AshMoney.AshPostgresExtension.html#install/1 "Link to this function")
# install(int)
[View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L8 "View Source")
Callback implementation for [`AshPostgres.CustomExtension.install/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:install/1).
[Link to this function](AshMoney.AshPostgresExtension.html#uninstall/1 "Link to this function")
# uninstall(v)
[View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L56 "View Source")
Callback implementation for [`AshPostgres.CustomExtension.uninstall/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:uninstall/1).
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
[`AshMoney`](AshMoney.html#content) provides a type for working with money in your Ash resources.
It also provides an `AshPostgres.Extension` that can be used to add support for money types and operators to your Postgres database.
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
A money type for Ash that uses the `ex_money` library.
When constructing a composite type, use a tuple in the following structure:
`composite_type(%{currency: "USD", amount: Decimal.new("0")}}, AshMoney.Types.Money)`
If you've added a custom type, like `:money`:
```
composite_type(%{currency: "USD", amount: Decimal.new("0")}}, :money)
```
# [](AshMoney.Types.Money.html#summary)Summary
## [Functions](AshMoney.Types.Money.html#functions)
[handle\_change?()](AshMoney.Types.Money.html#handle_change?/0)
[prepare\_change?()](AshMoney.Types.Money.html#prepare_change?/0)
# [](AshMoney.Types.Money.html#functions)Functions
[Link to this function](AshMoney.Types.Money.html#handle_change?/0 "Link to this function")
# handle\_change?()
[View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/types/money.ex#L1 "View Source")
[Link to this function](AshMoney.Types.Money.html#prepare_change?/0 "Link to this function")
# prepare\_change?()
[View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/types/money.ex#L1 "View Source")
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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
## [](getting-started-with-ash-money.html#bring-in-the-ash_money-dependency)Bring in the ash\_money dependency
```
def deps()
[
...
{:ash_money, "~> 0.1.15"}
]
end
```
## [](getting-started-with-ash-money.html#setup)Setup
### [](getting-started-with-ash-money.html#using-igniter-recommended)Using Igniter (recommended)
```
mix igniter.install ash_money
```
### [](getting-started-with-ash-money.html#manual)Manual
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:
```
attribute :balance, AshMoney.Types.Money
```
## [](getting-started-with-ash-money.html#add-to-known-types)Add to known types
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.
```
config :ash, :known_types, [AshMoney.Types.Money]
```
## [](getting-started-with-ash-money.html#referencing-with-money)Referencing with `:money`
You can add it to your compile time list of types for easier reference:
```
config :ash, :custom_types, money: AshMoney.Types.Money
```
Then compile ash again, `mix deps.compile ash --force`, and refer to it like so:
```
attribute :balance, :money
```
## [](getting-started-with-ash-money.html#adding-ashpostgres-support)Adding AshPostgres Support
First, add the `:ex_money_sql` dependency to your `mix.exs` file.
Then add [`AshMoney.AshPostgresExtension`](AshMoney.AshPostgresExtension.html) to your list of `installed_extensions` in your repo, and generate migrations.
```
defmodule YourRepo do
def installed_extensions do
[..., AshMoney.AshPostgresExtension]
end
end
```
## [](getting-started-with-ash-money.html#ashpostgres-support)AshPostgres Support
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:
```
sum :sum_of_usd_balances, :accounts, :balance do
filter expr(balance[:currency_code] == "USD")
end
```
## [](getting-started-with-ash-money.html#ashgraphql-support)AshGraphql Support
Add the following to your schema file:
```
object :money do
field(:amount, non_null(:decimal))
field(:currency, non_null(:string))
end
input_object :money_input do
field(:amount, non_null(:decimal))
field(:currency, non_null(:string))
end
```
[← Previous Page Home](readme.html)
[Next Page → Change Log](changelog.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
Adds AshMoney.AshPostgresExtension to installed\_extensions and installs :ex\_money\_sql.
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`.
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [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)
Installs AshMoney. Should be run with `mix igniter.install ash_money`
# [](Mix.Tasks.AshMoney.Install.html#summary)Summary
## [Functions](Mix.Tasks.AshMoney.Install.html#functions)
[igniter(igniter, argv)](Mix.Tasks.AshMoney.Install.html#igniter/2)
Callback implementation for [`Igniter.Mix.Task.igniter/2`](../igniter/0.5.0/Igniter.Mix.Task.html#c:igniter/2).
# [](Mix.Tasks.AshMoney.Install.html#functions)Functions
[Link to this function](Mix.Tasks.AshMoney.Install.html#igniter/2 "Link to this function")
# igniter(igniter, argv)
[View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/mix/tasks/ash_money.install.ex#L8 "View Source")
Callback implementation for [`Igniter.Mix.Task.igniter/2`](../igniter/0.5.0/Igniter.Mix.Task.html#c:igniter/2).
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[](https://github.com/ash-project/ash_money)
[ash\_money](https://github.com/ash-project/ash_money)
v0.1.15
- GUIDES
- Modules
- Mix Tasks
<!--THE END-->
<!--THE END-->
<!--THE END-->
Search documentation of ash\_money
Settings
# [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/README.md#L1 "View Source") Home
 
 [](https://opensource.org/licenses/MIT) [](https://hex.pm/packages/ash_money) [](../ash_money.html)
# AshMoney
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:
- An [`Ash.Type`](../ash/3.4.48/Ash.Type.html) for representing [`Money`](../ex_money/5.18.0/Money.html)
- An `AshPostgres.Extension` for supporting common money operations directly in the database
- An implementation of [`Comp`](../ash/3.4.48/Comp.html) for `%Money{}`, allowing Ash to compare them.
## [](readme.html#tutorials)Tutorials
- [Getting Started with AshMoney](getting-started-with-ash-money.html)
[← Previous Page API Reference](api-reference.html)
[Next Page → Getting Started With AshMoney](getting-started-with-ash-money.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
```
--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_cloak.md:
--------------------------------------------------------------------------------
```markdown
[ash\_cloak](https://github.com/ash-project/ash_cloak)
v0.1.2
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cloak
Settings
# [View Source](https://github.com/ash-project/ash_cloak "View Source") API Reference ash\_cloak v0.1.2
## [](api-reference.html#modules)Modules
[AshCloak](AshCloak.html)
An extension for encrypting attributes of a resource.
[AshCloak.Changes.Encrypt](AshCloak.Changes.Encrypt.html)
Takes an argument, and encrypts it into an attribute called `encrypted_{attribute}`
[AshCloak.Cloak.Options](AshCloak.Cloak.Options.html)
Mod Docs
[AshCloak.Info](AshCloak.Info.html)
Introspection functions for the [`AshCloak`](AshCloak.html) extension.
[Next Page → Home](readme.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cloak](https://github.com/ash-project/ash_cloak)
v0.1.2
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cloak
Settings
# [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)
Takes an argument, and encrypts it into an attribute called `encrypted_{attribute}`
# [](AshCloak.Changes.Encrypt.html#summary)Summary
## [Functions](AshCloak.Changes.Encrypt.html#functions)
[atomic(changeset, opts, \_)](AshCloak.Changes.Encrypt.html#atomic/3)
Callback implementation for [`Ash.Resource.Change.atomic/3`](../ash/3.4.1/Ash.Resource.Change.html#c:atomic/3).
[change(changeset, opts, \_)](AshCloak.Changes.Encrypt.html#change/3)
Callback implementation for [`Ash.Resource.Change.change/3`](../ash/3.4.1/Ash.Resource.Change.html#c:change/3).
# [](AshCloak.Changes.Encrypt.html#functions)Functions
[Link to this function](AshCloak.Changes.Encrypt.html#atomic/3 "Link to this function")
# atomic(changeset, opts, \_)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/changes/encrypt.ex#L19 "View Source")
Callback implementation for [`Ash.Resource.Change.atomic/3`](../ash/3.4.1/Ash.Resource.Change.html#c:atomic/3).
[Link to this function](AshCloak.Changes.Encrypt.html#change/3 "Link to this function")
# change(changeset, opts, \_)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/changes/encrypt.ex#L5 "View Source")
Callback implementation for [`Ash.Resource.Change.change/3`](../ash/3.4.1/Ash.Resource.Change.html#c:change/3).
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cloak](https://github.com/ash-project/ash_cloak)
v0.1.2
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cloak
Settings
# [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)
Mod Docs
# [](AshCloak.Cloak.Options.html#summary)Summary
## [Functions](AshCloak.Cloak.Options.html#functions)
[attributes(value)](AshCloak.Cloak.Options.html#attributes/1)
Hello 3
[decrypt\_by\_default(value)](AshCloak.Cloak.Options.html#decrypt_by_default/1)
Hello 3
[on\_decrypt(value)](AshCloak.Cloak.Options.html#on_decrypt/1)
Hello 3
[vault(value)](AshCloak.Cloak.Options.html#vault/1)
Hello 3
# [](AshCloak.Cloak.Options.html#functions)Functions
[Link to this macro](AshCloak.Cloak.Options.html#attributes/1 "Link to this macro")
# attributes(value)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro)
Hello 3
[Link to this macro](AshCloak.Cloak.Options.html#decrypt_by_default/1 "Link to this macro")
# decrypt\_by\_default(value)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro)
Hello 3
[Link to this macro](AshCloak.Cloak.Options.html#on_decrypt/1 "Link to this macro")
# on\_decrypt(value)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro)
Hello 3
[Link to this macro](AshCloak.Cloak.Options.html#vault/1 "Link to this macro")
# vault(value)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro)
Hello 3
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cloak](https://github.com/ash-project/ash_cloak)
v0.1.2
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cloak
Settings
# [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)
An extension for encrypting attributes of a resource.
See the getting started guide for more information.
# [](AshCloak.html#summary)Summary
## [Functions](AshCloak.html#functions)
[cloak(body)](AshCloak.html#cloak/1)
Hello!
[encrypt\_and\_set(changeset, key, value)](AshCloak.html#encrypt_and_set/3)
Encrypts and writes to an encrypted attribute.
# [](AshCloak.html#functions)Functions
[Link to this macro](AshCloak.html#cloak/1 "Link to this macro")
# cloak(body)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak.ex#L41 "View Source") (macro)
Hello!
[Link to this function](AshCloak.html#encrypt_and_set/3 "Link to this function")
# encrypt\_and\_set(changeset, key, value)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak.ex#L50 "View Source")
```
@spec encrypt_and_set(Ash.Changeset.t(), attr :: atom(), term :: term()) ::
Ash.Changeset.t()
```
Encrypts and writes to an encrypted attribute.
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
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cloak](https://github.com/ash-project/ash_cloak)
v0.1.2
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cloak
Settings
# [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)
Introspection functions for the [`AshCloak`](AshCloak.html) extension.
# [](AshCloak.Info.html#summary)Summary
## [Functions](AshCloak.Info.html#functions)
[cloak\_attributes(dsl\_or\_extended)](AshCloak.Info.html#cloak_attributes/1)
The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.
[cloak\_attributes!(dsl\_or\_extended)](AshCloak.Info.html#cloak_attributes!/1)
The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.
[cloak\_decrypt\_by\_default(dsl\_or\_extended)](AshCloak.Info.html#cloak_decrypt_by_default/1)
A list of attributes that should be decrypted (their calculation should be loaded) by default.
[cloak\_decrypt\_by\_default!(dsl\_or\_extended)](AshCloak.Info.html#cloak_decrypt_by_default!/1)
A list of attributes that should be decrypted (their calculation should be loaded) by default.
[cloak\_on\_decrypt(dsl\_or\_extended)](AshCloak.Info.html#cloak_on_decrypt/1)
A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}`
[cloak\_on\_decrypt!(dsl\_or\_extended)](AshCloak.Info.html#cloak_on_decrypt!/1)
A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}`
[cloak\_options(dsl\_or\_extended)](AshCloak.Info.html#cloak_options/1)
cloak DSL options
[cloak\_vault(dsl\_or\_extended)](AshCloak.Info.html#cloak_vault/1)
The vault to use to encrypt & decrypt the value
[cloak\_vault!(dsl\_or\_extended)](AshCloak.Info.html#cloak_vault!/1)
The vault to use to encrypt & decrypt the value
# [](AshCloak.Info.html#functions)Functions
[Link to this function](AshCloak.Info.html#cloak_attributes/1 "Link to this function")
# cloak\_attributes(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
```
@spec cloak_attributes(dsl_or_extended :: module() | map()) ::
{:ok, [atom()]} | :error
```
The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.
[Link to this function](AshCloak.Info.html#cloak_attributes!/1 "Link to this function")
# cloak\_attributes!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
```
@spec cloak_attributes!(dsl_or_extended :: module() | map()) :: [atom()] | no_return()
```
The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.
[Link to this function](AshCloak.Info.html#cloak_decrypt_by_default/1 "Link to this function")
# cloak\_decrypt\_by\_default(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
```
@spec cloak_decrypt_by_default(dsl_or_extended :: module() | map()) ::
{:ok, [atom()]} | :error
```
A list of attributes that should be decrypted (their calculation should be loaded) by default.
[Link to this function](AshCloak.Info.html#cloak_decrypt_by_default!/1 "Link to this function")
# cloak\_decrypt\_by\_default!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
```
@spec cloak_decrypt_by_default!(dsl_or_extended :: module() | map()) ::
[atom()] | no_return()
```
A list of attributes that should be decrypted (their calculation should be loaded) by default.
[Link to this function](AshCloak.Info.html#cloak_on_decrypt/1 "Link to this function")
# cloak\_on\_decrypt(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
```
@spec cloak_on_decrypt(dsl_or_extended :: module() | map()) ::
{:ok, (any(), any(), any(), any() -> any()) | mfa()} | :error
```
A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}`
[Link to this function](AshCloak.Info.html#cloak_on_decrypt!/1 "Link to this function")
# cloak\_on\_decrypt!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
```
@spec cloak_on_decrypt!(dsl_or_extended :: module() | map()) ::
((any(), any(), any(), any() -> any()) | mfa()) | no_return()
```
A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}`
[Link to this function](AshCloak.Info.html#cloak_options/1 "Link to this function")
# cloak\_options(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
```
@spec cloak_options(dsl_or_extended :: module() | map()) :: %{
required(atom()) => any()
}
```
cloak DSL options
Returns a map containing the and any configured or default values.
[Link to this function](AshCloak.Info.html#cloak_vault/1 "Link to this function")
# cloak\_vault(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
```
@spec cloak_vault(dsl_or_extended :: module() | map()) :: {:ok, module()} | :error
```
The vault to use to encrypt & decrypt the value
[Link to this function](AshCloak.Info.html#cloak_vault!/1 "Link to this function")
# cloak\_vault!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source")
```
@spec cloak_vault!(dsl_or_extended :: module() | map()) :: module() | no_return()
```
The vault to use to encrypt & decrypt the value
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cloak](https://github.com/ash-project/ash_cloak)
v0.1.2
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cloak
Settings
# [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/documentation/dsls/DSL:-AshCloak.md#L1 "View Source") DSL: AshCloak
An extension for encrypting attributes of a resource.
See the getting started guide for more information.
## [](dsl-ashcloak.html#cloak)cloak
Encrypt attributes of a resource
### [](dsl-ashcloak.html#options)Options
NameTypeDefaultDocs[`vault`](dsl-ashcloak.html#cloak-vault)`module`The vault to use to encrypt & 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}`
[← Previous Page How does AshCloak work?](how-does-ash-cloak-work.html)
[Next Page → Change Log](changelog.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cloak](https://github.com/ash-project/ash_cloak)
v0.1.2
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cloak
Settings
# [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
## [](getting-started-with-ash-cloak.html#installation)Installation
Add `ash_cloak` to your list of dependencies in `mix.exs`:
```
{:ash_cloak, "~> 0.1.2"}
```
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.
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)
### [](getting-started-with-ash-cloak.html#add-the-ashcloak-extension-to-your-resource)Add the [`AshCloak`](AshCloak.html) extension to your resource
```
defmodule User do
use Ash.Resource, extensions: [AshCloak]
cloak do
# the vault to use to encrypt them
vault MyApp.Vault
# the attributes to encrypt
attributes [:address, :phone_number]
# This is just equivalent to always providing `load: fields` on all calls
decrypt_by_default [:address]
# An MFA or function to be invoked beforce any decryption
on_decrypt fn records, field, context ->
# Ash has policies that allow forbidding certain users to load data.
# You should generally use those for authorization rules, and
# only use this callback for auditing/logging.
Audit.user_accessed_encrypted_field(records, field, context)
if context.user.name == "marty" do
{:error, "No martys at the party!"}
else
:ok
end
end
end
end
```
[← Previous Page Home](readme.html)
[Next Page → How does AshCloak work?](how-does-ash-cloak-work.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cloak](https://github.com/ash-project/ash_cloak)
v0.1.2
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cloak
Settings
# [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?
## [](how-does-ash-cloak-work.html#rewrite-attributes-to-calculations)Rewrite attributes to calculations
First, AshCloak changes the name of each cloaked attribute to `encrypted_<name>`, and sets `public?: false` and `sensitive?: true`.
Then it adds a *calculation* matching the original attribute that, when loaded, will decrypt the given attribute and call any configured `on_decrypt` callbacks.
## [](how-does-ash-cloak-work.html#modify-actions)Modify Actions
AshCloak then goes through each action that accepts the attribute and removes the attribute from the accept list.
Then it adds an argument by the same name, and a `change` that encrypts the attribute value.
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.
## [](how-does-ash-cloak-work.html#add-preparation-and-change)Add `preparation` and `change`
Finally, it add a `preparation` and a `change` that will automatically load the corresponding calculations for any attribute in the `decrypt_by_default` list.
## [](how-does-ash-cloak-work.html#the-result)The result
The cloaked attribute will now seamlessly encrypt when writing and decrypt on request.
[← Previous Page Get Started with AshCloak](getting-started-with-ash-cloak.html)
[Next Page → DSL: AshCloak](dsl-ashcloak.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cloak](https://github.com/ash-project/ash_cloak)
v0.1.2
- GUIDES
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cloak
Settings
# [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/README.md#L1 "View Source") Home
 
 [](https://opensource.org/licenses/MIT) [](https://hex.pm/packages/ash_cloak) [](../ash_cloak.html)
# AshCloak
AshCloak is an [Ash](../ash.html) extension to seamlessly encrypt and decrypt attributes of your resources.
Encrypting attributes ensures that sensitive fields are not stored in the clear in your data layer.
AshCloak can be used with any Ash data layer.
It's recommended to use [Cloak](https://github.com/danielberkompas/cloak) as a vault implementation but you could also build your own.
## [](readme.html#tutorials)Tutorials
- [Get Started with AshCloak](getting-started-with-ash-cloak.html)
## [](readme.html#topics)Topics
- [How does AshCloak work?](how-does-ash-cloak-work.html)
## [](readme.html#reference)Reference
- [AshCloak DSL](dsl-ashcloak.html)
[← Previous Page API Reference](api-reference.html)
[Next Page → Get Started with AshCloak](getting-started-with-ash-cloak.html)
[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")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
```
--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_archival.md:
--------------------------------------------------------------------------------
```markdown
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival "View Source") API Reference ash\_archival v1.0.4
## [](api-reference.html#modules)Modules
[AshArchival](AshArchival.html)
An Archival extension for [`Ash.Resource`](../ash/3.4.23/Ash.Resource.html)
[AshArchival.Resource](AshArchival.Resource.html)
Configures a resource to be archived instead of destroyed for all destroy actions.
[AshArchival.Resource.Info](AshArchival.Resource.Info.html)
Introspection helpers for [`AshArchival.Resource`](AshArchival.Resource.html)
[Next Page → Home](readme.html)
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival.ex#L1 "View Source") AshArchival (ash\_archival v1.0.4)
An Archival extension for [`Ash.Resource`](../ash/3.4.23/Ash.Resource.html)
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/resource.ex#L1 "View Source") AshArchival.Resource (ash\_archival v1.0.4)
Configures a resource to be archived instead of destroyed for all destroy actions.
For more information, see [the getting started guide](get-started-with-ash-archival.html)
# [](AshArchival.Resource.html#summary)Summary
## [Functions](AshArchival.Resource.html#functions)
[archive(body)](AshArchival.Resource.html#archive/1)
# [](AshArchival.Resource.html#functions)Functions
[Link to this macro](AshArchival.Resource.html#archive/1 "Link to this macro")
# archive(body)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/resource.ex#L60 "View Source") (macro)
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L1 "View Source") AshArchival.Resource.Info (ash\_archival v1.0.4)
Introspection helpers for [`AshArchival.Resource`](AshArchival.Resource.html)
# [](AshArchival.Resource.Info.html#summary)Summary
## [Functions](AshArchival.Resource.Info.html#functions)
[archive\_archive\_related(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_archive_related/1)
A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation.
[archive\_archive\_related!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_archive_related!/1)
A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation.
[archive\_attribute(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_attribute/1)
The attribute in which to store the archival flag (the current datetime).
[archive\_attribute!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_attribute!/1)
The attribute in which to store the archival flag (the current datetime).
[archive\_attribute\_type(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_attribute_type/1)
The attribute type.
[archive\_attribute\_type!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_attribute_type!/1)
The attribute type.
[archive\_base\_filter?(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_base_filter?/1)
Whether or not a base filter exists that applies the `is_nil(archived_at)` rule.
[archive\_exclude\_destroy\_actions(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_destroy_actions/1)
A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern.
[archive\_exclude\_destroy\_actions!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_destroy_actions!/1)
A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern.
[archive\_exclude\_read\_actions(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_read_actions/1)
A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter.
[archive\_exclude\_read\_actions!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_read_actions!/1)
A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter.
[archive\_exclude\_upsert\_actions(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_upsert_actions/1)
This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more.
[archive\_exclude\_upsert\_actions!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_upsert_actions!/1)
This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more.
[archive\_options(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_options/1)
archive DSL options
# [](AshArchival.Resource.Info.html#functions)Functions
[Link to this function](AshArchival.Resource.Info.html#archive_archive_related/1 "Link to this function")
# archive\_archive\_related(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_archive_related(dsl_or_extended :: module() | map()) ::
{:ok, [atom()]} | :error
```
A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation.
[Link to this function](AshArchival.Resource.Info.html#archive_archive_related!/1 "Link to this function")
# archive\_archive\_related!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_archive_related!(dsl_or_extended :: module() | map()) ::
[atom()] | no_return()
```
A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation.
[Link to this function](AshArchival.Resource.Info.html#archive_attribute/1 "Link to this function")
# archive\_attribute(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_attribute(dsl_or_extended :: module() | map()) :: {:ok, atom()} | :error
```
The attribute in which to store the archival flag (the current datetime).
[Link to this function](AshArchival.Resource.Info.html#archive_attribute!/1 "Link to this function")
# archive\_attribute!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_attribute!(dsl_or_extended :: module() | map()) :: atom() | no_return()
```
The attribute in which to store the archival flag (the current datetime).
[Link to this function](AshArchival.Resource.Info.html#archive_attribute_type/1 "Link to this function")
# archive\_attribute\_type(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_attribute_type(dsl_or_extended :: module() | map()) ::
{:ok, atom()} | :error
```
The attribute type.
[Link to this function](AshArchival.Resource.Info.html#archive_attribute_type!/1 "Link to this function")
# archive\_attribute\_type!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_attribute_type!(dsl_or_extended :: module() | map()) ::
atom() | no_return()
```
The attribute type.
[Link to this function](AshArchival.Resource.Info.html#archive_base_filter?/1 "Link to this function")
# archive\_base\_filter?(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_base_filter?(dsl_or_extended :: module() | map()) :: atom()
```
Whether or not a base filter exists that applies the `is_nil(archived_at)` rule.
[Link to this function](AshArchival.Resource.Info.html#archive_exclude_destroy_actions/1 "Link to this function")
# archive\_exclude\_destroy\_actions(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_exclude_destroy_actions(dsl_or_extended :: module() | map()) ::
{:ok, [atom()]} | :error
```
A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern.
[Link to this function](AshArchival.Resource.Info.html#archive_exclude_destroy_actions!/1 "Link to this function")
# archive\_exclude\_destroy\_actions!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_exclude_destroy_actions!(dsl_or_extended :: module() | map()) ::
[atom()] | no_return()
```
A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern.
[Link to this function](AshArchival.Resource.Info.html#archive_exclude_read_actions/1 "Link to this function")
# archive\_exclude\_read\_actions(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_exclude_read_actions(dsl_or_extended :: module() | map()) ::
{:ok, [atom()]} | :error
```
A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter.
[Link to this function](AshArchival.Resource.Info.html#archive_exclude_read_actions!/1 "Link to this function")
# archive\_exclude\_read\_actions!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_exclude_read_actions!(dsl_or_extended :: module() | map()) ::
[atom()] | no_return()
```
A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter.
[Link to this function](AshArchival.Resource.Info.html#archive_exclude_upsert_actions/1 "Link to this function")
# archive\_exclude\_upsert\_actions(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_exclude_upsert_actions(dsl_or_extended :: module() | map()) ::
{:ok, [atom()]} | :error
```
This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more.
[Link to this function](AshArchival.Resource.Info.html#archive_exclude_upsert_actions!/1 "Link to this function")
# archive\_exclude\_upsert\_actions!(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_exclude_upsert_actions!(dsl_or_extended :: module() | map()) ::
[atom()] | no_return()
```
This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more.
[Link to this function](AshArchival.Resource.Info.html#archive_options/1 "Link to this function")
# archive\_options(dsl\_or\_extended)
[View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source")
```
@spec archive_options(dsl_or_extended :: module() | map()) :: %{
required(atom()) => any()
}
```
archive DSL options
Returns a map containing the and any configured or default values.
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/dsls/DSL:-AshArchival.Resource.md#L1 "View Source") DSL: AshArchival.Resource
Configures a resource to be archived instead of destroyed for all destroy actions.
For more information, see [the getting started guide](get-started-with-ash-archival.html)
## [](dsl-asharchival-resource.html#archive)archive
A section for configuring how archival is configured for a resource.
### [](dsl-asharchival-resource.html#options)Options
NameTypeDefaultDocs[`attribute`](dsl-asharchival-resource.html#archive-attribute)`atom``:archived_at`The attribute in which to store the archival flag (the current datetime).[`attribute_type`](dsl-asharchival-resource.html#archive-attribute_type)`atom``:utc_datetime_usec`The attribute type.[`base_filter?`](dsl-asharchival-resource.html#archive-base_filter?)`atom``false`Whether or not a base filter exists that applies the `is_nil(archived_at)` rule.[`exclude_read_actions`](dsl-asharchival-resource.html#archive-exclude_read_actions)`atom | list(atom)``[]`A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter.[`exclude_upsert_actions`](dsl-asharchival-resource.html#archive-exclude_upsert_actions)`atom | list(atom)``[]`This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more.[`exclude_destroy_actions`](dsl-asharchival-resource.html#archive-exclude_destroy_actions)`atom | list(atom)``[]`A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern.[`archive_related`](dsl-asharchival-resource.html#archive-archive_related)`list(atom)``[]`A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation.
[← Previous Page Upserts & Identities](upserts-and-identities.html)
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/dsls/DSL:-AshArchival.Resource.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/tutorials/get-started-with-ash-archival.md#L1 "View Source") Get Started with AshArchival
## [](get-started-with-ash-archival.html#installation)Installation
First, add the dependency to your `mix.exs` file
```
{:ash_archival, "~> 1.0.4"}
```
and add `:ash_archival` to your `.formatter.exs`
```
import_deps: [..., :ash_archival]
```
## [](get-started-with-ash-archival.html#adding-to-a-resource)Adding to a resource
To add archival to a resource, add the extension to the resource:
```
use Ash.Resource,
extensions: [..., AshArchival.Resource]
```
And thats it! Now, when you destroy a record, it will be archived instead, using an `archived_at` attribute.
See [How Does Ash Archival Work?](get-started-with-ash-archival.html) for what modifications are made to a resource, and read on for info on the tradeoffs of leveraging `Ash.Resource.Dsl.resource.base_filter`.
## [](get-started-with-ash-archival.html#base-filter)Base Filter
Using a `Ash.Resource.Dsl.resource.base_filter` for your `archived_at` field has a lot of benefits if you are using `ash_postgres`, but comes with one major drawback, which is that it is not possible to exclude certain read actions from archival. If you wish to use a base filter, you will need to create a separate resource to read from the archived items. We may introduce a way to bypass the base filter at some point in the future.
To add a `base_filter` and `base_filter_sql` to your resource:
```
resource do
base_filter expr(is_nil(archived_at))
end
postgres do
...
base_filter_sql "(archived_at IS NULL)"
end
```
Add `base_filter? true` to the `archive` configuration of your resource to tell it that it doesn't need to add the filter itself.
### [](get-started-with-ash-archival.html#benefits-of-base_filter)Benefits of base\_filter
1. unique indexes will exclude archived items
2. custom indexes will exclude archived items
3. check constraints will not be applied to archived items
If you want these benefits, add the appropriate `base_filter`.
## [](get-started-with-ash-archival.html#more)More
See the [Unarchiving guide](unarchiving.html) For more.
[← Previous Page Change Log](changelog.html)
[Next Page → Un-archiving](unarchiving.html)
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/tutorials/get-started-with-ash-archival.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/topics/how-does-ash-archival-work.md#L1 "View Source") How does Archival Work?
We make modifications to the resource to enable soft deletes. Here's a breakdown of what the extension does:
## [](how-does-ash-archival-work.html#resource-modifications)Resource Modifications
1. Adds a private `archived_at` `utc_datetime_usec` attribute.
2. Adds a preparation that filters each action for `is_nil(archived_at)` (except for excluded actions, or if you have `base_filter?` set to `true`).
3. Marks all destroy actions as `soft?`, turning them into updates (except for excluded actions)
4. Adds a change to all destroy actions that sets `archived_at` to the current timestamp
5. Adds a change that will iteratively load and destroy anything configured in `AshArchival.Resource.archive.archive_related`
[← Previous Page Un-archiving](unarchiving.html)
[Next Page → Upserts & Identities](upserts-and-identities.html)
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/topics/how-does-ash-archival-work.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/README.md#L1 "View Source") Home
 
 [](https://opensource.org/licenses/MIT) [](https://hex.pm/packages/ash_archival) [](../ash_archival.html)
# AshArchival
AshArchival is an [Ash](../ash.html) extension that provides a push-button solution for soft deleting records, instead of destroying them.
## [](readme.html#tutorials)Tutorials
- [Get Started with AshArchival](get-started-with-ash-archival.html)
## [](readme.html#topics)Topics
- [How does AshArchival work?](how-does-ash-archival-work.html)
- [Unarchiving](unarchiving.html)
## [](readme.html#reference)Reference
- [AshArchival DSL](dsl-asharchival-resource.html)
[← Previous Page API Reference](api-reference.html)
[Next Page → Change Log](changelog.html)
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/README.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/topics/unarchiving.md#L1 "View Source") Un-archiving
If you want to unarchive a resource that uses a base filter, you will need to define a separate resource that uses the same storage and has no base filter. The rest of this guide applies for folks who *aren't* using a `base_filter`.
Un-archiving can be accomplished by creating a read action that is skipped, using `exclude_read_actions`. Then, you can create an update action that sets that attribute to `nil`. For example:
```
archive do
...
exclude_read_actions :archived
end
actions do
read :archived do
filter expr(not is_nil(archived_at))
end
update :unarchive do
change set_attribute(:archived_at, nil)
# if an individual record is used to unarchive
# it must use the `archived` read action for its atomic upgrade
atomic_upgrade_with :archived
end
end
```
You could then do something like this:
```
Resource
|> Ash.get!(id, action: :archived)
|> Ash.Changeset.for_update(:unarchive, %{})
|> Ash.update!()
```
More idiomatically, you would define a code interfaceon the domain, and call that:
```
# to unarchive by `id`
Resource
|> Ash.Query.for_read(:archived, %{})
|> Ash.Query.filter(id == ^id)
|> Domain.unarchive!()
```
[← Previous Page Get Started with AshArchival](get-started-with-ash-archival.html)
[Next Page → How does Archival Work?](how-does-ash-archival-work.html)
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/topics/unarchiving.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_archival](https://github.com/ash-project/ash_archival)
v1.0.4
- Pages
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_archival
Settings
# [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/topics/upserts-and-identities.md#L1 "View Source") Upserts & Identities
Its important to consider identities when using AshArchival *without* a `base_filter` set up.
If you are using a `base_filter`, then all identities implicitly include that `base_filter` in their `where` (handled by the data layer).
Take the following identities, for example:
```
identities do
identity :unique_email, [:email], where: expr(is_nil(archived_at))
# and
identity :unique_email, [:email]
end
```
## [](upserts-and-identities.html#with-is_nil-archived_at)With `is_nil(archived_at)`
Using this identity allows multiple archived records with the same email, but only one *non-archived* record per email. It enables reuse of archived email addresses for new active records, maintaining data integrity by preventing duplicate active records while preserving archived data.
When you upsert a record using this identity, it will only consider active records.
## [](upserts-and-identities.html#without-is_nil-archived_at)Without `is_nil(archived_at)`
This identity configuration enforces strict email uniqueness across all records. Once an email is used, it can't be used again, even after that record is archived.
When you upsert a record using this identity, it will consider all records.
[← Previous Page How does Archival Work?](how-does-ash-archival-work.html)
[Next Page → DSL: AshArchival.Resource](dsl-asharchival-resource.html)
[Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/topics/upserts-and-identities.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version")
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir")
```
--------------------------------------------------------------------------------
/test/docs/ash-docs/ash_cubdb.md:
--------------------------------------------------------------------------------
```markdown
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cubdb "View Source") API Reference ash\_cubdb v0.6.2
## [](api-reference.html#modules)Modules
[AshCubDB](AshCubDB.html)
[`AshCubDB`](AshCubDB.html#content) is an [Ash DataLayer](https://ash-hq.org/docs/module/ash/latest/ash-datalayer) which adds support for persisting Ash resources with [CubDB](https://hex.pm/packages/cubdb).
[AshCubDB.DataLayer](AshCubDB.DataLayer.html)
A CubDB data layer for Ash.
[AshCubDB.Dir](AshCubDB.Dir.html)
Utilities for working with the underlying data directory.
[AshCubDB.Info](AshCubDB.Info.html)
Auto-generated introspection for the AshCubDB DSL.
[AshCubDB.Migration](AshCubDB.Migration.html)
We store and check metadata when opening a database to ensure that the resource and attributes match, and possibly perform migrations.
[AshCubDB.Query](AshCubDB.Query.html)
A struct which holds information about a resource query as it is being built.
[AshCubDB.Serde](AshCubDB.Serde.html)
Handle serialising and deserialising of records into CubDB.
[Next Page → AshCubDB](readme.html)
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/data_layer.ex#L1 "View Source") AshCubDB.DataLayer (ash\_cubdb v0.6.2)
A CubDB data layer for Ash.
## [](AshCubDB.DataLayer.html#module-dsl-documentation)DSL Documentation
### [](AshCubDB.DataLayer.html#module-index)Index
- cubdb
### [](AshCubDB.DataLayer.html#module-docs)Docs
## [](AshCubDB.DataLayer.html#module-cubdb)cubdb
CubDB data layer configuration.
Examples:
```
cubdb do
directory "/opt/storage/my_awesome_resource"
auto_compact? true
auto_file_sync? true
name :my_awesome_resource
end
```
* * *
- `:directory` - The directory within which to store the CubDB data.
If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application.
- `:otp_app` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The OTP application in whose `priv` directory data should be stored.
Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource.
- `:auto_compact?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Whether or not to automatically compact the CubDB database.
See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-is-compaction) for more information. The default value is `true`.
- `:auto_file_sync?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Whether or not to automatically flush the buffer to disk on write.
See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-does-file-sync-mean) The default value is `true`.
- `:name` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The name of the CubDB database.
By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database.
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/dir.ex#L1 "View Source") AshCubDB.Dir (ash\_cubdb v0.6.2)
Utilities for working with the underlying data directory.
# [](AshCubDB.Dir.html#summary)Summary
## [Functions](AshCubDB.Dir.html#functions)
[readable?(resource)](AshCubDB.Dir.html#readable?/1)
Is the directory able to be read from by the current user?
[writable?(resource)](AshCubDB.Dir.html#writable?/1)
Is the directory able to be written to by the current user?
# [](AshCubDB.Dir.html#functions)Functions
[Link to this function](AshCubDB.Dir.html#readable?/1 "Link to this function")
# readable?(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/dir.ex#L23 "View Source")
Is the directory able to be read from by the current user?
[Link to this function](AshCubDB.Dir.html#writable?/1 "Link to this function")
# writable?(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/dir.ex#L11 "View Source")
Is the directory able to be written to by the current user?
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L1 "View Source") AshCubDB (ash\_cubdb v0.6.2)
[`AshCubDB`](AshCubDB.html#content) is an [Ash DataLayer](https://ash-hq.org/docs/module/ash/latest/ash-datalayer) which adds support for persisting Ash resources with [CubDB](https://hex.pm/packages/cubdb).
CubDB is an Elixir-based key value store which supports all Erlang-native terms. More information can be found in [the CubDB readme](https://hexdocs.pm/cubdb/readme.html).
# [](AshCubDB.html#summary)Summary
## [Functions](AshCubDB.html#functions)
[back\_up(resource, target\_path)](AshCubDB.html#back_up/2)
Creates a backup of the database into the target directory path.
[clear(resource)](AshCubDB.html#clear/1)
Deletes all entries, resulting in an empty database.
[compact(resource)](AshCubDB.html#compact/1)
Runs a database compaction.
[compacting?(resource)](AshCubDB.html#compacting?/1)
Returns true if a compaction operation is currently running, false otherwise.
[current\_db\_file(resource)](AshCubDB.html#current_db_file/1)
Returns the path of the current database file.
[data\_dir(resource)](AshCubDB.html#data_dir/1)
Returns the path of the data directory, as given when the [`CubDB`](../cubdb/2.0.2/CubDB.html) process was started.
[dirt\_factor(resource)](AshCubDB.html#dirt_factor/1)
Returns the dirt factor.
[file\_sync(resource)](AshCubDB.html#file_sync/1)
Performs an `fsync`, forcing to flush all data that might be buffered by the OS to disk.
[halt\_compaction(resource)](AshCubDB.html#halt_compaction/1)
Stops a running compaction.
[start(resource)](AshCubDB.html#start/1)
Ensure that the CubDB process is running for the specified resource.
[stop(resource)](AshCubDB.html#stop/1)
Stop the CubDB process running for a specific resource.
# [](AshCubDB.html#functions)Functions
[Link to this function](AshCubDB.html#back_up/2 "Link to this function")
# back\_up(resource, target\_path)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L54 "View Source")
```
@spec back_up(Ash.Resource.t(), Path.t()) :: :ok | {:error, any()}
```
Creates a backup of the database into the target directory path.
Wrapper around [`CubDB.back_up/2`](../cubdb/2.0.2/CubDB.html#back_up/2)
[Link to this function](AshCubDB.html#clear/1 "Link to this function")
# clear(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L67 "View Source")
```
@spec clear(Ash.Resource.t()) :: :ok
```
Deletes all entries, resulting in an empty database.
Wrapper around [`CubDB.clear/1`](../cubdb/2.0.2/CubDB.html#clear/1)
[Link to this function](AshCubDB.html#compact/1 "Link to this function")
# compact(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L80 "View Source")
```
@spec compact(Ash.Resource.t()) :: :ok | {:error, any()}
```
Runs a database compaction.
Wrapper around [`CubDB.compact/1`](../cubdb/2.0.2/CubDB.html#compact/1)
[Link to this function](AshCubDB.html#compacting?/1 "Link to this function")
# compacting?(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L93 "View Source")
```
@spec compacting?(Ash.Resource.t()) :: boolean()
```
Returns true if a compaction operation is currently running, false otherwise.
Wrapper around [`CubDB.compacting?/1`](../cubdb/2.0.2/CubDB.html#compacting?/1)
[Link to this function](AshCubDB.html#current_db_file/1 "Link to this function")
# current\_db\_file(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L106 "View Source")
```
@spec current_db_file(Ash.Resource.t()) :: String.t()
```
Returns the path of the current database file.
Wrapper around [`CubDB.current_db_file/1`](../cubdb/2.0.2/CubDB.html#current_db_file/1)
[Link to this function](AshCubDB.html#data_dir/1 "Link to this function")
# data\_dir(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L118 "View Source")
```
@spec data_dir(Ash.Resource.t()) :: String.t()
```
Returns the path of the data directory, as given when the [`CubDB`](../cubdb/2.0.2/CubDB.html) process was started.
Wrapper around [`CubDB.data_dir/1`](../cubdb/2.0.2/CubDB.html#data_dir/1)
[Link to this function](AshCubDB.html#dirt_factor/1 "Link to this function")
# dirt\_factor(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L130 "View Source")
```
@spec dirt_factor(Ash.Resource.t()) :: float()
```
Returns the dirt factor.
Wrapper around [`CubDB.dirt_factor/1`](../cubdb/2.0.2/CubDB.html#dirt_factor/1)
[Link to this function](AshCubDB.html#file_sync/1 "Link to this function")
# file\_sync(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L142 "View Source")
```
@spec file_sync(Ash.Resource.t()) :: :ok
```
Performs an `fsync`, forcing to flush all data that might be buffered by the OS to disk.
Wrapper around [`CubDB.file_sync/1`](../cubdb/2.0.2/CubDB.html#file_sync/1)
[Link to this function](AshCubDB.html#halt_compaction/1 "Link to this function")
# halt\_compaction(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L154 "View Source")
```
@spec halt_compaction(Ash.Resource.t()) :: :ok | {:error, :no_compaction_running}
```
Stops a running compaction.
Wrapper around [`CubDB.halt_compaction/1`](../cubdb/2.0.2/CubDB.html#halt_compaction/1)
[Link to this function](AshCubDB.html#start/1 "Link to this function")
# start(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L17 "View Source")
```
@spec start(Ash.Resource.t()) :: {:ok, pid()} | {:error, any()}
```
Ensure that the CubDB process is running for the specified resource.
[Link to this function](AshCubDB.html#stop/1 "Link to this function")
# stop(resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L42 "View Source")
```
@spec stop(Ash.Resource.t()) :: :ok
```
Stop the CubDB process running for a specific resource.
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L1 "View Source") AshCubDB.Info (ash\_cubdb v0.6.2)
Auto-generated introspection for the AshCubDB DSL.
# [](AshCubDB.Info.html#summary)Summary
## [Functions](AshCubDB.Info.html#functions)
[cubdb\_auto\_compact?(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_auto_compact?/1)
Whether or not to automatically compact the CubDB database.
[cubdb\_auto\_file\_sync?(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_auto_file_sync?/1)
Whether or not to automatically flush the buffer to disk on write.
[cubdb\_directory(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_directory/1)
The directory within which to store the CubDB data.
[cubdb\_directory!(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_directory!/1)
The directory within which to store the CubDB data.
[cubdb\_name(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_name/1)
The name of the CubDB database.
[cubdb\_name!(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_name!/1)
The name of the CubDB database.
[cubdb\_options(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_options/1)
cubdb DSL options
[cubdb\_otp\_app(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_otp_app/1)
The OTP application in whose `priv` directory data should be stored.
[cubdb\_otp\_app!(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_otp_app!/1)
The OTP application in whose `priv` directory data should be stored.
[field\_layout(resource\_or\_dsl\_state)](AshCubDB.Info.html#field_layout/1)
Retrieve the cached field layout for the resource.
# [](AshCubDB.Info.html#functions)Functions
[Link to this function](AshCubDB.Info.html#cubdb_auto_compact?/1 "Link to this function")
# cubdb\_auto\_compact?(dsl\_or\_extended)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source")
```
@spec cubdb_auto_compact?(dsl_or_extended :: module() | map()) :: boolean()
```
Whether or not to automatically compact the CubDB database.
See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-is-compaction) for more information.
[Link to this function](AshCubDB.Info.html#cubdb_auto_file_sync?/1 "Link to this function")
# cubdb\_auto\_file\_sync?(dsl\_or\_extended)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source")
```
@spec cubdb_auto_file_sync?(dsl_or_extended :: module() | map()) :: boolean()
```
Whether or not to automatically flush the buffer to disk on write.
See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-does-file-sync-mean)
[Link to this function](AshCubDB.Info.html#cubdb_directory/1 "Link to this function")
# cubdb\_directory(dsl\_or\_extended)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source")
```
@spec cubdb_directory(dsl_or_extended :: module() | map()) ::
{:ok, nil | String.t()} | :error
```
The directory within which to store the CubDB data.
If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application.
[Link to this function](AshCubDB.Info.html#cubdb_directory!/1 "Link to this function")
# cubdb\_directory!(dsl\_or\_extended)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source")
```
@spec cubdb_directory!(dsl_or_extended :: module() | map()) ::
(nil | String.t()) | no_return()
```
The directory within which to store the CubDB data.
If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application.
[Link to this function](AshCubDB.Info.html#cubdb_name/1 "Link to this function")
# cubdb\_name(dsl\_or\_extended)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source")
```
@spec cubdb_name(dsl_or_extended :: module() | map()) :: {:ok, atom()} | :error
```
The name of the CubDB database.
By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database.
[Link to this function](AshCubDB.Info.html#cubdb_name!/1 "Link to this function")
# cubdb\_name!(dsl\_or\_extended)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source")
```
@spec cubdb_name!(dsl_or_extended :: module() | map()) :: atom() | no_return()
```
The name of the CubDB database.
By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database.
[Link to this function](AshCubDB.Info.html#cubdb_options/1 "Link to this function")
# cubdb\_options(dsl\_or\_extended)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source")
```
@spec cubdb_options(dsl_or_extended :: module() | map()) :: %{
required(atom()) => any()
}
```
cubdb DSL options
Returns a map containing the and any configured or default values.
[Link to this function](AshCubDB.Info.html#cubdb_otp_app/1 "Link to this function")
# cubdb\_otp\_app(dsl\_or\_extended)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source")
```
@spec cubdb_otp_app(dsl_or_extended :: module() | map()) :: {:ok, atom()} | :error
```
The OTP application in whose `priv` directory data should be stored.
Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource.
[Link to this function](AshCubDB.Info.html#cubdb_otp_app!/1 "Link to this function")
# cubdb\_otp\_app!(dsl\_or\_extended)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source")
```
@spec cubdb_otp_app!(dsl_or_extended :: module() | map()) :: atom() | no_return()
```
The OTP application in whose `priv` directory data should be stored.
Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource.
[Link to this function](AshCubDB.Info.html#field_layout/1 "Link to this function")
# field\_layout(resource\_or\_dsl\_state)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L13 "View Source")
```
@spec field_layout(Ash.Resource.t() | Spark.Dsl.t()) :: nil | {tuple(), tuple()}
```
Retrieve the cached field layout for the resource.
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/migration.ex#L1 "View Source") AshCubDB.Migration (ash\_cubdb v0.6.2)
We store and check metadata when opening a database to ensure that the resource and attributes match, and possibly perform migrations.
# [](AshCubDB.Migration.html#summary)Summary
## [Functions](AshCubDB.Migration.html#functions)
[check(db, resource)](AshCubDB.Migration.html#check/2)
Check that a newly opened database doesn't need to be migrated.
# [](AshCubDB.Migration.html#functions)Functions
[Link to this function](AshCubDB.Migration.html#check/2 "Link to this function")
# check(db, resource)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/migration.ex#L13 "View Source")
```
@spec check(GenServer.server(), Ash.Resource.t()) :: :ok | {:error, any()}
```
Check that a newly opened database doesn't need to be migrated.
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/query.ex#L1 "View Source") AshCubDB.Query (ash\_cubdb v0.6.2)
A struct which holds information about a resource query as it is being built.
# [](AshCubDB.Query.html#summary)Summary
## [Types](AshCubDB.Query.html#types)
[t()](AshCubDB.Query.html#t:t/0)
# [](AshCubDB.Query.html#types)Types
[Link to this type](AshCubDB.Query.html#t:t/0 "Link to this type")
# t()
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/query.ex#L21 "View Source")
```
@type t() :: %AshCubDB.Query{
aggregates: [Ash.Resource.Aggregate.t()],
calculations: [Ash.Resource.Calculation.t()],
distinct: Ash.Sort.t(),
distinct_sort: Ash.Sort.t(),
domain: Ash.Domain.t(),
filter: nil | Ash.Filter.t(),
limit: :infinity | non_neg_integer(),
offset: non_neg_integer(),
relationships: %{
optional(atom()) => Ash.Resource.Relationships.relationship()
},
resource: Ash.Resource.t(),
sort: nil | Ash.Sort.t(),
tenant: any()
}
```
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/serde.ex#L1 "View Source") AshCubDB.Serde (ash\_cubdb v0.6.2)
Handle serialising and deserialising of records into CubDB.
# [](AshCubDB.Serde.html#summary)Summary
## [Functions](AshCubDB.Serde.html#functions)
[deserialise(resource, key, data)](AshCubDB.Serde.html#deserialise/3)
Convert the key and data back into a record..
[serialise(record)](AshCubDB.Serde.html#serialise/1)
Serialise the record into key and value tuples for storage in CubDB.
# [](AshCubDB.Serde.html#functions)Functions
[Link to this function](AshCubDB.Serde.html#deserialise/3 "Link to this function")
# deserialise(resource, key, data)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/serde.ex#L38 "View Source")
```
@spec deserialise(Ash.Resource.t(), tuple(), tuple()) ::
{:ok, Ash.Resource.record()} | {:error, any()}
```
Convert the key and data back into a record..
[Link to this function](AshCubDB.Serde.html#serialise/1 "Link to this function")
# serialise(record)
[View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/serde.ex#L14 "View Source")
```
@spec serialise(Ash.Resource.record()) :: {:ok, tuple(), tuple()} | {:error, any()}
```
Serialise the record into key and value tuples for storage in CubDB.
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/documentation/dsls/DSL:-AshCubDB.DataLayer.md#L1 "View Source") DSL: AshCubDB.DataLayer
A CubDB data layer for Ash.
## [](dsl-ashcubdb-datalayer.html#dsl-documentation)DSL Documentation
### [](dsl-ashcubdb-datalayer.html#index)Index
- cubdb
### [](dsl-ashcubdb-datalayer.html#docs)Docs
## [](dsl-ashcubdb-datalayer.html#cubdb)cubdb
CubDB data layer configuration.
Examples:
```
cubdb do
directory "/opt/storage/my_awesome_resource"
auto_compact? true
auto_file_sync? true
name :my_awesome_resource
end
```
* * *
- `:directory` - The directory within which to store the CubDB data.
If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application.
- `:otp_app` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The OTP application in whose `priv` directory data should be stored.
Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource.
- `:auto_compact?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Whether or not to automatically compact the CubDB database.
See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-is-compaction) for more information. The default value is `true`.
- `:auto_file_sync?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Whether or not to automatically flush the buffer to disk on write.
See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-does-file-sync-mean) The default value is `true`.
- `:name` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The name of the CubDB database.
By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database.
## [](dsl-ashcubdb-datalayer.html#cubdb-1)cubdb
CubDB data layer configuration.
### [](dsl-ashcubdb-datalayer.html#examples)Examples
```
cubdb do
directory "/opt/storage/my_awesome_resource"
auto_compact? true
auto_file_sync? true
name :my_awesome_resource
end
```
### [](dsl-ashcubdb-datalayer.html#options)Options
NameTypeDefaultDocs[`directory`](dsl-ashcubdb-datalayer.html#cubdb-directory)`nil | String.t`The directory within which to store the CubDB data. If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application.[`otp_app`](dsl-ashcubdb-datalayer.html#cubdb-otp_app)`atom`The OTP application in whose `priv` directory data should be stored. Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource.[`auto_compact?`](dsl-ashcubdb-datalayer.html#cubdb-auto_compact?)`boolean``true`Whether or not to automatically compact the CubDB database. See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-is-compaction) for more information.[`auto_file_sync?`](dsl-ashcubdb-datalayer.html#cubdb-auto_file_sync?)`boolean``true`Whether or not to automatically flush the buffer to disk on write. See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-does-file-sync-mean)[`name`](dsl-ashcubdb-datalayer.html#cubdb-name)`atom`The name of the CubDB database. By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database.
[← Previous Page Change Log](changelog.html)
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) ([current file](https://preview.hex.pm/preview/ash_cubdb/0.6.2/show/documentation/dsls/DSL:-AshCubDB.DataLayer.md)) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
[ash\_cubdb](https://harton.dev/james/ash_cubdb)
v0.6.2
- Guides
- Modules
<!--THE END-->
<!--THE END-->
Search documentation of ash\_cubdb
Settings
# [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/README.md#L1 "View Source") AshCubDB
[](https://drone.harton.dev/james/ash_cubdb) [](https://hex.pm/packages/ash_cubdb) [](https://firstdonoharm.dev/version/3/0/full.html)
An [Ash DataLayer](https://ash-hq.org/docs/module/ash/latest/ash-datalayer) which adds support for [CubDB](https://hex.pm/packages/cubdb).
## [](readme.html#status)Status
AshCubDb is still a work in progress. Feel free to give it a go.
FeatureStatusCreate✅Upsert (by primary key)✅Upsert (by identity)❌Read (all)✅Read (by primary key)✅Read (filters)✅Read (sort)✅Read (distinct sort)✅Read (calculations)✅Read (aggregates)❌Update✅Destroy✅Transactions❌
## [](readme.html#github-mirror)Github Mirror
This repository is mirrored [on Github](https://github.com/jimsynz/ash_cubdb) from it's primary location [on my Forgejo instance](https://harton.dev/james/ash_cubdb). Feel free to raise issues and open PRs on Github.
## [](readme.html#installation)Installation
AshCubDB is [available in Hex](https://hex.pm/packages/ash_cubdb), the package can be installed by adding `ash_cubdb` to your list of dependencies in `mix.exs`:
```
def deps do
[
{:ash_cubdb, "~> 0.6.2"}
]
end
```
Documentation for the latest release can be found on [HexDocs](../ash_cubdb.html) and for the `main` branch on [docs.harton.nz](https://docs.harton.nz/james/ash_cubdb).
## [](readme.html#license)License
This software is licensed under the terms of the [HL3-FULL](https://firstdonoharm.dev), see the `LICENSE.md` file included with this package for the terms.
This license actively proscribes this software being used by and for some industries, countries and activities. If your usage of this software doesn't comply with the terms of this license, then [contact me](mailto:[email protected]) with the details of your use-case to organise the purchase of a license - the cost of which may include a donation to a suitable charity or NGO.
[← Previous Page API Reference](api-reference.html)
[Next Page → Change Log](changelog.html)
[Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) ([current file](https://preview.hex.pm/preview/ash_cubdb/0.6.2/show/README.md)) Search HexDocs
Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir")
```