super_resources 1.0.0.rc1 → 1.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +6 -17
- data/lib/generators/rails/super_controller_generator.rb +13 -0
- data/lib/generators/rails/templates/controller.rb +9 -0
- data/lib/super_resources/nesting.rb +8 -37
- data/lib/super_resources/railtie.rb +9 -0
- data/lib/super_resources/resources.rb +18 -31
- data/lib/super_resources/routing.rb +40 -0
- data/lib/super_resources/support/cancan.rb +46 -0
- data/lib/super_resources/{has_scope.rb → support/has_scope.rb} +0 -0
- data/lib/super_resources/version.rb +1 -1
- data/lib/super_resources.rb +15 -8
- data/spec/dummy/app/controllers/adapted_resources_controller.rb +2 -2
- data/spec/dummy/app/controllers/great_grandparent_resources_controller.rb +7 -7
- metadata +8 -7
- data/.DS_Store +0 -0
- data/lib/super_resources/cancan.rb +0 -15
- data/spec/dummy/.DS_Store +0 -0
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -126,13 +126,12 @@ SuperResources derives the class of the target resource from the controller name
|
|
126
126
|
|
127
127
|
Yes, you can adapt these to suit your needs.
|
128
128
|
|
129
|
-
|
130
|
-
The block for `resource` shall answer a single instance of the resource class and the block for `collection` shall answer an array of instances of the resource class.
|
129
|
+
The `resource` and `collection` methods are used internally by the default RESTful actions provided by SuperResources, and can have their implementations customised easily. The return values of these methods are memoized for various reasons, for this you should make use of the `memoize_resource` and `memoize_collection` methods respectivley. Both methods take a block that is only executed as necessary.
|
131
130
|
|
132
131
|
For example, if you wanted 'TeamsController#collection' to return only those teams that the current user has joined, you would write an implementation of `collection` that calls its super, passing a block the evalautes to the right collection:
|
133
132
|
|
134
133
|
def collection
|
135
|
-
|
134
|
+
memoize_collection { resource_class.joined_by(current_user) }
|
136
135
|
end
|
137
136
|
|
138
137
|
If that looks strange, bear in mind it's been designed so that you don't need to know how SuperResources internally uses your preferred implementation.
|
@@ -156,20 +155,10 @@ While SuperResource uses the `find` method as the default, you can choose anothe
|
|
156
155
|
|
157
156
|
### Builder Method
|
158
157
|
|
159
|
-
SuperResources extracts the construction of a new resource into the `build_resource` method. If you need to
|
160
|
-
|
161
|
-
If you need to do specialized work for the build, pass the a block to `build_resource` that evaluates to an object with the state you need. For example:
|
162
|
-
|
163
|
-
build_resource do
|
164
|
-
resource_class.new do |p|
|
165
|
-
# initialize the state here
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
More commonly, you would redefine the whole method so that it always behaves the same way for its enclosing controller:
|
158
|
+
SuperResources extracts the construction of a new resource into the `build_resource` method. If you need to do specialized work for the build, override the `build_resource` method and be sure to memoize your result:
|
170
159
|
|
171
160
|
def build_resource
|
172
|
-
|
161
|
+
memoize_resource do
|
173
162
|
resource_class.new do |p|
|
174
163
|
# initialize the state here
|
175
164
|
end
|
@@ -200,9 +189,9 @@ Just do it. Declare them in your controller and match them in routes. All the Su
|
|
200
189
|
|
201
190
|
## Acknowledgments
|
202
191
|
|
203
|
-
SuperResources would never have happened without InheritedResources
|
192
|
+
SuperResources would never have happened without [InheritedResources](https://github.com/josevalim/inherited_resources) existing first.
|
204
193
|
We preferred the idea of abstracting and extracting RESTful actions out of all our controllers and we're not so keen on scaffolds generating
|
205
|
-
un-DRY code. We used InheritedResources in a production deployed application [meetlinkshare.com
|
194
|
+
un-DRY code. We used InheritedResources in a production deployed application [MeetLinkShare](http://www.meetlinkshare.com), gained some experience and decided we wanted an even DRYer tool.
|
206
195
|
|
207
196
|
The basic mechanics of SuperResources was hacked out during Rails Camp 12 in Tasmania, Australia and was subsequently applied to MeetLinkShare.
|
208
197
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module Generators
|
5
|
+
class SuperControllerGenerator < Rails::Generators::ScaffoldControllerGenerator
|
6
|
+
def self.source_root
|
7
|
+
@source_root ||= File.expand_path('../templates', __FILE__)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<% if namespaced? -%>
|
2
|
+
require_dependency "<%= namespaced_file_path %>/application_controller"
|
3
|
+
|
4
|
+
<% end -%>
|
5
|
+
<% module_namespacing do -%>
|
6
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
7
|
+
include SuperResources::Controller
|
8
|
+
end
|
9
|
+
<% end -%>
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module SuperResources
|
2
2
|
module Nesting
|
3
3
|
extend ActiveSupport::Concern
|
4
|
+
|
4
5
|
include Resources
|
6
|
+
include Routing
|
5
7
|
|
6
8
|
included do
|
7
9
|
helper_method :association_chain, :with_chain, :method_missing, :respond_to?
|
@@ -24,28 +26,16 @@ module SuperResources
|
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
27
|
-
def collection
|
28
|
-
|
29
|
-
@collection = yield
|
30
|
-
else
|
31
|
-
@collection ||= end_of_association_chain
|
32
|
-
end
|
29
|
+
def collection
|
30
|
+
memoize_collection { end_of_association_chain }
|
33
31
|
end
|
34
32
|
|
35
|
-
def resource
|
36
|
-
|
37
|
-
@resource = yield
|
38
|
-
else
|
39
|
-
@resource ||= end_of_association_chain.send(finder_method, params[:id])
|
40
|
-
end
|
33
|
+
def resource
|
34
|
+
memoize_resource { end_of_association_chain.send(finder_method, params[:id]) }
|
41
35
|
end
|
42
36
|
|
43
|
-
def build_resource(
|
44
|
-
|
45
|
-
@resource = yield
|
46
|
-
else
|
47
|
-
@resource ||= end_of_association_chain.build(resource_params)
|
48
|
-
end
|
37
|
+
def build_resource(params={})
|
38
|
+
memoize_resource { end_of_association_chain.build(params) }
|
49
39
|
end
|
50
40
|
|
51
41
|
def nested?
|
@@ -82,29 +72,10 @@ module SuperResources
|
|
82
72
|
route.parts \
|
83
73
|
.select { |p| p.to_s =~ %r(_id$) } \
|
84
74
|
.map { |p| p.to_s.gsub(/_id$/, '').to_sym }
|
85
|
-
@symbols_for_association_chain
|
86
75
|
end
|
87
76
|
|
88
77
|
def with_chain(object)
|
89
78
|
association_chain + [ object ]
|
90
79
|
end
|
91
|
-
|
92
|
-
def route
|
93
|
-
@route ||= begin
|
94
|
-
routes.formatter.send(:match_route, nil, path_parameters) do |route|
|
95
|
-
# TODO: don't assume the first route is good, validate!
|
96
|
-
# TODO: don't use break
|
97
|
-
break route
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def routes
|
103
|
-
request.env['action_dispatch.routes']
|
104
|
-
end
|
105
|
-
|
106
|
-
def path_parameters
|
107
|
-
request.env['action_dispatch.request.path_parameters'].symbolize_keys
|
108
|
-
end
|
109
80
|
end
|
110
81
|
end
|
@@ -3,7 +3,7 @@ module SuperResources
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
helper_method :collection, :
|
6
|
+
helper_method :collection, :resource, :resource_class, :parent, :nested?
|
7
7
|
end
|
8
8
|
|
9
9
|
protected
|
@@ -24,28 +24,20 @@ module SuperResources
|
|
24
24
|
controller_name.to_sym
|
25
25
|
end
|
26
26
|
|
27
|
-
def collection
|
28
|
-
|
29
|
-
@collection = yield
|
30
|
-
else
|
31
|
-
@collection ||= resource_class.scoped
|
32
|
-
end
|
27
|
+
def collection
|
28
|
+
memoize_collection { resource_class.scoped }
|
33
29
|
end
|
34
30
|
|
35
|
-
def
|
36
|
-
@collection
|
31
|
+
def memoize_collection(&block)
|
32
|
+
@collection ||= block.call
|
37
33
|
end
|
38
34
|
|
39
|
-
def resource
|
40
|
-
|
41
|
-
@resource = yield
|
42
|
-
else
|
43
|
-
@resource ||= resource_class.send(finder_method, params[:id])
|
44
|
-
end
|
35
|
+
def resource
|
36
|
+
memoize_resource { resource_class.send(finder_method, params[:id]) }
|
45
37
|
end
|
46
38
|
|
47
|
-
def
|
48
|
-
@resource
|
39
|
+
def memoize_resource(&block)
|
40
|
+
@resource ||= block.call
|
49
41
|
end
|
50
42
|
|
51
43
|
def finder_method
|
@@ -60,25 +52,20 @@ module SuperResources
|
|
60
52
|
nil
|
61
53
|
end
|
62
54
|
|
63
|
-
def
|
64
|
-
|
65
|
-
@resource = yield
|
66
|
-
else
|
67
|
-
@resource ||= resource_class.new(resource_params)
|
68
|
-
end
|
55
|
+
def resource_params
|
56
|
+
params[resource_params_name] || {}
|
69
57
|
end
|
70
58
|
|
71
|
-
def
|
72
|
-
|
73
|
-
resource.save
|
59
|
+
def build_resource(params={})
|
60
|
+
memoize_resource { resource_class.new(params) }
|
74
61
|
end
|
75
62
|
|
76
|
-
def
|
77
|
-
|
63
|
+
def create_resource(params)
|
64
|
+
build_resource(params).save
|
78
65
|
end
|
79
|
-
|
80
|
-
def
|
81
|
-
params
|
66
|
+
|
67
|
+
def update_resource(params)
|
68
|
+
resource.update_attributes(params)
|
82
69
|
end
|
83
70
|
|
84
71
|
def destroy_resource
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SuperResources
|
2
|
+
module Routing
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def routes
|
8
|
+
Rails.application.routes || request.env['action_dispatch.routes']
|
9
|
+
end
|
10
|
+
|
11
|
+
def router
|
12
|
+
routes.router
|
13
|
+
end
|
14
|
+
|
15
|
+
def route
|
16
|
+
@route ||= recognize_route(request.path.present? ? request : mock_request)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def recognize_route(request)
|
22
|
+
router.recognize(request) do |route, matches, params|
|
23
|
+
return route
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def path_parameters
|
28
|
+
request.env['action_dispatch.request.path_parameters'].symbolize_keys
|
29
|
+
end
|
30
|
+
|
31
|
+
def mock_request
|
32
|
+
# used when there's no path in +request+. only seems to happen when
|
33
|
+
# testing controller actions
|
34
|
+
@mock_request ||= begin
|
35
|
+
env = Rack::MockRequest.env_for(url_for(path_parameters), :method => request.method)
|
36
|
+
request.class.new(env)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module SuperController
|
2
|
+
module Cancan
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def index(*args)
|
6
|
+
authorize! :index, resource_class
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def show(*args)
|
11
|
+
authorize! :show, resource
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def new(*args)
|
16
|
+
authorize! :new, resource_class
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def edit(*args)
|
21
|
+
authorize! :edit, resource
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def create(*args)
|
26
|
+
authorize! :create, resource_class
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def update(*args)
|
31
|
+
authorize! :update, resource
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def destroy(*args)
|
36
|
+
authorize! :destroy, resource
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def collection
|
43
|
+
super.accessible_by(current_ability)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
File without changes
|
data/lib/super_resources.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
module SuperResources
|
2
|
-
|
3
|
-
autoload :Controller, "super_resources/controller"
|
4
|
-
autoload :Nesting, "super_resources/nesting"
|
5
|
-
autoload :Resources, "super_resources/resources"
|
6
|
-
autoload :URLHelpers, "super_resources/url_helpers"
|
7
|
-
autoload :Version, "super_resources/version"
|
2
|
+
extend ActiveSupport::Autoload
|
8
3
|
|
9
|
-
autoload :
|
10
|
-
autoload :
|
4
|
+
autoload :Actions
|
5
|
+
autoload :Controller
|
6
|
+
autoload :Nesting
|
7
|
+
autoload :Resources
|
8
|
+
autoload :Routing
|
9
|
+
autoload :URLHelpers
|
10
|
+
autoload :Version
|
11
|
+
|
12
|
+
autoload_under 'support' do
|
13
|
+
autoload :HasScope
|
14
|
+
autoload :Cancan
|
15
|
+
end
|
11
16
|
end
|
17
|
+
|
18
|
+
require 'super_resources/railtie' if defined?(Rails)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class GreatGrandparentResourcesController < ApplicationController
|
2
2
|
def index
|
3
|
-
super do
|
3
|
+
super.tap do
|
4
4
|
collection.each do |r|
|
5
5
|
r.description = 'We are all great grandparents'
|
6
6
|
r.save
|
@@ -9,37 +9,37 @@ class GreatGrandparentResourcesController < ApplicationController
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def show
|
12
|
-
super do
|
12
|
+
super.tap do
|
13
13
|
resource.description = 'I am a great grandparent'
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
def new
|
18
|
-
super do
|
18
|
+
super.tap do
|
19
19
|
resource.description = 'I am becoming a new great grandparent'
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
def edit
|
24
|
-
super do
|
24
|
+
super.tap do
|
25
25
|
resource.description = 'I am becoming a great grandparent again'
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
def create
|
30
|
-
super do
|
30
|
+
super.tap do
|
31
31
|
resource.description = 'I am a new great grandparent'
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def update
|
36
|
-
super do
|
36
|
+
super.tap do
|
37
37
|
resource.description = 'I am a great grandparent again'
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
def destroy
|
42
|
-
super do
|
42
|
+
super.tap do
|
43
43
|
@obituary = 'Here lies a dead great grandparent'
|
44
44
|
end
|
45
45
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: super_resources
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc2
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-02-
|
13
|
+
date: 2013-02-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: railties
|
@@ -36,19 +36,22 @@ executables: []
|
|
36
36
|
extensions: []
|
37
37
|
extra_rdoc_files: []
|
38
38
|
files:
|
39
|
-
- .DS_Store
|
40
39
|
- .gitignore
|
41
40
|
- Gemfile
|
42
41
|
- LICENSE.txt
|
43
42
|
- README.md
|
44
43
|
- Rakefile
|
44
|
+
- lib/generators/rails/super_controller_generator.rb
|
45
|
+
- lib/generators/rails/templates/controller.rb
|
45
46
|
- lib/super_resources.rb
|
46
47
|
- lib/super_resources/actions.rb
|
47
|
-
- lib/super_resources/cancan.rb
|
48
48
|
- lib/super_resources/controller.rb
|
49
|
-
- lib/super_resources/has_scope.rb
|
50
49
|
- lib/super_resources/nesting.rb
|
50
|
+
- lib/super_resources/railtie.rb
|
51
51
|
- lib/super_resources/resources.rb
|
52
|
+
- lib/super_resources/routing.rb
|
53
|
+
- lib/super_resources/support/cancan.rb
|
54
|
+
- lib/super_resources/support/has_scope.rb
|
52
55
|
- lib/super_resources/url_helpers.rb
|
53
56
|
- lib/super_resources/version.rb
|
54
57
|
- spec/controllers/actions_nested_spec.rb
|
@@ -59,7 +62,6 @@ files:
|
|
59
62
|
- spec/controllers/resources_adapted_spec.rb
|
60
63
|
- spec/controllers/resources_nested_spec.rb
|
61
64
|
- spec/controllers/resources_spec.rb
|
62
|
-
- spec/dummy/.DS_Store
|
63
65
|
- spec/dummy/README.rdoc
|
64
66
|
- spec/dummy/Rakefile
|
65
67
|
- spec/dummy/app/assets/javascripts/application.js
|
@@ -146,7 +148,6 @@ test_files:
|
|
146
148
|
- spec/controllers/resources_adapted_spec.rb
|
147
149
|
- spec/controllers/resources_nested_spec.rb
|
148
150
|
- spec/controllers/resources_spec.rb
|
149
|
-
- spec/dummy/.DS_Store
|
150
151
|
- spec/dummy/README.rdoc
|
151
152
|
- spec/dummy/Rakefile
|
152
153
|
- spec/dummy/app/assets/javascripts/application.js
|
data/.DS_Store
DELETED
Binary file
|
data/spec/dummy/.DS_Store
DELETED
Binary file
|