Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added :default_formatter option parameter and Membrane.Time.pretty_du… #134

Merged
merged 6 commits into from
Jan 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 23 additions & 34 deletions lib/membrane/element/base/mixin/common_behaviour.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ defmodule Membrane.Element.Base.Mixin.CommonBehaviour do

For more information on implementing elements, see `Membrane.Element.Base`.
"""
alias Membrane.{Action, Core, Element, Event}
alias Membrane.{Action, Core, Element, Event, Time}
alias Core.CallbackHandler
alias Element.{Action, CallbackContext, Pad}

use Bunch

@typedoc """
Type that defines all valid return values from most callbacks.
"""
Expand Down Expand Up @@ -148,31 +150,14 @@ defmodule Membrane.Element.Base.Mixin.CommonBehaviour do
"""
@callback handle_shutdown(state :: Element.state_t()) :: :ok

@default_quoted_specs %{
atom:
quote do
atom()
end,
boolean:
quote do
boolean()
end,
string:
quote do
String.t()
end,
keyword:
quote do
keyword()
end,
struct:
quote do
struct()
end,
caps:
quote do
struct()
end
@default_types_params %{
atom: [spec: quote_expr(atom)],
boolean: [spec: quote_expr(boolean)],
string: [spec: quote_expr(String.t())],
keyword: [spec: quote_expr(keyword)],
struct: [spec: quote_expr(struct)],
caps: [spec: quote_expr(struct)],
time: [spec: quote_expr(Time.t()), inspector: &Time.to_code_str/1]
}

@doc """
Expand All @@ -185,10 +170,12 @@ defmodule Membrane.Element.Base.Mixin.CommonBehaviour do

* `type:` atom, used for parsing
* `spec:` typespec for value in struct. If ommitted, for types:
`#{inspect(Map.keys(@default_quoted_specs))}` the default typespec is provided.
`#{inspect(Map.keys(@default_types_params))}` the default typespec is provided.
For others typespec is set to `t:any/0`
* `default:` default value for option. If not present, value for this option
will have to be provided each time options struct is created
* `inspector:` function converting fields' value to a string. Used when
creating documentation instead of `inspect/1`
* `description:` string describing an option. It will be present in value returned by `options/0`
and in typedoc for the struct.
"""
Expand All @@ -203,8 +190,15 @@ defmodule Membrane.Element.Base.Mixin.CommonBehaviour do

default_val_desc =
if Keyword.has_key?(v, :default) do
inspector =
v
|> Keyword.get(
:inspector,
@default_types_params[v[:type]][:inspector] || quote(do: &inspect/1)
)

quote do
"Defaults to `#{inspect(unquote(v)[:default])}`"
"Defaults to `#{unquote(inspector).(unquote(v)[:default])}`"
end
else
""
Expand Down Expand Up @@ -263,12 +257,7 @@ defmodule Membrane.Element.Base.Mixin.CommonBehaviour do
with_default_specs =
kw
|> Enum.map(fn {k, v} ->
quoted_any =
quote do
any()
end

default_val = @default_quoted_specs |> Map.get(v[:type], quoted_any)
default_val = @default_types_params[v[:type]][:spec] || quote_expr(any)

{k, v |> Keyword.put_new(:spec, default_val)}
end)
Expand Down
83 changes: 83 additions & 0 deletions lib/membrane/time.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ defmodule Membrane.Time do
@type non_neg_t :: non_neg_integer
@type native_t :: integer

@units_abbreviations %{
days: "d",
hours: "h",
minutes: "min",
seconds: "s",
milliseconds: "ms",
microseconds: "us",
nanoseconds: "ns"
}

@doc """
Checks whether value is Membrane.Time.t
"""
Expand All @@ -60,6 +70,60 @@ defmodule Membrane.Time do
"""
defguard is_native_t(value) when is_integer(value)

@doc """
Returns duration as a string with unit. Chosen unit is the biggest possible
that doesn't involve precission loss.

## Examples

iex> import #{inspect(__MODULE__)}
iex> 10 |> milliseconds() |> pretty_duration()
"10 ms"
iex> 60_000_000 |> microseconds() |> pretty_duration()
"1 min"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test for days like this to at least ensure the whole array is traversed:

      iex> 6
      ...> |> #{inspect(__MODULE__)}.days()
      ...> |> #{inspect(__MODULE__)}.pretty_duration()
      "6 d"

iex> 2 |> nanoseconds() |> pretty_duration()
"2 ns"

"""
@spec pretty_duration(t) :: String.t()
def pretty_duration(time) when is_t(time) do
{time, unit} = time |> best_unit()

"#{time} #{@units_abbreviations[unit]}"
end

@doc """
Returns quoted code producing given amount time. Chosen unit is the biggest possible
that doesn't involve precission loss.

## Examples

iex> import #{inspect(__MODULE__)}
iex> 10 |> milliseconds() |> to_code() |> Macro.to_string()
quote do 10 |> Membrane.Time.milliseconds() end |> Macro.to_string()
iex> 60_000_000 |> microseconds() |> to_code() |> Macro.to_string()
quote do 1 |> Membrane.Time.minutes() end |> Macro.to_string()
iex> 2 |> nanoseconds() |> to_code() |> Macro.to_string()
quote do 2 |> #{inspect(__MODULE__)}.nanoseconds() end |> Macro.to_string()

"""
@spec to_code(t) :: Macro.t()
def to_code(time) when is_t(time) do
{time, unit} = time |> best_unit()

quote do
unquote(time) |> unquote(__MODULE__).unquote(unit)()
end
end

@doc """
Returns string representation of result of `to_code/1`.
"""
@spec pretty_duration(t) :: Macro.t()
def to_code_str(time) when is_t(time) do
time |> to_code() |> Macro.to_string()
end

@doc """
Returns current time in pretty format (currently iso8601), as string
Uses system_time/0 under the hood.
Expand Down Expand Up @@ -372,4 +436,23 @@ defmodule Membrane.Time do
def to_days(value) when is_t(value) do
(value / (1 |> day)) |> round
end

@spec units() :: Keyword.t(t)
defp units() do
[
days: 1 |> day(),
hours: 1 |> hour(),
minutes: 1 |> minute(),
seconds: 1 |> second(),
milliseconds: 1 |> millisecond(),
microseconds: 1 |> microsecond(),
nanoseconds: 1 |> nanosecond()
]
end

defp best_unit(time) do
{unit, divisor} = units() |> Enum.find(fn {_unit, divisor} -> time |> rem(divisor) == 0 end)

{time |> div(divisor), unit}
end
end
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ defmodule Membrane.Mixfile do
{:espec, "~> 1.6", only: :test},
{:excoveralls, "~> 0.8", only: :test},
{:qex, "~> 0.3"},
{:bunch, "~> 0.1.2"}
# {:bunch, "~> 0.1.2"}
{:bunch, github: "membraneframework/bunch"}
]
end
end
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
%{
"bunch": {:hex, :bunch, "0.1.2", "2389a4bcdb62382fbfeed9e19952cc22452dc0c01b4bcac941cce00ef212d7b4", [:mix], [], "hexpm"},
"bunch": {:git, "https://github.com/membraneframework/bunch.git", "ad163a027e7dce7fedd09313cdb78edca2188604", []},
"certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
"earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"},
"espec": {:hex, :espec, "1.6.3", "d9355788e508b82743a1b1b9aa5ac64ba37b0547c6210328d909e8a6eb56d42e", [:mix], [{:meck, "0.8.12", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
Expand Down
7 changes: 7 additions & 0 deletions test/membrane/time_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule Membrane.TimeTest do
use ExUnit.Case, async: true

@module Membrane.Time

doctest @module
end