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 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
@@ -0,0 +1,6 @@
1
+ task :test do
2
+ Dir["test/*_test.rb"].each { |file| load file }
3
+ end
4
+
5
+ task :default => :test
6
+
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: []