nileshtrivedi-lp_resource_builder 0.5.1

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 (240) hide show
  1. data/LICENSE +22 -0
  2. data/README.rdoc +326 -0
  3. data/Rakefile +35 -0
  4. data/generators/scaffold_resource/USAGE +29 -0
  5. data/generators/scaffold_resource/scaffold_resource_generator.rb +183 -0
  6. data/generators/scaffold_resource/templates/controller.rb +69 -0
  7. data/generators/scaffold_resource/templates/fixtures.yml +10 -0
  8. data/generators/scaffold_resource/templates/functional_test.rb +57 -0
  9. data/generators/scaffold_resource/templates/helper.rb +22 -0
  10. data/generators/scaffold_resource/templates/migration.rb +15 -0
  11. data/generators/scaffold_resource/templates/model.rb +60 -0
  12. data/generators/scaffold_resource/templates/old_migration.rb +13 -0
  13. data/generators/scaffold_resource/templates/rspec/functional_spec.rb +255 -0
  14. data/generators/scaffold_resource/templates/rspec/helper_spec.rb +11 -0
  15. data/generators/scaffold_resource/templates/rspec/routing_spec.rb +61 -0
  16. data/generators/scaffold_resource/templates/rspec/unit_spec.rb +11 -0
  17. data/generators/scaffold_resource/templates/rspec/views/edit_spec.rb +28 -0
  18. data/generators/scaffold_resource/templates/rspec/views/index_spec.rb +26 -0
  19. data/generators/scaffold_resource/templates/rspec/views/new_spec.rb +30 -0
  20. data/generators/scaffold_resource/templates/rspec/views/show_spec.rb +25 -0
  21. data/generators/scaffold_resource/templates/shoulda_functional_test.rb +19 -0
  22. data/generators/scaffold_resource/templates/unit_test.rb +7 -0
  23. data/generators/scaffold_resource/templates/view__form.erb +6 -0
  24. data/generators/scaffold_resource/templates/view__form.haml +5 -0
  25. data/generators/scaffold_resource/templates/view_edit.erb +2 -0
  26. data/generators/scaffold_resource/templates/view_edit.haml +11 -0
  27. data/generators/scaffold_resource/templates/view_index.erb +2 -0
  28. data/generators/scaffold_resource/templates/view_index.haml +19 -0
  29. data/generators/scaffold_resource/templates/view_new.erb +2 -0
  30. data/generators/scaffold_resource/templates/view_new.haml +9 -0
  31. data/generators/scaffold_resource/templates/view_partial_edit.html.erb +18 -0
  32. data/generators/scaffold_resource/templates/view_partial_index.html.erb +28 -0
  33. data/generators/scaffold_resource/templates/view_partial_new.html.erb +13 -0
  34. data/generators/scaffold_resource/templates/view_partial_show.html.erb +9 -0
  35. data/generators/scaffold_resource/templates/view_show.erb +2 -0
  36. data/generators/scaffold_resource/templates/view_show.haml +9 -0
  37. data/init.rb +1 -0
  38. data/lib/resource_controller/accessors.rb +77 -0
  39. data/lib/resource_controller/action_options.rb +40 -0
  40. data/lib/resource_controller/actions.rb +75 -0
  41. data/lib/resource_controller/base.rb +15 -0
  42. data/lib/resource_controller/class_methods.rb +24 -0
  43. data/lib/resource_controller/controller.rb +69 -0
  44. data/lib/resource_controller/failable_action_options.rb +25 -0
  45. data/lib/resource_controller/helpers/current_objects.rb +69 -0
  46. data/lib/resource_controller/helpers/internal.rb +76 -0
  47. data/lib/resource_controller/helpers/nested.rb +63 -0
  48. data/lib/resource_controller/helpers/singleton_customizations.rb +60 -0
  49. data/lib/resource_controller/helpers/urls.rb +128 -0
  50. data/lib/resource_controller/helpers.rb +28 -0
  51. data/lib/resource_controller/response_collector.rb +27 -0
  52. data/lib/resource_controller/singleton.rb +15 -0
  53. data/lib/resource_controller/version.rb +9 -0
  54. data/lib/resource_controller.rb +20 -0
  55. data/lib/urligence.rb +50 -0
  56. data/rails/init.rb +6 -0
  57. data/test/Rakefile +10 -0
  58. data/test/app/controllers/accounts_controller.rb +6 -0
  59. data/test/app/controllers/application.rb +7 -0
  60. data/test/app/controllers/cms/options_controller.rb +3 -0
  61. data/test/app/controllers/cms/products_controller.rb +3 -0
  62. data/test/app/controllers/comments_controller.rb +3 -0
  63. data/test/app/controllers/images_controller.rb +4 -0
  64. data/test/app/controllers/options_controller.rb +8 -0
  65. data/test/app/controllers/people_controller.rb +9 -0
  66. data/test/app/controllers/photos_controller.rb +11 -0
  67. data/test/app/controllers/posts_controller.rb +10 -0
  68. data/test/app/controllers/projects_controller.rb +3 -0
  69. data/test/app/controllers/somethings_controller.rb +3 -0
  70. data/test/app/controllers/tags_controller.rb +13 -0
  71. data/test/app/controllers/users_controller.rb +12 -0
  72. data/test/app/helpers/accounts_helper.rb +2 -0
  73. data/test/app/helpers/application_helper.rb +3 -0
  74. data/test/app/helpers/cms/products_helper.rb +2 -0
  75. data/test/app/helpers/comments_helper.rb +2 -0
  76. data/test/app/helpers/images_helper.rb +2 -0
  77. data/test/app/helpers/options_helper.rb +2 -0
  78. data/test/app/helpers/people_helper.rb +2 -0
  79. data/test/app/helpers/photos_helper.rb +2 -0
  80. data/test/app/helpers/posts_helper.rb +2 -0
  81. data/test/app/helpers/projects_helper.rb +2 -0
  82. data/test/app/helpers/somethings_helper.rb +2 -0
  83. data/test/app/helpers/tags_helper.rb +2 -0
  84. data/test/app/helpers/users_helper.rb +2 -0
  85. data/test/app/models/account.rb +4 -0
  86. data/test/app/models/comment.rb +3 -0
  87. data/test/app/models/image.rb +3 -0
  88. data/test/app/models/option.rb +3 -0
  89. data/test/app/models/photo.rb +4 -0
  90. data/test/app/models/post.rb +3 -0
  91. data/test/app/models/product.rb +3 -0
  92. data/test/app/models/project.rb +2 -0
  93. data/test/app/models/something.rb +2 -0
  94. data/test/app/models/tag.rb +3 -0
  95. data/test/app/models/user.rb +3 -0
  96. data/test/app/views/accounts/_form.html.erb +4 -0
  97. data/test/app/views/accounts/edit.html.erb +14 -0
  98. data/test/app/views/accounts/new.html.erb +12 -0
  99. data/test/app/views/accounts/show.html.erb +5 -0
  100. data/test/app/views/cms/options/edit.rhtml +17 -0
  101. data/test/app/views/cms/options/index.rhtml +20 -0
  102. data/test/app/views/cms/options/new.rhtml +16 -0
  103. data/test/app/views/cms/options/show.rhtml +8 -0
  104. data/test/app/views/cms/products/edit.rhtml +17 -0
  105. data/test/app/views/cms/products/index.rhtml +20 -0
  106. data/test/app/views/cms/products/new.rhtml +16 -0
  107. data/test/app/views/cms/products/show.rhtml +8 -0
  108. data/test/app/views/comments/edit.rhtml +27 -0
  109. data/test/app/views/comments/index.rhtml +24 -0
  110. data/test/app/views/comments/new.rhtml +26 -0
  111. data/test/app/views/comments/show.rhtml +18 -0
  112. data/test/app/views/images/_form.html.erb +4 -0
  113. data/test/app/views/images/edit.html.erb +14 -0
  114. data/test/app/views/images/new.html.erb +12 -0
  115. data/test/app/views/layouts/application.rhtml +17 -0
  116. data/test/app/views/layouts/comments.rhtml +17 -0
  117. data/test/app/views/layouts/options.rhtml +17 -0
  118. data/test/app/views/layouts/people.rhtml +17 -0
  119. data/test/app/views/layouts/photos.rhtml +17 -0
  120. data/test/app/views/layouts/projects.rhtml +17 -0
  121. data/test/app/views/layouts/somethings.rhtml +17 -0
  122. data/test/app/views/layouts/tags.rhtml +17 -0
  123. data/test/app/views/options/_form.html.erb +8 -0
  124. data/test/app/views/options/edit.html.erb +16 -0
  125. data/test/app/views/options/index.html.erb +21 -0
  126. data/test/app/views/options/new.html.erb +12 -0
  127. data/test/app/views/options/show.html.erb +10 -0
  128. data/test/app/views/people/edit.rhtml +17 -0
  129. data/test/app/views/people/index.rhtml +20 -0
  130. data/test/app/views/people/new.rhtml +16 -0
  131. data/test/app/views/people/show.rhtml +8 -0
  132. data/test/app/views/photos/edit.rhtml +17 -0
  133. data/test/app/views/photos/index.rhtml +20 -0
  134. data/test/app/views/photos/new.rhtml +16 -0
  135. data/test/app/views/photos/show.rhtml +8 -0
  136. data/test/app/views/posts/edit.rhtml +22 -0
  137. data/test/app/views/posts/index.rhtml +22 -0
  138. data/test/app/views/posts/new.rhtml +21 -0
  139. data/test/app/views/posts/show.rhtml +13 -0
  140. data/test/app/views/projects/edit.rhtml +17 -0
  141. data/test/app/views/projects/index.rhtml +20 -0
  142. data/test/app/views/projects/new.rhtml +16 -0
  143. data/test/app/views/projects/show.rhtml +8 -0
  144. data/test/app/views/somethings/edit.rhtml +17 -0
  145. data/test/app/views/somethings/index.rhtml +20 -0
  146. data/test/app/views/somethings/new.rhtml +16 -0
  147. data/test/app/views/somethings/show.rhtml +8 -0
  148. data/test/app/views/tags/edit.rhtml +17 -0
  149. data/test/app/views/tags/index.rhtml +20 -0
  150. data/test/app/views/tags/index.rjs +0 -0
  151. data/test/app/views/tags/new.rhtml +16 -0
  152. data/test/app/views/tags/show.rhtml +8 -0
  153. data/test/app/views/users/edit.rhtml +17 -0
  154. data/test/app/views/users/index.rhtml +20 -0
  155. data/test/app/views/users/new.rhtml +16 -0
  156. data/test/app/views/users/show.rhtml +8 -0
  157. data/test/config/boot.rb +109 -0
  158. data/test/config/database.yml +13 -0
  159. data/test/config/environment.rb +64 -0
  160. data/test/config/environments/development.rb +21 -0
  161. data/test/config/environments/test.rb +19 -0
  162. data/test/config/routes.rb +58 -0
  163. data/test/db/migrate/001_create_posts.rb +12 -0
  164. data/test/db/migrate/002_create_products.rb +11 -0
  165. data/test/db/migrate/003_create_comments.rb +13 -0
  166. data/test/db/migrate/004_create_options.rb +13 -0
  167. data/test/db/migrate/005_create_photos.rb +11 -0
  168. data/test/db/migrate/006_create_tags.rb +17 -0
  169. data/test/db/migrate/007_create_somethings.rb +11 -0
  170. data/test/db/migrate/008_create_accounts.rb +11 -0
  171. data/test/db/migrate/009_add_account_id_to_photos.rb +9 -0
  172. data/test/db/migrate/010_create_projects.rb +11 -0
  173. data/test/db/migrate/011_create_images.rb +12 -0
  174. data/test/db/migrate/012_create_users.rb +11 -0
  175. data/test/db/schema.rb +72 -0
  176. data/test/script/console +3 -0
  177. data/test/script/destroy +3 -0
  178. data/test/script/generate +3 -0
  179. data/test/script/server +3 -0
  180. data/test/test/fixtures/accounts.yml +7 -0
  181. data/test/test/fixtures/comments.yml +11 -0
  182. data/test/test/fixtures/images.yml +6 -0
  183. data/test/test/fixtures/options.yml +9 -0
  184. data/test/test/fixtures/photos.yml +9 -0
  185. data/test/test/fixtures/photos_tags.yml +3 -0
  186. data/test/test/fixtures/posts.yml +9 -0
  187. data/test/test/fixtures/products.yml +7 -0
  188. data/test/test/fixtures/projects.yml +7 -0
  189. data/test/test/fixtures/somethings.yml +7 -0
  190. data/test/test/fixtures/tags.yml +7 -0
  191. data/test/test/fixtures/users.yml +5 -0
  192. data/test/test/functional/cms/options_controller_test.rb +23 -0
  193. data/test/test/functional/cms/products_controller_test.rb +23 -0
  194. data/test/test/functional/comments_controller_test.rb +26 -0
  195. data/test/test/functional/images_controller_test.rb +37 -0
  196. data/test/test/functional/people_controller_test.rb +34 -0
  197. data/test/test/functional/photos_controller_test.rb +130 -0
  198. data/test/test/functional/posts_controller_test.rb +34 -0
  199. data/test/test/functional/projects_controller_test.rb +18 -0
  200. data/test/test/functional/somethings_controller_test.rb +28 -0
  201. data/test/test/functional/tags_controller_test.rb +64 -0
  202. data/test/test/functional/users_controller_test.rb +24 -0
  203. data/test/test/test_helper.rb +12 -0
  204. data/test/test/unit/accessors_test.rb +110 -0
  205. data/test/test/unit/account_test.rb +7 -0
  206. data/test/test/unit/action_options_test.rb +109 -0
  207. data/test/test/unit/base_test.rb +11 -0
  208. data/test/test/unit/comment_test.rb +10 -0
  209. data/test/test/unit/failable_action_options_test.rb +77 -0
  210. data/test/test/unit/helpers/current_objects_test.rb +133 -0
  211. data/test/test/unit/helpers/internal_test.rb +106 -0
  212. data/test/test/unit/helpers/nested_test.rb +86 -0
  213. data/test/test/unit/helpers/singleton_current_objects_test.rb +68 -0
  214. data/test/test/unit/helpers/singleton_nested_test.rb +77 -0
  215. data/test/test/unit/helpers/singleton_urls_test.rb +67 -0
  216. data/test/test/unit/helpers/urls_test.rb +75 -0
  217. data/test/test/unit/helpers_test.rb +25 -0
  218. data/test/test/unit/image_test.rb +7 -0
  219. data/test/test/unit/option_test.rb +10 -0
  220. data/test/test/unit/photo_test.rb +10 -0
  221. data/test/test/unit/post_test.rb +10 -0
  222. data/test/test/unit/project_test.rb +10 -0
  223. data/test/test/unit/response_collector_test.rb +49 -0
  224. data/test/test/unit/something_test.rb +10 -0
  225. data/test/test/unit/tag_test.rb +10 -0
  226. data/test/test/unit/urligence_test.rb +203 -0
  227. data/test/vendor/plugins/shoulda/Rakefile +32 -0
  228. data/test/vendor/plugins/shoulda/bin/convert_to_should_syntax +40 -0
  229. data/test/vendor/plugins/shoulda/init.rb +3 -0
  230. data/test/vendor/plugins/shoulda/lib/shoulda/active_record_helpers.rb +580 -0
  231. data/test/vendor/plugins/shoulda/lib/shoulda/color.rb +77 -0
  232. data/test/vendor/plugins/shoulda/lib/shoulda/controller_tests/controller_tests.rb +467 -0
  233. data/test/vendor/plugins/shoulda/lib/shoulda/controller_tests/formats/html.rb +201 -0
  234. data/test/vendor/plugins/shoulda/lib/shoulda/controller_tests/formats/xml.rb +170 -0
  235. data/test/vendor/plugins/shoulda/lib/shoulda/gem/proc_extensions.rb +14 -0
  236. data/test/vendor/plugins/shoulda/lib/shoulda/gem/shoulda.rb +239 -0
  237. data/test/vendor/plugins/shoulda/lib/shoulda/general.rb +118 -0
  238. data/test/vendor/plugins/shoulda/lib/shoulda/private_helpers.rb +22 -0
  239. data/test/vendor/plugins/shoulda/lib/shoulda.rb +43 -0
  240. metadata +342 -0
@@ -0,0 +1,170 @@
1
+ module ThoughtBot # :nodoc:
2
+ module Shoulda # :nodoc:
3
+ module Controller # :nodoc:
4
+ module XML
5
+ def self.included(other) #:nodoc:
6
+ other.class_eval do
7
+ extend ThoughtBot::Shoulda::Controller::XML::ClassMethods
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ # Macro that creates a test asserting that the controller responded with an XML content-type
13
+ # and that the XML contains +<name/>+ as the root element.
14
+ def should_respond_with_xml_for(name = nil)
15
+ should "have ContentType set to 'application/xml'" do
16
+ assert_xml_response
17
+ end
18
+
19
+ if name
20
+ should "return <#{name}/> as the root element" do
21
+ body = @response.body.first(100).map {|l| " #{l}"}
22
+ assert_select name.to_s.dasherize, 1, "Body:\n#{body}...\nDoes not have <#{name}/> as the root element."
23
+ end
24
+ end
25
+ end
26
+ alias should_respond_with_xml should_respond_with_xml_for
27
+
28
+ protected
29
+
30
+ def make_show_xml_tests(res) # :nodoc:
31
+ context "on GET to #{controller_name_from_class}#show as xml" do
32
+ setup do
33
+ request_xml
34
+ record = get_existing_record(res)
35
+ parent_params = make_parent_params(res, record)
36
+ get :show, parent_params.merge({ res.identifier => record.to_param })
37
+ end
38
+
39
+ if res.denied.actions.include?(:show)
40
+ should_not_assign_to res.object
41
+ should_respond_with 401
42
+ else
43
+ should_assign_to res.object
44
+ should_respond_with :success
45
+ should_respond_with_xml_for res.object
46
+ end
47
+ end
48
+ end
49
+
50
+ def make_edit_xml_tests(res) # :nodoc:
51
+ # XML doesn't need an :edit action
52
+ end
53
+
54
+ def make_new_xml_tests(res) # :nodoc:
55
+ # XML doesn't need a :new action
56
+ end
57
+
58
+ def make_index_xml_tests(res) # :nodoc:
59
+ context "on GET to #{controller_name_from_class}#index as xml" do
60
+ setup do
61
+ request_xml
62
+ parent_params = make_parent_params(res)
63
+ get(:index, parent_params)
64
+ end
65
+
66
+ if res.denied.actions.include?(:index)
67
+ should_not_assign_to res.object.to_s.pluralize
68
+ should_respond_with 401
69
+ else
70
+ should_respond_with :success
71
+ should_respond_with_xml_for res.object.to_s.pluralize
72
+ should_assign_to res.object.to_s.pluralize
73
+ end
74
+ end
75
+ end
76
+
77
+ def make_destroy_xml_tests(res) # :nodoc:
78
+ context "on DELETE to #{controller_name_from_class}#destroy as xml" do
79
+ setup do
80
+ request_xml
81
+ @record = get_existing_record(res)
82
+ parent_params = make_parent_params(res, @record)
83
+ delete :destroy, parent_params.merge({ res.identifier => @record.to_param })
84
+ end
85
+
86
+ if res.denied.actions.include?(:destroy)
87
+ should_respond_with 401
88
+
89
+ should "not destroy record" do
90
+ assert @record.reload
91
+ end
92
+ else
93
+ should "destroy record" do
94
+ assert_raises(::ActiveRecord::RecordNotFound, "@#{res.object} was not destroyed.") do
95
+ @record.reload
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ def make_create_xml_tests(res) # :nodoc:
103
+ context "on POST to #{controller_name_from_class}#create as xml" do
104
+ setup do
105
+ request_xml
106
+ parent_params = make_parent_params(res)
107
+ @count = res.klass.count
108
+ post :create, parent_params.merge(res.object => res.create.params)
109
+ end
110
+
111
+ if res.denied.actions.include?(:create)
112
+ should_respond_with 401
113
+ should_not_assign_to res.object
114
+
115
+ should "not create new record" do
116
+ assert_equal @count, res.klass.count
117
+ end
118
+ else
119
+ should_assign_to res.object
120
+
121
+ should "not have errors on @#{res.object}" do
122
+ assert_equal [], pretty_error_messages(assigns(res.object)), "@#{res.object} has errors:"
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ def make_update_xml_tests(res) # :nodoc:
129
+ context "on PUT to #{controller_name_from_class}#update as xml" do
130
+ setup do
131
+ request_xml
132
+ @record = get_existing_record(res)
133
+ parent_params = make_parent_params(res, @record)
134
+ put :update, parent_params.merge(res.identifier => @record.to_param, res.object => res.update.params)
135
+ end
136
+
137
+ if res.denied.actions.include?(:update)
138
+ should_not_assign_to res.object
139
+ should_respond_with 401
140
+ else
141
+ should_assign_to res.object
142
+
143
+ should "not have errors on @#{res.object}" do
144
+ assert_equal [], assigns(res.object).errors.full_messages, "@#{res.object} has errors:"
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ # Sets the next request's format to 'application/xml'
152
+ def request_xml
153
+ @request.accept = "application/xml"
154
+ end
155
+
156
+ # Asserts that the controller's response was 'application/xml'
157
+ def assert_xml_response
158
+ content_type = (@response.headers["Content-Type"] || @response.headers["type"]).to_s
159
+ regex = %r{\bapplication/xml\b}
160
+
161
+ msg = "Content Type '#{content_type.inspect}' doesn't match '#{regex.inspect}'\n"
162
+ msg += "Body: #{@response.body.first(100).chomp} ..."
163
+
164
+ assert_match regex, content_type, msg
165
+ end
166
+
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,14 @@
1
+ # Stolen straight from ActiveSupport
2
+
3
+ class Proc #:nodoc:
4
+ def bind(object)
5
+ block, time = self, Time.now
6
+ (class << object; self end).class_eval do
7
+ method_name = "__bind_#{time.to_i}_#{time.usec}"
8
+ define_method(method_name, &block)
9
+ method = instance_method(method_name)
10
+ remove_method(method_name)
11
+ method
12
+ end.bind(object)
13
+ end
14
+ end
@@ -0,0 +1,239 @@
1
+ require File.join(File.dirname(__FILE__), 'proc_extensions')
2
+
3
+ module Thoughtbot
4
+ module Shoulda
5
+ class << self
6
+ attr_accessor :current_context
7
+ end
8
+
9
+ VERSION = '1.1.1'
10
+
11
+ # = Should statements
12
+ #
13
+ # Should statements are just syntactic sugar over normal Test::Unit test methods. A should block
14
+ # contains all the normal code and assertions you're used to seeing, with the added benefit that
15
+ # they can be wrapped inside context blocks (see below).
16
+ #
17
+ # == Example:
18
+ #
19
+ # class UserTest << Test::Unit::TestCase
20
+ #
21
+ # def setup
22
+ # @user = User.new("John", "Doe")
23
+ # end
24
+ #
25
+ # should "return its full name"
26
+ # assert_equal 'John Doe', @user.full_name
27
+ # end
28
+ #
29
+ # end
30
+ #
31
+ # ...will produce the following test:
32
+ # * <tt>"test: User should return its full name. "</tt>
33
+ #
34
+ # Note: The part before <tt>should</tt> in the test name is gleamed from the name of the Test::Unit class.
35
+
36
+ def should(name, &blk)
37
+ if Shoulda.current_context
38
+ Shoulda.current_context.should(name, &blk)
39
+ else
40
+ context_name = self.name.gsub(/Test/, "")
41
+ context = Thoughtbot::Shoulda::Context.new(context_name, self) do
42
+ should(name, &blk)
43
+ end
44
+ context.build
45
+ end
46
+ end
47
+
48
+ # Just like should, but never runs, and instead prints an 'X' in the Test::Unit output.
49
+ def should_eventually(name, &blk)
50
+ context_name = self.name.gsub(/Test/, "")
51
+ context = Thoughtbot::Shoulda::Context.new(context_name, self) do
52
+ should_eventually(name, &blk)
53
+ end
54
+ context.build
55
+ end
56
+
57
+ # = Contexts
58
+ #
59
+ # A context block groups should statements under a common set of setup/teardown methods.
60
+ # Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability
61
+ # and readability of your test code.
62
+ #
63
+ # A context block can contain setup, should, should_eventually, and teardown blocks.
64
+ #
65
+ # class UserTest << Test::Unit::TestCase
66
+ # context "A User instance" do
67
+ # setup do
68
+ # @user = User.find(:first)
69
+ # end
70
+ #
71
+ # should "return its full name"
72
+ # assert_equal 'John Doe', @user.full_name
73
+ # end
74
+ # end
75
+ # end
76
+ #
77
+ # This code will produce the method <tt>"test: A User instance should return its full name. "</tt>.
78
+ #
79
+ # Contexts may be nested. Nested contexts run their setup blocks from out to in before each
80
+ # should statement. They then run their teardown blocks from in to out after each should statement.
81
+ #
82
+ # class UserTest << Test::Unit::TestCase
83
+ # context "A User instance" do
84
+ # setup do
85
+ # @user = User.find(:first)
86
+ # end
87
+ #
88
+ # should "return its full name"
89
+ # assert_equal 'John Doe', @user.full_name
90
+ # end
91
+ #
92
+ # context "with a profile" do
93
+ # setup do
94
+ # @user.profile = Profile.find(:first)
95
+ # end
96
+ #
97
+ # should "return true when sent :has_profile?"
98
+ # assert @user.has_profile?
99
+ # end
100
+ # end
101
+ # end
102
+ # end
103
+ #
104
+ # This code will produce the following methods
105
+ # * <tt>"test: A User instance should return its full name. "</tt>
106
+ # * <tt>"test: A User instance with a profile should return true when sent :has_profile?. "</tt>
107
+ #
108
+ # <b>Just like should statements, a context block can exist next to normal <tt>def test_the_old_way; end</tt>
109
+ # tests</b>. This means you do not have to fully commit to the context/should syntax in a test file.
110
+
111
+ def context(name, &blk)
112
+ if Shoulda.current_context
113
+ Shoulda.current_context.context(name, &blk)
114
+ else
115
+ context = Thoughtbot::Shoulda::Context.new(name, self, &blk)
116
+ context.build
117
+ end
118
+ end
119
+
120
+ class Context # :nodoc:
121
+
122
+ attr_accessor :name # my name
123
+ attr_accessor :parent # may be another context, or the original test::unit class.
124
+ attr_accessor :subcontexts # array of contexts nested under myself
125
+ attr_accessor :setup_block # block given via a setup method
126
+ attr_accessor :teardown_block # block given via a teardown method
127
+ attr_accessor :shoulds # array of hashes representing the should statements
128
+ attr_accessor :should_eventuallys # array of hashes representing the should eventually statements
129
+
130
+ def initialize(name, parent, &blk)
131
+ Shoulda.current_context = self
132
+ self.name = name
133
+ self.parent = parent
134
+ self.setup_block = nil
135
+ self.teardown_block = nil
136
+ self.shoulds = []
137
+ self.should_eventuallys = []
138
+ self.subcontexts = []
139
+
140
+ blk.bind(self).call
141
+ Shoulda.current_context = nil
142
+ end
143
+
144
+ def context(name, &blk)
145
+ subcontexts << Context.new(name, self, &blk)
146
+ Shoulda.current_context = self
147
+ end
148
+
149
+ def setup(&blk)
150
+ self.setup_block = blk
151
+ end
152
+
153
+ def teardown(&blk)
154
+ self.teardown_block = blk
155
+ end
156
+
157
+ def should(name, &blk)
158
+ self.shoulds << { :name => name, :block => blk }
159
+ end
160
+
161
+ def should_eventually(name, &blk)
162
+ self.should_eventuallys << { :name => name, :block => blk }
163
+ end
164
+
165
+ def full_name
166
+ parent_name = parent.full_name if am_subcontext?
167
+ return [parent_name, name].join(" ").strip
168
+ end
169
+
170
+ def am_subcontext?
171
+ parent.is_a?(self.class) # my parent is the same class as myself.
172
+ end
173
+
174
+ def test_unit_class
175
+ am_subcontext? ? parent.test_unit_class : parent
176
+ end
177
+
178
+ def create_test_from_should_hash(should)
179
+ test_name = ["test:", full_name, "should", "#{should[:name]}. "].flatten.join(' ').to_sym
180
+
181
+ if test_unit_class.instance_methods.include?(test_name.to_s)
182
+ warn " * WARNING: '#{test_name}' is already defined"
183
+ end
184
+
185
+ context = self
186
+ test_unit_class.send(:define_method, test_name) do |*args|
187
+ begin
188
+ context.run_all_setup_blocks(self)
189
+ should[:block].bind(self).call
190
+ ensure
191
+ context.run_all_teardown_blocks(self)
192
+ end
193
+ end
194
+ end
195
+
196
+ def run_all_setup_blocks(binding)
197
+ self.parent.run_all_setup_blocks(binding) if am_subcontext?
198
+ setup_block.bind(binding).call if setup_block
199
+ end
200
+
201
+ def run_all_teardown_blocks(binding)
202
+ teardown_block.bind(binding).call if teardown_block
203
+ self.parent.run_all_teardown_blocks(binding) if am_subcontext?
204
+ end
205
+
206
+ def print_should_eventuallys
207
+ should_eventuallys.each do |should|
208
+ test_name = [full_name, "should", "#{should[:name]}. "].flatten.join(' ')
209
+ puts " * DEFERRED: " + test_name
210
+ end
211
+ subcontexts.each { |context| context.print_should_eventuallys }
212
+ end
213
+
214
+ def build
215
+ shoulds.each do |should|
216
+ create_test_from_should_hash(should)
217
+ end
218
+
219
+ subcontexts.each { |context| context.build }
220
+
221
+ print_should_eventuallys
222
+ end
223
+
224
+ def method_missing(method, *args, &blk)
225
+ test_unit_class.send(method, *args, &blk)
226
+ end
227
+
228
+ end
229
+ end
230
+ end
231
+
232
+ module Test # :nodoc: all
233
+ module Unit
234
+ class TestCase
235
+ extend Thoughtbot::Shoulda
236
+ end
237
+ end
238
+ end
239
+
@@ -0,0 +1,118 @@
1
+ module ThoughtBot # :nodoc:
2
+ module Shoulda # :nodoc:
3
+ module General
4
+ def self.included(other) # :nodoc:
5
+ other.class_eval do
6
+ extend ThoughtBot::Shoulda::General::ClassMethods
7
+ end
8
+ end
9
+
10
+ module ClassMethods
11
+ # Loads all fixture files (<tt>test/fixtures/*.yml</tt>)
12
+ def load_all_fixtures
13
+ all_fixtures = Dir.glob(File.join(Test::Unit::TestCase.fixture_path, "*.yml")).collect do |f|
14
+ File.basename(f, '.yml').to_sym
15
+ end
16
+ fixtures *all_fixtures
17
+ end
18
+ end
19
+
20
+ # Prints a message to stdout, tagged with the name of the calling method.
21
+ def report!(msg = "")
22
+ puts("#{caller.first}: #{msg}")
23
+ end
24
+
25
+ # Asserts that two arrays contain the same elements, the same number of times. Essentially ==, but unordered.
26
+ #
27
+ # assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
28
+ def assert_same_elements(a1, a2, msg = nil)
29
+ [:select, :inject, :size].each do |m|
30
+ [a1, a2].each {|a| assert_respond_to(a, m, "Are you sure that #{a.inspect} is an array? It doesn't respond to #{m}.") }
31
+ end
32
+
33
+ assert a1h = a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h }
34
+ assert a2h = a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
35
+
36
+ assert_equal(a1h, a2h, msg)
37
+ end
38
+
39
+ # Asserts that the given collection contains item x. If x is a regular expression, ensure that
40
+ # at least one element from the collection matches x. +extra_msg+ is appended to the error message if the assertion fails.
41
+ #
42
+ # assert_contains(['a', '1'], /\d/) => passes
43
+ # assert_contains(['a', '1'], 'a') => passes
44
+ # assert_contains(['a', '1'], /not there/) => fails
45
+ def assert_contains(collection, x, extra_msg = "")
46
+ collection = [collection] unless collection.is_a?(Array)
47
+ msg = "#{x.inspect} not found in #{collection.to_a.inspect} #{extra_msg}"
48
+ case x
49
+ when Regexp: assert(collection.detect { |e| e =~ x }, msg)
50
+ else assert(collection.include?(x), msg)
51
+ end
52
+ end
53
+
54
+ # Asserts that the given collection does not contain item x. If x is a regular expression, ensure that
55
+ # none of the elements from the collection match x.
56
+ def assert_does_not_contain(collection, x, extra_msg = "")
57
+ collection = [collection] unless collection.is_a?(Array)
58
+ msg = "#{x.inspect} found in #{collection.to_a.inspect} " + extra_msg
59
+ case x
60
+ when Regexp: assert(!collection.detect { |e| e =~ x }, msg)
61
+ else assert(!collection.include?(x), msg)
62
+ end
63
+ end
64
+
65
+ # Asserts that the given object can be saved
66
+ #
67
+ # assert_save User.new(params)
68
+ def assert_save(obj)
69
+ assert obj.save, "Errors: #{pretty_error_messages obj}"
70
+ obj.reload
71
+ end
72
+
73
+ # Asserts that the given object is valid
74
+ #
75
+ # assert_valid User.new(params)
76
+ def assert_valid(obj)
77
+ assert obj.valid?, "Errors: #{pretty_error_messages obj}"
78
+ end
79
+
80
+ # Asserts that an email was delivered. Can take a block that can further
81
+ # narrow down the types of emails you're expecting.
82
+ #
83
+ # assert_sent_email
84
+ #
85
+ # Passes if ActionMailer::Base.deliveries has an email
86
+ #
87
+ # assert_sent_email do |email|
88
+ # email.subject =~ /hi there/ && email.to.include?('none@none.com')
89
+ # end
90
+ #
91
+ # Passes if there is an email with subject containing 'hi there' and
92
+ # 'none@none.com' as one of the recipients.
93
+ #
94
+ def assert_sent_email
95
+ emails = ActionMailer::Base.deliveries
96
+ assert !emails.empty?, "No emails were sent"
97
+ if block_given?
98
+ matching_emails = emails.select {|email| yield email }
99
+ assert !matching_emails.empty?, "None of the emails matched."
100
+ end
101
+ end
102
+
103
+ # Asserts that no ActionMailer mails were delivered
104
+ #
105
+ # assert_did_not_send_email
106
+ def assert_did_not_send_email
107
+ msg = "Sent #{ActionMailer::Base.deliveries.size} emails.\n"
108
+ ActionMailer::Base.deliveries.each { |m| msg << " '#{m.subject}' sent to #{m.to.to_sentence}\n" }
109
+ assert ActionMailer::Base.deliveries.empty?, msg
110
+ end
111
+
112
+ def pretty_error_messages(obj)
113
+ obj.errors.map { |a, m| "#{a} #{m} (#{obj.send(a).inspect})" }
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,22 @@
1
+ module ThoughtBot # :nodoc:
2
+ module Shoulda # :nodoc:
3
+ module Private # :nodoc:
4
+ # Returns the values for the entries in the args hash who's keys are listed in the wanted array.
5
+ # Will raise if there are keys in the args hash that aren't listed.
6
+ def get_options!(args, *wanted)
7
+ ret = []
8
+ opts = (args.last.is_a?(Hash) ? args.pop : {})
9
+ wanted.each {|w| ret << opts.delete(w)}
10
+ raise ArgumentError, "Unsupported options given: #{opts.keys.join(', ')}" unless opts.keys.empty?
11
+ return *ret
12
+ end
13
+
14
+ # Returns the model class constant, as determined by the test class name.
15
+ #
16
+ # class TestUser; model_class; end => User
17
+ def model_class
18
+ self.name.gsub(/Test$/, '').constantize
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ require 'shoulda/gem/shoulda'
2
+ require 'shoulda/private_helpers'
3
+ require 'shoulda/general'
4
+ require 'shoulda/active_record_helpers'
5
+ require 'shoulda/controller_tests/controller_tests.rb'
6
+ require 'yaml'
7
+
8
+ shoulda_options = {}
9
+
10
+ possible_config_paths = []
11
+ possible_config_paths << File.join(ENV["HOME"], ".shoulda.conf") if ENV["HOME"]
12
+ possible_config_paths << "shoulda.conf"
13
+ possible_config_paths << File.join("test", "shoulda.conf")
14
+ possible_config_paths << File.join(RAILS_ROOT, "test", "shoulda.conf") if defined?(RAILS_ROOT)
15
+
16
+ possible_config_paths.each do |config_file|
17
+ if File.exists? config_file
18
+ shoulda_options = YAML.load_file(config_file).symbolize_keys
19
+ break
20
+ end
21
+ end
22
+
23
+ require 'shoulda/color' if shoulda_options[:color]
24
+
25
+ module Test # :nodoc: all
26
+ module Unit
27
+ class TestCase
28
+
29
+ include ThoughtBot::Shoulda::General
30
+ include ThoughtBot::Shoulda::Controller
31
+
32
+ extend ThoughtBot::Shoulda::ActiveRecord
33
+ end
34
+ end
35
+ end
36
+
37
+ module ActionController #:nodoc: all
38
+ module Integration
39
+ class Session
40
+ include ThoughtBot::Shoulda::General
41
+ end
42
+ end
43
+ end