-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a03e72a
commit d3d65ff
Showing
8 changed files
with
184 additions
and
206 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
Welcome to the home for [my personal livebooks](https://github.com/christhekeele/livebooks/tree/latest/livebooks): Elixir experiments, guides, and accompanying source code. | ||
|
||
Their history can be accessed [on GitHub](https://github.com/christhekeele/livebooks) by the clicking the "view source" | ||
`</>` icon next to the title of every page. Corrections, proposals, and discussions can be made in the GitHub [issues](https://github.com/christhekeele/livebooks/issues), [pull requests](https://github.com/christhekeele/livebooks/pulls), and [discussions](https://github.com/christhekeele/livebooks/discussions) pages respectively. | ||
`</>` icon next to the title of every page. Corrections, proposals, and discussions can be made in the GitHub [pull requests](https://github.com/christhekeele/livebooks/pulls), [issues](https://github.com/christhekeele/livebooks/issues), and [discussions](https://github.com/christhekeele/livebooks/discussions) pages respectively. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
defmodule Livebook do | ||
use Application | ||
|
||
@impl true | ||
def start(_type, _args) do | ||
[Livebook.Module] | ||
|> Supervisor.start_link(strategy: :one_for_one, name: Livebook.Supervisor) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
defmodule Livebook.Helpers do | ||
@moduledoc """ | ||
Common helpers for these livebooks. | ||
> #### `use Livebook.Helpers` {: .info} | ||
> | ||
> When you `use Livebook.Helpers`, the following will be | ||
> imported into your livebook: | ||
> | ||
> - `Livebook.Module.defmodule/3`: an iterable version of `defmodule/2` | ||
""" | ||
|
||
defmacro __using__(_opts \\ []) do | ||
quote do | ||
import Livebook.Module, only: [defmodule: 3] | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
defmodule Livebook.Module do | ||
require Matcha.Table.ETS | ||
|
||
@table_name __MODULE__ | ||
|
||
@doc false | ||
def child_spec(options) do | ||
%{ | ||
id: __MODULE__, | ||
start: {__MODULE__, :start_link, options} | ||
} | ||
end | ||
|
||
@doc false | ||
def start_link(_options \\ []) do | ||
if :ets.whereis(@table_name) == :undefined do | ||
:ets.new(@table_name, [:ordered_set, :named_table, :public]) | ||
# We don't actually need to start/supervise any process | ||
:ignore | ||
else | ||
{:error, {:already_started, self()}} | ||
end | ||
end | ||
|
||
@doc """ | ||
Defines a module that can be iterated upon in a livebook. | ||
When defining a module, you can specify a `v: integer` version for it. | ||
You can then later redefine it in your livebook with an incremented version number | ||
without error. | ||
Under the covers, the code provided to define each module is stored, | ||
and retreived and prepended to the code in later versions. | ||
This means that functions defined in previous versions can be patched without warning by | ||
using `defoverridable/1` just before re-defining them, and invoked in the new definition | ||
using `super/1`. Note that because `alias/2`, `require/2`, and `import/2` are lexically scoped, | ||
they must be repeated every module definition. Module attributes (`@/1`), however, work as expected. | ||
Each version of the module is compiled and available as `"\#{module_name}.__v\#{version}"`, | ||
then `alias/2`'d to `module_name` so later code works as expected. | ||
Inspired by [this macro](https://github.com/brettbeatty/experiments_elixir/blob/master/module_patching.livemd) | ||
and [this forum post](https://elixirforum.com/t/how-do-i-redifine-modules-in-livebook/56442/2). | ||
""" | ||
defmacro defmodule( | ||
_module_name = {:__aliases__, _, [alias | []]}, | ||
_version_specifier = [v: version], | ||
_code = [do: block] | ||
) | ||
when version >= 0 do | ||
module = Macro.expand({:__aliases__, [alias: false], [:"Elixir", alias]}, __CALLER__) | ||
version = Macro.expand(version, __CALLER__) | ||
version_namespace = "__v#{version}" |> String.to_atom() | ||
module_namespace = module |> Module.concat(version_namespace) | ||
add_version(module, version, block) | ||
|
||
code = | ||
history(module, version) | ||
|> Macro.prewalk(fn | ||
aliases = {:__aliases__, meta, [^alias | rest]} -> | ||
# IO.inspect(aliases, label: :FOUND) | ||
{:__aliases__, meta, [:"Elixir", alias | rest]} | ||
|
||
code -> | ||
code | ||
end) | ||
|
||
:ok | ||
|
||
quote do | ||
defmodule unquote(module_namespace), do: unquote(code) | ||
alias unquote(module_namespace), as: unquote(module) | ||
end | ||
end | ||
|
||
@doc false | ||
def add_version(module, version, block) do | ||
:ets.insert(@table_name, {{module, version}, block}) | ||
end | ||
|
||
@doc false | ||
def history(module, since_version) do | ||
Matcha.Table.ETS.select @table_name do | ||
{{^module, version}, block} when version <= since_version -> block | ||
end | ||
|> Enum.flat_map(&unblock/1) | ||
end | ||
|
||
defp unblock({:__block__, _meta, block}), do: block | ||
defp unblock(ast), do: [ast] | ||
end |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.