Skip to content

Commit

Permalink
Merge pull request #6 from benwilson512/modernize
Browse files Browse the repository at this point in the history
Modernize Vex
  • Loading branch information
benwilson512 committed Aug 30, 2014
2 parents d94d2b1 + 93b21b8 commit 3ebfa77
Show file tree
Hide file tree
Showing 24 changed files with 144 additions and 108 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ notifications:
recipients:
- [email protected]
otp_release:
- R16B
- 17.1
before_install:
- git clone https://github.com/elixir-lang/elixir
- cd elixir && make && cd ..
before_script: "export PATH=`pwd`/elixir/bin:$PATH"
script: "MIX_ENV=test mix do deps.get, test"
script: "MIX_ENV=test mix do deps.get, test"
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
THE SOFTWARE.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ options.
Ensure a value matches a regular expression:

```elixir
Vex.valid? widget, identifier: [format: %r(^id-)]
Vex.valid? widget, identifier: [format: ~r/(^id-)/]
```

This validation can be skipped for `nil` or blank values by including
Expand Down Expand Up @@ -259,7 +259,7 @@ defrecord User, username: nil, password: nil, password_confirmation: nil do

validates :username, presence: true,
length: [min: 4],
format: %r/^[[:alpha:]][[:alnum:]]+$/
format: ~r/^[[:alpha:]][[:alnum:]]+$/
validates :password, length: [min: 4],
confirmation: true
end
Expand Down Expand Up @@ -293,7 +293,7 @@ user = [username: "actualuser",
password_confirmation: "abcdefghi",
_vex: [username: [presence: true,
length: [min: 4],
format: %r/^[[:alpha:]][[:alnum:]]+$/]],
format: ~r/^[[:alpha:]][[:alnum:]]+$/]],
password: [length: [min: 4], confirmation: true]]
Vex.valid?(user)
```
Expand Down
13 changes: 6 additions & 7 deletions lib/vex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ defmodule Vex do
iex> Vex.validator(:presence)
Vex.Validators.Presence
iex> Vex.validator(:exclusion)
Vex.Validators.Exclusion
Vex.Validators.Exclusion
"""
def validator(name) do
case name |> validator(sources) do
Expand All @@ -73,22 +73,21 @@ defmodule Vex do
iex> Vex.validator(:presence, [Vex.Validators, [presence: :presence_stub]])
Vex.Validators.Presence
iex> Vex.validator(:presence, [[presence: :presence_stub], Vex.Validators])
:presence_stub
:presence_stub
"""
def validator(name, sources) do
Enum.find_value sources, fn (source) ->
Enum.find_value sources, fn (source) ->
Vex.Validator.Source.lookup(source, name)
end
end

defp sources do
Mix.project |> Keyword.get(:vex, []) |> Keyword.get(:sources, [Vex.Validators])
Mix.Project.get.project |> Keyword.get(:vex, []) |> Keyword.get(:sources, [Vex.Validators])
end

defp extract(data, attribute, :confirmation) do
[attribute, binary_to_atom("#{attribute}_confirmation")]
|>
Enum.map(fn (attr) -> Vex.Extract.attribute(data, attr) end)
[attribute, String.to_atom("#{attribute}_confirmation")]
|> Enum.map(fn (attr) -> Vex.Extract.attribute(data, attr) end)
end
defp extract(data, attribute, _name) do
Vex.Extract.attribute(data, attribute)
Expand Down
8 changes: 6 additions & 2 deletions lib/vex/blank.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defprotocol Vex.Blank do
@only [Atom, Tuple, List, BitString, Any]
@only [Atom, Tuple, List, BitString, Map, Any]
@doc "Whether an item is blank"
def blank?(value)
end
Expand Down Expand Up @@ -29,6 +29,10 @@ defimpl Vex.Blank, for: Atom do
def blank?(_), do: false
end

defimpl Vex.Blank, for: Map do
def blank?(map), do: map_size(map) == 0
end

defimpl Vex.Blank, for: Any do
def blank?(_), do: false
end
end
6 changes: 4 additions & 2 deletions lib/vex/exceptions.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
defexception Vex.InvalidValidatorError, [file: nil, line: nil, validator: nil, sources: []] do
defmodule Vex.InvalidValidatorError do
defexception [file: nil, line: nil, validator: nil, sources: []]

def message(exception) do
"#{Exception.format_file_line(exception.file, exception.line)}validator #{inspect exception.validator} from sources #{inspect exception.sources}"
end
end
end
29 changes: 25 additions & 4 deletions lib/vex/extract.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defprotocol Vex.Extract do

@doc "Extract an attribute's value"
def attribute(data, name)

end

defimpl Vex.Extract, for: List do
Expand All @@ -17,13 +17,34 @@ defimpl Vex.Extract, for: List do
end
end

defmodule Vex.Extract.Struct do
defmacro for_struct do
quote do
defimpl Vex.Blank, for: __MODULE__ do
def blank?(struct), do: (struct |> Map.from_struct |> map_size) == 0
end

defimpl Vex.Extract, for: __MODULE__ do
def settings(%{__struct__: module}) do
module.__vex_validations__
end

def attribute(map, name) do
Map.get(map, name)
end
end
end
end
end

defimpl Vex.Extract, for: Tuple do
def settings(record) do
[name | _tail] = tuple_to_list(record)
[name | _tail] = Tuple.to_list(record)
record_validations(name)
end

def attribute(record, attribute) do
[name | _tail] = tuple_to_list(record)
[name | _tail] = Tuple.to_list(record)
case record_attribute_index(name, attribute) do
nil -> nil
number when is_integer(number) -> elem(record, number)
Expand All @@ -44,6 +65,6 @@ defimpl Vex.Extract, for: Tuple do
rescue
_ -> nil
end
end
end

end
9 changes: 6 additions & 3 deletions lib/vex/record.ex → lib/vex/struct.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule Vex.Record do
defmodule Vex.Struct do

defmacro __using__(_) do
quote do
Expand All @@ -11,11 +11,14 @@ defmodule Vex.Record do

defmacro __before_compile__(_) do
quote do
def __record__(:vex_validations), do: @vex_validations
def __vex_validations__(), do: @vex_validations

require Vex.Extract.Struct
Vex.Extract.Struct.for_struct
end
end

defmacro validates(name, validations // []) do
defmacro validates(name, validations \\ []) do
quote do
@vex_validations Keyword.put(@vex_validations, unquote(name), unquote(validations))
end
Expand Down
6 changes: 3 additions & 3 deletions lib/vex/validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ defmodule Vex.Validator do
iex> Vex.Validator.validate?([name: "foo"], if: &(&1[:name] != "foo"))
false
iex> Vex.Validator.validate?([name: "foo"], unless: &(&1[:name] != "foo"))
true
true
"""
def validate?(data, options) when is_list(options) do
cond do
Expand All @@ -62,11 +62,11 @@ defmodule Vex.Validator do
end
defp validate_if(data, condition) when is_function(condition) do
!!condition.(data)
end
end

defp do_validate_if_condition(data, {name, value}) when is_atom(name) do
Vex.Extract.attribute(data, name) == value
end
end
defp do_validate_if_condition(data, condition) when is_atom(condition) do
!Vex.Blank.blank?(Vex.Extract.attribute(data, condition))
end
Expand Down
8 changes: 4 additions & 4 deletions lib/vex/validator/source.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defprotocol Vex.Validator.Source do

def lookup(source, name)

end
Expand All @@ -9,7 +9,7 @@ defimpl Vex.Validator.Source, for: Atom do
import Mix.Utils, only: [camelize: 1]

def lookup(source, name) do
validator_by_function(source, name) || validator_by_structure(source, name)
validator_by_function(source, name) || validator_by_structure(source, name)
end

defp validator_by_function(source, name) do
Expand All @@ -20,7 +20,7 @@ defimpl Vex.Validator.Source, for: Atom do
end

defp validator_by_structure(source, name) do
check Module.concat(source, camelize(atom_to_binary(name)))
check Module.concat(source, camelize(Atom.to_string(name)))
end

defp check(validator) do
Expand All @@ -38,4 +38,4 @@ defimpl Vex.Validator.Source, for: List do
Keyword.get(list, name)
end

end
end
2 changes: 1 addition & 1 deletion lib/vex/validators/acceptance.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ defmodule Vex.Validators.Acceptance do
iex> Vex.Validators.Acceptance.validate(nil, message: "must be accepted!")
{:error, "must be accepted!"}
iex> Vex.Validators.Acceptance.validate(1, [as: "yes"])
{:error, %s(must be accepted with `"yes"`)}
{:error, ~S(must be accepted with `"yes"`)}
iex> Vex.Validators.Acceptance.validate("verily", [as: "verily"])
:ok
Expand Down
8 changes: 4 additions & 4 deletions lib/vex/validators/by.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ defmodule Vex.Validators.By do
iex> Vex.Validators.By.validate({}, [function: &is_list/1, allow_blank: true])
:ok
iex> Vex.Validators.By.validate([1], [function: &is_list/1, message: "must be a list"])
:ok
:ok
iex> Vex.Validators.By.validate("a", [function: &is_list/1, message: "must be a list"])
{:error, "must be a list"}
Expand All @@ -43,10 +43,10 @@ defmodule Vex.Validators.By do
An example:
iex> Vex.Validators.By.validate("blah", [function: &is_list/1, message: "<%= inspect value %> isn't a list"])
{:error, %s("blah" isn't a list)}
{:error, ~S("blah" isn't a list)}
"""
use Vex.Validator

@message_fields [value: "The bad value"]
def validate(value, func) when is_function(func), do: validate(value, function: func)
def validate(value, options) when is_list(options) do
Expand All @@ -59,4 +59,4 @@ defmodule Vex.Validators.By do
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/vex/validators/confirmation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ defmodule Vex.Validators.Confirmation do
An example:
iex> Vex.Validators.Confirmation.validate(["foo", nil], message: "<%= inspect confirmation %> doesn't match <%= inspect value %>")
{:error, %s(nil doesn't match "foo")}
{:error, ~S(nil doesn't match "foo")}
"""
use Vex.Validator
Expand Down
14 changes: 7 additions & 7 deletions lib/vex/validators/exclusion.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ defmodule Vex.Validators.Exclusion do
iex> Vex.Validators.Exclusion.validate(1, [in: [1, 2, 3]])
{:error, "must not be one of [1, 2, 3]"}
iex> Vex.Validators.Exclusion.validate(1, [in: [1, 2, 3], message: "<%= value %> shouldn't be in <%= inspect list %>"])
{:error, "1 shouldn't be in [1, 2, 3]"}
{:error, "1 shouldn't be in [1, 2, 3]"}
iex> Vex.Validators.Exclusion.validate(4, [1, 2, 3])
:ok
iex> Vex.Validators.Exclusion.validate("a", %w(a b c))
{:error, %s(must not be one of ["a", "b", "c"])}
iex> Vex.Validators.Exclusion.validate("a", in: %w(a b c), message: "must not be abc, talkin' 'bout 123")
iex> Vex.Validators.Exclusion.validate("a", ~w(a b c))
{:error, ~S(must not be one of ["a", "b", "c"])}
iex> Vex.Validators.Exclusion.validate("a", in: ~w(a b c), message: "must not be abc, talkin' 'bout 123")
{:error, "must not be abc, talkin' 'bout 123"}
## Custom Error Messages
Expand All @@ -34,8 +34,8 @@ defmodule Vex.Validators.Exclusion do
An example:
iex> Vex.Validators.Exclusion.validate("a", in: %w(a b c), message: "<%= inspect value %> is a disallowed value")
{:error, %s("a" is a disallowed value)}
iex> Vex.Validators.Exclusion.validate("a", in: ~w(a b c), message: "<%= inspect value %> is a disallowed value")
{:error, ~S("a" is a disallowed value)}
"""

use Vex.Validator
Expand All @@ -56,4 +56,4 @@ defmodule Vex.Validators.Exclusion do
defp result(true, _), do: :ok
defp result(false, message), do: {:error, message}

end
end
28 changes: 16 additions & 12 deletions lib/vex/validators/format.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@ defmodule Vex.Validators.Format do
* `:with`: The regular expression.
* `:message`: Optional. A custom error message. May be in EEx format
and use the fields described in "Custom Error Messages," below.
and use the fields described in "Custom Error Messages," below.
The regular expression can be provided in place of the keyword list if no other options
are needed.
## Examples
iex> Vex.Validators.Format.validate("foo", %r"^f")
iex> Vex.Validators.Format.validate("foo", ~r/^f/)
:ok
iex> Vex.Validators.Format.validate("foo", %r"o{3,}")
iex> Vex.Validators.Format.validate("foo", ~r/o{3,}/)
{:error, "must have the correct format"}
iex> Vex.Validators.Format.validate("foo", [with: %r"^f"])
iex> Vex.Validators.Format.validate("foo", [with: ~r/^f/])
:ok
iex> Vex.Validators.Format.validate("bar", [with: %r"^f", message: "must start with an f"])
{:error, "must start with an f"}
iex> Vex.Validators.Format.validate("", [with: %r"^f", allow_blank: true])
iex> Vex.Validators.Format.validate("bar", [with: ~r/^f/, message: "must start with an f"])
{:error, "must start with an f"}
iex> Vex.Validators.Format.validate("", [with: ~r/^f/, allow_blank: true])
:ok
iex> Vex.Validators.Format.validate(nil, [with: %r"^f", allow_nil: true])
iex> Vex.Validators.Format.validate(nil, [with: ~r/^f/, allow_nil: true])
:ok
## Custom Error Messages
Expand All @@ -35,22 +35,26 @@ defmodule Vex.Validators.Format do
An example:
iex> Vex.Validators.Format.validate("bar", [with: %r"^f", message: "<%= value %> doesn't start with an f"])
iex> Vex.Validators.Format.validate("bar", [with: ~r/"^f"/, message: "<%= value %> doesn't start with an f"])
{:error, "bar doesn't start with an f"}
"""
use Vex.Validator

@message_fields [value: "The bad value"]
def validate(value, format) when is_regex(format), do: validate(value, with: format)
def validate(value, options) do
def validate(value, options) when is_list(options) do
unless_skipping(value, options) do
pattern = Keyword.get(options, :with)
result Regex.match?(pattern, value), message(options, "must have the correct format",
value: value)
end
end

def validate(value, format) do
if Regex.regex?(format), do: validate(value, with: format)
end


defp result(true, _), do: :ok
defp result(false, message), do: {:error, message}

end
end
Loading

0 comments on commit 3ebfa77

Please sign in to comment.