README.md

# Rsim

Rsim - is image manager that allows to upload and fetch images easily. It is integrated with ecto and AWS S3.

```elixir

# will upload image to s3, save to DB and provide image_id
{:ok, image_id} = Rsim.save_image_from_file(path, :user)
# will upload image to s3 from provided URL, save to DB and provide image_id
{:ok, image_id} = Rsim.save_image_from_url(url, :user) 
 
 # returns image src
{:ok, image_src} = Rsim.get_image_url(image_id)
# resize image on the fly, saves new resized image and returns image src
{:ok, image_src} = Rsim.get_image_url(image_id, 200, 150)
```


## Installation

Add `rsim` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:rsim, "~> 0.1.0"}
  ]
end

# add configuration 
# currently only AWS S3 storage is suppoted so make sure
# you have config for :ex_aws as well  

config :ex_aws,
  access_key_id: "",
  secret_access_key: "",
  region: ""

config :rsim,
  repo: MyApp.Repo,
  s3: [bucket: "bucket-name"]


```

Copy and run migration to add `images` table from `priv/repo/migrations/`. Add `image_id` to your application
tables that will have images. 

```elixir
  def change do
    alter table(:users) do
      add(:image_id, references(:images, on_delete: :nothing, type: :binary_id))
    end

    create(index(:users, [:image_id]))
  end
  
  
defmodule MyApp.Accounts.User do
  use Ecto.Schema
  schema "users" do
    # other fields
    belongs_to(:image, Rsim.EctoImage, type: :binary_id)
  end
end    
```


## Real world example

```elixir

defmodule MyApp.Accounts.Registrator do

  def register_user(user_params) do
    user_params = if !Map.has_key?(user_params, :image_id) && Map.has_key?(user_params, :image_url) do
      Map.put(user_params, :image_id, save_image_from_url!(user_params.image_url))

    sign_up_changeset(%User{}, user_params)
      |> Repo.insert()   
  end  

  defp save_image_from_url!(url) when is_nil(url), do: nil
  defp save_image_from_url!(url) do
    # case Rsim.save_image_from_file(uploaded_file.path, :user) do 
    case Rsim.save_image_from_url(url, :user) do
      {:ok, image} -> image.id
      {:error, error} -> nil
    end
  end
end


defmodule MyApp.Accounts.Avatar do
  alias MyApp.Accounts.User
  
  def fetch_avatar_url(user = %User{}) do
    fetch_image_url(user.image_id)
  end

  defp fetch_image_url(image_id) when is_nil(image_id), do: nil
  defp fetch_image_url(image_id) do
    # case Rsim.get_image_url(image_id) do          # get original image URL    
    case Rsim.get_image_url(image_id, 150, 150) do  #  resize image on the fly and get image_url
      {:ok, image_url} -> image_url
      {:error, _} -> nil
    end
  end
end
```  


## How It Works

```
__________
|  images |
|_________|
|    id   |    - unique image id
|   path  |    - path in storage 
|   mime  |    - image mime type
|   size  |    - image file size
|   width |    - image width
|  height |    - image height
|parent_id|    - reference for parent image (appears on resized images)
|_________|
     |
     |___________    
__________      |
|  users  |     |
|_________|     |
|    id   |     |
|   etc   |     |
| image_id| ----|
|         |
|_________|
```

When you upload new image via `Rsim.save_image_from_file` or `Rsim.save_image_from_url` it will be uploaded to storage.
(Currently only AWS s3 is supported). Then image info will be saved to `images` table, including path in storage and 
some metadata like image width/height, mime, file size. `images.id` will be returned - it's responsibility of your app 
to save it to application tables that have images. 

When you fetch image URL - storage will transform path to valid image source. You also can get resized image on the fly. 
In that case new resized image will be saves with reference `images.parent_id` to original image.