resource_controller 0.4.9 → 0.5.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.
Files changed (54) hide show
  1. data/README.rdoc +37 -1
  2. data/Rakefile +2 -2
  3. data/lib/resource_controller.rb +10 -5
  4. data/lib/resource_controller/class_methods.rb +3 -1
  5. data/lib/resource_controller/controller.rb +6 -0
  6. data/lib/resource_controller/helpers/nested.rb +21 -3
  7. data/lib/resource_controller/helpers/singleton_customizations.rb +60 -0
  8. data/lib/resource_controller/helpers/urls.rb +5 -1
  9. data/lib/resource_controller/singleton.rb +15 -0
  10. data/lib/resource_controller/version.rb +2 -2
  11. data/test/app/controllers/accounts_controller.rb +6 -0
  12. data/test/app/controllers/cms/products_controller.rb +1 -1
  13. data/test/app/controllers/images_controller.rb +4 -0
  14. data/test/app/controllers/options_controller.rb +8 -0
  15. data/test/app/helpers/accounts_helper.rb +2 -0
  16. data/test/app/helpers/images_helper.rb +2 -0
  17. data/test/app/models/account.rb +1 -0
  18. data/test/app/models/image.rb +3 -0
  19. data/test/app/models/user.rb +3 -0
  20. data/test/app/views/accounts/_form.html.erb +4 -0
  21. data/test/app/views/accounts/edit.html.erb +14 -0
  22. data/test/app/views/accounts/new.html.erb +12 -0
  23. data/test/app/views/accounts/show.html.erb +5 -0
  24. data/test/app/views/images/_form.html.erb +4 -0
  25. data/test/app/views/images/edit.html.erb +14 -0
  26. data/test/app/views/images/new.html.erb +12 -0
  27. data/test/app/views/options/_form.html.erb +8 -0
  28. data/test/app/views/options/edit.html.erb +16 -0
  29. data/test/app/views/options/index.html.erb +21 -0
  30. data/test/app/views/options/new.html.erb +12 -0
  31. data/test/app/views/options/show.html.erb +10 -0
  32. data/test/config/database.yml +0 -3
  33. data/test/config/environment.rb +2 -2
  34. data/test/config/routes.rb +8 -1
  35. data/test/db/migrate/002_create_products.rb +1 -1
  36. data/test/db/migrate/004_create_options.rb +3 -2
  37. data/test/db/migrate/011_create_images.rb +12 -0
  38. data/test/db/migrate/012_create_users.rb +11 -0
  39. data/test/db/schema.rb +13 -6
  40. data/test/log/development.log +84 -0
  41. data/test/log/test.log +4880 -0
  42. data/test/test/fixtures/images.yml +6 -0
  43. data/test/test/fixtures/users.yml +5 -0
  44. data/test/test/functional/images_controller_test.rb +37 -0
  45. data/test/test/unit/helpers/current_objects_test.rb +6 -0
  46. data/test/test/unit/helpers/nested_test.rb +5 -1
  47. data/test/test/unit/helpers/singleton_current_objects_test.rb +68 -0
  48. data/test/test/unit/helpers/singleton_nested_test.rb +77 -0
  49. data/test/test/unit/helpers/singleton_urls_test.rb +67 -0
  50. data/test/test/unit/helpers/urls_test.rb +5 -1
  51. data/test/test/unit/image_test.rb +7 -0
  52. metadata +35 -5
  53. data/README +0 -282
  54. data/lib/tasks/gem.rake +0 -67
data/README.rdoc CHANGED
@@ -102,6 +102,42 @@ With actions that can fail, the scoping defaults to success. That means that cr
102
102
  end
103
103
 
104
104
  end
105
+
106
+ == Singleton Resource
107
+
108
+ If you want to create a singleton RESTful controller inherit from ResourceController::Singleton.
109
+
110
+ class AccountsController < ResourceController::Singleton
111
+ end
112
+
113
+ *Note:* This type of controllers handle a single resource only so the index action and all the collection helpers (collection_url, collection_path...) are not available for them.
114
+
115
+ Loading objects in singletons is similar to plural controllers with one exception. For non-nested singleton controllers you should override the object method as it defaults to nil for them.
116
+
117
+ class AccountsController < ResourceController::Singleton
118
+ private
119
+ def object
120
+ @object ||= Account.find(session[:account_id])
121
+ end
122
+ end
123
+
124
+ In other cases you can use the default logic and override it only if you use permalinks or anything special.
125
+
126
+ Singleton nesting with both :has_many and :has_one associations is provided...
127
+
128
+ map.resource :account, :has_many => :options # /account/options, account is a singleton parent
129
+ map.resources :users, :has_one => :image # /users/1/image, image is a singleton child
130
+
131
+ If you have the :has_many association with a singleton parent remember to override parent_object for your :has_many controller as it returns nil by default in this case.
132
+
133
+ class OptionsController < ResourceController::Base
134
+ belongs_to :account
135
+
136
+ protected
137
+ def parent_object
138
+ Account.find(session[:account_id])
139
+ end
140
+ end
105
141
 
106
142
  == Helpers (ResourceController::Helpers)
107
143
 
@@ -279,4 +315,4 @@ resource_controller was created, and is maintained by {James Golick}[http://jame
279
315
 
280
316
  == License
281
317
 
282
- resource_controller is available under the {MIT License}[http://en.wikipedia.org/wiki/MIT_License]
318
+ resource_controller is available under the {MIT License}[http://en.wikipedia.org/wiki/MIT_License]
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rake/rdoctask'
4
4
  require File.dirname(__FILE__)+'/lib/resource_controller/version'
5
- Dir['lib/tasks/**.rake'].each { |tasks| load tasks }
5
+ Dir['tasks/**.rake'].each { |tasks| load tasks }
6
6
 
7
7
  desc 'Default: run unit tests.'
8
8
  task :default => :test
@@ -19,7 +19,7 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
19
19
  rdoc.rdoc_dir = 'rdoc'
20
20
  rdoc.title = 'ResourceController'
21
21
  rdoc.options << '--line-numbers' << '--inline-source'
22
- rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('README.rdoc')
23
23
  rdoc.rdoc_files.include('lib/**/*.rb')
24
24
  end
25
25
 
@@ -1,14 +1,19 @@
1
1
  module ResourceController
2
- ACTIONS = [:index, :show, :new_action, :create, :edit, :update, :destroy].freeze
3
- FAILABLE_ACTIONS = ACTIONS - [:index, :new_action, :edit].freeze
4
- NAME_ACCESSORS = [:model_name, :route_name, :object_name]
2
+ ACTIONS = [:index, :show, :new_action, :create, :edit, :update, :destroy].freeze
3
+ SINGLETON_ACTIONS = (ACTIONS - [:index]).freeze
4
+ FAILABLE_ACTIONS = ACTIONS - [:index, :new_action, :edit].freeze
5
+ NAME_ACCESSORS = [:model_name, :route_name, :object_name]
5
6
 
6
7
  module ActionControllerExtension
7
8
  unloadable
8
9
 
9
- def resource_controller
10
+ def resource_controller(*args)
10
11
  include ResourceController::Controller
11
- end
12
+
13
+ if args.include?(:singleton)
14
+ include ResourceController::Helpers::SingletonCustomizations
15
+ end
16
+ end
12
17
  end
13
18
  end
14
19
 
@@ -10,8 +10,10 @@ module ResourceController
10
10
  config = {}
11
11
  config.merge!(opts.pop) if opts.last.is_a?(Hash)
12
12
 
13
+ all_actions = (singleton? ? ResourceController::SINGLETON_ACTIONS : ResourceController::ACTIONS) - [:new_action] + [:new]
14
+
13
15
  actions_to_remove = []
14
- actions_to_remove += (ResourceController::ACTIONS - [:new_action] + [:new]) - opts unless opts.first == :all
16
+ actions_to_remove += all_actions - opts unless opts.first == :all
15
17
  actions_to_remove += [*config[:except]] if config[:except]
16
18
  actions_to_remove.uniq!
17
19
 
@@ -57,6 +57,12 @@ module ResourceController
57
57
  flash "Successfully removed!"
58
58
  wants.html { redirect_to collection_url }
59
59
  end
60
+
61
+ class << self
62
+ def singleton?
63
+ false
64
+ end
65
+ end
60
66
  end
61
67
  end
62
68
  end
@@ -1,7 +1,7 @@
1
1
  # Nested and Polymorphic Resource Helpers
2
2
  #
3
3
  module ResourceController::Helpers::Nested
4
- protected
4
+ protected
5
5
  # Returns the relevant association proxy of the parent. (i.e. /posts/1/comments # => @post.comments)
6
6
  #
7
7
  def parent_association
@@ -11,7 +11,19 @@ module ResourceController::Helpers::Nested
11
11
  # Returns the type of the current parent
12
12
  #
13
13
  def parent_type
14
- @parent_type ||= [*belongs_to].find { |parent| !params["#{parent}_id".to_sym].nil? }
14
+ @parent_type ||= parent_type_from_params || parent_type_from_request
15
+ end
16
+
17
+ # Returns the type of the current parent extracted from params
18
+ #
19
+ def parent_type_from_params
20
+ [*belongs_to].find { |parent| !params["#{parent}_id".to_sym].nil? }
21
+ end
22
+
23
+ # Returns the type of the current parent extracted form a request path
24
+ #
25
+ def parent_type_from_request
26
+ [*belongs_to].find { |parent| request.path.split('/').include? parent.to_s }
15
27
  end
16
28
 
17
29
  # Returns true/false based on whether or not a parent is present.
@@ -20,6 +32,12 @@ module ResourceController::Helpers::Nested
20
32
  !parent_type.nil?
21
33
  end
22
34
 
35
+ # Returns true/false based on whether or not a parent is a singleton.
36
+ #
37
+ def parent_singleton?
38
+ !parent_type_from_request.nil?
39
+ end
40
+
23
41
  # Returns the current parent param, if there is a parent. (i.e. params[:post_id])
24
42
  def parent_param
25
43
  params["#{parent_type}_id".to_sym]
@@ -34,7 +52,7 @@ module ResourceController::Helpers::Nested
34
52
  # Returns the current parent object if a parent object is present.
35
53
  #
36
54
  def parent_object
37
- parent? ? parent_model.find(parent_param) : nil
55
+ parent? && !parent_singleton? ? parent_model.find(parent_param) : nil
38
56
  end
39
57
 
40
58
  # If there is a parent, returns the relevant association proxy. Otherwise returns model.
@@ -0,0 +1,60 @@
1
+ # Singleton Resource Helpers
2
+ #
3
+ # Used internally to transform a plural RESTful controller into a singleton
4
+ #
5
+ module ResourceController::Helpers::SingletonCustomizations
6
+ def self.included(subclass)
7
+ subclass.class_eval do
8
+ methods_to_undefine = [:param, :index, :collection, :load_collection, :collection_url,
9
+ :collection_path, :hash_for_collection_url, :hash_for_collection_path]
10
+ methods_to_undefine.each { |method| undef_method(method) if method_defined? method }
11
+
12
+ class << self
13
+ def singleton?
14
+ true
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ protected
21
+ # Used to fetch the current object in a singleton controller.
22
+ #
23
+ # By defult this method is able to fetch the current object for resources nested with the :has_one association only. (i.e. /users/1/image # => @user.image)
24
+ # In other cases you should override this method and provide your custom code to fetch a singleton resource object, like using a session hash.
25
+ #
26
+ # class AccountsController < ResourceController::Singleton
27
+ # private
28
+ # def object
29
+ # @object ||= Account.find(session[:account_id])
30
+ # end
31
+ # end
32
+ #
33
+ def object
34
+ @object ||= parent? ? end_of_association_chain : nil
35
+ end
36
+
37
+ # Returns the :has_one association proxy of the parent. (i.e. /users/1/image # => @user.image)
38
+ #
39
+ def parent_association
40
+ @parent_association ||= parent_object.send(model_name.to_sym)
41
+ end
42
+
43
+ # Used internally to provide the options to smart_url in a singleton controller.
44
+ #
45
+ def object_url_options(action_prefix = nil, alternate_object = nil)
46
+ [action_prefix] + namespaces + [parent_url_options, route_name.to_sym]
47
+ end
48
+
49
+ # Builds the object, but doesn't save it, during the new, and create action.
50
+ #
51
+ def build_object
52
+ @object ||= singleton_build_object_base.send parent? ? "build_#{model_name}".to_sym : :new, object_params
53
+ end
54
+
55
+ # Singleton controllers don't build off of association proxy, so we can't use end_of_association_chain here
56
+ #
57
+ def singleton_build_object_base
58
+ parent? ? parent_object : model
59
+ end
60
+ end
@@ -110,7 +110,11 @@ module ResourceController::Helpers::Urls
110
110
  end
111
111
 
112
112
  def parent_url_options
113
- parent? ? [parent_type.to_sym, parent_object] : nil
113
+ if parent?
114
+ parent_singleton? ? parent_type.to_sym : [parent_type.to_sym, parent_object]
115
+ else
116
+ nil
117
+ end
114
118
  end
115
119
 
116
120
  # Returns all of the current namespaces of the current controller, symbolized, in array form.
@@ -0,0 +1,15 @@
1
+ module ResourceController
2
+
3
+ # == ResourceController::Singleton
4
+ #
5
+ # Inherit from this class to create your RESTful singleton controller. See the README for usage.
6
+ #
7
+ class Singleton < ApplicationController
8
+ unloadable
9
+
10
+ def self.inherited(subclass)
11
+ super
12
+ subclass.class_eval { resource_controller :singleton }
13
+ end
14
+ end
15
+ end
@@ -1,8 +1,8 @@
1
1
  module ResourceController
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 4
5
- TINY = 9
4
+ MINOR = 5
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,6 @@
1
+ class AccountsController < ResourceController::Singleton
2
+ protected
3
+ def object
4
+ Account.find(:first)
5
+ end
6
+ end
@@ -1,3 +1,3 @@
1
1
  class Cms::ProductsController < ResourceController::Base
2
- create.flash_now 'something'
2
+ create.flash 'something'
3
3
  end
@@ -0,0 +1,4 @@
1
+ class ImagesController < ResourceController::Singleton
2
+ belongs_to :user
3
+ actions :create
4
+ end
@@ -0,0 +1,8 @@
1
+ class OptionsController < ResourceController::Base
2
+ belongs_to :account
3
+
4
+ protected
5
+ def parent_object
6
+ Account.find(:first)
7
+ end
8
+ end
@@ -0,0 +1,2 @@
1
+ module AccountsHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module ImagesHelper
2
+ end
@@ -1,3 +1,4 @@
1
1
  class Account < ActiveRecord::Base
2
2
  has_many :photos
3
+ has_many :options
3
4
  end
@@ -0,0 +1,3 @@
1
+ class Image < ActiveRecord::Base
2
+ belongs_to :user
3
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ has_one :image
3
+ end
@@ -0,0 +1,4 @@
1
+ <p>
2
+ <label for="account_name">Name:</label>
3
+ <%= f.text_field :name %>
4
+ </p>
@@ -0,0 +1,14 @@
1
+ <h1>Editing Account</h1>
2
+
3
+ <%= error_messages_for :account %>
4
+
5
+ <% form_for(:account, :url => object_url, :html => { :method => :put }) do |f| %>
6
+ <%= render :partial => "form", :locals => { :f => f } %>
7
+ <p>
8
+ <%=submit_tag "Update"%>
9
+ </p>
10
+ <% end %>
11
+
12
+ <br/>
13
+
14
+ <%= link_to 'Show', object_url %>
@@ -0,0 +1,12 @@
1
+ <h1>New Account</h1>
2
+
3
+ <%= error_messages_for :account %>
4
+
5
+ <% form_for(:account, :url => object_url) do |f| %>
6
+ <%= render :partial => "form", :locals => { :f => f } %>
7
+ <p>
8
+ <%= submit_tag "Create" %>
9
+ </p>
10
+ <% end %>
11
+ <br/>
12
+ <%= link_to 'Back', object_url %>
@@ -0,0 +1,5 @@
1
+ <p>
2
+ <strong>Name:</strong><%=h @account.name %>
3
+ </p>
4
+
5
+ <%= link_to 'Edit', edit_object_url %>
@@ -0,0 +1,4 @@
1
+ <p>
2
+ <label for="image_user_id">User:</label>
3
+ <%= f.text_field :user_id %>
4
+ </p>
@@ -0,0 +1,14 @@
1
+ <h1>Editing Image</h1>
2
+
3
+ <%= error_messages_for :image %>
4
+
5
+ <% form_for(:image, :url => object_url, :html => { :method => :put }) do |f| %>
6
+ <%= render :partial => "form", :locals => { :f => f } %>
7
+ <p>
8
+ <%=submit_tag "Update"%>
9
+ </p>
10
+ <% end %>
11
+
12
+ <br/>
13
+
14
+ <%= link_to 'Show', object_url %>
@@ -0,0 +1,12 @@
1
+ <h1>New Image</h1>
2
+
3
+ <%= error_messages_for :image %>
4
+
5
+ <% form_for(:image, :url => object_url) do |f| %>
6
+ <%= render :partial => "form", :locals => { :f => f } %>
7
+ <p>
8
+ <%= submit_tag "Create" %>
9
+ </p>
10
+ <% end %>
11
+ <br/>
12
+ <%= link_to 'Back', object_url %>
@@ -0,0 +1,8 @@
1
+ <p>
2
+ <label for="option_account_id">Account:</label>
3
+ <%= f.text_field :account_id %>
4
+ </p>
5
+ <p>
6
+ <label for="option_name">Title:</label>
7
+ <%= f.text_field :title %>
8
+ </p>
@@ -0,0 +1,16 @@
1
+ <h1>Editing Option</h1>
2
+
3
+ <%= error_messages_for :option %>
4
+
5
+ <% form_for(:option, :url => object_url, :html => { :method => :put }) do |f| %>
6
+ <%= render :partial => "form", :locals => { :f => f } %>
7
+ <p>
8
+ <%=submit_tag "Update"%>
9
+ </p>
10
+ <% end %>
11
+
12
+ <br/>
13
+
14
+ <%= link_to 'Show', object_url %>
15
+ |
16
+ <%= link_to 'Back', collection_url %>
@@ -0,0 +1,21 @@
1
+ <h1>Listing Options</h1>
2
+
3
+ <table>
4
+ <tr>
5
+ <th>Account</th>
6
+ <th>Title</th>
7
+ </tr>
8
+ <%- @options.each do |option|%>
9
+ <tr>
10
+ <td><%=h option.account_id %></td>
11
+ <td><%=h option.title %></td>
12
+
13
+ <td><%=link_to 'Show', object_url(option) %></td>
14
+ <td><%=link_to 'Edit', edit_object_url(option) %></td>
15
+ <td><%=link_to 'Destroy', object_url(option), :confirm => 'Are you sure?', :method => :delete %></td>
16
+ </tr>
17
+ <% end %>
18
+ </table>
19
+ <br/>
20
+
21
+ <%= link_to 'New Option', new_object_url %>