shoulda-matchers 3.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. checksums.yaml +5 -5
  2. data/{MIT-LICENSE → LICENSE} +1 -1
  3. data/README.md +407 -232
  4. data/docs/errors/NonCaseSwappableValueError.md +2 -2
  5. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +7 -80
  6. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +4 -3
  7. data/lib/shoulda/matchers/action_controller/flash_store.rb +2 -4
  8. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +36 -30
  9. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +8 -10
  10. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +7 -9
  11. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +18 -15
  12. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +3 -2
  13. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +3 -3
  14. data/lib/shoulda/matchers/action_controller/route_matcher.rb +88 -29
  15. data/lib/shoulda/matchers/action_controller/route_params.rb +2 -2
  16. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +4 -4
  17. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +3 -3
  18. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +19 -13
  19. data/lib/shoulda/matchers/action_controller.rb +2 -0
  20. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +1 -1
  21. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +5 -9
  22. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +2 -2
  23. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +1 -1
  24. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +1 -1
  25. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +42 -39
  26. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +1 -1
  27. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +52 -26
  28. data/lib/shoulda/matchers/active_model/helpers.rb +2 -2
  29. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +32 -30
  30. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +2 -1
  31. data/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
  32. data/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb +26 -0
  33. data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +1 -1
  34. data/lib/shoulda/matchers/active_model/qualifiers.rb +2 -0
  35. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +30 -6
  36. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +8 -3
  37. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +31 -16
  38. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +52 -16
  39. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +137 -84
  40. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +159 -46
  41. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +130 -66
  42. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +251 -24
  43. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +12 -9
  44. data/lib/shoulda/matchers/active_model/validation_matcher.rb +38 -6
  45. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +2 -4
  46. data/lib/shoulda/matchers/active_model/validator.rb +4 -9
  47. data/lib/shoulda/matchers/active_model.rb +3 -5
  48. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +10 -7
  49. data/lib/shoulda/matchers/active_record/association_matcher.rb +386 -111
  50. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +5 -2
  51. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +4 -4
  52. data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +1 -1
  53. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +11 -6
  54. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +14 -15
  55. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +30 -8
  56. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +34 -11
  57. data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +69 -0
  58. data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +1 -1
  59. data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +74 -0
  60. data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +3 -2
  61. data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -5
  62. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +458 -42
  63. data/lib/shoulda/matchers/active_record/have_attached_matcher.rb +185 -0
  64. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +63 -23
  65. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +164 -48
  66. data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +106 -0
  67. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +13 -11
  68. data/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
  69. data/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +132 -0
  70. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +18 -18
  71. data/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +1 -3
  72. data/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +0 -2
  73. data/lib/shoulda/matchers/active_record/uniqueness.rb +1 -1
  74. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +430 -200
  75. data/lib/shoulda/matchers/active_record.rb +28 -20
  76. data/lib/shoulda/matchers/configuration.rb +12 -1
  77. data/lib/shoulda/matchers/doublespeak/double.rb +1 -1
  78. data/lib/shoulda/matchers/doublespeak/double_collection.rb +3 -3
  79. data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +8 -5
  80. data/lib/shoulda/matchers/doublespeak/object_double.rb +6 -2
  81. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +1 -5
  82. data/lib/shoulda/matchers/doublespeak/world.rb +2 -2
  83. data/lib/shoulda/matchers/doublespeak.rb +2 -1
  84. data/lib/shoulda/matchers/error.rb +1 -1
  85. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +109 -29
  86. data/lib/shoulda/matchers/independent.rb +2 -2
  87. data/lib/shoulda/matchers/integrations/configuration.rb +8 -4
  88. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
  89. data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -2
  90. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
  91. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
  92. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
  93. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
  94. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
  95. data/lib/shoulda/matchers/rails_shim.rb +172 -51
  96. data/lib/shoulda/matchers/routing.rb +2 -2
  97. data/lib/shoulda/matchers/util/word_wrap.rb +17 -12
  98. data/lib/shoulda/matchers/util.rb +39 -5
  99. data/lib/shoulda/matchers/version.rb +1 -1
  100. data/lib/shoulda/matchers/warn.rb +4 -3
  101. data/shoulda-matchers.gemspec +33 -15
  102. metadata +31 -338
  103. data/.gitignore +0 -12
  104. data/.hound.yml +0 -3
  105. data/.hound_config/ruby.yml +0 -12
  106. data/.travis.yml +0 -19
  107. data/.yardopts +0 -10
  108. data/Appraisals +0 -73
  109. data/CONTRIBUTING.md +0 -101
  110. data/Gemfile +0 -15
  111. data/Gemfile.lock +0 -70
  112. data/NEWS.md +0 -986
  113. data/Rakefile +0 -39
  114. data/custom_plan.rb +0 -88
  115. data/doc_config/gh-pages/index.html.erb +0 -9
  116. data/doc_config/yard/setup.rb +0 -22
  117. data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +0 -5967
  118. data/doc_config/yard/templates/default/fulldoc/html/css/full_list.css +0 -12
  119. data/doc_config/yard/templates/default/fulldoc/html/css/global.css +0 -62
  120. data/doc_config/yard/templates/default/fulldoc/html/css/solarized.css +0 -69
  121. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +0 -312
  122. data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +0 -32
  123. data/doc_config/yard/templates/default/fulldoc/html/full_list_class.erb +0 -1
  124. data/doc_config/yard/templates/default/fulldoc/html/full_list_method.erb +0 -8
  125. data/doc_config/yard/templates/default/fulldoc/html/js/app.js +0 -298
  126. data/doc_config/yard/templates/default/fulldoc/html/js/full_list.js +0 -1
  127. data/doc_config/yard/templates/default/fulldoc/html/js/jquery.stickyheaders.js +0 -289
  128. data/doc_config/yard/templates/default/fulldoc/html/js/underscore.min.js +0 -6
  129. data/doc_config/yard/templates/default/fulldoc/html/setup.rb +0 -8
  130. data/doc_config/yard/templates/default/layout/html/breadcrumb.erb +0 -14
  131. data/doc_config/yard/templates/default/layout/html/fonts.erb +0 -1
  132. data/doc_config/yard/templates/default/layout/html/footer.erb +0 -6
  133. data/doc_config/yard/templates/default/layout/html/layout.erb +0 -23
  134. data/doc_config/yard/templates/default/layout/html/search.erb +0 -13
  135. data/doc_config/yard/templates/default/layout/html/setup.rb +0 -40
  136. data/doc_config/yard/templates/default/method_details/html/source.erb +0 -10
  137. data/doc_config/yard/templates/default/module/html/box_info.erb +0 -31
  138. data/gemfiles/4.0.0.gemfile +0 -38
  139. data/gemfiles/4.0.0.gemfile.lock +0 -223
  140. data/gemfiles/4.0.1.gemfile +0 -38
  141. data/gemfiles/4.0.1.gemfile.lock +0 -225
  142. data/gemfiles/4.1.gemfile +0 -38
  143. data/gemfiles/4.1.gemfile.lock +0 -220
  144. data/gemfiles/4.2.gemfile +0 -38
  145. data/gemfiles/4.2.gemfile.lock +0 -243
  146. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +0 -159
  147. data/lib/shoulda/matchers/independent/delegate_method_matcher/stubbed_target.rb +0 -37
  148. data/script/SUPPORTED_VERSIONS +0 -1
  149. data/script/install_gems_in_all_appraisals +0 -14
  150. data/script/run_all_tests +0 -14
  151. data/script/update_gem_in_all_appraisals +0 -15
  152. data/script/update_gems_in_all_appraisals +0 -14
  153. data/spec/acceptance/active_model_integration_spec.rb +0 -23
  154. data/spec/acceptance/independent_matchers_spec.rb +0 -125
  155. data/spec/acceptance/multiple_libraries_integration_spec.rb +0 -55
  156. data/spec/acceptance/rails_integration_spec.rb +0 -156
  157. data/spec/acceptance_spec_helper.rb +0 -23
  158. data/spec/doublespeak_spec_helper.rb +0 -2
  159. data/spec/report_warnings.rb +0 -7
  160. data/spec/spec_helper.rb +0 -21
  161. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +0 -133
  162. data/spec/support/acceptance/helpers/active_model_helpers.rb +0 -11
  163. data/spec/support/acceptance/helpers/array_helpers.rb +0 -13
  164. data/spec/support/acceptance/helpers/base_helpers.rb +0 -19
  165. data/spec/support/acceptance/helpers/command_helpers.rb +0 -55
  166. data/spec/support/acceptance/helpers/file_helpers.rb +0 -19
  167. data/spec/support/acceptance/helpers/gem_helpers.rb +0 -31
  168. data/spec/support/acceptance/helpers/minitest_helpers.rb +0 -11
  169. data/spec/support/acceptance/helpers/n_unit_helpers.rb +0 -25
  170. data/spec/support/acceptance/helpers/pluralization_helpers.rb +0 -13
  171. data/spec/support/acceptance/helpers/rails_version_helpers.rb +0 -11
  172. data/spec/support/acceptance/helpers/rspec_helpers.rb +0 -24
  173. data/spec/support/acceptance/helpers/ruby_version_helpers.rb +0 -9
  174. data/spec/support/acceptance/helpers/step_helpers.rb +0 -127
  175. data/spec/support/acceptance/helpers.rb +0 -31
  176. data/spec/support/acceptance/matchers/have_output.rb +0 -31
  177. data/spec/support/acceptance/matchers/indicate_number_of_tests_was_run_matcher.rb +0 -55
  178. data/spec/support/acceptance/matchers/indicate_that_tests_were_run_matcher.rb +0 -103
  179. data/spec/support/tests/bundle.rb +0 -94
  180. data/spec/support/tests/command_runner.rb +0 -230
  181. data/spec/support/tests/current_bundle.rb +0 -61
  182. data/spec/support/tests/database.rb +0 -28
  183. data/spec/support/tests/database_adapters/postgresql.rb +0 -25
  184. data/spec/support/tests/database_adapters/sqlite3.rb +0 -26
  185. data/spec/support/tests/database_configuration.rb +0 -33
  186. data/spec/support/tests/database_configuration_registry.rb +0 -28
  187. data/spec/support/tests/filesystem.rb +0 -100
  188. data/spec/support/tests/version.rb +0 -45
  189. data/spec/support/unit/active_record/create_table.rb +0 -54
  190. data/spec/support/unit/attribute.rb +0 -47
  191. data/spec/support/unit/capture.rb +0 -40
  192. data/spec/support/unit/change_value.rb +0 -111
  193. data/spec/support/unit/create_model_arguments/basic.rb +0 -135
  194. data/spec/support/unit/create_model_arguments/has_many.rb +0 -15
  195. data/spec/support/unit/create_model_arguments/uniqueness_matcher.rb +0 -74
  196. data/spec/support/unit/helpers/active_model_helpers.rb +0 -27
  197. data/spec/support/unit/helpers/active_model_versions.rb +0 -28
  198. data/spec/support/unit/helpers/active_record_versions.rb +0 -24
  199. data/spec/support/unit/helpers/active_resource_builder.rb +0 -27
  200. data/spec/support/unit/helpers/allow_value_matcher_helpers.rb +0 -15
  201. data/spec/support/unit/helpers/class_builder.rb +0 -90
  202. data/spec/support/unit/helpers/column_type_helpers.rb +0 -26
  203. data/spec/support/unit/helpers/confirmation_matcher_helpers.rb +0 -17
  204. data/spec/support/unit/helpers/controller_builder.rb +0 -63
  205. data/spec/support/unit/helpers/database_helpers.rb +0 -20
  206. data/spec/support/unit/helpers/i18n_faker.rb +0 -15
  207. data/spec/support/unit/helpers/mailer_builder.rb +0 -12
  208. data/spec/support/unit/helpers/model_builder.rb +0 -114
  209. data/spec/support/unit/helpers/rails_versions.rb +0 -28
  210. data/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb +0 -44
  211. data/spec/support/unit/i18n.rb +0 -7
  212. data/spec/support/unit/load_environment.rb +0 -12
  213. data/spec/support/unit/matchers/deprecate.rb +0 -60
  214. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +0 -51
  215. data/spec/support/unit/matchers/fail_with_message_matcher.rb +0 -62
  216. data/spec/support/unit/matchers/print_warning_including.rb +0 -59
  217. data/spec/support/unit/model_creation_strategies/active_model.rb +0 -111
  218. data/spec/support/unit/model_creation_strategies/active_record.rb +0 -77
  219. data/spec/support/unit/model_creators/active_model.rb +0 -39
  220. data/spec/support/unit/model_creators/active_record/has_and_belongs_to_many.rb +0 -95
  221. data/spec/support/unit/model_creators/active_record/has_many.rb +0 -67
  222. data/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb +0 -42
  223. data/spec/support/unit/model_creators/active_record.rb +0 -43
  224. data/spec/support/unit/model_creators/basic.rb +0 -97
  225. data/spec/support/unit/model_creators.rb +0 -19
  226. data/spec/support/unit/rails_application.rb +0 -126
  227. data/spec/support/unit/record_builder_with_i18n_validation_message.rb +0 -69
  228. data/spec/support/unit/record_validating_confirmation_builder.rb +0 -51
  229. data/spec/support/unit/record_with_different_error_attribute_builder.rb +0 -92
  230. data/spec/support/unit/shared_examples/ignoring_interference_by_writer.rb +0 -79
  231. data/spec/support/unit/shared_examples/numerical_submatcher.rb +0 -17
  232. data/spec/support/unit/shared_examples/set_session_or_flash.rb +0 -360
  233. data/spec/support/unit/validation_matcher_scenario.rb +0 -62
  234. data/spec/unit/shoulda/matchers/action_controller/callback_matcher_spec.rb +0 -82
  235. data/spec/unit/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +0 -28
  236. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +0 -592
  237. data/spec/unit/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +0 -42
  238. data/spec/unit/shoulda/matchers/action_controller/render_template_matcher_spec.rb +0 -76
  239. data/spec/unit/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +0 -62
  240. data/spec/unit/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +0 -90
  241. data/spec/unit/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +0 -31
  242. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +0 -330
  243. data/spec/unit/shoulda/matchers/action_controller/route_params_spec.rb +0 -30
  244. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +0 -67
  245. data/spec/unit/shoulda/matchers/action_controller/set_session_matcher_spec.rb +0 -17
  246. data/spec/unit/shoulda/matchers/action_controller/set_session_or_flash_matcher_spec.rb +0 -562
  247. data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +0 -115
  248. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +0 -823
  249. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +0 -86
  250. data/spec/unit/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb +0 -20
  251. data/spec/unit/shoulda/matchers/active_model/helpers_spec.rb +0 -162
  252. data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +0 -266
  253. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +0 -91
  254. data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +0 -149
  255. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +0 -207
  256. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +0 -1015
  257. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +0 -288
  258. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +0 -1837
  259. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +0 -380
  260. data/spec/unit/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +0 -107
  261. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +0 -1242
  262. data/spec/unit/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +0 -251
  263. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +0 -168
  264. data/spec/unit/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +0 -111
  265. data/spec/unit/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +0 -85
  266. data/spec/unit/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +0 -41
  267. data/spec/unit/shoulda/matchers/active_record/serialize_matcher_spec.rb +0 -86
  268. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +0 -1418
  269. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +0 -190
  270. data/spec/unit/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +0 -21
  271. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +0 -271
  272. data/spec/unit/shoulda/matchers/doublespeak/object_double_spec.rb +0 -77
  273. data/spec/unit/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +0 -72
  274. data/spec/unit/shoulda/matchers/doublespeak/stub_implementation_spec.rb +0 -101
  275. data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +0 -80
  276. data/spec/unit/shoulda/matchers/doublespeak_spec.rb +0 -27
  277. data/spec/unit/shoulda/matchers/independent/delegate_method_matcher/stubbed_target_spec.rb +0 -43
  278. data/spec/unit/shoulda/matchers/independent/delegate_method_matcher_spec.rb +0 -517
  279. data/spec/unit/shoulda/matchers/routing/route_matcher_spec.rb +0 -242
  280. data/spec/unit/shoulda/matchers/util/word_wrap_spec.rb +0 -252
  281. data/spec/unit_spec_helper.rb +0 -46
  282. data/spec/warnings_spy/filesystem.rb +0 -45
  283. data/spec/warnings_spy/partitioner.rb +0 -36
  284. data/spec/warnings_spy/reader.rb +0 -53
  285. data/spec/warnings_spy/reporter.rb +0 -88
  286. data/spec/warnings_spy.rb +0 -64
  287. data/tasks/documentation.rb +0 -199
  288. data/zeus.json +0 -11
@@ -2,46 +2,189 @@ module Shoulda
2
2
  module Matchers
3
3
  module ActiveRecord
4
4
  # The `define_enum_for` matcher is used to test that the `enum` macro has
5
- # been used to decorate an attribute with enum methods.
5
+ # been used to decorate an attribute with enum capabilities.
6
6
  #
7
7
  # class Process < ActiveRecord::Base
8
8
  # enum status: [:running, :stopped, :suspended]
9
+ #
10
+ # alias_attribute :kind, :SomeLegacyField
11
+ #
12
+ # enum kind: [:foo, :bar]
9
13
  # end
10
14
  #
11
15
  # # RSpec
12
- # describe Process do
16
+ # RSpec.describe Process, type: :model do
13
17
  # it { should define_enum_for(:status) }
14
- # end
18
+ # it { should define_enum_for(:kind) }
15
19
  # end
16
20
  #
17
21
  # # Minitest (Shoulda)
18
22
  # class ProcessTest < ActiveSupport::TestCase
19
23
  # should define_enum_for(:status)
24
+ # should define_enum_for(:kind)
20
25
  # end
21
26
  #
22
27
  # #### Qualifiers
23
28
  #
24
- # ##### with
29
+ # ##### with_values
25
30
  #
26
- # Use `with` to test that the enum has been defined with a certain set of
27
- # known values.
31
+ # Use `with_values` to test that the attribute can only receive a certain
32
+ # set of possible values.
28
33
  #
29
34
  # class Process < ActiveRecord::Base
30
35
  # enum status: [:running, :stopped, :suspended]
31
36
  # end
32
37
  #
33
38
  # # RSpec
34
- # describe Process do
39
+ # RSpec.describe Process, type: :model do
40
+ # it do
41
+ # should define_enum_for(:status).
42
+ # with_values([:running, :stopped, :suspended])
43
+ # end
44
+ # end
45
+ #
46
+ # # Minitest (Shoulda)
47
+ # class ProcessTest < ActiveSupport::TestCase
48
+ # should define_enum_for(:status).
49
+ # with_values([:running, :stopped, :suspended])
50
+ # end
51
+ #
52
+ # If the values backing your enum attribute are arbitrary instead of a
53
+ # series of integers starting from 0, pass a hash to `with_values` instead
54
+ # of an array:
55
+ #
56
+ # class Process < ActiveRecord::Base
57
+ # enum status: {
58
+ # running: 0,
59
+ # stopped: 1,
60
+ # suspended: 3,
61
+ # other: 99
62
+ # }
63
+ # end
64
+ #
65
+ # # RSpec
66
+ # RSpec.describe Process, type: :model do
67
+ # it do
68
+ # should define_enum_for(:status).
69
+ # with_values(running: 0, stopped: 1, suspended: 3, other: 99)
70
+ # end
71
+ # end
72
+ #
73
+ # # Minitest (Shoulda)
74
+ # class ProcessTest < ActiveSupport::TestCase
75
+ # should define_enum_for(:status).
76
+ # with_values(running: 0, stopped: 1, suspended: 3, other: 99)
77
+ # end
78
+ #
79
+ # ##### backed_by_column_of_type
80
+ #
81
+ # Use `backed_by_column_of_type` when the column backing your column type
82
+ # is a string instead of an integer:
83
+ #
84
+ # class LoanApplication < ActiveRecord::Base
85
+ # enum status: {
86
+ # active: "active",
87
+ # pending: "pending",
88
+ # rejected: "rejected"
89
+ # }
90
+ # end
91
+ #
92
+ # # RSpec
93
+ # RSpec.describe LoanApplication, type: :model do
94
+ # it do
95
+ # should define_enum_for(:status).
96
+ # with_values(
97
+ # active: "active",
98
+ # pending: "pending",
99
+ # rejected: "rejected"
100
+ # ).
101
+ # backed_by_column_of_type(:string)
102
+ # end
103
+ # end
104
+ #
105
+ # # Minitest (Shoulda)
106
+ # class LoanApplicationTest < ActiveSupport::TestCase
107
+ # should define_enum_for(:status).
108
+ # with_values(
109
+ # active: "active",
110
+ # pending: "pending",
111
+ # rejected: "rejected"
112
+ # ).
113
+ # backed_by_column_of_type(:string)
114
+ # end
115
+ #
116
+ ## ##### with_prefix
117
+ #
118
+ # Use `with_prefix` to test that the enum is defined with a `_prefix`
119
+ # option (Rails 6+ only). Can take either a boolean or a symbol:
120
+ #
121
+ # class Issue < ActiveRecord::Base
122
+ # enum status: [:open, :closed], _prefix: :old
123
+ # end
124
+ #
125
+ # # RSpec
126
+ # RSpec.describe Issue, type: :model do
35
127
  # it do
36
128
  # should define_enum_for(:status).
37
- # with([:running, :stopped, :suspended])
129
+ # with_values([:open, :closed]).
130
+ # with_prefix(:old)
38
131
  # end
39
132
  # end
40
133
  #
41
134
  # # Minitest (Shoulda)
42
135
  # class ProcessTest < ActiveSupport::TestCase
43
136
  # should define_enum_for(:status).
44
- # with([:running, :stopped, :suspended])
137
+ # with_values([:open, :closed]).
138
+ # with_prefix(:old)
139
+ # end
140
+ #
141
+ # ##### with_suffix
142
+ #
143
+ # Use `with_suffix` to test that the enum is defined with a `_suffix`
144
+ # option (Rails 5 only). Can take either a boolean or a symbol:
145
+ #
146
+ # class Issue < ActiveRecord::Base
147
+ # enum status: [:open, :closed], _suffix: true
148
+ # end
149
+ #
150
+ # # RSpec
151
+ # RSpec.describe Issue, type: :model do
152
+ # it do
153
+ # should define_enum_for(:status).
154
+ # with_values([:open, :closed]).
155
+ # with_suffix
156
+ # end
157
+ # end
158
+ #
159
+ # # Minitest (Shoulda)
160
+ # class ProcessTest < ActiveSupport::TestCase
161
+ # should define_enum_for(:status).
162
+ # with_values([:open, :closed]).
163
+ # with_suffix
164
+ # end
165
+ #
166
+ # ##### without_scopes
167
+ #
168
+ # Use `without_scopes` to test that the enum is defined with
169
+ # '_scopes: false' option (Rails 5 only). Can take either a boolean or a
170
+ # symbol:
171
+ #
172
+ # class Issue < ActiveRecord::Base
173
+ # enum status: [:open, :closed], _scopes: false
174
+ # end
175
+ #
176
+ # # RSpec
177
+ # RSpec.describe Issue, type: :model do
178
+ # it do
179
+ # should define_enum_for(:status).
180
+ # without_scopes
181
+ # end
182
+ # end
183
+ #
184
+ # # Minitest (Shoulda)
185
+ # class ProcessTest < ActiveSupport::TestCase
186
+ # should define_enum_for(:status).
187
+ # without_scopes
45
188
  # end
46
189
  #
47
190
  # @return [DefineEnumForMatcher]
@@ -54,51 +197,171 @@ module Shoulda
54
197
  class DefineEnumForMatcher
55
198
  def initialize(attribute_name)
56
199
  @attribute_name = attribute_name
57
- @options = {}
200
+ @options = { expected_enum_values: [], scopes: true }
58
201
  end
59
202
 
60
- def with(expected_enum_values)
203
+ def description
204
+ description = "#{simple_description} backed by "
205
+ description << Shoulda::Matchers::Util.a_or_an(expected_column_type)
206
+
207
+ if expected_enum_values.any?
208
+ description << ' with values '
209
+ description << Shoulda::Matchers::Util.inspect_value(
210
+ expected_enum_values,
211
+ )
212
+ end
213
+
214
+ if options[:prefix]
215
+ description << ", prefix: #{options[:prefix].inspect}"
216
+ end
217
+
218
+ if options[:suffix]
219
+ description << ", suffix: #{options[:suffix].inspect}"
220
+ end
221
+
222
+ description
223
+ end
224
+
225
+ def with_values(expected_enum_values)
61
226
  options[:expected_enum_values] = expected_enum_values
62
227
  self
63
228
  end
64
229
 
230
+ def with(expected_enum_values)
231
+ Shoulda::Matchers.warn_about_deprecated_method(
232
+ 'The `with` qualifier on `define_enum_for`',
233
+ '`with_values`',
234
+ )
235
+ with_values(expected_enum_values)
236
+ end
237
+
238
+ def with_prefix(expected_prefix = true)
239
+ options[:prefix] = expected_prefix
240
+ self
241
+ end
242
+
243
+ def with_suffix(expected_suffix = true)
244
+ options[:suffix] = expected_suffix
245
+ self
246
+ end
247
+
248
+ def backed_by_column_of_type(expected_column_type)
249
+ options[:expected_column_type] = expected_column_type
250
+ self
251
+ end
252
+
253
+ def without_scopes
254
+ options[:scopes] = false
255
+ self
256
+ end
257
+
65
258
  def matches?(subject)
66
259
  @record = subject
67
- enum_defined? && enum_values_match? && column_type_is_integer?
260
+
261
+ enum_defined? &&
262
+ enum_values_match? &&
263
+ column_type_matches? &&
264
+ enum_value_methods_exist? &&
265
+ scope_presence_matches?
68
266
  end
69
267
 
70
268
  def failure_message
71
- "Expected #{expectation}"
269
+ message =
270
+ if enum_defined?
271
+ "Expected #{model} to #{expectation}. "
272
+ else
273
+ "Expected #{model} to #{expectation}, but "
274
+ end
275
+
276
+ message << "#{failure_message_continuation}."
277
+
278
+ Shoulda::Matchers.word_wrap(message)
72
279
  end
73
- alias :failure_message_for_should :failure_message
74
280
 
75
281
  def failure_message_when_negated
76
- "Did not expect #{expectation}"
282
+ message = "Expected #{model} not to #{expectation}, but it did."
283
+ Shoulda::Matchers.word_wrap(message)
77
284
  end
78
- alias :failure_message_for_should_not :failure_message_when_negated
79
285
 
80
- def description
81
- desc = "define :#{attribute_name} as an enum"
286
+ private
82
287
 
83
- if options[:expected_enum_values]
84
- desc << " with #{options[:expected_enum_values]}"
85
- end
288
+ attr_reader :attribute_name, :options, :record,
289
+ :failure_message_continuation
290
+
291
+ def expectation # rubocop:disable Metrics/MethodLength
292
+ if enum_defined?
293
+ expectation = "#{simple_description} backed by "
294
+ expectation << Shoulda::Matchers::Util.a_or_an(expected_column_type)
295
+
296
+ if expected_enum_values.any?
297
+ expectation << ', mapping '
298
+ expectation << presented_enum_mapping(
299
+ normalized_expected_enum_values,
300
+ )
301
+ end
86
302
 
87
- desc << " and store the value in a column with an integer type"
303
+ if expected_prefix
304
+ expectation <<
305
+ if expected_suffix
306
+ ', '
307
+ else
308
+ ' and '
309
+ end
88
310
 
89
- desc
311
+ expectation << 'prefixing accessor methods with '
312
+ expectation << "#{expected_prefix}_".inspect
313
+ end
314
+
315
+ if expected_suffix
316
+ expectation <<
317
+ if expected_prefix
318
+ ', and '
319
+ else
320
+ ' and '
321
+ end
322
+
323
+ expectation << 'suffixing accessor methods with '
324
+ expectation << "_#{expected_suffix}".inspect
325
+ end
326
+
327
+ if exclude_scopes?
328
+ expectation << ' with no scopes'
329
+ end
330
+
331
+ expectation
332
+ else
333
+ simple_description
334
+ end
90
335
  end
91
336
 
92
- protected
337
+ def simple_description
338
+ "define :#{attribute_name} as an enum"
339
+ end
340
+
341
+ def presented_enum_mapping(enum_values)
342
+ enum_values.
343
+ map { |output_to_input|
344
+ output_to_input.
345
+ map(&Shoulda::Matchers::Util.method(:inspect_value)).
346
+ join(' to ')
347
+ }.
348
+ to_sentence
349
+ end
93
350
 
94
- attr_reader :record, :attribute_name, :options
351
+ def normalized_expected_enum_values
352
+ to_hash(expected_enum_values)
353
+ end
95
354
 
96
- def expectation
97
- "#{model.name} to #{description}"
355
+ def expected_enum_value_names
356
+ to_array(expected_enum_values)
98
357
  end
99
358
 
100
359
  def expected_enum_values
101
- hashify(options[:expected_enum_values]).with_indifferent_access
360
+ options[:expected_enum_values]
361
+ end
362
+
363
+ def normalized_actual_enum_values
364
+ to_hash(actual_enum_values)
102
365
  end
103
366
 
104
367
  def actual_enum_values
@@ -106,40 +369,193 @@ module Shoulda
106
369
  end
107
370
 
108
371
  def enum_defined?
109
- model.defined_enums.include?(attribute_name.to_s)
372
+ if model.defined_enums.include?(attribute_name.to_s)
373
+ true
374
+ else
375
+ @failure_message_continuation =
376
+ "no such enum exists on #{model}"
377
+ false
378
+ end
110
379
  end
111
380
 
112
381
  def enum_values_match?
113
- expected_enum_values.empty? || actual_enum_values == expected_enum_values
382
+ passed =
383
+ expected_enum_values.empty? ||
384
+ normalized_actual_enum_values == normalized_expected_enum_values
385
+
386
+ if passed
387
+ true
388
+ else
389
+ @failure_message_continuation =
390
+ "However, #{attribute_name.inspect} actually maps " +
391
+ presented_enum_mapping(normalized_actual_enum_values)
392
+ false
393
+ end
394
+ end
395
+
396
+ def column_type_matches?
397
+ if column.type == expected_column_type.to_sym
398
+ true
399
+ else
400
+ @failure_message_continuation =
401
+ "However, #{attribute_name.inspect} is "\
402
+ "#{Shoulda::Matchers::Util.a_or_an(column.type)}"\
403
+ ' column'
404
+ false
405
+ end
114
406
  end
115
407
 
116
- def column_type_is_integer?
117
- column.type == :integer
408
+ def expected_column_type
409
+ options[:expected_column_type] || :integer
118
410
  end
119
411
 
120
412
  def column
121
- model.columns_hash[attribute_name.to_s]
413
+ key = attribute_name.to_s
414
+ column_name = model.attribute_alias(key) || key
415
+
416
+ model.columns_hash[column_name]
122
417
  end
123
418
 
124
419
  def model
125
420
  record.class
126
421
  end
127
422
 
128
- def hashify(value)
129
- if value.nil?
130
- return {}
423
+ def enum_value_methods_exist?
424
+ if instance_methods_exist?
425
+ true
426
+ else
427
+ message = missing_methods_message
428
+
429
+ message << " (we can't tell which)"
430
+
431
+ @failure_message_continuation = message
432
+
433
+ false
434
+ end
435
+ end
436
+
437
+ def scope_presence_matches?
438
+ if exclude_scopes?
439
+ if singleton_methods_exist?
440
+ message = "#{attribute_name.inspect} does map to these values "
441
+ message << 'but class scope methods were present'
442
+
443
+ @failure_message_continuation = message
444
+
445
+ false
446
+ else
447
+ true
448
+ end
449
+ elsif singleton_methods_exist?
450
+ true
451
+ else
452
+ if enum_defined?
453
+ message = 'But the class scope methods are not present'
454
+ else
455
+ message = missing_methods_message
456
+
457
+ message << 'or the class scope methods are not present'
458
+ message << " (we can't tell which)"
459
+ end
460
+
461
+ @failure_message_continuation = message
462
+
463
+ false
131
464
  end
465
+ end
132
466
 
133
- if value.is_a?(Array)
134
- new_value = {}
467
+ def missing_methods_message
468
+ message = "#{attribute_name.inspect} does map to these "
469
+ message << 'values, but the enum is '
135
470
 
136
- value.each_with_index do |v, i|
137
- new_value[v] = i
471
+ if expected_prefix
472
+ if expected_suffix
473
+ message << 'configured with either a different prefix or '
474
+ message << 'suffix, or no prefix or suffix at all'
475
+ else
476
+ message << 'configured with either a different prefix or no '
477
+ message << 'prefix at all'
138
478
  end
479
+ elsif expected_suffix
480
+ message << 'configured with either a different suffix or no '
481
+ message << 'suffix at all'
482
+ else
483
+ ''
484
+ end
485
+ end
486
+
487
+ def singleton_methods_exist?
488
+ expected_singleton_methods.all? do |method|
489
+ model.singleton_methods.include?(method)
490
+ end
491
+ end
492
+
493
+ def instance_methods_exist?
494
+ expected_instance_methods.all? do |method|
495
+ record.methods.include?(method)
496
+ end
497
+ end
139
498
 
140
- new_value
499
+ def expected_singleton_methods
500
+ expected_enum_value_names.map do |name|
501
+ [expected_prefix, name, expected_suffix].
502
+ select(&:present?).
503
+ join('_').
504
+ to_sym
505
+ end
506
+ end
507
+
508
+ def expected_instance_methods
509
+ methods = expected_enum_value_names.map do |name|
510
+ [expected_prefix, name, expected_suffix].
511
+ select(&:present?).
512
+ join('_')
513
+ end
514
+
515
+ methods.flat_map do |m|
516
+ ["#{m}?".to_sym, "#{m}!".to_sym]
517
+ end
518
+ end
519
+
520
+ def expected_prefix
521
+ if options.include?(:prefix)
522
+ if options[:prefix] == true
523
+ attribute_name
524
+ else
525
+ options[:prefix]
526
+ end
527
+ end
528
+ end
529
+
530
+ def expected_suffix
531
+ if options.include?(:suffix)
532
+ if options[:suffix] == true
533
+ attribute_name
534
+ else
535
+ options[:suffix]
536
+ end
537
+ end
538
+ end
539
+
540
+ def exclude_scopes?
541
+ !options[:scopes]
542
+ end
543
+
544
+ def to_hash(value)
545
+ if value.is_a?(Array)
546
+ value.each_with_index.inject({}) do |hash, (item, index)|
547
+ hash.merge(item.to_s => index)
548
+ end
549
+ else
550
+ value.stringify_keys
551
+ end
552
+ end
553
+
554
+ def to_array(value)
555
+ if value.is_a?(Array)
556
+ value.map(&:to_s)
141
557
  else
142
- value
558
+ value.keys.map(&:to_s)
143
559
  end
144
560
  end
145
561
  end