pager-resource_controller 1.0.20080513

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 (201) hide show
  1. data/LICENSE +22 -0
  2. data/README +276 -0
  3. data/Rakefile +55 -0
  4. data/TODO +1 -0
  5. data/generators/scaffold_resource/USAGE +29 -0
  6. data/generators/scaffold_resource/scaffold_resource_generator.rb +101 -0
  7. data/generators/scaffold_resource/templates/controller.rb +2 -0
  8. data/generators/scaffold_resource/templates/fixtures.yml +10 -0
  9. data/generators/scaffold_resource/templates/functional_test.rb +57 -0
  10. data/generators/scaffold_resource/templates/helper.rb +2 -0
  11. data/generators/scaffold_resource/templates/migration.rb +15 -0
  12. data/generators/scaffold_resource/templates/model.rb +2 -0
  13. data/generators/scaffold_resource/templates/old_migration.rb +13 -0
  14. data/generators/scaffold_resource/templates/shoulda_functional_test.rb +19 -0
  15. data/generators/scaffold_resource/templates/unit_test.rb +7 -0
  16. data/generators/scaffold_resource/templates/view__form.erb +6 -0
  17. data/generators/scaffold_resource/templates/view__form.haml +5 -0
  18. data/generators/scaffold_resource/templates/view_edit.erb +16 -0
  19. data/generators/scaffold_resource/templates/view_edit.haml +11 -0
  20. data/generators/scaffold_resource/templates/view_index.erb +22 -0
  21. data/generators/scaffold_resource/templates/view_index.haml +19 -0
  22. data/generators/scaffold_resource/templates/view_new.erb +12 -0
  23. data/generators/scaffold_resource/templates/view_new.haml +9 -0
  24. data/generators/scaffold_resource/templates/view_show.erb +9 -0
  25. data/generators/scaffold_resource/templates/view_show.haml +9 -0
  26. data/init.rb +6 -0
  27. data/install.rb +1 -0
  28. data/lib/resource_controller.rb +11 -0
  29. data/lib/resource_controller/accessors.rb +76 -0
  30. data/lib/resource_controller/action_options.rb +28 -0
  31. data/lib/resource_controller/actions.rb +75 -0
  32. data/lib/resource_controller/base.rb +15 -0
  33. data/lib/resource_controller/class_methods.rb +22 -0
  34. data/lib/resource_controller/controller.rb +63 -0
  35. data/lib/resource_controller/failable_action_options.rb +17 -0
  36. data/lib/resource_controller/helpers.rb +28 -0
  37. data/lib/resource_controller/helpers/current_objects.rb +69 -0
  38. data/lib/resource_controller/helpers/internal.rb +59 -0
  39. data/lib/resource_controller/helpers/nested.rb +45 -0
  40. data/lib/resource_controller/helpers/urls.rb +124 -0
  41. data/lib/resource_controller/response_collector.rb +21 -0
  42. data/lib/urligence.rb +50 -0
  43. data/rails/init.rb +1 -0
  44. data/test/Rakefile +10 -0
  45. data/test/app/controllers/application.rb +7 -0
  46. data/test/app/controllers/cms/options_controller.rb +3 -0
  47. data/test/app/controllers/cms/products_controller.rb +2 -0
  48. data/test/app/controllers/comments_controller.rb +3 -0
  49. data/test/app/controllers/people_controller.rb +9 -0
  50. data/test/app/controllers/photos_controller.rb +10 -0
  51. data/test/app/controllers/posts_controller.rb +10 -0
  52. data/test/app/controllers/projects_controller.rb +3 -0
  53. data/test/app/controllers/somethings_controller.rb +3 -0
  54. data/test/app/controllers/tags_controller.rb +13 -0
  55. data/test/app/controllers/users_controller.rb +12 -0
  56. data/test/app/helpers/application_helper.rb +3 -0
  57. data/test/app/helpers/cms/products_helper.rb +2 -0
  58. data/test/app/helpers/comments_helper.rb +2 -0
  59. data/test/app/helpers/options_helper.rb +2 -0
  60. data/test/app/helpers/people_helper.rb +2 -0
  61. data/test/app/helpers/photos_helper.rb +2 -0
  62. data/test/app/helpers/posts_helper.rb +2 -0
  63. data/test/app/helpers/projects_helper.rb +2 -0
  64. data/test/app/helpers/somethings_helper.rb +2 -0
  65. data/test/app/helpers/tags_helper.rb +2 -0
  66. data/test/app/helpers/users_helper.rb +2 -0
  67. data/test/app/models/account.rb +3 -0
  68. data/test/app/models/comment.rb +3 -0
  69. data/test/app/models/option.rb +3 -0
  70. data/test/app/models/photo.rb +4 -0
  71. data/test/app/models/post.rb +3 -0
  72. data/test/app/models/product.rb +3 -0
  73. data/test/app/models/project.rb +2 -0
  74. data/test/app/models/something.rb +2 -0
  75. data/test/app/models/tag.rb +3 -0
  76. data/test/app/views/cms/options/edit.rhtml +17 -0
  77. data/test/app/views/cms/options/index.rhtml +20 -0
  78. data/test/app/views/cms/options/new.rhtml +16 -0
  79. data/test/app/views/cms/options/show.rhtml +8 -0
  80. data/test/app/views/cms/products/edit.rhtml +17 -0
  81. data/test/app/views/cms/products/index.rhtml +20 -0
  82. data/test/app/views/cms/products/new.rhtml +16 -0
  83. data/test/app/views/cms/products/show.rhtml +8 -0
  84. data/test/app/views/comments/edit.rhtml +27 -0
  85. data/test/app/views/comments/index.rhtml +24 -0
  86. data/test/app/views/comments/new.rhtml +26 -0
  87. data/test/app/views/comments/show.rhtml +18 -0
  88. data/test/app/views/layouts/application.rhtml +17 -0
  89. data/test/app/views/layouts/comments.rhtml +17 -0
  90. data/test/app/views/layouts/options.rhtml +17 -0
  91. data/test/app/views/layouts/people.rhtml +17 -0
  92. data/test/app/views/layouts/photos.rhtml +17 -0
  93. data/test/app/views/layouts/projects.rhtml +17 -0
  94. data/test/app/views/layouts/somethings.rhtml +17 -0
  95. data/test/app/views/layouts/tags.rhtml +17 -0
  96. data/test/app/views/people/edit.rhtml +17 -0
  97. data/test/app/views/people/index.rhtml +20 -0
  98. data/test/app/views/people/new.rhtml +16 -0
  99. data/test/app/views/people/show.rhtml +8 -0
  100. data/test/app/views/photos/edit.rhtml +17 -0
  101. data/test/app/views/photos/index.rhtml +20 -0
  102. data/test/app/views/photos/new.rhtml +16 -0
  103. data/test/app/views/photos/show.rhtml +8 -0
  104. data/test/app/views/posts/edit.rhtml +22 -0
  105. data/test/app/views/posts/index.rhtml +22 -0
  106. data/test/app/views/posts/new.rhtml +21 -0
  107. data/test/app/views/posts/show.rhtml +13 -0
  108. data/test/app/views/projects/edit.rhtml +17 -0
  109. data/test/app/views/projects/index.rhtml +20 -0
  110. data/test/app/views/projects/new.rhtml +16 -0
  111. data/test/app/views/projects/show.rhtml +8 -0
  112. data/test/app/views/somethings/edit.rhtml +17 -0
  113. data/test/app/views/somethings/index.rhtml +20 -0
  114. data/test/app/views/somethings/new.rhtml +16 -0
  115. data/test/app/views/somethings/show.rhtml +8 -0
  116. data/test/app/views/tags/edit.rhtml +17 -0
  117. data/test/app/views/tags/index.rhtml +20 -0
  118. data/test/app/views/tags/index.rjs +0 -0
  119. data/test/app/views/tags/new.rhtml +16 -0
  120. data/test/app/views/tags/show.rhtml +8 -0
  121. data/test/app/views/users/edit.rhtml +17 -0
  122. data/test/app/views/users/index.rhtml +20 -0
  123. data/test/app/views/users/new.rhtml +16 -0
  124. data/test/app/views/users/show.rhtml +8 -0
  125. data/test/config/boot.rb +45 -0
  126. data/test/config/database.yml +16 -0
  127. data/test/config/environment.rb +64 -0
  128. data/test/config/environments/development.rb +21 -0
  129. data/test/config/environments/test.rb +19 -0
  130. data/test/config/routes.rb +51 -0
  131. data/test/db/migrate/001_create_posts.rb +12 -0
  132. data/test/db/migrate/002_create_products.rb +11 -0
  133. data/test/db/migrate/003_create_comments.rb +13 -0
  134. data/test/db/migrate/004_create_options.rb +12 -0
  135. data/test/db/migrate/005_create_photos.rb +11 -0
  136. data/test/db/migrate/006_create_tags.rb +17 -0
  137. data/test/db/migrate/007_create_somethings.rb +11 -0
  138. data/test/db/migrate/008_create_accounts.rb +11 -0
  139. data/test/db/migrate/009_add_account_id_to_photos.rb +9 -0
  140. data/test/db/migrate/010_create_projects.rb +11 -0
  141. data/test/db/schema.rb +65 -0
  142. data/test/script/console +3 -0
  143. data/test/script/destroy +3 -0
  144. data/test/script/generate +3 -0
  145. data/test/script/server +3 -0
  146. data/test/test/fixtures/accounts.yml +7 -0
  147. data/test/test/fixtures/comments.yml +11 -0
  148. data/test/test/fixtures/options.yml +9 -0
  149. data/test/test/fixtures/photos.yml +9 -0
  150. data/test/test/fixtures/photos_tags.yml +3 -0
  151. data/test/test/fixtures/posts.yml +9 -0
  152. data/test/test/fixtures/products.yml +7 -0
  153. data/test/test/fixtures/projects.yml +7 -0
  154. data/test/test/fixtures/somethings.yml +7 -0
  155. data/test/test/fixtures/tags.yml +7 -0
  156. data/test/test/functional/cms/options_controller_test.rb +20 -0
  157. data/test/test/functional/cms/products_controller_test.rb +18 -0
  158. data/test/test/functional/comments_controller_test.rb +26 -0
  159. data/test/test/functional/people_controller_test.rb +34 -0
  160. data/test/test/functional/photos_controller_test.rb +128 -0
  161. data/test/test/functional/posts_controller_test.rb +34 -0
  162. data/test/test/functional/projects_controller_test.rb +18 -0
  163. data/test/test/functional/somethings_controller_test.rb +28 -0
  164. data/test/test/functional/tags_controller_test.rb +64 -0
  165. data/test/test/functional/users_controller_test.rb +24 -0
  166. data/test/test/test_helper.rb +12 -0
  167. data/test/test/unit/accessors_test.rb +91 -0
  168. data/test/test/unit/account_test.rb +7 -0
  169. data/test/test/unit/action_options_test.rb +66 -0
  170. data/test/test/unit/base_test.rb +11 -0
  171. data/test/test/unit/comment_test.rb +10 -0
  172. data/test/test/unit/failable_action_options_test.rb +50 -0
  173. data/test/test/unit/helpers/current_objects_test.rb +127 -0
  174. data/test/test/unit/helpers/internal_test.rb +88 -0
  175. data/test/test/unit/helpers/nested_test.rb +82 -0
  176. data/test/test/unit/helpers/urls_test.rb +71 -0
  177. data/test/test/unit/helpers_test.rb +25 -0
  178. data/test/test/unit/option_test.rb +10 -0
  179. data/test/test/unit/photo_test.rb +10 -0
  180. data/test/test/unit/post_test.rb +10 -0
  181. data/test/test/unit/project_test.rb +10 -0
  182. data/test/test/unit/response_collector_test.rb +31 -0
  183. data/test/test/unit/something_test.rb +10 -0
  184. data/test/test/unit/tag_test.rb +10 -0
  185. data/test/test/unit/urligence_test.rb +203 -0
  186. data/test/vendor/plugins/shoulda/README +123 -0
  187. data/test/vendor/plugins/shoulda/Rakefile +29 -0
  188. data/test/vendor/plugins/shoulda/bin/convert_to_should_syntax +40 -0
  189. data/test/vendor/plugins/shoulda/init.rb +3 -0
  190. data/test/vendor/plugins/shoulda/lib/shoulda.rb +47 -0
  191. data/test/vendor/plugins/shoulda/lib/shoulda/active_record_helpers.rb +338 -0
  192. data/test/vendor/plugins/shoulda/lib/shoulda/color.rb +77 -0
  193. data/test/vendor/plugins/shoulda/lib/shoulda/context.rb +143 -0
  194. data/test/vendor/plugins/shoulda/lib/shoulda/controller_tests/controller_tests.rb +470 -0
  195. data/test/vendor/plugins/shoulda/lib/shoulda/controller_tests/formats/html.rb +192 -0
  196. data/test/vendor/plugins/shoulda/lib/shoulda/controller_tests/formats/xml.rb +162 -0
  197. data/test/vendor/plugins/shoulda/lib/shoulda/general.rb +119 -0
  198. data/test/vendor/plugins/shoulda/lib/shoulda/private_helpers.rb +17 -0
  199. data/test/vendor/plugins/shoulda/tasks/list_tests.rake +40 -0
  200. data/uninstall.rb +1 -0
  201. metadata +410 -0
@@ -0,0 +1,77 @@
1
+ require 'test/unit/ui/console/testrunner'
2
+
3
+ # Completely stolen from redgreen gem
4
+ #
5
+ # Adds colored output to your tests. Specify <tt>color: true</tt> in
6
+ # your <tt>~/.shoulda.conf</tt> file to enable.
7
+ #
8
+ # *Bug*: for some reason, this adds another line of output to the end of
9
+ # every rake task, as though there was another (empty) set of tests.
10
+ # A fix would be most welcome.
11
+ #
12
+ module ThoughtBot::Shoulda::Color
13
+ COLORS = { :clear => 0, :red => 31, :green => 32, :yellow => 33 } # :nodoc:
14
+ def self.method_missing(color_name, *args) # :nodoc:
15
+ color(color_name) + args.first + color(:clear)
16
+ end
17
+ def self.color(color) # :nodoc:
18
+ "\e[#{COLORS[color.to_sym]}m"
19
+ end
20
+ end
21
+
22
+ module Test # :nodoc:
23
+ module Unit # :nodoc:
24
+ class TestResult # :nodoc:
25
+ alias :old_to_s :to_s
26
+ def to_s
27
+ if old_to_s =~ /\d+ tests, \d+ assertions, (\d+) failures, (\d+) errors/
28
+ ThoughtBot::Shoulda::Color.send($1.to_i != 0 || $2.to_i != 0 ? :red : :green, $&)
29
+ end
30
+ end
31
+ end
32
+
33
+ class AutoRunner # :nodoc:
34
+ alias :old_initialize :initialize
35
+ def initialize(standalone)
36
+ old_initialize(standalone)
37
+ @runner = proc do |r|
38
+ Test::Unit::UI::Console::RedGreenTestRunner
39
+ end
40
+ end
41
+ end
42
+
43
+ class Failure # :nodoc:
44
+ alias :old_long_display :long_display
45
+ def long_display
46
+ # old_long_display.sub('Failure', ThoughtBot::Shoulda::Color.red('Failure'))
47
+ ThoughtBot::Shoulda::Color.red(old_long_display)
48
+ end
49
+ end
50
+
51
+ class Error # :nodoc:
52
+ alias :old_long_display :long_display
53
+ def long_display
54
+ # old_long_display.sub('Error', ThoughtBot::Shoulda::Color.yellow('Error'))
55
+ ThoughtBot::Shoulda::Color.yellow(old_long_display)
56
+ end
57
+ end
58
+
59
+ module UI # :nodoc:
60
+ module Console # :nodoc:
61
+ class RedGreenTestRunner < Test::Unit::UI::Console::TestRunner # :nodoc:
62
+ def output_single(something, level=NORMAL)
63
+ return unless (output?(level))
64
+ something = case something
65
+ when '.' then ThoughtBot::Shoulda::Color.green('.')
66
+ when 'F' then ThoughtBot::Shoulda::Color.red("F")
67
+ when 'E' then ThoughtBot::Shoulda::Color.yellow("E")
68
+ else something
69
+ end
70
+ @io.write(something)
71
+ @io.flush
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,143 @@
1
+ module ThoughtBot # :nodoc:
2
+ module Shoulda # :nodoc:
3
+ # = context and should blocks
4
+ #
5
+ # A context block groups should statements under a common setup/teardown method.
6
+ # Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability
7
+ # and readability of your test code.
8
+ #
9
+ # A context block can contain setup, should, should_eventually, and teardown blocks.
10
+ #
11
+ # class UserTest << Test::Unit::TestCase
12
+ # context "a User instance" do
13
+ # setup do
14
+ # @user = User.find(:first)
15
+ # end
16
+ #
17
+ # should "return its full name"
18
+ # assert_equal 'John Doe', @user.full_name
19
+ # end
20
+ # end
21
+ # end
22
+ #
23
+ # This code will produce the method <tt>"test a User instance should return its full name"</tt>.
24
+ #
25
+ # Contexts may be nested. Nested contexts run their setup blocks from out to in before each test.
26
+ # They then run their teardown blocks from in to out after each test.
27
+ #
28
+ # class UserTest << Test::Unit::TestCase
29
+ # context "a User instance" do
30
+ # setup do
31
+ # @user = User.find(:first)
32
+ # end
33
+ #
34
+ # should "return its full name"
35
+ # assert_equal 'John Doe', @user.full_name
36
+ # end
37
+ #
38
+ # context "with a profile" do
39
+ # setup do
40
+ # @user.profile = Profile.find(:first)
41
+ # end
42
+ #
43
+ # should "return true when sent :has_profile?"
44
+ # assert @user.has_profile?
45
+ # end
46
+ # end
47
+ # end
48
+ # end
49
+ #
50
+ # This code will produce the following methods
51
+ # * <tt>"test: a User instance should return its full name."</tt>
52
+ # * <tt>"test: a User instance with a profile should return true when sent :has_profile?."</tt>
53
+ #
54
+ # <b>A context block can exist next to normal <tt>def test_the_old_way; end</tt> tests</b>,
55
+ # meaning you do not have to fully commit to the context/should syntax in a test file.
56
+ #
57
+
58
+ module Context
59
+ def Context.included(other) # :nodoc:
60
+ @@context_names = []
61
+ @@setup_blocks = []
62
+ @@teardown_blocks = []
63
+ end
64
+
65
+ # Defines a test method. Can be called either inside our outside of a context.
66
+ # Optionally specify <tt>:unimplimented => true</tt> (see should_eventually).
67
+ #
68
+ # Example:
69
+ #
70
+ # class UserTest << Test::Unit::TestCase
71
+ # should "return first user on find(:first)"
72
+ # assert_equal users(:first), User.find(:first)
73
+ # end
74
+ # end
75
+ #
76
+ # Would create a test named
77
+ # 'test: should return first user on find(:first)'
78
+ #
79
+ def should(name, opts = {}, &should_block)
80
+ test_name = ["test:", @@context_names, "should", "#{name}. "].flatten.join(' ').to_sym
81
+
82
+ name_defined = eval("self.instance_methods.include?('#{test_name.to_s.gsub(/['"]/, '\$1')}')", should_block.binding)
83
+ raise ArgumentError, "'#{test_name}' is already defined" and return if name_defined
84
+
85
+ setup_blocks = @@setup_blocks.dup
86
+ teardown_blocks = @@teardown_blocks.dup
87
+
88
+ if opts[:unimplemented]
89
+ define_method test_name do |*args|
90
+ # XXX find a better way of doing this.
91
+ assert true
92
+ STDOUT.putc "X" # Tests for this model are missing.
93
+ end
94
+ else
95
+ define_method test_name do |*args|
96
+ begin
97
+ setup_blocks.each {|b| b.bind(self).call }
98
+ should_block.bind(self).call(*args)
99
+ ensure
100
+ teardown_blocks.reverse.each {|b| b.bind(self).call }
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ # Creates a context block with the given name.
107
+ def context(name, &context_block)
108
+ saved_setups = @@setup_blocks.dup
109
+ saved_teardowns = @@teardown_blocks.dup
110
+ saved_contexts = @@context_names.dup
111
+
112
+ @@context_names << name
113
+ context_block.bind(self).call
114
+
115
+ @@context_names = saved_contexts
116
+ @@setup_blocks = saved_setups
117
+ @@teardown_blocks = saved_teardowns
118
+ end
119
+
120
+ # Run before every should block in the current context.
121
+ # If a setup block appears in a nested context, it will be run after the setup blocks
122
+ # in the parent contexts.
123
+ def setup(&setup_block)
124
+ @@setup_blocks << setup_block
125
+ end
126
+
127
+ # Run after every should block in the current context.
128
+ # If a teardown block appears in a nested context, it will be run before the teardown
129
+ # blocks in the parent contexts.
130
+ def teardown(&teardown_block)
131
+ @@teardown_blocks << teardown_block
132
+ end
133
+
134
+ # Defines a specification that is not yet implemented.
135
+ # Will be displayed as an 'X' when running tests, and failures will not be shown.
136
+ # This is equivalent to:
137
+ # should(name, {:unimplemented => true}, &block)
138
+ def should_eventually(name, &block)
139
+ should("eventually #{name}", {:unimplemented => true}, &block)
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,470 @@
1
+ module ThoughtBot # :nodoc:
2
+ module Shoulda # :nodoc:
3
+ module Controller
4
+ def self.included(other) # :nodoc:
5
+ other.class_eval do
6
+ extend ThoughtBot::Shoulda::Controller::ClassMethods
7
+ include ThoughtBot::Shoulda::Controller::InstanceMethods
8
+ ThoughtBot::Shoulda::Controller::ClassMethods::VALID_FORMATS.each do |format|
9
+ include "ThoughtBot::Shoulda::Controller::#{format.to_s.upcase}".constantize
10
+ end
11
+ end
12
+ end
13
+
14
+ # = Macro test helpers for your controllers
15
+ #
16
+ # By using the macro helpers you can quickly and easily create concise and easy to read test suites.
17
+ #
18
+ # This code segment:
19
+ # context "on GET to :show for first record" do
20
+ # setup do
21
+ # get :show, :id => 1
22
+ # end
23
+ #
24
+ # should_assign_to :user
25
+ # should_respond_with :success
26
+ # should_render_template :show
27
+ # should_not_set_the_flash
28
+ #
29
+ # should "do something else really cool" do
30
+ # assert_equal 1, assigns(:user).id
31
+ # end
32
+ # end
33
+ #
34
+ # Would produce 5 tests for the +show+ action
35
+ #
36
+ # Furthermore, the should_be_restful helper will create an entire set of tests which will verify that your
37
+ # controller responds restfully to a variety of requested formats.
38
+ module ClassMethods
39
+ # Formats tested by #should_be_restful. Defaults to [:html, :xml]
40
+ VALID_FORMATS = Dir.glob(File.join(File.dirname(__FILE__), 'formats', '*')).map { |f| File.basename(f, '.rb') }.map(&:to_sym) # :doc:
41
+ VALID_FORMATS.each {|f| require "shoulda/controller_tests/formats/#{f}.rb"}
42
+
43
+ # Actions tested by #should_be_restful
44
+ VALID_ACTIONS = [:index, :show, :new, :edit, :create, :update, :destroy] # :doc:
45
+
46
+ # A ResourceOptions object is passed into should_be_restful in order to configure the tests for your controller.
47
+ #
48
+ # Example:
49
+ # class UsersControllerTest < Test::Unit::TestCase
50
+ # load_all_fixtures
51
+ #
52
+ # def setup
53
+ # ...normal setup code...
54
+ # @user = User.find(:first)
55
+ # end
56
+ #
57
+ # should_be_restful do |resource|
58
+ # resource.identifier = :id
59
+ # resource.klass = User
60
+ # resource.object = :user
61
+ # resource.parent = []
62
+ # resource.actions = [:index, :show, :new, :edit, :update, :create, :destroy]
63
+ # resource.formats = [:html, :xml]
64
+ #
65
+ # resource.create.params = { :name => "bob", :email => 'bob@bob.com', :age => 13}
66
+ # resource.update.params = { :name => "sue" }
67
+ #
68
+ # resource.create.redirect = "user_url(@user)"
69
+ # resource.update.redirect = "user_url(@user)"
70
+ # resource.destroy.redirect = "users_url"
71
+ #
72
+ # resource.create.flash = /created/i
73
+ # resource.update.flash = /updated/i
74
+ # resource.destroy.flash = /removed/i
75
+ # end
76
+ # end
77
+ #
78
+ # Whenever possible, the resource attributes will be set to sensible defaults.
79
+ #
80
+ class ResourceOptions
81
+ # Configuration options for the create, update, destroy actions under should_be_restful
82
+ class ActionOptions
83
+ # String evaled to get the target of the redirection.
84
+ # All of the instance variables set by the controller will be available to the
85
+ # evaled code.
86
+ #
87
+ # Example:
88
+ # resource.create.redirect = "user_url(@user.company, @user)"
89
+ #
90
+ # Defaults to a generated url based on the name of the controller, the action, and the resource.parents list.
91
+ attr_accessor :redirect
92
+
93
+ # String or Regexp describing a value expected in the flash. Will match against any flash key.
94
+ #
95
+ # Defaults:
96
+ # destroy:: /removed/
97
+ # create:: /created/
98
+ # update:: /updated/
99
+ attr_accessor :flash
100
+
101
+ # Hash describing the params that should be sent in with this action.
102
+ attr_accessor :params
103
+ end
104
+
105
+ # Configuration options for the denied actions under should_be_restful
106
+ #
107
+ # Example:
108
+ # context "The public" do
109
+ # setup do
110
+ # @request.session[:logged_in] = false
111
+ # end
112
+ #
113
+ # should_be_restful do |resource|
114
+ # resource.parent = :user
115
+ #
116
+ # resource.denied.actions = [:index, :show, :edit, :new, :create, :update, :destroy]
117
+ # resource.denied.flash = /get outta here/i
118
+ # resource.denied.redirect = 'new_session_url'
119
+ # end
120
+ # end
121
+ #
122
+ class DeniedOptions
123
+ # String evaled to get the target of the redirection.
124
+ # All of the instance variables set by the controller will be available to the
125
+ # evaled code.
126
+ #
127
+ # Example:
128
+ # resource.create.redirect = "user_url(@user.company, @user)"
129
+ attr_accessor :redirect
130
+
131
+ # String or Regexp describing a value expected in the flash. Will match against any flash key.
132
+ #
133
+ # Example:
134
+ # resource.create.flash = /created/
135
+ attr_accessor :flash
136
+
137
+ # Actions that should be denied (only used by resource.denied). <i>Note that these actions will
138
+ # only be tested if they are also listed in +resource.actions+</i>
139
+ # The special value of :all will deny all of the REST actions.
140
+ attr_accessor :actions
141
+ end
142
+
143
+ # Name of key in params that references the primary key.
144
+ # Will almost always be :id (default), unless you are using a plugin or have patched rails.
145
+ attr_accessor :identifier
146
+
147
+ # Name of the ActiveRecord class this resource is responsible for. Automatically determined from
148
+ # test class if not explicitly set. UserTest => :user
149
+ attr_accessor :klass
150
+
151
+ # Name of the instantiated ActiveRecord object that should be used by some of the tests.
152
+ # Defaults to the underscored name of the AR class. CompanyManager => :company_manager
153
+ attr_accessor :object
154
+
155
+ # Name of the parent AR objects.
156
+ #
157
+ # Example:
158
+ # # in the routes...
159
+ # map.resources :companies do
160
+ # map.resources :people do
161
+ # map.resources :limbs
162
+ # end
163
+ # end
164
+ #
165
+ # # in the tests...
166
+ # class PeopleControllerTest < Test::Unit::TestCase
167
+ # should_be_restful do |resource|
168
+ # resource.parent = :companies
169
+ # end
170
+ # end
171
+ #
172
+ # class LimbsControllerTest < Test::Unit::TestCase
173
+ # should_be_restful do |resource|
174
+ # resource.parents = [:companies, :people]
175
+ # end
176
+ # end
177
+ attr_accessor :parent
178
+ alias parents parent
179
+ alias parents= parent=
180
+
181
+ # Actions that should be tested. Must be a subset of VALID_ACTIONS (default).
182
+ # Tests for each actionw will only be generated if the action is listed here.
183
+ # The special value of :all will test all of the REST actions.
184
+ #
185
+ # Example (for a read-only controller):
186
+ # resource.actions = [:show, :index]
187
+ attr_accessor :actions
188
+
189
+ # Formats that should be tested. Must be a subset of VALID_FORMATS (default).
190
+ # Each action will be tested against the formats listed here. The special value
191
+ # of :all will test all of the supported formats.
192
+ #
193
+ # Example:
194
+ # resource.actions = [:html, :xml]
195
+ attr_accessor :formats
196
+
197
+ # ActionOptions object specifying options for the create action.
198
+ attr_accessor :create
199
+
200
+ # ActionOptions object specifying options for the update action.
201
+ attr_accessor :update
202
+
203
+ # ActionOptions object specifying options for the desrtoy action.
204
+ attr_accessor :destroy
205
+
206
+ # DeniedOptions object specifying which actions should return deny a request, and what should happen in that case.
207
+ attr_accessor :denied
208
+
209
+ def initialize # :nodoc:
210
+ @create = ActionOptions.new
211
+ @update = ActionOptions.new
212
+ @destroy = ActionOptions.new
213
+ @denied = DeniedOptions.new
214
+
215
+ @create.flash ||= /created/i
216
+ @update.flash ||= /updated/i
217
+ @destroy.flash ||= /removed/i
218
+ @denied.flash ||= /denied/i
219
+
220
+ @create.params ||= {}
221
+ @update.params ||= {}
222
+
223
+ @actions = VALID_ACTIONS
224
+ @formats = VALID_FORMATS
225
+ @denied.actions = []
226
+ end
227
+
228
+ def normalize!(target) # :nodoc:
229
+ @denied.actions = VALID_ACTIONS if @denied.actions == :all
230
+ @actions = VALID_ACTIONS if @actions == :all
231
+ @formats = VALID_FORMATS if @formats == :all
232
+
233
+ @denied.actions = @denied.actions.map(&:to_sym)
234
+ @actions = @actions.map(&:to_sym)
235
+ @formats = @formats.map(&:to_sym)
236
+
237
+ ensure_valid_members(@actions, VALID_ACTIONS, 'actions')
238
+ ensure_valid_members(@denied.actions, VALID_ACTIONS, 'denied.actions')
239
+ ensure_valid_members(@formats, VALID_FORMATS, 'formats')
240
+
241
+ @identifier ||= :id
242
+ @namespace ||= target.name.split('::').slice(0..-2).map(&:underscore).map(&:to_sym)
243
+ @klass ||= target.name.split('::').last.gsub(/ControllerTest$/, '').singularize.constantize
244
+ @object ||= @klass.name.tableize.singularize
245
+ @parent ||= []
246
+ @parent = [@parent] unless @parent.is_a? Array
247
+
248
+ singular_args = @parent.map {|n| "@#{object}.#{n}"}
249
+
250
+ namespace_pieces = [@namespace, @parent].flatten
251
+ @namespace_prefix = ''
252
+ @namespace_prefix << namespace_pieces.join('_')
253
+ @namespace_prefix << '_' unless @namespace_prefix == ''
254
+
255
+ @destroy.redirect ||= "#{@namespace_prefix}#{@object.pluralize}_url(#{singular_args.join(', ')})"
256
+
257
+ singular_args << "@#{object}"
258
+ @create.redirect ||= "#{@namespace_prefix}#{@object}_url(#{singular_args.join(', ')})"
259
+ @update.redirect ||= "#{@namespace_prefix}#{@object}_url(#{singular_args.join(', ')})"
260
+ @denied.redirect ||= "new_session_url"
261
+ end
262
+
263
+ private
264
+
265
+ def ensure_valid_members(ary, valid_members, name) # :nodoc:
266
+ invalid = ary - valid_members
267
+ raise ArgumentError, "Unsupported #{name}: #{invalid.inspect}" unless invalid.empty?
268
+ end
269
+ end
270
+
271
+ # :section: should_be_restful
272
+ # Generates a full suite of tests for a restful controller.
273
+ #
274
+ # The following definition will generate tests for the +index+, +show+, +new+,
275
+ # +edit+, +create+, +update+ and +destroy+ actions, in both +html+ and +xml+ formats:
276
+ #
277
+ # should_be_restful do |resource|
278
+ # resource.parent = :user
279
+ #
280
+ # resource.create.params = { :title => "first post", :body => 'blah blah blah'}
281
+ # resource.update.params = { :title => "changed" }
282
+ # end
283
+ #
284
+ # This generates about 40 tests, all of the format:
285
+ # "on GET to :show should assign @user."
286
+ # "on GET to :show should not set the flash."
287
+ # "on GET to :show should render 'show' template."
288
+ # "on GET to :show should respond with success."
289
+ # "on GET to :show as xml should assign @user."
290
+ # "on GET to :show as xml should have ContentType set to 'application/xml'."
291
+ # "on GET to :show as xml should respond with success."
292
+ # "on GET to :show as xml should return <user/> as the root element."
293
+ # The +resource+ parameter passed into the block is a ResourceOptions object, and
294
+ # is used to configure the tests for the details of your resources.
295
+ #
296
+ def should_be_restful(&blk) # :yields: resource
297
+ resource = ResourceOptions.new
298
+ blk.call(resource)
299
+ resource.normalize!(self)
300
+
301
+ resource.formats.each do |format|
302
+ resource.actions.each do |action|
303
+ if self.respond_to? :"make_#{action}_#{format}_tests"
304
+ self.send(:"make_#{action}_#{format}_tests", resource)
305
+ else
306
+ should "test #{action} #{format}" do
307
+ flunk "Test for #{action} as #{format} not implemented"
308
+ end
309
+ end
310
+ end
311
+ end
312
+ end
313
+
314
+ # :section: Test macros
315
+
316
+ # Macro that creates a test asserting that the flash contains the given value.
317
+ # val can be a String, a Regex, or nil (indicating that the flash should not be set)
318
+ #
319
+ # Example:
320
+ #
321
+ # should_set_the_flash_to "Thank you for placing this order."
322
+ # should_set_the_flash_to /created/i
323
+ # should_set_the_flash_to nil
324
+ def should_set_the_flash_to(val)
325
+ if val
326
+ should "have #{val.inspect} in the flash" do
327
+ assert_contains flash.values, val, ", Flash: #{flash.inspect}"
328
+ end
329
+ else
330
+ should "not set the flash" do
331
+ assert_equal({}, flash, "Flash was set to:\n#{flash.inspect}")
332
+ end
333
+ end
334
+ end
335
+
336
+ # Macro that creates a test asserting that the flash is empty. Same as
337
+ # @should_set_the_flash_to nil@
338
+ def should_not_set_the_flash
339
+ should_set_the_flash_to nil
340
+ end
341
+
342
+ # Macro that creates a test asserting that the controller assigned to @name
343
+ #
344
+ # Example:
345
+ #
346
+ # should_assign_to :user
347
+ def should_assign_to(name)
348
+ should "assign @#{name}" do
349
+ assert assigns(name.to_sym), "The action isn't assigning to @#{name}"
350
+ end
351
+ end
352
+
353
+ # Macro that creates a test asserting that the controller did not assign to @name
354
+ #
355
+ # Example:
356
+ #
357
+ # should_not_assign_to :user
358
+ def should_not_assign_to(name)
359
+ should "not assign to @#{name}" do
360
+ assert !assigns(name.to_sym), "@#{name} was visible"
361
+ end
362
+ end
363
+
364
+ # Macro that creates a test asserting that the controller responded with a 'response' status code.
365
+ # Example:
366
+ #
367
+ # should_respond_with :success
368
+ def should_respond_with(response)
369
+ should "respond with #{response}" do
370
+ assert_response response
371
+ end
372
+ end
373
+
374
+ # Macro that creates a test asserting that the controller rendered the given template.
375
+ # Example:
376
+ #
377
+ # should_render_template :new
378
+ def should_render_template(template)
379
+ should "render '#{template}' template" do
380
+ assert_template template.to_s
381
+ end
382
+ end
383
+
384
+ # Macro that creates a test asserting that the controller returned a redirect to the given path.
385
+ # The given string is evaled to produce the resulting redirect path. All of the instance variables
386
+ # set by the controller are available to the evaled string.
387
+ # Example:
388
+ #
389
+ # should_redirect_to '"/"'
390
+ # should_redirect_to "users_url(@user)"
391
+ def should_redirect_to(url)
392
+ should "redirect to \"#{url}\"" do
393
+ instantiate_variables_from_assigns do
394
+ assert_redirected_to eval(url, self.send(:binding), __FILE__, __LINE__)
395
+ end
396
+ end
397
+ end
398
+
399
+ # Macro that creates a test asserting that the rendered view contains a <form> element.
400
+ def should_render_a_form
401
+ should "display a form" do
402
+ assert_select "form", true, "The template doesn't contain a <form> element"
403
+ end
404
+ end
405
+ end
406
+
407
+ module InstanceMethods # :nodoc:
408
+
409
+ private # :enddoc:
410
+
411
+ SPECIAL_INSTANCE_VARIABLES = %w{
412
+ _cookies
413
+ _flash
414
+ _headers
415
+ _params
416
+ _request
417
+ _response
418
+ _session
419
+ action_name
420
+ before_filter_chain_aborted
421
+ cookies
422
+ flash
423
+ headers
424
+ ignore_missing_templates
425
+ logger
426
+ params
427
+ request
428
+ request_origin
429
+ response
430
+ session
431
+ template
432
+ template_class
433
+ template_root
434
+ url
435
+ variables_added
436
+ }.map(&:to_s)
437
+
438
+ def instantiate_variables_from_assigns(*names, &blk)
439
+ old = {}
440
+ names = (@response.template.assigns.keys - SPECIAL_INSTANCE_VARIABLES) if names.empty?
441
+ names.each do |name|
442
+ old[name] = instance_variable_get("@#{name}")
443
+ instance_variable_set("@#{name}", assigns(name.to_sym))
444
+ end
445
+ blk.call
446
+ names.each do |name|
447
+ instance_variable_set("@#{name}", old[name])
448
+ end
449
+ end
450
+
451
+ def get_existing_record(res) # :nodoc:
452
+ returning(instance_variable_get("@#{res.object}")) do |record|
453
+ assert(record, "This test requires you to set @#{res.object} in your setup block")
454
+ end
455
+ end
456
+
457
+ def make_parent_params(resource, record = nil, parent_names = nil) # :nodoc:
458
+ parent_names ||= resource.parents.reverse
459
+ return {} if parent_names == [] # Base case
460
+ parent_name = parent_names.shift
461
+ parent = record ? record.send(parent_name) : parent_name.to_s.classify.constantize.find(:first)
462
+
463
+ { :"#{parent_name}_id" => parent.id }.merge(make_parent_params(resource, parent, parent_names))
464
+ end
465
+
466
+ end
467
+ end
468
+ end
469
+ end
470
+