hatch 0.0.4
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 +15 -0
- data/Rakefile +6 -0
- data/hatch.gemspec +18 -0
- data/lib/hatch.rb +134 -0
- data/test/helper.rb +23 -0
- data/test/polymorphism_test.rb +18 -0
- data/test/validation_test.rb +34 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZmM2NTJmMWJlZWRmMGRmNTliMmUwMmFkYjhiYTcyM2I2ZDkwY2Y5Mg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZDhmMDliNGQ3Zjk2MzRlNzk5YTRjMDI4Y2E4ZTI3Yjg1YWZlNjhmOQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YTUwMzA0MjVmOGJjZjc0OTRkM2FhNDRlNDM3MGFkMTM0YWY4ZTc5ZGYyMzlm
|
10
|
+
NDc2ZDFhNjBlOWM1ODAwYTA3NTBmZjQzYjljNmI5OTkxYmEwMjRmZTVhMDcz
|
11
|
+
ZWZmYTc3YzRmM2ZhODA2OGRmZDU5MjUxNTRjYjdhMTZjMGYyODE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZTNlNTgwNjVjMGU0ZGU3NGIzNWMwNGNlMzlhZWEyYzJhN2EyNzZhNjQxNzE4
|
14
|
+
ZTY3NjI3ZjAxNzU2MDQ4MDgwOTE0MmIzM2M4YjU4YmIxMGU4ZDAyNzQyOGJk
|
15
|
+
ZGI1MmE0OGUzMGUxMTFjZWNjNzY1ZTUwMTgzODA2ZDZmMzgyM2I=
|
data/Rakefile
ADDED
data/hatch.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'hatch'
|
3
|
+
s.version = '0.0.4'
|
4
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
5
|
+
s.summary = 'Keep valid objects only'
|
6
|
+
s.description = "An address without a street? A person without a name? You don't need no invalid objects!"
|
7
|
+
s.authors = ['Lucas Tolchinsky']
|
8
|
+
s.email = ['lucas.tolchinsky@gmail.com']
|
9
|
+
s.homepage = 'http://github.com/tonchis/hatch'
|
10
|
+
s.license = 'MIT'
|
11
|
+
|
12
|
+
s.files = Dir[
|
13
|
+
'*.gemspec',
|
14
|
+
'Rakefile',
|
15
|
+
'test/*.*',
|
16
|
+
'lib/*.rb'
|
17
|
+
]
|
18
|
+
end
|
data/lib/hatch.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
module Hatch
|
2
|
+
def self.included(klass)
|
3
|
+
klass.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
def errors
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
@@validations = {}
|
16
|
+
@@attributes = {}
|
17
|
+
|
18
|
+
def self.extended(klass)
|
19
|
+
klass_symbol = klass.to_s.to_sym
|
20
|
+
@@validations[klass_symbol] = {}
|
21
|
+
@@attributes[klass_symbol] = []
|
22
|
+
|
23
|
+
klass.class_eval <<-EOS
|
24
|
+
class Invalid#{klass}
|
25
|
+
include InvalidInstanceMethods
|
26
|
+
end
|
27
|
+
EOS
|
28
|
+
end
|
29
|
+
|
30
|
+
def attributes(*args)
|
31
|
+
@@attributes[self.to_s.to_sym] = args
|
32
|
+
end
|
33
|
+
|
34
|
+
def certify(attribute, error, &block)
|
35
|
+
@@validations[self.to_s.to_sym][attribute] = {error: error, validation: block}
|
36
|
+
end
|
37
|
+
|
38
|
+
def hatch(args = {})
|
39
|
+
validated_attributes = []
|
40
|
+
klass_symbol = self.to_s.to_sym
|
41
|
+
@@attributes[klass_symbol].each do |attribute|
|
42
|
+
validation = @@validations[klass_symbol][attribute]
|
43
|
+
validated_attributes << Validator.validate(attribute,
|
44
|
+
args[attribute],
|
45
|
+
validation[:error],
|
46
|
+
&validation[:validation])
|
47
|
+
end
|
48
|
+
|
49
|
+
build(validated_attributes)
|
50
|
+
end
|
51
|
+
|
52
|
+
module InvalidInstanceMethods
|
53
|
+
attr_reader :errors
|
54
|
+
|
55
|
+
def initialize(*validated_attributes)
|
56
|
+
@validated_attributes = validated_attributes
|
57
|
+
select_errors
|
58
|
+
respond_to_instance_methods
|
59
|
+
end
|
60
|
+
|
61
|
+
def valid?
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def respond_to_instance_methods
|
68
|
+
extended_klass = Kernel.const_get(self.class.to_s.split("Invalid").last)
|
69
|
+
address_instance_methods = extended_klass.instance_methods(false)
|
70
|
+
|
71
|
+
attributes_with_reader = @validated_attributes.select do |validated_attribute|
|
72
|
+
address_instance_methods.include?(validated_attribute.attr)
|
73
|
+
end
|
74
|
+
|
75
|
+
attributes_with_reader.each do |attribute|
|
76
|
+
self.class.send :define_method, attribute.attr, -> {attribute.value}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def select_errors
|
81
|
+
@errors = @validated_attributes.select do |validated_attribute|
|
82
|
+
validated_attribute.invalid?
|
83
|
+
end.map(&:error)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def build(validated_attributes)
|
90
|
+
validation = -> do
|
91
|
+
valid = true
|
92
|
+
validated_attributes.each do |validated_attribute|
|
93
|
+
valid = valid && validated_attribute.valid?
|
94
|
+
end
|
95
|
+
|
96
|
+
valid
|
97
|
+
end
|
98
|
+
|
99
|
+
if validation.call
|
100
|
+
set_instance_variables(new, *validated_attributes)
|
101
|
+
else
|
102
|
+
const_get("Invalid#{self}").new(*validated_attributes)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def set_instance_variables(instance, *args)
|
107
|
+
@@attributes[instance.class.to_s.to_sym].each_with_index do |attribute, index|
|
108
|
+
instance.instance_variable_set("@#{attribute}", args[index].value)
|
109
|
+
end
|
110
|
+
instance
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class Validator
|
115
|
+
attr_reader :attr, :value, :error
|
116
|
+
|
117
|
+
def self.validate(attr, value, error, &block)
|
118
|
+
new(attr, value, error, yield(value))
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize(attr, value, error, valid)
|
122
|
+
@attr, @value, @error, @valid = attr, value, error, valid
|
123
|
+
end
|
124
|
+
|
125
|
+
def valid?
|
126
|
+
@valid
|
127
|
+
end
|
128
|
+
|
129
|
+
def invalid?
|
130
|
+
!@valid
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'pry'
|
3
|
+
require_relative '../lib/hatch'
|
4
|
+
|
5
|
+
class Address
|
6
|
+
attr_reader :city, :street, :number
|
7
|
+
|
8
|
+
include Hatch
|
9
|
+
attributes :city, :street, :number
|
10
|
+
|
11
|
+
certify(:street, "Address must have a street") do |street|
|
12
|
+
!street.nil? && !street.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
certify(:number, "Address must have a positive number") do |number|
|
16
|
+
!number.nil? && number > 0
|
17
|
+
end
|
18
|
+
|
19
|
+
certify(:city, "Address must have a city") do |city|
|
20
|
+
!city.nil? && !city.empty?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
class PolymorphismTest < Test::Unit::TestCase
|
4
|
+
def test_polymorphism
|
5
|
+
address = Address.hatch(street: "Fake St", number: 1234, city: "Buenos Aires")
|
6
|
+
assert address.is_a?(Address)
|
7
|
+
assert_equal address.street, "Fake St"
|
8
|
+
assert_equal address.number, 1234
|
9
|
+
assert_equal address.city, "Buenos Aires"
|
10
|
+
|
11
|
+
address = Address.hatch(street: "Fake St", number: -1, city: "Buenos Aires")
|
12
|
+
assert address.is_a?(Address::InvalidAddress)
|
13
|
+
assert_equal address.street, "Fake St"
|
14
|
+
assert_equal address.number, -1
|
15
|
+
assert_equal address.city, "Buenos Aires"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
class ValidTest < Test::Unit::TestCase
|
4
|
+
def test_valid
|
5
|
+
address = Address.hatch(street: "Fake St", city: "Buenos Aires", number: 1234)
|
6
|
+
assert address.is_a?(Address)
|
7
|
+
assert_equal address.instance_variable_get("@street"), "Fake St"
|
8
|
+
assert_equal address.instance_variable_get("@number"), 1234
|
9
|
+
assert_equal address.instance_variable_get("@city"), "Buenos Aires"
|
10
|
+
|
11
|
+
assert address.respond_to?(:errors)
|
12
|
+
assert address.errors.empty?
|
13
|
+
assert address.respond_to?(:valid?)
|
14
|
+
assert address.valid?
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_invalid
|
18
|
+
address = Address.hatch(city: "Buenos Aires", street: "", number: 1234)
|
19
|
+
assert address.is_a?(Address::InvalidAddress)
|
20
|
+
assert address.errors.include?("Address must have a street")
|
21
|
+
assert !address.errors.include?("Address must have a positive number")
|
22
|
+
assert !address.errors.include?("Address must have a city")
|
23
|
+
|
24
|
+
address = Address.hatch(street: "", number: -4)
|
25
|
+
assert address.is_a?(Address::InvalidAddress)
|
26
|
+
assert address.errors.include?("Address must have a street")
|
27
|
+
assert address.errors.include?("Address must have a positive number")
|
28
|
+
assert address.errors.include?("Address must have a city")
|
29
|
+
|
30
|
+
assert address.respond_to?(:valid?)
|
31
|
+
assert !address.valid?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hatch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lucas Tolchinsky
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-04-19 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: An address without a street? A person without a name? You don't need
|
14
|
+
no invalid objects!
|
15
|
+
email:
|
16
|
+
- lucas.tolchinsky@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- hatch.gemspec
|
22
|
+
- Rakefile
|
23
|
+
- test/helper.rb
|
24
|
+
- test/polymorphism_test.rb
|
25
|
+
- test/validation_test.rb
|
26
|
+
- lib/hatch.rb
|
27
|
+
homepage: http://github.com/tonchis/hatch
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 2.0.0
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: Keep valid objects only
|
51
|
+
test_files: []
|