bound 1.0.0 → 1.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 +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
|
[](https://travis-ci.org/neopoly/bound) [](http://badge.fury.io/rb/bound) [](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
|