Skip to content

Commit ebb0f89

Browse files
committed
Fix Dialyzer issues and add missing docs
1 parent 25175db commit ebb0f89

File tree

6 files changed

+106
-45
lines changed

6 files changed

+106
-45
lines changed

lib/upload.ex

+50-22
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ defmodule Upload do
33
An opinionated file uploader.
44
"""
55

6+
@type variant_id :: String.t() | atom()
7+
68
alias Upload.Blob
79
alias Upload.Stat
810

@@ -37,7 +39,16 @@ defmodule Upload do
3739
end
3840
end
3941

40-
@spec variant_exists?(Blob.t(), String.t() | atom()) :: boolean()
42+
@doc """
43+
Checks if a variant exists for a given `Upload.Blob` and the variant identifier.
44+
45+
## Example
46+
47+
iex> Upload.variant_exists?(person.avatar, :small)
48+
iex> true
49+
50+
"""
51+
@spec variant_exists?(Blob.t(), variant_id()) :: boolean()
4152
def variant_exists?(%Blob{id: blob_id}, variant) do
4253
repo = Upload.Config.repo()
4354

@@ -46,7 +57,17 @@ defmodule Upload do
4657
|> repo.exists?()
4758
end
4859

49-
@spec get_variant(Blob.t(), String.t() | atom()) :: nil | Blob.t()
60+
@doc """
61+
Returns the variant for a given `Upload.Blob` and the variant identifier or `nil` if it
62+
does not exist.
63+
64+
## Example
65+
66+
iex> Upload.get_variant(person.avatar, :small)
67+
%Blob{}
68+
69+
"""
70+
@spec get_variant(Blob.t(), variant_id()) :: nil | Blob.t()
5071
def get_variant(%Blob{id: blob_id}, variant) do
5172
repo = Upload.Config.repo()
5273

@@ -64,13 +85,13 @@ defmodule Upload do
6485
6586
## Example
6687
67-
```elixir
68-
create_variant(original_blob, "small", &transform_fn/3)
69-
```
88+
iex> create_variant(original_blob, :small, &transform_fn/3)
89+
{:ok, %Blob{}}
7090
"""
7191
@spec create_variant(Blob.t(), String.t(), any()) :: {:ok, Blob.t()} | {:error, any()}
7292
def create_variant(original_blob, variant, transform_fn) when is_function(transform_fn, 3) do
7393
repo = Upload.Config.repo()
94+
variant = to_string(variant)
7495

7596
{:ok, multi_result} =
7697
Ecto.Multi.new()
@@ -87,13 +108,14 @@ defmodule Upload do
87108
88109
## Example
89110
90-
```elixir
91-
create_multiple_variants(blob, ["small", "large"], &transform_fn/3)
92-
```
111+
iex> create_multiple_variants(blob, [:small, :large], &transform_fn/3)
112+
{:ok, [%Blob{...}, %Blob{...}]}
93113
"""
94-
@spec create_multiple_variants(Blob.t(), [String.t()], any()) :: any()
114+
@spec create_multiple_variants(Blob.t(), [variant_id()], any()) ::
115+
{:ok, [Blob.t()]} | {:error, any()}
95116
def create_multiple_variants(original_blob, variants, transform_fn)
96117
when is_function(transform_fn, 3) do
118+
variants = Enum.map(variants, &to_string/1)
97119
repo = Upload.Config.repo()
98120
multi = Ecto.Multi.new()
99121

@@ -116,26 +138,32 @@ defmodule Upload do
116138
@doc """
117139
Set the visiblity of a `Blob` using the struct or it's key.
118140
119-
This only applies when Upload is configured to use S3.
141+
> #### Note {: .warning}
142+
>
143+
> This only applies when Upload is configured to use S3.
120144
121-
Supported access control lists for Amazon S3 are:
145+
Supported canned access control lists for Amazon S3 are:
122146
123147
| ACL | Permissions Added to ACL |
124148
|------------------------------|---------------------------------------------------------------------------------|
125-
| "private" | Owner gets `FULL_CONTROL`. No one else has access rights (default). |
126-
| "public_read" | Owner gets `FULL_CONTROL`. The `AllUsers` group gets READ access. |
127-
| "public_read_write" | Owner gets `FULL_CONTROL`. The `AllUsers` group gets `READ` and `WRITE` access. |
128-
| | Granting this on a bucket is generally not recommended. |
129-
| "authenticated_read" | Owner gets `FULL_CONTROL`. The `AuthenticatedUsers` group gets `READ` access. |
130-
| "bucket_owner_read" | Object owner gets `FULL_CONTROL`. Bucket owner gets `READ` access. |
131-
| "bucket_owner_full_control" | Both the object owner and the bucket owner get `FULL_CONTROL` over the object. |
149+
| private | Owner gets `FULL_CONTROL`. No one else has access rights (default). |
150+
| public_read | Owner gets `FULL_CONTROL`. The `AllUsers` group gets READ access. |
151+
| public_read_write | Owner gets `FULL_CONTROL`. The `AllUsers` group gets `READ` and `WRITE` access. Granting this on a bucket is generally not recommended. |
152+
| authenticated_read | Owner gets `FULL_CONTROL`. The `AuthenticatedUsers` group gets `READ` access. |
153+
| bucket_owner_read | Object owner gets `FULL_CONTROL`. Bucket owner gets `READ` access. |
154+
| bucket_owner_full_control | Both the object owner and the bucket owner get `FULL_CONTROL` over the object. |
155+
156+
## Example
157+
158+
iex> Upload.put_access_control_list(person.avatar, :public_read)
159+
:ok
132160
"""
133161
@spec put_access_control_list(Blob.t() | Blob.key(), String.t()) :: :ok | {:error, term()}
134-
def put_access_control_list(%Blob{key: key}, acl) do
135-
put_access_control_list(key, acl)
162+
def put_access_control_list(%Blob{key: key} = _blob, canned_acl) do
163+
put_access_control_list(key, canned_acl)
136164
end
137165

138-
def put_access_control_list(key, acl) do
139-
Upload.Storage.put_access_control_list(key, acl)
166+
def put_access_control_list(key, canned_acl) do
167+
Upload.Storage.put_access_control_list(key, [{:acl, canned_acl}])
140168
end
141169
end

lib/upload/blob.ex

+2-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ defmodule Upload.Blob do
7777
mime = get_field(changeset, :content_type)
7878
extension = MIME.extensions(mime) |> List.first()
7979

80-
if mime && extension do
80+
if extension do
8181
update_change(changeset, :key, fn key ->
8282
key <> "." <> extension
8383
end)
@@ -88,6 +88,7 @@ defmodule Upload.Blob do
8888

8989
defp add_extension_from_mime(changeset), do: changeset
9090

91+
@doc false
9192
def change_blob(%Stat{} = stat, key) do
9293
changeset(
9394
%__MODULE__{},

lib/upload/changeset.ex

+44-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
defmodule Upload.Changeset do
22
@moduledoc """
3-
Functions to use with changesets to upload an attachment on a schema.
3+
Functions for use with changesets to upload and validate attachments.
44
55
```elixir
6+
schema "people" do
7+
belongs_to(:avatar, Upload.Blob, on_replace: :delete, type: :binary_id)
8+
end
9+
610
%Person{}
711
|> Person.changeset(%{avatar: upload})
812
|> cast_attachment(:avatar, required: true)
@@ -30,26 +34,38 @@ defmodule Upload.Changeset do
3034
terabyte: 1.0e12
3135
}
3236

33-
@spec put_attachment(changeset(), field(), Plug.Upload.t(), String.t()) :: changeset()
34-
defp put_attachment(changeset, field, %Plug.Upload{} = upload, key) do
37+
@spec put_attachment(changeset(), field(), Plug.Upload.t() | Upload.Stat.t(), String.t()) ::
38+
changeset()
39+
def put_attachment(changeset, field, %Plug.Upload{} = upload, key) do
3540
put_attachment(changeset, field, Upload.stat!(upload), key)
3641
end
3742

38-
@spec put_attachment(changeset(), field(), Upload.Stat.t(), String.t()) :: changeset()
39-
defp put_attachment(changeset, field, %Upload.Stat{} = stat, key) do
43+
def put_attachment(changeset, field, %Upload.Stat{} = stat, key) do
4044
put_assoc(changeset, field, Upload.Blob.change_blob(stat, key))
4145
end
4246

43-
@spec put_attachment(changeset(), field(), Ecto.Changeset.t(), String.t()) :: changeset()
44-
defp put_attachment(changeset, field, %Ecto.Changeset{} = blob_changeset, key) do
45-
put_assoc(changeset, field, blob_changeset |> put_change(:key, key))
46-
end
47-
4847
@spec put_attachment(changeset(), field(), Upload.Blob.t()) :: changeset()
4948
def put_attachment(changeset, field, %Upload.Blob{} = blob) do
5049
put_assoc(changeset, field, blob)
5150
end
5251

52+
@doc """
53+
Adds an attachment to the changeset changes.
54+
55+
## Options
56+
57+
* `:required` - Require the attachment.
58+
* `:key_function` - A 1-arity function that is given the changeset and is
59+
expected to return the path of the attachment in external storage without
60+
the file type extension.
61+
62+
## Example
63+
64+
iex> Upload.Changeset.cast_attachment(changeset, :avatar)
65+
66+
iex> Upload.Changeset.cast_attachment(changeset, :avatar, required: true, key_function: &key_function/1)
67+
68+
"""
5369
@spec cast_attachment(changeset(), field(), cast_opts()) :: changeset()
5470
def cast_attachment(changeset, field, opts \\ []) do
5571
key =
@@ -96,7 +112,16 @@ defmodule Upload.Changeset do
96112
end)
97113
end
98114

99-
@spec validate_attachment_type(changeset, field, type_opts) :: changeset
115+
@doc """
116+
Require that the attachment is of a specific MIME type. This is determined by
117+
looking at the file's contents and can be trusted.
118+
119+
## Example
120+
121+
iex> validate_attachment_type(changeset, :avatar, allow: ["image/jpeg", "image/png"])
122+
123+
"""
124+
@spec validate_attachment_type(changeset(), field(), type_opts()) :: changeset()
100125
def validate_attachment_type(changeset, field, opts) do
101126
{message, opts} = Keyword.pop(opts, :message, "is not a supported file type")
102127

@@ -109,6 +134,14 @@ defmodule Upload.Changeset do
109134
end)
110135
end
111136

137+
@doc """
138+
Require that the attachment size is in a specific range.
139+
140+
## Example
141+
142+
iex> validate_attachment_size(changeset, :avatar, smaller_than: {1, :megabyte})
143+
144+
"""
112145
@spec validate_attachment_size(changeset, field, size_opts) :: changeset
113146
def validate_attachment_size(changeset, field, opts) do
114147
size = {number, unit} = Keyword.fetch!(opts, :smaller_than)

lib/upload/stat.ex

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defmodule Upload.Stat do
2-
@moduledoc false
2+
@moduledoc "Metadata of a given file"
33
defstruct [:path, :filename, :byte_size, :checksum, :metadata, :content_type]
44

55
@chunk_size 2_048

mix.exs

+2-3
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ defmodule Upload.Mixfile do
5050
{:cloak, "~> 1.1.4"},
5151
{:ecto_sql, "~> 3.8"},
5252
{:ecto, "~> 3.6"},
53-
{:file_store,
54-
git: "https://github.com/joydrive/file_store.git", branch: "jd-add-acl-support"},
53+
{:file_store, git: "https://github.com/joydrive/file_store.git", sha: "2ce496f"},
5554
{:file_type, "~> 0.1"},
5655
{:image, "~> 0.48"},
5756
{:plug, "~> 1.13"},
@@ -61,7 +60,7 @@ defmodule Upload.Mixfile do
6160
# Test dependencies for this package
6261
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
6362
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
64-
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
63+
{:ex_doc, "~> 0.34", only: :dev, runtime: false},
6564
{:excoveralls, "~> 0.18", only: :test},
6665
{:mix_test_watch, "~> 1.0", only: :dev, runtime: false}
6766
]

0 commit comments

Comments
 (0)