imperator-ext 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
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
+ ```