activeoopish 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 +34 -0
- data/.rspec +3 -0
- data/.travis.yml +3 -0
- data/Gemfile +8 -0
- data/README.md +72 -0
- data/Rakefile +5 -0
- data/activeoopish.gemspec +24 -0
- data/lib/activeoopish/rspec_helper.rb +66 -0
- data/lib/activeoopish/validator.rb +220 -0
- data/lib/activeoopish/version.rb +3 -0
- data/lib/activeoopish.rb +2 -0
- data/spec/activeoopish/rspec_helper_spec.rb +69 -0
- data/spec/activeoopish/validator_spec.rb +203 -0
- data/spec/spec_helper.rb +11 -0
- metadata +142 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 256e008c0ef28562a42f0b9250ddfa37096abd72
|
4
|
+
data.tar.gz: e1e3b3fefbd6db22dfd6049b92d5918299111b0a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bf0203fca919d91c95e622300bb53ea52c960ab2477893651173c23113337a844959a6f070ec6f7e40d4dce8ffe9f60822420f589e3850851cee5138f70dae28
|
7
|
+
data.tar.gz: bd3eee6bfba42b3e75d5b7150d846e29959746af601868da40a203ec3d93fac963c3eb0741b80594ec5fece2929d318cbff479754bb779da5929eb6956b4dd9d
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
Gemfile.lock
|
30
|
+
.ruby-version
|
31
|
+
.ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# AvtiveOOPish
|
2
|
+
|
3
|
+
[](https://travis-ci.org/yuku-t/activeoopish) [](https://codeclimate.com/github/yuku-t/activeoopish) [](https://coveralls.io/r/yuku-t/activeoopish) [](https://gemnasium.com/yuku-t/activeoopish)
|
4
|
+
|
5
|
+
Simple tools for better OOP in Rails projects.
|
6
|
+
|
7
|
+
## ActiveOOPish::Validator
|
8
|
+
|
9
|
+
Encapsulates the responsibility of validating a model into a validator.
|
10
|
+
|
11
|
+
```rb
|
12
|
+
class BookValidator < ActiveOOPish::Validator
|
13
|
+
declear do
|
14
|
+
validates :author, presence: true
|
15
|
+
|
16
|
+
validates :title, length: { minimum: 3, maximum: 255 }
|
17
|
+
|
18
|
+
validate :title_must_include_author_name, if: :biography?
|
19
|
+
end
|
20
|
+
|
21
|
+
def title_must_include_author_name(book)
|
22
|
+
unless book.title.include?(book.author.name)
|
23
|
+
book.errors.add(:author, "cannot write a biography for other people")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def biography?(book)
|
28
|
+
book.category == :biography
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Book < ActiveRecord::Base
|
33
|
+
belongs_to :author, class_name: 'User'
|
34
|
+
|
35
|
+
BookValidator.monitor(self)
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
### RSpec
|
40
|
+
|
41
|
+
```rb
|
42
|
+
require 'activeoopish/rspec'
|
43
|
+
require 'shoulda-matchers'
|
44
|
+
|
45
|
+
declear BookValidator, :with_activeoopish_helper do
|
46
|
+
include_context 'describe declaration' do
|
47
|
+
it { should validate_presence_of(:author) }
|
48
|
+
|
49
|
+
it { should validate_length_of(:title).is_at_least(3).is_at_most(255) }
|
50
|
+
|
51
|
+
context 'when #biography? returns true' do
|
52
|
+
before do
|
53
|
+
allow_any_instance_of(described_class).to receive(:biography?).and_return(true)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'calls #title_must_include_author_name' do
|
57
|
+
expect_any_instance_of(described_class)
|
58
|
+
.to receive(:title_must_include_author_name).with(subject).once
|
59
|
+
subject.valid?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#biography?' do
|
65
|
+
# ...
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#title_must_include_author_name' do
|
69
|
+
# ...
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "activeoopish/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'activeoopish'
|
7
|
+
spec.version = ActiveOOPish::VERSION
|
8
|
+
spec.authors = ['Yuku Takahashi']
|
9
|
+
spec.email = ['yuku@qiita.com']
|
10
|
+
spec.summary = 'Simple OOP-ish tools for Rails'
|
11
|
+
spec.homepage = 'https://github.com/increments/activeoopish'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
15
|
+
spec.require_paths = ['lib']
|
16
|
+
|
17
|
+
spec.add_dependency 'activesupport'
|
18
|
+
spec.add_dependency 'activemodel'
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
21
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
22
|
+
spec.add_development_dependency 'rspec'
|
23
|
+
spec.add_development_dependency 'shoulda-matchers'
|
24
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'rspec/core'
|
2
|
+
require 'rspec/matchers'
|
3
|
+
require 'active_model'
|
4
|
+
require 'active_support'
|
5
|
+
|
6
|
+
RSpec::Matchers.define :be_monitored_by do |validator_class|
|
7
|
+
match do |actual|
|
8
|
+
validator_class.respond_to?(:monitoring?) && validator_class.monitoring?(actual)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ActiveOOPish
|
13
|
+
module RSpecHelper
|
14
|
+
module SharedContext
|
15
|
+
extend ActiveSupport::Concern
|
16
|
+
|
17
|
+
included do
|
18
|
+
let(:model_class) do
|
19
|
+
Class.new(ActiveOOPish::RSpecHelper::ValidationTarget) do
|
20
|
+
def self.name
|
21
|
+
'ValidationTarget'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
shared_context 'describe declaration', :describe_declaration do
|
27
|
+
subject do
|
28
|
+
described_class.monitor(model_class)
|
29
|
+
model_class.new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ValidationTarget
|
36
|
+
include ActiveModel::Validations
|
37
|
+
|
38
|
+
def initialize(attributes = {})
|
39
|
+
@attributes = attributes
|
40
|
+
end
|
41
|
+
|
42
|
+
def read_attribute_for_validation(key)
|
43
|
+
@attributes[key.to_sym]
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def remove_trailing_equal(string)
|
49
|
+
string[0...-1].to_sym
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(name, *args)
|
53
|
+
if name.to_s.end_with?('=')
|
54
|
+
name = remove_trailing_equal(name)
|
55
|
+
@attributes[name] = args.first
|
56
|
+
elsif @attributes.include?(name)
|
57
|
+
read_attribute_for_validation(name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
RSpec.configure do |config|
|
65
|
+
config.include ActiveOOPish::RSpecHelper::SharedContext, :with_activeoopish_helpers
|
66
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
|
3
|
+
module ActiveOOPish
|
4
|
+
# Public: Base class for validators.
|
5
|
+
#
|
6
|
+
# Example
|
7
|
+
#
|
8
|
+
# class BookValidator < ActiveOOPish::Validator
|
9
|
+
# declear do
|
10
|
+
# validates :author, presence: true
|
11
|
+
# validate :title_must_include_author_name, if: :biography?
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# private
|
15
|
+
#
|
16
|
+
# def title_must_include_author_name(book)
|
17
|
+
# unless book.title.include?(book.author.name)
|
18
|
+
# book.errors.add(:author, "cannot write a biography for other people")
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def biography?(book)
|
23
|
+
# book.category == :biography
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# class Book < ActiveRecord::Base
|
28
|
+
# belongs_to :author, class_name: 'User'
|
29
|
+
# BookValidator.monitor(self)
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# BookValidator.monitor?(Book)
|
33
|
+
# # => true
|
34
|
+
#
|
35
|
+
# book = Book.new(title: 'Qiitan biography', author: User.new(name: 'Yaotti'))
|
36
|
+
#
|
37
|
+
# book.valid?
|
38
|
+
# # => false
|
39
|
+
#
|
40
|
+
# book.errors.full_messages_for(:author).first
|
41
|
+
# # => "author cannot write a biography of another person"
|
42
|
+
#
|
43
|
+
class Validator
|
44
|
+
# Public: Generic ActiveOOPish::Validator related error.
|
45
|
+
# Exceptions raised in this class should inherit from Error.
|
46
|
+
class Error < StandardError
|
47
|
+
end
|
48
|
+
|
49
|
+
# Public: Raised when
|
50
|
+
class AlreadyMonitored < Error
|
51
|
+
end
|
52
|
+
|
53
|
+
# Public: Raised when a non-decleared validator tries to validate a model.
|
54
|
+
class DeclarationNotFound < Error
|
55
|
+
end
|
56
|
+
|
57
|
+
class << self
|
58
|
+
# Public: Start validating the given model_class's instances.
|
59
|
+
#
|
60
|
+
# model_class - A model Class to be validated by it.
|
61
|
+
#
|
62
|
+
# Raises AlreadyMonitored if the given model_class has already been
|
63
|
+
# monitored.
|
64
|
+
# Raises DeclarationNotFound when it has not decleared yet.
|
65
|
+
# Returns nothing.
|
66
|
+
def monitor(model_class)
|
67
|
+
fail AlreadyMonitored if monitoring?(model_class)
|
68
|
+
define_accessor_to_model_class(model_class)
|
69
|
+
apply_decleared_rules_to_model_class(model_class)
|
70
|
+
monitoring_model_classes << model_class
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: Whether the given model_class is watched by it.
|
74
|
+
#
|
75
|
+
# target - A class which includes ActiveModel::Validations or its instance.
|
76
|
+
#
|
77
|
+
# Returns true or false.
|
78
|
+
def monitoring?(target)
|
79
|
+
model_class = target.is_a?(ActiveModel::Validations) ? target.class : target
|
80
|
+
monitoring_model_classes.include?(model_class)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Internal: Its name.
|
84
|
+
#
|
85
|
+
# Returns a Symbol.
|
86
|
+
def injection_name
|
87
|
+
@method_name ||= "__validator_#{name.downcase.gsub('::', '_')}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def inspect
|
91
|
+
name
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def monitoring_model_classes
|
97
|
+
@monitoring_model_classes ||= []
|
98
|
+
end
|
99
|
+
|
100
|
+
# Internal: Declear how to validate a model class.
|
101
|
+
#
|
102
|
+
# block - A block which will be applied later.
|
103
|
+
# It may call `validates`, `validates_with` and `validate`
|
104
|
+
# methods to self of the block context.
|
105
|
+
#
|
106
|
+
# Example
|
107
|
+
#
|
108
|
+
# Validator.declear do
|
109
|
+
# # Delegated to @model_class.
|
110
|
+
# validates :name, presence: true
|
111
|
+
# validates_with AnActiveModelValidator
|
112
|
+
#
|
113
|
+
# # Create a proxy method to the corresponding validator method.
|
114
|
+
# validate :validate_method
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# Returns nothing.
|
118
|
+
def declear(&block)
|
119
|
+
@declaration = block
|
120
|
+
end
|
121
|
+
|
122
|
+
# Internal: Define a private instance method something like
|
123
|
+
#
|
124
|
+
# def __validator_bookvalidator
|
125
|
+
# @__validator_bookvalidator ||= BookValidator.new
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
# to the given model_class.
|
129
|
+
#
|
130
|
+
# model_class - A model Class to be validated by it.
|
131
|
+
#
|
132
|
+
# Returns nothing.
|
133
|
+
def define_accessor_to_model_class(model_class)
|
134
|
+
validator_class = self
|
135
|
+
|
136
|
+
model_class.class_eval do
|
137
|
+
unless private_instance_methods(false).include?(validator_class.injection_name)
|
138
|
+
define_method(validator_class.injection_name) do
|
139
|
+
instance_variable_set("@#{validator_class.injection_name}", validator_class.new)
|
140
|
+
end
|
141
|
+
private validator_class.injection_name
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def apply_decleared_rules_to_model_class(model_class)
|
147
|
+
fail DeclarationNotFound unless @declaration
|
148
|
+
@model_class = model_class
|
149
|
+
begin
|
150
|
+
class_eval(&@declaration)
|
151
|
+
ensure
|
152
|
+
@model_class = nil
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# TODO: Support `validate` in `with_options` block.
|
157
|
+
delegate(
|
158
|
+
:validates, :validates_with, :validates_associated, :with_options,
|
159
|
+
to: :@model_class
|
160
|
+
)
|
161
|
+
|
162
|
+
# Internal: Define a private method to the model class which works as a
|
163
|
+
# proxy for the validator's corresponding method. The validator method
|
164
|
+
# will be called with the model instance as the first argument.
|
165
|
+
#
|
166
|
+
# Suppose a `UserValidator` declears:
|
167
|
+
#
|
168
|
+
# validate :must_be_qiitan, if: :active?
|
169
|
+
#
|
170
|
+
# then, this method defines some private methods to the @model_class
|
171
|
+
# something like:
|
172
|
+
#
|
173
|
+
# def __validator_uservalidator_validate_must_be_qiitan
|
174
|
+
# __validator_uservalidator.__send__(:must_be_qiitan, self)
|
175
|
+
# end
|
176
|
+
#
|
177
|
+
# def __validator_uservalidator_if_active?
|
178
|
+
# __validator_uservalidator.__send__(:active?, self)
|
179
|
+
# end
|
180
|
+
#
|
181
|
+
# validate(
|
182
|
+
# :__validator_uservalidator_validate_must_be_qiitan,
|
183
|
+
# if: __validator_user_validator_if_active?
|
184
|
+
# )
|
185
|
+
#
|
186
|
+
# Note that it does not support a block and a proc for :if and :unless
|
187
|
+
# options, though ActiveModel::Validations::ClassMethods.validate
|
188
|
+
# supports them.
|
189
|
+
#
|
190
|
+
# Returns nothing.
|
191
|
+
def validate(method_name, options = {})
|
192
|
+
validator_class = self
|
193
|
+
|
194
|
+
proxy_method_name = "#{validator_class.injection_name}_validate_#{method_name}"
|
195
|
+
proxy_map = { proxy_method_name => method_name }
|
196
|
+
|
197
|
+
(options.keys & [:if, :unless]).each do |key|
|
198
|
+
value = options[key]
|
199
|
+
proxy_name = "#{validator_class.injection_name}_#{key}_#{value}"
|
200
|
+
proxy_map[proxy_name] = value
|
201
|
+
options[key] = proxy_name
|
202
|
+
end
|
203
|
+
|
204
|
+
@model_class.class_eval do
|
205
|
+
proxy_map.each_pair do |model_method_name, validator_method_name|
|
206
|
+
unless private_instance_methods(false).include?(model_method_name)
|
207
|
+
define_method(model_method_name) do
|
208
|
+
__send__(validator_class.injection_name).__send__(validator_method_name, self)
|
209
|
+
end
|
210
|
+
private model_method_name
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Add the proxy method to the model_class as a validation method.
|
215
|
+
validate(proxy_method_name, **options)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
data/lib/activeoopish.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'activeoopish/rspec_helper'
|
2
|
+
|
3
|
+
describe 'activeoopish matchers' do
|
4
|
+
describe 'be_monitored_by matcher', :with_activeoopish_helpers do
|
5
|
+
let(:validator_class) do
|
6
|
+
Class.new(ActiveOOPish::Validator) do
|
7
|
+
declear do
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.name
|
11
|
+
'Sample::Validator'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
shared_examples_for 'be_monitored_by' do
|
17
|
+
context 'and the model_class is not monitored by the validator_class' do
|
18
|
+
it { should_not be_monitored_by validator_class }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'and the model_class is monitored by the validator_class' do
|
22
|
+
before do
|
23
|
+
validator_class.monitor(model_class)
|
24
|
+
end
|
25
|
+
|
26
|
+
it { should be_monitored_by validator_class }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when the subject is the model_class' do
|
31
|
+
subject do
|
32
|
+
model_class
|
33
|
+
end
|
34
|
+
|
35
|
+
include_examples 'be_monitored_by'
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when the subject is the instance of the model_class' do
|
39
|
+
subject do
|
40
|
+
model_class.new
|
41
|
+
end
|
42
|
+
|
43
|
+
include_examples 'be_monitored_by'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class SampleValidator < ActiveOOPish::Validator
|
49
|
+
declear do
|
50
|
+
validates(
|
51
|
+
:attr,
|
52
|
+
exclusion: { in: %w(a b c) },
|
53
|
+
inclusion: { in: %w(x y z) },
|
54
|
+
length: { minimum: 1, maximum: 10 },
|
55
|
+
numericality: { only_integer: true },
|
56
|
+
presence: true
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe SampleValidator, :with_activeoopish_helpers do
|
62
|
+
include_context 'describe declaration' do
|
63
|
+
it { should validate_exclusion_of(:attr).in_array(%w(a b c)) }
|
64
|
+
# it { should validate_inclusion_of(:attr).in_array(%w(x y z)) }
|
65
|
+
it { should validate_length_of(:attr).is_at_least(1).is_at_most(10) }
|
66
|
+
it { should validate_numericality_of(:attr).only_integer }
|
67
|
+
it { should validate_presence_of(:attr) }
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'activeoopish/rspec_helper'
|
2
|
+
|
3
|
+
describe ActiveOOPish::Validator, :with_activeoopish_helpers do
|
4
|
+
describe '.monitor' do
|
5
|
+
subject do
|
6
|
+
validator_class.monitor(model_class)
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'when the validator_class does not have declaration' do
|
10
|
+
let(:validator_class) do
|
11
|
+
Class.new(described_class) do
|
12
|
+
def self.name
|
13
|
+
'Sample::Validator'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it { expect { subject }.to raise_error(described_class::DeclarationNotFound) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when the validator_class has declaration' do
|
22
|
+
let(:validator_class) do
|
23
|
+
Class.new(described_class) do
|
24
|
+
declear do
|
25
|
+
validates(
|
26
|
+
:attr,
|
27
|
+
exclusion: { in: %w(a b c) },
|
28
|
+
inclusion: { in: %w(x y z) },
|
29
|
+
length: { minimum: 1, maximum: 10 },
|
30
|
+
numericality: { only_integer: true },
|
31
|
+
presence: true
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.name
|
36
|
+
'Sample::Validator'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'apply decleared validations to the given model_class' do
|
42
|
+
instance = model_class.new
|
43
|
+
expect(instance).not_to validate_exclusion_of(:attr).in_array(%w(a b c))
|
44
|
+
expect(instance).not_to validate_inclusion_of(:attr).in_array(%w(x y z))
|
45
|
+
expect(instance).not_to validate_length_of(:attr).is_at_least(1).is_at_most(10)
|
46
|
+
expect(instance).not_to validate_numericality_of(:attr).only_integer
|
47
|
+
expect(instance).not_to validate_presence_of(:attr)
|
48
|
+
|
49
|
+
subject
|
50
|
+
|
51
|
+
instance = model_class.new
|
52
|
+
expect(instance).to validate_exclusion_of(:attr).in_array(%w(a b c))
|
53
|
+
# expect(instance).to validate_inclusion_of(:attr).in_array(%w(x y z))
|
54
|
+
expect(instance).to validate_length_of(:attr).is_at_least(1).is_at_most(10)
|
55
|
+
expect(instance).to validate_numericality_of(:attr).only_integer
|
56
|
+
expect(instance).to validate_presence_of(:attr)
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'and it declears `validate` with if and unless options' do
|
60
|
+
let(:validator_class) do
|
61
|
+
Class.new(described_class) do
|
62
|
+
declear do
|
63
|
+
validate :validate_method, if: :if_cond?, unless: :unless_cond?
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.name
|
67
|
+
'Sample::Validator'
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def if_cond?(_)
|
73
|
+
true
|
74
|
+
end
|
75
|
+
|
76
|
+
def unless_cond?(_)
|
77
|
+
false
|
78
|
+
end
|
79
|
+
|
80
|
+
def validate_method(_)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'makes corresponding methods be called with the instance when validating it' do
|
86
|
+
subject # Start monitoring
|
87
|
+
instance = model_class.new
|
88
|
+
|
89
|
+
expect_any_instance_of(validator_class)
|
90
|
+
.to receive(:if_cond?).with(instance).once.and_call_original
|
91
|
+
expect_any_instance_of(validator_class)
|
92
|
+
.to receive(:unless_cond?).with(instance).once.and_call_original
|
93
|
+
expect_any_instance_of(validator_class)
|
94
|
+
.to receive(:validate_method).with(instance).once
|
95
|
+
|
96
|
+
instance.valid? # Validate the instance
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'and if condition returns false' do
|
100
|
+
before do
|
101
|
+
allow_any_instance_of(validator_class).to receive(:if_cond?).and_return(false)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'skips the corresponding validate method' do
|
105
|
+
subject # Start monitoring
|
106
|
+
instance = model_class.new
|
107
|
+
|
108
|
+
expect_any_instance_of(validator_class).not_to receive(:validate_method)
|
109
|
+
instance.valid?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'and unless condition returns true' do
|
114
|
+
before do
|
115
|
+
allow_any_instance_of(validator_class).to receive(:unless_cond?).and_return(true)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'skips the corresponding validate method' do
|
119
|
+
subject # Start monitoring
|
120
|
+
instance = model_class.new
|
121
|
+
|
122
|
+
expect_any_instance_of(validator_class).not_to receive(:validate_method)
|
123
|
+
instance.valid?
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'and the model_class has already been monitored' do
|
129
|
+
before do
|
130
|
+
validator_class.monitor(model_class)
|
131
|
+
end
|
132
|
+
|
133
|
+
it { expect { subject }.to raise_error(described_class::AlreadyMonitored) }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe '.monitoring?' do
|
139
|
+
subject do
|
140
|
+
validator_class.monitoring?(param)
|
141
|
+
end
|
142
|
+
|
143
|
+
let(:validator_class) do
|
144
|
+
Class.new(described_class) do
|
145
|
+
declear do
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.name
|
149
|
+
'Sample::Validator'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
shared_examples_for '.monitoring?' do
|
155
|
+
context 'and it does not monitor the model_class' do
|
156
|
+
it { should be_falsey }
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'and it monitors the model_class' do
|
160
|
+
before do
|
161
|
+
validator_class.monitor(model_class)
|
162
|
+
end
|
163
|
+
|
164
|
+
it { should be_truthy }
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'when the given parameter is model_class' do
|
169
|
+
let(:param) do
|
170
|
+
model_class
|
171
|
+
end
|
172
|
+
|
173
|
+
include_examples '.monitoring?'
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'when the given parameter is the instance of model_class' do
|
177
|
+
let(:param) do
|
178
|
+
model_class.new
|
179
|
+
end
|
180
|
+
|
181
|
+
include_examples '.monitoring?'
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe '.injection_name' do
|
186
|
+
subject do
|
187
|
+
validator_class.injection_name
|
188
|
+
end
|
189
|
+
|
190
|
+
let(:validator_class) do
|
191
|
+
Class.new(described_class) do
|
192
|
+
declear do
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.name
|
196
|
+
'Sample::Validator'
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
it { should be_a String }
|
202
|
+
end
|
203
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activeoopish
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Yuku Takahashi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activemodel
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.7'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.7'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: shoulda-matchers
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- yuku@qiita.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".travis.yml"
|
107
|
+
- Gemfile
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- activeoopish.gemspec
|
111
|
+
- lib/activeoopish.rb
|
112
|
+
- lib/activeoopish/rspec_helper.rb
|
113
|
+
- lib/activeoopish/validator.rb
|
114
|
+
- lib/activeoopish/version.rb
|
115
|
+
- spec/activeoopish/rspec_helper_spec.rb
|
116
|
+
- spec/activeoopish/validator_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
homepage: https://github.com/increments/activeoopish
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubyforge_project:
|
138
|
+
rubygems_version: 2.2.2
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: Simple OOP-ish tools for Rails
|
142
|
+
test_files: []
|