imperator-ext 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +69 -0
- data/LICENSE.txt +20 -0
- data/README.md +339 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/docs/Design.md +197 -0
- data/imperator-ext.gemspec +90 -0
- data/lib/imperator/command/class_factory.rb +121 -0
- data/lib/imperator/command/macros.rb +13 -0
- data/lib/imperator/command/method_factory.rb +17 -0
- data/lib/imperator/command/rest.rb +7 -0
- data/lib/imperator/command/rest_helper.rb +78 -0
- data/lib/imperator/command-ext.rb +14 -0
- data/lib/imperator/factory.rb +3 -0
- data/lib/imperator/mongoid/attribute_helper.rb +29 -0
- data/lib/imperator/mongoid/command/rest.rb +9 -0
- data/lib/imperator/mongoid/command.rb +21 -0
- data/lib/imperator/mongoid.rb +6 -0
- data/lib/imperator-ext.rb +5 -0
- data/spec/imperator-ext/command/class_factory_spec.rb +186 -0
- data/spec/imperator-ext/command/macros_spec.rb +32 -0
- data/spec/imperator-ext/command/method_factory_spec.rb +64 -0
- data/spec/imperator-ext/command/rest_helper_spec.rb +7 -0
- data/spec/imperator-ext/command/rest_spec.rb +8 -0
- data/spec/imperator-ext/mongoid/command_spec.rb +10 -0
- data/spec/imperator-ext/mongoid/rest_command_spec.rb +8 -0
- data/spec/imperator-ext/shared_ex/attribute_helper_ex.rb +54 -0
- data/spec/imperator-ext/shared_ex/command_ex.rb +0 -0
- data/spec/imperator-ext/shared_ex/rest_helper_ex.rb +15 -0
- data/spec/spec_helper.rb +11 -0
- metadata +195 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
gem 'virtus', :git => 'git://github.com/kristianmandrup/virtus.git'
|
4
|
+
gem 'imperator' #, :git => 'git://github.com/kristianmandrup/imperator.git'
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem 'mongoid', '~> 3.0'
|
8
|
+
end
|
9
|
+
|
10
|
+
group :development do
|
11
|
+
gem "rspec", ">= 2.8.0"
|
12
|
+
gem "rdoc", ">= 3.12"
|
13
|
+
gem "bundler", ">= 1.0.0"
|
14
|
+
gem "jeweler", "~> 1.8.4"
|
15
|
+
gem "simplecov",">= 0.5"
|
16
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/kristianmandrup/virtus.git
|
3
|
+
revision: 4b0d2cb9c5ec937b940dad0048fb7396f0e05466
|
4
|
+
specs:
|
5
|
+
virtus (0.5.1)
|
6
|
+
backports (~> 2.6.1)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activemodel (3.2.7)
|
12
|
+
activesupport (= 3.2.7)
|
13
|
+
builder (~> 3.0.0)
|
14
|
+
activesupport (3.2.7)
|
15
|
+
i18n (~> 0.6)
|
16
|
+
multi_json (~> 1.0)
|
17
|
+
backports (2.6.2)
|
18
|
+
builder (3.0.0)
|
19
|
+
diff-lcs (1.1.3)
|
20
|
+
git (1.2.5)
|
21
|
+
i18n (0.6.0)
|
22
|
+
imperator (0.2.0)
|
23
|
+
activemodel
|
24
|
+
uuidtools
|
25
|
+
virtus
|
26
|
+
jeweler (1.8.4)
|
27
|
+
bundler (~> 1.0)
|
28
|
+
git (>= 1.2.5)
|
29
|
+
rake
|
30
|
+
rdoc
|
31
|
+
json (1.7.4)
|
32
|
+
mongoid (3.0.3)
|
33
|
+
activemodel (~> 3.1)
|
34
|
+
moped (~> 1.1)
|
35
|
+
origin (~> 1.0)
|
36
|
+
tzinfo (~> 0.3.22)
|
37
|
+
moped (1.2.0)
|
38
|
+
multi_json (1.3.6)
|
39
|
+
origin (1.0.4)
|
40
|
+
rake (0.9.2.2)
|
41
|
+
rdoc (3.12)
|
42
|
+
json (~> 1.4)
|
43
|
+
rspec (2.11.0)
|
44
|
+
rspec-core (~> 2.11.0)
|
45
|
+
rspec-expectations (~> 2.11.0)
|
46
|
+
rspec-mocks (~> 2.11.0)
|
47
|
+
rspec-core (2.11.1)
|
48
|
+
rspec-expectations (2.11.2)
|
49
|
+
diff-lcs (~> 1.1.3)
|
50
|
+
rspec-mocks (2.11.1)
|
51
|
+
simplecov (0.6.4)
|
52
|
+
multi_json (~> 1.0)
|
53
|
+
simplecov-html (~> 0.5.3)
|
54
|
+
simplecov-html (0.5.3)
|
55
|
+
tzinfo (0.3.33)
|
56
|
+
uuidtools (2.1.3)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
bundler (>= 1.0.0)
|
63
|
+
imperator
|
64
|
+
jeweler (~> 1.8.4)
|
65
|
+
mongoid (~> 3.0)
|
66
|
+
rdoc (>= 3.12)
|
67
|
+
rspec (>= 2.8.0)
|
68
|
+
simplecov (>= 0.5)
|
69
|
+
virtus!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Kristian Mandrup
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
## Imperator extensions
|
2
|
+
|
3
|
+
This gem includes some extensions and integrations for the [imperator](https://github.com/Agowan/imperator) gem.
|
4
|
+
|
5
|
+
The current integrations are for:
|
6
|
+
|
7
|
+
* [mongoid](https://github.com/mongoid/mongoid)
|
8
|
+
|
9
|
+
The gem is designed to simplify designing commands for REST actions and includes some useful macros to facilitate common patterns.
|
10
|
+
|
11
|
+
## Mongoid imperator
|
12
|
+
|
13
|
+
The Mongoid integration provides the class method `#attributes_for`,
|
14
|
+
which can be used to define Command attributes that match the fields of the Mongoid object that the command is designed to operate on.
|
15
|
+
|
16
|
+
`attributes_for Post`
|
17
|
+
|
18
|
+
Will create Command attributes for all fields defined by the Post class (model).
|
19
|
+
You can finetune the Command attributes to be created by using the `:except` and `:only options`.
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class UpdatePostCommand < Imperator::Mongoid::Command
|
23
|
+
attributes_for Post, only: [:subject, :body]
|
24
|
+
|
25
|
+
# OR
|
26
|
+
attributes_for Post, except: [:state]
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
In addition this gem extends the `Imperator::Command` class with the attributes
|
31
|
+
`object` and `initiator`. The initiator is meant to be set to the object that initiates the command, typically a Controller instance. The `initiator` can then be used to define delegation methods to the controller etc.
|
32
|
+
|
33
|
+
## Rest Commands
|
34
|
+
|
35
|
+
If you inherit from the `Imperator::Command::Rest` class, you gain access to the
|
36
|
+
REST convenience methods: `update`, `delete` and `create_new` which creates action methods with some default appropriate REST logic for the particular action.
|
37
|
+
See the code for more info on how to use this for your own needs.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
class UpdatePostCommand < Imperator::Command::Rest
|
41
|
+
attribute :some_object_id
|
42
|
+
attribute :some_value
|
43
|
+
|
44
|
+
validates_presence_of :some_object_id
|
45
|
+
|
46
|
+
update do
|
47
|
+
puts "updated OK"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
## Mongoid integration
|
53
|
+
|
54
|
+
Imperator also comes with a little Mongodi adaptor class, which provides the `attributes_for` method in order to easily redefine Mongoid model fields as Command attributes. Similar adaptors could be created for other ORMs such as Active Record etc.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class UpdatePostCommand < Imperator::Mongoid::Command::Rest
|
58
|
+
|
59
|
+
attributes_for Post, except: [:status, :rating]
|
60
|
+
|
61
|
+
validates :name, presence: true
|
62
|
+
|
63
|
+
update do
|
64
|
+
puts "#{object} was updated"
|
65
|
+
end
|
66
|
+
|
67
|
+
on_error do
|
68
|
+
puts "The Post could not be updated: #{object}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
## Class Factory
|
74
|
+
|
75
|
+
The `Imperator::Command::ClassFactory` can be used to easily create Command wrappers for your model classes.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Imperator::Command::ClassFactory.create :publish, Post do
|
79
|
+
action do
|
80
|
+
find_object.publish!
|
81
|
+
end
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
It is especially handy for creating Rest Command wrappers. Here we include the `macros` to simplify the code.
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require 'imperator/command/macros'
|
89
|
+
|
90
|
+
imperator_command_factory.use do |factory|
|
91
|
+
# put common/shared logic in this REST base class
|
92
|
+
factory.default_rest_class = Imperator::Mongoid::Command::Rest
|
93
|
+
|
94
|
+
factory.create_rest :all, Post do
|
95
|
+
on_error do
|
96
|
+
puts "Oops! There was an error!"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
factory.create_rest :update, Article do
|
101
|
+
attributes_for Article, only: [:title, :body]
|
102
|
+
|
103
|
+
on_error do
|
104
|
+
puts "Oops! There was an error!"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Same using :auto_attributes option
|
109
|
+
factory.create_rest :update, Article, auto_attributes: true, except: [:status] do
|
110
|
+
on_error do
|
111
|
+
puts "Oops! There was an error!"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
## ClassFactory macros
|
118
|
+
|
119
|
+
If you include the macros, the following macros are exposed:
|
120
|
+
|
121
|
+
* `#create_command`
|
122
|
+
* `#create_rest_command`
|
123
|
+
* `#imperator_class_factory
|
124
|
+
|
125
|
+
Usage example:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
# ensure all attributes of model are reflected in Command (if adaptor makes it possible)
|
129
|
+
Imperator::Command::ClassFactory.default_options = {auto_attributes: true}
|
130
|
+
|
131
|
+
module PostController
|
132
|
+
class Update < Action
|
133
|
+
|
134
|
+
command do
|
135
|
+
@command ||= create_rest_command(:update, Post).new object_hash
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
## Command Method Factory
|
141
|
+
|
142
|
+
By including the `Imperator::Command::MethodFactory` you get access to the `#command_method` macro, which lets you easily define Imperator Command methods.
|
143
|
+
This is typically used in a Controller.
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
class ServicesController
|
147
|
+
include Imperator::Command::MethodFactory
|
148
|
+
|
149
|
+
command_method :sign_in
|
150
|
+
end
|
151
|
+
```
|
152
|
+
|
153
|
+
Focused Controller usage example:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
module ServicesController
|
157
|
+
|
158
|
+
class SignIn < Action
|
159
|
+
include Imperator::Command::MethodFactory
|
160
|
+
|
161
|
+
def run
|
162
|
+
sign_in.perform
|
163
|
+
end
|
164
|
+
|
165
|
+
command_method :sign_in
|
166
|
+
end
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
170
|
+
Often you will want commands namespace scoped however. This is supported via the `:ns` option.
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
module ServicesController
|
174
|
+
|
175
|
+
class SignIn < Action
|
176
|
+
include Imperator::Command::MethodFactory
|
177
|
+
|
178
|
+
def run
|
179
|
+
sign_in.perform
|
180
|
+
end
|
181
|
+
|
182
|
+
command_method :sign_in, ns: self.parent
|
183
|
+
end
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
Creates a `#sign_in_command` with namespaced scoping:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
module ServicesController
|
191
|
+
class SignIn < Action
|
192
|
+
def sign_in_command
|
193
|
+
@sign_in_command ||= Services::SignInCommand.new initiator: self
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
```
|
198
|
+
|
199
|
+
This is the recommended pattern for linking Focused Controller actions to Imperator commands.
|
200
|
+
|
201
|
+
## Rest Commands
|
202
|
+
|
203
|
+
The class `Imperator::Command::Rest` can be used as a base class for REST CUD commands (Create, Update, Delete). This class includes the module `Imperator::Command::RestHelper` which includes a number of useful methods for defining typical REST action behavior for an Imperator Command.
|
204
|
+
|
205
|
+
Usage example:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
class UpdatePostCommand < Imperator::Command::Rest
|
209
|
+
update_action do
|
210
|
+
notify :updated
|
211
|
+
end
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
Mongoid example:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
class UpdatePostCommand < Imperator::Mongoid::Command::Rest
|
219
|
+
update_action do
|
220
|
+
notify :updated
|
221
|
+
end
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
## Controllers, Actions and Commands
|
226
|
+
|
227
|
+
An analysis of the current MVC design pattern in Rails, in particular how the Controllers
|
228
|
+
are designed and can be improved by employing different Design Patterns to ensure better
|
229
|
+
decoupling, single responsibility and how to aovid the typical Rails anti-pattern of flat classes, bloated with methods.
|
230
|
+
|
231
|
+
## Using Imperator Commands in the current Controller pattern
|
232
|
+
|
233
|
+
Demonstrates just how ugly the current Controller convention is!!!
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
class PostController < ApplicationController
|
237
|
+
def update
|
238
|
+
update_command.valid? ? update_valid : update_invalid
|
239
|
+
end
|
240
|
+
|
241
|
+
protected
|
242
|
+
|
243
|
+
def update_valid
|
244
|
+
update_command.perform and redirect_to(root_url)
|
245
|
+
end
|
246
|
+
|
247
|
+
def update_invalid
|
248
|
+
render edit_post_path(command.object)
|
249
|
+
end
|
250
|
+
|
251
|
+
def update_command
|
252
|
+
@update_command ||= UpdatePostCommand.new(params[:post])
|
253
|
+
end
|
254
|
+
end
|
255
|
+
```
|
256
|
+
|
257
|
+
## Using Imperator Commands with Focused Controllers
|
258
|
+
|
259
|
+
Much nicer encapsulated logic using FocusedController :)
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
module PostController
|
263
|
+
class Update < Action
|
264
|
+
invalid_path do
|
265
|
+
root_url
|
266
|
+
end
|
267
|
+
|
268
|
+
# generated by naming convention
|
269
|
+
# command { @command ||= UpdatePostCommand.new post_hash }
|
270
|
+
end
|
271
|
+
end
|
272
|
+
```
|
273
|
+
|
274
|
+
Demonstrating some customization of Controller logic
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
module PostController
|
278
|
+
class Update < Action
|
279
|
+
valid do
|
280
|
+
command.perform
|
281
|
+
redirect_to root_url
|
282
|
+
end
|
283
|
+
|
284
|
+
def invalid
|
285
|
+
flash_msg "#{command.object} was invalid!", :error
|
286
|
+
super
|
287
|
+
end
|
288
|
+
|
289
|
+
command { @command ||= command_class.new object_hash.merge(:status => :complete) }
|
290
|
+
end
|
291
|
+
end
|
292
|
+
```
|
293
|
+
|
294
|
+
And more...
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
module PostController
|
298
|
+
class Update < Action
|
299
|
+
valid do
|
300
|
+
flash_msg "#{command.object} was valid!"
|
301
|
+
command.perform
|
302
|
+
puts "#{command} was performed!"
|
303
|
+
valid_redirect
|
304
|
+
end
|
305
|
+
|
306
|
+
valid_redirect_path do
|
307
|
+
root_url
|
308
|
+
end
|
309
|
+
|
310
|
+
def invalid
|
311
|
+
flash_msg "#{command.object} was invalid!", :error
|
312
|
+
super
|
313
|
+
end
|
314
|
+
|
315
|
+
command do
|
316
|
+
@command ||= begin
|
317
|
+
c = UpdatePostCommand.new post_hash
|
318
|
+
c.complete_it!
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
```
|
324
|
+
|
325
|
+
## Contributing to imperator-ext
|
326
|
+
|
327
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
328
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
329
|
+
* Fork the project.
|
330
|
+
* Start a feature/bugfix branch.
|
331
|
+
* Commit and push until you are happy with your contribution.
|
332
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
333
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
334
|
+
|
335
|
+
## Copyright
|
336
|
+
|
337
|
+
Copyright (c) 2012 Kristian Mandrup. See LICENSE.txt for
|
338
|
+
further details.
|
339
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "imperator-ext"
|
18
|
+
gem.homepage = "http://github.com/kristianmandrup/imperator-ext"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Various extensions for Imperator}
|
21
|
+
gem.description = %Q{Factories, Macros, REST helpers and Mongoid integration}
|
22
|
+
gem.email = "kmandrup@gmail.com"
|
23
|
+
gem.authors = ["Kristian Mandrup"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rdoc/task'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "imperator-ext #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/docs/Design.md
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
## Controllers, Actions and Commands
|
2
|
+
|
3
|
+
An analysis of the current MVC design pattern in Rails, in particular how the Controllers
|
4
|
+
are designed and can be improved by employing different Design Patterns to ensure better
|
5
|
+
decoupling, single responsibility and how to aovid the typical Rails anti-pattern of flat classes, bloated with methods.
|
6
|
+
|
7
|
+
## Using Imperator Commands in the current Controller pattern
|
8
|
+
|
9
|
+
Demonstrates just how ugly the current Controller convention is!!!
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
class PostController < ApplicationController
|
13
|
+
def update
|
14
|
+
update_command.valid? ? update_valid : update_invalid
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def update_valid
|
20
|
+
update_command.perform and redirect_to(root_url)
|
21
|
+
end
|
22
|
+
|
23
|
+
def update_invalid
|
24
|
+
render edit_post_path(command.object)
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_command
|
28
|
+
@update_command ||= UpdatePostCommand.new(params[:post])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
## Using Imperator Commands with Focused Controllers
|
34
|
+
|
35
|
+
Much nicer encapsulated logic using FocusedController :)
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
module PostController
|
39
|
+
class Update < UpdateCommandAction
|
40
|
+
invalid_path do
|
41
|
+
root_url
|
42
|
+
end
|
43
|
+
|
44
|
+
# generated by naming convention
|
45
|
+
# command { @command ||= UpdatePostCommand.new post_hash }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
Demonstrating some customization of Controller logic
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
module PostController
|
54
|
+
class Update < UpdateCommandAction
|
55
|
+
valid do
|
56
|
+
command.perform
|
57
|
+
redirect_to root_url
|
58
|
+
end
|
59
|
+
|
60
|
+
def invalid
|
61
|
+
flash_msg "#{command.object} was invalid!", :error
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
65
|
+
command { @command ||= command_class.new object_hash.merge(:status => :complete) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
And more...
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
module PostController
|
74
|
+
class Update < UpdateCommandAction
|
75
|
+
valid do
|
76
|
+
flash_msg "#{command.object} was valid!"
|
77
|
+
command.perform
|
78
|
+
puts "#{command} was performed!"
|
79
|
+
valid_redirect
|
80
|
+
end
|
81
|
+
|
82
|
+
valid_redirect_path do
|
83
|
+
root_url
|
84
|
+
end
|
85
|
+
|
86
|
+
def invalid
|
87
|
+
flash_msg "#{command.object} was invalid!", :error
|
88
|
+
super
|
89
|
+
end
|
90
|
+
|
91
|
+
command do
|
92
|
+
@command ||= begin
|
93
|
+
c = UpdatePostCommand.new post_hash
|
94
|
+
c.complete_it!
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
## Restfull Commands
|
102
|
+
|
103
|
+
If you inherit from the `Imperator::Command::Restfull` class, you gain access to the
|
104
|
+
REST convenience methods: `update`, `delete` and `create_new` which creates action methods with some default appropriate REST logic for the particular action.
|
105
|
+
See the code for more info on how to use this for your own needs.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
class UpdatePostCommand < Imperator::Command::Restfull
|
109
|
+
attribute :some_object_id
|
110
|
+
attribute :some_value
|
111
|
+
|
112
|
+
validates_presence_of :some_object_id
|
113
|
+
|
114
|
+
update do
|
115
|
+
puts "updated OK"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
## Mongoid integration
|
121
|
+
|
122
|
+
Imperator also comes with a little Mongodi adaptor class, which provides the `attributes_for` method in order to easily redefine Mongoid model fields as Command attributes. Similar adaptors could be created for other ORMs such as Active Record etc.
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
class UpdatePostCommand < Imperator::Mongoid::RestCommand
|
126
|
+
|
127
|
+
attributes_for Post, except: [:status, :rating]
|
128
|
+
|
129
|
+
validates :name, presence: true
|
130
|
+
|
131
|
+
update do
|
132
|
+
puts "#{object} was updated"
|
133
|
+
end
|
134
|
+
|
135
|
+
on_error do
|
136
|
+
puts "The Post could not be updated: #{object}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
## Class Factory
|
142
|
+
|
143
|
+
The `Imperator::Command::ClassFactory` can be used to easily create Command wrappers for your model classes.
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
Imperator::Command::ClassFactory.create :publish, Post do
|
147
|
+
action do
|
148
|
+
find_object.publish!
|
149
|
+
end
|
150
|
+
end
|
151
|
+
```
|
152
|
+
|
153
|
+
It is especially handy for creating Rest Command wrappers.
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
Imperator::Command::ClassFactory.use do |factory|
|
157
|
+
factory.default_rest_class = Imperator::Mongoid::RestCommand
|
158
|
+
factory.create_rest :all, Post do
|
159
|
+
on_error do
|
160
|
+
puts "Oops! There was an error!"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
factory.create_rest :update, Article do
|
165
|
+
attributes_for Article, only: [:title, :body]
|
166
|
+
|
167
|
+
on_error do
|
168
|
+
puts "Oops! There was an error!"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Same using :auto_attributes option
|
173
|
+
factory.create_rest :update, Article, auto_attributes: true, except: [:status] do
|
174
|
+
on_error do
|
175
|
+
puts "Oops! There was an error!"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
## Using ClassFactory macros
|
182
|
+
|
183
|
+
There are also two global macros `create_command` and `create_rest_command` available.
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
# ensure all attributes of model are reflected in Command (if adaptor makes it possible)
|
187
|
+
Imperator::Command::ClassFactory.default_options = {auto_attributes: true}
|
188
|
+
|
189
|
+
module PostController
|
190
|
+
class Update < UpdateCommandAction
|
191
|
+
|
192
|
+
command do
|
193
|
+
@command ||= create_rest_command(:update, Post).new object_hash
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
```
|