-
Notifications
You must be signed in to change notification settings - Fork 30
[CC-1740] Add Elixir to SQLite #134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Created .formatter.exs for code formatting. - Added .gitattributes for text file handling. - Included .gitignore to exclude unnecessary files. - Set up codecrafters.yml for configuration. - Implemented download_sample_databases.sh to fetch sample databases. - Established mix.exs for project configuration. - Added README.md with instructions for the SQLite challenge. - Created your_program.sh for local execution. - Included compile.sh and run.sh for CodeCrafters integration. - Developed main.ex as the entry point for the SQLite implementation.
WalkthroughElixir support is added for the "Build Your Own SQLite" challenge, including starter templates, solution scaffolding, Docker build configuration, and course integration. The changes introduce scripts for compilation and execution, project configuration files, code formatting and ignore rules, documentation, and a basic Elixir CLI implementation to process SQLite database files. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI (Elixir)
participant SQLite File
User->>CLI (Elixir): Run with [db_file, ".dbinfo"]
CLI (Elixir)->>SQLite File: Open db_file in binary mode
CLI (Elixir)->>SQLite File: Seek to byte 16, read 2 bytes
SQLite File-->>CLI (Elixir): Return page size bytes
CLI (Elixir)->>User: Output page size
Note over CLI (Elixir): If invalid args, print usage message
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🔭 Outside diff range comments (2)
solutions/elixir/01-dr6/code/download_sample_databases.sh (1)
3-10
: 🛠️ Refactor suggestionAdd error handling to download script.
Withoutset -e
orcurl -f
, failed downloads won’t stop the script. It’s important to exit on any failure and detect HTTP errors.+# Exit on failure, undefined variables, and pipe errors +set -euo pipefail echo "Downloading superheroes.db: ~1MB (used in stage 7)" -curl -Lo superheroes.db https://raw.githubusercontent.com/codecrafters-io/sample-sqlite-databases/master/superheroes.db +curl -fLo superheroes.db https://raw.githubusercontent.com/codecrafters-io/sample-sqlite-databases/master/superheroes.db echo "Downloading companies.db: ~7MB (used in stage 8)" -curl -Lo companies.db https://raw.githubusercontent.com/codecrafters-io/sample-sqlite-databases/master/companies.db +curl -fLo companies.db https://raw.githubusercontent.com/codecrafters-io/sample-sqlite-databases/master/companies.db echo "Sample databases downloaded."compiled_starters/elixir/download_sample_databases.sh (1)
1-10
: 🛠️ Refactor suggestionAdd fail-fast error handling to database downloads.
Currently the script lacks
set -e
and flags to fail on HTTP errors, which could lead to partial or silent failures. To ensure robustness, prependset -e
and usecurl -fLo
:+#!/bin/sh +set -e echo "Downloading superheroes.db: ~1MB (used in stage 7)" - curl -Lo superheroes.db https://.../superheroes.db + curl -fLo superheroes.db https://.../superheroes.db echo "Downloading companies.db: ~7MB (used in stage 8)" - curl -Lo companies.db https://.../companies.db + curl -fLo companies.db https://.../companies.db echo "Sample databases downloaded."This change ensures any download error exits the script immediately.
🧹 Nitpick comments (17)
solutions/elixir/01-dr6/explanation.md (1)
14-18
: Add language specifier to code fence.The fenced code block should specify a language for better syntax highlighting and adherence to markdown best practices.
-``` +```bash git add . git commit -m "pass 1st stage" # any msg git push origin master🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
14-14: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
compiled_starters/elixir/lib/main.ex (2)
7-16
: Consider adding file closing.The file is opened but never explicitly closed. While Elixir/Erlang will eventually clean up resources, it's a good practice to explicitly close files when done with them.
file = File.open!(database_file_path, [:read, :binary]) # You can use print statements as follows for debugging, they'll be visible when running tests. IO.puts(:stderr, "Logs from your program will appear here!") # Uncomment this to pass the first stage # :file.position(file, 16) # Skip the first 16 bytes of the header # <<page_size::16>> = IO.binread(file, 2) # IO.puts("database page size: #{page_size}") +# File.close(file)
5-16
: Consider handling other commands gracefully.The code only handles the ".dbinfo" command but doesn't provide feedback for other commands. Consider adding an "else" clause to handle unrecognized commands with a helpful error message.
case command do ".dbinfo" -> file = File.open!(database_file_path, [:read, :binary]) # You can use print statements as follows for debugging, they'll be visible when running tests. IO.puts(:stderr, "Logs from your program will appear here!") # Uncomment this to pass the first stage # :file.position(file, 16) # Skip the first 16 bytes of the header # <<page_size::16>> = IO.binread(file, 2) # IO.puts("database page size: #{page_size}") + _ -> + IO.puts("Error: unknown command '#{command}'") endcompiled_starters/elixir/.codecrafters/run.sh (1)
1-8
: Use/usr/bin/env
in shebang for portability.
Prefer invoking the shell viaenv
to ensure compatibility across environments where/bin/sh
might differ.-#!/bin/sh +#!/usr/bin/env shstarter_templates/elixir/code/.codecrafters/run.sh (1)
1-8
: Use/usr/bin/env
in shebang for portability.
Align with other scripts and avoid hardcoding/bin/sh
.-#!/bin/sh +#!/usr/bin/env shstarter_templates/elixir/code/.codecrafters/compile.sh (1)
1-8
: Use/usr/bin/env
in shebang for consistency.
Switch to#!/usr/bin/env sh
to mirror other scripts and improve portability.-#!/bin/sh +#!/usr/bin/env shsolutions/elixir/01-dr6/code/download_sample_databases.sh (1)
1-2
: Use/usr/bin/env
in shebang for portability.
Consistent shebang usage across scripts will avoid path issues.-#!/bin/sh +#!/usr/bin/env shsolutions/elixir/01-dr6/code/lib/main.ex (2)
7-11
: Consider closing the file after reading.While Elixir's garbage collection will eventually close the file, it's a good practice to explicitly close it after reading to release system resources promptly.
file = File.open!(database_file_path, [:read, :binary]) :file.position(file, 16) # Skip the first 16 bytes of the header <<page_size::16>> = IO.binread(file, 2) IO.puts("database page size: #{page_size}") +File.close(file)
4-12
: Add error handling for file operations.The current implementation uses
File.open!
which will raise an exception if the file doesn't exist or can't be opened. Consider adding error handling to provide a more user-friendly message.case args do [database_file_path, command] -> case command do ".dbinfo" -> - file = File.open!(database_file_path, [:read, :binary]) - - :file.position(file, 16) # Skip the first 16 bytes of the header - <<page_size::16>> = IO.binread(file, 2) - IO.puts("database page size: #{page_size}") + try do + file = File.open!(database_file_path, [:read, :binary]) + + :file.position(file, 16) # Skip the first 16 bytes of the header + <<page_size::16>> = IO.binread(file, 2) + IO.puts("database page size: #{page_size}") + File.close(file) + rescue + error in File.Error -> + IO.puts("Error: Could not open database file '#{database_file_path}': #{error.reason}") + System.halt(1) + endstarter_templates/elixir/code/lib/main.ex (1)
12-15
: Consider adding more context about SQLite file format.For educational purposes, it might be helpful to add a brief comment explaining why we're skipping 16 bytes (it's the SQLite file header) and that the page size is stored as a 16-bit integer at that position according to the SQLite file format specification.
# Uncomment this to pass the first stage # :file.position(file, 16) # Skip the first 16 bytes of the header # <<page_size::16>> = IO.binread(file, 2) # IO.puts("database page size: #{page_size}") +# Note: The first 16 bytes of a SQLite file contain the header +# The page size is stored as a 2-byte integer at offset 16 +# See: https://www.sqlite.org/fileformat.htmldockerfiles/elixir-1.18.Dockerfile (1)
23-25
: Consider multi-stage build for leaner images
Right now you’re moving build artifacts into/app-cached
. As an alternative, use a multi-stage Docker build: one stage to compile and cache deps, another minimal stage copying only the escript output.# Builder stage FROM elixir:1.18.3-alpine AS builder WORKDIR /app COPY . . RUN mix local.hex --force && \ mix local.rebar --force && \ mix deps.get && \ mix deps.compile && \ .codecrafters/compile.sh # Final stage FROM alpine:latest WORKDIR /app COPY --from=builder /app/codecrafters_sqlite /usr/local/bin/ ENTRYPOINT ["codecrafters_sqlite"]solutions/elixir/01-dr6/code/README.md (3)
61-61
: Use clearer approximation notation
Replace~1MB
with a more standard form (e.g., “≈1 MB” or “about 1 MB”) for readability.
65-65
: Use clearer approximation notation
Replace~7MB
with “≈7 MB” or “about 7 MB” to avoid confusion with markdown syntax.
67-68
: Make phrasing more concise
Change “These aren’t included in the repository because they’re large in size.” to “These aren’t included because they’re large.”🧰 Tools
🪛 LanguageTool
[style] ~67-~67: This wording could be more concise.
Context: ...luded in the repository because they're large in size. You can download them by running this ...(ADJECTIVE_IN_ATTRIBUTE)
compiled_starters/elixir/README.md (3)
61-61
: Use clearer approximation notation
In the compiled starter README, change~1MB
to “≈1 MB” or “about 1 MB” for consistency.
65-65
: Use clearer approximation notation
Update~7MB
to “≈7 MB” or “about 7 MB” to prevent markdown parsing issues.
67-68
: Streamline wording
Revise “These aren’t included in the repository because they're large in size.” to “These aren’t included because they’re large.”🧰 Tools
🪛 LanguageTool
[style] ~67-~67: This wording could be more concise.
Context: ...luded in the repository because they're large in size. You can download them by running this ...(ADJECTIVE_IN_ATTRIBUTE)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (33)
compiled_starters/elixir/.codecrafters/compile.sh
(1 hunks)compiled_starters/elixir/.codecrafters/run.sh
(1 hunks)compiled_starters/elixir/.formatter.exs
(1 hunks)compiled_starters/elixir/.gitattributes
(1 hunks)compiled_starters/elixir/.gitignore
(1 hunks)compiled_starters/elixir/README.md
(1 hunks)compiled_starters/elixir/codecrafters.yml
(1 hunks)compiled_starters/elixir/download_sample_databases.sh
(1 hunks)compiled_starters/elixir/lib/main.ex
(1 hunks)compiled_starters/elixir/mix.exs
(1 hunks)compiled_starters/elixir/your_program.sh
(1 hunks)course-definition.yml
(1 hunks)dockerfiles/elixir-1.18.Dockerfile
(1 hunks)solutions/elixir/01-dr6/code/.codecrafters/compile.sh
(1 hunks)solutions/elixir/01-dr6/code/.codecrafters/run.sh
(1 hunks)solutions/elixir/01-dr6/code/.formatter.exs
(1 hunks)solutions/elixir/01-dr6/code/.gitattributes
(1 hunks)solutions/elixir/01-dr6/code/.gitignore
(1 hunks)solutions/elixir/01-dr6/code/README.md
(1 hunks)solutions/elixir/01-dr6/code/codecrafters.yml
(1 hunks)solutions/elixir/01-dr6/code/download_sample_databases.sh
(1 hunks)solutions/elixir/01-dr6/code/lib/main.ex
(1 hunks)solutions/elixir/01-dr6/code/mix.exs
(1 hunks)solutions/elixir/01-dr6/code/your_program.sh
(1 hunks)solutions/elixir/01-dr6/diff/lib/main.ex.diff
(1 hunks)solutions/elixir/01-dr6/explanation.md
(1 hunks)starter_templates/elixir/code/.codecrafters/compile.sh
(1 hunks)starter_templates/elixir/code/.codecrafters/run.sh
(1 hunks)starter_templates/elixir/code/.formatter.exs
(1 hunks)starter_templates/elixir/code/.gitignore
(1 hunks)starter_templates/elixir/code/lib/main.ex
(1 hunks)starter_templates/elixir/code/mix.exs
(1 hunks)starter_templates/elixir/config.yml
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
starter_templates/elixir/code/lib/main.ex (2)
compiled_starters/elixir/lib/main.ex (1)
main
(2-20)solutions/elixir/01-dr6/code/lib/main.ex (1)
main
(2-16)
compiled_starters/elixir/lib/main.ex (2)
solutions/elixir/01-dr6/code/lib/main.ex (1)
main
(2-16)starter_templates/elixir/code/lib/main.ex (1)
main
(2-20)
solutions/elixir/01-dr6/code/lib/main.ex (2)
compiled_starters/elixir/lib/main.ex (1)
main
(2-20)starter_templates/elixir/code/lib/main.ex (1)
main
(2-20)
🪛 markdownlint-cli2 (0.17.2)
solutions/elixir/01-dr6/explanation.md
14-14: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
🪛 LanguageTool
solutions/elixir/01-dr6/code/README.md
[uncategorized] ~58-~58: Loose punctuation mark.
Context: ...s that you can use: 1. superheroes.db
: - This is a small version of the tes...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~62-~62: Loose punctuation mark.
Context: ... - It is ~1MB in size. 1. companies.db
: - This is a small version of the tes...
(UNLIKELY_OPENING_PUNCTUATION)
[style] ~67-~67: This wording could be more concise.
Context: ...luded in the repository because they're large in size. You can download them by running this ...
(ADJECTIVE_IN_ATTRIBUTE)
compiled_starters/elixir/README.md
[uncategorized] ~58-~58: Loose punctuation mark.
Context: ...s that you can use: 1. superheroes.db
: - This is a small version of the tes...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~62-~62: Loose punctuation mark.
Context: ... - It is ~1MB in size. 1. companies.db
: - This is a small version of the tes...
(UNLIKELY_OPENING_PUNCTUATION)
[style] ~67-~67: This wording could be more concise.
Context: ...luded in the repository because they're large in size. You can download them by running this ...
(ADJECTIVE_IN_ATTRIBUTE)
🪛 Hadolint (2.12.0)
dockerfiles/elixir-1.18.Dockerfile
[error] 10-10: invalid flag: --exclude
(DL1000)
🔇 Additional comments (36)
compiled_starters/elixir/.gitattributes (1)
1-1
: Approve: Normalize line endings for Elixir compiled starter
Adding* text=auto
ensures consistent line endings across platforms and aligns with other language templates.compiled_starters/elixir/mix.exs (4)
1-4
: Approve: Mix project module declaration
TheApp.MixProject
module anduse Mix.Project
invocation follow standard Mix conventions.
5-14
: Approve: Project settings configuration
The project is correctly configured with:codecrafters_sqlite
as the app, semantic versioning, Elixir requirement, production flag, and escript main module.
16-21
: Approve: Application runtime configuration
Declaring:logger
inextra_applications
is appropriate for CLI logging.
23-29
: Approve: Dependency placeholder
No active dependencies are declared yet. The commented examples provide guidance on adding Hex or Git dependencies when needed.solutions/elixir/01-dr6/code/.gitattributes (1)
1-1
: Approve: Normalize line endings for Elixir solution code
Setting* text=auto
aligns solution files with the starter template and ensures consistent Git behavior.starter_templates/elixir/config.yml (1)
1-3
: Approve: Starter template metadata configuration
Therequired_executable
anduser_editable_file
keys correctly describe the tooling requirement and user entry point.compiled_starters/elixir/.formatter.exs (1)
1-4
: Approve: Formatter configuration
Theinputs
list correctly includesmix.exs
,.formatter.exs
, and all Elixir source files underconfig
,lib
, andtest
.solutions/elixir/01-dr6/code/codecrafters.yml (2)
1-5
: Configuration: debug flag
Thedebug: false
default is sensible to minimize verbose logs. Developers can enable it when troubleshooting.
7-11
: Configuration: language pack
Pinning toelixir-1.18
ensures consistency across CI and local environments. Looks correct.starter_templates/elixir/code/.gitignore (1)
1-27
: Comprehensive .gitignore for Elixir starter template
All common build artifacts—compiled binaries,_build
,deps
,cover
, docs, crash dumps, archives, etc.—are excluded. This will keep the starter repo clean.course-definition.yml (1)
32-37
: Enable Elixir track in course definition
Adding- slug: "elixir"
correctly exposes the new Elixir implementation alongside other languages.solutions/elixir/01-dr6/code/.formatter.exs (1)
1-4
: Elixir formatter configuration
Theinputs
patterns covermix.exs
,.formatter.exs
, and all.ex
/.exs
files underconfig
,lib
, andtest
. This aligns with standard Mix formatting conventions.compiled_starters/elixir/codecrafters.yml (2)
1-5
: Configuration: debug flag in compiled starter
The defaultdebug: false
is appropriate. Enable when you need detailed runtime logs.
7-11
: Configuration: language pack for compiled starter
Specifyingelixir-1.18
ensures the compiled starter uses the correct Elixir version.starter_templates/elixir/code/.formatter.exs (1)
1-4
: Looks good - appropriate formatter configuration.This is a standard Elixir formatter configuration that properly specifies patterns for files to be formatted by the
mix format
tool.solutions/elixir/01-dr6/code/.codecrafters/run.sh (1)
1-11
: Execution script looks correct.The run script properly executes the compiled Elixir program at the expected location with appropriate error handling. The use of
exec
is a good practice to replace the shell process with the target process.solutions/elixir/01-dr6/code/.codecrafters/compile.sh (1)
1-12
: Compilation script is correctly implemented.The compile script correctly builds the Elixir project as an escript and moves it to the expected location for execution. The error handling with
set -e
ensures compilation failures are properly reported.solutions/elixir/01-dr6/explanation.md (1)
1-18
: Clear instructions for first stage implementation.The explanation provides clear guidance on how to implement the first stage by directing the user to the relevant code file and showing the specific code to uncomment.
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
14-14: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
compiled_starters/elixir/lib/main.ex (1)
1-21
: Main module implementation looks good.The CLI module correctly implements the entry point for the SQLite challenge with appropriate command handling. The commented code provides a good scaffold for users to implement the first stage.
compiled_starters/elixir/.codecrafters/run.sh (1)
9-12
: Run script logic is correct.
Theset -e
ensures the script fails fast on any error, andexec ... "$@"
properly forwards all arguments to the escript.starter_templates/elixir/code/.codecrafters/run.sh (1)
9-12
: Starter template run script is solid.
set -e
andexec /tmp/codecrafters-build-sqlite-elixir "$@"
correctly handle errors and argument passing.solutions/elixir/01-dr6/code/.gitignore (1)
1-31
: .gitignore rules are comprehensive for Elixir artifacts
The patterns cover database files, build outputs, coverage reports, dependencies, docs, crash dumps, and package artifacts. This aligns well with Elixir project conventions.starter_templates/elixir/code/.codecrafters/compile.sh (1)
9-13
: Compile script follows best practices.
mix escript.build
and moving the executable into/tmp
withset -e
is exactly what's needed for the CodeCrafters workflow.solutions/elixir/01-dr6/code/lib/main.ex (1)
1-17
: Well-structured CLI module for SQLite database inspection.The implementation correctly opens the database file in binary mode, skips the 16-byte SQLite header, reads the 2-byte page size value, and outputs it in the expected format. The command-line argument handling is also properly implemented.
solutions/elixir/01-dr6/diff/lib/main.ex.diff (1)
10-19
: Clean transformation from template to implementation.The diff correctly removes the debug statements and comments, and implements the necessary code to read and display the database page size. This transformation is exactly what we'd expect when moving from a template to a working solution.
compiled_starters/elixir/.codecrafters/compile.sh (1)
1-12
: Well-structured compile script for the CodeCrafters environment.The script correctly builds the Elixir escript and moves it to the expected location for the CodeCrafters platform. The use of
set -e
ensures the script will exit immediately if any command fails, which is good practice.starter_templates/elixir/code/lib/main.ex (1)
1-21
: Clear and helpful starter template for Elixir SQLite implementation.The template provides a good starting point with clear comments and instructions on what to uncomment to complete the first stage. The debug print statement helps users understand how to output debug information during testing.
compiled_starters/elixir/.gitignore (1)
1-30
: Ignore patterns cover relevant build artifacts and temporary files.All key Elixir/Mix artifacts (
/_build/
,/deps/
,/cover/
,/doc/
), compiled escript binaries,.db
files, VM dumps, and other transient files are excluded. This will keep the repository clean and focused on source files.compiled_starters/elixir/your_program.sh (1)
1-25
: Local run script correctly compiles and executes the Elixir escript.The shebang and
set -e
directive ensure immediate exit on failure, and thecd
into the script directory guarantees the correct working context. Themix escript.build
andmv
commands mirror the remote compile script, andexec
properly launches the escript with passed arguments. Ensure the file has executable permissions (chmod +x
) when committing.solutions/elixir/01-dr6/code/your_program.sh (1)
1-26
: Solution run script follows the same compile-and-exec pattern.This script mirrors the starter version for local execution within the solution directory. It uses
set -e
, compiles viamix escript.build
, moves the binary, and execs it. Confirm executable permissions are set, and consider consolidating shared logic if maintenance overhead grows.starter_templates/elixir/code/mix.exs (1)
1-31
: Mix project configuration is properly defined.The
project/0
,application/0
, anddeps/0
definitions correctly set up:codecrafters_sqlite
with the appropriate version, Elixir requirement, and escript entry point. The empty deps list is suitable for a starter template, and the file aligns with standard Mix conventions.solutions/elixir/01-dr6/code/mix.exs (4)
1-4
: Project module declaration is solid
TheApp.MixProject
module correctly usesMix.Project
and sets up the application scaffold.
5-14
: Mix project configuration looks correct
Theproject/0
function properly declares the app name, version, Elixir requirement, escript main module, and deps invocation.
17-21
: OTP application settings are appropriate
Declaring only:logger
inextra_applications
is sufficient for this CLI-focused project.
24-29
: Dependencies placeholder is ready for future additions
No active deps are needed yet; commented examples can guide adding Hex or Git dependencies.
# .git & README.md are unique per-repository. We ignore them on first copy to prevent cache misses | ||
COPY --exclude=.git --exclude=README.md . /app | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invalid COPY --exclude
flags: switch to .dockerignore
--exclude
isn’t supported by Docker’s COPY
. Use a .dockerignore
file to omit .git
and README.md
.
Apply this diff:
- COPY --exclude=.git --exclude=README.md . /app
+ COPY . /app
And add a .dockerignore
alongside your Dockerfile:
.git
README.md
🧰 Tools
🪛 Hadolint (2.12.0)
[error] 10-10: invalid flag: --exclude
(DL1000)
Summary by CodeRabbit