bound 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/README.md +97 -2
- data/lib/bound.rb +8 -0
- data/lib/bound/version.rb +1 -1
- data/spec/bound_spec.rb +40 -0
- metadata +15 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40eb13fe1d57fd0d42c0d0fc5c91348173071cb2
|
4
|
+
data.tar.gz: 61658fd50d9bf0dd3790b75bca0588e80863ca4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86f3b026b006ff92b6652cd3e37df15206bcb304150c4d9c3c0fee70809b02a65fce814f67dc92f40e4df87ef93e351b182420e38d968e8c8cac27e8fbe8b9c5
|
7
|
+
data.tar.gz: ebc0035cc9f636a9af6f798a3cb70ab0420834f73b8212b2f42d74b640217a6ebb5385b7e3e98830c42f6cbe0282757f2839e55a44d0c55b4d0167437177afd7
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,19 @@
|
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/neopoly/bound.png)](https://travis-ci.org/neopoly/bound) [![Gem Version](https://badge.fury.io/rb/bound.png)](http://badge.fury.io/rb/bound) [![Code Climate](https://codeclimate.com/github/neopoly/bound.png)](https://codeclimate.com/github/neopoly/bound)
|
4
4
|
|
5
|
-
|
5
|
+
**In short:** The mission: Bring the notion of interfaces to ruby.
|
6
|
+
|
7
|
+
**More detailed:** When you build separated or distributed architectures in ruby,
|
8
|
+
you probably encountered the problem of stale mocks or wrongly mocked interfaces
|
9
|
+
of specific services at the boundaries of the different domains.
|
10
|
+
|
11
|
+
To tackle this problem, we use `Bound`. Instead of providing just a list of
|
12
|
+
arguments to a poor little boundary method, it will just accept an argument, its
|
13
|
+
request, to speak in more technical terms. By implementing the request and
|
14
|
+
response objects through `Bound`, you get validated interfaces and more explicit
|
15
|
+
and self documenting code for free.
|
16
|
+
|
17
|
+
See **Usage** below for more details with a concrete example.
|
6
18
|
|
7
19
|
## Installation
|
8
20
|
|
@@ -20,7 +32,90 @@ Or install it yourself as:
|
|
20
32
|
|
21
33
|
## Usage
|
22
34
|
|
23
|
-
|
35
|
+
Consider the folowing scenario:
|
36
|
+
|
37
|
+
A generic domain which is responsible for administration and management of user
|
38
|
+
registrations:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
module UserDesk
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
It will somehow provide access to a registration service which gives you the
|
46
|
+
possibility to create new user accounts:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class UserDesk::RegistrationService
|
50
|
+
def register_account(email, password)
|
51
|
+
ensure_validity!(email)
|
52
|
+
user_uid = do_things_on_a_magical_repository(email, password)
|
53
|
+
|
54
|
+
user_uid
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
# ...
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
Since the scope of this service can (and will) be very large, it will be painful
|
63
|
+
to provide consistency around the different other domains, which get an instance
|
64
|
+
of the registration service injected. Especially order changes in a larger
|
65
|
+
argument list or added optional arguments could lead to false passing tests and
|
66
|
+
therefor probably runtime bugs.
|
67
|
+
|
68
|
+
By utilizing `Bound`, you could implement this service like following:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
class UserDesk::RegistrationService
|
72
|
+
Registration = Bound.required(
|
73
|
+
:email,
|
74
|
+
:password
|
75
|
+
)
|
76
|
+
|
77
|
+
SuccessfulRegistration = Bound.required(:user_uid)
|
78
|
+
|
79
|
+
def register_account(registration)
|
80
|
+
ensure_validity!(registration.email)
|
81
|
+
user_uid = do_things_on_a_magical_repository(
|
82
|
+
registration.email,
|
83
|
+
registration.password
|
84
|
+
)
|
85
|
+
|
86
|
+
SuccessfulRegistration.new(:user_uid => user_uid)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
# ...
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
The consumer would now instanciate the boundary class instead of just
|
95
|
+
passing arbitrary arguments to the service:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
registration = UserDesk::RegistrationService::Registration.new(
|
99
|
+
:email => params[:email],
|
100
|
+
:password => params[:password]
|
101
|
+
)
|
102
|
+
|
103
|
+
result = registration_service.register_account(registration)
|
104
|
+
|
105
|
+
do_stuff_with(result.user_uid)
|
106
|
+
```
|
107
|
+
|
108
|
+
Side note: the `Registration` bound here would also accept any `Object`, which provides the
|
109
|
+
methods `email` and `password`.
|
110
|
+
|
111
|
+
Bound would also loudly fail, if one of the required arguments is omitted or a
|
112
|
+
unknown argument is provided. (Specific additional features like nested and
|
113
|
+
optional arguments can be seen in the specs).
|
114
|
+
|
115
|
+
By concretinzing the boundaries, the overall structure of your architecture will
|
116
|
+
become more rigid and solid. The mocking part on the consumer-side would only
|
117
|
+
occur for the actual `register_account` call, which is fairly trivial now from
|
118
|
+
the perspective of boundaries (known object in, known object out).
|
24
119
|
|
25
120
|
## Contributing
|
26
121
|
|
data/lib/bound.rb
CHANGED
@@ -241,6 +241,14 @@ class Bound
|
|
241
241
|
attribute
|
242
242
|
end
|
243
243
|
|
244
|
+
def ==(other)
|
245
|
+
return false unless other
|
246
|
+
|
247
|
+
get_attributes.all? do |attribute|
|
248
|
+
attribute.value == other.public_send(attribute.name)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
244
252
|
private
|
245
253
|
|
246
254
|
def validate!
|
data/lib/bound/version.rb
CHANGED
data/spec/bound_spec.rb
CHANGED
@@ -63,6 +63,32 @@ describe Bound do
|
|
63
63
|
assert_includes user.get_attributes.map(&:name), :age
|
64
64
|
end
|
65
65
|
|
66
|
+
describe 'equality' do
|
67
|
+
let(:user) { User.new(hash) }
|
68
|
+
|
69
|
+
it 'is given if all the attributes are same' do
|
70
|
+
reference_user = User.new(hash)
|
71
|
+
|
72
|
+
assert_equal user, reference_user
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'is not given if attributes differ' do
|
76
|
+
reference_user = User.new(hash.merge(:name => 'DIFF'))
|
77
|
+
|
78
|
+
refute_equal user, reference_user
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'is given for other objects with same signature' do
|
82
|
+
reference_user = Struct.new(:name, :age).new(user.name, user.age)
|
83
|
+
|
84
|
+
assert_equal user, reference_user
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'is not given for nil' do
|
88
|
+
refute_equal user, nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
66
92
|
describe 'wrong initialization' do
|
67
93
|
it 'fails if new is not called with symbols' do
|
68
94
|
assert_raises ArgumentError do
|
@@ -221,6 +247,20 @@ describe Bound do
|
|
221
247
|
end
|
222
248
|
end
|
223
249
|
|
250
|
+
describe 'equality' do
|
251
|
+
let(:user) { BloggingUser.new(hash) }
|
252
|
+
it 'is given if the nested attributes are equal' do
|
253
|
+
assert_equal BloggingUser.new(hash), user
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'is not given if nested attributes differ' do
|
257
|
+
second_hash = Marshal.load(Marshal.dump hash)
|
258
|
+
second_hash[:posts][0][:title] = 'DIFFERENT'
|
259
|
+
|
260
|
+
refute_equal BloggingUser.new(second_hash), user
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
224
264
|
it 'fails if posts is no array' do
|
225
265
|
hash[:posts] = {:title => 'broken'}
|
226
266
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bound
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakob Holderbaum
|
@@ -9,62 +9,62 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-04-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - ~>
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '1.3'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - ~>
|
25
|
+
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.3'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: minitest
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - ~>
|
46
|
+
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: 5.0.7
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- - ~>
|
53
|
+
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: 5.0.7
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: simplecov
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
|
-
- -
|
60
|
+
- - ">="
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: '0'
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - ">="
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
70
|
description:
|
@@ -75,8 +75,8 @@ executables: []
|
|
75
75
|
extensions: []
|
76
76
|
extra_rdoc_files: []
|
77
77
|
files:
|
78
|
-
- .gitignore
|
79
|
-
- .travis.yml
|
78
|
+
- ".gitignore"
|
79
|
+
- ".travis.yml"
|
80
80
|
- Gemfile
|
81
81
|
- LICENSE.txt
|
82
82
|
- README.md
|
@@ -99,17 +99,17 @@ require_paths:
|
|
99
99
|
- lib
|
100
100
|
required_ruby_version: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- -
|
102
|
+
- - ">="
|
103
103
|
- !ruby/object:Gem::Version
|
104
104
|
version: '0'
|
105
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
|
-
- -
|
107
|
+
- - ">="
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: '0'
|
110
110
|
requirements: []
|
111
111
|
rubyforge_project:
|
112
|
-
rubygems_version: 2.
|
112
|
+
rubygems_version: 2.2.2
|
113
113
|
signing_key:
|
114
114
|
specification_version: 4
|
115
115
|
summary: Implements a nice helper for fast boundary definitions
|