karsthammer-inherited_resources 1.1.2

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.
Files changed (36) hide show
  1. data/CHANGELOG +119 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +516 -0
  4. data/Rakefile +43 -0
  5. data/lib/generators/rails/USAGE +10 -0
  6. data/lib/generators/rails/inherited_resources_controller_generator.rb +11 -0
  7. data/lib/generators/rails/templates/controller.rb +5 -0
  8. data/lib/inherited_resources.rb +37 -0
  9. data/lib/inherited_resources/actions.rb +67 -0
  10. data/lib/inherited_resources/base.rb +44 -0
  11. data/lib/inherited_resources/base_helpers.rb +270 -0
  12. data/lib/inherited_resources/belongs_to_helpers.rb +97 -0
  13. data/lib/inherited_resources/blank_slate.rb +12 -0
  14. data/lib/inherited_resources/class_methods.rb +267 -0
  15. data/lib/inherited_resources/dsl.rb +26 -0
  16. data/lib/inherited_resources/polymorphic_helpers.rb +155 -0
  17. data/lib/inherited_resources/responder.rb +6 -0
  18. data/lib/inherited_resources/singleton_helpers.rb +95 -0
  19. data/lib/inherited_resources/url_helpers.rb +188 -0
  20. data/lib/inherited_resources/version.rb +3 -0
  21. data/test/aliases_test.rb +144 -0
  22. data/test/association_chain_test.rb +125 -0
  23. data/test/base_test.rb +278 -0
  24. data/test/belongs_to_test.rb +105 -0
  25. data/test/class_methods_test.rb +132 -0
  26. data/test/customized_base_test.rb +168 -0
  27. data/test/customized_belongs_to_test.rb +76 -0
  28. data/test/defaults_test.rb +70 -0
  29. data/test/nested_belongs_to_test.rb +108 -0
  30. data/test/optional_belongs_to_test.rb +164 -0
  31. data/test/polymorphic_test.rb +186 -0
  32. data/test/redirect_to_test.rb +51 -0
  33. data/test/singleton_test.rb +83 -0
  34. data/test/test_helper.rb +40 -0
  35. data/test/url_helpers_test.rb +665 -0
  36. metadata +142 -0
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+ require File.join(File.dirname(__FILE__), 'lib', 'inherited_resources', 'version')
7
+
8
+ begin
9
+ require 'jeweler'
10
+ Jeweler::Tasks.new do |s|
11
+ s.name = "inherited_resources"
12
+ s.version = InheritedResources::VERSION.dup
13
+ s.rubyforge_project = "inherited_resources"
14
+ s.summary = "Inherited Resources speeds up development by making your controllers inherit all restful actions so you just have to focus on what is important."
15
+ s.email = "jose.valim@gmail.com"
16
+ s.homepage = "http://github.com/josevalim/inherited_resources"
17
+ s.description = "Inherited Resources speeds up development by making your controllers inherit all restful actions so you just have to focus on what is important."
18
+ s.authors = ['José Valim']
19
+ s.files = FileList["[A-Z]*", "init.rb", "{lib}/**/*"]
20
+ s.add_dependency("responders", "~> 0.6.0")
21
+ s.add_dependency("has_scope", "~> 0.5.0")
22
+ end
23
+
24
+ Jeweler::GemcutterTasks.new
25
+ rescue LoadError
26
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
27
+ end
28
+
29
+ desc 'Run tests for InheritedResources.'
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.pattern = 'test/**/*_test.rb'
32
+ t.verbose = true
33
+ end
34
+
35
+ desc 'Generate documentation for InheritedResources.'
36
+ Rake::RDocTask.new(:rdoc) do |rdoc|
37
+ rdoc.rdoc_dir = 'rdoc'
38
+ rdoc.title = 'InheritedResources'
39
+ rdoc.options << '--line-numbers' << '--inline-source'
40
+ rdoc.rdoc_files.include('README.rdoc')
41
+ rdoc.rdoc_files.include('MIT-LICENSE')
42
+ rdoc.rdoc_files.include('lib/**/*.rb')
43
+ end
@@ -0,0 +1,10 @@
1
+ Description:
2
+ Stubs out a scaffolded controller and its views using InheritedResources.
3
+ Pass the model name, either CamelCased or under_scored. The controller
4
+ name is retrieved as a pluralized version of the model name.
5
+
6
+ To create a controller within a module, specify the model name as a
7
+ path like 'parent_module/controller_name'.
8
+
9
+ This generates a controller class in app/controllers and invokes helper,
10
+ template engine and test framework generators.
@@ -0,0 +1,11 @@
1
+ require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
2
+
3
+ module Rails
4
+ module Generators
5
+ class InheritedResourcesControllerGenerator < ScaffoldControllerGenerator
6
+ def self.source_root
7
+ @source_root ||= File.expand_path("templates", File.dirname(__FILE__))
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ class <%= controller_class_name %>Controller < InheritedResources::Base
2
+ <% if options[:singleton] -%>
3
+ defaults :singleton => true
4
+ <% end -%>
5
+ end
@@ -0,0 +1,37 @@
1
+ require 'responders'
2
+
3
+ module InheritedResources
4
+ ACTIONS = [ :index, :show, :new, :edit, :create, :update, :destroy ] unless self.const_defined?(:ACTIONS)
5
+
6
+ autoload :Actions, 'inherited_resources/actions'
7
+ autoload :Base, 'inherited_resources/base'
8
+ autoload :BaseHelpers, 'inherited_resources/base_helpers'
9
+ autoload :BelongsToHelpers, 'inherited_resources/belongs_to_helpers'
10
+ autoload :ClassMethods, 'inherited_resources/class_methods'
11
+ autoload :DSL, 'inherited_resources/dsl'
12
+ autoload :PolymorphicHelpers, 'inherited_resources/polymorphic_helpers'
13
+ autoload :SingletonHelpers, 'inherited_resources/singleton_helpers'
14
+ autoload :UrlHelpers, 'inherited_resources/url_helpers'
15
+ autoload :VERSION, 'inherited_resources/version'
16
+
17
+ # Change the flash keys used by FlashResponder.
18
+ def self.flash_keys=(array)
19
+ Responders::FlashResponder.flash_keys = array
20
+ end
21
+
22
+ class Railtie < ::Rails::Railtie
23
+ config.inherited_resources = InheritedResources
24
+ config.generators.scaffold_controller = :inherited_resources_controller
25
+ end
26
+ end
27
+
28
+ class ActionController::Base
29
+ # If you cannot inherit from InheritedResources::Base you can call
30
+ # inherit_resource in your controller to have all the required modules and
31
+ # funcionality included.
32
+ def self.inherit_resources
33
+ InheritedResources::Base.inherit_resources(self)
34
+ initialize_resources_class_accessors!
35
+ create_resources_url_helpers!
36
+ end
37
+ end
@@ -0,0 +1,67 @@
1
+ module InheritedResources
2
+ # Holds all default actions for InheritedResouces.
3
+ module Actions
4
+
5
+ # GET /resources
6
+ def index(options={}, &block)
7
+ respond_with(*(with_chain(collection) << options), &block)
8
+ end
9
+ alias :index! :index
10
+
11
+ # GET /resources/1
12
+ def show(options={}, &block)
13
+ respond_with(*(with_chain(resource) << options), &block)
14
+ end
15
+ alias :show! :show
16
+
17
+ # GET /resources/new
18
+ def new(options={}, &block)
19
+ respond_with(*(with_chain(build_resource) << options), &block)
20
+ end
21
+ alias :new! :new
22
+
23
+ # GET /resources/1/edit
24
+ def edit(options={}, &block)
25
+ respond_with(*(with_chain(resource) << options), &block)
26
+ end
27
+ alias :edit! :edit
28
+
29
+ # POST /resources
30
+ def create(options={}, &block)
31
+ object = build_resource
32
+
33
+ if create_resource(object)
34
+ options[:location] ||= resource_url rescue nil
35
+ end
36
+
37
+ respond_with_dual_blocks(object, options, &block)
38
+ end
39
+ alias :create! :create
40
+
41
+ # PUT /resources/1
42
+ def update(options={}, &block)
43
+ object = resource
44
+
45
+ if update_resource(object, params[resource_instance_name])
46
+ options[:location] ||= resource_url rescue nil
47
+ end
48
+
49
+ respond_with_dual_blocks(object, options, &block)
50
+ end
51
+ alias :update! :update
52
+
53
+ # DELETE /resources/1
54
+ def destroy(options={}, &block)
55
+ object = resource
56
+ options[:location] ||= collection_url rescue nil
57
+
58
+ destroy_resource(object)
59
+ respond_with_dual_blocks(object, options, &block)
60
+ end
61
+ alias :destroy! :destroy
62
+
63
+ # Make aliases protected
64
+ protected :index!, :show!, :new!, :create!, :edit!, :update!, :destroy!
65
+ end
66
+ end
67
+
@@ -0,0 +1,44 @@
1
+ require 'inherited_resources/blank_slate'
2
+ require 'inherited_resources/responder'
3
+
4
+ module InheritedResources
5
+ # = Base
6
+ #
7
+ # This is the base class that holds all actions. If you see the code for each
8
+ # action, they are quite similar to Rails default scaffold.
9
+ #
10
+ # To change your base behavior, you can overwrite your actions and call super,
11
+ # call <tt>default</tt> class method, call <<tt>actions</tt> class method
12
+ # or overwrite some helpers in the base_helpers.rb file.
13
+ #
14
+ class Base < ::ApplicationController
15
+ # Overwrite inherit_resources to add specific InheritedResources behavior.
16
+ def self.inherit_resources(base)
17
+ base.class_eval do
18
+ include InheritedResources::Actions
19
+ include InheritedResources::BaseHelpers
20
+ extend InheritedResources::ClassMethods
21
+ extend InheritedResources::UrlHelpers
22
+
23
+ # Add at least :html mime type
24
+ respond_to :html
25
+ self.responder = InheritedResources::Responder
26
+
27
+ helper_method :collection_url, :collection_path, :resource_url, :resource_path,
28
+ :new_resource_url, :new_resource_path, :edit_resource_url, :edit_resource_path,
29
+ :parent_url, :parent_path, :resource, :collection, :resource_class, :association_chain,
30
+ :resource_instance_name, :resource_collection_name
31
+
32
+ base.with_options :instance_writer => false do |c|
33
+ c.class_inheritable_accessor :resource_class
34
+ c.class_inheritable_array :parents_symbols
35
+ c.class_inheritable_hash :resources_configuration
36
+ end
37
+
38
+ protected :resource_class, :parents_symbols, :resources_configuration
39
+ end
40
+ end
41
+
42
+ inherit_resources(self)
43
+ end
44
+ end
@@ -0,0 +1,270 @@
1
+ # Whenever base is required load the dumb responder since it's used inside actions.
2
+ require 'inherited_resources/blank_slate'
3
+
4
+ module InheritedResources
5
+ # Base helpers for InheritedResource work. Some methods here can be overwriten
6
+ # and you will need to do that to customize your controllers from time to time.
7
+ #
8
+ module BaseHelpers
9
+
10
+ protected
11
+
12
+ # This is how the collection is loaded.
13
+ #
14
+ # You might want to overwrite this method if you want to add pagination
15
+ # for example. When you do that, don't forget to cache the result in an
16
+ # instance_variable:
17
+ #
18
+ # def collection
19
+ # @projects ||= end_of_association_chain.paginate(params[:page]).all
20
+ # end
21
+ #
22
+ def collection
23
+ get_collection_ivar || set_collection_ivar(end_of_association_chain.all)
24
+ end
25
+
26
+ # This is how the resource is loaded.
27
+ #
28
+ # You might want to overwrite this method when you are using permalink.
29
+ # When you do that, don't forget to cache the result in an
30
+ # instance_variable:
31
+ #
32
+ # def resource
33
+ # @project ||= end_of_association_chain.find_by_permalink!(params[:id])
34
+ # end
35
+ #
36
+ # You also might want to add the exclamation mark at the end of the method
37
+ # because it will raise a 404 if nothing can be found. Otherwise it will
38
+ # probably render a 500 error message.
39
+ #
40
+ def resource
41
+ get_resource_ivar || set_resource_ivar(end_of_association_chain.find(params[:id]))
42
+ end
43
+
44
+ # This method is responsable for building the object on :new and :create
45
+ # methods. If you overwrite it, don't forget to cache the result in an
46
+ # instance variable.
47
+ #
48
+ def build_resource
49
+ get_resource_ivar || set_resource_ivar(end_of_association_chain.send(method_for_build, params[resource_instance_name] || {}))
50
+ end
51
+
52
+ # Responsible for saving the resource on :create method. Overwriting this
53
+ # allow you to control the way resource is saved. Let's say you have a
54
+ # PassworsController who is responsible for finding an user by email and
55
+ # sent password instructions for him. Instead of overwriting the entire
56
+ # :create method, you could do something:
57
+ #
58
+ # def create_resource(object)
59
+ # object.send_instructions_by_email
60
+ # end
61
+ #
62
+ def create_resource(object)
63
+ object.save
64
+ end
65
+
66
+ # Responsible for updating the resource in :update method. This allow you
67
+ # to handle how the resource is gona be updated, let's say in a different
68
+ # way then the usual :update_attributes:
69
+ #
70
+ # def update_resource(object, attributes)
71
+ # object.reset_password!(attributes)
72
+ # end
73
+ #
74
+ def update_resource(object, attributes)
75
+ object.update_attributes(attributes)
76
+ end
77
+
78
+ # Handle the :destroy method for the resource. Overwrite it to call your
79
+ # own method for destroing the resource, as:
80
+ #
81
+ # def destroy_resource(object)
82
+ # object.cancel
83
+ # end
84
+ #
85
+ def destroy_resource(object)
86
+ object.destroy
87
+ end
88
+
89
+ # This class allows you to set a instance variable to begin your
90
+ # association chain. For example, usually your projects belongs to users
91
+ # and that means that they belong to the current logged in user. So you
92
+ # could do this:
93
+ #
94
+ # def begin_of_association_chain
95
+ # @current_user
96
+ # end
97
+ #
98
+ # So every time we instantiate a project, we will do:
99
+ #
100
+ # @current_user.projects.build(params[:project])
101
+ # @current_user.projects.find(params[:id])
102
+ #
103
+ # The variable set in begin_of_association_chain is not sent when building
104
+ # urls, so this is never going to happen when calling resource_url:
105
+ #
106
+ # project_url(@current_user, @project)
107
+ #
108
+ # If the user actually scopes the url, you should use belongs_to method
109
+ # and declare that projects belong to user.
110
+ #
111
+ def begin_of_association_chain
112
+ nil
113
+ end
114
+
115
+ # Returns if the controller has a parent. When only base helpers are loaded,
116
+ # it's always false and should not be overwriten.
117
+ #
118
+ def parent?
119
+ false
120
+ end
121
+
122
+ # Returns the association chain, with all parents (does not include the
123
+ # current resource).
124
+ #
125
+ def association_chain
126
+ @association_chain ||=
127
+ symbols_for_association_chain.inject([begin_of_association_chain]) do |chain, symbol|
128
+ chain << evaluate_parent(symbol, resources_configuration[symbol], chain.last)
129
+ end.compact.freeze
130
+ end
131
+
132
+ # Overwrite this method to provide other interpolation options when
133
+ # the flash message is going to be set.
134
+ #
135
+ # def interpolation_options
136
+ # { }
137
+ # end
138
+
139
+ private
140
+
141
+ # Adds the given object to association chain.
142
+ def with_chain(object)
143
+ association_chain + [ object ]
144
+ end
145
+
146
+ # Fast accessor to resource_collection_name
147
+ #
148
+ def resource_collection_name #:nodoc:
149
+ self.resources_configuration[:self][:collection_name]
150
+ end
151
+
152
+ # Fast accessor to resource_instance_name
153
+ #
154
+ def resource_instance_name #:nodoc:
155
+ self.resources_configuration[:self][:instance_name]
156
+ end
157
+
158
+ # This methods gets your begin_of_association_chain, join it with your
159
+ # parents chain and returns the scoped association.
160
+ #
161
+ def end_of_association_chain #:nodoc:
162
+ if chain = association_chain.last
163
+ if method_for_association_chain
164
+ apply_scopes_if_available(chain.send(method_for_association_chain))
165
+ else
166
+ # This only happens when we specify begin_of_association_chain in
167
+ # a singletion controller without parents. In this case, the chain
168
+ # is exactly the begin_of_association_chain which is already an
169
+ # instance and then not scopable.
170
+ chain
171
+ end
172
+ else
173
+ apply_scopes_if_available(resource_class)
174
+ end
175
+ end
176
+
177
+ # Returns the appropriated method to build the resource.
178
+ #
179
+ def method_for_build #:nodoc:
180
+ (begin_of_association_chain || parent?) ? method_for_association_build : :new
181
+ end
182
+
183
+ # Returns the name of the method used for build the resource in cases
184
+ # where we have a parent. This is overwritten in singleton scenarios.
185
+ #
186
+ def method_for_association_build
187
+ :build
188
+ end
189
+
190
+ # Returns the name of the method to be called, before returning the end
191
+ # of the association chain. This is overwriten by singleton cases
192
+ # where no method for association chain is called.
193
+ #
194
+ def method_for_association_chain #:nodoc:
195
+ resource_collection_name
196
+ end
197
+
198
+ # Get resource ivar based on the current resource controller.
199
+ #
200
+ def get_resource_ivar #:nodoc:
201
+ instance_variable_get("@#{resource_instance_name}")
202
+ end
203
+
204
+ # Set resource ivar based on the current resource controller.
205
+ #
206
+ def set_resource_ivar(resource) #:nodoc:
207
+ instance_variable_set("@#{resource_instance_name}", resource)
208
+ end
209
+
210
+ # Get collection ivar based on the current resource controller.
211
+ #
212
+ def get_collection_ivar #:nodoc:
213
+ instance_variable_get("@#{resource_collection_name}")
214
+ end
215
+
216
+ # Set collection ivar based on the current resource controller.
217
+ #
218
+ def set_collection_ivar(collection) #:nodoc:
219
+ instance_variable_set("@#{resource_collection_name}", collection)
220
+ end
221
+
222
+ # Used to allow to specify success and failure within just one block:
223
+ #
224
+ # def create
225
+ # create! do |success, failure|
226
+ # failure.html { redirect_to root_url }
227
+ # end
228
+ # end
229
+ #
230
+ # It also calculates the response url in case a block without arity is
231
+ # given and returns it. Otherwise returns nil.
232
+ #
233
+ def respond_with_dual_blocks(object, options, &block) #:nodoc:
234
+ args = (with_chain(object) << options)
235
+
236
+ case block.try(:arity)
237
+ when 2
238
+ respond_with(*args) do |responder|
239
+ blank_slate = InheritedResources::BlankSlate.new
240
+ if object.errors.empty?
241
+ block.call(responder, blank_slate)
242
+ else
243
+ block.call(blank_slate, responder)
244
+ end
245
+ end
246
+ when 1
247
+ respond_with(*args, &block)
248
+ else
249
+ options[:location] = block.call if block
250
+ respond_with(*args)
251
+ end
252
+ end
253
+
254
+ # Hook to apply scopes. By default returns only the target_object given.
255
+ # It's extend by HasScopeHelpers.
256
+ #
257
+ def apply_scopes_if_available(target_object) #:nodoc:
258
+ respond_to?(:apply_scopes) ? apply_scopes(target_object) : target_object
259
+ end
260
+
261
+ # Symbols chain in base helpers return nothing. This is later overwriten
262
+ # by belongs_to and can be complex in polymorphic cases.
263
+ #
264
+ def symbols_for_association_chain #:nodoc:
265
+ []
266
+ end
267
+
268
+ end
269
+ end
270
+