Current section
19 Versions
Jump to
Current section
19 Versions
Compare versions
6
files changed
+318
additions
-258
deletions
| @@ -12,7 +12,7 @@ The package can be installed by adding `mishka_developer_tools` to your list of | |
| 12 12 | ```elixir |
| 13 13 | def deps do |
| 14 14 | [ |
| 15 | - {:mishka_developer_tools, "~> 0.0.6"} |
| 15 | + {:mishka_developer_tools, "~> 0.0.8"} |
| 16 16 | ] |
| 17 17 | end |
| 18 18 | ``` |
| @@ -28,8 +28,8 @@ At first, you need to call the `__using__` macro of the Mishka developer tools. | |
| 28 28 | ```elixir |
| 29 29 | use MishkaDeveloperTools.DB.CRUD, |
| 30 30 | module: YOUR_SCHEMA_MODULE, |
| 31 | - error_atom: :YOUR_REQUESTED_ATOM, |
| 32 | - repo: YOUR_REPO_MODULE |
| 31 | + repo: YOUR_REPO_MODULE, |
| 32 | + id: :uuid OR ANY_TYPE_YOU_WANT |
| 33 33 | ``` |
| 34 34 | |
| 35 35 | And after that, you can define `@behaviour`; it is optional. |
| @@ -41,38 +41,46 @@ Now is the time you can have your CRUD function; it should be noted you are not | |
| 41 41 | |
| 42 42 | |
| 43 43 | ```elixir |
| 44 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_add, 1} |
| 44 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :create, 1} |
| 45 45 | def create(attrs) do |
| 46 46 | crud_add(attrs) |
| 47 47 | end |
| 48 48 | |
| 49 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_add, 1} |
| 49 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :create, 1} |
| 50 50 | def create(attrs, allowed_fields) do |
| 51 51 | crud_add(attrs, allowed_fields) |
| 52 52 | end |
| 53 53 | |
| 54 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_edit, 1} |
| 54 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :edit, 1} |
| 55 55 | def edit(attrs) do |
| 56 56 | crud_edit(attrs) |
| 57 57 | end |
| 58 58 | |
| 59 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_edit, 1} |
| 59 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :edit, 1} |
| 60 60 | def edit(attrs, allowed_fields) do |
| 61 61 | crud_edit(attrs, allowed_fields) |
| 62 62 | end |
| 63 63 | |
| 64 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_delete, 1} |
| 64 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :delete, 1} |
| 65 65 | def delete(id) do |
| 66 66 | crud_delete(id) |
| 67 67 | end |
| 68 68 | |
| 69 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_get_record, 1} |
| 69 | + # It is optional |
| 70 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :delete, 2} |
| 71 | + def delete(id) do |
| 72 | + crud_delete(id) |
| 73 | + end |
| 74 | + |
| 75 | + # It is optional |
| 76 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :show_by_id, 1} |
| 70 77 | def show_by_id(id) do |
| 71 78 | crud_get_record(id) |
| 72 79 | end |
| 73 80 | |
| 74 81 | # It is optional |
| 75 | - def get_something(field) do |
| 82 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :show_by_field, 1} |
| 83 | + def show_by_field(field) do |
| 76 84 | crud_get_by_field("field", field) |
| 77 85 | end |
| 78 86 | ``` |
| \ No newline at end of file |
| @@ -1,66 +0,0 @@ | |
| 1 | - # کتابخانه و ابزار های توسعه پروژه الیکسیر |
| 2 | - اخیرا تیم [میشکا](https://github.com/mishka-group) در حال توسعه [سیستم مدیریت محتوا](https://github.com/mishka-group/mishka-cms) می باشد که تصمیم بر این گرفتیم برخی از شبهه کد های آن را استخراج و تبدیل به افزونه قابل نصب در پروژه های الیکسیر کنیم.در حقیقت این کتابخانه تشکیل شده از چند ماکرو و فانکشن برای راحتی کار با بانک اطلاعاتی و دیگر بخش ها یک پروژه می باشد. |
| 3 | - |
| 4 | - ## نصب |
| 5 | - ```elixir |
| 6 | - def deps do |
| 7 | - [ |
| 8 | - {:mishka_developer_tools, "~> 0.0.6"} |
| 9 | - ] |
| 10 | - end |
| 11 | - ``` |
| 12 | - > برای مشاهده اسناد این کتابخانه به لینک https://hexdocs.pm/mishka_developer_tools مراجعه کنید. |
| 13 | - |
| 14 | - ## پیادهسازی سریع CRUD |
| 15 | - برای پیادهسازی توابع اولیه ساخت CRUD فقط کافی می باشد موارد زیر را به ترتیب انجام بدهید. |
| 16 | - |
| 17 | - ### فراخوانی ماکرو |
| 18 | - ```elixir |
| 19 | - use MishkaDeveloperTools.DB.CRUD, |
| 20 | - module: YOUR_SCHEMA, |
| 21 | - error_atom: :YOUR_REQUESTED_ATOM, |
| 22 | - repo: YOUR_REPO_MODULE |
| 23 | - ``` |
| 24 | - شما بعد از فراخوانی تابع `__using__` کافی می باشد سه پارامتر بالا از جمعه {`module`, `error_atom`, `repo`} را ارزش گذاری نمایید. و همینطور برای نظم بخشیدن به ساختار کد های خودتان می توانید `@behaviour` که در این کتابخانه نوشته شده است را نیز در فایل خود فراخوانی کنید. |
| 25 | - ```elixir |
| 26 | - @behaviour MishkaDeveloperTools.DB.CRUD |
| 27 | - ``` |
| 28 | - و حال نوبت به این رسیده است که ماکرو های `CRUD` را فراخوانی کنید. توجه داشته باشید این ماکرو ها حتما نیازی ندارند که در یک تابع قرار داده بشود |
| 29 | - |
| 30 | - ```elixir |
| 31 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_add, 1} |
| 32 | - def create(attrs) do |
| 33 | - crud_add(attrs) |
| 34 | - end |
| 35 | - |
| 36 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_add, 1} |
| 37 | - def create(attrs, allowed_fields) do |
| 38 | - crud_add(attrs, allowed_fields) |
| 39 | - end |
| 40 | - |
| 41 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_edit, 1} |
| 42 | - def edit(attrs) do |
| 43 | - crud_edit(attrs) |
| 44 | - end |
| 45 | - |
| 46 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_edit, 1} |
| 47 | - def edit(attrs, allowed_fields) do |
| 48 | - crud_edit(attrs, allowed_fields) |
| 49 | - end |
| 50 | - |
| 51 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_delete, 1} |
| 52 | - def delete(id) do |
| 53 | - crud_delete(id) |
| 54 | - end |
| 55 | - |
| 56 | - @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :crud_get_record, 1} |
| 57 | - def show_by_id(id) do |
| 58 | - crud_get_record(id) |
| 59 | - end |
| 60 | - ``` |
| 61 | - |
| 62 | - لازم به ذکر است ماکرو دیگری نیز وجود دارد که برای آن `callback` درست نگردیده است ولی می توانید به صورت زیر فراخوانی گردد |
| 63 | - ```elixir |
| 64 | - crud_get_by_field("alias_link", alias_link) |
| 65 | - ``` |
| 66 | - در ماکرو بالا بجای `alias_link` می توانید فیلد مورد نظری که می خواهد به صورت `get_by` فراخوانی کنید را قرار بدهید. |
| \ No newline at end of file |
| @@ -7,8 +7,8 @@ | |
| 7 7 | [<<"lib">>,<<"lib/macros">>,<<"lib/macros/database">>, |
| 8 8 | <<"lib/macros/database/crud.ex">>,<<"lib/mishka_developer_tools.ex">>, |
| 9 9 | <<"lib/application.ex">>,<<".formatter.exs">>,<<"mix.exs">>,<<"LICENSE">>, |
| 10 | - <<"README.md">>,<<"README_FA.md">>]}. |
| 11 | - {<<"licenses">>,[<<"Apache License 2.0">>]}. |
| 10 | + <<"README.md">>]}. |
| 11 | + {<<"licenses">>,[<<"Apache-2.0">>]}. |
| 12 12 | {<<"links">>, |
| 13 13 | [{<<"GitHub">>, |
| 14 14 | <<"https://github.com/mishka-group/mishka_developer_tools">>}]}. |
| @@ -18,15 +18,15 @@ | |
| 18 18 | {<<"name">>,<<"ecto_sql">>}, |
| 19 19 | {<<"optional">>,false}, |
| 20 20 | {<<"repository">>,<<"hexpm">>}, |
| 21 | - {<<"requirement">>,<<"~> 3.8">>}], |
| 21 | + {<<"requirement">>,<<"~> 3.9">>}], |
| 22 22 | [{<<"app">>,<<"postgrex">>}, |
| 23 23 | {<<"name">>,<<"postgrex">>}, |
| 24 24 | {<<"optional">>,false}, |
| 25 25 | {<<"repository">>,<<"hexpm">>}, |
| 26 | - {<<"requirement">>,<<"~> 0.16.3">>}], |
| 26 | + {<<"requirement">>,<<"~> 0.16.5">>}], |
| 27 27 | [{<<"app">>,<<"ecto_enum">>}, |
| 28 28 | {<<"name">>,<<"ecto_enum">>}, |
| 29 29 | {<<"optional">>,false}, |
| 30 30 | {<<"repository">>,<<"hexpm">>}, |
| 31 31 | {<<"requirement">>,<<"~> 1.4">>}]]}. |
| 32 | - {<<"version">>,<<"0.0.7">>}. |
| 32 | + {<<"version">>,<<"0.0.8">>}. |
| @@ -8,48 +8,48 @@ defmodule MishkaDeveloperTools.DB.CRUD do | |
| 8 8 | ```elixir |
| 9 9 | use MishkaDeveloperTools.DB.CRUD, |
| 10 10 | module: YOURschemaMODULE, |
| 11 | - error_atom: :your_error_tag, |
| 12 | - repo: Your.Repo |
| 11 | + repo: Your.Repo, |
| 12 | + id: :uuid OR ANY_TYPE_YOU_WANT |
| 13 13 | ``` |
| 14 14 | It should be noted that the following three parameters must be sent and also make sure you are connected to the database. |
| 15 15 | |
| 16 16 | ```elixir |
| 17 17 | module |
| 18 | - error_atom |
| 19 18 | repo |
| 19 | + id |
| 20 20 | ``` |
| 21 21 | """ |
| 22 22 | |
| 23 23 | # custom Typespecs |
| 24 | - @type data_uuid() :: Ecto.UUID.t |
| 25 24 | @type record_input() :: map() |
| 26 | - @type error_tag() :: atom() |
| 27 25 | @type repo_data() :: Ecto.Schema.t() |
| 28 26 | @type repo_error() :: Ecto.Changeset.t() |
| 29 27 | |
| 30 | - |
| 31 28 | @callback create(record_input()) :: |
| 32 | - {:error, :add, error_tag(), repo_error()} | |
| 33 | - {:ok, :add, error_tag(), repo_data()} |
| 29 | + {:error, :add, repo_error()} | {:ok, :add, repo_data()} |
| 34 30 | |
| 35 31 | @callback edit(record_input()) :: |
| 36 | - {:error, :edit, :uuid, error_tag()} | |
| 37 | - {:error, :edit, :get_record_by_id, error_tag()} | |
| 38 | - {:error, :edit, error_tag(), repo_error()} | |
| 39 | - {:ok, :edit, error_tag(), repo_data()} |
| 32 | + {:error, :edit, repo_error()} |
| 33 | + | {:ok, :edit, repo_data()} |
| 34 | + | {:error, :edit, {:error, :uuid | :not_found, String.t()}} |
| 40 35 | |
| 41 | - @callback delete(data_uuid()) :: |
| 42 | - {:error, :delete, :uuid, error_tag()} | |
| 43 | - {:error, :delete, :get_record_by_id, error_tag()} | |
| 44 | - {:error, :delete, :forced_to_delete, error_tag()} | |
| 45 | - {:error, :delete, error_tag(), repo_error()} | |
| 46 | - {:ok, :delete, error_tag(), repo_data()} |
| 36 | + @callback delete(Ecto.UUID.t() | non_neg_integer()) :: |
| 37 | + {:error, :delete, repo_error()} |
| 38 | + | {:error, :delete, {:error, :uuid | :not_found | :force_constraint, String.t()}} |
| 39 | + | {:ok, :delete, repo_data()} |
| 47 40 | |
| 41 | + @callback delete(Ecto.UUID.t() | non_neg_integer(), list(atom())) :: |
| 42 | + {:error, :delete, {:error, :uuid | :not_found, String.t()}} |
| 43 | + | {:error, :delete, repo_error()} |
| 44 | + | {:ok, :delete, repo_data()} |
| 48 45 | |
| 49 | - @callback show_by_id(data_uuid()) :: |
| 50 | - {:error, :get_record_by_id, error_tag()} | |
| 51 | - {:ok, :get_record_by_id, error_tag(), repo_data()} |
| 46 | + @callback show_by_id(Ecto.UUID.t() | non_neg_integer()) :: |
| 47 | + {:error, :not_found, String.t()} | struct() |
| 52 48 | |
| 49 | + @callback show_by_field(String.t(), any()) :: |
| 50 | + {:error, :not_found, String.t()} | struct() |
| 51 | + |
| 52 | + @optional_callbacks delete: 2, show_by_id: 1, show_by_field: 2 |
| 53 53 | |
| 54 54 | defmacro __using__(opts) do |
| 55 55 | quote(bind_quoted: [opts: opts]) do |
| @@ -63,56 +63,41 @@ defmodule MishkaDeveloperTools.DB.CRUD do | |
| 63 63 | |
| 64 64 | ## Example |
| 65 65 | ```elixir |
| 66 | - crud_add(map_of_info like: %{name: "trangell"}) |
| 66 | + crud_add(map_of_info like: %{"name" => "Mishka"}) |
| 67 67 | ``` |
| 68 68 | The input of this macro is a map and its output are a map. For example |
| 69 69 | |
| 70 70 | ```elixir |
| 71 | - {:ok, :add, error_atom, data} |
| 72 | - {:error, :add, error_atom, changeset} |
| 71 | + {:error, :add, repo_error()} |
| 72 | + {:ok, :add, repo_data()} |
| 73 73 | ``` |
| 74 74 | |
| 75 75 | If you want only the selected parameters to be separated from the list of submitted parameters and sent to the database, use the same macro with input 2 |
| 76 76 | |
| 77 77 | ### Example |
| 78 78 | ```elixir |
| 79 | - crud_add(map_of_info like: %{name: "trangell"}, [:name]) |
| 79 | + crud_add(map_of_info like: %{"name" => "Mishka"}, ["name"]) |
| 80 80 | ``` |
| 81 81 | """ |
| 82 82 | defmacro crud_add(attrs) do |
| 83 83 | quote do |
| 84 | - module_selected = Keyword.get(@interface_module, :module) |
| 85 | - error_atom = Keyword.get(@interface_module, :error_atom) |
| 86 | - repo = Keyword.get(@interface_module, :repo) |
| 87 | - |
| 88 | - add = module_selected.__struct__ |
| 89 | - |> module_selected.changeset(unquote(attrs)) |
| 90 | - |> repo.insert() |
| 91 | - case add do |
| 92 | - {:ok, data} -> {:ok, :add, error_atom, data} |
| 93 | - {:error, changeset} -> {:error, :add, error_atom, changeset} |
| 94 | - end |
| 84 | + initial = get_initial_macro_data(@interface_module) |
| 85 | + create_record(unquote(attrs), initial.module_selected, initial.repo) |
| 95 86 | end |
| 96 87 | end |
| 97 88 | |
| 98 | - defmacro crud_add(attrs, allowed_fields) do |
| 89 | + defmacro crud_add(attrs, allowed_fields) do |
| 99 90 | quote do |
| 100 | - module_selected = Keyword.get(@interface_module, :module) |
| 101 | - error_atom = Keyword.get(@interface_module, :error_atom) |
| 102 | - repo = Keyword.get(@interface_module, :repo) |
| 91 | + initial = get_initial_macro_data(@interface_module) |
| 103 92 | |
| 104 | - add = module_selected.__struct__ |
| 105 | - |> module_selected.changeset(Map.take(unquote(attrs), unquote(allowed_fields))) |
| 106 | - |> repo.insert() |
| 107 | - case add do |
| 108 | - {:ok, data} -> {:ok, :add, error_atom, data} |
| 109 | - {:error, changeset} -> {:error, :add, error_atom, changeset} |
| 110 | - end |
| 93 | + create_record( |
| 94 | + Map.take(unquote(attrs), unquote(allowed_fields)), |
| 95 | + initial.module_selected, |
| 96 | + initial.repo |
| 97 | + ) |
| 111 98 | end |
| 112 99 | end |
| 113 100 | |
| 114 | - |
| 115 | - |
| 116 101 | @doc """ |
| 117 102 | ### Edit a record in a database Macro |
| 118 103 | |
| @@ -120,51 +105,53 @@ defmodule MishkaDeveloperTools.DB.CRUD do | |
| 120 105 | |
| 121 106 | ## Example |
| 122 107 | ```elixir |
| 123 | - crud_edit(map_of_info like: %{id: "6d80d5f4-781b-4fa8-9796-1821804de6ba",name: "trangell"}) |
| 108 | + crud_edit(map_of_info like: %{"id" => "6d80d5f4-781b-4fa8-9796-1821804de6ba", "name" => "Mishka"}) |
| 124 109 | ``` |
| 125 110 | > Note that the sending ID must be of UUID type. |
| 126 111 | |
| 127 112 | The input of this macro is a map and its output are a map. For example |
| 128 113 | |
| 129 114 | ```elixir |
| 130 | - # If your request has been saved successfully |
| 131 | - {:ok, :edit, error_atom, info} |
| 132 | - # If your ID is not uuid type |
| 133 | - {:error, :edit, error_atom, :uuid} |
| 134 | - # If there is an error in sending the data |
| 135 | - {:error, :edit, error_atom, changeset} |
| 136 | - # If no record is found for your ID |
| 137 | - {:error, :delete, error_atom, :get_record_by_id} |
| 115 | + {:error, :edit, repo_error()} |
| 116 | + {:ok, :edit, repo_data()} |
| 117 | + {:error, :edit, {:error, :uuid | :not_found, String.t()}} |
| 138 118 | ``` |
| 139 119 | |
| 140 120 | It should be noted that if you want only the selected fields to be separated from the submitted parameters and sent to the database, use the macro with dual input. |
| 141 121 | |
| 142 122 | ## Example |
| 143 123 | ```elixir |
| 144 | - crud_edit(map_of_info like: %{id: "6d80d5f4-781b-4fa8-9796-1821804de6ba", name: "trangell"}, [:id, :name]) |
| 124 | + crud_edit(map_of_info like: %{"id" => "6d80d5f4-781b-4fa8-9796-1821804de6ba", "name" => "Mishka"}, , ["id", "name"]) |
| 145 125 | ``` |
| 146 126 | """ |
| 147 | - defmacro crud_edit(attr) do |
| 127 | + defmacro crud_edit(attrs) do |
| 148 128 | quote do |
| 149 | - module_selected = Keyword.get(@interface_module, :module) |
| 150 | - error_atom = Keyword.get(@interface_module, :error_atom) |
| 151 | - repo = Keyword.get(@interface_module, :repo) |
| 129 | + initial = get_initial_macro_data(@interface_module) |
| 130 | + converted_attrs = unquote(attrs) |
| 152 131 | |
| 153 | - MishkaDeveloperTools.DB.CRUD.edit_record(unquote(attr), module_selected, error_atom, repo) |
| 132 | + edit_record_by_fetch( |
| 133 | + {initial.id_type, converted_attrs["id"]}, |
| 134 | + unquote(attrs), |
| 135 | + initial.module_selected, |
| 136 | + initial.repo |
| 137 | + ) |
| 154 138 | end |
| 155 139 | end |
| 156 140 | |
| 157 | - defmacro crud_edit(attrs, allowed_fields) do |
| 141 | + defmacro crud_edit(attrs, allowed_fields) do |
| 158 142 | quote do |
| 159 | - module_selected = Keyword.get(@interface_module, :module) |
| 160 | - error_atom = Keyword.get(@interface_module, :error_atom) |
| 161 | - repo = Keyword.get(@interface_module, :repo) |
| 143 | + initial = get_initial_macro_data(@interface_module) |
| 144 | + converted_attrs = unquote(attrs) |
| 162 145 | |
| 163 | - MishkaDeveloperTools.DB.CRUD.edit_record(Map.take(unquote(attrs), unquote(allowed_fields)), module_selected, error_atom, repo) |
| 146 | + edit_record_by_fetch( |
| 147 | + {initial.id_type, converted_attrs["id"]}, |
| 148 | + Map.take(converted_attrs, unquote(allowed_fields)), |
| 149 | + initial.module_selected, |
| 150 | + initial.repo |
| 151 | + ) |
| 164 152 | end |
| 165 153 | end |
| 166 154 | |
| 167 | - |
| 168 155 | @doc """ |
| 169 156 | ### delete a record from the database with the help of ID Macro |
| 170 157 | |
| @@ -175,37 +162,43 @@ defmodule MishkaDeveloperTools.DB.CRUD do | |
| 175 162 | ## Example |
| 176 163 | ```elixir |
| 177 164 | crud_delete("6d80d5f4-781b-4fa8-9796-1821804de6ba") |
| 165 | + crud_delete("6d80d5f4-781b-4fa8-9796-1821804de6ba", [:comment, :post]) |
| 178 166 | ``` |
| 179 167 | Output: |
| 180 168 | You should note that this macro prevents the orphan data of the record requested to be deleted. So, use this macro when the other data is not dependent on the data with the ID sent by you. |
| 181 169 | |
| 182 | - |
| 183 | - |
| 184 170 | Outputs: |
| 185 171 | |
| 186 172 | ```elixir |
| 187 | - # This message will be returned when your data has been successfully deleted |
| 188 | - {:ok, :delete, error_atom, struct} |
| 189 | - # This error will be returned if the ID sent by you is not a UUID |
| 190 | - {:error, :delete, error_atom, :uuid} |
| 191 | - # This error is reversed when an error occurs while sending data |
| 192 | - {:error, :delete, error_atom, changeset} |
| 193 | - # This error will be reversed when there is no submitted ID in the database |
| 194 | - {:error, :delete, error_atom, :get_record_by_id} |
| 195 | - # This error is reversed when another record is associated with this record |
| 196 | - {:error, :delete, error_atom, :forced_to_delete} |
| 173 | + {:error, :delete, repo_error()} |
| 174 | + {:error, :delete, {:error, :uuid | :not_found | :force_constraint, String.t()}} |
| 175 | + {:ok, :delete, repo_data()} |
| 197 176 | ``` |
| 198 177 | """ |
| 199 178 | defmacro crud_delete(id) do |
| 200 179 | quote do |
| 201 | - module_selected = Keyword.get(@interface_module, :module) |
| 202 | - error_atom = Keyword.get(@interface_module, :error_atom) |
| 203 | - repo = Keyword.get(@interface_module, :repo) |
| 180 | + initial = get_initial_macro_data(@interface_module) |
| 204 181 | |
| 205 | - MishkaDeveloperTools.DB.CRUD.delete_record(unquote(id), module_selected, error_atom, repo) |
| 182 | + delete_record_by_force_constraint( |
| 183 | + {initial.id_type, unquote(id)}, |
| 184 | + initial.module_selected, |
| 185 | + initial.repo |
| 186 | + ) |
| 206 187 | end |
| 207 188 | end |
| 208 189 | |
| 190 | + defmacro crud_delete(id, assoc) do |
| 191 | + quote do |
| 192 | + initial = get_initial_macro_data(@interface_module) |
| 193 | + |
| 194 | + delete_record_by_no_assoc_constraint( |
| 195 | + {initial.id_type, unquote(id)}, |
| 196 | + initial.module_selected, |
| 197 | + unquote(assoc), |
| 198 | + initial.repo |
| 199 | + ) |
| 200 | + end |
| 201 | + end |
| 209 202 | |
| 210 203 | @doc """ |
| 211 204 | ### Macro Finding a record in a database with the help of ID |
| @@ -222,21 +215,17 @@ defmodule MishkaDeveloperTools.DB.CRUD do | |
| 222 215 | Outputs: |
| 223 216 | |
| 224 217 | ``` |
| 225 | - {:error, error_atom, :get_record_by_id} |
| 226 | - {:ok, error_atom, :get_record_by_id, record_info} |
| 218 | + {:error, :not_found, String.t()} | struct() |
| 227 219 | ``` |
| 228 220 | |
| 229 221 | """ |
| 230 222 | defmacro crud_get_record(id) do |
| 231 223 | quote do |
| 232 | - module_selected = Keyword.get(@interface_module, :module) |
| 233 | - error_atom = Keyword.get(@interface_module, :error_atom) |
| 234 | - repo = Keyword.get(@interface_module, :repo) |
| 235 | - MishkaDeveloperTools.DB.CRUD.get_record_by_id(unquote(id), module_selected, error_atom, repo) |
| 224 | + initial = get_initial_macro_data(@interface_module) |
| 225 | + fetch_record_by_id(unquote(id), initial.module_selected, initial.repo) |
| 236 226 | end |
| 237 227 | end |
| 238 228 | |
| 239 | - |
| 240 229 | @doc """ |
| 241 230 | ### Macro Find a record in the database with the help of the requested field |
| 242 231 | |
| @@ -251,109 +240,144 @@ defmodule MishkaDeveloperTools.DB.CRUD do | |
| 251 240 | Outputs: |
| 252 241 | |
| 253 242 | ``` |
| 254 | - {:error, error_atom, :get_record_by_field} |
| 255 | - {:ok, error_atom, :get_record_by_field, record_info} |
| 243 | + {:error, :not_found, String.t()} | struct() |
| 256 244 | ``` |
| 257 245 | |
| 258 246 | """ |
| 259 247 | defmacro crud_get_by_field(field, value) do |
| 260 248 | quote do |
| 261 | - module_selected = Keyword.get(@interface_module, :module) |
| 262 | - error_atom = Keyword.get(@interface_module, :error_atom) |
| 263 | - repo = Keyword.get(@interface_module, :repo) |
| 264 | - |
| 265 | - MishkaDeveloperTools.DB.CRUD.get_record_by_field(unquote(field), unquote(value), module_selected, error_atom, repo) |
| 249 | + initial = get_initial_macro_data(@interface_module) |
| 250 | + fetch_record_by_field(unquote(field), unquote(value), initial.module_selected, initial.repo) |
| 266 251 | end |
| 267 252 | end |
| 268 253 | |
| 254 | + ### Functions to create macro |
| 269 255 | |
| 270 | - |
| 271 | - |
| 272 | - |
| 273 | - # functions to create macro |
| 256 | + @spec create_record(record_input(), module(), module()) :: |
| 257 | + {:error, :add, repo_error()} | {:ok, :add, repo_data()} |
| 274 258 | @doc false |
| 275 | - def update(changeset, attrs, module, repo) do |
| 276 | - module.changeset(changeset, attrs) |
| 277 | - |> repo.update |
| 278 | - end |
| 279 | - |
| 280 | - |
| 281 | - @doc false |
| 282 | - def uuid(id) do |
| 283 | - case Ecto.UUID.cast(id) do |
| 284 | - {:ok, record_id} -> {:ok, :uuid, record_id} |
| 285 | - _ -> {:error, :uuid} |
| 259 | + def create_record(attrs, module, repo) do |
| 260 | + module.changeset(module.__struct__, attrs) |
| 261 | + |> repo.insert() |
| 262 | + |> case do |
| 263 | + {:ok, data} -> {:ok, :add, data} |
| 264 | + {:error, error_data} -> {:error, :add, error_data} |
| 286 265 | end |
| 287 266 | end |
| 288 267 | |
| 289 | - |
| 268 | + @spec edit_record_by_fetch( |
| 269 | + {:uuid | any(), String.t() | non_neg_integer()}, |
| 270 | + record_input(), |
| 271 | + module(), |
| 272 | + module() |
| 273 | + ) :: |
| 274 | + {:error, :edit, repo_error()} |
| 275 | + | {:ok, :edit, repo_data()} |
| 276 | + | {:error, :edit, {:error, :uuid | :not_found, String.t()}} |
| 290 277 | @doc false |
| 291 | - def get_record_by_id(id, module, error_atom, repo) do |
| 292 | - case repo.get(module, id) do |
| 293 | - nil -> {:error, :get_record_by_id, error_atom} |
| 294 | - record_info -> {:ok, :get_record_by_id, error_atom, record_info} |
| 278 | + def edit_record_by_fetch({_type, _id} = id_info, attrs, module, repo) do |
| 279 | + with {:ok, valid_id} <- record_id_check(id_info), |
| 280 | + data_received when is_struct(data_received) <- |
| 281 | + fetch_record_by_id(valid_id, module, repo), |
| 282 | + created_changeset <- module.changeset(data_received, attrs), |
| 283 | + {:edit, {:ok, data}} <- {:edit, repo.update(created_changeset)} do |
| 284 | + {:ok, :edit, data} |
| 285 | + else |
| 286 | + {:edit, {:error, error_data}} -> {:error, :edit, error_data} |
| 287 | + {:error, _action, _extra} = error_data -> {:error, :edit, error_data} |
| 288 | + end |
| 289 | + end |
| 290 | + |
| 291 | + @spec delete_record_by_force_constraint( |
| 292 | + {:uuid | any, String.t() | non_neg_integer()}, |
| 293 | + module(), |
| 294 | + module() |
| 295 | + ) :: |
| 296 | + {:error, :delete, repo_error()} |
| 297 | + | {:error, :delete, {:error, :uuid | :not_found | :force_constraint, String.t()}} |
| 298 | + | {:ok, :delete, repo_data()} |
| 299 | + @doc false |
| 300 | + def delete_record_by_force_constraint({_type, _id} = id_info, module, repo) do |
| 301 | + with {:ok, valid_id} <- record_id_check(id_info), |
| 302 | + data_received when is_struct(data_received) <- |
| 303 | + fetch_record_by_id(valid_id, module, repo), |
| 304 | + {:delete, {:ok, data}} <- {:delete, repo.delete(data_received)} do |
| 305 | + {:ok, :delete, data} |
| 306 | + else |
| 307 | + {:delete, {:error, error_data}} -> {:error, :delete, error_data} |
| 308 | + {:error, _action, _extra} = error_data -> {:error, :delete, error_data} |
| 295 309 | end |
| 296 310 | rescue |
| 297 | - _ -> {:error, :get_record_by_id, error_atom} |
| 311 | + _e -> |
| 312 | + {:error, :delete, |
| 313 | + {:error, :force_constraint, "There are one or more dependencies to delete this record."}} |
| 298 314 | end |
| 299 315 | |
| 300 | - |
| 316 | + @spec delete_record_by_no_assoc_constraint( |
| 317 | + {:uuid | any, String.t() | non_neg_integer()}, |
| 318 | + module(), |
| 319 | + list(atom()), |
| 320 | + module() |
| 321 | + ) :: |
| 322 | + {:error, :delete, {:error, :uuid | :not_found, String.t()}} |
| 323 | + | {:error, :delete, repo_error()} |
| 324 | + | {:ok, :delete, repo_data()} |
| 301 325 | @doc false |
| 302 | - def get_record_by_field(field, value, module, error_atom, repo) do |
| 303 | - case repo.get_by(module, "#{field}": value) do |
| 304 | - nil -> {:error, :get_record_by_field, error_atom} |
| 305 | - record_info -> {:ok, :get_record_by_field, error_atom, record_info} |
| 306 | - end |
| 307 | - rescue |
| 308 | - _ -> {:error, :get_record_by_field, error_atom} |
| 309 | - end |
| 310 | - |
| 311 | - |
| 312 | - @doc false |
| 313 | - def edit_record(attrs, module, error_atom, repo) do |
| 314 | - try do |
| 315 | - with {:ok, :uuid, record_id} <- uuid(if Map.has_key?(attrs, :id), do: attrs.id, else: attrs["id"]), |
| 316 | - {:ok, :get_record_by_id, error_atom, record_info} <- get_record_by_id(record_id, module, error_atom, repo), |
| 317 | - {:ok, info} <- update(record_info, attrs, module, repo) do |
| 318 | - |
| 319 | - {:ok, :edit, error_atom, info} |
| 320 | - else |
| 321 | - {:error, :uuid} -> |
| 322 | - {:error, :edit, :uuid, error_atom} |
| 323 | - |
| 324 | - {:error, changeset} -> |
| 325 | - {:error, :edit, error_atom, changeset} |
| 326 | - |
| 327 | - _ -> |
| 328 | - {:error, :edit, :get_record_by_id, error_atom} |
| 329 | - end |
| 330 | - rescue |
| 331 | - _e -> {:error, :edit, :get_record_by_id, error_atom} |
| 326 | + def delete_record_by_no_assoc_constraint({_type, _id} = id_info, module, assoc, repo) do |
| 327 | + with {:ok, valid_id} <- record_id_check(id_info), |
| 328 | + data_received when is_struct(data_received) <- |
| 329 | + fetch_record_by_id(valid_id, module, repo), |
| 330 | + created_change <- Ecto.Changeset.change(struct(module, %{id: data_received.id})), |
| 331 | + created_assoc <- |
| 332 | + Enum.reduce(assoc, created_change, fn item, acc -> |
| 333 | + Ecto.Changeset.no_assoc_constraint(acc, item) |
| 334 | + end), |
| 335 | + {:delete, {:ok, data}} <- {:delete, repo.delete(created_assoc)} do |
| 336 | + {:ok, :delete, data} |
| 337 | + else |
| 338 | + {:delete, {:error, error_data}} -> {:error, :delete, error_data} |
| 339 | + {:error, _action, _extra} = error_data -> {:error, :delete, error_data} |
| 332 340 | end |
| 333 341 | end |
| 334 342 | |
| 335 | - |
| 336 343 | @doc false |
| 337 | - def delete_record(id, module, error_atom, repo) do |
| 338 | - try do |
| 339 | - with {:ok, :uuid, record_id} <- uuid(id), |
| 340 | - {:ok, :get_record_by_id, error_atom, record_info} <- get_record_by_id(record_id, module, error_atom, repo), |
| 341 | - {:ok, struct} <- repo.delete(record_info) do |
| 342 | - |
| 343 | - {:ok, :delete, error_atom, struct} |
| 344 | - else |
| 345 | - {:error, :uuid} -> |
| 346 | - {:error, :delete, :uuid, error_atom} |
| 347 | - |
| 348 | - {:error, changeset} -> |
| 349 | - {:error, :delete, error_atom, changeset} |
| 350 | - |
| 351 | - _ -> |
| 352 | - {:error, :delete, :get_record_by_id, error_atom} |
| 353 | - end |
| 354 | - rescue |
| 355 | - _e -> {:error, :delete, :forced_to_delete, error_atom} |
| 344 | + defp record_id_check({:uuid, id}) do |
| 345 | + with :error <- Ecto.UUID.cast(id) do |
| 346 | + {:error, :uuid, "The submitted ID is not valid."} |
| 356 347 | end |
| 357 348 | end |
| 358 349 | |
| 350 | + defp record_id_check({_, id}), do: {:ok, id} |
| 351 | + |
| 352 | + @spec fetch_record_by_id(String.t() | integer(), module(), module()) :: |
| 353 | + {:error, :not_found, String.t()} | struct() |
| 354 | + @doc false |
| 355 | + def fetch_record_by_id(id, module, repo) do |
| 356 | + with nil <- repo.get(module, id) do |
| 357 | + {:error, :not_found, "There is no data for this request."} |
| 358 | + end |
| 359 | + end |
| 360 | + |
| 361 | + @spec fetch_record_by_field(String.t() | atom(), any, module(), module()) :: |
| 362 | + {:error, :not_found, String.t()} | struct() |
| 363 | + @doc false |
| 364 | + def fetch_record_by_field(field, value, module, repo) do |
| 365 | + with nil <- repo.get_by(module, "#{field}": value) do |
| 366 | + {:error, :not_found, "There is no data for this request."} |
| 367 | + end |
| 368 | + end |
| 369 | + |
| 370 | + @spec get_initial_macro_data(keyword) :: %{ |
| 371 | + id_type: atom(), |
| 372 | + module_selected: module(), |
| 373 | + repo: module() |
| 374 | + } |
| 375 | + @doc false |
| 376 | + def get_initial_macro_data(interface_module) do |
| 377 | + %{ |
| 378 | + module_selected: Keyword.get(interface_module, :module), |
| 379 | + repo: Keyword.get(interface_module, :repo), |
| 380 | + id_type: Keyword.get(interface_module, :id) |
| 381 | + } |
| 382 | + end |
| 359 383 | end |
| @@ -1,2 +1,90 @@ | |
| 1 1 | defmodule MishkaDeveloperTools do |
| 2 | + @moduledoc """ |
| 3 | + # Mishka Elixir Developer Tools |
| 4 | + |
| 5 | + Recently I have been working on [MishkaCms](https://github.com/mishka-group/mishka-cms), an open-source and free **CMS** for **Elixir** and **Phoenix**. It is very easy to use and has many APIs on both API, and HTML render. In this CMS, I should create some custom macros and modules which are helpful for Elixir developers epically Phoenix **LiveView** fans. Then I test them, and if they are usable, I put them into this project as separated tools. |
| 6 | + So when you want to start a project with Elixir, Mishka Developer Tools is an excellent opportunity to finish or start your project as well as possible. |
| 7 | + |
| 8 | + > You don't need to configure database for this library. The priority is your project database or ORM. |
| 9 | + |
| 10 | + ## Installation |
| 11 | + |
| 12 | + The package can be installed by adding `mishka_developer_tools` to your list of dependencies in `mix.exs`: |
| 13 | + |
| 14 | + ```elixir |
| 15 | + def deps do |
| 16 | + [ |
| 17 | + {:mishka_developer_tools, "~> 0.0.8"} |
| 18 | + ] |
| 19 | + end |
| 20 | + ``` |
| 21 | + |
| 22 | + The docs can be found at [https://hexdocs.pm/mishka_developer_tools](https://hexdocs.pm/mishka_developer_tools). |
| 23 | + |
| 24 | + --- |
| 25 | + --- |
| 26 | + |
| 27 | + ## Creating basic CRUD |
| 28 | + At first, you need to call the `__using__` macro of the Mishka developer tools. |
| 29 | + |
| 30 | + ```elixir |
| 31 | + use MishkaDeveloperTools.DB.CRUD, |
| 32 | + module: YOUR_SCHEMA_MODULE, |
| 33 | + repo: YOUR_REPO_MODULE, |
| 34 | + id: :uuid OR ANY_TYPE_YOU_WANT |
| 35 | + ``` |
| 36 | + |
| 37 | + And after that, you can define `@behaviour`; it is optional. |
| 38 | + ```elixir |
| 39 | + @behaviour MishkaDeveloperTools.DB.CRUD |
| 40 | + ``` |
| 41 | + |
| 42 | + Now is the time you can have your CRUD function; it should be noted you are not forced to use these macros under functions. |
| 43 | + |
| 44 | + |
| 45 | + ```elixir |
| 46 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :create, 1} |
| 47 | + def create(attrs) do |
| 48 | + crud_add(attrs) |
| 49 | + end |
| 50 | + |
| 51 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :create, 1} |
| 52 | + def create(attrs, allowed_fields) do |
| 53 | + crud_add(attrs, allowed_fields) |
| 54 | + end |
| 55 | + |
| 56 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :edit, 1} |
| 57 | + def edit(attrs) do |
| 58 | + crud_edit(attrs) |
| 59 | + end |
| 60 | + |
| 61 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :edit, 1} |
| 62 | + def edit(attrs, allowed_fields) do |
| 63 | + crud_edit(attrs, allowed_fields) |
| 64 | + end |
| 65 | + |
| 66 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :delete, 1} |
| 67 | + def delete(id) do |
| 68 | + crud_delete(id) |
| 69 | + end |
| 70 | + |
| 71 | + # It is optional |
| 72 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :delete, 2} |
| 73 | + def delete(id) do |
| 74 | + crud_delete(id) |
| 75 | + end |
| 76 | + |
| 77 | + # It is optional |
| 78 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :show_by_id, 1} |
| 79 | + def show_by_id(id) do |
| 80 | + crud_get_record(id) |
| 81 | + end |
| 82 | + |
| 83 | + # It is optional |
| 84 | + @doc delegate_to: {MishkaDeveloperTools.DB.CRUD, :show_by_field, 1} |
| 85 | + def show_by_field(field) do |
| 86 | + crud_get_by_field("field", field) |
| 87 | + end |
| 88 | + ``` |
| 89 | + """ |
| 2 90 | end |
Loading more files…