activemodel-interdependence 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +97 -0
- data/.rspec +4 -0
- data/.rubocop.yml +208 -0
- data/.rubocop_todo.yml +34 -0
- data/.yardopts +1 -0
- data/Gemfile +83 -0
- data/Gemfile.lock +336 -0
- data/Guardfile +68 -0
- data/LICENSE +21 -0
- data/README.md +76 -0
- data/Rakefile +95 -0
- data/activemodel-interdependence.gemspec +34 -0
- data/circle.yml +7 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +9 -0
- data/config/reek.yml +120 -0
- data/config/rubocop.yml +1 -0
- data/config/yardstick.yml +2 -0
- data/lib/activemodel/interdependence.rb +12 -0
- data/lib/activemodel/model/interdependence.rb +97 -0
- data/lib/activemodel/validator/interdependence.rb +107 -0
- data/lib/interdependence.rb +87 -0
- data/lib/interdependence/activemodel/class_methods.rb +50 -0
- data/lib/interdependence/activemodel/validates_with.rb +128 -0
- data/lib/interdependence/common_mixin.rb +84 -0
- data/lib/interdependence/dependency/base.rb +177 -0
- data/lib/interdependence/dependency/model.rb +61 -0
- data/lib/interdependence/dependency/validator.rb +43 -0
- data/lib/interdependence/dependency_resolver/base.rb +114 -0
- data/lib/interdependence/dependency_resolver/model.rb +76 -0
- data/lib/interdependence/dependency_resolver/validator.rb +34 -0
- data/lib/interdependence/dependency_set.rb +15 -0
- data/lib/interdependence/dependency_set_graph.rb +66 -0
- data/lib/interdependence/graph.rb +103 -0
- data/lib/interdependence/model.rb +70 -0
- data/lib/interdependence/model/validator.rb +99 -0
- data/lib/interdependence/observable_dependency_set_graph.rb +23 -0
- data/lib/interdependence/types.rb +199 -0
- data/lib/interdependence/validator.rb +67 -0
- data/lib/interdependence/validator/validator.rb +105 -0
- data/lib/interdependence/version.rb +3 -0
- metadata +213 -0
data/Guardfile
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'guard'
|
2
|
+
|
3
|
+
ignore /tmp/, /doc/
|
4
|
+
|
5
|
+
guard :bundler do
|
6
|
+
watch('Gemfile')
|
7
|
+
watch('*.gemspec')
|
8
|
+
end
|
9
|
+
|
10
|
+
group :documentation do
|
11
|
+
guard :yardstick do
|
12
|
+
watch(%r{^(lib\/(.+)\.rb)$}) { |m| m[0] }
|
13
|
+
end
|
14
|
+
|
15
|
+
# Critique documentation (visual help and recommendations)
|
16
|
+
guard :inch do
|
17
|
+
watch(/.+\.rb/)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Build documentation
|
22
|
+
guard :yard, cli: '--plugin virtus' do
|
23
|
+
watch(%r{app/.+\.rb})
|
24
|
+
watch(%r{lib/.+\.rb})
|
25
|
+
watch(%r{ext/.+\.c})
|
26
|
+
end
|
27
|
+
|
28
|
+
group :critique do
|
29
|
+
# Critique code smells
|
30
|
+
guard :reek, cli: %w(-c config/reek.yml), run_all_with: ['lib'] do
|
31
|
+
watch(%r{.+\.rb$}) { 'lib' }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Critique the most painful code
|
35
|
+
guard :flog do
|
36
|
+
watch(%r{^lib/(.+)\.rb$})
|
37
|
+
|
38
|
+
# Rails example
|
39
|
+
watch(%r{^app/(.+)\.rb$})
|
40
|
+
end
|
41
|
+
|
42
|
+
# Critique code for structural similarities
|
43
|
+
guard :flay do
|
44
|
+
watch(%r{^lib/(.+)\.rb$})
|
45
|
+
|
46
|
+
# Rails example
|
47
|
+
watch(%r{^app/(.+)\.rb$})
|
48
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$})
|
49
|
+
watch('config/routes.rb')
|
50
|
+
|
51
|
+
# Flay specs
|
52
|
+
watch(%r{^spec/(.+)_spec\.rb$})
|
53
|
+
end
|
54
|
+
|
55
|
+
# Critique code style
|
56
|
+
guard :rubocop do
|
57
|
+
watch(%r{(.+\.rb)$}) { |m| m[0] }
|
58
|
+
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# RSpec is often the longest running guard so we put it last
|
63
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
64
|
+
# watch(%r(.*$)) { 'spec' }
|
65
|
+
watch(%r{^spec/.+_spec\.rb$})
|
66
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
67
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
68
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013-2015 BlockScore
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all 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,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# ActiveModel Interdependence
|
2
|
+
|
3
|
+
[](https://circleci.com/gh/BlockScore/activemodel-interdependence) [](https://codeclimate.com/github/BlockScore/activemodel-interdependence) [](https://codeclimate.com/github/BlockScore/activemodel-interdependence/coverage) [](http://inch-ci.org/github/blockscore/activemodel-interdependence)
|
4
|
+
|
5
|
+
Specify that validations depend on the validation of other fields
|
6
|
+
|
7
|
+
Lets classes that implement `ActiveModel::Model` or `ActiveModel::Validator`
|
8
|
+
specify that they are dependent on other fields being valid. These specifications
|
9
|
+
are translated into a dependency graph, sorted, and applied in order to a model.
|
10
|
+
As a result, fields are only validated once the fields that they depend on are
|
11
|
+
validated.
|
12
|
+
|
13
|
+
## Install
|
14
|
+
|
15
|
+
Simply run
|
16
|
+
```shell
|
17
|
+
gem install activemodel-interdependence
|
18
|
+
```
|
19
|
+
|
20
|
+
or add activemodel-interdependence to your gemfile
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'activemodel-interdependence'
|
24
|
+
```
|
25
|
+
|
26
|
+
and run `bundle install` from your shell.
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class DayValidator < ActiveModel::EachValidator
|
32
|
+
validates :month_field, inclusion: 1..12
|
33
|
+
validates :year_field, inclusion: 0..2015
|
34
|
+
|
35
|
+
def validate_each(record, attribute, value)
|
36
|
+
month = dependency(record, :month_field)
|
37
|
+
year = dependency(record, :year_field)
|
38
|
+
|
39
|
+
return if (1..Time.days_in_month(month, year)).cover?(value)
|
40
|
+
record.errors[attribute] << "is not valid for the month #{Date::MONTHNAMES[month]}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def dependency(record, proxy_name)
|
44
|
+
name = options.fetch(:dependencies, {}).fetch(proxy_name)
|
45
|
+
|
46
|
+
record.send(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Birthday
|
51
|
+
include ActiveModel::Model
|
52
|
+
attr_accessor :day, :month, :year
|
53
|
+
|
54
|
+
validates :day, day: {
|
55
|
+
dependencies: {
|
56
|
+
month_field: :month,
|
57
|
+
year_field: :year
|
58
|
+
}
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
leap_day = Birthday.new(day: 29, month: 2, year: 2000)
|
63
|
+
leap_day.valid? # => true
|
64
|
+
|
65
|
+
not_a_leap_day = Birthday.new(day: 29, month: 2, year: 1999)
|
66
|
+
not_a_leap_day.valid? # => false
|
67
|
+
not_a_leap_day.errors.full_messages # => ["Day is not valid for the month February"]
|
68
|
+
|
69
|
+
bad_month = Birthday.new(day: 29, month: 0, year: 1999)
|
70
|
+
bad_month.valid? # => false
|
71
|
+
bad_month.errors.full_messages # => ["Month is not included in the list"]
|
72
|
+
```
|
73
|
+
|
74
|
+
## License
|
75
|
+
|
76
|
+
**activemodel-interdependence** is released under the MIT license. See `LICENSE` for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler.require(:metrics)
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'yard-virtus'
|
6
|
+
require 'yard/virtus/mixin_handler_monkey_patch'
|
7
|
+
|
8
|
+
YARD::Rake::YardocTask.new do |t|
|
9
|
+
t.files = %w(lib/**/*.rb)
|
10
|
+
end
|
11
|
+
|
12
|
+
Devtools.init_rake_tasks
|
13
|
+
|
14
|
+
##
|
15
|
+
# Run mutant tasks and give a nice help message
|
16
|
+
class MutantTask
|
17
|
+
include Rake::FileUtilsExt
|
18
|
+
|
19
|
+
def initialize(*)
|
20
|
+
@matcher = next_matcher
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
if matcher?
|
25
|
+
run_mutant
|
26
|
+
else
|
27
|
+
puts 'No matchers remaining'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def next_matcher
|
34
|
+
matcher = read_next_matcher
|
35
|
+
matcher ? matcher.chomp : :no_matchers
|
36
|
+
end
|
37
|
+
|
38
|
+
def read_next_matcher
|
39
|
+
open('.mutants').readlines.first
|
40
|
+
end
|
41
|
+
|
42
|
+
HELP = <<-HELP.gsub(/^\s+\|/, '')
|
43
|
+
|Provide a valid mutant matcher. Here are some examples:
|
44
|
+
|
|
45
|
+
| rake 'mutant[*]'
|
46
|
+
| rake 'mutant[Foo]'
|
47
|
+
| rake 'mutant[Foo*]'
|
48
|
+
| rake 'mutant[Foo#instance_method]'
|
49
|
+
| rake 'mutant[Foo.class_method]'
|
50
|
+
| rake 'mutant[Foo::Bar]'
|
51
|
+
| rake 'mutant[Foo::Bar*]'
|
52
|
+
|
|
53
|
+
|and so on.
|
54
|
+
|
|
55
|
+
|Try to be specific. Even a few methods can take a while.
|
56
|
+
HELP
|
57
|
+
|
58
|
+
attr_reader :matcher
|
59
|
+
|
60
|
+
def run_mutant
|
61
|
+
shift_mutants if mutant?
|
62
|
+
end
|
63
|
+
|
64
|
+
def shift_mutants
|
65
|
+
File.write('.mutants', shifted_mutants)
|
66
|
+
end
|
67
|
+
|
68
|
+
def shifted_mutants
|
69
|
+
open('.mutants').readlines.tap(&:shift).join('')
|
70
|
+
end
|
71
|
+
|
72
|
+
def mutant
|
73
|
+
@mutant ||= sh(command)
|
74
|
+
end
|
75
|
+
alias_method :mutant?, :mutant
|
76
|
+
|
77
|
+
def matcher?
|
78
|
+
!matcher.equal?(:no_matchers)
|
79
|
+
end
|
80
|
+
|
81
|
+
def print_help
|
82
|
+
puts HELP
|
83
|
+
end
|
84
|
+
|
85
|
+
def command
|
86
|
+
"bundle exec mutant -I lib -r interdependence --use rspec #{matcher}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
desc 'Run mutant'
|
91
|
+
task :mutant, %i(matcher) do |_, args|
|
92
|
+
MutantTask.new(args).run
|
93
|
+
end
|
94
|
+
|
95
|
+
task default: :mutant
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'interdependence/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "activemodel-interdependence"
|
9
|
+
spec.version = Interdependence::VERSION
|
10
|
+
spec.authors = ["John Backus"]
|
11
|
+
spec.email = ["john@blockscore.com"]
|
12
|
+
|
13
|
+
spec.summary = %q{Use validators interdependently}
|
14
|
+
spec.description = %q{Interdependent model validations}
|
15
|
+
spec.homepage = "https://github.com/blockscore/activemodel-interdependence"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.required_ruby_version = '>= 2.2.0'
|
23
|
+
|
24
|
+
spec.add_dependency 'activemodel', '~> 4.2'
|
25
|
+
spec.add_dependency 'activesupport', '~> 4.2'
|
26
|
+
spec.add_dependency 'virtus', '~> 1'
|
27
|
+
spec.add_dependency 'adamantium', '~> 0.2'
|
28
|
+
|
29
|
+
spec.add_development_dependency "rspec", '~> 3.0'
|
30
|
+
spec.add_development_dependency "rspec-core", '~> 3.0'
|
31
|
+
spec.add_development_dependency "rspec-its", '~> 1.2'
|
32
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
33
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
34
|
+
end
|
data/circle.yml
ADDED
data/config/devtools.yml
ADDED
data/config/flay.yml
ADDED
data/config/flog.yml
ADDED
data/config/mutant.yml
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
---
|
2
|
+
name: interdependence
|
3
|
+
namespace: Interdependence
|
4
|
+
expect_coverage: 1
|
5
|
+
ignore_subjects:
|
6
|
+
# I don't care about testing #inspect
|
7
|
+
- 'Interdependence::Dependency::Base#inspect'
|
8
|
+
# merge! causes infinite runtime on circle
|
9
|
+
- 'Interdependence::DependencySetGraph#merge!'
|
data/config/reek.yml
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
---
|
2
|
+
Attribute:
|
3
|
+
enabled: false
|
4
|
+
exclude: []
|
5
|
+
BooleanParameter:
|
6
|
+
enabled: true
|
7
|
+
exclude: []
|
8
|
+
ClassVariable:
|
9
|
+
enabled: true
|
10
|
+
exclude: []
|
11
|
+
ControlParameter:
|
12
|
+
enabled: true
|
13
|
+
exclude: []
|
14
|
+
DataClump:
|
15
|
+
enabled: true
|
16
|
+
exclude: []
|
17
|
+
max_copies: 2
|
18
|
+
min_clump_size: 2
|
19
|
+
DuplicateMethodCall:
|
20
|
+
enabled: false
|
21
|
+
exclude: []
|
22
|
+
max_calls: 1
|
23
|
+
allow_calls: []
|
24
|
+
FeatureEnvy:
|
25
|
+
enabled: true
|
26
|
+
exclude:
|
27
|
+
- '#=='
|
28
|
+
- '#resolvable?'
|
29
|
+
- !ruby/regexp /(\.|#)included/
|
30
|
+
- !ruby/regexp /ActiveModel::/
|
31
|
+
- 'Interdependence::DependencyResolver::Model#resolve_with'
|
32
|
+
IrresponsibleModule:
|
33
|
+
enabled: false
|
34
|
+
exclude: []
|
35
|
+
LongParameterList:
|
36
|
+
enabled: true
|
37
|
+
exclude: []
|
38
|
+
max_params: 3
|
39
|
+
overrides:
|
40
|
+
initialize:
|
41
|
+
max_params: 5
|
42
|
+
LongYieldList:
|
43
|
+
enabled: true
|
44
|
+
exclude: []
|
45
|
+
max_params: 3
|
46
|
+
ModuleInitialize:
|
47
|
+
enabled: true
|
48
|
+
exclude: []
|
49
|
+
NestedIterators:
|
50
|
+
enabled: true
|
51
|
+
exclude: []
|
52
|
+
max_allowed_nesting: 2
|
53
|
+
ignore_iterators: []
|
54
|
+
NilCheck:
|
55
|
+
enabled: true
|
56
|
+
exclude:
|
57
|
+
- !ruby/regexp /Coercer#self.call/
|
58
|
+
PrimaDonnaMethod:
|
59
|
+
enabled: true
|
60
|
+
exclude:
|
61
|
+
- !ruby/regexp /DependencySetGraph/
|
62
|
+
RepeatedConditional:
|
63
|
+
enabled: true
|
64
|
+
exclude: []
|
65
|
+
max_ifs: 2
|
66
|
+
TooManyInstanceVariables:
|
67
|
+
enabled: true
|
68
|
+
exclude: []
|
69
|
+
max_instance_variables: 9
|
70
|
+
TooManyMethods:
|
71
|
+
enabled: true
|
72
|
+
exclude: []
|
73
|
+
max_methods: 25
|
74
|
+
TooManyStatements:
|
75
|
+
enabled: true
|
76
|
+
exclude:
|
77
|
+
- initialize
|
78
|
+
max_statements: 5
|
79
|
+
UncommunicativeMethodName:
|
80
|
+
enabled: true
|
81
|
+
exclude: []
|
82
|
+
reject:
|
83
|
+
- !ruby/regexp /^[a-z]$/
|
84
|
+
- !ruby/regexp /[0-9]$/
|
85
|
+
- !ruby/regexp /[A-Z]/
|
86
|
+
accept: []
|
87
|
+
UncommunicativeModuleName:
|
88
|
+
enabled: true
|
89
|
+
exclude: []
|
90
|
+
reject:
|
91
|
+
- !ruby/regexp /^.$/
|
92
|
+
- !ruby/regexp /[0-9]$/
|
93
|
+
accept:
|
94
|
+
- Inline::C
|
95
|
+
UncommunicativeParameterName:
|
96
|
+
enabled: true
|
97
|
+
exclude: []
|
98
|
+
reject:
|
99
|
+
- !ruby/regexp /^.$/
|
100
|
+
- !ruby/regexp /[0-9]$/
|
101
|
+
- !ruby/regexp /[A-Z]/
|
102
|
+
- !ruby/regexp /^_/
|
103
|
+
accept: []
|
104
|
+
UncommunicativeVariableName:
|
105
|
+
enabled: true
|
106
|
+
exclude: []
|
107
|
+
reject:
|
108
|
+
- !ruby/regexp /^.$/
|
109
|
+
- !ruby/regexp /[0-9]$/
|
110
|
+
- !ruby/regexp /[A-Z]/
|
111
|
+
accept:
|
112
|
+
- _
|
113
|
+
UnusedParameters:
|
114
|
+
enabled: true
|
115
|
+
exclude: []
|
116
|
+
UtilityFunction:
|
117
|
+
enabled: true
|
118
|
+
exclude:
|
119
|
+
- '#resolvable?'
|
120
|
+
- 'Interdependence::Types::UnsetField#=='
|