use_case 0.1.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.
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+ gemspec
3
+
4
+ # Only used for tests
5
+ gem "virtus", :git => "https://github.com/solnic/virtus.git"
6
+
7
+ # Validations are optional, but required in order to test UseCase
8
+ # itself
9
+ gem "activemodel"
data/Gemfile.lock ADDED
@@ -0,0 +1,51 @@
1
+ GIT
2
+ remote: https://github.com/solnic/virtus.git
3
+ revision: 97a161f19d20a961094112464075c284d68d35e4
4
+ specs:
5
+ virtus (1.0.0.beta1)
6
+ abstract_type (~> 0.0.5)
7
+ adamantium (~> 0.0.7)
8
+ backports (~> 3.0, >= 3.1.0)
9
+ coercible (~> 0.2)
10
+ descendants_tracker (~> 0.0.1)
11
+
12
+ PATH
13
+ remote: .
14
+ specs:
15
+ use_case (0.1.0)
16
+
17
+ GEM
18
+ remote: http://rubygems.org/
19
+ specs:
20
+ abstract_type (0.0.5)
21
+ backports (~> 3.0, >= 3.0.3)
22
+ activemodel (3.2.12)
23
+ activesupport (= 3.2.12)
24
+ builder (~> 3.0.0)
25
+ activesupport (3.2.12)
26
+ i18n (~> 0.6)
27
+ multi_json (~> 1.0)
28
+ adamantium (0.0.7)
29
+ backports (~> 3.0, >= 3.0.3)
30
+ ice_nine (~> 0.7.0)
31
+ backports (3.1.1)
32
+ builder (3.0.4)
33
+ coercible (0.2.0)
34
+ backports (~> 3.0, >= 3.1.0)
35
+ descendants_tracker (~> 0.0.1)
36
+ descendants_tracker (0.0.1)
37
+ i18n (0.6.4)
38
+ ice_nine (0.7.0)
39
+ minitest (4.7.0)
40
+ multi_json (1.6.1)
41
+ rake (10.0.3)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ activemodel
48
+ minitest (~> 4)
49
+ rake
50
+ use_case!
51
+ virtus!
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Gitorious AS
2
+
3
+ Christian Johansen (christian@gitorious.com)
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/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new(:test) do |test|
4
+ test.libs << "test"
5
+ test.pattern = "test/**/*_test.rb"
6
+ end
7
+
8
+ task :default => :test
data/Readme.md ADDED
@@ -0,0 +1,298 @@
1
+ # Use Case
2
+
3
+ Compose non-trivial business logic into use cases, that combine:
4
+
5
+ * Input parameter abstractions; type safety and coercion, and white-listing of
6
+ supported input for any given operation
7
+ * Pre-conditions: System-level conditions that must be met, e.g. "a user must be
8
+ logged in" etc.
9
+ * Input parameter validation: ActiveRecord-like validations as composable
10
+ objects. Combine specific sets of validation rules for the same input in
11
+ different contexts etc.
12
+ * Commands: Avoid defensive coding by performing the core actions in commands
13
+ that receive type-converted input, and are only executed when pre-conditions
14
+ are met and input validated.
15
+
16
+ ## Example
17
+
18
+ At its very simplest, a use case wraps a command. A command is any Ruby object
19
+ with an `execute` method that receives one argument (input parameters). Because
20
+ UseCase is intended as a mechanism for implementing non-trivial use cases, the
21
+ following contrived example will seem particularly useless.
22
+
23
+ ```rb
24
+ require "use_case"
25
+
26
+ class HelloWorldCommand
27
+ def execute(params)
28
+ puts "Hello, #{params[:place]}!"
29
+ end
30
+ end
31
+
32
+ class PrintHelloWorld
33
+ include UseCase
34
+
35
+ def initialize
36
+ command(HelloWorldCommand.new)
37
+ end
38
+ end
39
+
40
+ # Usage
41
+ PrintHelloWorld.new.execute(:place => "World")
42
+ ```
43
+
44
+ Not that you are free to design your constructors any way you want.
45
+
46
+ ## Useful example
47
+
48
+ A more useful example will use every aspect of a `UseCase`. Pre-conditions are
49
+ conditions not directly related to input parameters, and whose failure signifies
50
+ other forms of errors than simple validation errors. If you have a Rails
51
+ application that uses controller filters, then those are very likely good
52
+ candidates for pre-conditions.
53
+
54
+ The following example is a simplified use case from
55
+ [Gitorious](http://gitorious.org) where we want to create a new repository. To
56
+ do this, we need a user that can admin the project under which we want the new
57
+ repository to live.
58
+
59
+ This example illustrates how to solve common design challenges in Rails
60
+ applications, but that does not mean that `UseCase` is only useful to Rails
61
+ applications.
62
+
63
+ ```rb
64
+ require "use_case"
65
+ require "virtus"
66
+
67
+ # Input parameters can be sanitized and pre-processed anyway you like. One nice
68
+ # way to go about it is to use Datamapper 2's Virtus gem to define a parameter
69
+ # set.
70
+ #
71
+ # This class uses Project.find to look up a project by id if project_id is
72
+ # provided and project is not. This is the only class that has
73
+ class NewRepositoryInput
74
+ include Virtus
75
+ attribute :name, String
76
+ attribute :description, String
77
+ attribute :project, Project
78
+ attribute :project_id, Integer
79
+
80
+ def project
81
+ @project ||= Project.find(@project_id)
82
+ end
83
+ end
84
+
85
+ # Validate new repositories. Extremely simplified example.
86
+ NewRepositoryValidator = UseCase::Validator.define do
87
+ validates_presence_of :name, :project
88
+ end
89
+
90
+ # This is often implemented as a controller filter in many Rails apps.
91
+ # Unfortunately that means we have to duplicate the check when exposing the use
92
+ # case in other contexts (e.g. a stand-alone API app, console API etc).
93
+ class UserLoggedInPrecondition
94
+ # The constructor is only used by us and can look and do whever we want
95
+ def initialize(user)
96
+ @user = user
97
+ end
98
+
99
+ # A pre-condition must define this method
100
+ # Params is an instance of NewRepositoryInput
101
+ def satiesfied?(params)
102
+ !@user.nil?
103
+ end
104
+ end
105
+
106
+ # Another pre-condition that uses app-wide state
107
+ class ProjectAdminPrecondition
108
+ def initialize(auth, user)
109
+ @auth = auth
110
+ @user = user
111
+ end
112
+
113
+ def satiesfied?(params)
114
+ @auth.can_admin?(@user, params.project)
115
+ end
116
+ end
117
+
118
+ # The business logic. Here we can safely assume that all pre-conditions are
119
+ # satiesfied, and that input is valid and has the correct type.
120
+ class CreateRepositoryCommand
121
+ def initialize(user)
122
+ @user = user
123
+ end
124
+
125
+ # Params is an instance of NewRepositoryInput
126
+ def execute(params)
127
+ params.project.repositories.create(:name => params.name, :user => @user)
128
+ end
129
+ end
130
+
131
+ # The UseCase - this is just wiring together the various classes
132
+ class CreateRepository
133
+ include UseCase
134
+
135
+ # There's no contract to satiesfy with the constructor - design it to receive
136
+ # any dependencies you need.
137
+ def initialize(auth, user)
138
+ input_class(NewRepositoryInput)
139
+ pre_condition(UserLoggedInPrecondition.new(user))
140
+ pre_condition(ProjectAdminPrecondition.new(auth, user))
141
+ # Multiple validators can be added if needed
142
+ validator(NewRepositoryValidator)
143
+ command(CreateRepositoryCommand.new(user))
144
+ end
145
+ end
146
+
147
+ # Finally, the actual usage. This example is a Rails controller
148
+ class RepositoryController < ApplicationController
149
+ include Gitorious::Authorization # Adds stuff like can_admin?(actor, thing)
150
+
151
+ # ...
152
+
153
+ def create
154
+ outcome = CreateRepository.new(self, current_user).execute(params)
155
+
156
+ outcome.pre_condition_failed do |condition|
157
+ redirect_to(login_path) and return if condition.is_a?(UserLoggedInPrecondition)
158
+ flash[:error] = "You're not allowed to do that"
159
+ redirect_to project_path
160
+ end
161
+
162
+ outcome.failure do |model|
163
+ # Render form with validation errors
164
+ render :new, :locals => { :repository => model }
165
+ end
166
+
167
+ outcome.success do |repository|
168
+ redirect_to(repository_path(repository))
169
+ end
170
+ end
171
+ end
172
+ ```
173
+
174
+ ## Input sanitation
175
+
176
+ In your `UseCase` instance (typically in the constructor), you can call the
177
+ `input_class` method to specify which class is used to santize inputs. If you do
178
+ not use this, inputs are forwarded to pre-conditions and commands untouched
179
+ (i.e. as a `Hash`).
180
+
181
+ Datamapper 2's [Virtus](https://github.com/solnic/virtus) is a very promising
182
+ solution for input sanitation and some level of type-safety. If you provide a
183
+ `Virtus` backed class as `input_class` you will get an instance of that class as
184
+ `params` in pre-conditions and commands.
185
+
186
+ ## Validations
187
+
188
+ The validator uses `ActiveModel::Validations`, so any Rails validation can go in
189
+ here. The main difference is that the validator is created as a stand-alone
190
+ object that can be used with any model instance. This design allows you to
191
+ define multiple context-sensitive validations for a single object.
192
+
193
+ You can of course provide your own validation if you want - any object that
194
+ defines `call(object)` and returns something that responds to `valid?` is good.
195
+ I am following the Datamapper project closely in this area.
196
+
197
+ Because `UseCase::Validation` is not a required part of `UseCase`, and people
198
+ may want to control their own dependencies, `activemodel` is _not_ a hard
199
+ dependency. To use this feature, `gem install activemodel`.
200
+
201
+ ## Pre-conditions
202
+
203
+ A pre-condition is any object that responds to `satiesfied?(params)` where
204
+ params will either be a `Hash` or an instance of whatever you passed to
205
+ `input_class`. The method should return `true/false`. If it raises, the outcome
206
+ of the use case will call the `pre_condition_failed` block with the raised
207
+ error. If it fails, the `pre_condition_failed` block will be called with the
208
+ pre-condition instance that failed.
209
+
210
+ ## Commands
211
+
212
+ A command is any Ruby object that defines an `execute(params)` method. Its
213
+ return value will be passed to the outcome's `success` block. Any errors raised
214
+ by this method is not rescued, so be sure to wrap `use_case.execute(params)` in
215
+ a rescue block if you're worried that it raises.
216
+
217
+ ## Use cases
218
+
219
+ A use case simply glues together all the components. Define a class, include
220
+ `UseCase`, and configure the instance in the constructor. The constructor can
221
+ take any arguments you like, making this solution suitable for DI (dependency
222
+ injection) style designs.
223
+
224
+ The use case can optionally call `input_class` once, `pre_condition` multiple
225
+ times, and `validator` multiple times. It *must* call `command` once with the
226
+ command object.
227
+
228
+ ## Outcomes
229
+
230
+ `UseCase#execute` returns an `Outcome`. You can use the outcome in primarily two
231
+ ways. The primary approach is one that takes blocks for the three situations:
232
+ `success(&block)`, `failure(&block)`, and `pre_condition_failed(&block)`. Only
233
+ one of these will ever be called. This allows you to declaratively describe
234
+ further flow in your program.
235
+
236
+ For use on the console and other situations, this style is not the most
237
+ convenient. For that reason each of the three methods above can also be called
238
+ without a block, and they always return something:
239
+
240
+ * `success` returns the command result
241
+ * `failure` returns the validation object (e.g. `failure.errors.inspect`)
242
+ * `pre_condition_failed` returns the pre-condition that failed, *or* an
243
+ exception object, if a pre-condition raised an exception.
244
+
245
+ In addition to these, the outcome object responds to `success?` and
246
+ `pre_condition_failed?`.
247
+
248
+ ## Inspiration and design considerations
249
+
250
+ This small library is very much inspired by
251
+ [Mutations](http://github.com/cypriss/mutations). Nice as it is, I found it to
252
+ be a little limiting in terms of what kinds of commands it could comfortably
253
+ encapsulate. Treating everything as a hash of inputs makes it hard to do things
254
+ like "redirect if there's no user, render form if there are validation errors
255
+ and redirect to new object if successful".
256
+
257
+ As I started working on my own solution I quickly recognized the power in
258
+ separating input parameter type constraints/coercions from validation rules.
259
+ This is another area where UseCase differs from Mutations. UseCase is probably
260
+ slightly more "enterprise" than Mutations, but fits the kinds of problems I
261
+ intend to solve with it better than Mutations did.
262
+
263
+ ## Testing
264
+
265
+ Using UseCase will allow you to test almost all logic completely without loading
266
+ Rails. In the example above, the input conversion is the only place that
267
+ directly touches any classes from the Rails application. The rest of the classes
268
+ work by the "data in, data out" principle, meaning you can easily test them with
269
+ any kind of object (which spares you of loading heavy ActiveRecord-bound models,
270
+ running opaque controller tets etc).
271
+
272
+ ## License
273
+
274
+ UseCase is free software licensed under the MIT license.
275
+
276
+ ```
277
+ The MIT License (MIT)
278
+
279
+ Copyright (C) 2013 Gitorious AS
280
+
281
+ Permission is hereby granted, free of charge, to any person obtaining a copy
282
+ of this software and associated documentation files (the "Software"), to deal
283
+ in the Software without restriction, including without limitation the rights
284
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
285
+ copies of the Software, and to permit persons to whom the Software is
286
+ furnished to do so, subject to the following conditions:
287
+
288
+ The above copyright notice and this permission notice shall be included in all
289
+ copies or substantial portions of the Software.
290
+
291
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
292
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
293
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
294
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
295
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
296
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
297
+ SOFTWARE.
298
+ ```
data/lib/use_case.rb ADDED
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+ # --
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (C) 2013 Gitorious AS
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #++
25
+ require "use_case/outcome"
26
+ require "use_case/validator"
27
+ require "ostruct"
28
+
29
+ module UseCase
30
+ def input_class(input_class)
31
+ @input_class = input_class
32
+ end
33
+
34
+ def validator(validator)
35
+ validators << validator
36
+ end
37
+
38
+ def pre_condition(pc)
39
+ pre_conditions << pc
40
+ end
41
+
42
+ def command(command)
43
+ @command = command
44
+ end
45
+
46
+ def execute(params)
47
+ input = @input_class && @input_class.new(params) || params
48
+
49
+ if outcome = verify_pre_conditions(input)
50
+ return outcome
51
+ end
52
+
53
+ if outcome = validate_params(input)
54
+ return outcome
55
+ end
56
+
57
+ SuccessfulOutcome.new(self, @command.execute(input))
58
+ end
59
+
60
+ private
61
+ def verify_pre_conditions(input)
62
+ pre_conditions.each do |pc|
63
+ begin
64
+ return PreConditionFailed.new(self, pc) if !pc.satiesfied?(input)
65
+ rescue Exception => err
66
+ return PreConditionFailed.new(self, err)
67
+ end
68
+ end
69
+ nil
70
+ end
71
+
72
+ def validate_params(input)
73
+ validators.each do |validator|
74
+ result = validator.call(input)
75
+ return FailedOutcome.new(self, result) if !result.valid?
76
+ end
77
+ nil
78
+ end
79
+
80
+ def pre_conditions; @pre_conditions ||= []; end
81
+ def validators; @validators ||= []; end
82
+ end
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+ # --
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (C) 2013 Gitorious AS
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #++
25
+
26
+ module UseCase
27
+ class Outcome
28
+ attr_reader :use_case
29
+
30
+ def initialize(use_case = nil)
31
+ @use_case = use_case
32
+ end
33
+
34
+ def pre_condition_failed?; false; end
35
+ def success?; false; end
36
+ def success; end
37
+ def pre_condition_failed; end
38
+ def failure; end
39
+ end
40
+
41
+ class SuccessfulOutcome < Outcome
42
+ def initialize(use_case = nil, result = nil)
43
+ super(use_case)
44
+ @result = result
45
+ end
46
+
47
+ def success?; true; end
48
+
49
+ def success
50
+ yield @result if block_given?
51
+ @result
52
+ end
53
+
54
+ def to_s
55
+ "#<UseCase::SuccessfulOutcome: #{@result}>"
56
+ end
57
+ end
58
+
59
+ class PreConditionFailed < Outcome
60
+ def initialize(use_case = nil, pre_condition = nil)
61
+ super(use_case)
62
+ @pre_condition = pre_condition
63
+ end
64
+
65
+ def pre_condition_failed?; true; end
66
+
67
+ def pre_condition_failed
68
+ yield @pre_condition if block_given?
69
+ @pre_condition
70
+ end
71
+
72
+ def to_s
73
+ "#<UseCase::PreConditionFailed: #{@pre_condition}>"
74
+ end
75
+ end
76
+
77
+ class FailedOutcome < Outcome
78
+ def initialize(use_case = nil, errors = nil)
79
+ super(use_case)
80
+ @errors = errors
81
+ end
82
+
83
+ def failure
84
+ yield @errors if block_given?
85
+ @errors
86
+ end
87
+
88
+ def to_s
89
+ "#<UseCase::FailedOutcome: #{@errors}>"
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+ # --
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (C) 2013 Gitorious AS
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #++
25
+ require "active_model"
26
+
27
+ module UseCase
28
+ module Validator
29
+ def self.define(&block)
30
+ klass = Class.new do
31
+ include ActiveModel::Validations
32
+
33
+ def initialize(target)
34
+ @target = target
35
+ end
36
+
37
+ def method_missing(name, *args, &block)
38
+ @target.send(name, *args, &block)
39
+ end
40
+
41
+ def self.call(object)
42
+ validator = new(object)
43
+ validator.valid?
44
+ validator
45
+ end
46
+ end
47
+
48
+ klass.instance_eval(&block)
49
+ klass
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ # --
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (C) 2013 Gitorious AS
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #++
25
+
26
+ module UseCase
27
+ VERSION = "0.1.0"
28
+ end
data/test.rb ADDED
@@ -0,0 +1,96 @@
1
+ require "virtus"
2
+ require "use_case"
3
+
4
+ class Project
5
+ attr_reader :title
6
+ def initialize(title); @title = title; end
7
+ def self.find(id); new("<Project> My name is #{id}"); end
8
+ end
9
+
10
+ class User
11
+ attr_reader :name
12
+ def initialize(name); @name = name; end
13
+ def self.find(id); new("<User> My name is #{id}"); end
14
+ end
15
+
16
+ class NewRepositoryInput
17
+ include Virtus
18
+
19
+ attribute :name, String
20
+ attribute :description, String
21
+ attribute :merge_requests_enabled, Boolean, :default => true
22
+ attribute :private_repository, Boolean, :default => true
23
+
24
+ attribute :user, User
25
+ attribute :user_id, Integer
26
+ attribute :project, Project
27
+ attribute :project_id, Integer
28
+
29
+ def project; @project ||= Project.find(@project_id); end
30
+ def user; @user ||= User.find(@user_id); end
31
+ end
32
+
33
+ class NewRepositoryValidator
34
+ include UseCase::Validator
35
+ validates_presence_of :name, :description, :merge_requests_enabled, :private_repository
36
+ end
37
+
38
+ class UserLoggedInPrecondition
39
+ def initialize(user)
40
+ @user = user
41
+ end
42
+
43
+ def satiesfied?(params)
44
+ @user[:id] == 42
45
+ end
46
+ end
47
+
48
+ class ProjectAdminPrecondition
49
+ def initialize(user)
50
+ @user = user
51
+ end
52
+
53
+ def satiesfied?(params)
54
+ @user[:name] == params.name
55
+ end
56
+ end
57
+
58
+ class CreateRepositoryCommand
59
+ def initialize(user)
60
+ @user = user
61
+ end
62
+
63
+ def execute(params)
64
+ @user.merge(params)
65
+ end
66
+ end
67
+
68
+ class CreateRepository
69
+ include UseCase
70
+
71
+ def initialize(user)
72
+ input_class(NewRepositoryInput)
73
+ pre_condition(UserLoggedInPrecondition.new(user))
74
+ pre_condition(ProjectAdminPrecondition.new(user))
75
+ validator(NewRepositoryValidator)
76
+ command(CreateRepositoryCommand.new(user))
77
+ end
78
+ end
79
+
80
+ ### Example
81
+
82
+ outcome = CreateRepository.new({ :id => 42, :name => "Boy" }).execute({ :name => "Boy" })
83
+
84
+ outcome.precondition_failed do |pc|
85
+ puts "Pre-condition failed! #{pc}"
86
+ end
87
+
88
+ outcome.success do |result|
89
+ puts "Your request was successful! #{result}"
90
+ end
91
+
92
+ outcome.failure do |errors|
93
+ puts "There was a failure #{errors}"
94
+ end
95
+
96
+ puts outcome.to_s
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+ # --
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (C) 2013 Gitorious AS
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #++
25
+ require "virtus"
26
+ require "use_case"
27
+
28
+ class Model
29
+ attr_reader :id, :name
30
+
31
+ def initialize(id, name)
32
+ @id = id
33
+ @name = name
34
+ end
35
+
36
+ def to_s; "#<#{self.class.name}[id: #{id}, name: #{name}]>"; end
37
+ def self.find(id); new(id, "From #{self.class.name}.find"); end
38
+ end
39
+
40
+ class Project < Model; end
41
+ class Repository < Model; end
42
+
43
+ class User < Model
44
+ def can_admin?; @can_admin; end
45
+ def can_admin=(ca); @can_admin = ca; end
46
+ end
47
+
48
+ class NewRepositoryInput
49
+ include Virtus
50
+ attribute :name, String
51
+ end
52
+
53
+ NewRepositoryValidator = UseCase::Validator.define do
54
+ validates_presence_of :name
55
+ end
56
+
57
+ class UserLoggedInPrecondition
58
+ def initialize(user); @user = user; end
59
+ def satiesfied?(params); @user && @user.id == 42; end
60
+ end
61
+
62
+ class ProjectAdminPrecondition
63
+ def initialize(user); @user = user; end
64
+ def satiesfied?(params); @user.can_admin?; end
65
+ end
66
+
67
+ class CreateRepositoryCommand
68
+ def initialize(user); @user = user; end
69
+ def execute(params); Repository.new(1349, params.name); end
70
+ end
71
+
72
+ class CreateRepository
73
+ include UseCase
74
+
75
+ def initialize(user)
76
+ input_class(NewRepositoryInput)
77
+ pre_condition(UserLoggedInPrecondition.new(user))
78
+ pre_condition(ProjectAdminPrecondition.new(user))
79
+ validator(NewRepositoryValidator)
80
+ command(CreateRepositoryCommand.new(user))
81
+ end
82
+ end
83
+
84
+ class ExplodingRepository
85
+ include UseCase
86
+
87
+ def initialize(user)
88
+ cmd = CreateRepositoryCommand.new(user)
89
+ def cmd.execute(params); raise "Crash!"; end
90
+ command(cmd)
91
+ end
92
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ # --
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (C) 2013 Gitorious AS
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #++
25
+ require "bundler/setup"
26
+ require "minitest/autorun"
27
+
28
+ Bundler.require(:default, :test)
@@ -0,0 +1,144 @@
1
+ # encoding: utf-8
2
+ # --
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (C) 2013 Gitorious AS
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #++
25
+ require "test_helper"
26
+ require "use_case/outcome"
27
+
28
+ describe UseCase::Outcome do
29
+ it "exposes use case" do
30
+ use_case = 42
31
+ outcome = UseCase::Outcome.new(use_case)
32
+
33
+ assert_equal use_case, outcome.use_case
34
+ end
35
+
36
+ it "defaults to not failing and not being successful (noop)" do
37
+ outcome = UseCase::Outcome.new
38
+ outcome.success { fail "Shouldn't succeed" }
39
+ outcome.pre_condition_failed { fail "Shouldn't have failed pre-conditions" }
40
+ outcome.failure { fail "Shouldn't fail" }
41
+
42
+ refute outcome.pre_condition_failed?
43
+ refute outcome.success?
44
+ end
45
+
46
+ describe UseCase::SuccessfulOutcome do
47
+ it "exposes use case" do
48
+ use_case = { :id => 42 }
49
+ outcome = UseCase::SuccessfulOutcome.new(use_case)
50
+
51
+ assert_equal use_case, outcome.use_case
52
+ end
53
+
54
+ it "does not fail" do
55
+ outcome = UseCase::SuccessfulOutcome.new
56
+ outcome.pre_condition_failed { fail "Shouldn't have failed pre-conditions" }
57
+ outcome.failure { fail "Shouldn't fail" }
58
+
59
+ refute outcome.pre_condition_failed?
60
+ assert outcome.success?
61
+ end
62
+
63
+ it "yields and returns result" do
64
+ result = 42
65
+ yielded_result = nil
66
+ outcome = UseCase::SuccessfulOutcome.new(nil, result)
67
+ returned_result = outcome.success { |res| yielded_result = res }
68
+
69
+ assert_equal result, yielded_result
70
+ assert_equal result, returned_result
71
+ end
72
+
73
+ it "gets result without block" do
74
+ outcome = UseCase::SuccessfulOutcome.new(nil, 42)
75
+ assert_equal 42, outcome.success
76
+ end
77
+ end
78
+
79
+ describe UseCase::PreConditionFailed do
80
+ it "exposes use case" do
81
+ use_case = { :id => 42 }
82
+ outcome = UseCase::PreConditionFailed.new(use_case)
83
+
84
+ assert_equal use_case, outcome.use_case
85
+ end
86
+
87
+ it "does not succeed or fail" do
88
+ outcome = UseCase::PreConditionFailed.new
89
+ outcome.success { fail "Shouldn't succeed" }
90
+ outcome.failure { fail "Shouldn't fail" }
91
+
92
+ assert outcome.pre_condition_failed?
93
+ refute outcome.success?
94
+ end
95
+
96
+ it "yields and returns failed pre-condition" do
97
+ pre_condition = 42
98
+ yielded_pc = nil
99
+ outcome = UseCase::PreConditionFailed.new(nil, pre_condition)
100
+ returned_pc = outcome.pre_condition_failed { |pc| yielded_pc = pc }
101
+
102
+ assert_equal pre_condition, yielded_pc
103
+ assert_equal pre_condition, returned_pc
104
+ end
105
+
106
+ it "gets pre_condition without block" do
107
+ outcome = UseCase::PreConditionFailed.new(nil, 42)
108
+ assert_equal 42, outcome.pre_condition_failed
109
+ end
110
+ end
111
+
112
+ describe UseCase::FailedOutcome do
113
+ it "exposes use case" do
114
+ use_case = { :id => 42 }
115
+ outcome = UseCase::FailedOutcome.new(use_case)
116
+
117
+ assert_equal use_case, outcome.use_case
118
+ end
119
+
120
+ it "does not succeed or fail pre-conditions" do
121
+ outcome = UseCase::FailedOutcome.new
122
+ outcome.success { fail "Shouldn't succeed" }
123
+ outcome.pre_condition_failed { fail "Shouldn't fail pre-conditions" }
124
+
125
+ refute outcome.pre_condition_failed?
126
+ refute outcome.success?
127
+ end
128
+
129
+ it "yields and returns validation failure" do
130
+ failure = 42
131
+ yielded_result = nil
132
+ outcome = UseCase::FailedOutcome.new(nil, failure)
133
+ returned_result = outcome.failure { |result| yielded_result = result }
134
+
135
+ assert_equal failure, yielded_result
136
+ assert_equal failure, returned_result
137
+ end
138
+
139
+ it "gets failure without block" do
140
+ outcome = UseCase::FailedOutcome.new(nil, 42)
141
+ assert_equal 42, outcome.failure
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ # --
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (C) 2013 Gitorious AS
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #++
25
+ require "test_helper"
26
+ require "use_case/validator"
27
+
28
+ NewPersonValidator = UseCase::Validator.define do
29
+ validates_presence_of :name
30
+ end
31
+
32
+ class Person
33
+ attr_accessor :name
34
+ end
35
+
36
+ describe UseCase::Validator do
37
+ it "passes valid object" do
38
+ person = Person.new
39
+ person.name = "Christian"
40
+ result = NewPersonValidator.call(person)
41
+
42
+ assert result.valid?
43
+ end
44
+
45
+ it "fails invalid object" do
46
+ result = NewPersonValidator.call(Person.new)
47
+
48
+ refute result.valid?
49
+ assert_equal 1, result.errors.count
50
+ end
51
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+ # --
3
+ # The MIT License (MIT)
4
+ #
5
+ # Copyright (C) 2013 Gitorious AS
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in all
15
+ # copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #++
25
+ require "test_helper"
26
+ require "use_case"
27
+ require "sample_use_case"
28
+
29
+ describe UseCase do
30
+ before do
31
+ @logged_in_user = User.new(42, "Christian")
32
+ @logged_in_user.can_admin = true
33
+ end
34
+
35
+ it "fails first pre-condition; no user logged in" do
36
+ outcome = CreateRepository.new(nil).execute({})
37
+
38
+ outcome.pre_condition_failed do |pc|
39
+ assert_equal UserLoggedInPrecondition, pc.class
40
+ end
41
+ end
42
+
43
+ it "fails second pre-condition; user cannot admin" do
44
+ @logged_in_user.can_admin = false
45
+ outcome = CreateRepository.new(@logged_in_user).execute({})
46
+
47
+ outcome.pre_condition_failed do |pc|
48
+ assert_equal ProjectAdminPrecondition, pc.class
49
+ end
50
+ end
51
+
52
+ it "fails with error if pre-condition raises" do
53
+ def @logged_in_user.id; raise "Oops!"; end
54
+ outcome = CreateRepository.new(@logged_in_user).execute({})
55
+
56
+ outcome.pre_condition_failed do |pc|
57
+ assert_equal RuntimeError, pc.class
58
+ end
59
+ end
60
+
61
+ it "fails on input validation" do
62
+ outcome = CreateRepository.new(@logged_in_user).execute({})
63
+
64
+ validation = outcome.failure do |v|
65
+ refute v.valid?
66
+ assert_equal 1, v.errors.count
67
+ assert v.errors[:name]
68
+ end
69
+
70
+ refute_nil validation
71
+ end
72
+
73
+ it "executes command" do
74
+ outcome = CreateRepository.new(@logged_in_user).execute({ :name => "My repository" })
75
+
76
+ result = outcome.success do |res|
77
+ assert_equal "My repository", res.name
78
+ assert res.is_a?(Repository)
79
+ end
80
+
81
+ refute_nil result
82
+ end
83
+
84
+ it "raises if command raises" do
85
+ use_case = ExplodingRepository.new(@logged_in_user)
86
+
87
+ assert_raises RuntimeError do
88
+ use_case.execute(nil)
89
+ end
90
+ end
91
+ end
data/use_case.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ require "./lib/use_case/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "use_case"
5
+ s.version = UseCase::VERSION
6
+ s.author = "Christian Johansen"
7
+ s.email = "christian@gitorious.com"
8
+ s.homepage = "http://gitorious.org/gitorious/use_case"
9
+ s.summary = s.description = ""
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files test`.split("\n")
13
+ s.require_path = "lib"
14
+
15
+ s.add_development_dependency "minitest", "~> 4"
16
+ s.add_development_dependency "rake"
17
+
18
+ s.required_ruby_version = ">= 1.9.2"
19
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: use_case
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christian Johansen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '4'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '4'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: ''
47
+ email: christian@gitorious.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - Gemfile
53
+ - Gemfile.lock
54
+ - LICENSE
55
+ - Rakefile
56
+ - Readme.md
57
+ - lib/use_case.rb
58
+ - lib/use_case/outcome.rb
59
+ - lib/use_case/validator.rb
60
+ - lib/use_case/version.rb
61
+ - test.rb
62
+ - test/sample_use_case.rb
63
+ - test/test_helper.rb
64
+ - test/use_case/outcome_test.rb
65
+ - test/use_case/validator_test.rb
66
+ - test/use_case_test.rb
67
+ - use_case.gemspec
68
+ homepage: http://gitorious.org/gitorious/use_case
69
+ licenses: []
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: 1.9.2
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 1.8.24
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: ''
92
+ test_files:
93
+ - test/sample_use_case.rb
94
+ - test/test_helper.rb
95
+ - test/use_case/outcome_test.rb
96
+ - test/use_case/validator_test.rb
97
+ - test/use_case_test.rb