detox 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +19 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +40 -0
  8. data/Rakefile +11 -0
  9. data/detox.gemspec +27 -0
  10. data/gemfiles/activemodel_3_2_x.gemfile +5 -0
  11. data/gemfiles/activemodel_4_0_x.gemfile +5 -0
  12. data/lib/active_model/validations/absence.rb +19 -0
  13. data/lib/detox.rb +11 -0
  14. data/lib/detox/array_validity.rb +23 -0
  15. data/lib/detox/locales/en.yml +21 -0
  16. data/lib/detox/locales/ja.yml +21 -0
  17. data/lib/detox/validations/all.rb +41 -0
  18. data/lib/detox/validations/any.rb +57 -0
  19. data/lib/detox/validations/ordering.rb +34 -0
  20. data/lib/detox/validations/possession.rb +30 -0
  21. data/lib/detox/validity_broker.rb +55 -0
  22. data/lib/detox/version.rb +4 -0
  23. data/spec/detox/array_validity_spec.rb +214 -0
  24. data/spec/detox/validations/all_absence_spec.rb +147 -0
  25. data/spec/detox/validations/all_acceptance_spec.rb +352 -0
  26. data/spec/detox/validations/all_exclusion_spec.rb +137 -0
  27. data/spec/detox/validations/all_format_spec.rb +351 -0
  28. data/spec/detox/validations/all_inclusion_spec.rb +352 -0
  29. data/spec/detox/validations/all_length_spec.rb +388 -0
  30. data/spec/detox/validations/all_numericality_spec.rb +369 -0
  31. data/spec/detox/validations/all_presence_spec.rb +159 -0
  32. data/spec/detox/validations/any_absence_spec.rb +597 -0
  33. data/spec/detox/validations/any_acceptance_spec.rb +597 -0
  34. data/spec/detox/validations/any_exclusion_spec.rb +597 -0
  35. data/spec/detox/validations/any_format_spec.rb +597 -0
  36. data/spec/detox/validations/any_inclusion_spec.rb +597 -0
  37. data/spec/detox/validations/any_length_spec.rb +597 -0
  38. data/spec/detox/validations/any_numericality_spec.rb +609 -0
  39. data/spec/detox/validations/any_presence_spec.rb +596 -0
  40. data/spec/detox/validations/any_validator_check_validity_spec.rb +126 -0
  41. data/spec/detox/validations/ordering_spec.rb +452 -0
  42. data/spec/detox/validations/possession_spec.rb +263 -0
  43. data/spec/detox/validity_broker_spec.rb +126 -0
  44. data/spec/spec_helper.rb +24 -0
  45. data/spec/test_class/all_absence_test.rb +14 -0
  46. data/spec/test_class/all_acceptance_test.rb +27 -0
  47. data/spec/test_class/all_exclusion_test.rb +15 -0
  48. data/spec/test_class/all_format_test.rb +26 -0
  49. data/spec/test_class/all_inclusion_test.rb +27 -0
  50. data/spec/test_class/all_length_test.rb +28 -0
  51. data/spec/test_class/all_numericality_test.rb +33 -0
  52. data/spec/test_class/all_presence_test.rb +14 -0
  53. data/spec/test_class/any_absence_test.rb +33 -0
  54. data/spec/test_class/any_acceptance_test.rb +34 -0
  55. data/spec/test_class/any_exclusion_test.rb +34 -0
  56. data/spec/test_class/any_format_test.rb +34 -0
  57. data/spec/test_class/any_inclusion_test.rb +33 -0
  58. data/spec/test_class/any_length_test.rb +33 -0
  59. data/spec/test_class/any_numericality_test.rb +35 -0
  60. data/spec/test_class/any_presence_test.rb +33 -0
  61. data/spec/test_class/array_validity_test.rb +6 -0
  62. data/spec/test_class/model_base.rb +11 -0
  63. data/spec/test_class/ordering_test.rb +33 -0
  64. data/spec/test_class/possession_test.rb +22 -0
  65. metadata +219 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4178ff8b870484f57cd8c4677afac7b1968526da
4
+ data.tar.gz: 682e7b0e9665a4b1bed0571aa24153260ca0ac76
5
+ SHA512:
6
+ metadata.gz: 6eed3e98b13ec01e85532664dd2b5319ecb112b54939d859b4da47ec26282cc01127d38a52cdfaaaf536eb18001bf70d7c2080a110c5813d8476a6e733237768
7
+ data.tar.gz: 8c449e6d0aa73fa2022aca80648fe43f5da3c05b3ac56fda822c1f21caa3d3b75a7150c5f616ce81b57ab998f186882f1618c22e9b7b1b1eee3902c2a842a6af
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor/bundle
19
+ coverage
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ gemfile:
6
+ - gemfiles/activemodel_3_2_x.gemfile
7
+ - gemfiles/activemodel_4_0_x.gemfile
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in detox.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 pinzolo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,40 @@
1
+ # Detox
2
+
3
+ [![Build Status](https://secure.travis-ci.org/pinzolo/detox.png)](http://travis-ci.org/pinzolo/detox)
4
+ [![Coverage Status](https://coveralls.io/repos/pinzolo/detox/badge.png)](https://coveralls.io/r/pinzolo/detox)
5
+ [![Code Climate](https://codeclimate.com/github/pinzolo/detox.png)](https://codeclimate.com/github/pinzolo/detox)
6
+
7
+ Detox is validator set that can apply to ActiveModel/ActiveRecord
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'detox'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install detox
22
+
23
+ ## Usage
24
+
25
+ Look [spec/test_class](https://github.com/pinzolo/detox/tree/master/spec/test_class) for sample usage
26
+
27
+ - Validators for attribute that has Array or Hash value
28
+ - **ordering**: validates continuous numbers
29
+ - **possession**: validates subset values
30
+ - **all_xxx**: validates that all value are valid, by basic validators (xxx is presence, format etc.)
31
+ - **any_xxx**: validates that has valid value, by basic validators (xxx is presence, format etc.)
32
+
33
+
34
+ ## Contributing
35
+
36
+ 1. Fork it
37
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
38
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
39
+ 4. Push to the branch (`git push origin my-new-feature`)
40
+ 5. Create new Pull Request
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => [:spec]
4
+ begin
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = 'spec/**/*_spec.rb'
8
+ spec.rspec_opts = ['--color']
9
+ end
10
+ rescue LoadError => e
11
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'detox/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "detox"
8
+ spec.version = Detox::VERSION
9
+ spec.authors = ["pinzolo"]
10
+ spec.email = ["pinzolo@gmail.com"]
11
+ spec.description = %q{Detox is collection of validators for AciveModel and AcitveRecord.}
12
+ spec.summary = spec.description
13
+ spec.homepage = "https://github.com/pinzolo/detox"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "activemodel", ">=3.2.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "coveralls"
27
+ end
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "activemodel", "~>3.2.0"
4
+
5
+ gemspec :path => "../"
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "activemodel", "~>4.0.0"
4
+
5
+ gemspec :path => "../"
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+
3
+ # via: activemodel/lib/active_model/validations/absence.rb (4.0-stable)
4
+ # https://github.com/rails/rails/blob/4-0-stable/activemodel/lib/active_model/validations/absence.rb
5
+ module ActiveModel
6
+ module Validations
7
+ class AbsenceValidator < EachValidator
8
+ def validate_each(record, attr_name, value)
9
+ record.errors.add(attr_name, :present, options) if value.present?
10
+ end
11
+ end
12
+
13
+ module HelperMethods
14
+ def validates_absence_of(*attr_names)
15
+ validates_with AbsenceValidator, _merge_attributes(attr_names)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ # coding: utf-8
2
+ require "active_model"
3
+ require "active_support/i18n"
4
+ require "detox/validations/ordering"
5
+ require "detox/validations/possession"
6
+ require "detox/validations/all"
7
+ require "detox/validations/any"
8
+
9
+ ActiveModel::Validations.__send__(:include, Detox::Validations)
10
+ I18n.load_path << Dir[Pathname.new(File.dirname(__FILE__)).join("detox", "locales", "*.yml")]
11
+
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ require "active_support/core_ext"
3
+
4
+ module Detox
5
+ module ArrayValidity
6
+ RESERVED_OPTIONS = [:ignore_nil_value, :ignore_blank_value].freeze
7
+
8
+ def convert_to_validatee(data, options = {})
9
+ values = if data.nil?
10
+ []
11
+ elsif data.is_a?(Array)
12
+ data.dup
13
+ elsif data.is_a?(Hash)
14
+ data.values
15
+ else
16
+ [data]
17
+ end
18
+ values = values.compact if options[:ignore_nil_value]
19
+ values = values.reject(&:blank?) if options[:ignore_blank_value]
20
+ values
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ invalid_as_order: "is not satisfied as order"
5
+ possession: "must possess target (target: %{target})"
6
+ all_presence: "can't contain blank value"
7
+ all_absence: "can't contain not blank value"
8
+ all_format: "contains invalid format value"
9
+ all_numericality: "contains not numeric value"
10
+ all_length: "contains invalid length value"
11
+ all_inclusion: "contains value that is not included in the list"
12
+ all_exclusion: "contains value that is included in the list"
13
+ all_acceptance: "must be all accepted"
14
+ any_presence: "must contain not blank value (required number is %{min_valid_count}-%{max_valid_count})"
15
+ any_absence: "must contain blank value (required number is %{min_valid_count}-%{max_valid_count})"
16
+ any_acceptance: "must be accepted (required number is %{min_valid_count}-%{max_valid_count})"
17
+ any_format: "must be valid format (required number is %{min_valid_count}-%{max_valid_count})"
18
+ any_length: "must be valid length (required number is %{min_valid_count}-%{max_valid_count})"
19
+ any_numericality: "must be numeric (required number is %{min_valid_count}-%{max_valid_count})"
20
+ any_inclusion: "must be included in the list (required number is %{min_valid_count}-%{max_valid_count})"
21
+ any_exclusion: "must be not included in the list (required number is %{min_valid_count}-%{max_valid_count})"
@@ -0,0 +1,21 @@
1
+ ja:
2
+ errors:
3
+ messages:
4
+ invalid_as_order: "は順序として成立していません"
5
+ possession: "には対象(%{target})が含まれている必要があります"
6
+ all_presence: "には空の値を含められません"
7
+ all_absence: "には空ではない値を含められません"
8
+ all_format: "には不正な形式の値が含まれています"
9
+ all_numericality: "は数値でない値を含んでいます"
10
+ all_length: "には長さが不正な値が含まれています"
11
+ all_inclusion: "にはリストに含まれていない値が含まれています"
12
+ all_exclusion: "にはリストに含まれている値が含まれています"
13
+ all_acceptance: "を全て承諾してください"
14
+ any_presence: "には空でない値が必要数(%{min_valid_count}~%{max_valid_count})含まれている必要があります"
15
+ any_absence: "には空の値が必要数(%{min_valid_count}~%{max_valid_count})含まれている必要があります"
16
+ any_acceptance: "は指定件数(%{min_valid_count}~%{max_valid_count})承諾してください"
17
+ any_format: "は必要数(%{min_valid_count}~%{max_valid_count})が正しい形式でなければなりません"
18
+ any_length: "は必要数(%{min_valid_count}~%{max_valid_count})が正しい長さでなければなりません"
19
+ any_numericality: "は必要数(%{min_valid_count}~%{max_valid_count})が数値でなければなりません"
20
+ any_inclusion: "は必要数(%{min_valid_count}~%{max_valid_count})がリストに含まれていなければなりません"
21
+ any_exclusion: "は必要数(%{min_valid_count}~%{max_valid_count})がリストに含まれていない値でなければなりません"
@@ -0,0 +1,41 @@
1
+ # coding: utf-8
2
+ require "active_support/core_ext"
3
+ require "detox/array_validity"
4
+ require "detox/validity_broker"
5
+
6
+ unless defined?(ActiveModel::Validations::AbsenceValidator)
7
+ require "active_model/validations/absence"
8
+ end
9
+
10
+ ignore_constants = [:ConfirmationValidator, :WithValidator]
11
+ validators_source = ActiveModel::Validations.constants.each_with_object("") do |const, src|
12
+ next if ignore_constants.include?(const)
13
+ next if Detox::Validations.constants.include?("All#{const}".to_sym)
14
+
15
+ src << <<-EOS
16
+ class All#{const} < ActiveModel::EachValidator
17
+ include Detox::ArrayValidity
18
+
19
+ def validate_each(record, attribute, value)
20
+ values = convert_to_validatee(value, options.slice(*Detox::ArrayValidity::RESERVED_OPTIONS))
21
+ return if values.blank?
22
+
23
+ new_options = options.dup.except(*Detox::ArrayValidity::RESERVED_OPTIONS).merge(:attributes => [attribute])
24
+ validator = ActiveModel::Validations::#{const}.new(new_options)
25
+ broker = Detox::ValidityBroker.new
26
+ validity = values.all? do |v|
27
+ broker.validatee = v
28
+ validator.validate(broker)
29
+ broker.valid?
30
+ end
31
+ unless validity
32
+ message = options[:message] || :all_#{const.to_s.underscore.gsub(/_validator\z/, "")}
33
+ record.errors.add(attribute, message)
34
+ end
35
+ end
36
+ end
37
+ EOS
38
+ end
39
+
40
+ Detox::Validations.module_eval(validators_source)
41
+
@@ -0,0 +1,57 @@
1
+ # coding: utf-8
2
+ require "active_support/core_ext"
3
+ require "detox/array_validity"
4
+ require "detox/validity_broker"
5
+
6
+ ignore_constants = [:ConfirmationValidator, :WithValidator]
7
+ validators_source = ActiveModel::Validations.constants.each_with_object("") do |const, src|
8
+ next if ignore_constants.include?(const)
9
+ next if Detox::Validations.constants.include?("Any#{const}".to_sym)
10
+
11
+ src << <<-EOS
12
+ class Any#{const} < ActiveModel::EachValidator
13
+ include Detox::ArrayValidity
14
+
15
+ def check_validity!
16
+ min_valid_count, max_valid_count = options[:min_valid_count], options[:max_valid_count]
17
+ if (min_valid_count != nil && !min_valid_count.is_a?(Integer)) ||
18
+ (max_valid_count != nil && !max_valid_count.is_a?(Integer)) ||
19
+ (min_valid_count != nil && min_valid_count <= 0) ||
20
+ (max_valid_count != nil && max_valid_count <= 0)
21
+ raise ArgumentError, ":min_valid_count and :max_valid_count must be a positive integer ( greater than 0 ) or nil"
22
+ end
23
+ if min_valid_count != nil && max_valid_count != nil && min_valid_count > max_valid_count
24
+ raise ArgumentError, ":min_valid_count must be less than or equal to :max_valid_count"
25
+ end
26
+ end
27
+
28
+ def validate_each(record, attribute, value)
29
+ values = convert_to_validatee(value)
30
+ return if values.blank?
31
+
32
+ new_options = options.dup.merge(:attributes => [attribute])
33
+ validator = ActiveModel::Validations::#{const}.new(new_options)
34
+ broker = Detox::ValidityBroker.new
35
+ valid_count = values.count do |v|
36
+ broker.validatee = v
37
+ validator.validate(broker)
38
+ broker.valid?
39
+ end
40
+
41
+ unless count_valid?(valid_count)
42
+ message = options[:message] || :any_#{const.to_s.underscore.gsub(/_validator\z/, "")}
43
+ record.errors.add(attribute, message, { :min_valid_count => 1, :max_valid_count => "" }.merge(options))
44
+ end
45
+ end
46
+
47
+ private
48
+ def count_valid?(valid_count)
49
+ (options[:min_valid_count] || 1) <= valid_count &&
50
+ (options[:max_valid_count].nil? || valid_count <= options[:max_valid_count])
51
+ end
52
+ end
53
+ EOS
54
+ end
55
+
56
+ Detox::Validations.module_eval(validators_source)
57
+
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ require "active_support/core_ext"
3
+ require "detox/array_validity"
4
+
5
+ module Detox::Validations
6
+ class OrderingValidator < ActiveModel::EachValidator
7
+ include Detox::ArrayValidity
8
+
9
+ def validate_each(record, attribute, value)
10
+ values = convert_to_validatee(value, options.slice(*Detox::ArrayValidity::RESERVED_OPTIONS))
11
+ return if values.blank?
12
+
13
+ unless values_valid?(values)
14
+ message = options[:message] || :invalid_as_order
15
+ record.errors.add(attribute, message)
16
+ end
17
+ end
18
+
19
+ private
20
+ def values_valid?(values)
21
+ valid_numbers(values.length).map(&:to_s).sort == values.map(&:to_s).sort
22
+ end
23
+
24
+ def valid_numbers(length)
25
+ last_number = first_number + length
26
+ (first_number...last_number).to_a
27
+ end
28
+
29
+ def first_number
30
+ num = options[:start_with].to_i
31
+ num.zero? ? 1 : num
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ require "detox/array_validity"
3
+
4
+ module Detox::Validations
5
+ class PossessionValidator < ActiveModel::EachValidator
6
+ include Detox::ArrayValidity
7
+
8
+ ERROR_MESSAGE = ":target must be supplied".freeze
9
+
10
+ def validate_each(record, attribute, value)
11
+ values = convert_to_validatee(value, options.slice(*Detox::ArrayValidity::RESERVED_OPTIONS))
12
+ return if values.blank?
13
+
14
+ unless values_valid?(values)
15
+ message = options[:message] || :possession
16
+ record.errors.add(attribute, message, options.merge(:target => [options[:target]].flatten.join(", ")))
17
+ end
18
+ end
19
+
20
+ def check_validity!
21
+ raise ArgumentError, ERROR_MESSAGE if options[:target].blank?
22
+ end
23
+
24
+ private
25
+ def values_valid?(values)
26
+ [options[:target]].flatten.all? { |t| values.include?(t) }
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+ module Detox
3
+ class ValidityBroker
4
+ attr_reader :errors
5
+ attr_accessor :validatee
6
+
7
+ def initialize
8
+ @validity = true
9
+ @errors = Errors.new(self)
10
+ end
11
+
12
+ def valid?
13
+ value = @validity
14
+ validitize
15
+ value
16
+ end
17
+
18
+ def invalid?
19
+ value = @validity
20
+ validitize
21
+ !value
22
+ end
23
+
24
+ def validitize
25
+ @validity = true
26
+ end
27
+
28
+ def invaliditize
29
+ @validity = false
30
+ end
31
+
32
+ def read_attribute_for_validation(attribute)
33
+ validatee
34
+ end
35
+
36
+ class Errors
37
+ def initialize(broker)
38
+ @broker = broker
39
+ end
40
+
41
+ def add(*inane_args)
42
+ @broker.invaliditize
43
+ end
44
+
45
+ # for support PresenceValidator (version 3.2.x)
46
+ def add_on_blank(*inane_args)
47
+ add(*inane_args) if @broker.validatee.blank?
48
+ end
49
+
50
+ def clear
51
+ @broker.validitize
52
+ end
53
+ end
54
+ end
55
+ end