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

Add Credo for linting #7

Merged
merged 1 commit into from
Jul 18, 2016
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
2 changes: 2 additions & 0 deletions apps/example/web/controllers/admin/post_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ defmodule Example.Admin.PostController do
plug :put_layout, {Example.LayoutView, "admin.html"}
plug :scrub_params, "post" when action in [:create, :update]
plug :assign_categories

plug :assign_authors

@filtrex [
%Config{type: :boolean, keys: ~w(draft)},
%Config{type: :date, keys: ~w(inserted_at updated_at), options: %{format: "{YYYY}-{0M}-{0D}"}},
%Config{type: :text, keys: ~w(title body)},
%Config{type: :number, keys: ~w(author_id)},
%Config{type: :number, keys: ~w(category_id)}
]

Expand Down
2 changes: 2 additions & 0 deletions apps/example/web/templates/admin/post/index.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<thead>
<tr>
<th><%= table_link(@conn, "Title", :title) %></th>
<th><%= table_link(@conn, "Body", :body) %></th>
<th><%= table_link(@conn, "Draft", :draft) %></th>
<th><%= table_link(@conn, "Category", :category_id) %></th>
<th><%= table_link(@conn, "Author", :author_id) %></th>
Expand All @@ -23,6 +24,7 @@
<%= for post <- @posts do %>
<tr>
<td><%= post.title %></td>
<td><%= post.body %></td>
<td><%= post.draft %></td>
<td><%= table_assoc_display_name(post, :category_id, @categories) %>
<td><%= table_assoc_display_name(post, :author_id, @authors) %>
Expand Down
89 changes: 60 additions & 29 deletions apps/torch/lib/mix/tasks/torch.gen.ex
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ defmodule Mix.Tasks.Torch.Gen do

## Example

mix torch.gen eex Admin Post posts category_id:references:category,categories:id,name title:string body:text inserted_at:date
mix torch.gen eex Admin Post posts category_id:references:category,categories:id,name title:string body:text
"""

use Mix.Task
Expand All @@ -100,20 +100,32 @@ defmodule Mix.Tasks.Torch.Gen do
assoc_plugs: assoc_plugs(attrs),
assoc_plug_definitions: assoc_plug_definitions(binding, attrs)]

binding
|> copy_templates(format, path)
|> copy_elixir(path)
|> print_message(format)
end

defp copy_templates(binding, format, path) do
Mix.Torch.copy_from [:torch], "priv/templates/#{format}", "", binding, [
{:eex, "index.#{format}.eex", "web/templates/#{path}/index.html.#{format}"},
{:eex, "edit.#{format}.eex", "web/templates/#{path}/edit.html.#{format}"},
{:eex, "new.#{format}.eex", "web/templates/#{path}/new.html.#{format}"},
{:eex, "_form.#{format}.eex", "web/templates/#{path}/_form.html.#{format}"},
{:eex, "_filters.#{format}.eex", "web/templates/#{path}/_filters.html.#{format}"}
]
binding
end

defp copy_elixir(binding, path) do
Mix.Torch.copy_from [:torch], "priv/templates/elixir", "", binding, [
{:eex, "controller.ex", "web/controllers/#{path}_controller.ex"},
{:eex, "view.ex", "web/views/#{path}_view.ex"}
]
binding
end

defp print_message(binding, format) do
Mix.shell.info """
Success!

Expand All @@ -137,7 +149,7 @@ defmodule Mix.Tasks.Torch.Gen do
<nav>
<h1>Torch Admin</h1>
<ul>
#{IO.ANSI.green}<li><%= Torch.NavigationView.nav_link @conn, "#{String.capitalize(binding[:plural])}", #{ binding[:namespace_underscore]}_#{binding[:singular]}_path(@conn, :index) %></li>#{IO.ANSI.reset}
#{IO.ANSI.green}<li><%= #{nav_link(binding)} %></li>#{IO.ANSI.reset}
</ul>
</nav>
</header>
Expand All @@ -150,10 +162,15 @@ defmodule Mix.Tasks.Torch.Gen do
nav
h1 Torch Admin
ul
#{IO.ANSI.green}li= Torch.NavigationView.nav_link @conn, "#{String.capitalize(binding[:plural])}", #{ binding[:namespace_underscore]}_#{binding[:singular]}_path(@conn, :index)#{IO.ANSI.reset}
#{IO.ANSI.green}li= #{nav_link(binding)}#{IO.ANSI.reset}
"""
end

@lint false
defp nav_link(binding) do
~s{Torch.NavigationView.nav_link @conn, "#{String.capitalize(binding[:plural])}", #{binding[:namespace_underscore]}_#{binding[:singular]}_path(@conn, :index)}
end

defp inputs("eex", attrs) do
inputs = inputs("slim", attrs)
for {label, input, error} <- inputs do
Expand All @@ -162,35 +179,49 @@ defmodule Mix.Tasks.Torch.Gen do
end

defp inputs("slim", attrs) do
attrs = Enum.reject(attrs, fn({key, _type}) -> key in [:inserted_at, :updated_at] end)
Enum.map attrs, fn
{_, {:array, _}} ->
{nil, nil, nil}
{key, {:references, data}} ->
{label(data[:assoc_singular]), ~s(= select f, #{inspect(key)}, @#{data[:assoc_plural]}, prompt: "Choose one"), error(key)}
{key, :file} ->
{label(key), ~s(= file_input f, #{inspect(key)}), error(key)}
{key, :integer} ->
{label(key), ~s(= number_input f, #{inspect(key)}), error(key)}
{key, :float} ->
{label(key), ~s(= number_input f, #{inspect(key)}, step: "any"), error(key)}
{key, :decimal} ->
{label(key), ~s(= number_input f, #{inspect(key)}, step: "any"), error(key)}
{key, :boolean} ->
{label(key), ~s(= select f, #{inspect(key)}, [{"True", true}, {"False", false}], prompt: "Choose one"), error(key)}
{key, :text} ->
{label(key), ~s(= textarea f, #{inspect(key)}), error(key)}
{key, :date} ->
{label(key), ~s(= date_select f, #{inspect(key)}), error(key)}
{key, :time} ->
{label(key), ~s(= time_select f, #{inspect(key)}), error(key)}
{key, :datetime} ->
{label(key), ~s(= datetime_select f, #{inspect(key)}), error(key)}
{key, _} ->
{label(key), ~s(= text_input f, #{inspect(key)}), error(key)}
for {key, _} = attr <- attrs, not key in [:inserted_at, :updated_at] do
do_input(attr)
end
end

@lint false
defp do_input({_, {:array, _}}) do
{nil, nil, nil}
end
defp do_input({key, {:references, data}}) do
{label(data[:assoc_singular]), ~s(= select f, #{inspect(key)}, @#{data[:assoc_plural]}, prompt: "Choose one"), error(key)}
end
defp do_input({key, :file}) do
{label(key), ~s(= file_input f, #{inspect(key)}), error(key)}
end
defp do_input({key, :integer}) do
{label(key), ~s(= number_input f, #{inspect(key)}), error(key)}
end
defp do_input({key, :float}) do
{label(key), ~s(= number_input f, #{inspect(key)}, step: "any"), error(key)}
end
defp do_input({key, :decimal}) do
{label(key), ~s(= number_input f, #{inspect(key)}, step: "any"), error(key)}
end
defp do_input({key, :boolean}) do
{label(key), ~s(= select f, #{inspect(key)}, [{"True", true}, {"False", false}], prompt: "Choose one"), error(key)}
end
defp do_input({key, :text}) do
{label(key), ~s(= textarea f, #{inspect(key)}), error(key)}
end
defp do_input({key, :date}) do
{label(key), ~s(= date_select f, #{inspect(key)}), error(key)}
end
defp do_input({key, :time}) do
{label(key), ~s(= time_select f, #{inspect(key)}), error(key)}
end
defp do_input({key, :datetime}) do
{label(key), ~s(= datetime_select f, #{inspect(key)}), error(key)}
end
defp do_input({key, _}) do
{label(key), ~s(= text_input f, #{inspect(key)}), error(key)}
end

defp assoc_plugs(attrs) do
for {_key, {:references, data}} <- attrs do
"plug :assign_#{data[:assoc_plural]}"
Expand Down
33 changes: 25 additions & 8 deletions apps/torch/lib/mix/torch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,22 @@ defmodule Mix.Torch do
path: "admin/super_user"]
"""
def inflect(namespace, singular) do
base = Mix.Torch.base
scoped = Phoenix.Naming.camelize(singular)
path = Phoenix.Naming.underscore(scoped)
singular = String.split(path, "/") |> List.last
module = base |> Module.concat(namespace) |> Module.concat(scoped) |> inspect
alias = String.split(module, ".") |> List.last
human = Phoenix.Naming.humanize(singular)
scoped = Phoenix.Naming.camelize(singular)
path = Phoenix.Naming.underscore(scoped)
singular =
path
|> String.split("/")
|> List.last
module =
base
|> Module.concat(namespace)
|> Module.concat(scoped)
|> inspect
alias =
module
|> String.split(".")
|> List.last
human = Phoenix.Naming.humanize(singular)

[alias: alias,
human: human,
Expand Down Expand Up @@ -106,7 +115,14 @@ defmodule Mix.Torch do
def uniques(attrs) do
attrs
|> Enum.filter(&String.ends_with?(&1, ":unique"))
|> Enum.map(& &1 |> String.split(":", parts: 2) |> hd |> String.to_atom)
|> Enum.map(&do_unique/1)
end

defp do_unique(attr) do
attr
|> String.split(":", parts: 2)
|> hd
|> String.to_atom
end

@doc """
Expand Down Expand Up @@ -193,6 +209,7 @@ defmodule Mix.Torch do
{String.to_atom(key), {String.to_atom(comp), String.to_atom(value)}}
end

@lint false
defp type_to_default(t) do
case t do
{:array, _} -> []
Expand Down
8 changes: 6 additions & 2 deletions apps/torch/lib/torch/views/filter_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,12 @@ defmodule Torch.FilterView do
def filter_date_input(prefix, field, params) do
prefix = to_string(prefix)
field = to_string(field)
{:safe, start} = date_input("#{prefix}[#{field}_between][start]", get_in(params, [prefix, "#{field}_between", "start"]))
{:safe, ending} = date_input("#{prefix}[#{field}_between][end]", get_in(params, [prefix, "#{field}_between", "end"]))
{:safe, start} =
date_input("#{prefix}[#{field}_between][start]",
get_in(params, [prefix, "#{field}_between", "start"]))
{:safe, ending} =
date_input("#{prefix}[#{field}_between][end]",
get_in(params, [prefix, "#{field}_between", "end"]))
raw(start <> ending)
end

Expand Down
1 change: 1 addition & 0 deletions apps/torch/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ defmodule Torch.Mixfile do
{:phoenix_html, "~> 2.6"},
{:ecto, ">= 1.0.0"},
{:scrivener_ecto, ">= 1.0.0"},
{:credo, "~> 0.4", only: [:dev, :test]},
{:ex_doc, "~> 0.13", only: :dev}]
end

Expand Down
1 change: 1 addition & 0 deletions bin/setup
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ cd apps/example && {
}

mix test
mix credo --strict
108 changes: 108 additions & 0 deletions config/.credo.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# This file contains the configuration for Credo and you are probably reading
# this after creating it with `mix credo.gen.config`.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
#
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
#
# Run any config using `mix credo -C <name>`. If no config name is given
# "default" is used.
name: "default",
#
# these are the files included in the analysis
files: %{
#
# you can give explicit globs or simply directories
# in the latter case `**/*.{ex,exs}` will be used
included: ["apps/torch/lib", "apps/torch/web"],
excluded: [~r"/_build/", ~r"/deps/"]
},
#
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
requires: [],
#
# Credo automatically checks for updates, like e.g. Hex does.
# You can disable this behaviour below:
check_for_updates: true,
#
# You can customize the parameters of any check by adding a second element
# to the tuple.
#
# To disable a check put `false` as second element:
#
# {Credo.Check.Design.DuplicatedCode, false}
#
checks: [
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},

# For some checks, like AliasUsage, you can only customize the priority
# Priority values are: `low, normal, high, higher`
{Credo.Check.Design.AliasUsage, false},

# For others you can set parameters

# If you don't want the `setup` and `test` macro calls in ExUnit tests
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},

# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
{Credo.Check.Design.TagTODO, exit_status: 2},
{Credo.Check.Design.TagFIXME},

{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 120},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{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: 40},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.PipeChainStart},
{Credo.Check.Refactor.CyclomaticComplexity},
{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.NameRedeclarationByAssignment},
{Credo.Check.Warning.NameRedeclarationByCase},
{Credo.Check.Warning.NameRedeclarationByDef},
{Credo.Check.Warning.NameRedeclarationByFn},
{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},

# Custom checks can be created using `mix credo.gen.check`.
#
]
}
]
}
4 changes: 3 additions & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
%{"certifi": {:hex, :certifi, "0.4.0", "a7966efb868b179023618d29a407548f70c52466bf1849b9e8ebd0e34b7ea11f", [:rebar3], []},
%{"bunt": {:hex, :bunt, "0.1.6", "5d95a6882f73f3b9969fdfd1953798046664e6f77ec4e486e6fafc7caad97c6f", [:mix], []},
"certifi": {:hex, :certifi, "0.4.0", "a7966efb868b179023618d29a407548f70c52466bf1849b9e8ebd0e34b7ea11f", [:rebar3], []},
"combine": {:hex, :combine, "0.9.1", "5fd778ee77032ae593bf79aedb8519d9e36283e4f869abd98c2d6029ca476db8", [:mix], []},
"connection": {:hex, :connection, "1.0.3", "3145f7416be3df248a4935f24e3221dc467c1e3a158d62015b35bd54da365786", [:mix], []},
"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
"credo": {:hex, :credo, "0.4.6", "30febde77446dfff48055f9ac3d8bfc9d9250e787c1ed75deab7073841ab7ad1", [:mix], [{:bunt, "~> 0.1.6", [hex: :bunt, optional: false]}]},
"db_connection": {:hex, :db_connection, "1.0.0-rc.3", "d9ceb670fe300271140af46d357b669983cd16bc0d01206d7d3222dde56cf038", [:mix], [{:sbroker, "~> 1.0.0-beta.3", [hex: :sbroker, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:connection, "~> 1.0.2", [hex: :connection, optional: false]}]},
"decimal": {:hex, :decimal, "1.1.2", "79a769d4657b2d537b51ef3c02d29ab7141d2b486b516c109642d453ee08e00c", [:mix], []},
"earmark": {:hex, :earmark, "1.0.1", "2c2cd903bfdc3de3f189bd9a8d4569a075b88a8981ded9a0d95672f6e2b63141", [:mix], []},
Expand Down