pager-resource_controller 1.0.20080513

Sign up to get free protection for your applications and to get access to all the features.
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
+