imperator-ext 0.2.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.
@@ -0,0 +1,90 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "imperator-ext"
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kristian Mandrup"]
12
+ s.date = "2012-08-14"
13
+ s.description = "Factories, Macros, REST helpers and Mongoid integration"
14
+ s.email = "kmandrup@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "docs/Design.md",
29
+ "imperator-ext.gemspec",
30
+ "lib/imperator-ext.rb",
31
+ "lib/imperator/command-ext.rb",
32
+ "lib/imperator/command/class_factory.rb",
33
+ "lib/imperator/command/macros.rb",
34
+ "lib/imperator/command/method_factory.rb",
35
+ "lib/imperator/command/rest.rb",
36
+ "lib/imperator/command/rest_helper.rb",
37
+ "lib/imperator/factory.rb",
38
+ "lib/imperator/mongoid.rb",
39
+ "lib/imperator/mongoid/attribute_helper.rb",
40
+ "lib/imperator/mongoid/command.rb",
41
+ "lib/imperator/mongoid/command/rest.rb",
42
+ "spec/imperator-ext/command/class_factory_spec.rb",
43
+ "spec/imperator-ext/command/macros_spec.rb",
44
+ "spec/imperator-ext/command/method_factory_spec.rb",
45
+ "spec/imperator-ext/command/rest_helper_spec.rb",
46
+ "spec/imperator-ext/command/rest_spec.rb",
47
+ "spec/imperator-ext/mongoid/command_spec.rb",
48
+ "spec/imperator-ext/mongoid/rest_command_spec.rb",
49
+ "spec/imperator-ext/shared_ex/attribute_helper_ex.rb",
50
+ "spec/imperator-ext/shared_ex/command_ex.rb",
51
+ "spec/imperator-ext/shared_ex/rest_helper_ex.rb",
52
+ "spec/spec_helper.rb"
53
+ ]
54
+ s.homepage = "http://github.com/kristianmandrup/imperator-ext"
55
+ s.licenses = ["MIT"]
56
+ s.require_paths = ["lib"]
57
+ s.rubygems_version = "1.8.24"
58
+ s.summary = "Various extensions for Imperator"
59
+
60
+ if s.respond_to? :specification_version then
61
+ s.specification_version = 3
62
+
63
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
+ s.add_runtime_dependency(%q<virtus>, [">= 0"])
65
+ s.add_runtime_dependency(%q<imperator>, [">= 0"])
66
+ s.add_development_dependency(%q<rspec>, [">= 2.8.0"])
67
+ s.add_development_dependency(%q<rdoc>, [">= 3.12"])
68
+ s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
69
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
70
+ s.add_development_dependency(%q<simplecov>, [">= 0.5"])
71
+ else
72
+ s.add_dependency(%q<virtus>, [">= 0"])
73
+ s.add_dependency(%q<imperator>, [">= 0"])
74
+ s.add_dependency(%q<rspec>, [">= 2.8.0"])
75
+ s.add_dependency(%q<rdoc>, [">= 3.12"])
76
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
77
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
78
+ s.add_dependency(%q<simplecov>, [">= 0.5"])
79
+ end
80
+ else
81
+ s.add_dependency(%q<virtus>, [">= 0"])
82
+ s.add_dependency(%q<imperator>, [">= 0"])
83
+ s.add_dependency(%q<rspec>, [">= 2.8.0"])
84
+ s.add_dependency(%q<rdoc>, [">= 3.12"])
85
+ s.add_dependency(%q<bundler>, [">= 1.0.0"])
86
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
87
+ s.add_dependency(%q<simplecov>, [">= 0.5"])
88
+ end
89
+ end
90
+
@@ -0,0 +1,121 @@
1
+ require 'imperator/command/rest_helper'
2
+ require 'imperator/command/rest'
3
+
4
+ class Imperator::Command
5
+ # http://johnragan.wordpress.com/2010/02/18/ruby-metaprogramming-dynamically-defining-classes-and-methods/
6
+ class ClassFactory
7
+ class << self
8
+ def use &block
9
+ yield self
10
+ end
11
+
12
+ def default_parent clazz
13
+ @default_parent ||= clazz
14
+ end
15
+
16
+ def get_default_parent
17
+ @default_parent ||= ::Imperator::Command
18
+ end
19
+
20
+ # Usage:
21
+ # Imperator::Command::ClassFactory.create :update, Post, parent: Imperator::Mongoid::Command do
22
+ # ..
23
+ # end
24
+ def build_command action, model, options = {}, &block
25
+ clazz_name = "#{action.to_s.camelize}#{model.to_s.camelize}Command"
26
+ parent = options[:parent] || get_default_parent
27
+ clazz = parent ? Class.new(parent) : Class.new
28
+ Object.const_set clazz_name, clazz
29
+ clazz = self.const_get(clazz_name)
30
+ if options[:auto_attributes]
31
+ clazz.instance_eval do
32
+ if respond_to? :attributes_for
33
+ attributes_for(model, :except => options[:except], :only => options[:only])
34
+ end
35
+ end
36
+ end
37
+ if block_given?
38
+ clazz.instance_eval &block
39
+ end
40
+ clazz
41
+ end
42
+
43
+ # Usage:
44
+ # Imperator::Command::ClassFactory.create_rest :all, Post, parent: Imperator::Mongoid::Command do
45
+ # ..
46
+ # end
47
+ def rest_command action, model, options = {}, &block
48
+ options.reverse_merge! default_options
49
+ options[:parent] ||= get_default_rest_class(model)
50
+ rest_commands_for(model, options, &block) and return if action.to_sym == :all
51
+ if rest_actions.include? action.to_sym
52
+ action_name = "#{action}_command_for"
53
+ send action_name, model, options, &block
54
+ else
55
+ raise ArgumentError, "Not a supported REST action. Must be one of #{rest_actions}, was #{action}"
56
+ end
57
+ end
58
+
59
+ attr_writer :default_rest_class
60
+
61
+ def get_default_rest_class model
62
+ model.ancestors.include?(Mongoid::Document) ? default_mongoid_rest_class : default_rest_class
63
+ end
64
+
65
+ def default_rest_class
66
+ @default_rest_class ||= Imperator::Command::Rest
67
+ end
68
+
69
+ def default_mongoid_rest_class
70
+ @default_mongoid_rest_class ||= Imperator::Mongoid::Command::Rest
71
+ end
72
+
73
+ def reset_rest_class
74
+ @default_rest_class = Imperator::Command::Rest
75
+ end
76
+
77
+ attr_writer :default_options
78
+
79
+ def default_options
80
+ @default_options ||= {}
81
+ end
82
+
83
+
84
+ protected
85
+
86
+ def rest_actions
87
+ [:create, :update, :delete]
88
+ end
89
+
90
+ def create_command_for model, options = {}, &block
91
+ options[:parent] ||= default_rest_class
92
+ c = build_command :create, model, options do
93
+ create_action
94
+ end
95
+ c.class_eval &block if block_given?
96
+ end
97
+
98
+ def update_command_for model, options = {}, &block
99
+ options[:parent] ||= default_rest_class
100
+ c = build_command :update, model, options do
101
+ update_action
102
+ end
103
+ c.class_eval &block if block_given?
104
+ end
105
+
106
+ def delete_command_for model, options = {}, &block
107
+ options[:parent] ||= default_rest_class
108
+ c = build_command :delete, model, options do
109
+ delete_action
110
+ end
111
+ c.class_eval &block if block_given?
112
+ end
113
+
114
+ def rest_commands_for model, options = {}, &block
115
+ rest_actions.each do |action|
116
+ send "#{action}_command_for", model, options, &block
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,13 @@
1
+ # Global macro methods
2
+
3
+ def imperator_class_factory
4
+ Imperator::Command::ClassFactory
5
+ end
6
+
7
+ def build_command action, model, options = {}, &block
8
+ imperator_class_factory.build_command action, model, options = {}, &block
9
+ end
10
+
11
+ def build_rest_command action, model, options = {}, &block
12
+ imperator_class_factory.rest_command action, model, options = {}, &block
13
+ end
@@ -0,0 +1,17 @@
1
+ class Imperator::Command
2
+ module MethodFactory
3
+ def command_method command, options = {}
4
+ namespace = (options[:ns] || '').to_s
5
+ namespace.sub! /Controller$/, ''
6
+ define_method "#{command}_command" do
7
+ instance_var = "@#{command}_command"
8
+ unless instance_variable_get(instance_var)
9
+ clazz = [namespace, "#{command.to_s.camelize}Command"].join('::').constantize
10
+ instance_variable_set instance_var, clazz.new(options.merge initiator: self)
11
+ end
12
+ end
13
+ end
14
+
15
+ extend self
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ require 'imperator/command/rest_helper'
2
+
3
+ class Imperator::Command
4
+ class Rest < Imperator::Command
5
+ include Imperator::Command::RestHelper
6
+ end
7
+ end
@@ -0,0 +1,78 @@
1
+ class Imperator::ResourceNotFoundError < StandardError; end
2
+
3
+ class Imperator::Command
4
+ module RestHelper
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ attr_writer :object_class
9
+ end
10
+
11
+ module ClassMethods
12
+ def create_action &block
13
+ action do
14
+ object_class.create attribute_set if object_class
15
+ instance_eval &block if block_given?
16
+ end
17
+ end
18
+
19
+ def update_action &block
20
+ action do
21
+ begin
22
+ find_object.update_attributes attribute_set if find_object
23
+ instance_eval &block if block_given?
24
+ rescue Imperator::ResourceNotFoundError => e
25
+ on_error e
26
+ end
27
+ end
28
+ end
29
+
30
+ def delete_action &block
31
+ action do
32
+ find_object.delete
33
+ instance_eval &block if block_given?
34
+ end
35
+ end
36
+
37
+ def on_error &block
38
+ define_method(:on_error, &block)
39
+ end
40
+
41
+ def for_class clazz
42
+ @object_class = clazz
43
+ end
44
+
45
+ def object_class
46
+ @object_class ||= filtered_class_name
47
+ end
48
+
49
+ protected
50
+
51
+ # convert to underscore format, fx UpdatePostCommand becomes update_post_command
52
+ # remove 'create', 'update' or 'delete' in the front of name: _post_command
53
+ # then remove command at the back: _post_
54
+ # then remove any '_': post
55
+ def filtered_class_name
56
+ self.class.name.underscore.sub(/^(create|update|delete)/, '').sub(/command$/, '').sub(/_/, '')
57
+ end
58
+ end
59
+
60
+ def object_class
61
+ self.class.object_class
62
+ end
63
+
64
+ def on_error exception
65
+ raise Imperator::InvalidCommandError, "The Command #{self} caused an error: #{exception}"
66
+ end
67
+
68
+ def find_object
69
+ object ||= object_class.find(self.id)
70
+ rescue
71
+ find_object_error
72
+ end
73
+
74
+ def find_object_error
75
+ raise Imperator::ResourceNotFoundError, "The resource #{self.id} could not be found"
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,14 @@
1
+ class Imperator::Command
2
+ # the main object this command works on
3
+ attribute :object, Object
4
+
5
+ # object that initiated the command.
6
+ # Can be used for method delegation etc
7
+ attribute :initiator, Object
8
+
9
+ def to_s
10
+ str = "Command: #{id}"
11
+ str << " - #{object}" if object
12
+ str
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ require 'imperator/command/class_factory'
2
+ require 'imperator/command/method_factory'
3
+
@@ -0,0 +1,29 @@
1
+ module Imperator
2
+ module Mongoid
3
+ module AttributeHelper
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def attributes_for clazz, options = {}
8
+ use_attributes = clazz.attribute_names
9
+
10
+ unless options[:except].blank?
11
+ use_attributes = use_attributes - [options[:except]].flatten.map(&:to_s)
12
+ end
13
+ unless options[:only].blank?
14
+ use_attributes = use_attributes & [options[:only]].flatten.map(&:to_s) # intersection
15
+ end
16
+
17
+ clazz.fields.each do |key, field|
18
+ name = key
19
+ type = field.type
20
+ # skip if this field is excluded for use in command
21
+ next unless use_attributes.include? name.to_s
22
+
23
+ attribute field.name, field.type, :default => field.default_val
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ require 'imperator/mongoid/command'
2
+
3
+ module Imperator::Mongoid
4
+ class Command < Imperator::Command
5
+ class Rest < Command
6
+ include Imperator::Command::RestHelper
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ require 'imperator/mongoid/attribute_helper'
2
+
3
+ # Usage
4
+
5
+ # class UpdatePostCommand < Imperator::Mongoid::Command
6
+ # attributes_for Post, except: [:state]
7
+ # attributes :title, :author
8
+
9
+ # validates_presence_of :object
10
+
11
+ # action do
12
+ # object = SomeObject.find(id)
13
+ # object.authored_by(author)
14
+ # end
15
+ # end
16
+
17
+ module Imperator::Mongoid
18
+ class Command < Imperator::Command
19
+ include Imperator::Mongoid::AttributeHelper
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ module Imperator
2
+ module Mongoid
3
+ end
4
+ end
5
+
6
+ require 'imperator/mongoid/command/rest'
@@ -0,0 +1,5 @@
1
+ require 'imperator'
2
+ require 'imperator/factory'
3
+ require 'imperator/command-ext'
4
+
5
+ require 'imperator/mongoid' if defined?(Mongoid)
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+
3
+ class UpdateRestCommand < Imperator::Command::Rest
4
+ end
5
+ class CreateRestCommand < Imperator::Command::Rest
6
+ end
7
+ class DeleteRestCommand < Imperator::Command::Rest
8
+ end
9
+
10
+ class Post
11
+ end
12
+
13
+ class Article
14
+ end
15
+
16
+ class Account
17
+ end
18
+
19
+ class User
20
+ end
21
+
22
+ class Foo
23
+ end
24
+
25
+ class Payment
26
+ end
27
+
28
+ describe Imperator::Command::ClassFactory do
29
+ subject { Imperator::Command::ClassFactory }
30
+
31
+ describe '.rest_command' do
32
+ context 'update' do
33
+ before :all do
34
+ subject.default_rest_class = Imperator::Command::Rest
35
+ subject.rest_command :update, Article, :parent => UpdateRestCommand, :auto_attributes => true do
36
+ def hello
37
+ "hello"
38
+ end
39
+ end
40
+ end
41
+
42
+ context 'UpdateArticleCommand created' do
43
+ let(:command) { UpdateArticleCommand.new }
44
+
45
+ specify { UpdateArticleCommand.superclass.should == UpdateRestCommand }
46
+ specify { command.hello.should == "hello" }
47
+ end
48
+ end
49
+
50
+ context 'create' do
51
+ before :all do
52
+ subject.default_rest_class = Imperator::Command::Rest
53
+ subject.rest_command :create, Article, :parent => CreateRestCommand, :auto_attributes => true do
54
+ def hello
55
+ "hello"
56
+ end
57
+ end
58
+ end
59
+
60
+ context 'CreateArticleCommand created' do
61
+ let(:command) { CreateArticleCommand.new }
62
+
63
+ specify { CreateArticleCommand.superclass.should == CreateRestCommand }
64
+ specify { command.hello.should == "hello" }
65
+ end
66
+ end
67
+
68
+ context 'delete' do
69
+ before :all do
70
+ subject.default_rest_class = Imperator::Command::Rest
71
+ subject.rest_command :delete, Article, :parent => DeleteRestCommand, :auto_attributes => true do
72
+ def hello
73
+ "hello"
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'DeleteArticleCommand created' do
79
+ let(:command) { DeleteArticleCommand.new }
80
+
81
+ specify { DeleteArticleCommand.superclass.should == DeleteRestCommand }
82
+ specify { command.hello.should == "hello" }
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ describe Imperator::Command::ClassFactory do
89
+
90
+ subject { Imperator::Command::ClassFactory }
91
+
92
+ describe '.build_command' do
93
+ before :all do
94
+ subject.build_command :show, Post
95
+ end
96
+
97
+ its(:get_default_parent) { should == Imperator::Command }
98
+ specify { ShowPostCommand.superclass.should == Imperator::Command }
99
+ end
100
+
101
+ describe '.rest_command' do
102
+ before :all do
103
+ subject.rest_command :update, Payment
104
+ end
105
+
106
+ specify { UpdatePaymentCommand.superclass.should == Imperator::Command::Rest }
107
+
108
+ describe ':all' do
109
+ before :all do
110
+ subject.rest_command :all, Foo
111
+ end
112
+
113
+ specify { CreateFooCommand.superclass.should == Imperator::Command::Rest }
114
+ specify { UpdateFooCommand.superclass.should == Imperator::Command::Rest }
115
+ specify { DeleteFooCommand.superclass.should == Imperator::Command::Rest }
116
+ end
117
+ end
118
+
119
+ describe '.default_rest_class' do
120
+ describe 'default class' do
121
+ its(:default_rest_class) { should == Imperator::Command::Rest }
122
+ end
123
+
124
+ describe 'set default_rest_class to UpdateRestCommand' do
125
+ before :all do
126
+ subject.default_rest_class = UpdateRestCommand
127
+ end
128
+
129
+ its(:default_rest_class) { should == UpdateRestCommand }
130
+ end
131
+ end
132
+
133
+ describe '.default_options' do
134
+ before do
135
+ subject.default_options = {hi: 'hello' }
136
+ end
137
+
138
+ its(:default_options) { should == {hi: 'hello' } }
139
+ end
140
+ end
141
+
142
+ describe 'private methods' do
143
+ describe Imperator::Command::ClassFactory do
144
+ subject { Imperator::Command::ClassFactory }
145
+
146
+ describe 'rest command builders' do
147
+ describe '.create_command_for' do
148
+ before do
149
+ subject.reset_rest_class
150
+ subject.send :create_command_for, Post
151
+ end
152
+
153
+ specify { CreatePostCommand.superclass.should == Imperator::Command::Rest }
154
+ end
155
+
156
+ describe '.update_command_for' do
157
+ before do
158
+ subject.default_rest_class = UpdateRestCommand
159
+ subject.send :update_command_for, Account
160
+ end
161
+
162
+ specify { UpdateAccountCommand.superclass.should == UpdateRestCommand }
163
+ end
164
+
165
+ describe '.delete_command_for' do
166
+ before do
167
+ subject.reset_rest_class
168
+ subject.send :delete_command_for, Account
169
+ end
170
+
171
+ specify { DeleteAccountCommand.superclass.should == Imperator::Command::Rest }
172
+ end
173
+
174
+ describe '.rest_commands_for' do
175
+ before :all do
176
+ subject.send :rest_commands_for, User
177
+ end
178
+
179
+ specify { CreateUserCommand.superclass.should == Imperator::Command::Rest }
180
+ specify { UpdateUserCommand.superclass.should == Imperator::Command::Rest }
181
+ specify { DeleteUserCommand.superclass.should == Imperator::Command::Rest }
182
+ end
183
+ end
184
+ end
185
+ end
186
+
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'imperator/command/macros'
3
+
4
+ class Message
5
+ end
6
+
7
+
8
+ describe 'Imperator command macros' do
9
+ describe '.imperator_class_factory' do
10
+ specify { imperator_class_factory.should == Imperator::Command::ClassFactory }
11
+ end
12
+
13
+ describe '.build_command' do
14
+ before do
15
+ build_command :update, Message
16
+ end
17
+
18
+ specify do
19
+ UpdateMessageCommand.superclass.should == Imperator::Command
20
+ end
21
+ end
22
+
23
+ describe '.build_rest_command' do
24
+ before do
25
+ build_rest_command :delete, Message
26
+ end
27
+
28
+ specify do
29
+ DeleteMessageCommand.superclass.should == Imperator::Command::Rest
30
+ end
31
+ end
32
+ end