pure_validator 0.3.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.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.travis.yml +5 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +47 -0
- data/LICENSE.txt +22 -0
- data/README.md +139 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/lib/pure_validator/args_validator.rb +107 -0
- data/lib/pure_validator/concern.rb +136 -0
- data/lib/pure_validator/core_extensions/class_attribute.rb +143 -0
- data/lib/pure_validator/core_extensions/humanize.rb +44 -0
- data/lib/pure_validator/errors.rb +23 -0
- data/lib/pure_validator/i18n.rb +7 -0
- data/lib/pure_validator/locales/en.yml +24 -0
- data/lib/pure_validator/locales/ru.yml +24 -0
- data/lib/pure_validator/validation_errors.rb +248 -0
- data/lib/pure_validator/validator.rb +150 -0
- data/lib/pure_validator/validators/email_validator.rb +48 -0
- data/lib/pure_validator/validators/exclusion_validator.rb +22 -0
- data/lib/pure_validator/validators/inclusion_validator.rb +27 -0
- data/lib/pure_validator/validators/length_validator.rb +32 -0
- data/lib/pure_validator/validators/not_nil_validator.rb +25 -0
- data/lib/pure_validator/validators/numericality_validator.rb +39 -0
- data/lib/pure_validator/validators/presence_validator.rb +26 -0
- data/lib/pure_validator/validators/regexp_validator.rb +21 -0
- data/lib/pure_validator/validators/url_validator.rb +25 -0
- data/lib/pure_validator/validators.rb +11 -0
- data/lib/pure_validator/version.rb +3 -0
- data/lib/pure_validator.rb +43 -0
- data/pure_validator.gemspec +26 -0
- data/spec/pure_validator/args_validator_spec.rb +169 -0
- data/spec/pure_validator/errors_spec.rb +10 -0
- data/spec/pure_validator/validation_errors_spec.rb +109 -0
- data/spec/pure_validator/validator_spec.rb +234 -0
- data/spec/pure_validator/validators/email_validator_spec.rb +35 -0
- data/spec/pure_validator/validators/exclusion_validator_spec.rb +23 -0
- data/spec/pure_validator/validators/inclusion_validator_spec.rb +23 -0
- data/spec/pure_validator/validators/length_validator_spec.rb +38 -0
- data/spec/pure_validator/validators/not_nil_validator_spec.rb +44 -0
- data/spec/pure_validator/validators/numericality_validator_spec.rb +49 -0
- data/spec/pure_validator/validators/presence_validator_spec.rb +38 -0
- data/spec/pure_validator/validators/regexp_validator_spec.rb +23 -0
- data/spec/pure_validator/validators/url_validator_spec.rb +35 -0
- data/spec/spec_helper.rb +21 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b2eaaaed1d01fb6d73c7caecc66ad7b97422502b
|
4
|
+
data.tar.gz: febaaa1df0a19e771ea4246253ccf6592a46d429
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fdebd2254d6696cfec46bc455893d9622721a0f70a63efc63364847ce7b500ba55c06e4f7891076d89965e6ff076e98ae3549e36153982bc9b7394cf051f26ac
|
7
|
+
data.tar.gz: 1d881ee39ddc16f215dbe39be4450ef49ec84e2ad6ae0bdc94c9bc3bf4dc2b9030aa943fbec50f97c86be8a1fb2eda342b4f5603ea0f9a756f03e18e69b90856
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pure_validator (0.3.0)
|
5
|
+
i18n
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
byebug (9.0.6)
|
11
|
+
codecov (0.1.9)
|
12
|
+
json
|
13
|
+
simplecov
|
14
|
+
url
|
15
|
+
diff-lcs (1.2.5)
|
16
|
+
docile (1.1.5)
|
17
|
+
i18n (0.6.9)
|
18
|
+
json (2.0.2)
|
19
|
+
rake (10.1.1)
|
20
|
+
rspec (2.14.1)
|
21
|
+
rspec-core (~> 2.14.0)
|
22
|
+
rspec-expectations (~> 2.14.0)
|
23
|
+
rspec-mocks (~> 2.14.0)
|
24
|
+
rspec-core (2.14.7)
|
25
|
+
rspec-expectations (2.14.4)
|
26
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
27
|
+
rspec-mocks (2.14.4)
|
28
|
+
simplecov (0.12.0)
|
29
|
+
docile (~> 1.1.0)
|
30
|
+
json (>= 1.8, < 3)
|
31
|
+
simplecov-html (~> 0.10.0)
|
32
|
+
simplecov-html (0.10.0)
|
33
|
+
url (0.3.2)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
ruby
|
37
|
+
|
38
|
+
DEPENDENCIES
|
39
|
+
bundler (~> 1.3)
|
40
|
+
byebug
|
41
|
+
codecov
|
42
|
+
pure_validator!
|
43
|
+
rake
|
44
|
+
rspec
|
45
|
+
|
46
|
+
BUNDLED WITH
|
47
|
+
1.13.6
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Albert Gazizov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
# PureValidator [](https://travis-ci.org/ddd-ruby/pure_validator) [](https://codeclimate.com/github/ddd-ruby/pure_validator) [](https://codecov.io/gh/ddd-ruby/pure_validator)
|
2
|
+
|
3
|
+
PureValidator is a simple, mostly dependency-free (except `i18n`) library to validate your domain Ruby objects.
|
4
|
+
|
5
|
+
It keeps the concerns of validation separate from the Entity / Value object itself.
|
6
|
+
|
7
|
+
This gives you the option to have different validations rules for different ocasions for the same object in a very clean and unit-testable way.
|
8
|
+
|
9
|
+
It is a simple step to separate those concerns, but it will give you unlimited flexibility and save your *SS from debugging entangled and conflicting validations for non-trivial requirements.
|
10
|
+
|
11
|
+
Do yourself a favor and start using PureValidator today, you will never look back!
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
Let's say you have the following class and you want to validate its instances:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
class Contact
|
18
|
+
attr_accessor :first_name, :last_name, :position, :age, :type, :email, :color, :status, :stage, :description, :companies
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
To validate objects of the Contact class define following validator:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
class ContactValidator
|
26
|
+
include PureValidator::Validator
|
27
|
+
|
28
|
+
validates :first_name, presence: true, length: { min: 4, max: 7 }
|
29
|
+
validates :last_name, length: { equal_to: 5 }
|
30
|
+
validates :position, length: { not_equal_to: 5 }
|
31
|
+
validates :age, numericality: { greater_than: 0, less_than: 150 }
|
32
|
+
validates :type, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 5 }
|
33
|
+
validates :email, email: true
|
34
|
+
validates :color, regexp: /#\w{6}/
|
35
|
+
validates :status, inclusion: { in: [:new, :lead] }
|
36
|
+
validates :stage, exclusion: { in: [:wrong, :bad] }
|
37
|
+
|
38
|
+
validate_associated :companies, validator: CompanyValidator
|
39
|
+
|
40
|
+
validate :check_description
|
41
|
+
|
42
|
+
def check_description(entity, errors)
|
43
|
+
if entity.description.nil?
|
44
|
+
errors.add(:description, "can't be empty")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
Instantiate the validator and call `validate` with a contact object:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
errors = ContactValidator.new.validate(contact)
|
54
|
+
```
|
55
|
+
|
56
|
+
`errors` is a Hash that contains all validation errors.
|
57
|
+
If the object is valid then errors will be an empty Hash.
|
58
|
+
|
59
|
+
### Adding own validators
|
60
|
+
|
61
|
+
PureValidator can be extended by adding your own validators.
|
62
|
+
To add a validator define a class with 2 static methods: `validate` and `validate_options`:
|
63
|
+
|
64
|
+
The following example shows the built-in inclusion validator, it validates that specified value is one of the defined values.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
class PureValidator::Validators::InclusionValidator
|
68
|
+
|
69
|
+
# Validates that given value inscluded in the specified list
|
70
|
+
# @param value [Object] object to validate
|
71
|
+
# @parm options [Hash] validation options, e.g. { in: [:small, :medium, :large], message: "not included in the list of allowed items" }
|
72
|
+
# where :in - list of allowed values,
|
73
|
+
# message - is a message to return if value is not included in the list
|
74
|
+
# @return [Array] empty array if object is valid, list of errors otherwise
|
75
|
+
def self.validate(value, options)
|
76
|
+
return [] if value.nil?
|
77
|
+
|
78
|
+
errors = []
|
79
|
+
if options[:in]
|
80
|
+
unless options[:in].include?(value)
|
81
|
+
errors << (options[:message] || PureValidator::I18n.t('errors.should_be_included_in_list', list: options[:in]))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
errors
|
85
|
+
end
|
86
|
+
|
87
|
+
# Validates that options specified in
|
88
|
+
# :inclusion are valid
|
89
|
+
def self.validate_options(options)
|
90
|
+
raise ArgumentError, "validation options should be a Hash" unless options.is_a?(Hash)
|
91
|
+
raise ArgumentError, "validation options should have :in option and it should be an array of allowed values" unless options[:in].is_a?(Array)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
```
|
96
|
+
|
97
|
+
And register it in PureValidator:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
PureValidator.add_validator(:inclusion, PureValidator::Validators::InclusionValidator)
|
101
|
+
```
|
102
|
+
|
103
|
+
Now you can use it:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
class SomeValidator
|
107
|
+
include PureValidator::Validator
|
108
|
+
|
109
|
+
validates :size, inclusion: { in: [:small, :medium, :large] }
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
## Installation
|
114
|
+
|
115
|
+
Add this line to your application's Gemfile:
|
116
|
+
|
117
|
+
gem 'pure_validator'
|
118
|
+
|
119
|
+
And then execute:
|
120
|
+
|
121
|
+
$ bundle
|
122
|
+
|
123
|
+
Or install it yourself as:
|
124
|
+
|
125
|
+
$ gem install pure_validator
|
126
|
+
|
127
|
+
## Contributing
|
128
|
+
|
129
|
+
1. Fork it
|
130
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
131
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
132
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
133
|
+
5. Create new Pull Request
|
134
|
+
|
135
|
+
|
136
|
+
## Authors
|
137
|
+
|
138
|
+
- Albert Gazizov, [@deeper4k](https://twitter.com/deeper4k)
|
139
|
+
- Roman Heinrich, [@mindreframer](https://twitter.com/mindreframer)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "pure_validator"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# Helper class for arguments validation
|
2
|
+
module PureValidator::ArgsValidator
|
3
|
+
class ArgError < StandardError; end
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# Checks that specifid +obj+ is a symbol
|
8
|
+
# @param obj some object
|
9
|
+
# @param obj_name object's name, used to clarify error causer in exception
|
10
|
+
def is_symbol!(obj, obj_name)
|
11
|
+
allow!(obj, [Symbol], "#{obj_name} should be a Symbol")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Checks that specifid +obj+ is a boolean
|
15
|
+
# @param obj some object
|
16
|
+
# @param obj_name object's name, used to clarify error causer in exception
|
17
|
+
def is_boolean!(obj, obj_name)
|
18
|
+
allow!(obj, [TrueClass, FalseClass], "#{obj_name} should be a Boolean")
|
19
|
+
end
|
20
|
+
|
21
|
+
# Checks that specifid +obj+ is an integer
|
22
|
+
# @param obj some object
|
23
|
+
# @param obj_name object's name, used to clarify error causer in exception
|
24
|
+
def is_integer!(obj, obj_name)
|
25
|
+
allow!(obj, [Integer], "#{obj_name} should be an Integer")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Checks that specifid +obj+ is an Array
|
29
|
+
# @param obj some object
|
30
|
+
# @param obj_name object's name, used to clarify error causer in exception
|
31
|
+
def is_array!(obj, obj_name)
|
32
|
+
allow!(obj, [Array], "#{obj_name} should be an Array")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Checks that specifid +obj+ is a Hash
|
36
|
+
# @param obj some object
|
37
|
+
# @param obj_name object's name, used to clarify error causer in exception
|
38
|
+
def is_hash!(obj, obj_name)
|
39
|
+
allow!(obj, [Hash], "#{obj_name} should be a Hash")
|
40
|
+
end
|
41
|
+
|
42
|
+
# Checks that specifid +obj+ is an integer or float
|
43
|
+
# @param obj some object
|
44
|
+
# @param obj_name object's name, used to clarify error causer in exception
|
45
|
+
def is_integer_or_float!(obj, obj_name)
|
46
|
+
allow!(obj, [Integer, Float], "#{obj_name} should be an Integer or Float")
|
47
|
+
end
|
48
|
+
|
49
|
+
# Checks that specifid +obj+ is a string or regexp
|
50
|
+
# @param obj some object
|
51
|
+
# @param obj_name object's name, used to clarify error causer in exception
|
52
|
+
def is_string_or_regexp!(obj, obj_name)
|
53
|
+
allow!(obj, [String, Regexp], "#{obj_name} should be a String or Regexp")
|
54
|
+
end
|
55
|
+
|
56
|
+
# Checks that specifid +obj+ is a symbol or class
|
57
|
+
# @param obj some object
|
58
|
+
# @param obj_name object's name, used to clarify error causer in exception
|
59
|
+
def is_class_or_symbol!(obj, obj_name)
|
60
|
+
allow!(obj, [Symbol, Class], "#{obj_name} should be a Symbol or Class")
|
61
|
+
end
|
62
|
+
|
63
|
+
# Checks that specifid +obj+ is a symbol or block
|
64
|
+
# @param obj some object
|
65
|
+
# @param obj_name object's name, used to clarify error causer in exception
|
66
|
+
def is_symbol_or_block!(obj, obj_name)
|
67
|
+
allow!(obj, [Symbol, Proc], "#{obj_name} should be a Symbol or Proc")
|
68
|
+
end
|
69
|
+
|
70
|
+
# Checks that specifid +hash+ has a specified +key+
|
71
|
+
# @param hash some hash
|
72
|
+
# @param key hash's key
|
73
|
+
def has_key!(hash, key)
|
74
|
+
unless hash.has_key?(key)
|
75
|
+
raise ArgError, "#{hash} should have '#{key}' key"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def not_nil!(obj, obj_name)
|
80
|
+
if obj.nil?
|
81
|
+
raise ArgError, "#{obj_name} can't be nil"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def has_only_allowed_keys!(hash, keys, obj_name)
|
86
|
+
remaining_keys = hash.keys - keys
|
87
|
+
unless remaining_keys.empty?
|
88
|
+
raise ArgError, "#{obj_name} has unacceptable options #{remaining_keys}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Checks that specified +block+ is given
|
93
|
+
# @param block some block
|
94
|
+
def block_given!(block)
|
95
|
+
unless block
|
96
|
+
raise ArgError, "Block should be given"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def allow!(obj, klasses, msg)
|
103
|
+
return if klasses.any?{|klass| obj.is_a?(klass)}
|
104
|
+
raise ArgError, msg
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module PureValidator
|
2
|
+
# Copied from here https://github.com/rails/rails/blob/master/activesupport/lib/active_support/concern.rb
|
3
|
+
#
|
4
|
+
# A typical module looks like this:
|
5
|
+
#
|
6
|
+
# module M
|
7
|
+
# def self.included(base)
|
8
|
+
# base.extend ClassMethods
|
9
|
+
# base.class_eval do
|
10
|
+
# scope :disabled, -> { where(disabled: true) }
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# module ClassMethods
|
15
|
+
# ...
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# By using <tt>ActiveSupport::Concern</tt> the above module could instead be
|
20
|
+
# written as:
|
21
|
+
#
|
22
|
+
# require 'active_support/concern'
|
23
|
+
#
|
24
|
+
# module M
|
25
|
+
# extend ActiveSupport::Concern
|
26
|
+
#
|
27
|
+
# included do
|
28
|
+
# scope :disabled, -> { where(disabled: true) }
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# module ClassMethods
|
32
|
+
# ...
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module
|
37
|
+
# and a +Bar+ module which depends on the former, we would typically write the
|
38
|
+
# following:
|
39
|
+
#
|
40
|
+
# module Foo
|
41
|
+
# def self.included(base)
|
42
|
+
# base.class_eval do
|
43
|
+
# def self.method_injected_by_foo
|
44
|
+
# ...
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# module Bar
|
51
|
+
# def self.included(base)
|
52
|
+
# base.method_injected_by_foo
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# class Host
|
57
|
+
# include Foo # We need to include this dependency for Bar
|
58
|
+
# include Bar # Bar is the module that Host really needs
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
|
62
|
+
# could try to hide these from +Host+ directly including +Foo+ in +Bar+:
|
63
|
+
#
|
64
|
+
# module Bar
|
65
|
+
# include Foo
|
66
|
+
# def self.included(base)
|
67
|
+
# base.method_injected_by_foo
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# class Host
|
72
|
+
# include Bar
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
|
76
|
+
# is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
|
77
|
+
# module dependencies are properly resolved:
|
78
|
+
#
|
79
|
+
# require 'active_support/concern'
|
80
|
+
#
|
81
|
+
# module Foo
|
82
|
+
# extend ActiveSupport::Concern
|
83
|
+
# included do
|
84
|
+
# def self.method_injected_by_foo
|
85
|
+
# ...
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# module Bar
|
91
|
+
# extend ActiveSupport::Concern
|
92
|
+
# include Foo
|
93
|
+
#
|
94
|
+
# included do
|
95
|
+
# self.method_injected_by_foo
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# class Host
|
100
|
+
# include Bar # works, Bar takes care now of its dependencies
|
101
|
+
# end
|
102
|
+
module Concern
|
103
|
+
class MultipleIncludedBlocks < StandardError #:nodoc:
|
104
|
+
def initialize
|
105
|
+
super "Cannot define multiple 'included' blocks for a Concern"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.extended(base) #:nodoc:
|
110
|
+
base.instance_variable_set(:@_dependencies, [])
|
111
|
+
end
|
112
|
+
|
113
|
+
def append_features(base)
|
114
|
+
if base.instance_variable_defined?(:@_dependencies)
|
115
|
+
base.instance_variable_get(:@_dependencies) << self
|
116
|
+
return false
|
117
|
+
else
|
118
|
+
return false if base < self
|
119
|
+
@_dependencies.each { |dep| base.send(:include, dep) }
|
120
|
+
super
|
121
|
+
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
|
122
|
+
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def included(base = nil, &block)
|
127
|
+
if base.nil?
|
128
|
+
raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
|
129
|
+
|
130
|
+
@_included_block = block
|
131
|
+
else
|
132
|
+
super
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Kernel
|
2
|
+
# class_eval on an object acts like singleton_class.class_eval.
|
3
|
+
def class_eval(*args, &block)
|
4
|
+
singleton_class.class_eval(*args, &block)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Module
|
9
|
+
def remove_possible_method(method)
|
10
|
+
if method_defined?(method) || private_method_defined?(method)
|
11
|
+
undef_method(method)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def redefine_method(method, &block)
|
16
|
+
remove_possible_method(method)
|
17
|
+
define_method(method, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Class
|
22
|
+
# Declare a class-level attribute whose value is inheritable by subclasses.
|
23
|
+
# Subclasses can change their own value and it will not impact parent class.
|
24
|
+
#
|
25
|
+
# class Base
|
26
|
+
# class_attribute :setting
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# class Subclass < Base
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# Base.setting = true
|
33
|
+
# Subclass.setting # => true
|
34
|
+
# Subclass.setting = false
|
35
|
+
# Subclass.setting # => false
|
36
|
+
# Base.setting # => true
|
37
|
+
#
|
38
|
+
# In the above case as long as Subclass does not assign a value to setting
|
39
|
+
# by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
|
40
|
+
# would read value assigned to parent class. Once Subclass assigns a value then
|
41
|
+
# the value assigned by Subclass would be returned.
|
42
|
+
#
|
43
|
+
# This matches normal Ruby method inheritance: think of writing an attribute
|
44
|
+
# on a subclass as overriding the reader method. However, you need to be aware
|
45
|
+
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
|
46
|
+
# In such cases, you don't want to do changes in places but use setters:
|
47
|
+
#
|
48
|
+
# Base.setting = []
|
49
|
+
# Base.setting # => []
|
50
|
+
# Subclass.setting # => []
|
51
|
+
#
|
52
|
+
# # Appending in child changes both parent and child because it is the same object:
|
53
|
+
# Subclass.setting << :foo
|
54
|
+
# Base.setting # => [:foo]
|
55
|
+
# Subclass.setting # => [:foo]
|
56
|
+
#
|
57
|
+
# # Use setters to not propagate changes:
|
58
|
+
# Base.setting = []
|
59
|
+
# Subclass.setting += [:foo]
|
60
|
+
# Base.setting # => []
|
61
|
+
# Subclass.setting # => [:foo]
|
62
|
+
#
|
63
|
+
# For convenience, an instance predicate method is defined as well.
|
64
|
+
# To skip it, pass <tt>instance_predicate: false</tt>.
|
65
|
+
#
|
66
|
+
# Subclass.setting? # => false
|
67
|
+
#
|
68
|
+
# Instances may overwrite the class value in the same way:
|
69
|
+
#
|
70
|
+
# Base.setting = true
|
71
|
+
# object = Base.new
|
72
|
+
# object.setting # => true
|
73
|
+
# object.setting = false
|
74
|
+
# object.setting # => false
|
75
|
+
# Base.setting # => true
|
76
|
+
#
|
77
|
+
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
|
78
|
+
#
|
79
|
+
# object.setting # => NoMethodError
|
80
|
+
# object.setting? # => NoMethodError
|
81
|
+
#
|
82
|
+
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
|
83
|
+
#
|
84
|
+
# object.setting = false # => NoMethodError
|
85
|
+
#
|
86
|
+
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
|
87
|
+
def class_attribute(*attrs)
|
88
|
+
options = attrs.last.is_a?(Hash) ? attrs.pop : {}
|
89
|
+
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
|
90
|
+
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
|
91
|
+
instance_predicate = options.fetch(:instance_predicate, true)
|
92
|
+
|
93
|
+
attrs.each do |name|
|
94
|
+
define_singleton_method(name) { nil }
|
95
|
+
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
|
96
|
+
|
97
|
+
ivar = "@#{name}"
|
98
|
+
|
99
|
+
define_singleton_method("#{name}=") do |val|
|
100
|
+
singleton_class.class_eval do
|
101
|
+
remove_possible_method(name)
|
102
|
+
define_method(name) { val }
|
103
|
+
end
|
104
|
+
|
105
|
+
if singleton_class?
|
106
|
+
class_eval do
|
107
|
+
remove_possible_method(name)
|
108
|
+
define_method(name) do
|
109
|
+
if instance_variable_defined? ivar
|
110
|
+
instance_variable_get ivar
|
111
|
+
else
|
112
|
+
singleton_class.send name
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
val
|
118
|
+
end
|
119
|
+
|
120
|
+
if instance_reader
|
121
|
+
remove_possible_method name
|
122
|
+
define_method(name) do
|
123
|
+
if instance_variable_defined?(ivar)
|
124
|
+
instance_variable_get ivar
|
125
|
+
else
|
126
|
+
self.class.public_send name
|
127
|
+
end
|
128
|
+
end
|
129
|
+
define_method("#{name}?") { !!public_send(name) } if instance_predicate
|
130
|
+
end
|
131
|
+
|
132
|
+
attr_writer name if instance_writer
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
unless respond_to?(:singleton_class?)
|
139
|
+
def singleton_class?
|
140
|
+
ancestors.first != self
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module PureValidator
|
2
|
+
module Humanize
|
3
|
+
# poor mans humanize... (to not depend on inflector)
|
4
|
+
# puts humanize "hello_there", format: :class
|
5
|
+
def self.humanize(value, options = {})
|
6
|
+
options[:format] = :sentence if options.empty?
|
7
|
+
|
8
|
+
values = if value.include? '_'
|
9
|
+
value.split('_')
|
10
|
+
else
|
11
|
+
[value]
|
12
|
+
end
|
13
|
+
values.each { |v| v.downcase! }
|
14
|
+
|
15
|
+
if options[:format] == :allcaps
|
16
|
+
values.each do |value|
|
17
|
+
value.capitalize!
|
18
|
+
end
|
19
|
+
|
20
|
+
if options.empty?
|
21
|
+
options[:seperator] = " "
|
22
|
+
end
|
23
|
+
|
24
|
+
return values.join " "
|
25
|
+
end
|
26
|
+
|
27
|
+
if options[:format] == :class
|
28
|
+
values.each do |value|
|
29
|
+
value.capitalize!
|
30
|
+
end
|
31
|
+
return values.join ""
|
32
|
+
end
|
33
|
+
|
34
|
+
if options[:format] == :sentence
|
35
|
+
values[0].capitalize!
|
36
|
+
return values.join " "
|
37
|
+
end
|
38
|
+
|
39
|
+
if options[:format] == :nocaps
|
40
|
+
return values.join " "
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|