diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..28c0829 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,59 @@ +name: Build and test + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + name: Build and test (Elixir ${{matrix.versions.elixir}}) + strategy: + matrix: + versions: [{os: 'ubuntu-20.04', otp: '23.3.4.9', elixir: '1.13.4-otp-23'}, {os: 'ubuntu-22.04', otp: '25.3.2.9', elixir: '1.15.7-otp-25'}] + runs-on: ${{matrix.versions.os}} + + steps: + - uses: actions/checkout@v4 + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + elixir-version: ${{matrix.versions.elixir}} + otp-version: ${{matrix.versions.otp}} + + # Define how to cache deps. Restores existing cache if present. + - name: Cache deps + id: cache-deps + uses: actions/cache@v3 + env: + cache-name: cache-elixir-deps + with: + path: deps + key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }} + restore-keys: ${{ runner.os }}-mix-${{env.cache-name}} + + # Define how to cache the `_build` directory. After the first run, + # this speeds up tests runs a lot. This includes not re-compiling our + # project's downloaded deps every run. + - name: Cache compiled build + id: cache-build + uses: actions/cache@v3 + env: + cache-name: cache-compiled-build + with: + path: _build + key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }} + restore-keys: | + ${{ runner.os }}-mix-${{ env.cache-name }}- + ${{ runner.os }}-mix- + + - name: Install dependencies + run: mix deps.get + + - name: Run tests + run: mix test diff --git a/README.md b/README.md index e49a996..1ab9316 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Calibex +# Calibex [![Build Status](https://github.com/kbrw/calibex/actions/workflows/.github/workflows/build-and-test.yml/badge.svg)](https://github.com/kbrw/calibex/actions/workflows/build-and-test.yml) [![Hex.pm](https://img.shields.io/hexpm/v/calibex.svg)](https://hex.pm/packages/calibex) [![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/calibex) ![Hex.pm License](https://img.shields.io/hexpm/l/calibex) Please read the doc at : [https://hexdocs.pm/calibex](https://hexdocs.pm/calibex) @@ -9,41 +9,63 @@ Simple algorithm for ICal encoding : *every ICal fields handled*. ## ICal Elixir bijective format -The ICal elixir term is exactly a representation of the ICal file format : for instance : +The ICal elixir term is an exact representation of the ICal file format. + +For instance : ```elixir -[vcalendar: [[ - prodid: "-//Google Inc//Google Calendar 70.9054//EN", - version: "2.0", - calscale: "GREGORIAN", - vevent: [[ - dtstart: %DateTime{}, - dtend: %DateTime{}, - organizer: [cn: "My Name",value: "mailto:me@example.com"], - attendee: [cutype: "INDIVIDUAL",role: "REQ-PARTICIPANT",partstat: "NEEDS-ACTION",rsvp: true, cn: "Moi", - "x-num-guests": 0, value: "mailto:me@example.com"], - ]]]]] +[ + vcalendar: [ + [ + prodid: "-//Google Inc//Google Calendar 70.9054//EN", + version: "2.0", + calscale: "GREGORIAN", + vevent: [ + [ + dtstart: %DateTime{}, + dtend: %DateTime{}, + organizer: [cn: "My Name", value: "mailto:me@example.com"], + attendee: [ + cutype: "INDIVIDUAL", + role: "REQ-PARTICIPANT", + partstat: "NEEDS-ACTION", + rsvp: true, + cn: "Moi", + "x-num-guests": 0, + value: "mailto:me@example.com" + ] + ] + ] + ] + ] +] ``` -`Calibex.encode/1` and `Calibex.decode/1` parse and format an ICal from this -terms : see functions doc to find encoding rules. +[`Calibex.encode/1`](https://hexdocs.pm/calibex/Calibex.html#encode/1) +and [`Calibex.decode/1`](https://hexdocs.pm/calibex/Calibex.html#decode/1) +parse and format an ICal from these terms : see functions doc to find encoding +rules. Using this terms make it possible to handle all types of ICal files and any fields type. But the downside of this approach is that it can be cumbersome to create and handle this tree of keyword lists. To help you in this tasks, -some helpers functions are provided : +some helpers functions are provided : -- `Calibex.new/1` -- `Calibex.new_root/1` -- `Calibex.request/1` +- [`Calibex.new/1`](https://hexdocs.pm/calibex/Calibex.html#decode/1) +- [`Calibex.new_root/1`](https://hexdocs.pm/calibex/Calibex.html#new_root/1) +- [`Calibex.request/1`](https://hexdocs.pm/calibex/Calibex.html#request/1) -## Example usage : email event request generation +## Example usage : email event request generation ```elixir -Calibex.request(dtstart: Timex.now, dtend: Timex.shift(Timex.now,hours: 1), summary: "Mon évènement", - organizer: "arnaud.wetzel@example.com", attendee: "jeanpierre@yahoo.fr", attendee: "jean@ya.fr") - |> Calibex.encode +Calibex.request( + dtstart: Timex.now(), + dtend: Timex.shift(Timex.now(), hours: 1), + summary: "Mon évènement", + organizer: "arnaud.wetzel@example.com", + attendee: "jeanpierre@yahoo.fr", + attendee: "jean@ya.fr" +) +|> Calibex.encode() ``` - - diff --git a/config/config.exs b/config/config.exs index 012704b..9f1e134 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,6 +1,6 @@ # This file is responsible for configuring your application # and its dependencies with the aid of the Mix.Config module. -use Mix.Config +import Config # This configuration is loaded before any dependency and is restricted # to this project. If another project depends on this project, this diff --git a/lib/helpers.ex b/lib/helpers.ex index 7543738..860e9c3 100644 --- a/lib/helpers.ex +++ b/lib/helpers.ex @@ -7,13 +7,13 @@ defmodule Calibex.Helper do @request_fill @new_fill ++ [:uid,:last_modified,:sequence,:dtstamp,:created,:status] def new(event, fill_attrs \\ @new_fill), do: fill_rec([vcalendar: [[vevent: [event]]]],fill_attrs) - def request(event, fill_attrs \\ @request_fill), do: + def request(event, fill_attrs \\ @request_fill), do: fill_rec([vcalendar: [[method: "REQUEST", vevent: [event]]]],fill_attrs) def new_root(cal, fill_attrs \\ @new_fill), do: fill_rec([vcalendar: [cal]],fill_attrs) def all_fill_attrs, do: @request_fill - + ## fill_rec recursively augment fields and add default fields if key matches `augment/3`, `default/2` def fill_rec(el,fill_attrs), do: fill_rec(el,Enum.group_by(fill_attrs,&parent/1),nil) def fill_rec([{_,_}|_]=props,fill_by_parent,parent) do @@ -24,8 +24,14 @@ defmodule Calibex.Helper do {k,fill_rec(v,fill_by_parent,k)} end) case fill_by_parent[parent] do - nil->props - tofill->Enum.filter_map(tofill,&(!props[&1]),&{&1,default(&1,props)}) ++ props + nil -> + props + + tofill -> + tofill + |> Enum.filter(&(!props[&1])) + |> Enum.map(&{&1, default(&1, props)}) + |> Enum.concat(props) end end def fill_rec(l,fill_by_parent,parent) when is_list(l), do: Enum.map(l,&fill_rec(&1,fill_by_parent,parent))