mcmire-rspec-rails 1.1.99.9

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 (164) hide show
  1. data/History.txt +174 -0
  2. data/License.txt +33 -0
  3. data/Manifest.txt +164 -0
  4. data/README.txt +45 -0
  5. data/Rakefile +63 -0
  6. data/TODO.txt +1 -0
  7. data/Upgrade.markdown +52 -0
  8. data/features/step_definitions/people.rb +6 -0
  9. data/features/support/env.rb +13 -0
  10. data/features/transactions/transactions_should_rollback.feature +16 -0
  11. data/generators/rspec/CHANGES +1 -0
  12. data/generators/rspec/rspec_generator.rb +37 -0
  13. data/generators/rspec/templates/previous_failures.txt +0 -0
  14. data/generators/rspec/templates/rcov.opts +2 -0
  15. data/generators/rspec/templates/rspec.rake +177 -0
  16. data/generators/rspec/templates/script/autospec +5 -0
  17. data/generators/rspec/templates/script/spec +4 -0
  18. data/generators/rspec/templates/script/spec_server +37 -0
  19. data/generators/rspec/templates/spec.opts +4 -0
  20. data/generators/rspec/templates/spec_helper.rb +47 -0
  21. data/generators/rspec_controller/USAGE +33 -0
  22. data/generators/rspec_controller/rspec_controller_generator.rb +45 -0
  23. data/generators/rspec_controller/templates/controller_spec.rb +25 -0
  24. data/generators/rspec_controller/templates/helper_spec.rb +11 -0
  25. data/generators/rspec_controller/templates/view_spec.rb +12 -0
  26. data/generators/rspec_default_values.rb +19 -0
  27. data/generators/rspec_model/USAGE +18 -0
  28. data/generators/rspec_model/rspec_model_generator.rb +35 -0
  29. data/generators/rspec_model/templates/model_spec.rb +15 -0
  30. data/generators/rspec_scaffold/rspec_scaffold_generator.rb +150 -0
  31. data/generators/rspec_scaffold/templates/controller_spec.rb +171 -0
  32. data/generators/rspec_scaffold/templates/edit_erb_spec.rb +27 -0
  33. data/generators/rspec_scaffold/templates/helper_spec.rb +11 -0
  34. data/generators/rspec_scaffold/templates/index_erb_spec.rb +28 -0
  35. data/generators/rspec_scaffold/templates/new_erb_spec.rb +27 -0
  36. data/generators/rspec_scaffold/templates/routing_spec.rb +63 -0
  37. data/generators/rspec_scaffold/templates/show_erb_spec.rb +23 -0
  38. data/init.rb +9 -0
  39. data/lib/autotest/discover.rb +1 -0
  40. data/lib/autotest/rails_rspec.rb +76 -0
  41. data/lib/spec/rails.rb +37 -0
  42. data/lib/spec/rails/example.rb +53 -0
  43. data/lib/spec/rails/example/assigns_hash_proxy.rb +39 -0
  44. data/lib/spec/rails/example/controller_example_group.rb +242 -0
  45. data/lib/spec/rails/example/cookies_proxy.rb +29 -0
  46. data/lib/spec/rails/example/functional_example_group.rb +84 -0
  47. data/lib/spec/rails/example/helper_example_group.rb +167 -0
  48. data/lib/spec/rails/example/model_example_group.rb +14 -0
  49. data/lib/spec/rails/example/render_observer.rb +67 -0
  50. data/lib/spec/rails/example/routing_helpers.rb +68 -0
  51. data/lib/spec/rails/example/view_example_group.rb +186 -0
  52. data/lib/spec/rails/extensions.rb +15 -0
  53. data/lib/spec/rails/extensions/action_controller/rescue.rb +42 -0
  54. data/lib/spec/rails/extensions/action_controller/test_case.rb +16 -0
  55. data/lib/spec/rails/extensions/action_controller/test_response.rb +21 -0
  56. data/lib/spec/rails/extensions/action_view/base.rb +33 -0
  57. data/lib/spec/rails/extensions/active_record/base.rb +43 -0
  58. data/lib/spec/rails/extensions/active_support/test_case.rb +7 -0
  59. data/lib/spec/rails/extensions/spec/matchers/have.rb +23 -0
  60. data/lib/spec/rails/extensions/spec/runner/configuration.rb +48 -0
  61. data/lib/spec/rails/interop/testcase.rb +14 -0
  62. data/lib/spec/rails/matchers.rb +40 -0
  63. data/lib/spec/rails/matchers/ar_be_valid.rb +44 -0
  64. data/lib/spec/rails/matchers/assert_select.rb +131 -0
  65. data/lib/spec/rails/matchers/change.rb +11 -0
  66. data/lib/spec/rails/matchers/have_text.rb +57 -0
  67. data/lib/spec/rails/matchers/include_text.rb +54 -0
  68. data/lib/spec/rails/matchers/redirect_to.rb +126 -0
  69. data/lib/spec/rails/matchers/render_template.rb +114 -0
  70. data/lib/spec/rails/mocks.rb +135 -0
  71. data/lib/spec/rails/spec_server.rb +97 -0
  72. data/lib/spec/rails/story_adapter.rb +79 -0
  73. data/lib/spec/rails/version.rb +16 -0
  74. data/rspec-rails.gemspec +39 -0
  75. data/spec/autotest/mappings_spec.rb +36 -0
  76. data/spec/rails_suite.rb +7 -0
  77. data/spec/resources/controllers/action_view_base_spec_controller.rb +2 -0
  78. data/spec/resources/controllers/application.rb +9 -0
  79. data/spec/resources/controllers/controller_spec_controller.rb +116 -0
  80. data/spec/resources/controllers/redirect_spec_controller.rb +70 -0
  81. data/spec/resources/controllers/render_spec_controller.rb +30 -0
  82. data/spec/resources/controllers/rjs_spec_controller.rb +58 -0
  83. data/spec/resources/helpers/addition_helper.rb +5 -0
  84. data/spec/resources/helpers/explicit_helper.rb +46 -0
  85. data/spec/resources/helpers/more_explicit_helper.rb +5 -0
  86. data/spec/resources/helpers/plugin_application_helper.rb +6 -0
  87. data/spec/resources/helpers/view_spec_helper.rb +13 -0
  88. data/spec/resources/models/animal.rb +4 -0
  89. data/spec/resources/models/person.rb +18 -0
  90. data/spec/resources/models/thing.rb +3 -0
  91. data/spec/resources/views/controller_spec/_partial.rhtml +0 -0
  92. data/spec/resources/views/controller_spec/action_setting_flash_after_session_reset.rhtml +1 -0
  93. data/spec/resources/views/controller_spec/action_setting_flash_before_session_reset.rhtml +1 -0
  94. data/spec/resources/views/controller_spec/action_setting_the_assigns_hash.rhtml +0 -0
  95. data/spec/resources/views/controller_spec/action_with_errors_in_template.rhtml +1 -0
  96. data/spec/resources/views/controller_spec/action_with_template.rhtml +1 -0
  97. data/spec/resources/views/layouts/application.rhtml +0 -0
  98. data/spec/resources/views/layouts/simple.rhtml +0 -0
  99. data/spec/resources/views/objects/_object.html.erb +1 -0
  100. data/spec/resources/views/render_spec/_a_partial.rhtml +0 -0
  101. data/spec/resources/views/render_spec/action_with_alternate_layout.rhtml +0 -0
  102. data/spec/resources/views/render_spec/some_action.html.erb +0 -0
  103. data/spec/resources/views/render_spec/some_action.js.rjs +1 -0
  104. data/spec/resources/views/render_spec/some_action.rjs +1 -0
  105. data/spec/resources/views/rjs_spec/_replacement_partial.rhtml +1 -0
  106. data/spec/resources/views/rjs_spec/hide_div.rjs +1 -0
  107. data/spec/resources/views/rjs_spec/hide_page_element.rjs +1 -0
  108. data/spec/resources/views/rjs_spec/insert_html.rjs +1 -0
  109. data/spec/resources/views/rjs_spec/replace.rjs +1 -0
  110. data/spec/resources/views/rjs_spec/replace_html.rjs +1 -0
  111. data/spec/resources/views/rjs_spec/replace_html_with_partial.rjs +1 -0
  112. data/spec/resources/views/rjs_spec/visual_effect.rjs +1 -0
  113. data/spec/resources/views/rjs_spec/visual_toggle_effect.rjs +1 -0
  114. data/spec/resources/views/tag_spec/no_tags.rhtml +1 -0
  115. data/spec/resources/views/tag_spec/single_div_with_no_attributes.rhtml +1 -0
  116. data/spec/resources/views/tag_spec/single_div_with_one_attribute.rhtml +1 -0
  117. data/spec/resources/views/view_spec/_partial.rhtml +2 -0
  118. data/spec/resources/views/view_spec/_partial_used_twice.rhtml +0 -0
  119. data/spec/resources/views/view_spec/_partial_with_local_variable.rhtml +1 -0
  120. data/spec/resources/views/view_spec/_partial_with_sub_partial.rhtml +1 -0
  121. data/spec/resources/views/view_spec/_spacer.rhtml +1 -0
  122. data/spec/resources/views/view_spec/accessor.rhtml +5 -0
  123. data/spec/resources/views/view_spec/block_helper.rhtml +3 -0
  124. data/spec/resources/views/view_spec/entry_form.rhtml +2 -0
  125. data/spec/resources/views/view_spec/explicit_helper.rhtml +2 -0
  126. data/spec/resources/views/view_spec/foo/show.rhtml +1 -0
  127. data/spec/resources/views/view_spec/implicit_helper.rhtml +2 -0
  128. data/spec/resources/views/view_spec/multiple_helpers.rhtml +3 -0
  129. data/spec/resources/views/view_spec/path_params.html.erb +1 -0
  130. data/spec/resources/views/view_spec/should_not_receive.rhtml +3 -0
  131. data/spec/resources/views/view_spec/template_with_partial.rhtml +5 -0
  132. data/spec/resources/views/view_spec/template_with_partial_using_collection.rhtml +3 -0
  133. data/spec/resources/views/view_spec/template_with_partial_with_array.rhtml +1 -0
  134. data/spec/spec/rails/example/assigns_hash_proxy_spec.rb +109 -0
  135. data/spec/spec/rails/example/configuration_spec.rb +65 -0
  136. data/spec/spec/rails/example/controller_example_group_spec.rb +275 -0
  137. data/spec/spec/rails/example/controller_isolation_spec.rb +56 -0
  138. data/spec/spec/rails/example/cookies_proxy_spec.rb +87 -0
  139. data/spec/spec/rails/example/error_handling_spec.rb +90 -0
  140. data/spec/spec/rails/example/example_group_factory_spec.rb +112 -0
  141. data/spec/spec/rails/example/helper_example_group_spec.rb +206 -0
  142. data/spec/spec/rails/example/model_example_group_spec.rb +20 -0
  143. data/spec/spec/rails/example/test_unit_assertion_accessibility_spec.rb +33 -0
  144. data/spec/spec/rails/example/view_example_group_spec.rb +335 -0
  145. data/spec/spec/rails/extensions/action_view_base_spec.rb +48 -0
  146. data/spec/spec/rails/extensions/active_record_spec.rb +14 -0
  147. data/spec/spec/rails/interop/testcase_spec.rb +70 -0
  148. data/spec/spec/rails/matchers/ar_be_valid_spec.rb +45 -0
  149. data/spec/spec/rails/matchers/assert_select_spec.rb +811 -0
  150. data/spec/spec/rails/matchers/errors_on_spec.rb +25 -0
  151. data/spec/spec/rails/matchers/have_text_spec.rb +70 -0
  152. data/spec/spec/rails/matchers/include_text_spec.rb +62 -0
  153. data/spec/spec/rails/matchers/redirect_to_spec.rb +253 -0
  154. data/spec/spec/rails/matchers/render_template_spec.rb +183 -0
  155. data/spec/spec/rails/matchers/should_change_spec.rb +15 -0
  156. data/spec/spec/rails/mocks/ar_classes.rb +10 -0
  157. data/spec/spec/rails/mocks/mock_model_spec.rb +106 -0
  158. data/spec/spec/rails/mocks/stub_model_spec.rb +80 -0
  159. data/spec/spec/rails/sample_modified_fixture.rb +8 -0
  160. data/spec/spec/rails/sample_spec.rb +8 -0
  161. data/spec/spec/rails/spec_server_spec.rb +107 -0
  162. data/spec/spec/rails/spec_spec.rb +11 -0
  163. data/spec/spec_helper.rb +78 -0
  164. metadata +262 -0
@@ -0,0 +1,11 @@
1
+ module Spec
2
+ module Matchers
3
+ class Change
4
+ def evaluate_value_proc_with_ensured_evaluation_of_proxy
5
+ value = evaluate_value_proc_without_ensured_evaluation_of_proxy
6
+ ActiveRecord::Associations::AssociationProxy === value ? value.dup : value
7
+ end
8
+ alias_method_chain :evaluate_value_proc, :ensured_evaluation_of_proxy
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,57 @@
1
+ module Spec
2
+ module Rails
3
+ module Matchers
4
+
5
+ class HaveText #:nodoc:
6
+
7
+ def initialize(expected)
8
+ @expected = expected
9
+ end
10
+
11
+ def matches?(response_or_text)
12
+ @actual = response_or_text.respond_to?(:body) ? response_or_text.body : response_or_text
13
+ return actual =~ expected if Regexp === expected
14
+ return actual == expected unless Regexp === expected
15
+ end
16
+
17
+ def failure_message
18
+ "expected #{expected.inspect}, got #{actual.inspect}"
19
+ end
20
+
21
+ def negative_failure_message
22
+ "expected not to have text #{expected.inspect}"
23
+ end
24
+
25
+ def description
26
+ "have text #{expected.inspect}"
27
+ end
28
+
29
+ private
30
+ attr_reader :expected
31
+ attr_reader :actual
32
+
33
+ end
34
+
35
+ # :call-seq:
36
+ # response.should have_text(expected)
37
+ # response.should_not have_text(expected)
38
+ #
39
+ # Accepts a String or a Regexp, matching a String using ==
40
+ # and a Regexp using =~.
41
+ #
42
+ # If response_or_text has a #body, then that is used as to match against
43
+ # else it uses response_or_text
44
+ #
45
+ # Use this instead of <tt>response.should have_tag()</tt>
46
+ # when you want to match the whole string or whole body
47
+ #
48
+ # == Examples
49
+ #
50
+ # response.should have_text("This is the expected text")
51
+ def have_text(text)
52
+ HaveText.new(text)
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,54 @@
1
+ module Spec
2
+ module Rails
3
+ module Matchers
4
+
5
+ class IncludeText #:nodoc:
6
+
7
+ def initialize(expected)
8
+ @expected = expected
9
+ end
10
+
11
+ def matches?(response_or_text)
12
+ @actual = response_or_text.respond_to?(:body) ? response_or_text.body : response_or_text
13
+ return actual.include?(expected)
14
+ end
15
+
16
+ def failure_message
17
+ "expected to find #{expected.inspect} in #{actual.inspect}"
18
+ end
19
+
20
+ def negative_failure_message
21
+ "expected not to include text #{expected.inspect}"
22
+ end
23
+
24
+ def description
25
+ "include text #{expected.inspect}"
26
+ end
27
+
28
+ private
29
+ attr_reader :expected
30
+ attr_reader :actual
31
+
32
+ end
33
+
34
+
35
+ # :call-seq:
36
+ # response.should include_text(expected)
37
+ # response.should_not include_text(expected)
38
+ #
39
+ # Accepts a String, matching using include?
40
+ #
41
+ # Use this instead of <tt>response.should have_text()</tt>
42
+ # when you either don't know or don't care where on the page
43
+ # this text appears.
44
+ #
45
+ # == Examples
46
+ #
47
+ # response.should include_text("This text will be in the actual string")
48
+ def include_text(text)
49
+ IncludeText.new(text)
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,126 @@
1
+ module Spec
2
+ module Rails
3
+ module Matchers
4
+
5
+ class RedirectTo #:nodoc:
6
+
7
+ include ActionController::StatusCodes
8
+
9
+ def initialize(request, expected)
10
+ @expected = expected
11
+ @request = request
12
+ end
13
+
14
+ def matches?(response_or_controller)
15
+ response = response_or_controller.respond_to?(:response) ?
16
+ response_or_controller.response :
17
+ response_or_controller
18
+
19
+ @redirected = response.redirect?
20
+ @actual = response.redirect_url
21
+ return false unless @redirected
22
+
23
+ if @expected_status
24
+ @actual_status = interpret_status(response.code.to_i)
25
+ @status_matched = @expected_status == @actual_status
26
+ else
27
+ @status_matched = true
28
+ end
29
+
30
+ if @expected.instance_of? Hash
31
+ return false unless @actual =~ %r{^\w+://#{@request.host}}
32
+ return false unless actual_redirect_to_valid_route
33
+ return actual_hash == expected_hash && @status_matched
34
+ else
35
+ return @actual == expected_url && @status_matched
36
+ end
37
+ end
38
+
39
+ def actual_hash
40
+ hash_from_url @actual
41
+ end
42
+
43
+ def expected_hash
44
+ hash_from_url expected_url
45
+ end
46
+
47
+ def actual_redirect_to_valid_route
48
+ actual_hash
49
+ end
50
+
51
+ def hash_from_url(url)
52
+ query_hash(url).merge(path_hash(url)).with_indifferent_access
53
+ end
54
+
55
+ def path_hash(url)
56
+ path = url.sub(%r{^\w+://#{@request.host}(?::\d+)?}, "").split("?", 2)[0]
57
+ ActionController::Routing::Routes.recognize_path path, { :method => :get }
58
+ end
59
+
60
+ def query_hash(url)
61
+ query = url.split("?", 2)[1] || ""
62
+ Rack::Utils.parse_query(query)
63
+ end
64
+
65
+ def with(options)
66
+ @expected_status = interpret_status(options[:status])
67
+ self
68
+ end
69
+
70
+ def expected_url
71
+ case @expected
72
+ when Hash
73
+ return ActionController::UrlRewriter.new(@request, {}).rewrite(@expected)
74
+ when :back
75
+ return @request.env['HTTP_REFERER']
76
+ when %r{^\w+://.*}
77
+ return @expected
78
+ else
79
+ return "http://#{@request.host}" + (@expected.split('')[0] == '/' ? '' : '/') + @expected
80
+ end
81
+ end
82
+
83
+ def failure_message
84
+ if @redirected
85
+ if @status_matched
86
+ return %Q{expected redirect to #{@expected.inspect}, got redirect to #{@actual.inspect}}
87
+ else
88
+ return %Q{expected redirect to #{@expected.inspect} with status #{@expected_status}, got #{@actual_status}}
89
+ end
90
+ else
91
+ return %Q{expected redirect to #{@expected.inspect}, got no redirect}
92
+ end
93
+ end
94
+
95
+ def negative_failure_message
96
+ return %Q{expected not to be redirected to #{@expected.inspect}, but was} if @redirected
97
+ end
98
+
99
+ def description
100
+ "redirect to #{@expected.inspect}"
101
+ end
102
+ end
103
+
104
+ # :call-seq:
105
+ # response.should redirect_to(url)
106
+ # response.should redirect_to(:action => action_name)
107
+ # response.should redirect_to(:controller => controller_name, :action => action_name)
108
+ # response.should_not redirect_to(url)
109
+ # response.should_not redirect_to(:action => action_name)
110
+ # response.should_not redirect_to(:controller => controller_name, :action => action_name)
111
+ #
112
+ # Passes if the response is a redirect to the url, action or controller/action.
113
+ # Useful in controller specs (integration or isolation mode).
114
+ #
115
+ # == Examples
116
+ #
117
+ # response.should redirect_to("path/to/action")
118
+ # response.should redirect_to("http://test.host/path/to/action")
119
+ # response.should redirect_to(:action => 'list')
120
+ def redirect_to(opts)
121
+ RedirectTo.new(request, opts)
122
+ end
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,114 @@
1
+ module Spec
2
+ module Rails
3
+ module Matchers
4
+
5
+ class RenderTemplate #:nodoc:
6
+
7
+ def initialize(expected, controller)
8
+ @controller = controller
9
+ @expected = expected
10
+ end
11
+
12
+ def matches?(response_or_controller)
13
+ response = response_or_controller.respond_to?(:response) ?
14
+ response_or_controller.response :
15
+ response_or_controller
16
+
17
+ if response.respond_to?(:rendered_file)
18
+ @actual = response.rendered_file
19
+ elsif response.respond_to?(:rendered)
20
+ case template = response.rendered[:template]
21
+ when nil
22
+ unless response.rendered[:partials].empty?
23
+ @actual = path_and_file(response.rendered[:partials].keys.first).join("/_")
24
+ end
25
+ when ActionView::Template
26
+ @actual = template.path
27
+ when String
28
+ @actual = template
29
+ end
30
+ else
31
+ @actual = response.rendered_template.to_s
32
+ end
33
+ return false if @actual.blank?
34
+ given_controller_path, given_file = path_and_file(@actual)
35
+ expected_controller_path, expected_file = path_and_file(@expected)
36
+ given_controller_path == expected_controller_path && given_file.match(expected_file)
37
+ end
38
+
39
+ def failure_message
40
+ "expected #{@expected.inspect}, got #{@actual.inspect}"
41
+ end
42
+
43
+ def negative_failure_message
44
+ "expected not to render #{@expected.inspect}, but did"
45
+ end
46
+
47
+ def description
48
+ "render template #{@expected.inspect}"
49
+ end
50
+
51
+ private
52
+ def path_and_file(path)
53
+ parts = path.split('/')
54
+ file = parts.pop
55
+ controller = parts.empty? ? current_controller_path : parts.join('/')
56
+ return controller, file
57
+ end
58
+
59
+ def controller_path_from(path)
60
+ parts = path.split('/')
61
+ parts.pop
62
+ parts.join('/')
63
+ end
64
+
65
+ def current_controller_path
66
+ @controller.class.to_s.underscore.gsub(/_controller$/,'')
67
+ end
68
+
69
+ end
70
+
71
+ # :call-seq:
72
+ # response.should render_template(template)
73
+ # response.should_not render_template(template)
74
+ #
75
+ # For use in controller code examples (integration or isolation mode).
76
+ #
77
+ # Passes if the specified template (view file) is rendered by the
78
+ # response. This file can be any view file, including a partial. However
79
+ # if it is a partial it must be rendered directly i.e. you can't detect
80
+ # that a partial has been rendered as part of a view using
81
+ # render_template. For that you should use a message expectation
82
+ # (mock) instead:
83
+ #
84
+ # controller.should_receive(:render).with(:partial => 'path/to/partial')
85
+ #
86
+ # <code>template</code> can include the controller path. It can also
87
+ # include an optional extension, which you only need to use when there
88
+ # is ambiguity.
89
+ #
90
+ # Note that partials must be spelled with the preceding underscore.
91
+ #
92
+ # == Examples
93
+ #
94
+ # response.should render_template('list')
95
+ # response.should render_template('same_controller/list')
96
+ # response.should render_template('other_controller/list')
97
+ #
98
+ # # with extensions
99
+ # response.should render_template('list.rjs')
100
+ # response.should render_template('list.haml')
101
+ # response.should render_template('same_controller/list.rjs')
102
+ # response.should render_template('other_controller/list.rjs')
103
+ #
104
+ # # partials
105
+ # response.should render_template('_a_partial')
106
+ # response.should render_template('same_controller/_a_partial')
107
+ # response.should render_template('other_controller/_a_partial')
108
+ def render_template(path)
109
+ RenderTemplate.new(path.to_s, @controller)
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,135 @@
1
+ module Spec
2
+ module Rails
3
+
4
+ class IllegalDataAccessException < StandardError; end
5
+
6
+ module Mocks
7
+
8
+ # Creates a mock object instance for a +model_class+ with common
9
+ # methods stubbed out. Additional methods may be easily stubbed (via
10
+ # add_stubs) if +stubs+ is passed.
11
+ def mock_model(model_class, options_and_stubs = {})
12
+ id = options_and_stubs[:id] || next_id
13
+ options_and_stubs = options_and_stubs.reverse_merge({
14
+ :id => id,
15
+ :to_param => id.to_s,
16
+ :new_record? => false,
17
+ :errors => stub("errors", :count => 0)
18
+ })
19
+ m = mock("#{model_class.name}_#{id}", options_and_stubs)
20
+ m.__send__(:__mock_proxy).instance_eval <<-CODE
21
+ def @target.as_new_record
22
+ self.stub!(:id).and_return nil
23
+ self.stub!(:to_param).and_return nil
24
+ self.stub!(:new_record?).and_return true
25
+ self
26
+ end
27
+ def @target.is_a?(other)
28
+ #{model_class}.ancestors.include?(other)
29
+ end
30
+ def @target.kind_of?(other)
31
+ #{model_class}.ancestors.include?(other)
32
+ end
33
+ def @target.instance_of?(other)
34
+ other == #{model_class}
35
+ end
36
+ def @target.class
37
+ #{model_class}
38
+ end
39
+ CODE
40
+ yield m if block_given?
41
+ m
42
+ end
43
+
44
+ module ModelStubber
45
+ def connection
46
+ raise Spec::Rails::IllegalDataAccessException.new("stubbed models are not allowed to access the database")
47
+ end
48
+ def new_record?
49
+ id.nil?
50
+ end
51
+ def as_new_record
52
+ self.id = nil
53
+ self
54
+ end
55
+ end
56
+
57
+ # :call-seq:
58
+ # stub_model(Model)
59
+ # stub_model(Model).as_new_record
60
+ # stub_model(Model, hash_of_stubs)
61
+ # stub_model(Model, instance_variable_name, hash_of_stubs)
62
+ #
63
+ # Creates an instance of +Model+ that is prohibited from accessing the
64
+ # database*. For each key in +hash_of_stubs+, if the model has a
65
+ # matching attribute (determined by asking it) are simply assigned the
66
+ # submitted values. If the model does not have a matching attribute, the
67
+ # key/value pair is assigned as a stub return value using RSpec's
68
+ # mocking/stubbing framework.
69
+ #
70
+ # <tt>new_record?</tt> is overridden to return the result of id.nil?
71
+ # This means that by default new_record? will return false. If you want
72
+ # the object to behave as a new record, sending it +as_new_record+ will
73
+ # set the id to nil. You can also explicitly set :id => nil, in which
74
+ # case new_record? will return true, but using +as_new_record+ makes the
75
+ # example a bit more descriptive.
76
+ #
77
+ # While you can use stub_model in any example (model, view, controller,
78
+ # helper), it is especially useful in view examples, which are
79
+ # inherently more state-based than interaction-based.
80
+ #
81
+ # == Database Independence
82
+ #
83
+ # +stub_model+ does not make your examples entirely
84
+ # database-independent. It does not stop the model class itself from
85
+ # loading up its columns from the database. It just prevents data access
86
+ # from the object itself. To completely decouple from the database, take
87
+ # a look at libraries like unit_record or NullDB.
88
+ #
89
+ # == Examples
90
+ #
91
+ # stub_model(Person)
92
+ # stub_model(Person).as_new_record
93
+ # stub_model(Person, :id => 37)
94
+ # stub_model(Person) do |person|
95
+ # person.first_name = "David"
96
+ # end
97
+ def stub_model(model_class, stubs={})
98
+ stubs = {:id => next_id}.merge(stubs)
99
+ returning model_class.new do |model|
100
+ model.id = stubs.delete(:id)
101
+ model.extend ModelStubber
102
+ stubs.each do |k,v|
103
+ if model.has_attribute?(k)
104
+ model[k] = stubs.delete(k)
105
+ end
106
+ end
107
+ model.stub!(stubs)
108
+ yield model if block_given?
109
+ end
110
+ end
111
+
112
+ # DEPRECATED - use object.stub!(:method => value, :method2 => value)
113
+ #
114
+ # Stubs methods on +object+ (if +object+ is a symbol or string a new mock
115
+ # with that name will be created). +stubs+ is a Hash of +method=>value+
116
+ def add_stubs(object, stubs = {}) #:nodoc:
117
+ Kernel.warn <<-WARNING
118
+ DEPRECATION NOTICE: add_stubs is deprecated and will be removed
119
+ from a future version of rspec-rails. Use this instead:
120
+
121
+ object.stub!(:method => value, :method2 => value)
122
+
123
+ WARNING
124
+ object.stub!(stubs)
125
+ end
126
+
127
+ private
128
+ @@model_id = 1000
129
+ def next_id
130
+ @@model_id += 1
131
+ end
132
+
133
+ end
134
+ end
135
+ end