Skip to content

Commit

Permalink
Add credo, makefile, ci-check, more travis builds and update deps
Browse files Browse the repository at this point in the history
  • Loading branch information
simonprev committed Apr 29, 2019
1 parent 0736d4d commit f8ced2a
Show file tree
Hide file tree
Showing 14 changed files with 287 additions and 113 deletions.
59 changes: 59 additions & 0 deletions .credo.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
%{
configs: [
%{
name: "default",
strict: true,
files: %{
included: ["lib/", "test/"],
excluded: []
},
checks: [
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},
{Credo.Check.Design.AliasUsage, if_called_more_often_than: 2, if_nested_deeper_than: 1},
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
{Credo.Check.Design.TagTODO},
{Credo.Check.Design.TagFIXME},
{Credo.Check.Readability.AliasOrder},
{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, max_length: 200},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc, false},
{Credo.Check.Readability.ModuleNames},
{Credo.Check.Readability.ParenthesesInCondition},
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
{Credo.Check.Readability.VariableNames},
{Credo.Check.Refactor.ABCSize, max_size: 50},
{Credo.Check.Refactor.CaseTrivialMatches},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.PipeChainStart, excluded_argument_types: ~w(atom binary fn keyword)a, excluded_functions: ~w(from)},
{Credo.Check.Refactor.CyclomaticComplexity},
{Credo.Check.Refactor.MapInto, false},
{Credo.Check.Refactor.NegatedConditionsInUnless},
{Credo.Check.Refactor.NegatedConditionsWithElse},
{Credo.Check.Refactor.Nesting},
{Credo.Check.Refactor.UnlessWithElse},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.LazyLogging, false},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.UnusedEnumOperation},
{Credo.Check.Warning.UnusedKeywordOperation},
{Credo.Check.Warning.UnusedListOperation},
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation},
{Credo.Check.Warning.OperationWithConstantResult},
{CredoEnvvar.Check.Warning.EnvironmentVariablesAtCompileTime}
]
}
]
}
22 changes: 22 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
language: 'elixir'

elixir: 1.8.1
otp_release: 21.3

matrix:
include:
- otp_release: 19.3
elixir: 1.6.6
- otp_release: 20.0
elixir: 1.6.5
- otp_release: 20.2
elixir: 1.7.4

cache:
directories:
- _build
- deps

script:
- ./priv/scripts/ci-check.sh
- mix coveralls.travis
54 changes: 54 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Introspection targets
# ---------------------

.PHONY: help
help: targets

.PHONY: targets
targets:
@echo "\033[34mTargets\033[0m"
@echo "\033[34m---------------------------------------------------------------\033[0m"
@perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}'

# Build targets
# -------------

.PHONY: dependencies
dependencies: dependencies-mix ## Install dependencies required by the application

.PHONY: dependencies-mix
dependencies-mix:
mix deps.get --force

# CI targets
# ----------

.PHONY: lint
lint: lint-compile lint-format lint-credo ## Run lint tools on the code

.PHONY: lint-compile
lint-compile:
mix compile --warnings-as-errors --force

.PHONY: lint-format
lint-format:
mix format --dry-run --check-formatted

.PHONY: lint-credo
lint-credo:
mix credo --strict

.PHONY: test
test: ## Run the test suite
mix test

.PHONY: test-coverage
test-coverage: ## Generate the code coverage report
mix coveralls

.PHONY: format
format: format-elixir ## Run formatting tools on the code

.PHONY: format-elixir
format-elixir:
mix format
46 changes: 46 additions & 0 deletions ci-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env sh

error_status=0

RED='\033[0;31m'
RED_BOLD='\033[1;31m'
GREEN='\033[0;32m'
GREEN_BOLD='\033[1;32m'
YELLOW='\033[0;33m'
NO_COLOR='\033[0m'

header() {
echo "\n\n${YELLOW}$1${NO_COLOR}"
}

run() {
eval "${@}"
last_exit_status=${?}

if [ ${last_exit_status} -ne 0 ]; then
echo "\n${RED}↳ Something went wrong. Program exited with ${last_exit_status}${NO_COLOR}"
error_status=${last_exit_status}
else
echo "${GREEN}↳ Passed ✔${NO_COLOR}"
fi
}

header "App tests…"
run mix test

header "Compilation without warnings…"
run make lint-compile

header "App code auto-formatting…"
run make lint-format

header "App code lint…"
run make lint-credo

if [ ${error_status} -ne 0 ]; then
echo "\n\n${YELLOW}▶▶ One of the checks ${RED_BOLD}failed${YELLOW}. Please fix it before committing.${NO_COLOR}"
else
echo "\n\n${YELLOW}▶▶ All checks ${GREEN_BOLD}passed${YELLOW}!${NO_COLOR}"
fi

exit $error_status
2 changes: 1 addition & 1 deletion coveralls.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"treat_no_relevant_lines_as_covered": true
},
"skip_files": [
"test/support",
"test/support"
]
}
106 changes: 46 additions & 60 deletions lib/kronky/changeset_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@ defmodule Kronky.ChangesetParser do
import Ecto.Changeset, only: [traverse_errors: 2]
alias Kronky.ValidationMessage

@doc "Extract a nested map of raw errors from a changeset
For examples, please see the test cases in the github repo.
"
def messages_as_map(changeset) do
Ecto.Changeset.traverse_errors(changeset, & &1)
end

@doc "Generate a list of `Kronky.ValidationMessage` structs from changeset errors
For examples, please see the test cases in the github repo.
Expand All @@ -33,7 +25,7 @@ defmodule Kronky.ChangesetParser do
|> Enum.reject(&match?(%Ecto.Changeset{action: :replace}, &1))
end

defp reject_replaced_changes(changeset = %{changes: changes}) do
defp reject_replaced_changes(%{changes: changes} = changeset) do
Enum.reduce(changes, changeset, fn {key, value}, acc ->
%{acc | changes: %{key => reject_replaced_changes(value)}}
end)
Expand Down Expand Up @@ -119,13 +111,7 @@ defmodule Kronky.ChangesetParser do
# Code Taken from the Pheonix DataCase.on_errors/1 boilerplate"
def interpolate_message({message, opts}) do
Enum.reduce(opts, message, fn {key, value}, acc ->
key_pattern = "%{#{key}}"

if String.contains?(acc, key_pattern) do
String.replace(acc, key_pattern, to_string(value))
else
acc
end
String.replace(acc, "%{#{key}}", to_string(value))
end)
end

Expand All @@ -138,49 +124,49 @@ defmodule Kronky.ChangesetParser do
## Supported
- :cast - generated by `Ecto.Changeset.cast/3`
- :association - generated by `Ecto.Changeset.assoc_constraint/3`, `Ecto.Changeset.cast_assoc/3`, `Ecto.Changeset.put_assoc/3`, `Ecto.Changeset.cast_embed/3`, `Ecto.Changeset.put_embed/3`
- :acceptance - generated by `Ecto.Changeset.validate_acceptance/3`
- :confirmation - generated by `Ecto.Changeset.validate_confirmation/3`
- :length - generated by `Ecto.Changeset.validate_length/3` when the `:is` option fails validation
- :min - generated by `Ecto.Changeset.validate_length/3` when the `:min` option fails validation
- :max - generated by `Ecto.Changeset.validate_length/3` when the `:max` option fails validation
- :less_than_or_equal_to - generated by `Ecto.Changeset.validate_length/3` when the `:less_than_or_equal_to` option fails validation
- :less_than - generated by `Ecto.Changeset.validate_length/3` when the `:less_than` option fails validation
- :greater_than_or_equal_to - generated by `Ecto.Changeset.validate_length/3` when the `:greater_than_or_equal_to` option fails validation
- :greater_than - generated by `Ecto.Changeset.validate_length/3` when the `:greater_than` option fails validation
- :equal_to - generated by `Ecto.Changeset.validate_length/3` when the `:equal_to` option fails validation
- :exclusion - generated by `Ecto.Changeset.validate_exclusion/4`
- :inclusion - generated by `Ecto.Changeset.validate_inclusion/4`
- :required - generated by `Ecto.Changeset.validate_required/3`
- :subset - generated by `Ecto.Changeset.validate_subset/4`
- :unique - generated by `Ecto.Changeset.unique_constraint/3`
- :foreign - generated by `Ecto.Changeset.foreign_key_constraint/3`
- :no_assoc_constraint - generated by `Ecto.Changeset.no_assoc_constraint/3`
- :unknown - supplied when validation cannot be matched. This will also match any custom errors added through
- `:cast` - generated by `Ecto.Changeset.cast/3`
- `:association` - generated by `Ecto.Changeset.assoc_constraint/3`, `Ecto.Changeset.cast_assoc/3`, `Ecto.Changeset.put_assoc/3`, `Ecto.Changeset.cast_embed/3`, `Ecto.Changeset.put_embed/3`
- `:acceptance` - generated by `Ecto.Changeset.validate_acceptance/3`
- `:confirmation` - generated by `Ecto.Changeset.validate_confirmation/3`
- `:length` - generated by `Ecto.Changeset.validate_length/3` when the `:is` option fails validation
- `:min` - generated by `Ecto.Changeset.validate_length/3` when the `:min` option fails validation
- `:max` - generated by `Ecto.Changeset.validate_length/3` when the `:max` option fails validation
- `:less_than_or_equal_to` - generated by `Ecto.Changeset.validate_length/3` when the `:less_than_or_equal_to` option fails validation
- `:less_than` - generated by `Ecto.Changeset.validate_length/3` when the `:less_than` option fails validation
- `:greater_than_or_equal_to` - generated by `Ecto.Changeset.validate_length/3` when the `:greater_than_or_equal_to` option fails validation
- `:greater_than` - generated by `Ecto.Changeset.validate_length/3` when the `:greater_than` option fails validation
- `:equal_to` - generated by `Ecto.Changeset.validate_length/3` when the `:equal_to` option fails validation
- `:exclusion` - generated by `Ecto.Changeset.validate_exclusion/4`
- `:inclusion` - generated by `Ecto.Changeset.validate_inclusion/4`
- `:required` - generated by `Ecto.Changeset.validate_required/3`
- `:subset` - generated by `Ecto.Changeset.validate_subset/4`
- `:unique` - generated by `Ecto.Changeset.unique_constraint/3`
- `:foreign` - generated by `Ecto.Changeset.foreign_key_constraint/3`
- `:no_assoc_constraint` - generated by `Ecto.Changeset.no_assoc_constraint/3`
- `:unknown` - supplied when validation cannot be matched. This will also match any custom errors added through
`Ecto.Changeset.add_error/4`, `Ecto.Changeset.validate_change/3`, and `Ecto.Changeset.validate_change/4`
"""
def to_code({message, opts}) do
opts
def to_code({message, validation_options}) do
validation_options
|> Enum.into(%{message: message})
|> do_to_code
end

defp do_to_code(%{code: code}), do: code
defp do_to_code(%{validation: :cast}), do: :cast
defp do_to_code(%{validation: :required}), do: :required
defp do_to_code(%{validation: :format}), do: :format
defp do_to_code(%{validation: :inclusion}), do: :inclusion
defp do_to_code(%{validation: :exclusion}), do: :exclusion
defp do_to_code(%{validation: :subset}), do: :subset
defp do_to_code(%{validation: :acceptance}), do: :acceptance
defp do_to_code(%{validation: :confirmation}), do: :confirmation
defp do_to_code(%{validation: :length, is: _}), do: :length
defp do_to_code(%{validation: :length, min: _}), do: :min
defp do_to_code(%{validation: :length, max: _}), do: :max

defp do_to_code(%{validation: :number, message: message}) do
|> validation_options_to_code()
end

defp validation_options_to_code(%{code: code}), do: code
defp validation_options_to_code(%{validation: :cast}), do: :cast
defp validation_options_to_code(%{validation: :required}), do: :required
defp validation_options_to_code(%{validation: :format}), do: :format
defp validation_options_to_code(%{validation: :inclusion}), do: :inclusion
defp validation_options_to_code(%{validation: :exclusion}), do: :exclusion
defp validation_options_to_code(%{validation: :subset}), do: :subset
defp validation_options_to_code(%{validation: :acceptance}), do: :acceptance
defp validation_options_to_code(%{validation: :confirmation}), do: :confirmation
defp validation_options_to_code(%{validation: :length, is: _}), do: :length
defp validation_options_to_code(%{validation: :length, min: _}), do: :min
defp validation_options_to_code(%{validation: :length, max: _}), do: :max

defp validation_options_to_code(%{validation: :number, message: message}) do
cond do
String.contains?(message, "less than or equal to") -> :less_than_or_equal_to
String.contains?(message, "greater than or equal to") -> :greater_than_or_equal_to
Expand All @@ -191,13 +177,13 @@ defmodule Kronky.ChangesetParser do
end
end

defp do_to_code(%{message: "is invalid", type: _}), do: :association
defp validation_options_to_code(%{message: "is invalid", type: _}), do: :association

defp do_to_code(%{message: "has already been taken"}), do: :unique
defp do_to_code(%{message: "does not exist"}), do: :foreign
defp do_to_code(%{message: "is still associated with this entry"}), do: :no_assoc
defp validation_options_to_code(%{message: "has already been taken"}), do: :unique
defp validation_options_to_code(%{message: "does not exist"}), do: :foreign
defp validation_options_to_code(%{message: "is still associated with this entry"}), do: :no_assoc

defp do_to_code(_unknown) do
defp validation_options_to_code(_unknown) do
:unknown
end
end
Loading

0 comments on commit f8ced2a

Please sign in to comment.