├── .gitignore ├── LICENSE.md ├── README.md ├── lib ├── mix │ └── tasks │ │ └── gen.ex └── mix_task_gen │ ├── assigns.ex │ └── options.ex ├── mix.exs ├── test ├── mix_generator_test.exs ├── options_test.exs └── test_helper.exs └── test_templates ├── child ├── README.md ├── lib │ └── child.ex ├── mix.exs └── template │ └── $PROJECT_NAME$ │ └── lib │ └── child.ex └── project ├── LICENSE.md ├── README.md ├── doc ├── .build ├── 404.html ├── Mix.Gen.Template.Project.html ├── api-reference.html ├── dist │ ├── app-372682077e.js │ ├── app-f7824c48e6.css │ └── sidebar_items-481e944cee.js ├── fonts │ ├── icomoon.eot │ ├── icomoon.svg │ ├── icomoon.ttf │ └── icomoon.woff └── index.html ├── lib └── project.ex ├── mix.exs └── template └── $PROJECT_NAME$ ├── .gitignore ├── README.md ├── config └── config.exs ├── lib ├── $PROJECT_NAME$.ex └── $PROJECT_NAME$ │ └── application.ex ├── mix.exs └── test ├── $PROJECT_NAME$_test.exs └── test_helper.exs /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------- 2 | 3 | __Copyright © 2017 [Dave Thomas](mailto:dave@pragdave.me)__ 4 | 5 | ------------------------------------------------------- 6 | 7 | Licensed under the Apache License, Version 2.0 (_the License_); 8 | you may not use these files except in compliance with the License. 9 | A copy of the License is [online](http://www.apache.org/licenses/LICENSE-2.0). 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an _AS IS BASIS_, 13 | _WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND_, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mix Generator—An alternative project generator for Mix 2 | 3 | Generate skeleton directories and files for new Elixir projects of 4 | various styles. This is the same idea as `mix new`, `mix nerves.new`, 5 | `mix phoenix.new`, but all under one roof. It is also open ended—anyone 6 | can write a new template, and anyone can publish a template for others 7 | to use. 8 | 9 | ### Install 10 | 11 | $ mix archive.install hex mix_templates 12 | $ mix archive.install hex mix_generator 13 | 14 | Then you can install templates using: 15 | 16 | $ mix template.install «template-name» 17 | 18 | How do you find templates? 19 | 20 | $ mix template.hex 21 | 22 | 23 | ### Use 24 | 25 | 26 | * `$ mix gen --help | -h` 27 | 28 | Show this information 29 | 30 | * `$ mix gen --list | -l` 31 | 32 | Show locally installed templates. Same as `mix template list` 33 | 34 | * `$ mix gen «template-name» --help` 35 | 36 | See specific information for «template-name» 37 | 38 | * `$ mix gen «template-name» «project-name» [ options ... ]` 39 | 40 | Generate a new project called «project-name» using the 41 | template «template-name». This will be created in 42 | a directory with the same name as the project. By default, this 43 | directory will be created under the current directory. This can be 44 | overridden with the `--into` option, which specifies a new containing 45 | directory. 46 | 47 | ### Options: 48 | 49 | As well as `--into «dir»` each individual template may define its own set 50 | of options. For example, the `project` template will have its own options 51 | for creating supervised apps and so on. 52 | 53 | Use `mix gen «template-name» --help` to see a list of these options. 54 | 55 | ### Examples 56 | 57 | $ mix gen project simple_app 58 | 59 | $ mix gen project super_app --supervised --into ~/projects/ 60 | 61 | -------------------------------------------------------------------------------- /lib/mix/tasks/gen.ex: -------------------------------------------------------------------------------- 1 | defmodule Mix.Tasks.Gen do 2 | 3 | @moduledoc File.read!(Path.join([__DIR__, "../../../README.md"])) 4 | 5 | use Private 6 | 7 | use Mix.Task 8 | 9 | alias MixTaskGen.{Assigns, Options} 10 | alias MixTemplates.{Cache, Specs} 11 | 12 | # @default_options %{ 13 | # into: ".", 14 | # force: false, 15 | # } 16 | 17 | @base_option_specs [ 18 | into: [ default: "." ], 19 | force: [ default: false ], 20 | ] 21 | 22 | def run(args) do 23 | parse_command(args) 24 | |> run_command 25 | end 26 | 27 | private do 28 | 29 | ##################### 30 | # Parameter parsing # 31 | ##################### 32 | 33 | def parse_command(["--help"]), do: :help 34 | def parse_command(["help"]), do: :help 35 | def parse_command(["-h"]), do: :help 36 | 37 | def parse_command(["--list"]), do: :list 38 | def parse_command(["-l"]), do: :list 39 | 40 | def parse_command([option = "-" <> _ | _rest]) do 41 | { :error, "unknown or misplaced option “#{option}”" } 42 | end 43 | 44 | def parse_command([_template_name]) do 45 | { :error, "missing name of project" } 46 | end 47 | 48 | def parse_command([template_name, "--help"]) do 49 | { :help, template_name } 50 | end 51 | 52 | def parse_command([template_name, "-h"]) do 53 | { :help, template_name } 54 | end 55 | 56 | def parse_command([_template_name, "-" <> _ | _rest]) do 57 | { :error, "missing name of project" } 58 | end 59 | 60 | def parse_command([ project, name | rest ]) do 61 | { :new_project, project, name, rest } 62 | end 63 | 64 | def parse_command(other) do 65 | error("Unknown command: mix gen #{Enum.join(other, " ")}") 66 | end 67 | 68 | def options_from_args(args) do 69 | { switches, extra } = 70 | case OptionParser.parse(args, switches: []) do 71 | { switches, [], extra } -> 72 | { switches, extra } 73 | { _switches, other, _extra } -> 74 | error("unknown option “#{Enum.join(other, " ")}”") 75 | end 76 | 77 | (extra ++ switches) 78 | |> Enum.map(&make_params_with_no_arg_true/1) 79 | end 80 | 81 | defp make_params_with_no_arg_true({param, nil}) do 82 | make_params_with_no_arg_true({param, true}) 83 | end 84 | defp make_params_with_no_arg_true({"--" <> param, value}) do 85 | { String.to_atom(param), value } 86 | end 87 | defp make_params_with_no_arg_true(other), do: other 88 | 89 | 90 | ######################## 91 | # mix gen project name # 92 | ######################## 93 | 94 | def run_command( { :new_project, template_name, project_name, args }) do 95 | find_template(template_name) 96 | |> generate_project(template_name, String.downcase(project_name), args) 97 | end 98 | 99 | 100 | ################ 101 | # mix gen list # 102 | ################ 103 | 104 | def run_command(:list), do: list_local_templates() 105 | 106 | 107 | ################ 108 | # mix gen help # 109 | ################ 110 | 111 | def run_command(:help), do: usage() 112 | 113 | ########################### 114 | # mix gen template --help # 115 | ########################### 116 | 117 | def run_command({:help, template_name}), do: template_usage(template_name) 118 | 119 | 120 | ######### 121 | # other # 122 | ######### 123 | 124 | def run_command({ :error, reason }), do: error(reason) 125 | 126 | 127 | ################################ 128 | # Helpers for generate_project # 129 | ################################ 130 | 131 | defp generate_project(nil, template_name, _project_name, _args) do 132 | error("Can't find template “#{template_name}”") 133 | end 134 | 135 | defp generate_project(template_module, template_name, project_name, args) do 136 | normalized_args = options_from_args(args) 137 | options = build_options(template_module, normalized_args) 138 | 139 | generate_with_options(template_module, template_name, project_name, options) 140 | end 141 | 142 | defp generate_with_options(template_module, template_name, project_name, options) do 143 | options = 144 | maybe_invoke_based_on(template_module.based_on(), 145 | template_name, 146 | project_name, 147 | options) 148 | 149 | assigns = global_assigns(options, template_module, project_name) 150 | create_output(assigns) 151 | template_module.clean_up(assigns) 152 | end 153 | 154 | defp build_options(template, args) do 155 | specs = Specs.accumulate_specs(template, @base_option_specs) 156 | Options.from_args(args, specs) 157 | end 158 | 159 | defp global_assigns(options, template_module, project_name) do 160 | %{ 161 | host_os: Assigns.os_type(), 162 | now: Assigns.date_time_values(), 163 | original_args: options, 164 | 165 | project_name: project_name, 166 | project_name_camel_case: Macro.camelize(project_name), 167 | target_dir: options.into, 168 | in_umbrella?: in_umbrella?(), 169 | 170 | target_subdir: project_name, 171 | template_module: template_module, 172 | template_name: template_module.name(), 173 | just_files: template_module.just_files(), 174 | 175 | elixir_version: System.version(), 176 | erlang_version: :erlang.system_info(:version), 177 | otp_release: :erlang.system_info(:otp_release), 178 | 179 | force: options.force, 180 | } 181 | |> Map.merge(options) 182 | end 183 | 184 | 185 | defp find_template(template_name) do 186 | Cache.find_template(template_name) 187 | end 188 | 189 | 190 | defp create_output(assigns) do 191 | case MixTemplates.generate(assigns.template_module, assigns) do 192 | { :error, reason } -> 193 | Mix.shell.info([ :red, "Error: ", :reset, reason ]) 194 | :ok -> 195 | Mix.shell.info([ :green, "Successfully generated ", 196 | :reset, assigns.project_name, 197 | :green, " in ", 198 | :reset, assigns.target_dir]) 199 | end 200 | 201 | end 202 | 203 | 204 | defp maybe_invoke_based_on(nil, _, _, options) do 205 | options 206 | end 207 | 208 | defp maybe_invoke_based_on(based_on_name, template_name, project_name, options) do 209 | based_on_module = find_template(based_on_name) 210 | if !based_on_module do 211 | Mix.raise(""" 212 | Cannot find template “#{based_on_name}” 213 | This is needed by the template “#{template_name}” 214 | """) 215 | end 216 | generate_with_options(based_on_module, based_on_name, project_name, options) 217 | Map.put(options, :force, "based_on") 218 | end 219 | 220 | 221 | ########### 222 | # Utility # 223 | ########### 224 | 225 | defp error(message, extras \\ nil) 226 | defp error(message, extras) when is_list(extras) do 227 | error(message, extras |> Enum.map(&inspect/1) |> Enum.join(", ")) 228 | end 229 | defp error(message, nil) do 230 | Mix.shell.info([ :red, "ERROR: ", :reset, message ]) 231 | exit(:normal) 232 | end 233 | defp error(message, extras) do 234 | Mix.shell.info([ :red, message, :reset, extras ]) 235 | exit(:normal) 236 | end 237 | 238 | defp usage() do 239 | IO.puts "USAGE:" 240 | list_local_templates() 241 | exit(:normal) 242 | end 243 | 244 | defp template_usage(template_name) do 245 | Mix.Task.run("template", [ template_name, "--help" ]) 246 | end 247 | 248 | # defp list_local_templates(title) do 249 | # IO.puts("\n#{title}\n") 250 | # list_local_templates() 251 | # end 252 | 253 | defp list_local_templates() do 254 | Mix.Task.run("template", []) 255 | end 256 | 257 | # stolen from mix/tasks/new.ex. 258 | defp in_umbrella? do 259 | apps = Path.dirname(File.cwd!) 260 | 261 | try do 262 | Mix.Project.in_project(:umbrella_check, "../..", fn _ -> 263 | path = Mix.Project.config[:apps_path] 264 | path && Path.expand(path) == apps 265 | end) 266 | catch 267 | _, _ -> false 268 | end 269 | end 270 | end 271 | end 272 | -------------------------------------------------------------------------------- /lib/mix_task_gen/assigns.ex: -------------------------------------------------------------------------------- 1 | defmodule MixTaskGen.Assigns do 2 | 3 | def date_time_values do 4 | {local_date_e, local_time_e} = :calendar.local_time() 5 | local_time = local_time_e |> Time.from_erl! |> Time.to_string 6 | local_date = local_date_e |> Date.from_erl! |> Date.to_string 7 | 8 | utc = DateTime.utc_now 9 | 10 | %{ 11 | utc: %{ 12 | time: utc |> DateTime.to_time |> Time.to_string, 13 | date: utc |> DateTime.to_date |> Date.to_string, 14 | date_time: DateTime.to_iso8601(utc), 15 | }, 16 | local: %{ 17 | time: local_time, 18 | date: local_date, 19 | date_time: "#{local_date} #{local_time}", 20 | }, 21 | } 22 | end 23 | 24 | def os_type do 25 | case :os.type() do 26 | { os, variant } -> 27 | "#{os} (#{variant})" 28 | { os } -> 29 | "#{os}" 30 | other -> 31 | to_string(other) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/mix_task_gen/options.ex: -------------------------------------------------------------------------------- 1 | defmodule MixTaskGen.Options do 2 | 3 | use Private 4 | 5 | 6 | def from_args(args, option_specs) do 7 | option_specs 8 | |> normalize_aliases 9 | |> get_defaults 10 | |> add_options_from(args, option_specs) 11 | |> verify_required_options_are_present(option_specs) 12 | end 13 | 14 | 15 | private do 16 | 17 | ##################### 18 | # normalize_aliases # 19 | ##################### 20 | 21 | defp normalize_aliases(specs) do 22 | for {name, spec} <- specs, into: %{} do 23 | if alias_name = spec[:same_as] do 24 | { name, specs[alias_name] } 25 | else 26 | { name, spec } 27 | end 28 | end 29 | end 30 | 31 | ##################### 32 | # get_defaults_from # 33 | ##################### 34 | 35 | defp get_defaults(option_specs) do 36 | option_specs 37 | |> Enum.reduce(%{}, &maybe_add_default/2) 38 | end 39 | 40 | defp maybe_add_default(spec = {_name, details}, options) do 41 | case Keyword.fetch(details, :default) do 42 | :error -> 43 | options 44 | { :ok, value } -> 45 | create_option(spec, value, options) 46 | end 47 | end 48 | 49 | #################### 50 | # add_options_from # 51 | #################### 52 | 53 | defp add_options_from(options, args, option_specs) do 54 | args 55 | |> Enum.reduce(options, fn arg, options -> 56 | option_from(options, arg, option_specs) 57 | end) 58 | end 59 | 60 | defp option_from(options, {name, value}, specs) do 61 | case Keyword.fetch(specs, name) do 62 | :error -> 63 | Mix.raise "Unknown option “--#{name}”" 64 | { :ok, spec } -> 65 | create_option({name, spec}, value, options) 66 | end 67 | end 68 | 69 | defp option_from(options, x, specs) do 70 | raise inspect [options, x, specs] 71 | end 72 | 73 | 74 | ####################################### 75 | # verify_required_options_are_present # 76 | ####################################### 77 | 78 | defp verify_required_options_are_present(options, specs) do 79 | for { name, details } <- specs, details[:required] do 80 | if !options[name] do 81 | Mix.raise("required parameter “--#{name}” is missing") 82 | end 83 | end 84 | options 85 | end 86 | 87 | ########### 88 | # Utility # 89 | ########### 90 | 91 | defp create_option(spec, value, options) do 92 | Map.put(options, name_of(spec), value) 93 | end 94 | 95 | defp name_of({ name, details }) do 96 | details[:to] || name 97 | end 98 | end 99 | 100 | end 101 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule MixGenerator.Mixfile do 2 | use Mix.Project 3 | 4 | @name :mix_generator 5 | @version "0.1.10" 6 | 7 | @mix_templates (if System.get_env("GEN_DEV") do 8 | { :mix_templates, path: "../mix_templates" } 9 | else 10 | { :mix_templates, ">= 0.1.10" } 11 | end) 12 | 13 | @extra_applications [ 14 | :crypto, 15 | ] 16 | 17 | @deps [ 18 | @mix_templates, 19 | { :private, ">= 0.0.0" }, 20 | { :ex_doc, ">= 0.0.0", only: :dev }, 21 | ] 22 | 23 | @description """ 24 | This application adds a `gen` task to mix, which generates project trees. 25 | 26 | Unlike `mix new`, it can be fully customized. You can create private variants 27 | for your own use, and publish public ones that can be shared. 28 | """ 29 | 30 | ############################################################ 31 | 32 | def project do 33 | in_production = Mix.env == :prod 34 | [ 35 | app: @name, 36 | version: @version, 37 | elixir: ">= 1.4.0", 38 | deps: @deps, 39 | build_embedded: in_production, 40 | start_permanent: in_production, 41 | package: package(), 42 | description: @description, 43 | ] 44 | end 45 | 46 | def application do 47 | [ 48 | extra_applications: @extra_applications 49 | ] 50 | end 51 | 52 | defp package do 53 | [ 54 | files: [ 55 | "lib", "mix.exs", "README.md", 56 | ], 57 | maintainers: [ 58 | "Dave Thomas ", 59 | ], 60 | licenses: [ 61 | "Apache 2 (see the file LICENSE.md for details)" 62 | ], 63 | links: %{ 64 | "GitHub" => "https://github.com/pragdave/mix_generator", 65 | } 66 | ] 67 | end 68 | 69 | end 70 | -------------------------------------------------------------------------------- /test/mix_generator_test.exs: -------------------------------------------------------------------------------- 1 | Code.require_file "./test_helper.exs", __DIR__ 2 | 3 | defmodule MixGeneratorTest do 4 | use ExUnit.Case 5 | 6 | @template Path.join(__DIR__, "../test_templates/project") 7 | @child_template Path.join(__DIR__, "../test_templates/child") 8 | @project_name "cecil" 9 | @project_name_camel_case "Cecil" 10 | 11 | test "basic project can be created" do 12 | in_tmp(%{ 13 | setup: fn -> 14 | Mix.Tasks.Gen.run([ @template, @project_name ]) 15 | end, 16 | test: fn -> 17 | ~w{ .gitignore 18 | README.md 19 | mix.exs 20 | config/config.exs 21 | lib/#{@project_name}.ex 22 | test/#{@project_name}_test.exs 23 | test/test_helper.exs 24 | } 25 | |> Enum.each(&assert_file/1) 26 | 27 | assert_file("mix.exs", ~r/@name\s+:#{@project_name}/) 28 | assert_file("lib/#{@project_name}.ex", ~r/defmodule #{@project_name_camel_case}/) 29 | end}) 30 | end 31 | 32 | test "basic project can be created when name is capitalized" do 33 | in_tmp(%{ 34 | setup: fn -> 35 | Mix.Tasks.Gen.run([ @template, String.capitalize(@project_name) ]) 36 | end, 37 | test: fn -> 38 | ~w{ .gitignore 39 | README.md 40 | mix.exs 41 | config/config.exs 42 | lib/#{@project_name}.ex 43 | test/#{@project_name}_test.exs 44 | test/test_helper.exs 45 | } 46 | |> Enum.each(&assert_file/1) 47 | 48 | assert_file("mix.exs", ~r/@name\s+:#{@project_name}/) 49 | assert_file("lib/#{@project_name}.ex", ~r/defmodule #{@project_name_camel_case}/) 50 | end}) 51 | end 52 | 53 | test "project with --sup can be created" do 54 | in_tmp(%{ 55 | setup: fn -> 56 | Mix.Tasks.Gen.run([ @template, @project_name, "--sup" ]) 57 | end, 58 | test: fn -> 59 | ~w{ .gitignore 60 | README.md 61 | mix.exs 62 | config/config.exs 63 | lib/#{@project_name}.ex 64 | lib/#{@project_name}/application.ex 65 | test/#{@project_name}_test.exs 66 | test/test_helper.exs 67 | } 68 | |> Enum.each(&assert_file/1) 69 | 70 | %{ 71 | "mix.exs" => 72 | ~r/@name\s+:#{@project_name}/, 73 | "lib/#{@project_name}.ex" => 74 | ~r/defmodule #{@project_name_camel_case}/, 75 | "lib/#{@project_name}/application.ex" => 76 | ~r/defmodule #{@project_name_camel_case}.Application/, 77 | "lib/#{@project_name}/application.ex" => 78 | ~r/#{@project_name_camel_case}.Supervisor/ 79 | } 80 | |> 81 | Enum.each(fn {file, content} -> 82 | assert_file(file, content) 83 | end) 84 | end}) 85 | end 86 | 87 | # the child project is like project, but adds a file lib/child.ex, and removes 88 | # lib/#{project_name}.ex 89 | 90 | test "template based on another can be created" do 91 | in_tmp(%{ 92 | setup: fn -> 93 | Mix.Tasks.Gen.run([ @child_template, @project_name, 94 | "--name-of-child", "cedric" ]) 95 | end, 96 | test: fn -> 97 | 98 | ~w{ .gitignore 99 | README.md 100 | mix.exs 101 | config/config.exs 102 | lib/child.ex 103 | test/#{@project_name}_test.exs 104 | test/test_helper.exs 105 | } 106 | |> Enum.each(&assert_file/1) 107 | 108 | %{ 109 | "mix.exs" => 110 | ~r/@name\s+:#{@project_name}/, 111 | "lib/child.ex" => 112 | ~r/Child is called cedric/, 113 | } 114 | |> 115 | Enum.each(fn {file, content} -> 116 | assert_file(file, content) 117 | end) 118 | 119 | assert !File.exists?("lib/#{@project_name}.ex") 120 | end}) 121 | end 122 | 123 | ############################################################ 124 | 125 | # stolen from mix/test/tasks/new 126 | 127 | defp assert_file(file) do 128 | assert File.regular?(file), "Expected #{file} to exist, but does not" 129 | end 130 | 131 | defp assert_file(file, matcher) when is_function(matcher, 1) do 132 | assert_file(file) 133 | matcher.(File.read!(file)) 134 | end 135 | 136 | defp assert_file(file, match) do 137 | assert_file file, &(assert &1 =~ match) 138 | end 139 | 140 | def in_tmp(%{setup: setup, test: tests}) do 141 | System.tmp_dir! 142 | |> File.cd!(fn -> 143 | File.rm_rf!(@project_name) 144 | setup.() 145 | assert File.dir?(@project_name) 146 | File.cd!(@project_name, fn -> 147 | tests.() 148 | end) 149 | File.rm_rf!(@project_name) 150 | end) 151 | end 152 | 153 | end 154 | -------------------------------------------------------------------------------- /test/options_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OptionsTest do 2 | use ExUnit.Case 3 | alias MixTaskGen.Options 4 | 5 | test "names extracted from spec" do 6 | spec = [ one: []] 7 | assert Options.name_of(hd spec) == :one 8 | 9 | spec = [ two: [ to: :second ]] 10 | assert Options.name_of(hd spec) == :second 11 | end 12 | 13 | test "defaults copied in" do 14 | spec = [ one: [ default: 1], two: [], three: [ default: :iii], four: []] 15 | options = Options.get_defaults(spec) 16 | assert Enum.count(options) == 2 17 | assert options.one == 1 18 | assert options.three == :iii 19 | end 20 | 21 | test "options created from args" do 22 | args = [ name: "fred", force: true, answer: 42 ] 23 | specs = [ 24 | name: [], 25 | force: [ to: :please ], 26 | answer: [] 27 | ] 28 | options = Options.add_options_from(%{}, args, specs) 29 | 30 | assert Enum.count(options) == 3 31 | assert options.name == "fred" 32 | assert options.please == true 33 | assert options.answer == 42 34 | end 35 | 36 | test "unknown option raises an error" do 37 | args = [ name: "fred", force: true, wibble: false, answer: 42 ] 38 | specs = [ 39 | name: [], 40 | force: [ to: :please ], 41 | answer: [] 42 | ] 43 | 44 | error = assert_raise(Mix.Error, fn -> 45 | Options.add_options_from(%{}, args, specs) 46 | end) 47 | 48 | assert error.message =~ ~r/wibble/ 49 | end 50 | 51 | test "missing required option raises error" do 52 | specs = [ 53 | name: [], 54 | wibble: [ required: true ], 55 | answer: [] 56 | ] 57 | 58 | error = assert_raise(Mix.Error, fn -> 59 | Options.verify_required_options_are_present(%{fred: 123}, specs) 60 | end) 61 | 62 | assert error.message =~ ~r/wibble/ 63 | end 64 | 65 | test "aliases" do 66 | specs = [ 67 | age: [ default: 22, required: true ], 68 | height: [ same_as: :age ], 69 | weight: [ same_as: :age ], 70 | waist: [ default: 999] 71 | ] 72 | 73 | specs1 = Options.normalize_aliases(specs) 74 | 75 | assert specs1.age[:default] == 22 76 | assert specs1.height[:default] == 22 77 | assert specs1.weight[:default] == 22 78 | assert specs1.waist[:default] == 999 79 | 80 | end 81 | 82 | test "api" do 83 | 84 | args = [ name: "fred", age: 33 ] 85 | specs = [ 86 | name: [], 87 | age: [ default: 22, required: true ], 88 | height: [ default: 1.5 ] 89 | ] 90 | 91 | options = Options.from_args(args, specs) 92 | assert Enum.count(options) == 3 93 | assert options.name == "fred" 94 | assert options.age == 33 95 | assert options.height == 1.5 96 | end 97 | 98 | end 99 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /test_templates/child/README.md: -------------------------------------------------------------------------------- 1 | # Child 2 | 3 | > TODO: Add description** 4 | 5 | 6 | ## Installation 7 | 8 | ```elixir 9 | @deps [ 10 | child: "~> 0.1.0" 11 | ] 12 | ``` 13 | 14 | ## License 15 | 16 | > TODO: Add license 17 | 18 | ---- 19 | Created: 2017-04-14Z 20 | -------------------------------------------------------------------------------- /test_templates/child/lib/child.ex: -------------------------------------------------------------------------------- 1 | defmodule Child do 2 | 3 | @moduledoc File.read!(Path.join([__DIR__, "../README.md"])) 4 | 5 | use MixTemplates, 6 | name: :child, 7 | short_desc: "Template for ....", 8 | source_dir: "../template", 9 | based_on: Path.join(__DIR__, "../../project"), 10 | options: [ 11 | name_of_child: [ to: :child_name ] 12 | ] 13 | 14 | 15 | def clean_up(assigns) do 16 | File.rm!(Path.join([assigns.target_subdir, "lib", "#{assigns.project_name}.ex"])) 17 | end 18 | end 19 | 20 | 21 | -------------------------------------------------------------------------------- /test_templates/child/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Child.Mixfile do 2 | use Mix.Project 3 | 4 | @name :child 5 | @version "0.1.0" 6 | 7 | @deps [ 8 | { :mix_templates, ">0.0.0", app: false }, 9 | { :ex_doc, ">0.0.0", only: [:dev, :test] }, 10 | ] 11 | 12 | @maintainers ["Your Name "] 13 | @github "https://github.com/•••/#{@name}" 14 | 15 | @description """ 16 | What your template does (less than 300 characters) 17 | """ 18 | 19 | # ------------------------------------------------------------ 20 | 21 | def project do 22 | in_production = Mix.env == :prod 23 | [ 24 | app: @name, 25 | version: @version, 26 | deps: @deps, 27 | elixir: "~> 1.4", 28 | package: package(), 29 | description: @description, 30 | build_embedded: in_production, 31 | start_permanent: in_production, 32 | ] 33 | end 34 | 35 | defp package do 36 | [ 37 | name: @name, 38 | files: ["lib", "mix.exs", "README.md", "LICENSE.md", "template"], 39 | maintainers: @maintainers, 40 | licenses: ["Apache 2.0"], 41 | links: %{ 42 | "GitHub" => @github, 43 | }, 44 | # extra: %{ "type" => "a_template_for_mix_gen" }, 45 | ] 46 | end 47 | 48 | end 49 | -------------------------------------------------------------------------------- /test_templates/child/template/$PROJECT_NAME$/lib/child.ex: -------------------------------------------------------------------------------- 1 | Child is called <%= @child_name %> 2 | -------------------------------------------------------------------------------- /test_templates/project/LICENSE.md: -------------------------------------------------------------------------------- 1 | #### Copyright © 2017 David Thomas (pragdave) 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you 4 | may not use this file except in compliance with the License. You may 5 | obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an _as is_ basis, 11 | **without warranties or conditions of any kind**, either express or 12 | implied. See the License for the specific language governing 13 | permissions and limitations under the License. 14 | -------------------------------------------------------------------------------- /test_templates/project/README.md: -------------------------------------------------------------------------------- 1 | # Project: a new mix template for projects 2 | 3 | This is an alternative for `mix new`, creating what I feel is an 4 | easier to read and maintain set of basic files. 5 | 6 | You use it in combination with the `mix gen` mix task, which you will need 7 | to install. 8 | 9 | ## New Project Template 10 | 11 | mix gen project «name» [ --into «path» ] [--sup[ervisor]] [--app[lication] «app»] [--module «module»] 12 | 13 | ## Install 14 | 15 | This template is installed using the `template.install` mix task. 16 | Projects are generated from it using the `mix gen` task. 17 | 18 | So, before using templates for the first time, you need to install these two tasks: 19 | 20 | $ mix archive.install mix_templates 21 | $ mix archive.install mix_generator 22 | 23 | Then you can install this template using 24 | 25 | $ mix template.install gen_template_project 26 | 27 | 28 | ## Use 29 | 30 | To create a basic project, with no supervision and no application, run: 31 | 32 | ~~~ 33 | $ mix gen project «project_name» 34 | ~~~ 35 | 36 | `«project_name»` is both the name of the subdirectory that will hold the 37 | project and the name that will be given to the application. This 38 | affects entries in `mix.exs` as well as the names of the main 39 | file in `lib/` and the skeleton test in `test/`. The application 40 | module name will be the camelcase version of «name». 41 | 42 | By default the subdirectory will be created under your 43 | current directory; you can change this with the `--into` option: 44 | 45 | ~~~ 46 | $ mix gen project «project_name» --into some/other/dir 47 | ~~~ 48 | 49 | ### Variants 50 | 51 | To create a project with a top-level supervisor contained in an 52 | application callback, use the `--sup` (or `--supervisor`) option. 53 | 54 | ~~~ 55 | $ mix gen project «project_name» --sup 56 | ~~~ 57 | 58 | The application's `start` function is created in 59 | `lib/«name»/application.ex`, along with a basic supervisor. 60 | 61 | You can change the name used for the application: 62 | 63 | ~~~ 64 | $ mix gen project «project_name» --app[lication] «app» 65 | ~~~ 66 | 67 | The original «project_name» parameter will be the 68 | name used for the directory, and «app» will be used when 69 | creating file names in that directory and when customizing the 70 | file contents. 71 | 72 | Finally, you can override the name used for the application module: 73 | 74 | 75 | ~~~ 76 | $ mix gen project «project_name» --module «Module» 77 | ~~~ 78 | 79 | «Module» must be a valid Elixir module name or alias. 80 | 81 | ~~~ 82 | 83 | -------------------------------------------------------------------------------- /test_templates/project/doc/.build: -------------------------------------------------------------------------------- 1 | dist/app-372682077e.js 2 | dist/app-f7824c48e6.css 3 | fonts/icomoon.eot 4 | fonts/icomoon.svg 5 | fonts/icomoon.ttf 6 | fonts/icomoon.woff 7 | dist/sidebar_items-481e944cee.js 8 | api-reference.html 9 | 404.html 10 | Mix.Gen.Template.Project.html 11 | index.html 12 | -------------------------------------------------------------------------------- /test_templates/project/doc/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 404 – gen_template_project v0.1.7 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 22 | 56 | 57 |
58 |
59 |
60 | 61 | 62 |

Page not found

63 | 64 |

Sorry, but the page you were trying to get to, does not exist. You 65 | may want to try searching this site using the sidebar or using our 66 | API Reference page to find what 67 | you were looking for.

68 | 69 |
70 |

71 | 72 | Built using 73 | ExDoc (v0.15.0), 74 | 75 | 76 | designed by 77 | Friedel Ziegelmayer. 78 | 79 |

80 |
82 |
83 |
84 |
85 |
86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /test_templates/project/doc/Mix.Gen.Template.Project.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Mix.Gen.Template.Project – gen_template_project v0.1.7 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 22 | 56 | 57 |
58 |
59 |
60 | 61 | 62 |

63 | gen_template_project v0.1.7 64 | Mix.Gen.Template.Project 65 | 66 | 67 |

68 | 69 | 70 |
71 |

Project: a new mix template for projects

72 |

This is an alternative for mix new, creating what I feel is an 73 | easier to read and maintain set of basic files.

74 |

You use it in combination with the mix gen mix task, which you will need 75 | to install.

76 |

77 | 78 | New Project Template 79 |

80 | 81 |
    mix gen project «name» [ --into «path» ] [--sup[ervisor]] [--app[lication] «app»] [--module «module»]
82 |

83 | 84 | Install 85 |

86 | 87 |

This template is installed using the template.install mix task. 88 | Projects are generated from it using the mix gen task.

89 |

So, before using templates for the first time, you need to install these two tasks:

90 |
$ mix archive.install mix_templates
 91 | $ mix archive.install mix_generator
92 |

Then you can install this template using

93 |
$ mix template.install gen_template_project
94 |

95 | 96 | Use 97 |

98 | 99 |

To create a basic project, with no supervision and no application, run:

100 |
$ mix gen project «project_name»
101 |

«project_name» is both the name of the subdirectory that will hold the 102 | project and the name that will be given to the application. This 103 | affects entries in mix.exs as well as the names of the main 104 | file in lib/ and the skeleton test in test/. The application 105 | module name will be the camelcase version of «name».

106 |

By default the subdirectory will be created under your 107 | current directory; you can change this with the --into option:

108 |
$ mix gen project «project_name» --into some/other/dir
109 |

Variants

110 |

To create a project with a top-level supervisor contained in an 111 | application callback, use the --sup (or --supervisor) option.

112 |
$ mix gen project «project_name» --sup
113 |

The application’s start function is created in 114 | lib/«name»/application.ex, along with a basic supervisor.

115 |

You can change the name used for the application:

116 |
$ mix gen project «project_name» --app[lication] «app»
117 |

The original «project_name» parameter will be the 118 | name used for the directory, and «app» will be used when 119 | creating file names in that directory and when customizing the 120 | file contents.

121 |

Finally, you can override the name used for the application module:

122 |
$ mix gen project «project_name» --module «Module»
123 |

«Module» must be a valid Elixir module name or alias.

124 |
125 | 126 |
127 | 128 | 129 | 130 |
131 |

132 | 133 | 134 | 135 | Summary 136 |

137 | 138 | 139 | 140 |
141 |

142 | Functions 143 |

144 |
145 |
146 | name() 147 |
148 | 149 |

Return the name of this template as an atom. This is 150 | the name passed to the gen command

151 |
152 | 153 |
154 |
155 | 158 | 159 |

Override this function to process command line options and 160 | set values passed into the template via assigns

161 |
162 | 163 |
164 |
165 |
166 | short_desc() 167 |
168 | 169 |

Return the short description of this template, or nil

170 |
171 | 172 |
173 |
174 |
175 | source_dir() 176 |
177 | 178 |

Return the absolute path to the tree that is to be copied when 179 | instantiating this template. This top-level dir will typically 180 | just contain a directory called $APP_NAME$

181 |
182 | 183 |
184 | 185 |
186 | 187 | 188 | 189 | 190 |
191 | 192 | 193 | 194 | 195 | 196 |
197 |

198 | 199 | 200 | 201 | Functions 202 |

203 |
204 | 205 |
206 | 207 | 208 | 209 | name() 210 | 211 | 212 | 213 |
214 |
215 |

Return the name of this template as an atom. This is 216 | the name passed to the gen command.

217 | 218 |
219 |
220 |
221 | 222 |
223 | 224 | 225 | 226 | populate_assigns(assigns, options) 227 | 228 | 229 | 230 |
231 |
232 |

Override this function to process command line options and 233 | set values passed into the template via assigns.

234 | 235 |
236 |
237 |
238 | 239 |
240 | 241 | 242 | 243 | short_desc() 244 | 245 | 246 | 247 |
248 |
249 |

Return the short description of this template, or nil.

250 | 251 |
252 |
253 |
254 | 255 |
256 | 257 | 258 | 259 | source_dir() 260 | 261 | 262 | 263 |
264 |
265 |

Return the absolute path to the tree that is to be copied when 266 | instantiating this template. This top-level dir will typically 267 | just contain a directory called $APP_NAME$.

268 | 269 |
270 |
271 | 272 |
273 | 274 | 275 | 276 |
277 |

278 | 279 | Built using 280 | ExDoc (v0.15.0), 281 | 282 | 283 | designed by 284 | Friedel Ziegelmayer. 285 | 286 |

287 |
289 |
290 |
291 |
292 |
293 | 294 | 295 | 296 | 297 | -------------------------------------------------------------------------------- /test_templates/project/doc/api-reference.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | API Reference – gen_template_project v0.1.7 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 22 | 56 | 57 |
58 |
59 |
60 | 61 | 62 |

63 | gen_template_project v0.1.7 64 | API Reference 65 |

66 | 67 | 68 |
69 |

70 | 71 | Modules 72 |

73 | 74 |
75 |
76 | 77 | 78 |

Project: a new mix template for projects

79 |
80 | 81 |
82 | 83 |
84 |
85 | 86 | 87 | 88 | 89 | 90 | 91 |
92 |

93 | 94 | Built using 95 | ExDoc (v0.15.0), 96 | 97 | 98 | designed by 99 | Friedel Ziegelmayer. 100 | 101 |

102 |
104 |
105 |
106 |
107 |
108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /test_templates/project/doc/dist/app-f7824c48e6.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Lato:300,700|Merriweather:300italic,300|Inconsolata:400,700);.hljs,article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}img,legend{border:0}.results ul,.sidebar ul{list-style:none}.night-mode-toggle:focus,.sidebar .sidebar-search .sidebar-searchInput:focus,.sidebar .sidebar-search .sidebar-searchInput:hover,.sidebar-toggle:active,.sidebar-toggle:focus,.sidebar-toggle:hover,a:active,a:hover{outline:0}.hljs-comment{color:#8e908c}.css .hljs-class,.css .hljs-id,.css .hljs-pseudo,.hljs-attribute,.hljs-regexp,.hljs-tag,.hljs-variable,.html .hljs-doctype,.ruby .hljs-constant,.xml .hljs-doctype,.xml .hljs-pi,.xml .hljs-tag .hljs-title{color:#c82829}.hljs-built_in,.hljs-constant,.hljs-literal,.hljs-number,.hljs-params,.hljs-pragma,.hljs-preprocessor{color:#f5871f}.css .hljs-rule .hljs-attribute,.ruby .hljs-class .hljs-title{color:#eab700}.hljs-header,.hljs-inheritance,.hljs-name,.hljs-string,.hljs-value,.ruby .hljs-symbol,.xml .hljs-cdata{color:#718c00}.css .hljs-hexcolor,.hljs-title{color:#3e999f}.coffeescript .hljs-title,.hljs-function,.javascript .hljs-title,.perl .hljs-sub,.python .hljs-decorator,.python .hljs-title,.ruby .hljs-function .hljs-title,.ruby .hljs-title .hljs-keyword{color:#4271ae}.hljs-keyword,.javascript .hljs-function{color:#8959a8}.hljs{overflow-x:auto;background:#fff;color:#4d4d4c;padding:.5em;-webkit-text-size-adjust:none}legend,td,th{padding:0}.coffeescript .javascript,.javascript .xml,.tex .hljs-formula,.xml .css,.xml .hljs-cdata,.xml .javascript,.xml .vbscript{opacity:.5}/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}abbr[title]{border-bottom:1px dotted}b,optgroup,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}.content-outer,body{background-color:#fff}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre,textarea{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}table{border-collapse:collapse;border-spacing:0}@font-face{font-family:icomoon;src:url(../fonts/icomoon.eot?h5z89e);src:url(../fonts/icomoon.eot?#iefixh5z89e) format('embedded-opentype'),url(../fonts/icomoon.ttf?h5z89e) format('truetype'),url(../fonts/icomoon.woff?h5z89e) format('woff'),url(../fonts/icomoon.svg?h5z89e#icomoon) format('svg');font-weight:400;font-style:normal}.icon-elem,[class*=" icon-"],[class^=icon-]{font-family:icomoon;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.sidebar,body{font-family:Lato,sans-serif}.icon-link:before{content:"\e005"}.icon-search:before{content:"\e036"}.icon-cross:before{content:"\e117"}@media screen and (max-width:768px){.icon-menu{font-size:1em}}@media screen and (min-width:769px){.icon-menu{font-size:1.25em}}@media screen and (min-width:1281px){.icon-menu{font-size:1.5em}}.icon-menu:before{content:"\e120"}.icon-angle-right:before{content:"\f105"}.icon-code:before{content:"\f121"}body,html{box-sizing:border-box;height:100%;width:100%}body{margin:0;font-size:16px;line-height:1.6875em}*,:after,:before{box-sizing:inherit}.main{display:-ms-flexbox;display:-ms-flex;display:flex;-ms-flex-pack:end;justify-content:flex-end}.sidebar{display:-ms-flexbox;display:-ms-flex;display:flex;min-height:0;-moz-flex-direction:column;-ms-flex-direction:column;flex-direction:column;width:300px;height:100%;position:fixed;top:0;left:0;z-index:4}.content{width:100%;padding-left:300px;overflow-y:auto;-webkit-overflow-scrolling:touch;height:100%;position:relative;z-index:3}@media screen and (max-width:768px){body .content{z-index:0;padding-left:0}body .sidebar{z-index:3;transform:translateX(-102%);will-change:transform}}body.sidebar-closed .sidebar,body.sidebar-closing .sidebar,body.sidebar-opening .sidebar{z-index:0}body.sidebar-opened .sidebar-toggle,body.sidebar-opening .sidebar-toggle{transform:translateX(250px)}@media screen and (max-width:768px){body.sidebar-opened .sidebar,body.sidebar-opening .sidebar{transform:translateX(0)}}body.sidebar-closed .content,body.sidebar-closing .content{padding-left:0}body.sidebar-closed .sidebar-toggle,body.sidebar-closing .sidebar-toggle{transform:none}body.sidebar-closed .icon-menu{color:#000}.sidebar-toggle i,.sidebar-toggle:hover{color:#e1e1e1}body.sidebar-opening .sidebar-toggle{transition:transform .3s ease-in-out}body.sidebar-opening .content{padding-left:300px;transition:padding-left .3s ease-in-out}@media screen and (max-width:768px){body.sidebar-opening .content{padding-left:0}body.sidebar-opening .sidebar{transition:transform .3s ease-in-out;z-index:3}}body.sidebar-closing .sidebar-toggle{transition:transform .3s ease-in-out}body.sidebar-closing .content{transition:padding-left .3s ease-in-out}@media screen and (max-width:768px){body.sidebar-closing .sidebar{z-index:3;transition:transform .3s ease-in-out;transform:translateX(-102%)}}.sidebar a,.sidebar-toggle{transition:color .3s ease-in-out}body.sidebar-closed .sidebar{visibility:hidden}.content-inner{max-width:949px;margin:0 auto;padding:3px 60px}.content-outer{min-height:100%}@media screen and (max-width:768px){.content-inner{padding:27px 20px 27px 40px}}.sidebar-toggle{position:fixed;z-index:99;left:18px;top:8px;background-color:transparent;border:none;padding:0;font-size:16px;will-change:transform;transform:translateX(250px)}@media screen and (max-width:768px){.sidebar-toggle{transform:translateX(0);left:5px;top:5px}.sidebar-opened .sidebar-toggle{left:18px;top:5px}}.sidebar{font-size:15px;line-height:18px;background:#373f52;color:#d5dae6;overflow:hidden}.sidebar .gradient{background:linear-gradient(#373f52,rgba(55,63,82,0));height:20px;margin-top:-20px;pointer-events:none;position:relative;top:20px;z-index:100}.sidebar ul li{margin:0;padding:0 10px}.sidebar a{color:#d5dae6;text-decoration:none}.sidebar a:hover{color:#fff}.sidebar .sidebar-projectLink{margin:23px 30px 0}.sidebar .sidebar-projectDetails{display:inline-block;text-align:right;vertical-align:top;margin-top:6px}.sidebar .sidebar-projectImage{display:inline-block;max-width:64px;max-height:64px;margin-left:15px;vertical-align:bottom}.sidebar .sidebar-projectName{font-weight:700;font-size:24px;line-height:30px;color:#fff;margin:0;padding:0;max-width:230px;word-break:break-all}.sidebar .sidebar-projectVersion{margin:0;padding:0;font-weight:300;font-size:16px;line-height:20px;color:#fff}.sidebar .sidebar-listNav{padding:10px 30px 20px;margin:0}.sidebar .sidebar-listNav li,.sidebar .sidebar-listNav li a{text-transform:uppercase;font-weight:300;font-size:14px}.sidebar .sidebar-listNav li{padding-left:17px;border-left:3px solid transparent;transition:all .3s linear;line-height:27px}.sidebar .sidebar-listNav li.selected,.sidebar .sidebar-listNav li.selected a,.sidebar .sidebar-listNav li:hover,.sidebar .sidebar-listNav li:hover a{border-color:#9768d1;color:#fff}.sidebar .sidebar-search{margin:23px 30px 18px;display:-ms-flexbox;display:-ms-flex;display:flex}.sidebar .sidebar-search i.icon-search{font-size:14px;color:#d5dae6}.sidebar .sidebar-search .sidebar-searchInput{background-color:transparent;border:none;border-radius:0;border-bottom:1px solid #959595;margin-left:5px;height:20px}.sidebar #full-list{margin:0 0 0 30px;padding:10px 20px 40px;overflow-y:auto;-webkit-overflow-scrolling:touch;-moz-flex:1 1 .01%;-ms-flex:1 1 .01%;flex:1 1 .01%;-ms-flex-positive:1;-ms-flex-negative:1;-ms-flex-preferred-size:.01%}.sidebar #full-list ul{display:none;margin:9px 15px;padding:0}.sidebar #full-list ul li{font-weight:300;line-height:18px;padding:2px 10px}.sidebar #full-list ul li a.expand:before{content:"+";font-family:monospaced;font-size:15px;float:left;width:13px;margin-left:-13px}.sidebar #full-list ul li.open a.expand:before{content:"−"}.sidebar #full-list ul li ul{display:none;margin:9px 6px}.sidebar #full-list li.open>ul,.sidebar #full-list ul li.open>ul{display:block}.sidebar #full-list ul li ul li{border-left:1px solid #959595;padding:0 10px}.sidebar #full-list ul li ul li.active:before{font-family:icomoon;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:"\f105";margin-left:-10px;font-size:16px;margin-right:5px}.sidebar #full-list li{padding:0;line-height:27px}.sidebar #full-list li.active{border-left:none}.sidebar #full-list li.active>a,.sidebar #full-list li.clicked>a{color:#fff}.sidebar #full-list li.group{text-transform:uppercase;font-weight:700;font-size:.8em;margin:2em 0 0;line-height:1.8em;color:#ddd}@media screen and (max-height:500px){.sidebar{overflow-y:auto}.sidebar #full-list{overflow:visible}}.content-inner{font-family:Merriweather,serif;font-size:1em;line-height:1.6875em}.content-inner h1,.content-inner h2,.content-inner h3,.content-inner h4,.content-inner h5,.content-inner h6{font-family:Lato,sans-serif;font-weight:700;line-height:1.5em;word-wrap:break-word}.content-inner h1{font-size:2em;margin:1em 0 .5em}.content-inner h1.section-heading{margin:1.5em 0 .5em}.content-inner h1 small{font-weight:300}.content-inner h1 a.view-source{font-size:1.2rem}.content-inner h2{font-size:1.6em;margin:1em 0 .5em;font-weight:700}.content-inner h3{font-size:1.375em;margin:1em 0 .5em;font-weight:700}.content-inner a{color:#000;text-decoration:none;text-shadow:.03em 0 #fff,-.03em 0 #fff,0 .03em #fff,0 -.03em #fff,.06em 0 #fff,-.06em 0 #fff,.09em 0 #fff,-.09em 0 #fff,.12em 0 #fff,-.12em 0 #fff,.15em 0 #fff,-.15em 0 #fff;background-image:linear-gradient(#fff,#fff),linear-gradient(#fff,#fff),linear-gradient(#000,#000);background-size:.05em 1px,.05em 1px,1px 1px;background-repeat:no-repeat,no-repeat,repeat-x;background-position:0 90%,100% 90%,0 90%}.content-inner a:selection{text-shadow:.03em 0 #b4d5fe,-.03em 0 #b4d5fe,0 .03em #b4d5fe,0 -.03em #b4d5fe,.06em 0 #b4d5fe,-.06em 0 #b4d5fe,.09em 0 #b4d5fe,-.09em 0 #b4d5fe,.12em 0 #b4d5fe,-.12em 0 #b4d5fe,.15em 0 #b4d5fe,-.15em 0 #b4d5fe;background:#b4d5fe}.content-inner a:-moz-selection{text-shadow:.03em 0 #b4d5fe,-.03em 0 #b4d5fe,0 .03em #b4d5fe,0 -.03em #b4d5fe,.06em 0 #b4d5fe,-.06em 0 #b4d5fe,.09em 0 #b4d5fe,-.09em 0 #b4d5fe,.12em 0 #b4d5fe,-.12em 0 #b4d5fe,.15em 0 #b4d5fe,-.15em 0 #b4d5fe;background:#b4d5fe}.content-inner a *,.content-inner a :after,.content-inner a :before,.content-inner a:after,.content-inner a:before{text-shadow:none}.content-inner a:visited{color:#000}.content-inner ul li{line-height:1.5em}.content-inner ul li>p{margin:0}.content-inner a.view-source{float:right;color:#959595;background:0 0;border:none;text-shadow:none;transition:color .3s ease-in-out;margin-top:1px}.content-inner a.view-source:hover{color:#373f52}.content-inner .note{color:#959595;margin:0 5px;font-size:14px;font-weight:400}.content-inner blockquote{font-style:italic;margin:.5em 0;padding:.25em 1.5em;border-left:3px solid #e1e1e1;display:inline-block}.content-inner blockquote :first-child{padding-top:0;margin-top:0}.content-inner blockquote :last-child{padding-bottom:0;margin-bottom:0}.content-inner table{margin:2em 0}.content-inner th{text-align:left;font-family:Lato,sans-serif;text-transform:uppercase;font-weight:700;padding-bottom:.5em}.content-inner tr{border-bottom:1px solid #d5dae6;vertical-align:bottom;height:2.5em}.content-inner .summary .summary-row .summary-signature a,.content-inner .summary h2 a{background:0 0;border:none;text-shadow:none}.content-inner td,.content-inner th{padding-left:1em;line-height:2em}.content-inner .section-heading:hover a.hover-link{opacity:1;text-decoration:none}.content-inner .section-heading a.hover-link{transition:opacity .3s ease-in-out;display:inline-block;opacity:0;padding:.3em .6em .6em;line-height:1em;margin-left:-2.7em;background:0 0;border:none;text-shadow:none;font-size:16px;vertical-align:middle}.content-inner .detail h2.section-heading{margin:1.5em 0 .5em .3em}.content-inner .visible-xs{display:none!important}@media screen and (max-width:768px){.content-inner .visible-xs{display:block!important}}.content-inner img{max-width:100%}.content-inner .summary h2{font-weight:700}.content-inner .summary .summary-row .summary-signature{font-family:Inconsolata,Menlo,Courier,monospace;font-weight:700}.content-inner .summary .summary-row .summary-synopsis{font-family:Merriweather,serif;font-style:italic;padding:0 1.2em;margin:0 0 .5em}.content-inner .summary .summary-row .summary-synopsis p{margin:0;padding:0}.content-inner .detail-header{margin:2em 0 1em;padding:.5em 1em;background:#f7f7f7;border-left:3px solid #9768d1;font-size:1em;font-family:Inconsolata,Menlo,Courier,monospace;position:relative}.content-inner .detail-header .note{float:right}.content-inner .detail-header .signature{font-size:1rem;font-weight:700}.content-inner .detail-header:hover a.detail-link{opacity:1;text-decoration:none}.content-inner .detail-header a.detail-link{transition:opacity .3s ease-in-out;position:absolute;top:0;left:0;display:block;opacity:0;padding:.6em;line-height:1.5em;margin-left:-2.5em;background:0 0;border:none;text-shadow:none}.content-inner .footer .line,.search-results h1{display:inline-block}.content-inner .specs pre,.content-inner code{font-family:Inconsolata,Menlo,Courier,monospace;font-style:normal;line-height:24px}.content-inner .specs{opacity:.7;padding-bottom:.05em}.content-inner .specs pre{font-size:.9em;white-space:pre-wrap;margin:0;padding:0}.content-inner .docstring{margin:1.2em 0 2.1em 1.2em}.content-inner .docstring h2,.content-inner .docstring h3,.content-inner .docstring h4,.content-inner .docstring h5{font-weight:700}.content-inner .docstring h2{font-size:1em}.content-inner .docstring h3{font-size:.95em}.content-inner .docstring h4{font-size:.9em}.content-inner .docstring h5{font-size:.85em}.content-inner a.no-underline,.content-inner pre a{color:#9768d1;text-shadow:none;text-decoration:none;background-image:none}.content-inner a.no-underline:active,.content-inner a.no-underline:focus,.content-inner a.no-underline:hover,.content-inner a.no-underline:visited,.content-inner pre a:active,.content-inner pre a:focus,.content-inner pre a:hover,.content-inner pre a:visited{color:#9768d1;text-decoration:none}.content-inner code{font-weight:400;background-color:#f7f9fc;vertical-align:baseline;border-radius:2px;padding:.1em .2em}.content-inner pre{margin:1.5em 0}.content-inner pre.spec{margin:0}.content-inner pre.spec code{padding:0}.content-inner pre code.hljs{white-space:inherit;padding:.5em 1em;background-color:#f7f9fc}.content-inner .footer{margin:4em auto 1em;text-align:center;font-style:italic;font-size:14px;color:#959595}.content-inner .footer a{color:#959595;text-decoration:none;text-shadow:.03em 0 #fff,-.03em 0 #fff,0 .03em #fff,0 -.03em #fff,.06em 0 #fff,-.06em 0 #fff,.09em 0 #fff,-.09em 0 #fff,.12em 0 #fff,-.12em 0 #fff,.15em 0 #fff,-.15em 0 #fff;background-image:linear-gradient(#fff,#fff),linear-gradient(#fff,#fff),linear-gradient(#959595,#959595);background-size:.05em 1px,.05em 1px,1px 1px;background-repeat:no-repeat,no-repeat,repeat-x;background-position:0 90%,100% 90%,0 90%}.content-inner .footer a:selection{text-shadow:.03em 0 #b4d5fe,-.03em 0 #b4d5fe,0 .03em #b4d5fe,0 -.03em #b4d5fe,.06em 0 #b4d5fe,-.06em 0 #b4d5fe,.09em 0 #b4d5fe,-.09em 0 #b4d5fe,.12em 0 #b4d5fe,-.12em 0 #b4d5fe,.15em 0 #b4d5fe,-.15em 0 #b4d5fe;background:#b4d5fe}.content-inner .footer a:-moz-selection{text-shadow:.03em 0 #b4d5fe,-.03em 0 #b4d5fe,0 .03em #b4d5fe,0 -.03em #b4d5fe,.06em 0 #b4d5fe,-.06em 0 #b4d5fe,.09em 0 #b4d5fe,-.09em 0 #b4d5fe,.12em 0 #b4d5fe,-.12em 0 #b4d5fe,.15em 0 #b4d5fe,-.15em 0 #b4d5fe;background:#b4d5fe}.results .result-id a,.search-results a.close-search{text-shadow:none;background-image:none;transition:color .3s ease-in-out}.content-inner .footer a *,.content-inner .footer a :after,.content-inner .footer a :before,.content-inner .footer a:after,.content-inner .footer a:before{text-shadow:none}.content-inner .footer a:visited{color:#959595}.search-results a.close-search{display:inline-block;float:right}.search-results a.close-search:active,.search-results a.close-search:focus,.search-results a.close-search:visited{color:#000}.search-results a.close-search:hover{color:#9768d1}.results .result-id{font-size:1.2em}.results .result-id a:active,.results .result-id a:focus,.results .result-id a:visited{color:#000}.results .result-id a:hover{color:#9768d1}.results .result-elem em,.results .result-id em{font-style:normal;color:#9768d1}.results ul{margin:0;padding:0}.night-mode-toggle{background:0 0;border:none}.night-mode-toggle:after{font-size:12px;content:'Switch to night mode';text-decoration:underline}body.night-mode{background:#212127}body.night-mode .hljs-comment{color:#969896}body.night-mode .css .hljs-class,body.night-mode .css .hljs-id,body.night-mode .css .hljs-pseudo,body.night-mode .hljs-attribute,body.night-mode .hljs-regexp,body.night-mode .hljs-tag,body.night-mode .hljs-variable,body.night-mode .html .hljs-doctype,body.night-mode .ruby .hljs-constant,body.night-mode .xml .hljs-doctype,body.night-mode .xml .hljs-pi,body.night-mode .xml .hljs-tag .hljs-title{color:#c66}body.night-mode .hljs-built_in,body.night-mode .hljs-constant,body.night-mode .hljs-literal,body.night-mode .hljs-number,body.night-mode .hljs-params,body.night-mode .hljs-pragma,body.night-mode .hljs-preprocessor{color:#de935f}body.night-mode .css .hljs-rule .hljs-attribute,body.night-mode .ruby .hljs-class .hljs-title{color:#f0c674}body.night-mode .hljs-header,body.night-mode .hljs-inheritance,body.night-mode .hljs-name,body.night-mode .hljs-string,body.night-mode .hljs-value,body.night-mode .ruby .hljs-symbol,body.night-mode .xml .hljs-cdata{color:#b5bd68}body.night-mode .css .hljs-hexcolor,body.night-mode .hljs-title{color:#8abeb7}body.night-mode .coffeescript .hljs-title,body.night-mode .hljs-function,body.night-mode .javascript .hljs-title,body.night-mode .perl .hljs-sub,body.night-mode .python .hljs-decorator,body.night-mode .python .hljs-title,body.night-mode .ruby .hljs-function .hljs-title,body.night-mode .ruby .hljs-title .hljs-keyword{color:#81a2be}body.night-mode .hljs-keyword,body.night-mode .javascript .hljs-function{color:#b294bb}body.night-mode .hljs{display:block;overflow-x:auto;background:#1d1f21;color:#c5c8c6;padding:.5em;-webkit-text-size-adjust:none}body.night-mode .coffeescript .javascript,body.night-mode .javascript .xml,body.night-mode .tex .hljs-formula,body.night-mode .xml .css,body.night-mode .xml .hljs-cdata,body.night-mode .xml .javascript,body.night-mode .xml .vbscript{opacity:.5}body.night-mode .content-outer{background:#212127}body.night-mode .night-mode-toggle:after{color:#959595;content:'Switch to day mode';text-decoration:underline}body.night-mode .close-search:active,body.night-mode .close-search:focus,body.night-mode .close-search:visited,body.night-mode .results .result-id a:active,body.night-mode .results .result-id a:focus,body.night-mode .results .result-id a:visited{color:#D2D2D2}body.night-mode .close-search:hover,body.night-mode .results .result-id a:hover{color:#9768d1}body.night-mode .content-inner{color:#B4B4B4}body.night-mode .content-inner h1,body.night-mode .content-inner h2,body.night-mode .content-inner h3,body.night-mode .content-inner h4,body.night-mode .content-inner h5,body.night-mode .content-inner h6{color:#D2D2D2}body.night-mode .content-inner a{color:#D2D2D2;text-decoration:none;text-shadow:.03em 0 #212127,-.03em 0 #212127,0 .03em #212127,0 -.03em #212127,.06em 0 #212127,-.06em 0 #212127,.09em 0 #212127,-.09em 0 #212127,.12em 0 #212127,-.12em 0 #212127,.15em 0 #212127,-.15em 0 #212127;background-image:linear-gradient(#212127,#212127),linear-gradient(#212127,#212127),linear-gradient(#D2D2D2,#D2D2D2);background-size:.05em 1px,.05em 1px,1px 1px;background-repeat:no-repeat,no-repeat,repeat-x;background-position:0 90%,100% 90%,0 90%}body.night-mode .content-inner a:selection{text-shadow:.03em 0 #b4d5fe,-.03em 0 #b4d5fe,0 .03em #b4d5fe,0 -.03em #b4d5fe,.06em 0 #b4d5fe,-.06em 0 #b4d5fe,.09em 0 #b4d5fe,-.09em 0 #b4d5fe,.12em 0 #b4d5fe,-.12em 0 #b4d5fe,.15em 0 #b4d5fe,-.15em 0 #b4d5fe;background:#b4d5fe}body.night-mode .content-inner a:-moz-selection{text-shadow:.03em 0 #b4d5fe,-.03em 0 #b4d5fe,0 .03em #b4d5fe,0 -.03em #b4d5fe,.06em 0 #b4d5fe,-.06em 0 #b4d5fe,.09em 0 #b4d5fe,-.09em 0 #b4d5fe,.12em 0 #b4d5fe,-.12em 0 #b4d5fe,.15em 0 #b4d5fe,-.15em 0 #b4d5fe;background:#b4d5fe}body.night-mode .content-inner a *,body.night-mode .content-inner a :after,body.night-mode .content-inner a :before,body.night-mode .content-inner a:after,body.night-mode .content-inner a:before{text-shadow:none}body.night-mode .content-inner a:visited{color:#D2D2D2}body.night-mode .content-inner .summary h2 a,body.night-mode .content-inner a.view-source{background:0 0;text-shadow:none}body.night-mode .content-inner .detail-header{background:#3A4152;color:#D2D2D2}body.night-mode .content-inner code,body.night-mode .content-inner pre code.hljs{background-color:#2C2C31}body.night-mode .content-inner pre a{text-shadow:none;background-image:none}body.night-mode .content-inner .footer{color:#959595}body.night-mode .content-inner .footer .line{display:inline-block}body.night-mode .content-inner .footer a{color:#959595;text-shadow:none;background-image:none;text-decoration:underline}.night-mode .sidebar-toggle i{color:#d5dae6}@media print{#sidebar{display:none}} -------------------------------------------------------------------------------- /test_templates/project/doc/dist/sidebar_items-481e944cee.js: -------------------------------------------------------------------------------- 1 | sidebarNodes={"extras":[{"id":"api-reference","title":"API Reference","group":"","headers":[{"id":"Modules","anchor":"modules"}]}],"exceptions":[],"modules":[{"id":"Mix.Gen.Template.Project","title":"Mix.Gen.Template.Project","functions":[{"id":"name/0","anchor":"name/0"},{"id":"populate_assigns/2","anchor":"populate_assigns/2"},{"id":"short_desc/0","anchor":"short_desc/0"},{"id":"source_dir/0","anchor":"source_dir/0"}]}],"protocols":[]} -------------------------------------------------------------------------------- /test_templates/project/doc/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pragdave/mix_generator/d66de2428f02ad838d1ccbd802b5ce3e114eaadf/test_templates/project/doc/fonts/icomoon.eot -------------------------------------------------------------------------------- /test_templates/project/doc/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test_templates/project/doc/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pragdave/mix_generator/d66de2428f02ad838d1ccbd802b5ce3e114eaadf/test_templates/project/doc/fonts/icomoon.ttf -------------------------------------------------------------------------------- /test_templates/project/doc/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pragdave/mix_generator/d66de2428f02ad838d1ccbd802b5ce3e114eaadf/test_templates/project/doc/fonts/icomoon.woff -------------------------------------------------------------------------------- /test_templates/project/doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | gen_template_project v0.1.7 – Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test_templates/project/lib/project.ex: -------------------------------------------------------------------------------- 1 | defmodule Mix.Gen.Template.Project do 2 | 3 | @moduledoc File.read!(Path.join([__DIR__, "../README.md"])) 4 | 5 | use MixTemplates, 6 | name: :project, 7 | short_desc: "Simple template for projects (with optional app and supervision)", 8 | source_dir: "../template", 9 | options: [ 10 | sup: [ to: :is_supervisor?, default: false ], 11 | app: [ ], 12 | ] 13 | 14 | 15 | 16 | end 17 | -------------------------------------------------------------------------------- /test_templates/project/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Project.Mixfile do 2 | use Mix.Project 3 | 4 | @name :gen_template_project 5 | @version "0.1.8" 6 | 7 | @deps [ 8 | { :mix_templates, ">0.0.0", app: false }, 9 | { :ex_doc, ">0.0.0", only: [:dev, :test] }, 10 | ] 11 | 12 | @maintainers ["Dave Thomas "] 13 | @github "https://github.com/pragdave/#{@name}" 14 | 15 | @description """ 16 | A replacement for `mix new «project»` that generates files which 17 | I believe to be easier to read and maintain. 18 | """ 19 | 20 | 21 | ############################################################ 22 | 23 | def project do 24 | in_production = Mix.env == :prod 25 | [ 26 | app: @name, 27 | version: @version, 28 | elixir: "~> 1.4", 29 | deps: @deps, 30 | package: package(), 31 | description: @description, 32 | build_embedded: in_production, 33 | start_permanent: in_production, 34 | ] 35 | end 36 | 37 | defp package do 38 | [ 39 | name: @name, 40 | files: ["lib", "mix.exs", "README.md", "LICENSE.md", "template"], 41 | maintainers: @maintainers, 42 | licenses: ["Apache 2.0"], 43 | links: %{ 44 | "GitHub" => @github, 45 | }, 46 | # extra: %{ "type" => "a_template_for_mix_gen" }, # waiting for hex release 47 | ] 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /test_templates/project/template/$PROJECT_NAME$/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | -------------------------------------------------------------------------------- /test_templates/project/template/$PROJECT_NAME$/README.md: -------------------------------------------------------------------------------- 1 | # <%= @project_name_camel_case %> 2 | 3 | > TODO: Add description** 4 | 5 | 6 | ## Installation 7 | 8 | ```elixir 9 | @deps [ 10 | <%= @project_name %>: "~> 0.1.0" 11 | ] 12 | ``` 13 | 14 | ## License 15 | 16 | > TODO: Add license 17 | 18 | ---- 19 | Created: <%= @now.utc.date %>Z 20 | -------------------------------------------------------------------------------- /test_templates/project/template/$PROJECT_NAME$/config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | # config(:<%= @project_name %>, key: :value) 4 | # 5 | # And access this configuration in your application as: 6 | # 7 | # Application.get_env(:<%= @project_name %>, :key) 8 | # 9 | # Or configure a 3rd-party app: 10 | # 11 | # config(:logger, level: :info) 12 | # 13 | 14 | # Example per-environment config: 15 | # 16 | # import_config("#{Mix.env}.exs") 17 | -------------------------------------------------------------------------------- /test_templates/project/template/$PROJECT_NAME$/lib/$PROJECT_NAME$.ex: -------------------------------------------------------------------------------- 1 | defmodule <%= @project_name_camel_case %> do 2 | 3 | end 4 | -------------------------------------------------------------------------------- /test_templates/project/template/$PROJECT_NAME$/lib/$PROJECT_NAME$/application.ex: -------------------------------------------------------------------------------- 1 | <% 2 | # ------------------------------------------------------------ 3 | MixTemplates.ignore_file_and_directory_unless @is_supervisor? 4 | # ------------------------------------------------------------ 5 | %> 6 | defmodule <%= @project_name_camel_case %>.Application do 7 | 8 | @moduledoc false 9 | 10 | use Application # See http://elixir-lang.org/docs/stable/elixir/Application.html 11 | 12 | def start(_type, _args) do 13 | import Supervisor.Spec, warn: false 14 | 15 | children = [ 16 | # worker(One.Worker, [arg1, arg2, arg3]), 17 | ] 18 | 19 | opts = [ 20 | strategy: :one_for_one, 21 | name: <%= @project_name_camel_case %>.Supervisor 22 | ] 23 | 24 | Supervisor.start_link(children, opts) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test_templates/project/template/$PROJECT_NAME$/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule <%= @project_name_camel_case %>.Mixfile do 2 | use Mix.Project 3 | 4 | @name :<%= @project_name %> 5 | @version "0.1.0" 6 | 7 | @deps [ 8 | # { :earmark, ">0.1.5" }, 9 | # { :ex_doc, "1.2.3", only: [ :dev, :test ] } 10 | # { :my_app: path: "../my_app" }, 11 | ] 12 | 13 | # ------------------------------------------------------------ 14 | 15 | def project do 16 | in_production = Mix.env == :prod 17 | [ 18 | app: @name, 19 | version: @version, 20 | elixir: ">= <%= @elixir_version %>", 21 | <%= if @in_umbrella? do %> 22 | build_path: "../../_build", 23 | config_path: "../../config/config.exs", 24 | deps_path: "../../deps", 25 | lockfile: "../../mix.lock", 26 | start_permanent: in_production, 27 | <% else %> 28 | deps: @deps, 29 | build_embedded: in_production, 30 | <% end %> 31 | ] 32 | end 33 | 34 | def application do 35 | [ 36 | <%= if @is_supervisor? do %> 37 | mod: { <%= @project_name_camel_case %>.Application, [] }, # Entry point module and parameters 38 | <% end %> 39 | extra_applications: [ # built-in apps that need starting 40 | :logger 41 | ], 42 | ] 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /test_templates/project/template/$PROJECT_NAME$/test/$PROJECT_NAME$_test.exs: -------------------------------------------------------------------------------- 1 | defmodule <%= @project_name_camel_case %>Test do 2 | use ExUnit.Case 3 | # doctest <%= @project_name_camel_case %> 4 | 5 | end 6 | -------------------------------------------------------------------------------- /test_templates/project/template/$PROJECT_NAME$/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------