Current section

19 Versions

Jump to

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…