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

Handle invalid options #8

Merged
merged 11 commits into from
Jan 20, 2020
Merged

Handle invalid options #8

merged 11 commits into from
Jan 20, 2020

Conversation

tteerawat
Copy link
Contributor

@tteerawat tteerawat commented Jan 14, 2020

What i did:

  • Extract parsing functions from Split module to a new module called Parser.
  • Handle invalid options and inputs.
  • Refactor/edit Router and Split modules.

Related issue: #7


defp apply_multiplier(orders, multiplier) do
Enum.map(orders, fn {name, amount} ->
new_amount = Float.round(amount * multiplier, 15)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

15 is the maximum number of fractional digits for Float.round/2.

@@ -3,32 +3,46 @@ defmodule Chopperbot.Split do

TODO:
[ ] add support for LINE format
[ ] add flexible discount ex. -10%
[ ] [Bug] String.split wrong on copy & paste the command in Slack
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should we remove this line too 😆 ?

Copy link
Member

Choose a reason for hiding this comment

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

yuppppp

Copy link
Member

@turboza turboza left a comment

Choose a reason for hiding this comment

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

@teerawat1992 nice job. this is quite neat refactoring!

I felt like transform_to_orders is still a little hard to read. Let me come back to think again tomorrow!

** (FunctionClauseError) no function clause matching in anonymous fn/1 in Chopperbot.Split.InputTransformer.transform!/1
"""
@spec transform!([String.t()]) :: [Order.t()]
def transform!(inputs) do
Copy link
Member

Choose a reason for hiding this comment

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

Just curious, why InputTransformer.transform! while we use OptionTransformer.transform (no bang)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think you mentioned on the outdated code 😆

Copy link
Member

Choose a reason for hiding this comment

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

Oh right! 😂


defp accumulate_multipliers(multipliers) do
multipliers
|> Enum.reduce(1.0, &(&1 * &2))
Copy link
Member

Choose a reason for hiding this comment

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

nice way to write reduce! 👍

end

defp transform_to_multipliers(multipliers, invalid_options, []) do
{multipliers, Enum.reverse(invalid_options)}
Copy link
Member

Choose a reason for hiding this comment

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

Oh, your reverse in order to display the same input order? quite neat.

build_response(input)

_ ->
build_line_suggestion_response()
Copy link
Member

Choose a reason for hiding this comment

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

nice for returning help here.

@@ -3,32 +3,46 @@ defmodule Chopperbot.Split do

TODO:
[ ] add support for LINE format
[ ] add flexible discount ex. -10%
[ ] [Bug] String.split wrong on copy & paste the command in Slack
Copy link
Member

Choose a reason for hiding this comment

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

yuppppp

{:error, :invalid_input, ["one-hundred", "ten", "baht"]}
"""
@spec parse(String.t()) ::
{:ok, parsed()}
Copy link
Member

Choose a reason for hiding this comment

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

I am thinking about parsed not sure if abstracting the type into parsed worth sacrificing the type reading, since parsed is not reused that much too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's used only once tho lol, can be replaced with just %{orders: [Order.t()], multiplier: float()} 🙇

"2️⃣",
"split alice 100 bob 200 +v",
"3️⃣",
"split alice 100 bob 200 share 100"
Copy link
Member

Choose a reason for hiding this comment

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

Oh, out of the scope, but maybe we can add the discount use case as well. (maybe later)

|> String.trim()
|> String.downcase()
|> case do
"split " <> input ->
Copy link
Member

Choose a reason for hiding this comment

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

I like this way of matching 👍

@tteerawat
Copy link
Contributor Author

I felt like transform_to_orders is still a little hard to read. Let me come back to think again tomorrow!

🙇

@tteerawat
Copy link
Contributor Author

tteerawat commented Jan 16, 2020

I felt like transform_to_orders is still a little hard to read. Let me come back to think again tomorrow!

Another candidate for transform_to_orders function (looks more complex IMO 😅 ),
Looking forward to yours 😉

  defp transform_to_orders(input_pairs, orders \\ [], invalid_inputs \\ [])

  defp transform_to_orders([input_pair | rest_input_pairs], orders, invalid_inputs) do
    with {:ok, name, amount} <- validate_input_pair(input_pair),
         {:ok, float_amount} <- validate_amount_string(amount) do
      order = {String.downcase(name), float_amount}

      transform_to_orders(
        rest_input_pairs,
        [order | orders],
        invalid_inputs
      )
    else
      {:error, invalid_input} ->
        transform_to_orders(
          rest_input_pairs,
          orders,
          [invalid_input | invalid_inputs]
        )
    end
  end

  defp transform_to_orders([], orders, invalid_inputs) do
    {Enum.reverse(orders), Enum.reverse(invalid_inputs)}
  end

  defp validate_input_pair(input_pair) do
    case input_pair do
      [name, amount] -> {:ok, name, amount}
      [invalid_input] -> {:error, invalid_input}
    end
  end

  defp validate_amount_string(string) do
    case Float.parse(string) do
      {float_number, ""} -> {:ok, float_number}
      _ -> {:error, string}
    end
  end

@turboza
Copy link
Member

turboza commented Jan 17, 2020

Ohhh. thank you for the alternative. I couldn't think of the solution better than these 2 yet 😂

I think the new one you provide looks cleaner and easier to understand. Although the previous one is actually smarter and shorter. So I would vote with the new one.

Copy link
Member

@turboza turboza left a comment

Choose a reason for hiding this comment

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

@teerawat1992 Awesome job! thanks for the contribution. 👍 💯
This is a great improvement. Feel free to ping me to merge when you are ready.

(I am not sure how to add you as a collaborator without paying more to Github 😅 )

iex> transform(["ant", "200", "pipe", "100", "share", "-30"])
{:ok, [{"ant", 200.0}, {"pipe", 100.0}, {"share", -30.0}]}

iex> transform(["Satoshi", "10.9", "Takeshi", "390.13", "satoshi", "112.50",])
Copy link
Member

Choose a reason for hiding this comment

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

excessive comma , at the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! 🙇


defp transform_to_orders([[no_pair] | rest_input_pairs], orders, invalid_inputs) do
transform_to_orders(rest_input_pairs, orders, [no_pair | invalid_inputs])
end
Copy link
Member

Choose a reason for hiding this comment

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

I don't see the case for having rest_input_pairs here.
How about this?

  defp transform_to_orders([[no_pair]], orders, invalid_inputs) do
    transform_to_orders([], orders, [no_pair | invalid_inputs])
  end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will disappear in the new version 🙇

@tteerawat
Copy link
Contributor Author

@turboza Refactored as suggested 🙇

@turboza turboza merged commit cbeb8bf into flipay:master Jan 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants