mocktail 1.2.3 → 3.0.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 (443) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +6 -5
  3. data/.gitignore +3 -0
  4. data/.standard.yml +8 -0
  5. data/CHANGELOG.md +19 -0
  6. data/Gemfile +8 -2
  7. data/Gemfile.lock +105 -38
  8. data/README.md +20 -922
  9. data/Rakefile +0 -1
  10. data/bin/console +1 -2
  11. data/bin/tapioca +29 -0
  12. data/docs/code_of_conduct.md +8 -0
  13. data/docs/configuring_minitest.md +20 -0
  14. data/docs/configuring_rspec.md +19 -0
  15. data/docs/example_test.md +594 -0
  16. data/docs/faq/existing_tests.md +47 -0
  17. data/docs/faq/mocking_http.md +151 -0
  18. data/docs/faq/mocking_the_subject.md +161 -0
  19. data/docs/faq/mocking_time.md +34 -0
  20. data/docs/faq/partial_mocks.md +34 -0
  21. data/docs/faq/verifying_real_interactions.md +52 -0
  22. data/docs/img/delegator_tree.png +0 -0
  23. data/docs/img/example_test.png +0 -0
  24. data/docs/img/extract_transform_load.png +0 -0
  25. data/docs/img/mocktail_sorbet.jpg +0 -0
  26. data/docs/img/mocktail_untyped.jpg +0 -0
  27. data/docs/img/spacer.png +0 -0
  28. data/docs/installation_sorbet.md +99 -0
  29. data/docs/installation_untyped.md +29 -0
  30. data/docs/other_uses.md +32 -0
  31. data/docs/stubbing_and_verifying.md +515 -0
  32. data/docs/support/api.md +819 -0
  33. data/docs/support/example_test.rb +94 -0
  34. data/docs/support/glossary.md +426 -0
  35. data/docs/tdd/class_methods.md +44 -0
  36. data/docs/tdd/poro/dependency_inception.md +136 -0
  37. data/docs/tdd/poro/dependency_injection.md +82 -0
  38. data/docs/tdd/poro.md +15 -0
  39. data/docs/tdd/third_party.md +220 -0
  40. data/docs/tdd.md +15 -0
  41. data/lib/mocktail/collects_calls.rb +2 -0
  42. data/lib/mocktail/debug.rb +16 -12
  43. data/lib/mocktail/dsl.rb +2 -0
  44. data/lib/mocktail/errors.rb +2 -0
  45. data/lib/mocktail/explains_nils.rb +2 -0
  46. data/lib/mocktail/explains_thing.rb +7 -4
  47. data/lib/mocktail/grabs_original_method_parameters.rb +30 -0
  48. data/lib/mocktail/handles_dry_call/fulfills_stubbing/describes_unsatisfied_stubbing.rb +3 -1
  49. data/lib/mocktail/handles_dry_call/fulfills_stubbing/finds_satisfaction.rb +5 -1
  50. data/lib/mocktail/handles_dry_call/fulfills_stubbing.rb +2 -0
  51. data/lib/mocktail/handles_dry_call/logs_call.rb +2 -0
  52. data/lib/mocktail/handles_dry_call/validates_arguments.rb +6 -4
  53. data/lib/mocktail/handles_dry_call.rb +2 -0
  54. data/lib/mocktail/handles_dry_new_call.rb +2 -0
  55. data/lib/mocktail/imitates_type/ensures_imitation_support.rb +2 -0
  56. data/lib/mocktail/imitates_type/makes_double/declares_dry_class/reconstructs_call.rb +4 -1
  57. data/lib/mocktail/imitates_type/makes_double/declares_dry_class.rb +32 -20
  58. data/lib/mocktail/imitates_type/makes_double/gathers_fakeable_instance_methods.rb +2 -0
  59. data/lib/mocktail/imitates_type/makes_double.rb +3 -0
  60. data/lib/mocktail/imitates_type.rb +3 -1
  61. data/lib/mocktail/initialize_based_on_type_system_mode_switching.rb +9 -0
  62. data/lib/mocktail/initializes_mocktail.rb +5 -0
  63. data/lib/mocktail/matcher_presentation.rb +4 -2
  64. data/lib/mocktail/matchers/any.rb +4 -3
  65. data/lib/mocktail/matchers/base.rb +10 -2
  66. data/lib/mocktail/matchers/captor.rb +9 -0
  67. data/lib/mocktail/matchers/includes.rb +2 -0
  68. data/lib/mocktail/matchers/includes_hash.rb +9 -0
  69. data/lib/mocktail/matchers/includes_key.rb +9 -0
  70. data/lib/mocktail/matchers/includes_string.rb +9 -0
  71. data/lib/mocktail/matchers/is_a.rb +2 -0
  72. data/lib/mocktail/matchers/matches.rb +2 -0
  73. data/lib/mocktail/matchers/not.rb +2 -0
  74. data/lib/mocktail/matchers/numeric.rb +5 -4
  75. data/lib/mocktail/matchers/that.rb +2 -0
  76. data/lib/mocktail/matchers.rb +3 -0
  77. data/lib/mocktail/raises_neato_no_method_error.rb +2 -0
  78. data/lib/mocktail/records_demonstration.rb +2 -0
  79. data/lib/mocktail/registers_matcher.rb +8 -3
  80. data/lib/mocktail/registers_stubbing.rb +2 -0
  81. data/lib/mocktail/replaces_next.rb +7 -1
  82. data/lib/mocktail/replaces_type/redefines_new.rb +3 -1
  83. data/lib/mocktail/replaces_type/redefines_singleton_methods.rb +14 -2
  84. data/lib/mocktail/replaces_type/runs_sorbet_sig_blocks_before_replacement.rb +37 -0
  85. data/lib/mocktail/replaces_type.rb +6 -0
  86. data/lib/mocktail/resets_state.rb +2 -0
  87. data/lib/mocktail/share/bind.rb +7 -5
  88. data/lib/mocktail/share/cleans_backtrace.rb +3 -5
  89. data/lib/mocktail/share/creates_identifier.rb +16 -9
  90. data/lib/mocktail/share/determines_matching_calls.rb +4 -2
  91. data/lib/mocktail/share/stringifies_call.rb +6 -3
  92. data/lib/mocktail/share/stringifies_method_name.rb +3 -1
  93. data/lib/mocktail/simulates_argument_error/reconciles_args_with_params.rb +2 -0
  94. data/lib/mocktail/simulates_argument_error/recreates_message.rb +2 -0
  95. data/lib/mocktail/simulates_argument_error/transforms_params.rb +16 -9
  96. data/lib/mocktail/simulates_argument_error.rb +2 -0
  97. data/lib/mocktail/sorbet/mocktail/collects_calls.rb +18 -0
  98. data/lib/mocktail/sorbet/mocktail/debug.rb +55 -0
  99. data/lib/mocktail/sorbet/mocktail/dsl.rb +46 -0
  100. data/lib/mocktail/sorbet/mocktail/errors.rb +19 -0
  101. data/lib/mocktail/sorbet/mocktail/explains_nils.rb +41 -0
  102. data/lib/mocktail/sorbet/mocktail/explains_thing.rb +137 -0
  103. data/lib/mocktail/sorbet/mocktail/grabs_original_method_parameters.rb +33 -0
  104. data/lib/mocktail/sorbet/mocktail/handles_dry_call/fulfills_stubbing/describes_unsatisfied_stubbing.rb +27 -0
  105. data/lib/mocktail/sorbet/mocktail/handles_dry_call/fulfills_stubbing/finds_satisfaction.rb +24 -0
  106. data/lib/mocktail/sorbet/mocktail/handles_dry_call/fulfills_stubbing.rb +45 -0
  107. data/lib/mocktail/sorbet/mocktail/handles_dry_call/logs_call.rb +12 -0
  108. data/lib/mocktail/sorbet/mocktail/handles_dry_call/validates_arguments.rb +45 -0
  109. data/lib/mocktail/sorbet/mocktail/handles_dry_call.rb +25 -0
  110. data/lib/mocktail/sorbet/mocktail/handles_dry_new_call.rb +42 -0
  111. data/lib/mocktail/sorbet/mocktail/imitates_type/ensures_imitation_support.rb +16 -0
  112. data/lib/mocktail/sorbet/mocktail/imitates_type/makes_double/declares_dry_class/reconstructs_call.rb +73 -0
  113. data/lib/mocktail/sorbet/mocktail/imitates_type/makes_double/declares_dry_class.rb +136 -0
  114. data/lib/mocktail/sorbet/mocktail/imitates_type/makes_double/gathers_fakeable_instance_methods.rb +28 -0
  115. data/lib/mocktail/sorbet/mocktail/imitates_type/makes_double.rb +29 -0
  116. data/lib/mocktail/sorbet/mocktail/imitates_type.rb +29 -0
  117. data/lib/mocktail/sorbet/mocktail/initialize_based_on_type_system_mode_switching.rb +11 -0
  118. data/lib/mocktail/sorbet/mocktail/initializes_mocktail.rb +25 -0
  119. data/lib/mocktail/sorbet/mocktail/matcher_presentation.rb +21 -0
  120. data/lib/mocktail/sorbet/mocktail/matchers/any.rb +27 -0
  121. data/lib/mocktail/sorbet/mocktail/matchers/base.rb +39 -0
  122. data/lib/mocktail/sorbet/mocktail/matchers/captor.rb +76 -0
  123. data/lib/mocktail/sorbet/mocktail/matchers/includes.rb +32 -0
  124. data/lib/mocktail/sorbet/mocktail/matchers/includes_hash.rb +12 -0
  125. data/lib/mocktail/sorbet/mocktail/matchers/includes_key.rb +12 -0
  126. data/lib/mocktail/sorbet/mocktail/matchers/includes_string.rb +12 -0
  127. data/lib/mocktail/sorbet/mocktail/matchers/is_a.rb +17 -0
  128. data/lib/mocktail/sorbet/mocktail/matchers/matches.rb +19 -0
  129. data/lib/mocktail/sorbet/mocktail/matchers/not.rb +17 -0
  130. data/lib/mocktail/sorbet/mocktail/matchers/numeric.rb +27 -0
  131. data/lib/mocktail/sorbet/mocktail/matchers/that.rb +32 -0
  132. data/lib/mocktail/sorbet/mocktail/matchers.rb +19 -0
  133. data/lib/mocktail/sorbet/mocktail/raises_neato_no_method_error.rb +93 -0
  134. data/lib/mocktail/sorbet/mocktail/records_demonstration.rb +43 -0
  135. data/lib/mocktail/sorbet/mocktail/registers_matcher.rb +65 -0
  136. data/lib/mocktail/sorbet/mocktail/registers_stubbing.rb +31 -0
  137. data/lib/mocktail/sorbet/mocktail/replaces_next.rb +55 -0
  138. data/lib/mocktail/sorbet/mocktail/replaces_type/redefines_new.rb +32 -0
  139. data/lib/mocktail/sorbet/mocktail/replaces_type/redefines_singleton_methods.rb +80 -0
  140. data/lib/mocktail/sorbet/mocktail/replaces_type/runs_sorbet_sig_blocks_before_replacement.rb +39 -0
  141. data/lib/mocktail/sorbet/mocktail/replaces_type.rb +36 -0
  142. data/lib/mocktail/sorbet/mocktail/resets_state.rb +14 -0
  143. data/lib/mocktail/sorbet/mocktail/share/bind.rb +18 -0
  144. data/lib/mocktail/sorbet/mocktail/share/cleans_backtrace.rb +22 -0
  145. data/lib/mocktail/sorbet/mocktail/share/creates_identifier.rb +39 -0
  146. data/lib/mocktail/sorbet/mocktail/share/determines_matching_calls.rb +72 -0
  147. data/lib/mocktail/sorbet/mocktail/share/stringifies_call.rb +84 -0
  148. data/lib/mocktail/sorbet/mocktail/share/stringifies_method_name.rb +16 -0
  149. data/lib/mocktail/sorbet/mocktail/simulates_argument_error/reconciles_args_with_params.rb +27 -0
  150. data/lib/mocktail/sorbet/mocktail/simulates_argument_error/recreates_message.rb +34 -0
  151. data/lib/mocktail/sorbet/mocktail/simulates_argument_error/transforms_params.rb +58 -0
  152. data/lib/mocktail/sorbet/mocktail/simulates_argument_error.rb +36 -0
  153. data/lib/mocktail/sorbet/mocktail/sorbet.rb +3 -0
  154. data/lib/mocktail/sorbet/mocktail/stringifies_method_signature.rb +53 -0
  155. data/lib/mocktail/sorbet/mocktail/typed.rb +5 -0
  156. data/lib/mocktail/sorbet/mocktail/value/cabinet.rb +91 -0
  157. data/lib/mocktail/sorbet/mocktail/value/call.rb +51 -0
  158. data/lib/mocktail/sorbet/mocktail/value/demo_config.rb +10 -0
  159. data/lib/mocktail/sorbet/mocktail/value/double.rb +10 -0
  160. data/lib/mocktail/sorbet/mocktail/value/double_data.rb +15 -0
  161. data/lib/mocktail/sorbet/mocktail/value/explanation.rb +68 -0
  162. data/lib/mocktail/sorbet/mocktail/value/explanation_data.rb +19 -0
  163. data/lib/mocktail/sorbet/mocktail/value/fake_method_data.rb +11 -0
  164. data/lib/mocktail/sorbet/mocktail/value/matcher_registry.rb +27 -0
  165. data/lib/mocktail/sorbet/mocktail/value/no_explanation_data.rb +20 -0
  166. data/lib/mocktail/sorbet/mocktail/value/signature.rb +35 -0
  167. data/lib/mocktail/sorbet/mocktail/value/stubbing.rb +26 -0
  168. data/lib/mocktail/sorbet/mocktail/value/top_shelf.rb +79 -0
  169. data/lib/mocktail/sorbet/mocktail/value/type_replacement.rb +11 -0
  170. data/lib/mocktail/sorbet/mocktail/value/type_replacement_data.rb +19 -0
  171. data/lib/mocktail/sorbet/mocktail/value/unsatisfying_call.rb +9 -0
  172. data/lib/mocktail/sorbet/mocktail/value/unsatisfying_call_explanation.rb +24 -0
  173. data/lib/mocktail/sorbet/mocktail/value.rb +19 -0
  174. data/lib/mocktail/sorbet/mocktail/verifies_call/finds_verifiable_calls.rb +21 -0
  175. data/lib/mocktail/sorbet/mocktail/verifies_call/raises_verification_error/gathers_calls_of_method.rb +15 -0
  176. data/lib/mocktail/sorbet/mocktail/verifies_call/raises_verification_error.rb +74 -0
  177. data/lib/mocktail/sorbet/mocktail/verifies_call.rb +37 -0
  178. data/lib/mocktail/sorbet/mocktail/version.rb +12 -0
  179. data/lib/mocktail/sorbet/mocktail.rb +154 -0
  180. data/lib/mocktail/sorbet.rb +1 -0
  181. data/lib/mocktail/stringifies_method_signature.rb +2 -0
  182. data/lib/mocktail/typed.rb +3 -0
  183. data/lib/mocktail/value/cabinet.rb +8 -1
  184. data/lib/mocktail/value/call.rb +44 -12
  185. data/lib/mocktail/value/demo_config.rb +6 -7
  186. data/lib/mocktail/value/double.rb +6 -7
  187. data/lib/mocktail/value/double_data.rb +11 -7
  188. data/lib/mocktail/value/explanation.rb +28 -3
  189. data/lib/mocktail/value/explanation_data.rb +14 -0
  190. data/lib/mocktail/value/fake_method_data.rb +7 -6
  191. data/lib/mocktail/value/matcher_registry.rb +2 -0
  192. data/lib/mocktail/value/no_explanation_data.rb +16 -0
  193. data/lib/mocktail/value/signature.rb +19 -27
  194. data/lib/mocktail/value/stubbing.rb +11 -12
  195. data/lib/mocktail/value/top_shelf.rb +5 -0
  196. data/lib/mocktail/value/type_replacement.rb +7 -8
  197. data/lib/mocktail/value/type_replacement_data.rb +10 -7
  198. data/lib/mocktail/value/unsatisfying_call.rb +5 -6
  199. data/lib/mocktail/value/unsatisfying_call_explanation.rb +18 -0
  200. data/lib/mocktail/value.rb +5 -2
  201. data/lib/mocktail/verifies_call/finds_verifiable_calls.rb +2 -0
  202. data/lib/mocktail/verifies_call/raises_verification_error/gathers_calls_of_method.rb +2 -0
  203. data/lib/mocktail/verifies_call/raises_verification_error.rb +2 -0
  204. data/lib/mocktail/verifies_call.rb +3 -0
  205. data/lib/mocktail/version.rb +8 -1
  206. data/lib/mocktail.rb +46 -5
  207. data/mocktail.gemspec +8 -4
  208. data/rbi/mocktail-pregenerated.rbi +1923 -0
  209. data/rbi/mocktail.rbi +77 -0
  210. data/rbi/sorbet-runtime.rbi +29 -0
  211. data/script/build +47 -0
  212. data/script/setup +17 -0
  213. data/script/spoom_me +7 -0
  214. data/script/strip_sigils +17 -0
  215. data/script/test +33 -0
  216. data/script/test_double_require_warnings +46 -0
  217. data/script/update +21 -0
  218. data/sorbet/config +12 -0
  219. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  220. data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
  221. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1083 -0
  222. data/sorbet/rbi/gems/docile@1.4.0.rbi +376 -0
  223. data/sorbet/rbi/gems/json@2.6.3.rbi +1533 -0
  224. data/sorbet/rbi/gems/language_server-protocol@3.17.0.3.rbi +14237 -0
  225. data/sorbet/rbi/gems/lint_roller@1.0.0.rbi +230 -0
  226. data/sorbet/rbi/gems/m@1.6.1.rbi +257 -0
  227. data/sorbet/rbi/gems/method_source@1.0.0.rbi +272 -0
  228. data/sorbet/rbi/gems/minitest@5.18.0.rbi +1491 -0
  229. data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  230. data/sorbet/rbi/gems/parallel@1.23.0.rbi +273 -0
  231. data/sorbet/rbi/gems/parser@3.2.2.1.rbi +7253 -0
  232. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +402 -0
  233. data/sorbet/rbi/gems/rake@13.0.6.rbi +3024 -0
  234. data/sorbet/rbi/gems/rbi@0.0.16.rbi +3049 -0
  235. data/sorbet/rbi/gems/regexp_parser@2.8.0.rbi +3748 -0
  236. data/sorbet/rbi/gems/rexml@3.2.5.rbi +4717 -0
  237. data/sorbet/rbi/gems/rubocop-ast@1.29.0.rbi +7003 -0
  238. data/sorbet/rbi/gems/rubocop-performance@1.18.0.rbi +8 -0
  239. data/sorbet/rbi/gems/rubocop-sorbet@0.7.0.rbi +1043 -0
  240. data/sorbet/rbi/gems/rubocop@1.52.0.rbi +55500 -0
  241. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1317 -0
  242. data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +216 -0
  243. data/sorbet/rbi/gems/simplecov@0.22.0.rbi +2148 -0
  244. data/sorbet/rbi/gems/simplecov_json_formatter@0.1.4.rbi +8 -0
  245. data/sorbet/rbi/gems/spoom@1.2.1.rbi +2536 -0
  246. data/sorbet/rbi/gems/standard-custom@1.0.1.rbi +63 -0
  247. data/sorbet/rbi/gems/standard-performance@1.1.0.rbi +8 -0
  248. data/sorbet/rbi/gems/standard@1.29.0.rbi +850 -0
  249. data/sorbet/rbi/gems/tapioca@0.11.6.rbi +3301 -0
  250. data/sorbet/rbi/gems/thor@1.2.2.rbi +3965 -0
  251. data/sorbet/rbi/gems/unicode-display_width@2.4.2.rbi +65 -0
  252. data/sorbet/rbi/gems/unparser@0.6.7.rbi +4524 -0
  253. data/sorbet/rbi/gems/yard-sorbet@0.8.1.rbi +428 -0
  254. data/sorbet/rbi/gems/yard@0.9.34.rbi +18219 -0
  255. data/sorbet/tapioca/config.yml +13 -0
  256. data/sorbet/tapioca/require.rb +4 -0
  257. data/spoom_data/05f0c49.json +1 -0
  258. data/spoom_data/1657012.json +1 -0
  259. data/spoom_data/167434b.json +1 -0
  260. data/spoom_data/1d0ba5b.json +1 -0
  261. data/spoom_data/1ffa724.json +1 -0
  262. data/spoom_data/2129b3d.json +1 -0
  263. data/spoom_data/24b1c92.json +1 -0
  264. data/spoom_data/2c46aee.json +1 -0
  265. data/spoom_data/305ec0b.json +1 -0
  266. data/spoom_data/30e9528.json +1 -0
  267. data/spoom_data/4638cd5.json +1 -0
  268. data/spoom_data/47c7dad.json +1 -0
  269. data/spoom_data/4b1edef.json +1 -0
  270. data/spoom_data/4de157f.json +1 -0
  271. data/spoom_data/526e7db.json +1 -0
  272. data/spoom_data/5d093b9.json +1 -0
  273. data/spoom_data/5db3b43.json +1 -0
  274. data/spoom_data/5fe2a65.json +1 -0
  275. data/spoom_data/6891312.json +1 -0
  276. data/spoom_data/6b0fef4.json +1 -0
  277. data/spoom_data/6b83d12.json +1 -0
  278. data/spoom_data/74c83c2.json +1 -0
  279. data/spoom_data/7644ff4.json +1 -0
  280. data/spoom_data/79054db.json +1 -0
  281. data/spoom_data/814e515.json +1 -0
  282. data/spoom_data/88c3b60.json +1 -0
  283. data/spoom_data/8bd4b6e.json +1 -0
  284. data/spoom_data/93f8153.json +1 -0
  285. data/spoom_data/95242fe.json +1 -0
  286. data/spoom_data/97f4c09.json +1 -0
  287. data/spoom_data/a13d150.json +1 -0
  288. data/spoom_data/a17f215.json +1 -0
  289. data/spoom_data/b705a9d.json +1 -0
  290. data/spoom_data/ba19195.json +1 -0
  291. data/spoom_data/bef51ca.json +1 -0
  292. data/spoom_data/dbb595a.json +1 -0
  293. data/spoom_data/e8fab92.json +1 -0
  294. data/spoom_data/f166c87.json +1 -0
  295. data/spoom_data/f57992d.json +1 -0
  296. data/spoom_data/f5a1e40.json +1 -0
  297. data/spoom_data/f6d6431.json +1 -0
  298. data/spoom_data/f72b67c.json +1 -0
  299. data/spoom_data/fc2f231.json +1 -0
  300. data/spoom_report.html +1248 -0
  301. data/src/mocktail/collects_calls.rb +18 -0
  302. data/src/mocktail/debug.rb +55 -0
  303. data/src/mocktail/dsl.rb +46 -0
  304. data/src/mocktail/errors.rb +19 -0
  305. data/src/mocktail/explains_nils.rb +41 -0
  306. data/src/mocktail/explains_thing.rb +137 -0
  307. data/src/mocktail/grabs_original_method_parameters.rb +33 -0
  308. data/src/mocktail/handles_dry_call/fulfills_stubbing/describes_unsatisfied_stubbing.rb +27 -0
  309. data/src/mocktail/handles_dry_call/fulfills_stubbing/finds_satisfaction.rb +24 -0
  310. data/src/mocktail/handles_dry_call/fulfills_stubbing.rb +45 -0
  311. data/src/mocktail/handles_dry_call/logs_call.rb +12 -0
  312. data/src/mocktail/handles_dry_call/validates_arguments.rb +45 -0
  313. data/src/mocktail/handles_dry_call.rb +25 -0
  314. data/src/mocktail/handles_dry_new_call.rb +42 -0
  315. data/src/mocktail/imitates_type/ensures_imitation_support.rb +16 -0
  316. data/src/mocktail/imitates_type/makes_double/declares_dry_class/reconstructs_call.rb +73 -0
  317. data/src/mocktail/imitates_type/makes_double/declares_dry_class.rb +136 -0
  318. data/src/mocktail/imitates_type/makes_double/gathers_fakeable_instance_methods.rb +28 -0
  319. data/src/mocktail/imitates_type/makes_double.rb +29 -0
  320. data/src/mocktail/imitates_type.rb +29 -0
  321. data/src/mocktail/initialize_based_on_type_system_mode_switching.rb +11 -0
  322. data/src/mocktail/initializes_mocktail.rb +25 -0
  323. data/src/mocktail/matcher_presentation.rb +21 -0
  324. data/src/mocktail/matchers/any.rb +27 -0
  325. data/src/mocktail/matchers/base.rb +39 -0
  326. data/src/mocktail/matchers/captor.rb +76 -0
  327. data/src/mocktail/matchers/includes.rb +32 -0
  328. data/src/mocktail/matchers/includes_hash.rb +12 -0
  329. data/src/mocktail/matchers/includes_key.rb +12 -0
  330. data/src/mocktail/matchers/includes_string.rb +12 -0
  331. data/src/mocktail/matchers/is_a.rb +17 -0
  332. data/src/mocktail/matchers/matches.rb +19 -0
  333. data/src/mocktail/matchers/not.rb +17 -0
  334. data/src/mocktail/matchers/numeric.rb +27 -0
  335. data/src/mocktail/matchers/that.rb +32 -0
  336. data/src/mocktail/matchers.rb +19 -0
  337. data/src/mocktail/raises_neato_no_method_error.rb +93 -0
  338. data/src/mocktail/records_demonstration.rb +43 -0
  339. data/src/mocktail/registers_matcher.rb +65 -0
  340. data/src/mocktail/registers_stubbing.rb +31 -0
  341. data/src/mocktail/replaces_next.rb +55 -0
  342. data/src/mocktail/replaces_type/redefines_new.rb +32 -0
  343. data/src/mocktail/replaces_type/redefines_singleton_methods.rb +80 -0
  344. data/src/mocktail/replaces_type/runs_sorbet_sig_blocks_before_replacement.rb +39 -0
  345. data/src/mocktail/replaces_type.rb +36 -0
  346. data/src/mocktail/resets_state.rb +14 -0
  347. data/src/mocktail/share/bind.rb +18 -0
  348. data/src/mocktail/share/cleans_backtrace.rb +22 -0
  349. data/src/mocktail/share/creates_identifier.rb +39 -0
  350. data/src/mocktail/share/determines_matching_calls.rb +72 -0
  351. data/src/mocktail/share/stringifies_call.rb +84 -0
  352. data/src/mocktail/share/stringifies_method_name.rb +16 -0
  353. data/src/mocktail/simulates_argument_error/reconciles_args_with_params.rb +27 -0
  354. data/src/mocktail/simulates_argument_error/recreates_message.rb +34 -0
  355. data/src/mocktail/simulates_argument_error/transforms_params.rb +58 -0
  356. data/src/mocktail/simulates_argument_error.rb +36 -0
  357. data/src/mocktail/sorbet.rb +3 -0
  358. data/src/mocktail/stringifies_method_signature.rb +53 -0
  359. data/src/mocktail/typed.rb +5 -0
  360. data/src/mocktail/value/cabinet.rb +91 -0
  361. data/src/mocktail/value/call.rb +51 -0
  362. data/src/mocktail/value/demo_config.rb +10 -0
  363. data/src/mocktail/value/double.rb +10 -0
  364. data/src/mocktail/value/double_data.rb +15 -0
  365. data/src/mocktail/value/explanation.rb +68 -0
  366. data/src/mocktail/value/explanation_data.rb +19 -0
  367. data/src/mocktail/value/fake_method_data.rb +11 -0
  368. data/src/mocktail/value/matcher_registry.rb +27 -0
  369. data/src/mocktail/value/no_explanation_data.rb +20 -0
  370. data/src/mocktail/value/signature.rb +35 -0
  371. data/src/mocktail/value/stubbing.rb +26 -0
  372. data/src/mocktail/value/top_shelf.rb +79 -0
  373. data/src/mocktail/value/type_replacement.rb +11 -0
  374. data/src/mocktail/value/type_replacement_data.rb +19 -0
  375. data/src/mocktail/value/unsatisfying_call.rb +9 -0
  376. data/src/mocktail/value/unsatisfying_call_explanation.rb +24 -0
  377. data/src/mocktail/value.rb +19 -0
  378. data/src/mocktail/verifies_call/finds_verifiable_calls.rb +21 -0
  379. data/src/mocktail/verifies_call/raises_verification_error/gathers_calls_of_method.rb +15 -0
  380. data/src/mocktail/verifies_call/raises_verification_error.rb +74 -0
  381. data/src/mocktail/verifies_call.rb +37 -0
  382. data/src/mocktail/version.rb +12 -0
  383. data/src/mocktail.rb +154 -0
  384. data/sub_projects/rbi_generator/Gemfile +4 -0
  385. data/sub_projects/rbi_generator/Gemfile.lock +63 -0
  386. data/sub_projects/rbi_generator/sorbet/rbi/gems/.gitattributes +1 -0
  387. data/sub_projects/sorbet_user/Gemfile +13 -0
  388. data/sub_projects/sorbet_user/Gemfile.lock +126 -0
  389. data/sub_projects/sorbet_user/Rakefile +10 -0
  390. data/sub_projects/sorbet_user/bin/tapioca +27 -0
  391. data/sub_projects/sorbet_user/rbi/mocktail.rbi +9 -0
  392. data/sub_projects/sorbet_user/script/test +13 -0
  393. data/sub_projects/sorbet_user/sorbet/config +4 -0
  394. data/sub_projects/sorbet_user/sorbet/rbi/gems/.gitattributes +1 -0
  395. data/sub_projects/sorbet_user/sorbet/rbi/gems/ast@2.4.3.rbi +585 -0
  396. data/sub_projects/sorbet_user/sorbet/rbi/gems/benchmark@0.4.0.rbi +618 -0
  397. data/sub_projects/sorbet_user/sorbet/rbi/gems/erubi@1.13.1.rbi +155 -0
  398. data/sub_projects/sorbet_user/sorbet/rbi/gems/json@2.21.2.rbi +2140 -0
  399. data/sub_projects/sorbet_user/sorbet/rbi/gems/language_server-protocol@3.17.0.4.rbi +14244 -0
  400. data/sub_projects/sorbet_user/sorbet/rbi/gems/lint_roller@1.1.0.rbi +239 -0
  401. data/sub_projects/sorbet_user/sorbet/rbi/gems/logger@1.7.0.rbi +963 -0
  402. data/sub_projects/sorbet_user/sorbet/rbi/gems/m@1.6.2.rbi +258 -0
  403. data/sub_projects/sorbet_user/sorbet/rbi/gems/method_source@1.1.0.rbi +9 -0
  404. data/sub_projects/sorbet_user/sorbet/rbi/gems/minitest@5.27.0.rbi +1549 -0
  405. data/sub_projects/sorbet_user/sorbet/rbi/gems/mocktail@3.0.0.rbi +1923 -0
  406. data/sub_projects/sorbet_user/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  407. data/sub_projects/sorbet_user/sorbet/rbi/gems/parallel@1.26.3.rbi +291 -0
  408. data/sub_projects/sorbet_user/sorbet/rbi/gems/parser@3.3.7.4.rbi +5451 -0
  409. data/sub_projects/sorbet_user/sorbet/rbi/gems/prism@1.4.0.rbi +41732 -0
  410. data/sub_projects/sorbet_user/sorbet/rbi/gems/racc@1.8.1.rbi +164 -0
  411. data/sub_projects/sorbet_user/sorbet/rbi/gems/rainbow@3.1.1.rbi +402 -0
  412. data/sub_projects/sorbet_user/sorbet/rbi/gems/rake@13.2.1.rbi +3028 -0
  413. data/sub_projects/sorbet_user/sorbet/rbi/gems/rbi@0.3.1.rbi +6599 -0
  414. data/sub_projects/sorbet_user/sorbet/rbi/gems/rbs@3.9.2.rbi +6976 -0
  415. data/sub_projects/sorbet_user/sorbet/rbi/gems/regexp_parser@2.10.0.rbi +3795 -0
  416. data/sub_projects/sorbet_user/sorbet/rbi/gems/rubocop-ast@1.43.0.rbi +7717 -0
  417. data/sub_projects/sorbet_user/sorbet/rbi/gems/rubocop-performance@1.24.0.rbi +9 -0
  418. data/sub_projects/sorbet_user/sorbet/rbi/gems/rubocop-sorbet@0.9.0.rbi +9 -0
  419. data/sub_projects/sorbet_user/sorbet/rbi/gems/rubocop@1.73.2.rbi +61306 -0
  420. data/sub_projects/sorbet_user/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1317 -0
  421. data/sub_projects/sorbet_user/sorbet/rbi/gems/sorbet-eraser@0.3.1.rbi +8 -0
  422. data/sub_projects/sorbet_user/sorbet/rbi/gems/spoom@1.6.1.rbi +7274 -0
  423. data/sub_projects/sorbet_user/sorbet/rbi/gems/standard-custom@1.0.2.rbi +8 -0
  424. data/sub_projects/sorbet_user/sorbet/rbi/gems/standard-performance@1.7.0.rbi +9 -0
  425. data/sub_projects/sorbet_user/sorbet/rbi/gems/standard-sorbet@0.0.3.rbi +53 -0
  426. data/sub_projects/sorbet_user/sorbet/rbi/gems/standard@1.47.0.rbi +935 -0
  427. data/sub_projects/sorbet_user/sorbet/rbi/gems/tapioca@0.16.11.rbi +3628 -0
  428. data/sub_projects/sorbet_user/sorbet/rbi/gems/thor@1.3.2.rbi +4378 -0
  429. data/sub_projects/sorbet_user/sorbet/rbi/gems/unicode-display_width@3.1.4.rbi +132 -0
  430. data/sub_projects/sorbet_user/sorbet/rbi/gems/unicode-emoji@4.2.0.rbi +254 -0
  431. data/sub_projects/sorbet_user/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +435 -0
  432. data/sub_projects/sorbet_user/sorbet/rbi/gems/yard@0.9.45.rbi +18916 -0
  433. data/sub_projects/sorbet_user/sorbet/tapioca/config.yml +13 -0
  434. data/sub_projects/sorbet_user/sorbet/tapioca/require.rb +4 -0
  435. data/sub_projects/sorbet_user/test/ensure_type_safety_test.rb +21 -0
  436. data/sub_projects/sorbet_user/test/paint_by_number_test.rb +113 -0
  437. data/sub_projects/sorbet_user/test/sorbet_test.rb +289 -0
  438. data/sub_projects/sorbet_user/test/test_helper.rb +35 -0
  439. data/sub_projects/untyped_user/Gemfile +4 -0
  440. data/sub_projects/untyped_user/Gemfile.lock +25 -0
  441. data/sub_projects/untyped_user/antitype_test.rb +54 -0
  442. data/sub_projects/untyped_user/script/test +5 -0
  443. metadata +392 -9
@@ -3,6 +3,8 @@ require_relative "fulfills_stubbing/describes_unsatisfied_stubbing"
3
3
 
4
4
  module Mocktail
5
5
  class FulfillsStubbing
6
+ extend T::Sig
7
+
6
8
  def initialize
7
9
  @finds_satisfaction = FindsSatisfaction.new
8
10
  @describes_unsatisfied_stubbing = DescribesUnsatisfiedStubbing.new
@@ -1,5 +1,7 @@
1
1
  module Mocktail
2
2
  class LogsCall
3
+ extend T::Sig
4
+
3
5
  def log(dry_call)
4
6
  Mocktail.cabinet.store_call(dry_call)
5
7
  end
@@ -1,5 +1,7 @@
1
1
  module Mocktail
2
2
  class ValidatesArguments
3
+ extend T::Sig
4
+
3
5
  def self.disable!
4
6
  Thread.current[:mocktail_arity_validation_disabled] = true
5
7
  end
@@ -9,16 +11,16 @@ module Mocktail
9
11
  end
10
12
 
11
13
  def self.disabled?
12
- Thread.current[:mocktail_arity_validation_disabled]
14
+ !!Thread.current[:mocktail_arity_validation_disabled]
13
15
  end
14
16
 
15
17
  def self.optional(disable, &blk)
16
18
  return blk.call unless disable
17
19
 
18
20
  disable!
19
- blk.call.tap do
20
- enable!
21
- end
21
+ ret = blk.call
22
+ enable!
23
+ ret
22
24
  end
23
25
 
24
26
  def initialize
@@ -4,6 +4,8 @@ require_relative "handles_dry_call/validates_arguments"
4
4
 
5
5
  module Mocktail
6
6
  class HandlesDryCall
7
+ extend T::Sig
8
+
7
9
  def initialize
8
10
  @validates_arguments = ValidatesArguments.new
9
11
  @logs_call = LogsCall.new
@@ -1,5 +1,7 @@
1
1
  module Mocktail
2
2
  class HandlesDryNewCall
3
+ extend T::Sig
4
+
3
5
  def initialize
4
6
  @validates_arguments = ValidatesArguments.new
5
7
  @logs_call = LogsCall.new
@@ -1,5 +1,7 @@
1
1
  module Mocktail
2
2
  class EnsuresImitationSupport
3
+ extend T::Sig
4
+
3
5
  def ensure(type)
4
6
  unless type.is_a?(Class) || type.is_a?(Module)
5
7
  raise UnsupportedMocktail.new <<~MSG.tr("\n", " ")
@@ -1,5 +1,7 @@
1
1
  module Mocktail
2
2
  class ReconstructsCall
3
+ extend T::Sig
4
+
3
5
  def reconstruct(double:, call_binding:, default_args:, dry_class:, type:, method:, original_method:, signature:)
4
6
  Call.new(
5
7
  singleton: false,
@@ -37,7 +39,8 @@ module Mocktail
37
39
  def non_default_args(params, default_args)
38
40
  named_args = params.allowed
39
41
  .reject { |p| default_args&.key?(p) }
40
- rest_arg = if params.rest && !default_args&.key?(params.rest)
42
+ rest_param = params.rest
43
+ rest_arg = if rest_param && !default_args&.key?(rest_param)
41
44
  params.rest
42
45
  end
43
46
 
@@ -2,60 +2,72 @@ require_relative "declares_dry_class/reconstructs_call"
2
2
 
3
3
  module Mocktail
4
4
  class DeclaresDryClass
5
+ extend T::Sig
6
+
7
+ DEFAULT_ANCESTORS = Class.new(Object).ancestors[1..]
8
+
5
9
  def initialize
6
10
  @raises_neato_no_method_error = RaisesNeatoNoMethodError.new
7
11
  @transforms_params = TransformsParams.new
8
12
  @stringifies_method_signature = StringifiesMethodSignature.new
13
+ @grabs_original_method_parameters = GrabsOriginalMethodParameters.new
9
14
  end
10
15
 
11
16
  def declare(type, instance_methods)
12
17
  dry_class = Class.new(Object) {
13
- include type if type.instance_of?(Module)
18
+ include type if type.is_a?(Module) && !type.is_a?(Class)
14
19
 
15
- def initialize(*args, **kwargs, &blk)
20
+ define_method :initialize do |*args, **kwargs, &blk|
16
21
  end
17
22
 
18
- define_method :is_a?, ->(thing) {
19
- type.ancestors.include?(thing)
20
- }
21
- alias_method :kind_of?, :is_a?
23
+ [:is_a?, :kind_of?].each do |method_name|
24
+ define_method method_name, ->(thing) {
25
+ # Mocktails extend from Object, so share the same ancestors, plus the passed type
26
+ [type, *DEFAULT_ANCESTORS].include?(thing)
27
+ }
28
+ end
22
29
 
23
- if type.instance_of?(Class)
30
+ if type.is_a?(Class)
24
31
  define_method :instance_of?, ->(thing) {
25
32
  type == thing
26
33
  }
27
34
  end
28
35
  }
29
36
 
30
- # These have special implementations, but if the user defines
31
- # any of them on the object itself, then they'll be replaced with normal
32
- # mocked methods. YMMV
37
+ add_more_methods!(dry_class, type, instance_methods)
38
+
39
+ dry_class # This is all fake! That's the whole point—it's not a real Foo, it's just some new class that quacks like a Foo
40
+ end
41
+
42
+ private
43
+
44
+ # These have special implementations, but if the user defines
45
+ # any of them on the object itself, then they'll be replaced with normal
46
+ # mocked methods. YMMV
47
+
48
+ def add_more_methods!(dry_class, type, instance_methods)
33
49
  add_stringify_methods!(dry_class, :to_s, type, instance_methods)
34
50
  add_stringify_methods!(dry_class, :inspect, type, instance_methods)
35
51
  define_method_missing_errors!(dry_class, type, instance_methods)
36
52
 
37
53
  define_double_methods!(dry_class, type, instance_methods)
38
-
39
- dry_class
40
54
  end
41
55
 
42
- private
43
-
44
56
  def define_double_methods!(dry_class, type, instance_methods)
45
- instance_methods.each do |method|
46
- dry_class.undef_method(method) if dry_class.method_defined?(method)
47
- parameters = type.instance_method(method).parameters
57
+ instance_methods.each do |method_name|
58
+ dry_class.undef_method(method_name) if dry_class.method_defined?(method_name)
59
+ parameters = @grabs_original_method_parameters.grab(type.instance_method(method_name))
48
60
  signature = @transforms_params.transform(Call.new, params: parameters)
49
61
  method_signature = @stringifies_method_signature.stringify(signature)
50
62
  __mocktail_closure = {
51
63
  dry_class: dry_class,
52
64
  type: type,
53
- method: method,
54
- original_method: type.instance_method(method),
65
+ method: method_name,
66
+ original_method: type.instance_method(method_name),
55
67
  signature: signature
56
68
  }
57
69
 
58
- dry_class.define_method method,
70
+ dry_class.define_method method_name,
59
71
  eval(<<-RUBBY, binding, __FILE__, __LINE__ + 1) # standard:disable Security/Eval
60
72
  ->#{method_signature} do
61
73
  ::Mocktail::Debug.guard_against_mocktail_accidentally_calling_mocks_if_debugging!
@@ -1,5 +1,7 @@
1
1
  module Mocktail
2
2
  class GathersFakeableInstanceMethods
3
+ extend T::Sig
4
+
3
5
  def gather(type)
4
6
  methods = type.instance_methods + [
5
7
  (:respond_to_missing? if type.private_method_defined?(:respond_to_missing?))
@@ -3,6 +3,8 @@ require_relative "makes_double/gathers_fakeable_instance_methods"
3
3
 
4
4
  module Mocktail
5
5
  class MakesDouble
6
+ extend T::Sig
7
+
6
8
  def initialize
7
9
  @declares_dry_class = DeclaresDryClass.new
8
10
  @gathers_fakeable_instance_methods = GathersFakeableInstanceMethods.new
@@ -11,6 +13,7 @@ module Mocktail
11
13
  def make(type)
12
14
  dry_methods = @gathers_fakeable_instance_methods.gather(type)
13
15
  dry_type = @declares_dry_class.declare(type, dry_methods)
16
+
14
17
  Double.new(
15
18
  original_type: type,
16
19
  dry_type: dry_type,
@@ -3,8 +3,10 @@ require_relative "imitates_type/makes_double"
3
3
 
4
4
  module Mocktail
5
5
  class ImitatesType
6
+ extend T::Sig
7
+ extend T::Generic
8
+
6
9
  def initialize
7
- @top_shelf = TopShelf.instance
8
10
  @ensures_imitation_support = EnsuresImitationSupport.new
9
11
  @makes_double = MakesDouble.new
10
12
  end
@@ -0,0 +1,9 @@
1
+ require_relative "typed"
2
+
3
+ # Constant boolean, so won't statically type-check, but `T.unsafe` can't be used
4
+ # because we haven't required sorbet-runtime yet
5
+ if eval("Mocktail::TYPED", binding, __FILE__, __LINE__)
6
+ require "sorbet-runtime"
7
+ else
8
+ require "#{Gem.loaded_specs["sorbet-eraser"].gem_dir}/lib/t"
9
+ end
@@ -1,9 +1,14 @@
1
1
  module Mocktail
2
2
  class InitializesMocktail
3
+ extend T::Sig
4
+
3
5
  def init
4
6
  [
5
7
  Mocktail::Matchers::Any,
6
8
  Mocktail::Matchers::Includes,
9
+ Mocktail::Matchers::IncludesString,
10
+ Mocktail::Matchers::IncludesKey,
11
+ Mocktail::Matchers::IncludesHash,
7
12
  Mocktail::Matchers::IsA,
8
13
  Mocktail::Matchers::Matches,
9
14
  Mocktail::Matchers::Not,
@@ -1,12 +1,14 @@
1
1
  module Mocktail
2
2
  class MatcherPresentation
3
+ extend T::Sig
4
+
3
5
  def respond_to_missing?(name, include_private = false)
4
6
  !!MatcherRegistry.instance.get(name) || super
5
7
  end
6
8
 
7
- def method_missing(name, *args, **kwargs, &blk)
9
+ def method_missing(name, *args, **kwargs, &blk) # standard:disable Style/ArgumentsForwarding
8
10
  if (matcher = MatcherRegistry.instance.get(name))
9
- matcher.new(*args, **kwargs, &blk)
11
+ matcher.new(*args, **kwargs, &blk) # standard:disable Style/ArgumentsForwarding
10
12
  else
11
13
  super
12
14
  end
@@ -1,12 +1,13 @@
1
1
  module Mocktail::Matchers
2
2
  class Any < Base
3
+ extend T::Sig
4
+
3
5
  def self.matcher_name
4
6
  :any
5
7
  end
6
8
 
7
- # Change this comment to a descriptive one once this is merged:
8
- # https://github.com/rubocop/rubocop/pull/10551
9
- def initialize # standard:disable Style/RedundantInitialize
9
+ def initialize
10
+ # Empty initialize is necessary b/c Base default expects an argument
10
11
  end
11
12
 
12
13
  def match?(actual)
@@ -1,17 +1,25 @@
1
1
  module Mocktail::Matchers
2
2
  class Base
3
+ extend T::Sig
4
+ extend T::Helpers
5
+
6
+ if Mocktail::TYPED && T::Private::RuntimeLevels.default_checked_level != :never
7
+
8
+ end
9
+
3
10
  # Custom matchers can receive any args, kwargs, or block they want. Usually
4
11
  # single-argument, though, so that's defaulted here and in #insepct
12
+
5
13
  def initialize(expected)
6
14
  @expected = expected
7
15
  end
8
16
 
9
17
  def self.matcher_name
10
- raise Mocktail::Error.new("The `matcher_name` class method must return a valid method name")
18
+ raise Mocktail::InvalidMatcherError.new("The `matcher_name` class method must return a valid method name")
11
19
  end
12
20
 
13
21
  def match?(actual)
14
- raise Mocktail::Error.new("Matchers must implement `match?(argument)`")
22
+ raise Mocktail::InvalidMatcherError.new("Matchers must implement `match?(argument)`")
15
23
  end
16
24
 
17
25
  def inspect
@@ -13,7 +13,11 @@ module Mocktail::Matchers
13
13
  # See Mockito, which is the earliest implementation I know of:
14
14
  # https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Captor.html
15
15
  class Captor
16
+ extend T::Sig
17
+
16
18
  class Capture < Mocktail::Matchers::Base
19
+ extend T::Sig
20
+
17
21
  def self.matcher_name
18
22
  :capture
19
23
  end
@@ -40,7 +44,12 @@ module Mocktail::Matchers
40
44
  end
41
45
  end
42
46
 
47
+ # This T.untyped is intentional. Even though a Capture is surely returned,
48
+ # in order for a verification demonstration to pass its own type check,
49
+ # it needs to think it's being returned whatever parameter is expected
50
+
43
51
  attr_reader :capture
52
+
44
53
  def initialize
45
54
  @capture = Capture.new
46
55
  end
@@ -1,5 +1,7 @@
1
1
  module Mocktail::Matchers
2
2
  class Includes < Base
3
+ extend T::Sig
4
+
3
5
  def self.matcher_name
4
6
  :includes
5
7
  end
@@ -0,0 +1,9 @@
1
+ module Mocktail::Matchers
2
+ class IncludesHash < Includes
3
+ extend T::Sig
4
+
5
+ def self.matcher_name
6
+ :includes_hash
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Mocktail::Matchers
2
+ class IncludesKey < Includes
3
+ extend T::Sig
4
+
5
+ def self.matcher_name
6
+ :includes_key
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Mocktail::Matchers
2
+ class IncludesString < Includes
3
+ extend T::Sig
4
+
5
+ def self.matcher_name
6
+ :includes_string
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,7 @@
1
1
  module Mocktail::Matchers
2
2
  class IsA < Base
3
+ extend T::Sig
4
+
3
5
  def self.matcher_name
4
6
  :is_a
5
7
  end
@@ -1,5 +1,7 @@
1
1
  module Mocktail::Matchers
2
2
  class Matches < Base
3
+ extend T::Sig
4
+
3
5
  def self.matcher_name
4
6
  :matches
5
7
  end
@@ -1,5 +1,7 @@
1
1
  module Mocktail::Matchers
2
2
  class Not < Base
3
+ extend T::Sig
4
+
3
5
  def self.matcher_name
4
6
  :not
5
7
  end
@@ -1,16 +1,17 @@
1
1
  module Mocktail::Matchers
2
2
  class Numeric < Base
3
+ extend T::Sig
4
+
3
5
  def self.matcher_name
4
6
  :numeric
5
7
  end
6
8
 
7
- # Change this comment to a descriptive one once this is merged:
8
- # https://github.com/rubocop/rubocop/pull/10551
9
- def initialize # standard:disable Style/RedundantInitialize
9
+ def initialize
10
+ # Empty initialize is necessary b/c Base default expects an argument
10
11
  end
11
12
 
12
13
  def match?(actual)
13
- [Integer, Float, (BigDecimal if defined?(BigDecimal))].include?(actual.class)
14
+ actual.is_a?(::Numeric)
14
15
  end
15
16
 
16
17
  def inspect
@@ -1,5 +1,7 @@
1
1
  module Mocktail::Matchers
2
2
  class That < Base
3
+ extend T::Sig
4
+
3
5
  def self.matcher_name
4
6
  :that
5
7
  end
@@ -7,6 +7,9 @@ require_relative "matchers/base"
7
7
  require_relative "matchers/any"
8
8
  require_relative "matchers/captor"
9
9
  require_relative "matchers/includes"
10
+ require_relative "matchers/includes_string"
11
+ require_relative "matchers/includes_hash"
12
+ require_relative "matchers/includes_key"
10
13
  require_relative "matchers/is_a"
11
14
  require_relative "matchers/matches"
12
15
  require_relative "matchers/not"
@@ -4,6 +4,8 @@ require_relative "share/creates_identifier"
4
4
 
5
5
  module Mocktail
6
6
  class RaisesNeatoNoMethodError
7
+ extend T::Sig
8
+
7
9
  def initialize
8
10
  @stringifies_call = StringifiesCall.new
9
11
  @stringifies_method_name = StringifiesMethodName.new
@@ -1,5 +1,7 @@
1
1
  module Mocktail
2
2
  class RecordsDemonstration
3
+ extend T::Sig
4
+
3
5
  def record(demonstration, demo_config)
4
6
  cabinet = Mocktail.cabinet
5
7
  prior_call_count = Mocktail.cabinet.calls.dup.size
@@ -1,5 +1,11 @@
1
1
  module Mocktail
2
2
  class RegistersMatcher
3
+ extend T::Sig
4
+
5
+ def initialize
6
+ @grabs_original_method_parameters = GrabsOriginalMethodParameters.new
7
+ end
8
+
3
9
  def register(matcher_type)
4
10
  if invalid_type?(matcher_type)
5
11
  raise InvalidMatcherError.new <<~MSG.tr("\n", " ")
@@ -32,12 +38,11 @@ module Mocktail
32
38
  return true unless matcher_type.respond_to?(:matcher_name)
33
39
  name = matcher_type.matcher_name
34
40
 
35
- !(name.is_a?(String) || name.is_a?(Symbol)) ||
36
- name.to_sym.inspect.start_with?(":\"")
41
+ !name.respond_to?(:to_sym) || name.to_sym.inspect.start_with?(":\"")
37
42
  end
38
43
 
39
44
  def invalid_match?(matcher_type)
40
- params = matcher_type.instance_method(:match?).parameters
45
+ params = @grabs_original_method_parameters.grab(matcher_type.instance_method(:match?))
41
46
  params.size > 1 || ![:req, :opt].include?(params.first[0])
42
47
  rescue NameError
43
48
  true
@@ -2,6 +2,8 @@ require_relative "records_demonstration"
2
2
 
3
3
  module Mocktail
4
4
  class RegistersStubbing
5
+ extend T::Sig
6
+
5
7
  def initialize
6
8
  @records_demonstration = RecordsDemonstration.new
7
9
  end
@@ -1,11 +1,17 @@
1
1
  module Mocktail
2
2
  class ReplacesNext
3
+ extend T::Sig
4
+
3
5
  def initialize
4
6
  @top_shelf = TopShelf.instance
5
7
  @redefines_new = RedefinesNew.new
6
8
  @imitates_type = ImitatesType.new
7
9
  end
8
10
 
11
+ def replace_once(type)
12
+ replace(type, 1).fetch(0)
13
+ end
14
+
9
15
  def replace(type, count)
10
16
  raise UnsupportedMocktail.new("Mocktail.of_next() only supports classes") unless type.is_a?(Class)
11
17
 
@@ -30,7 +36,7 @@ module Mocktail
30
36
  }
31
37
  end
32
38
 
33
- (mocktails.size == 1) ? mocktails.first : mocktails
39
+ mocktails
34
40
  end
35
41
  end
36
42
  end
@@ -1,5 +1,7 @@
1
1
  module Mocktail
2
2
  class RedefinesNew
3
+ extend T::Sig
4
+
3
5
  def initialize
4
6
  @handles_dry_new_call = HandlesDryNewCall.new
5
7
  end
@@ -13,7 +15,7 @@ module Mocktail
13
15
  handles_dry_new_call = @handles_dry_new_call
14
16
  type.define_singleton_method :new, ->(*args, **kwargs, &block) {
15
17
  if TopShelf.instance.new_replaced?(type) ||
16
- TopShelf.instance.of_next_registered?(type)
18
+ (type.is_a?(Class) && TopShelf.instance.of_next_registered?(type))
17
19
  handles_dry_new_call.handle(type, args, kwargs, block)
18
20
  else
19
21
  type_replacement.original_new.call(*args, **kwargs, &block)
@@ -1,5 +1,7 @@
1
1
  module Mocktail
2
2
  class RedefinesSingletonMethods
3
+ extend T::Sig
4
+
3
5
  def initialize
4
6
  @handles_dry_call = HandlesDryCall.new
5
7
  end
@@ -10,11 +12,11 @@ module Mocktail
10
12
 
11
13
  type_replacement.original_methods = type.singleton_methods.map { |name|
12
14
  type.method(name)
13
- } - [type_replacement.replacement_new]
15
+ }.reject { |method| sorbet_method_hook?(method) } - [type_replacement.replacement_new]
14
16
 
15
17
  declare_singleton_method_missing_errors!(type)
16
18
  handles_dry_call = @handles_dry_call
17
- type_replacement.replacement_methods = type_replacement.original_methods.map { |original_method|
19
+ type_replacement.replacement_methods = type_replacement.original_methods&.map { |original_method|
18
20
  type.singleton_class.send(:undef_method, original_method.name)
19
21
  type.define_singleton_method original_method.name, ->(*args, **kwargs, &block) {
20
22
  if TopShelf.instance.singleton_methods_replaced?(type)
@@ -58,5 +60,15 @@ module Mocktail
58
60
  )
59
61
  }
60
62
  end
63
+
64
+ private
65
+
66
+ def sorbet_method_hook?(method)
67
+ [
68
+ T::Sig,
69
+ T::Private::Methods::MethodHooks,
70
+ T::Private::Methods::SingletonMethodHooks
71
+ ].include?(method.owner)
72
+ end
61
73
  end
62
74
  end
@@ -0,0 +1,37 @@
1
+ module Mocktail
2
+ class RunsSorbetSigBlocksBeforeReplacement
3
+ extend T::Sig
4
+
5
+ # This is necessary because when Sorbet runs a sig block of a singleton
6
+ # method, it has the net effect of unwrapping/redefining the method. If
7
+ # we try to use Mocktail.replace(Foo) and Foo.bar has a Sorbet sig block,
8
+ # then we'll end up with three "versions" of the same method and no way
9
+ # to keep straight which one == which:
10
+ #
11
+ # A - Foo.bar, as defined in the original class
12
+ # B - Foo.bar, as redefined by RedefinesSingletonMethods
13
+ # C - Foo.bar, as wrapped by sorbet-runtime
14
+ #
15
+ # Initially, Foo.method(:bar) would == C, but after the type
16
+ # replacement, it would == B (with a reference back to C as the original),
17
+ # but after handling a single dry call, our invocation of
18
+ # GrabsOriginalMethodParameters.grab(Foo.method(:bar)) would invoke the
19
+ # Sorbet `sig` block, which has the net effect of redefining the method back
20
+ # to A.
21
+ #
22
+ # It's very fun and confusing and a great time.
23
+
24
+ def run(type)
25
+ return unless defined?(T::Private::Methods)
26
+
27
+ type.singleton_methods.each do |method_name|
28
+ method = type.method(method_name)
29
+
30
+ # Again: calling this for the side effect of running the sig block
31
+ #
32
+ # https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/private/methods/_methods.rb#L111
33
+ T::Private::Methods.signature_for_method(method)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,10 +1,14 @@
1
1
  require_relative "replaces_type/redefines_new"
2
2
  require_relative "replaces_type/redefines_singleton_methods"
3
+ require_relative "replaces_type/runs_sorbet_sig_blocks_before_replacement"
3
4
 
4
5
  module Mocktail
5
6
  class ReplacesType
7
+ extend T::Sig
8
+
6
9
  def initialize
7
10
  @top_shelf = TopShelf.instance
11
+ @runs_sorbet_sig_blocks_before_replacement = RunsSorbetSigBlocksBeforeReplacement.new
8
12
  @redefines_new = RedefinesNew.new
9
13
  @redefines_singleton_methods = RedefinesSingletonMethods.new
10
14
  end
@@ -14,6 +18,8 @@ module Mocktail
14
18
  raise UnsupportedMocktail.new("Mocktail.replace() only supports classes and modules")
15
19
  end
16
20
 
21
+ @runs_sorbet_sig_blocks_before_replacement.run(type)
22
+
17
23
  if type.is_a?(Class)
18
24
  @top_shelf.register_new_replacement!(type)
19
25
  @redefines_new.redefine(type)