use_case 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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