extended_inherited_resources 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "extended_inherited_resources"
8
+ gem.summary = "Slightly extended version of josevalim inherited_resources - for rails 2.3"
9
+ gem.description = ""
10
+ gem.email = "b.kosmowski@selleo.com"
11
+ gem.homepage = "http://github.com/stevo/extended_inherited_resources"
12
+ gem.authors = ["josevalim", "stevo"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "extended_inherited_resources #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,70 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{extended_inherited_resources}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["josevalim", "stevo"]
12
+ s.date = %q{2010-04-22}
13
+ s.description = %q{}
14
+ s.email = %q{b.kosmowski@selleo.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "extended_inherited_resources.gemspec",
27
+ "init.rb",
28
+ "lib/inherited_resources.rb",
29
+ "lib/inherited_resources/actions.rb",
30
+ "lib/inherited_resources/base.rb",
31
+ "lib/inherited_resources/base_helpers.rb",
32
+ "lib/inherited_resources/belongs_to_helpers.rb",
33
+ "lib/inherited_resources/blank_slate.rb",
34
+ "lib/inherited_resources/class_methods.rb",
35
+ "lib/inherited_resources/dsl.rb",
36
+ "lib/inherited_resources/legacy/respond_to.rb",
37
+ "lib/inherited_resources/legacy/responder.rb",
38
+ "lib/inherited_resources/locales/en.yml",
39
+ "lib/inherited_resources/polymorphic_helpers.rb",
40
+ "lib/inherited_resources/responder.rb",
41
+ "lib/inherited_resources/singleton_helpers.rb",
42
+ "lib/inherited_resources/url_helpers.rb",
43
+ "lib/inherited_resources/version.rb",
44
+ "test/helper.rb",
45
+ "test/test_extended_inherited_resources.rb"
46
+ ]
47
+ s.homepage = %q{http://github.com/stevo/extended_inherited_resources}
48
+ s.rdoc_options = ["--charset=UTF-8"]
49
+ s.require_paths = ["lib"]
50
+ s.rubygems_version = %q{1.3.6}
51
+ s.summary = %q{Slightly extended version of josevalim inherited_resources - for rails 2.3}
52
+ s.test_files = [
53
+ "test/helper.rb",
54
+ "test/test_extended_inherited_resources.rb"
55
+ ]
56
+
57
+ if s.respond_to? :specification_version then
58
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
59
+ s.specification_version = 3
60
+
61
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
62
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
63
+ else
64
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
65
+ end
66
+ else
67
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
68
+ end
69
+ end
70
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'inherited_resources'
@@ -0,0 +1,43 @@
1
+ # respond_to is the only file that should be loaded before hand. All others
2
+ # are loaded on demand.
3
+ #
4
+ unless defined?(ActionController::Responder)
5
+ require 'inherited_resources/legacy/responder'
6
+ require 'inherited_resources/legacy/respond_to'
7
+ end
8
+
9
+ require 'responders'
10
+ I18n.load_path.unshift File.expand_path(File.join(File.dirname(__FILE__), 'inherited_resources', 'locales', 'en.yml'))
11
+
12
+ module InheritedResources
13
+ ACTIONS = [ :index, :show, :new, :edit, :create, :update, :destroy ] unless self.const_defined?(:ACTIONS)
14
+
15
+ autoload :Actions, 'inherited_resources/actions'
16
+ autoload :Base, 'inherited_resources/base'
17
+ autoload :BaseHelpers, 'inherited_resources/base_helpers'
18
+ autoload :BelongsToHelpers, 'inherited_resources/belongs_to_helpers'
19
+ autoload :ClassMethods, 'inherited_resources/class_methods'
20
+ autoload :DSL, 'inherited_resources/dsl'
21
+ autoload :PolymorphicHelpers, 'inherited_resources/polymorphic_helpers'
22
+ autoload :SingletonHelpers, 'inherited_resources/singleton_helpers'
23
+ autoload :UrlHelpers, 'inherited_resources/url_helpers'
24
+ autoload :VERSION, 'inherited_resources/version'
25
+
26
+ # Change the flash keys used by FlashResponder.
27
+ def self.flash_keys=(array)
28
+ Responders::FlashResponder.flash_keys = array
29
+ end
30
+ end
31
+
32
+ class ActionController::Base
33
+ public :flash, :render
34
+
35
+ # If you cannot inherit from InheritedResources::Base you can call
36
+ # inherit_resource in your controller to have all the required modules and
37
+ # funcionality included.
38
+ def self.inherit_resources
39
+ InheritedResources::Base.inherit_resources(self)
40
+ initialize_resources_class_accessors!
41
+ create_resources_url_helpers!
42
+ end
43
+ 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(&block)
7
+ respond_with(*with_chain(collection), &block)
8
+ end
9
+ alias :index! :index
10
+
11
+ # GET /resources/1
12
+ def show(&block)
13
+ respond_with(*with_chain(resource), &block)
14
+ end
15
+ alias :show! :show
16
+
17
+ # GET /resources/new
18
+ def new(&block)
19
+ respond_with(*with_chain(build_resource), &block)
20
+ end
21
+ alias :new! :new
22
+
23
+ # GET /resources/1/edit
24
+ def edit(&block)
25
+ respond_with(*with_chain(resource), &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] ||= (configured_redirects(:create) || 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] ||= (configured_redirects(:update) || 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,47 @@
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
+ unloadable
16
+
17
+ # Overwrite inherit_resources to add specific InheritedResources behavior.
18
+ #
19
+ def self.inherit_resources(base)
20
+ base.class_eval do
21
+ include InheritedResources::Actions
22
+ include InheritedResources::BaseHelpers
23
+ extend InheritedResources::ClassMethods
24
+ extend InheritedResources::UrlHelpers
25
+
26
+ # Add at least :html mime type
27
+ respond_to :html
28
+ self.responder = InheritedResources::Responder
29
+
30
+ helper_method :collection_url, :collection_path, :resource_url, :resource_path,
31
+ :new_resource_url, :new_resource_path, :edit_resource_url, :edit_resource_path,
32
+ :parent_url, :parent_path, :resource, :collection, :resource_class, :association_chain,
33
+ :resource_instance_name, :resource_collection_name
34
+
35
+ base.with_options :instance_writer => false do |c|
36
+ c.class_inheritable_accessor :resource_class, :redirects
37
+ c.class_inheritable_array :parents_symbols
38
+ c.class_inheritable_hash :resources_configuration
39
+ end
40
+
41
+ protected :resource_class, :parents_symbols, :resources_configuration
42
+ end
43
+ end
44
+
45
+ inherit_resources(self)
46
+ end
47
+ end
@@ -0,0 +1,284 @@
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
+ def configured_redirects(for_action)
13
+
14
+ case self.class.redirects[for_action.to_sym]
15
+ when :index then
16
+ collection_url
17
+ when :edit then
18
+ edit_resource_url
19
+ when :show then
20
+ resource_url
21
+ when :root
22
+ root_url
23
+ end
24
+ end
25
+
26
+ # This is how the collection is loaded.
27
+ #
28
+ # You might want to overwrite this method if you want to add pagination
29
+ # for example. When you do that, don't forget to cache the result in an
30
+ # instance_variable:
31
+ #
32
+ # def collection
33
+ # @projects ||= end_of_association_chain.paginate(params[:page]).all
34
+ # end
35
+ #
36
+ def collection
37
+ get_collection_ivar || set_collection_ivar(end_of_association_chain.find(:all))
38
+ end
39
+
40
+ # This is how the resource is loaded.
41
+ #
42
+ # You might want to overwrite this method when you are using permalink.
43
+ # When you do that, don't forget to cache the result in an
44
+ # instance_variable:
45
+ #
46
+ # def resource
47
+ # @project ||= end_of_association_chain.find_by_permalink!(params[:id])
48
+ # end
49
+ #
50
+ # You also might want to add the exclamation mark at the end of the method
51
+ # because it will raise a 404 if nothing can be found. Otherwise it will
52
+ # probably render a 500 error message.
53
+ #
54
+ def resource
55
+ get_resource_ivar || set_resource_ivar(end_of_association_chain.find(params[:id]))
56
+ end
57
+
58
+ # This method is responsable for building the object on :new and :create
59
+ # methods. If you overwrite it, don't forget to cache the result in an
60
+ # instance variable.
61
+ #
62
+ def build_resource
63
+ get_resource_ivar || set_resource_ivar(end_of_association_chain.send(method_for_build, params[resource_instance_name] || {}))
64
+ end
65
+
66
+ # Responsible for saving the resource on :create method. Overwriting this
67
+ # allow you to control the way resource is saved. Let's say you have a
68
+ # PassworsController who is responsible for finding an user by email and
69
+ # sent password instructions for him. Instead of overwriting the entire
70
+ # :create method, you could do something:
71
+ #
72
+ # def create_resource(object)
73
+ # object.send_instructions_by_email
74
+ # end
75
+ #
76
+ def create_resource(object)
77
+ object.save
78
+ end
79
+
80
+ # Responsible for updating the resource in :update method. This allow you
81
+ # to handle how the resource is gona be updated, let's say in a different
82
+ # way then the usual :update_attributes:
83
+ #
84
+ # def update_resource(object, attributes)
85
+ # object.reset_password!(attributes)
86
+ # end
87
+ #
88
+ def update_resource(object, attributes)
89
+ object.update_attributes(attributes)
90
+ end
91
+
92
+ # Handle the :destroy method for the resource. Overwrite it to call your
93
+ # own method for destroing the resource, as:
94
+ #
95
+ # def destroy_resource(object)
96
+ # object.cancel
97
+ # end
98
+ #
99
+ def destroy_resource(object)
100
+ object.destroy
101
+ end
102
+
103
+ # This class allows you to set a instance variable to begin your
104
+ # association chain. For example, usually your projects belongs to users
105
+ # and that means that they belong to the current logged in user. So you
106
+ # could do this:
107
+ #
108
+ # def begin_of_association_chain
109
+ # @current_user
110
+ # end
111
+ #
112
+ # So every time we instantiate a project, we will do:
113
+ #
114
+ # @current_user.projects.build(params[:project])
115
+ # @current_user.projects.find(params[:id])
116
+ #
117
+ # The variable set in begin_of_association_chain is not sent when building
118
+ # urls, so this is never going to happen when calling resource_url:
119
+ #
120
+ # project_url(@current_user, @project)
121
+ #
122
+ # If the user actually scopes the url, you should use belongs_to method
123
+ # and declare that projects belong to user.
124
+ #
125
+ def begin_of_association_chain
126
+ nil
127
+ end
128
+
129
+ # Returns if the controller has a parent. When only base helpers are loaded,
130
+ # it's always false and should not be overwriten.
131
+ #
132
+ def parent?
133
+ false
134
+ end
135
+
136
+ # Returns the association chain, with all parents (does not include the
137
+ # current resource).
138
+ #
139
+ def association_chain
140
+ @association_chain ||=
141
+ symbols_for_association_chain.inject([begin_of_association_chain]) do |chain, symbol|
142
+ chain << evaluate_parent(symbol, resources_configuration[symbol], chain.last)
143
+ end.compact.freeze
144
+ end
145
+
146
+ # Overwrite this method to provide other interpolation options when
147
+ # the flash message is going to be set.
148
+ #
149
+ # def interpolation_options
150
+ # { }
151
+ # end
152
+
153
+ private
154
+
155
+ # Adds the given object to association chain.
156
+ def with_chain(object)
157
+ association_chain + [ object ]
158
+ end
159
+
160
+ # Fast accessor to resource_collection_name
161
+ #
162
+ def resource_collection_name #:nodoc:
163
+ self.resources_configuration[:self][:collection_name]
164
+ end
165
+
166
+ # Fast accessor to resource_instance_name
167
+ #
168
+ def resource_instance_name #:nodoc:
169
+ self.resources_configuration[:self][:instance_name]
170
+ end
171
+
172
+ # This methods gets your begin_of_association_chain, join it with your
173
+ # parents chain and returns the scoped association.
174
+ #
175
+ def end_of_association_chain #:nodoc:
176
+ if chain = association_chain.last
177
+ if method_for_association_chain
178
+ apply_scopes_if_available(chain.send(method_for_association_chain))
179
+ else
180
+ # This only happens when we specify begin_of_association_chain in
181
+ # a singletion controller without parents. In this case, the chain
182
+ # is exactly the begin_of_association_chain which is already an
183
+ # instance and then not scopable.
184
+ chain
185
+ end
186
+ else
187
+ apply_scopes_if_available(resource_class)
188
+ end
189
+ end
190
+
191
+ # Returns the appropriated method to build the resource.
192
+ #
193
+ def method_for_build #:nodoc:
194
+ (begin_of_association_chain || parent?) ? method_for_association_build : :new
195
+ end
196
+
197
+ # Returns the name of the method used for build the resource in cases
198
+ # where we have a parent. This is overwritten in singleton scenarios.
199
+ #
200
+ def method_for_association_build
201
+ :build
202
+ end
203
+
204
+ # Returns the name of the method to be called, before returning the end
205
+ # of the association chain. This is overwriten by singleton cases
206
+ # where no method for association chain is called.
207
+ #
208
+ def method_for_association_chain #:nodoc:
209
+ resource_collection_name
210
+ end
211
+
212
+ # Get resource ivar based on the current resource controller.
213
+ #
214
+ def get_resource_ivar #:nodoc:
215
+ instance_variable_get("@#{resource_instance_name}")
216
+ end
217
+
218
+ # Set resource ivar based on the current resource controller.
219
+ #
220
+ def set_resource_ivar(resource) #:nodoc:
221
+ instance_variable_set("@#{resource_instance_name}", resource)
222
+ end
223
+
224
+ # Get collection ivar based on the current resource controller.
225
+ #
226
+ def get_collection_ivar #:nodoc:
227
+ instance_variable_get("@#{resource_collection_name}")
228
+ end
229
+
230
+ # Set collection ivar based on the current resource controller.
231
+ #
232
+ def set_collection_ivar(collection) #:nodoc:
233
+ instance_variable_set("@#{resource_collection_name}", collection)
234
+ end
235
+
236
+ # Used to allow to specify success and failure within just one block:
237
+ #
238
+ # def create
239
+ # create! do |success, failure|
240
+ # failure.html { redirect_to root_url }
241
+ # end
242
+ # end
243
+ #
244
+ # It also calculates the response url in case a block without arity is
245
+ # given and returns it. Otherwise returns nil.
246
+ #
247
+ def respond_with_dual_blocks(object, options, &block) #:nodoc:
248
+ args = (with_chain(object) << options)
249
+
250
+ case block.try(:arity)
251
+ when 2
252
+ respond_with(*args) do |responder|
253
+ blank_slate = InheritedResources::BlankSlate.new
254
+ if object.errors.empty?
255
+ block.call(responder, blank_slate)
256
+ else
257
+ block.call(blank_slate, responder)
258
+ end
259
+ end
260
+ when 1
261
+ respond_with(*args, &block)
262
+ else
263
+ options[:location] = block.call if block
264
+ respond_with(*args)
265
+ end
266
+ end
267
+
268
+ # Hook to apply scopes. By default returns only the target_object given.
269
+ # It's extend by HasScopeHelpers.
270
+ #
271
+ def apply_scopes_if_available(target_object) #:nodoc:
272
+ respond_to?(:apply_scopes) ? apply_scopes(target_object) : target_object
273
+ end
274
+
275
+ # Symbols chain in base helpers return nothing. This is later overwriten
276
+ # by belongs_to and can be complex in polymorphic cases.
277
+ #
278
+ def symbols_for_association_chain #:nodoc:
279
+ []
280
+ end
281
+
282
+ end
283
+ end
284
+