# Task 02: Basic Code Execution **Phase**: 1 - Core (MVP) **Priority**: Critical **Estimated Time**: 1-2 hours **Dependencies**: Task 01 (Project Setup) ## Objective Implement the core `org-babel-execute:elixir` function that can execute Elixir code blocks using external process (one-shot execution). ## Prerequisites - Task 01 completed - Elixir installed and accessible via `elixir` command ## Steps ### Step 1: Add customization group and variables Add to `ob-elixir.el`: ```elisp ;;; Customization (defgroup ob-elixir nil "Org Babel support for Elixir." :group 'org-babel :prefix "ob-elixir-") (defcustom ob-elixir-command "elixir" "Command to execute Elixir code. Can be a full path or command name if in PATH." :type 'string :group 'ob-elixir :safe #'stringp) ``` ### Step 2: Add default header arguments ```elisp ;;; Header Arguments (defvar org-babel-default-header-args:elixir '((:results . "value") (:session . "none")) "Default header arguments for Elixir code blocks.") ``` ### Step 3: Register the language ```elisp ;;; Language Registration ;; File extension for tangling (add-to-list 'org-babel-tangle-lang-exts '("elixir" . "ex")) ;; Associate with elixir-mode for syntax highlighting (if available) (with-eval-after-load 'org-src (add-to-list 'org-src-lang-modes '("elixir" . elixir))) ``` ### Step 4: Implement the execute function ```elisp ;;; Execution (defun org-babel-execute:elixir (body params) "Execute a block of Elixir code with org-babel. BODY is the Elixir code to execute. PARAMS is an alist of header arguments. This function is called by `org-babel-execute-src-block'." (let* ((result-type (cdr (assq :result-type params))) (result-params (cdr (assq :result-params params))) (result (ob-elixir--execute body result-type))) (org-babel-reassemble-table (org-babel-result-cond result-params result (org-babel-script-escape result)) (org-babel-pick-name (cdr (assq :colname-names params)) (cdr (assq :colnames params))) (org-babel-pick-name (cdr (assq :rowname-names params)) (cdr (assq :rownames params)))))) ``` ### Step 5: Implement the internal execute function ```elisp (defun ob-elixir--execute (body result-type) "Execute BODY as Elixir code. RESULT-TYPE is either `value' or `output'. For `value', wraps code to capture return value. For `output', captures stdout directly. Returns the result as a string." (let* ((tmp-file (org-babel-temp-file "ob-elixir-" ".exs")) (code (if (eq result-type 'value) (ob-elixir--wrap-for-value body) body))) (with-temp-file tmp-file (insert code)) (let ((result (org-babel-eval (format "%s %s" ob-elixir-command (org-babel-process-file-name tmp-file)) ""))) (string-trim result)))) ``` ### Step 6: Implement the value wrapper ```elisp (defconst ob-elixir--value-wrapper "result = ( %s ) IO.puts(inspect(result, limit: :infinity, printable_limit: :infinity, charlists: :as_lists)) " "Wrapper template for capturing Elixir expression value. %s is replaced with the user's code.") (defun ob-elixir--wrap-for-value (body) "Wrap BODY to capture its return value. The wrapper evaluates BODY, then prints the result using `inspect/2` with infinite limits to avoid truncation." (format ob-elixir--value-wrapper body)) ``` ### Step 7: Add tests Add to `test/test-ob-elixir.el`: ```elisp (ert-deftest ob-elixir-test-elixir-available () "Test that Elixir is available." (should (executable-find ob-elixir-command))) (ert-deftest ob-elixir-test-simple-value () "Test simple value evaluation." (skip-unless (executable-find ob-elixir-command)) (let ((result (ob-elixir--execute "1 + 1" 'value))) (should (equal "2" result)))) (ert-deftest ob-elixir-test-simple-output () "Test simple output evaluation." (skip-unless (executable-find ob-elixir-command)) (let ((result (ob-elixir--execute "IO.puts(\"hello\")" 'output))) (should (equal "hello" result)))) (ert-deftest ob-elixir-test-multiline-value () "Test multiline code value evaluation." (skip-unless (executable-find ob-elixir-command)) (let ((result (ob-elixir--execute "x = 10\ny = 20\nx + y" 'value))) (should (equal "30" result)))) (ert-deftest ob-elixir-test-list-result () "Test list result." (skip-unless (executable-find ob-elixir-command)) (let ((result (ob-elixir--execute "[1, 2, 3]" 'value))) (should (equal "[1, 2, 3]" result)))) (ert-deftest ob-elixir-test-map-result () "Test map result." (skip-unless (executable-find ob-elixir-command)) (let ((result (ob-elixir--execute "%{a: 1, b: 2}" 'value))) (should (string-match-p "%{a: 1, b: 2}" result)))) ``` ### Step 8: Test in an org buffer Create a test org file `test.org`: ```org * Test ob-elixir ** Basic arithmetic (value) #+BEGIN_SRC elixir 1 + 1 #+END_SRC ** Output test #+BEGIN_SRC elixir :results output IO.puts("Hello, World!") #+END_SRC ** List manipulation #+BEGIN_SRC elixir Enum.map([1, 2, 3], fn x -> x * 2 end) #+END_SRC ``` Press `C-c C-c` on each block to test. ## Acceptance Criteria - [ ] `org-babel-execute:elixir` function exists - [ ] Simple expressions evaluate correctly: `1 + 1` returns `2` - [ ] `:results value` captures return value (default) - [ ] `:results output` captures stdout - [ ] Multiline code executes correctly - [ ] Lists and maps are returned in Elixir format - [ ] All tests pass: `make test` ## Troubleshooting ### "Cannot find elixir" Ensure Elixir is in PATH: ```bash which elixir elixir --version ``` Or set the full path: ```elisp (setq ob-elixir-command "/usr/local/bin/elixir") ``` ### Results are truncated The wrapper uses `limit: :infinity` to prevent truncation. If still truncated, check for very large outputs. ### ANSI codes in output We'll handle this in a later task. For now, output should be clean with the current approach. ## Files Modified - `ob-elixir.el` - Add execution functions - `test/test-ob-elixir.el` - Add execution tests ## References - [docs/03-org-babel-implementation-guide.md](../docs/03-org-babel-implementation-guide.md) - [docs/04-elixir-integration-strategies.md](../docs/04-elixir-integration-strategies.md)