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

Refactor Github.Client #10

Merged
merged 2 commits into from May 18, 2018
Merged

Conversation

tallysmartins
Copy link
Member

@tallysmartins tallysmartins commented Apr 18, 2018

The GitHub.Client was very coupled, so I made a refactor based on this post from Jose Valim that explains how to properly use mocks and define contracts.

@coveralls
Copy link

coveralls commented Apr 18, 2018

Pull Request Test Coverage Report for Build 22

  • 1 of 7 (14.29%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+11.6%) to 30.994%

Changes Missing Coverage Covered Lines Changed/Added Lines %
lib/elixir_bench/github/client_http.ex 0 6 0.0%
Totals Coverage Status
Change from base Build 17: 11.6%
Covered Lines: 53
Relevant Lines: 171

💛 - Coveralls

@tallysmartins tallysmartins changed the title Refactor Github.Client WIP: Refactor Github.Client Apr 18, 2018
@tallysmartins tallysmartins changed the title WIP: Refactor Github.Client Refactor Github.Client Apr 18, 2018
Copy link
Member

@PragTob PragTob left a comment

Choose a reason for hiding this comment

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

Looks good just a couple of smaller comments :)

def get_plain(%Client{} = client, url, params \\ []) do
get(client, url(client, url, params), [{"accept", "text/plain"}], &(&1))
end
@callback get_plain(path :: term) :: {:ok, config :: term} | {:error, reason :: term}
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 think path is term - isn't it a String?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, I will change it.


@behaviour ElixirBench.Github.Client

@params []
Copy link
Member

Choose a reason for hiding this comment

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

not sure why we'd need this?

end

defp url(path) do
Path.join(@base_url, path) <> "?" <> URI.encode_query(@params)
Copy link
Member

Choose a reason for hiding this comment

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

we seem to have totally given up on the ability to pass parameters along?

It seems like we didn't need it before so we could drop it imo until we need it again, but then we can also drop this whole part and just leave it at Path.Join.

I have a feeling they might crop up/be needed again eventually but we could wait until that happens

end
end

defp get(url, headers, cb) do
Copy link
Member

Choose a reason for hiding this comment

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

this is picky me, but I prefer callback over cb :)


@params []
@base_url "https://raw.githubusercontent.com"
@ssl_options []
Copy link
Member

Choose a reason for hiding this comment

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

same I don't think we need this, could be inlined

end

def get_yaml(_) do
{:ok, %{}}
Copy link
Member

Choose a reason for hiding this comment

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

I think we should return at least some sensible data here :)

config/test.exs Outdated
@@ -17,3 +17,6 @@ config :elixir_bench, ElixirBench.Repo,
database: "elixir_bench_test",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox

# Github client for getting repository configuration file bench/config.yml
Copy link
Member

Choose a reason for hiding this comment

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

Not sure we need this comment in every file, I feel like this might be better as module documentation

Copy link
Member Author

Choose a reason for hiding this comment

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

A module docs should definitely should be added, but I also like the comments in every settings described in the environment configuration files. Can we do both?

defp url(%{base_url: base}, url, params) do
Path.join(base, url) <> "?" <> URI.encode_query(params)
end
@callback get_yaml(path :: term) :: {:ok, config :: term} | {:error, reason :: term}
Copy link
Member

Choose a reason for hiding this comment

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

if I see this correctly currently only get_yaml is used so we could even drop the other methods.

Am I missing something? @michalmuskala

Signed-off-by: Tallys Martins <tallysmartins@gmail.com>
Copy link
Member

@PragTob PragTob left a comment

Choose a reason for hiding this comment

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

Comments mostly about what we can omit in the documentation - otherwise smaller nitpicks. Looking good though :)

end
- a `get_yaml/1` function that takes the url path of the project and returns
a map with the configuration found in the benchee.yml that is stored
inside the `benchee directory`.
Copy link
Member

Choose a reason for hiding this comment

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

I feel like this just duplicates information from the function definition/spec further down. If you want to add more information to the function itself, I believe you can add @doc aboove the callback definition

Copy link
Member Author

Choose a reason for hiding this comment

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

I removed this.

defp url(%{base_url: base}, url, params) do
Path.join(base, url) <> "?" <> URI.encode_query(params)
end
@callback get_yaml(path :: String) :: {:ok, config :: Map} | {:error, reason :: term}
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 believe these typespecs actually work but we don't really know whithout dialyzer setup :) Needs to be String.t and Map.t imo

Copy link
Member Author

Choose a reason for hiding this comment

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

I will read more about how dialyzer and typespecs works. 👨‍🎓


This client uses HTTP requests to gather data from yml files in given
repository hosted on Github. It uses the `:hackney` library to actually send
the requests and the `yamerl` library to decode the data in the yml format.
Copy link
Member

Choose a reason for hiding this comment

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

mentioning libraries used under the hood is too application specific imo - we can do without that. Those might change or not - whoever uses the module doesn't need to know about them though :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Clear, just left a brief description about the module.

This module implements the following functions:

- a `get_yaml/1` function that takes the url path of the project and return
the raw parsed data in the yaml format.
Copy link
Member

Choose a reason for hiding this comment

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

The information which methods are implemented isn't needed - mix docs generates it and otherwise you can see it looking at the module itself.

Copy link
Member Author

Choose a reason for hiding this comment

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

Alright, removed this 👍


defp url(path) do
base_url = 'https://raw.githubusercontent.com'
Path.join(base_url, path)
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 think we need the intermediary variable - we can just embed it in the method call or if we really wanted make it a module attribute

Copy link
Member Author

Choose a reason for hiding this comment

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

Inlined!


- a `get_yaml/1` function that takes the url path of the project and return
a map with mocked data about the raw configuration found in the
benchee.yml file.
Copy link
Member

Choose a reason for hiding this comment

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

same thing as above, we don't need that information mix docs automatically generates it and in the file we see it either way :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed!


def get_yaml(_) do
{:ok,
%{
Copy link
Member

Choose a reason for hiding this comment

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

I believe the indentation is slightly wrong here, if you wanted you could run mix format (in Elixir >= 1.6) on just this file to fix it :)

Copy link
Member Author

Choose a reason for hiding this comment

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

sorry I've missed that

alias ElixirBench.{Benchmarks, Benchmarks.Job}
alias ElixirBench.Repos

describe "when correct params" do
Copy link
Member

Choose a reason for hiding this comment

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

usually (well in my usage and iirc elixir core as well) describe isn't used for contexts or cases but rather methods so like `describe ".create_job/2" ... test "returns a new job given correct parametres"

Copy link
Member Author

Choose a reason for hiding this comment

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

...I will keep that in mind 👍

{:ok, repo} = Repos.create_repo(%{owner: "elixir-ecto", name: "ecto"})
job_attrs = %{branch_name: "mm/benche", commit_sha: "ABC123"}

jobs_count = ElixirBench.Repo.all(Job) |> length
Copy link
Member

Choose a reason for hiding this comment

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

you can do Repo.aggregate(Job, :count, :id) iirc to just get the count directly from the db :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed! Later on I think I will write an assert_difference/3 function to care about the insertion/deletion assertions. Something like assert_difference(queryable, function, value)

jobs_count = ElixirBench.Repo.all(Job) |> length
assert {:ok, %Job{}} = Benchmarks.create_job(repo, job_attrs)

new_count = ElixirBench.Repo.all(Job) |> length
Copy link
Member

Choose a reason for hiding this comment

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

see above :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed!

Signed-off-by: Tallys Martins <tallysmartins@gmail.com>
Copy link
Member

@PragTob PragTob left a comment

Choose a reason for hiding this comment

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

💃

img_20180317_110254

@PragTob PragTob merged commit ff3858f into elixir-bench:master May 18, 2018
@tallysmartins tallysmartins deleted the code_refactoring branch May 18, 2018 15:32
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.

None yet

3 participants