iron_admin 0.5.0 → 0.6.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 (451) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/iron_admin_manifest.js +12 -0
  3. data/app/components/iron_admin/dashboards/metric_card_component.html.haml +8 -3
  4. data/app/components/iron_admin/dashboards/metric_card_component.rb +21 -1
  5. data/app/components/iron_admin/form/belongs_to_component.rb +60 -6
  6. data/app/components/iron_admin/form/nested_form_component.html.haml +28 -0
  7. data/app/components/iron_admin/form/nested_form_component.rb +81 -0
  8. data/app/components/iron_admin/resources/data_table_component.html.haml +1 -1
  9. data/app/components/iron_admin/resources/data_table_component.rb +6 -0
  10. data/app/components/iron_admin/resources/show_field_component.rb +2 -0
  11. data/app/controllers/iron_admin/application_controller.rb +32 -1
  12. data/app/controllers/iron_admin/concerns/action_executable.rb +44 -0
  13. data/app/controllers/iron_admin/concerns/filterable.rb +84 -0
  14. data/app/controllers/iron_admin/concerns/json_params_coercion.rb +67 -0
  15. data/app/controllers/iron_admin/concerns/nested_permittable.rb +36 -0
  16. data/app/controllers/iron_admin/concerns/scopeable.rb +84 -0
  17. data/app/controllers/iron_admin/concerns/searchable.rb +13 -16
  18. data/app/controllers/iron_admin/exports_controller.rb +15 -11
  19. data/app/controllers/iron_admin/imports_controller.rb +83 -0
  20. data/app/controllers/iron_admin/live_controller.rb +12 -0
  21. data/app/controllers/iron_admin/resources_controller.rb +118 -117
  22. data/app/controllers/iron_admin/search_controller.rb +5 -5
  23. data/app/controllers/iron_admin/tools_controller.rb +63 -4
  24. data/app/helpers/iron_admin/application_helper.rb +33 -14
  25. data/app/helpers/iron_admin/field_display_helper.rb +160 -1
  26. data/app/javascript/iron_admin/controllers/cp_nested_form_controller.js +113 -0
  27. data/app/javascript/iron_admin/controllers/filter_operator_controller.js +10 -0
  28. data/app/javascript/iron_admin/index.js +4 -0
  29. data/app/views/iron_admin/dashboard/index.html.haml +7 -2
  30. data/app/views/iron_admin/form/_nested_row.html.haml +70 -0
  31. data/app/views/iron_admin/imports/new.html.haml +27 -0
  32. data/app/views/iron_admin/imports/preview.html.haml +31 -0
  33. data/app/views/iron_admin/resources/_form.html.haml +81 -5
  34. data/app/views/iron_admin/resources/action_form.html.haml +62 -0
  35. data/app/views/iron_admin/resources/bulk_action_form.html.haml +62 -0
  36. data/app/views/iron_admin/resources/index.html.haml +60 -18
  37. data/app/views/iron_admin/tools/action_form.html.haml +57 -0
  38. data/app/views/iron_admin/tools/show.html.haml +20 -1
  39. data/config/importmap.rb +2 -0
  40. data/config/locales/en.yml +36 -0
  41. data/config/routes.rb +7 -0
  42. data/docs/components/filter-components.md +60 -0
  43. data/docs/getting-started/quick-start.md +17 -1
  44. data/docs/guides/authentication.md +7 -6
  45. data/docs/guides/custom-adapters.md +628 -0
  46. data/docs/guides/extending.md +132 -3
  47. data/docs/guides/fields.md +1 -1
  48. data/docs/guides/resources.md +78 -2
  49. data/docs/guides/troubleshooting.md +29 -0
  50. data/docs/reference/routes.md +10 -0
  51. data/lib/generators/iron_admin/install/install_generator.rb +28 -4
  52. data/lib/generators/iron_admin/install_audit/install_audit_generator.rb +26 -10
  53. data/lib/iron_admin/action_field.rb +56 -0
  54. data/lib/iron_admin/adapters/active_record.rb +237 -0
  55. data/lib/iron_admin/adapters/base.rb +340 -0
  56. data/lib/iron_admin/adapters/http/column_descriptor.rb +10 -0
  57. data/lib/iron_admin/adapters/http/configuration.rb +27 -0
  58. data/lib/iron_admin/adapters/http/connection.rb +117 -0
  59. data/lib/iron_admin/adapters/http/model_proxy.rb +75 -0
  60. data/lib/iron_admin/adapters/http/query.rb +111 -0
  61. data/lib/iron_admin/adapters/http/record.rb +83 -0
  62. data/lib/iron_admin/adapters/http/type_inferrer.rb +51 -0
  63. data/lib/iron_admin/adapters/http.rb +241 -0
  64. data/lib/iron_admin/adapters/mongoid/association_wrapper.rb +74 -0
  65. data/lib/iron_admin/adapters/mongoid/column_descriptor.rb +10 -0
  66. data/lib/iron_admin/adapters/mongoid.rb +284 -0
  67. data/lib/iron_admin/adapters/registry.rb +44 -0
  68. data/lib/iron_admin/concerns/importable.rb +81 -0
  69. data/lib/iron_admin/concerns/live_updatable.rb +38 -0
  70. data/lib/iron_admin/concerns/nestable.rb +69 -0
  71. data/lib/iron_admin/concerns/soft_deletable.rb +61 -0
  72. data/lib/iron_admin/configuration.rb +20 -0
  73. data/lib/iron_admin/dashboard.rb +15 -20
  74. data/lib/iron_admin/engine.rb +28 -1
  75. data/lib/iron_admin/errors.rb +29 -0
  76. data/lib/iron_admin/field_inferrer.rb +61 -22
  77. data/lib/iron_admin/filters/active_record_query_builder.rb +63 -0
  78. data/lib/iron_admin/filters/base_query_builder.rb +55 -0
  79. data/lib/iron_admin/filters/http_query_builder.rb +39 -0
  80. data/lib/iron_admin/filters/mongoid_query_builder.rb +58 -0
  81. data/lib/iron_admin/filters/query_builder.rb +12 -0
  82. data/lib/iron_admin/import/column_mapper.rb +42 -0
  83. data/lib/iron_admin/import/import_preview.rb +10 -0
  84. data/lib/iron_admin/import/import_result.rb +10 -0
  85. data/lib/iron_admin/import/importer.rb +231 -0
  86. data/lib/iron_admin/import/parser/csv.rb +34 -0
  87. data/lib/iron_admin/import/parser/json.rb +36 -0
  88. data/lib/iron_admin/import/type_caster.rb +47 -0
  89. data/lib/iron_admin/live/broadcaster.rb +43 -0
  90. data/lib/iron_admin/live/poll_cache.rb +28 -0
  91. data/lib/iron_admin/live.rb +29 -0
  92. data/lib/iron_admin/nested_association.rb +15 -0
  93. data/lib/iron_admin/nested_attributes_validator.rb +25 -0
  94. data/lib/iron_admin/policy.rb +72 -13
  95. data/lib/iron_admin/resource.rb +148 -90
  96. data/lib/iron_admin/resource_registry.rb +77 -4
  97. data/lib/iron_admin/tool.rb +50 -0
  98. data/lib/iron_admin/tool_action.rb +73 -0
  99. data/lib/iron_admin/tool_context.rb +49 -0
  100. data/lib/iron_admin/version.rb +1 -1
  101. data/lib/iron_admin.rb +28 -0
  102. data/vendor/bundle/ruby/3.2.0/cache/addressable-2.9.0.gem +0 -0
  103. data/vendor/bundle/ruby/3.2.0/cache/crack-1.0.1.gem +0 -0
  104. data/vendor/bundle/ruby/3.2.0/cache/faraday-2.14.1.gem +0 -0
  105. data/vendor/bundle/ruby/3.2.0/cache/faraday-net_http-3.4.2.gem +0 -0
  106. data/vendor/bundle/ruby/3.2.0/cache/hashdiff-1.2.1.gem +0 -0
  107. data/vendor/bundle/ruby/3.2.0/cache/net-http-0.9.1.gem +0 -0
  108. data/vendor/bundle/ruby/3.2.0/cache/public_suffix-7.0.5.gem +0 -0
  109. data/vendor/bundle/ruby/3.2.0/cache/rexml-3.4.4.gem +0 -0
  110. data/vendor/bundle/ruby/3.2.0/cache/webmock-3.26.2.gem +0 -0
  111. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/bigdecimal-4.0.1/bigdecimal.so +0 -0
  112. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/bigdecimal-4.0.1/gem_make.out +6 -6
  113. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/bigdecimal-4.0.1/mkmf.log +25 -25
  114. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/date-3.5.1/date_core.so +0 -0
  115. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/date-3.5.1/gem_make.out +6 -6
  116. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/date-3.5.1/mkmf.log +4 -4
  117. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/erb-6.0.1/erb/escape.so +0 -0
  118. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/erb-6.0.1/gem_make.out +6 -6
  119. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/erb-6.0.1/mkmf.log +2 -2
  120. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/io-console-0.8.2/gem_make.out +6 -6
  121. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/io-console-0.8.2/io/console.so +0 -0
  122. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/io-console-0.8.2/mkmf.log +22 -22
  123. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/json-2.18.1/gem_make.out +6 -6
  124. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/json-2.18.1/json/ext/generator.so +0 -0
  125. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/json-2.18.1/json/ext/parser.so +0 -0
  126. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/json-2.18.1/mkmf.log +9 -9
  127. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/nio4r-2.7.5/gem_make.out +6 -6
  128. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/nio4r-2.7.5/mkmf.log +12 -12
  129. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/nio4r-2.7.5/nio4r_ext.so +0 -0
  130. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/prism-1.9.0/gem_make.out +6 -6
  131. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/prism-1.9.0/mkmf.log +6 -6
  132. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/prism-1.9.0/prism/prism.so +0 -0
  133. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/psych-5.3.1/gem_make.out +6 -6
  134. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/psych-5.3.1/mkmf.log +7 -7
  135. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/psych-5.3.1/psych.so +0 -0
  136. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/racc-1.8.1/gem_make.out +6 -6
  137. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/racc-1.8.1/racc/cparse.so +0 -0
  138. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/redcarpet-3.6.1/gem_make.out +6 -6
  139. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/redcarpet-3.6.1/redcarpet.so +0 -0
  140. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/stringio-3.2.0/gem_make.out +6 -6
  141. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/stringio-3.2.0/mkmf.log +2 -2
  142. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/stringio-3.2.0/stringio.so +0 -0
  143. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/websocket-driver-0.8.0/gem_make.out +6 -6
  144. data/vendor/bundle/ruby/3.2.0/extensions/x86_64-linux/3.2.0/websocket-driver-0.8.0/websocket_mask.so +0 -0
  145. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/CHANGELOG.md +326 -0
  146. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/LICENSE.txt +202 -0
  147. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/README.md +121 -0
  148. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/lib/addressable/idna/native.rb +66 -0
  149. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/lib/addressable/idna/pure.rb +4710 -0
  150. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/lib/addressable/idna.rb +26 -0
  151. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/lib/addressable/template.rb +1040 -0
  152. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/lib/addressable/uri.rb +2602 -0
  153. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/lib/addressable/version.rb +31 -0
  154. data/vendor/bundle/ruby/3.2.0/gems/addressable-2.9.0/lib/addressable.rb +4 -0
  155. data/vendor/bundle/ruby/3.2.0/gems/bigdecimal-4.0.1/ext/bigdecimal/Makefile +3 -3
  156. data/vendor/bundle/ruby/3.2.0/gems/bigdecimal-4.0.1/lib/bigdecimal.so +0 -0
  157. data/vendor/bundle/ruby/3.2.0/gems/crack-1.0.1/History +58 -0
  158. data/vendor/bundle/ruby/3.2.0/gems/crack-1.0.1/LICENSE +20 -0
  159. data/vendor/bundle/ruby/3.2.0/gems/crack-1.0.1/README.md +43 -0
  160. data/vendor/bundle/ruby/3.2.0/gems/crack-1.0.1/lib/crack/json.rb +113 -0
  161. data/vendor/bundle/ruby/3.2.0/gems/crack-1.0.1/lib/crack/util.rb +17 -0
  162. data/vendor/bundle/ruby/3.2.0/gems/crack-1.0.1/lib/crack/version.rb +3 -0
  163. data/vendor/bundle/ruby/3.2.0/gems/crack-1.0.1/lib/crack/xml.rb +240 -0
  164. data/vendor/bundle/ruby/3.2.0/gems/crack-1.0.1/lib/crack.rb +8 -0
  165. data/vendor/bundle/ruby/3.2.0/gems/date-3.5.1/ext/date/Makefile +3 -3
  166. data/vendor/bundle/ruby/3.2.0/gems/date-3.5.1/lib/date_core.so +0 -0
  167. data/vendor/bundle/ruby/3.2.0/gems/erb-6.0.1/ext/erb/escape/Makefile +3 -3
  168. data/vendor/bundle/ruby/3.2.0/gems/erb-6.0.1/lib/erb/escape.so +0 -0
  169. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/CHANGELOG.md +574 -0
  170. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/LICENSE.md +20 -0
  171. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/README.md +67 -0
  172. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/Rakefile +12 -0
  173. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/examples/client_spec.rb +119 -0
  174. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/examples/client_test.rb +144 -0
  175. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/adapter/test.rb +311 -0
  176. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/adapter.rb +101 -0
  177. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/adapter_registry.rb +30 -0
  178. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/connection.rb +565 -0
  179. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/encoders/flat_params_encoder.rb +105 -0
  180. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/encoders/nested_params_encoder.rb +183 -0
  181. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/error.rb +202 -0
  182. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/logging/formatter.rb +118 -0
  183. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/methods.rb +6 -0
  184. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/middleware.rb +72 -0
  185. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/middleware_registry.rb +83 -0
  186. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/options/connection_options.rb +23 -0
  187. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/options/env.rb +204 -0
  188. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/options/proxy_options.rb +38 -0
  189. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/options/request_options.rb +23 -0
  190. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/options/ssl_options.rb +76 -0
  191. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/options.rb +219 -0
  192. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/parameters.rb +5 -0
  193. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/rack_builder.rb +248 -0
  194. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/request/authorization.rb +54 -0
  195. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/request/instrumentation.rb +58 -0
  196. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/request/json.rb +70 -0
  197. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/request/url_encoded.rb +60 -0
  198. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/request.rb +139 -0
  199. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/response/json.rb +74 -0
  200. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/response/logger.rb +39 -0
  201. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/response/raise_error.rb +83 -0
  202. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/response.rb +95 -0
  203. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/utils/headers.rb +150 -0
  204. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/utils/params_hash.rb +61 -0
  205. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/utils.rb +121 -0
  206. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday/version.rb +5 -0
  207. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/lib/faraday.rb +158 -0
  208. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/external_adapters/faraday_specs_setup.rb +14 -0
  209. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/adapter/test_spec.rb +442 -0
  210. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/adapter_registry_spec.rb +28 -0
  211. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/adapter_spec.rb +55 -0
  212. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/connection_spec.rb +841 -0
  213. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/error_spec.rb +175 -0
  214. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/middleware_registry_spec.rb +31 -0
  215. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/middleware_spec.rb +213 -0
  216. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/options/env_spec.rb +76 -0
  217. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/options/options_spec.rb +297 -0
  218. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/options/proxy_options_spec.rb +79 -0
  219. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/options/request_options_spec.rb +19 -0
  220. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/params_encoders/flat_spec.rb +42 -0
  221. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/params_encoders/nested_spec.rb +151 -0
  222. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/rack_builder_spec.rb +317 -0
  223. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/request/authorization_spec.rb +118 -0
  224. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/request/instrumentation_spec.rb +74 -0
  225. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/request/json_spec.rb +199 -0
  226. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/request/url_encoded_spec.rb +93 -0
  227. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/request_spec.rb +110 -0
  228. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/response/json_spec.rb +206 -0
  229. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/response/logger_spec.rb +299 -0
  230. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/response/raise_error_spec.rb +286 -0
  231. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/response_spec.rb +84 -0
  232. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/utils/headers_spec.rb +109 -0
  233. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday/utils_spec.rb +120 -0
  234. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/faraday_spec.rb +43 -0
  235. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/spec_helper.rb +133 -0
  236. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/support/disabling_stub.rb +14 -0
  237. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/support/fake_safe_buffer.rb +15 -0
  238. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/support/faraday_middleware_subclasses.rb +18 -0
  239. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/support/helper_methods.rb +96 -0
  240. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/support/shared_examples/adapter.rb +105 -0
  241. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/support/shared_examples/params_encoder.rb +18 -0
  242. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/support/shared_examples/request_method.rb +263 -0
  243. data/vendor/bundle/ruby/3.2.0/gems/faraday-2.14.1/spec/support/streaming_response_checker.rb +35 -0
  244. data/vendor/bundle/ruby/3.2.0/gems/faraday-net_http-3.4.2/LICENSE.md +21 -0
  245. data/vendor/bundle/ruby/3.2.0/gems/faraday-net_http-3.4.2/README.md +57 -0
  246. data/vendor/bundle/ruby/3.2.0/gems/faraday-net_http-3.4.2/lib/faraday/adapter/net_http.rb +206 -0
  247. data/vendor/bundle/ruby/3.2.0/gems/faraday-net_http-3.4.2/lib/faraday/net_http/version.rb +7 -0
  248. data/vendor/bundle/ruby/3.2.0/gems/faraday-net_http-3.4.2/lib/faraday/net_http.rb +10 -0
  249. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/Gemfile +8 -0
  250. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/LICENSE +19 -0
  251. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/README.md +323 -0
  252. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/Rakefile +18 -0
  253. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/changelog.md +127 -0
  254. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/hashdiff.gemspec +39 -0
  255. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/lib/hashdiff/compare_hashes.rb +101 -0
  256. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/lib/hashdiff/diff.rb +185 -0
  257. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/lib/hashdiff/lcs.rb +66 -0
  258. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/lib/hashdiff/lcs_compare_arrays.rb +32 -0
  259. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/lib/hashdiff/linear_compare_array.rb +159 -0
  260. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/lib/hashdiff/patch.rb +88 -0
  261. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/lib/hashdiff/util.rb +155 -0
  262. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/lib/hashdiff/version.rb +5 -0
  263. data/vendor/bundle/ruby/3.2.0/gems/hashdiff-1.2.1/lib/hashdiff.rb +10 -0
  264. data/vendor/bundle/ruby/3.2.0/gems/io-console-0.8.2/ext/io/console/Makefile +3 -3
  265. data/vendor/bundle/ruby/3.2.0/gems/io-console-0.8.2/lib/io/console.so +0 -0
  266. data/vendor/bundle/ruby/3.2.0/gems/json-2.18.1/ext/json/ext/generator/Makefile +3 -3
  267. data/vendor/bundle/ruby/3.2.0/gems/json-2.18.1/ext/json/ext/parser/Makefile +3 -3
  268. data/vendor/bundle/ruby/3.2.0/gems/json-2.18.1/lib/json/ext/generator.so +0 -0
  269. data/vendor/bundle/ruby/3.2.0/gems/json-2.18.1/lib/json/ext/parser.so +0 -0
  270. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/BSDL +22 -0
  271. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/COPYING +56 -0
  272. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/README.md +93 -0
  273. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/doc/net-http/examples.rdoc +31 -0
  274. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/doc/net-http/included_getters.rdoc +3 -0
  275. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http/exceptions.rb +35 -0
  276. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http/generic_request.rb +429 -0
  277. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http/header.rb +985 -0
  278. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http/proxy_delta.rb +17 -0
  279. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http/request.rb +88 -0
  280. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http/requests.rb +444 -0
  281. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http/response.rb +739 -0
  282. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http/responses.rb +1242 -0
  283. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http/status.rb +84 -0
  284. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/http.rb +2608 -0
  285. data/vendor/bundle/ruby/3.2.0/gems/net-http-0.9.1/lib/net/https.rb +23 -0
  286. data/vendor/bundle/ruby/3.2.0/gems/nio4r-2.7.5/ext/nio4r/Makefile +3 -3
  287. data/vendor/bundle/ruby/3.2.0/gems/nio4r-2.7.5/lib/nio4r_ext.so +0 -0
  288. data/vendor/bundle/ruby/3.2.0/gems/prism-1.9.0/ext/prism/Makefile +3 -3
  289. data/vendor/bundle/ruby/3.2.0/gems/prism-1.9.0/lib/prism/prism.so +0 -0
  290. data/vendor/bundle/ruby/3.2.0/gems/psych-5.3.1/ext/psych/Makefile +3 -3
  291. data/vendor/bundle/ruby/3.2.0/gems/psych-5.3.1/lib/psych.so +0 -0
  292. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/CHANGELOG.md +649 -0
  293. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/Gemfile +16 -0
  294. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/LICENSE.txt +22 -0
  295. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/README.md +231 -0
  296. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/SECURITY.md +24 -0
  297. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/data/list.txt +16298 -0
  298. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/lib/public_suffix/domain.rb +235 -0
  299. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/lib/public_suffix/errors.rb +41 -0
  300. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/lib/public_suffix/list.rb +247 -0
  301. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/lib/public_suffix/rule.rb +350 -0
  302. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/lib/public_suffix/version.rb +14 -0
  303. data/vendor/bundle/ruby/3.2.0/gems/public_suffix-7.0.5/lib/public_suffix.rb +177 -0
  304. data/vendor/bundle/ruby/3.2.0/gems/racc-1.8.1/ext/racc/cparse/Makefile +3 -3
  305. data/vendor/bundle/ruby/3.2.0/gems/racc-1.8.1/lib/racc/cparse.so +0 -0
  306. data/vendor/bundle/ruby/3.2.0/gems/redcarpet-3.6.1/ext/redcarpet/Makefile +3 -3
  307. data/vendor/bundle/ruby/3.2.0/gems/redcarpet-3.6.1/lib/redcarpet.so +0 -0
  308. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/LICENSE.txt +22 -0
  309. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/NEWS.md +843 -0
  310. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/README.md +57 -0
  311. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/context.rdoc +143 -0
  312. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/child.rdoc +87 -0
  313. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/document.rdoc +276 -0
  314. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/element.rdoc +602 -0
  315. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/node.rdoc +97 -0
  316. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/rdoc/parent.rdoc +267 -0
  317. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/child_toc.rdoc +12 -0
  318. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/document_toc.rdoc +30 -0
  319. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/element_toc.rdoc +55 -0
  320. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/master_toc.rdoc +135 -0
  321. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/node_toc.rdoc +16 -0
  322. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tasks/tocs/parent_toc.rdoc +25 -0
  323. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/doc/rexml/tutorial.rdoc +1358 -0
  324. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/attlistdecl.rb +63 -0
  325. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/attribute.rb +210 -0
  326. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/cdata.rb +68 -0
  327. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/child.rb +96 -0
  328. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/comment.rb +80 -0
  329. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/doctype.rb +306 -0
  330. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/document.rb +471 -0
  331. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/dtd/attlistdecl.rb +11 -0
  332. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/dtd/dtd.rb +47 -0
  333. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/dtd/elementdecl.rb +18 -0
  334. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/dtd/entitydecl.rb +57 -0
  335. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/dtd/notationdecl.rb +40 -0
  336. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/element.rb +2578 -0
  337. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/encoding.rb +48 -0
  338. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/entity.rb +142 -0
  339. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/formatters/default.rb +116 -0
  340. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/formatters/pretty.rb +142 -0
  341. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/formatters/transitive.rb +58 -0
  342. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/functions.rb +446 -0
  343. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/instruction.rb +79 -0
  344. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/light/node.rb +188 -0
  345. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/namespace.rb +63 -0
  346. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/node.rb +80 -0
  347. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/output.rb +30 -0
  348. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parent.rb +166 -0
  349. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parseexception.rb +53 -0
  350. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parsers/baseparser.rb +949 -0
  351. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parsers/lightparser.rb +59 -0
  352. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parsers/pullparser.rb +213 -0
  353. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parsers/sax2parser.rb +270 -0
  354. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parsers/streamparser.rb +67 -0
  355. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parsers/treeparser.rb +89 -0
  356. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parsers/ultralightparser.rb +57 -0
  357. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/parsers/xpathparser.rb +739 -0
  358. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/quickpath.rb +267 -0
  359. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/rexml.rb +39 -0
  360. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/sax2listener.rb +98 -0
  361. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/security.rb +28 -0
  362. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/source.rb +388 -0
  363. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/streamlistener.rb +93 -0
  364. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/text.rb +420 -0
  365. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/undefinednamespaceexception.rb +9 -0
  366. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/validation/relaxng.rb +540 -0
  367. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/validation/validation.rb +144 -0
  368. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/validation/validationexception.rb +10 -0
  369. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/xmldecl.rb +130 -0
  370. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/xmltokens.rb +85 -0
  371. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/xpath.rb +70 -0
  372. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml/xpath_parser.rb +980 -0
  373. data/vendor/bundle/ruby/3.2.0/gems/rexml-3.4.4/lib/rexml.rb +3 -0
  374. data/vendor/bundle/ruby/3.2.0/gems/stringio-3.2.0/ext/stringio/Makefile +3 -3
  375. data/vendor/bundle/ruby/3.2.0/gems/stringio-3.2.0/lib/stringio.so +0 -0
  376. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/CHANGELOG.md +2148 -0
  377. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/LICENSE +20 -0
  378. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/README.md +1229 -0
  379. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/api.rb +111 -0
  380. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/assertion_failure.rb +13 -0
  381. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/callback_registry.rb +37 -0
  382. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/config.rb +20 -0
  383. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/cucumber.rb +12 -0
  384. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/deprecation.rb +11 -0
  385. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/errors.rb +19 -0
  386. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +228 -0
  387. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/curb_adapter.rb +354 -0
  388. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +239 -0
  389. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/excon_adapter.rb +167 -0
  390. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/http_lib_adapter.rb +9 -0
  391. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +21 -0
  392. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
  393. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/http_rb/request.rb +28 -0
  394. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/http_rb/response.rb +95 -0
  395. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/http_rb/streamer.rb +44 -0
  396. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/http_rb/webmock.rb +77 -0
  397. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/http_rb_adapter.rb +39 -0
  398. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/httpclient_adapter.rb +260 -0
  399. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/manticore_adapter.rb +147 -0
  400. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/net_http.rb +310 -0
  401. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/net_http_response.rb +36 -0
  402. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/patron_adapter.rb +132 -0
  403. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +190 -0
  404. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/matchers/any_arg_matcher.rb +15 -0
  405. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/matchers/hash_argument_matcher.rb +23 -0
  406. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/matchers/hash_excluding_matcher.rb +17 -0
  407. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/matchers/hash_including_matcher.rb +19 -0
  408. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/minitest.rb +43 -0
  409. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/rack_response.rb +73 -0
  410. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/request_body_diff.rb +66 -0
  411. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/request_execution_verifier.rb +79 -0
  412. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/request_pattern.rb +428 -0
  413. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/request_registry.rb +37 -0
  414. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/request_signature.rb +56 -0
  415. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/request_signature_snippet.rb +63 -0
  416. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/request_stub.rb +134 -0
  417. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/response.rb +161 -0
  418. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/responses_sequence.rb +42 -0
  419. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/rspec/matchers/request_pattern_matcher.rb +80 -0
  420. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/rspec/matchers/webmock_matcher.rb +69 -0
  421. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/rspec/matchers.rb +29 -0
  422. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/rspec.rb +44 -0
  423. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/stub_registry.rb +84 -0
  424. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/stub_request_snippet.rb +40 -0
  425. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/test_unit.rb +22 -0
  426. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/hash_counter.rb +45 -0
  427. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/hash_keys_stringifier.rb +27 -0
  428. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/hash_validator.rb +19 -0
  429. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/headers.rb +77 -0
  430. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/parsers/json.rb +72 -0
  431. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/parsers/parse_error.rb +7 -0
  432. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/parsers/xml.rb +16 -0
  433. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/query_mapper.rb +283 -0
  434. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/uri.rb +113 -0
  435. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/values_stringifier.rb +22 -0
  436. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/util/version_checker.rb +113 -0
  437. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/version.rb +5 -0
  438. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock/webmock.rb +175 -0
  439. data/vendor/bundle/ruby/3.2.0/gems/webmock-3.26.2/lib/webmock.rb +61 -0
  440. data/vendor/bundle/ruby/3.2.0/gems/websocket-driver-0.8.0/ext/websocket-driver/Makefile +3 -3
  441. data/vendor/bundle/ruby/3.2.0/gems/websocket-driver-0.8.0/lib/websocket_mask.so +0 -0
  442. data/vendor/bundle/ruby/3.2.0/specifications/addressable-2.9.0.gemspec +29 -0
  443. data/vendor/bundle/ruby/3.2.0/specifications/crack-1.0.1.gemspec +27 -0
  444. data/vendor/bundle/ruby/3.2.0/specifications/faraday-2.14.1.gemspec +0 -0
  445. data/vendor/bundle/ruby/3.2.0/specifications/faraday-net_http-3.4.2.gemspec +26 -0
  446. data/vendor/bundle/ruby/3.2.0/specifications/hashdiff-1.2.1.gemspec +30 -0
  447. data/vendor/bundle/ruby/3.2.0/specifications/net-http-0.9.1.gemspec +27 -0
  448. data/vendor/bundle/ruby/3.2.0/specifications/public_suffix-7.0.5.gemspec +24 -0
  449. data/vendor/bundle/ruby/3.2.0/specifications/rexml-3.4.4.gemspec +25 -0
  450. data/vendor/bundle/ruby/3.2.0/specifications/webmock-3.26.2.gemspec +44 -0
  451. metadata +377 -2
@@ -1,6 +1,15 @@
1
1
  # Quick Start
2
2
 
3
- Get a full admin panel running in 5 steps.
3
+ Get a full admin panel running in 6 steps.
4
+
5
+ ## Prerequisites
6
+
7
+ IronAdmin uses [Tailwind CSS](https://tailwindcss.com/) for all styling and requires `tailwindcss-rails` >= 4.0. Ensure your application has a supported version installed:
8
+
9
+ ```bash
10
+ bundle add tailwindcss-rails --version ">= 4.0"
11
+ rails tailwindcss:install
12
+ ```
4
13
 
5
14
  ## 1. Install
6
15
 
@@ -81,4 +90,11 @@ class AdminDashboard < IronAdmin::Dashboard
81
90
  end
82
91
  ```
83
92
 
93
+ ## 6. Build CSS and Start
94
+
95
+ ```bash
96
+ rails tailwindcss:build
97
+ bin/rails server
98
+ ```
99
+
84
100
  Visit `/admin` and your admin panel is ready.
@@ -73,7 +73,7 @@ Per-resource access control:
73
73
  class UserResource < IronAdmin::Resource
74
74
  policy do
75
75
  allow :read, :create, :update
76
- deny :delete, if: ->(record) { record.admin? }
76
+ deny :destroy, if: ->(user) { !user.superadmin? }
77
77
  end
78
78
  end
79
79
  ```
@@ -99,19 +99,20 @@ end
99
99
  ### Conditional Policies
100
100
 
101
101
  - `allow` with `if:` receives the **current user**
102
- - `deny` with `if:` receives the **record**
102
+ - `deny` with `if:` receives the **current user**
103
+ - Deny rules take precedence over allow rules
103
104
 
104
105
  ```ruby
105
106
  policy do
106
107
  allow :read
107
108
  allow :update, if: ->(user) { user.admin? || user.manager? }
108
- deny :delete, if: ->(record) { record.protected? }
109
+ deny :destroy, if: ->(user) { !user.superadmin? }
109
110
  end
110
111
  ```
111
112
 
112
113
  ### Custom Action Authorization
113
114
 
114
- Custom actions and bulk actions require explicit policy permission:
115
+ Custom actions and bulk actions are **allowed by default**, even when a policy is defined. This separates custom action authorization from CRUD policy. To restrict a custom action, add an `allow` rule with a condition:
115
116
 
116
117
  ```ruby
117
118
  class UserResource < IronAdmin::Resource
@@ -125,8 +126,8 @@ class UserResource < IronAdmin::Resource
125
126
 
126
127
  policy do
127
128
  allow :read, :update
128
- allow :lock # Authorize the custom action
129
- allow :archive # Authorize the bulk action
129
+ allow :lock, if: ->(user) { user.admin? } # Restrict the custom action
130
+ allow :archive, if: ->(user) { user.admin? } # Restrict the bulk action
130
131
  end
131
132
  end
132
133
  ```
@@ -0,0 +1,628 @@
1
+ # Building a Custom Adapter
2
+
3
+ This guide covers everything you need to build a custom IronAdmin adapter from scratch. An adapter enables IronAdmin to work with any data source — a different ORM, a REST API, a flat file, or anything else that can provide records.
4
+
5
+ ## Architecture Overview
6
+
7
+ Every IronAdmin controller, component, and helper interacts with data exclusively through the adapter. The adapter is the **only** boundary between IronAdmin and your data source.
8
+
9
+ ```
10
+ Controller → Resource.adapter → YourAdapter → Your Data Source
11
+ ```
12
+
13
+ The adapter is instantiated once per resource class with the model class as its argument:
14
+
15
+ ```ruby
16
+ adapter = YourAdapter.new(Product) # Product is your model/data class
17
+ adapter.all # => returns all records
18
+ adapter.find("abc123") # => returns one record
19
+ adapter.filter(scope, :status, "active") # => filtered scope
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### 1. Create the adapter class
25
+
26
+ ```ruby
27
+ # lib/my_app/iron_admin/sequel_adapter.rb
28
+ class MyApp::IronAdmin::SequelAdapter < IronAdmin::Adapters::Base
29
+ # Implement all 35 methods (see reference below)
30
+ end
31
+ ```
32
+
33
+ ### 2. Configure a resource to use it
34
+
35
+ ```ruby
36
+ # app/iron_admin/resources/product_resource.rb
37
+ module IronAdmin
38
+ module Resources
39
+ class ProductResource < IronAdmin::Resource
40
+ self.adapter_class = MyApp::IronAdmin::SequelAdapter
41
+ end
42
+ end
43
+ end
44
+ ```
45
+
46
+ You can pass a class directly (as above). The built-in adapters (`:active_record`, `:mongoid`, `:http`) are registered in `Adapters::Registry` and can be referenced by symbol. Custom adapters should be passed as classes:
47
+
48
+ ```ruby
49
+ # Pass the class directly — no registry needed
50
+ self.adapter_class = MyApp::IronAdmin::SequelAdapter
51
+ ```
52
+
53
+ ### 3. Create a QueryBuilder (optional but recommended)
54
+
55
+ If your data source supports operator-based filters (`:string` and `:number` filter types), create a query builder:
56
+
57
+ ```ruby
58
+ class MyApp::IronAdmin::SequelQueryBuilder < IronAdmin::Filters::BaseQueryBuilder
59
+ private
60
+
61
+ def apply_string_filter
62
+ # Implement contains, equals, starts_with, ends_with
63
+ end
64
+
65
+ def apply_number_filter
66
+ # Implement equals, greater_than, less_than, between
67
+ end
68
+ end
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Complete Method Reference
74
+
75
+ `Adapters::Base` defines 35 methods organized into 9 sections. All methods raise `NotImplementedError` by default except `resource_name` and `human_name` (which use `ActiveModel::Naming`).
76
+
77
+ ### Schema Introspection (8 methods)
78
+
79
+ These methods let IronAdmin discover your model's structure. `FieldInferrer` calls them to auto-generate field configurations.
80
+
81
+ #### `columns` → `Array<#name, #type>`
82
+
83
+ Returns descriptors for every field/column in the model. Each object **must** respond to:
84
+
85
+ | Method | Return | Example |
86
+ |--------|--------|---------|
87
+ | `.name` | `String` | `"email"` |
88
+ | `.type` | `Symbol` | `:string` |
89
+
90
+ Valid type symbols (mapped by `FieldInferrer::TYPE_MAP`):
91
+
92
+ | Symbol | Rendered as |
93
+ |--------|------------|
94
+ | `:string` | Text input (auto-detects `:url` and `:email` by name pattern) |
95
+ | `:text` | Textarea |
96
+ | `:integer`, `:float`, `:decimal` | Number input (`:number`) |
97
+ | `:boolean` | Checkbox |
98
+ | `:date` | Date picker |
99
+ | `:datetime` | Datetime picker |
100
+ | `:time` | Time input |
101
+ | `:json`, `:jsonb` | JSON editor |
102
+
103
+ Any unrecognized type falls back to `:text`.
104
+
105
+ **Tip:** Create a `ColumnDescriptor` value object:
106
+
107
+ ```ruby
108
+ ColumnDescriptor = Struct.new(:name, :type) do
109
+ def to_s = name
110
+ end
111
+ ```
112
+
113
+ **Example (Sequel):**
114
+
115
+ ```ruby
116
+ def columns
117
+ model_class.db_schema.map do |name, info|
118
+ ColumnDescriptor.new(name.to_s, map_type(info[:type]))
119
+ end
120
+ end
121
+ ```
122
+
123
+ #### `column_names` → `Array<String>`
124
+
125
+ Flat list of column/field names as strings.
126
+
127
+ ```ruby
128
+ def column_names
129
+ columns.map(&:name)
130
+ end
131
+ ```
132
+
133
+ #### `has_column?(name)` → `Boolean`
134
+
135
+ Checks if a column/field exists. Accepts `Symbol` or `String`.
136
+
137
+ ```ruby
138
+ def has_column?(name)
139
+ column_names.include?(name.to_s)
140
+ end
141
+ ```
142
+
143
+ #### `enums` → `Hash{String => Hash}`
144
+
145
+ Returns enum definitions. The outer key is the column name (String), the inner hash maps enum values to their storage values.
146
+
147
+ ```ruby
148
+ # Expected format:
149
+ { "status" => { "active" => 0, "inactive" => 1, "archived" => 2 } }
150
+ ```
151
+
152
+ `FieldInferrer` calls `enums.key?(name)` and `enums[name].keys` to generate select fields. Return `{}` if your data source has no enums.
153
+
154
+ #### `associations(kind = nil)` → `Array<AssociationDescriptor>`
155
+
156
+ Returns association descriptors, optionally filtered by kind. Each object **must** respond to:
157
+
158
+ | Method | Return | Notes |
159
+ |--------|--------|-------|
160
+ | `.name` | `Symbol` | Association name (e.g., `:user`) |
161
+ | `.klass` | `Class` | The associated model class |
162
+ | `.foreign_key` | `String` | Foreign key column (e.g., `"user_id"`) |
163
+ | `.polymorphic?` | `Boolean` | Whether this is a polymorphic association |
164
+ | `.foreign_type` | `String` | Polymorphic type column (e.g., `"commentable_type"`) — only called when `polymorphic?` is `true` |
165
+
166
+ `kind` is one of: `:belongs_to`, `:has_many`, `:has_one`, `:has_and_belongs_to_many`, or `nil` (return all). When `kind` is provided, `associations(kind)` must return only descriptors of that kind. IronAdmin consumers rely on `associations(kind)` for filtering — they do not call `.macro` on individual descriptors.
167
+
168
+ **Tip:** Create an `AssociationWrapper` class that tracks the kind internally and use it to filter in `associations(kind)`.
169
+
170
+ Return `[]` if your data source has no associations.
171
+
172
+ #### `association(name)` → `AssociationDescriptor | nil`
173
+
174
+ Returns a single association descriptor by name, or `nil` if not found.
175
+
176
+ #### `attachments` → `Hash`
177
+
178
+ Returns file attachment descriptors. Each value must respond to `.macro` returning `:has_one_attached` or `:has_many_attached`. This is ActiveStorage-specific — return `{}` for non-Rails data sources.
179
+
180
+ #### `rich_text_attributes` → `Array<Symbol>`
181
+
182
+ Returns ActionText rich text attribute names. Each name starts with `rich_text_` (e.g., `:rich_text_content`). Return `[]` for non-Rails data sources.
183
+
184
+ ### Naming (3 methods)
185
+
186
+ #### `resource_name` → `String`
187
+
188
+ **Implemented in Base.** Returns the URL-friendly plural name (e.g., `"users"`). Uses `model_class.model_name.plural`. Override only if your model doesn't include `ActiveModel::Naming`.
189
+
190
+ #### `human_name` → `String`
191
+
192
+ **Implemented in Base.** Returns the human-readable name (e.g., `"User"`). Uses `model_class.model_name.human`. Override only if your model doesn't include `ActiveModel::Naming`.
193
+
194
+ #### `table_name` → `String | nil`
195
+
196
+ Returns the storage name (table, collection, endpoint path). Return `nil` if not applicable.
197
+
198
+ ### Query Building (10 methods)
199
+
200
+ These methods build and manipulate query scopes. A "scope" is any chainable query object — an `ActiveRecord::Relation`, a `Mongoid::Criteria`, or your own query builder.
201
+
202
+ #### `all` → `Scope`
203
+
204
+ Returns a base scope representing all records.
205
+
206
+ #### `find(id)` → `Record`
207
+
208
+ Finds a record by primary key. **Must raise `IronAdmin::RecordNotFound`** when the record doesn't exist. Wrap your ORM's native not-found exception:
209
+
210
+ ```ruby
211
+ def find(id)
212
+ model_class.find(id)
213
+ rescue MyOrm::NotFoundError => e
214
+ raise IronAdmin::RecordNotFound, e.message
215
+ end
216
+ ```
217
+
218
+ #### `find_by(attrs)` → `Record | nil`
219
+
220
+ Finds a record by attributes hash. Returns `nil` when not found (does **not** raise).
221
+
222
+ #### `filter(scope, column, value)` → `Scope`
223
+
224
+ Filters a scope by a column value. Must handle these value types:
225
+
226
+ | Value type | Expected behavior |
227
+ |-----------|-------------------|
228
+ | Scalar (String, Integer, etc.) | Exact match |
229
+ | `Array` | IN / any-of match |
230
+ | `Range` | Between (may be beginless or endless) |
231
+
232
+ ```ruby
233
+ def filter(scope, column, value)
234
+ case value
235
+ when Array then scope.where(column => value) # IN clause
236
+ when Range then scope.where(column => value) # BETWEEN
237
+ else scope.where(column => value) # exact match
238
+ end
239
+ end
240
+ ```
241
+
242
+ #### `order_by(scope, column, direction)` → `Scope`
243
+
244
+ Orders a scope. `direction` is `:asc` or `:desc`.
245
+
246
+ #### `limit(scope, max)` → `Scope`
247
+
248
+ Limits the scope to `max` records.
249
+
250
+ #### `preload(scope, association_names)` → `Scope`
251
+
252
+ Eager-loads associations to prevent N+1 queries. `association_names` is an `Array<Symbol>`. If your data source doesn't support eager loading, return the scope unmodified.
253
+
254
+ #### `distinct_values(column)` → `Array`
255
+
256
+ Returns sorted, unique, non-nil values for a column. Used to populate filter dropdowns.
257
+
258
+ #### `pluck(scope, column)` → `Array`
259
+
260
+ Extracts raw values for a single column from a scope.
261
+
262
+ #### `count(scope = nil)` → `Integer`
263
+
264
+ Counts records. When `scope` is `nil`, count all records.
265
+
266
+ ### Search (2 methods)
267
+
268
+ #### `search_column(scope, column, query)` → `Scope`
269
+
270
+ Searches a single column for a substring match (case-insensitive). The `query` string comes from user input — **always escape it** to prevent injection:
271
+
272
+ - SQL: Use parameterized `LIKE`/`ILIKE` with `sanitize_like`
273
+ - MongoDB: Use `Regexp.escape` before building regex
274
+ - HTTP API: URL-encode the query
275
+
276
+ #### `search_columns(scope, columns, query)` → `Scope`
277
+
278
+ Searches multiple columns with OR logic. Same escaping rules apply.
279
+
280
+ ### CRUD (4 methods)
281
+
282
+ #### `build(attrs = {})` → `Record`
283
+
284
+ Creates a new unsaved record with the given attributes.
285
+
286
+ #### `save(record)` → `Boolean`
287
+
288
+ Persists a record. Returns `true` on success, `false` on validation failure.
289
+
290
+ #### `update(record, attrs)` → `Boolean`
291
+
292
+ Updates a record's attributes. Returns `true` on success, `false` on validation failure.
293
+
294
+ #### `destroy!(record)` → `void`
295
+
296
+ Permanently deletes a record. Should raise on failure (bang method).
297
+
298
+ ### Transactions (1 method)
299
+
300
+ #### `transaction(&block)` → `Object`
301
+
302
+ Wraps a block in an atomic transaction. If your data source doesn't support transactions, simply `yield`:
303
+
304
+ ```ruby
305
+ def transaction
306
+ yield
307
+ end
308
+ ```
309
+
310
+ ### Scope Manipulation (1 method)
311
+
312
+ #### `unscope_column(scope, column)` → `Scope`
313
+
314
+ Removes a WHERE condition on a specific column from a scope. Used by the soft-delete feature to bypass the default `deleted_at IS NULL` filter.
315
+
316
+ If your scope object supports `unscope`, use it. Otherwise, rebuild the scope without the column:
317
+
318
+ ```ruby
319
+ def unscope_column(scope, column)
320
+ # Rebuild scope excluding the column from conditions
321
+ end
322
+ ```
323
+
324
+ ### Batch (1 method)
325
+
326
+ #### `find_each(scope, &block)` → `void`
327
+
328
+ Iterates all records in memory-efficient batches. Used by CSV export. If your data source doesn't support batching, fall back to `.each`:
329
+
330
+ ```ruby
331
+ def find_each(scope, &block)
332
+ scope.each(&block)
333
+ end
334
+ ```
335
+
336
+ ### Adapter-Agnostic Interface (5 methods)
337
+
338
+ These methods bridge differences between ORMs so controllers remain adapter-agnostic.
339
+
340
+ #### `record_changes(record)` → `Hash`
341
+
342
+ Returns the changes hash after a save/update operation. Used by audit logging.
343
+
344
+ | ORM | Implementation |
345
+ |-----|---------------|
346
+ | ActiveRecord | `record.saved_changes` |
347
+ | Mongoid | `record.previous_changes` |
348
+ | HTTP API | `{}` (or track changes client-side) |
349
+
350
+ #### `wrap_rollback(&block)` → `void`
351
+
352
+ Executes a block and converts `IronAdmin::Rollback` to the adapter-native rollback mechanism. This is called **inside** `transaction`:
353
+
354
+ ```ruby
355
+ # Controller does:
356
+ adapter.transaction do
357
+ adapter.wrap_rollback do
358
+ # ... action code ...
359
+ raise IronAdmin::Rollback if result == false
360
+ end
361
+ end
362
+ ```
363
+
364
+ | ORM | Implementation |
365
+ |-----|---------------|
366
+ | ActiveRecord | Rescue `IronAdmin::Rollback`, re-raise as `ActiveRecord::Rollback` |
367
+ | Mongoid | Rescue `IronAdmin::Rollback`, return `nil` |
368
+ | No transactions | Rescue `IronAdmin::Rollback`, return `nil` |
369
+
370
+ #### `query_builder_class` → `Class`
371
+
372
+ Returns the `BaseQueryBuilder` subclass for operator-based filters (`:string` and `:number` filter types). If you don't support operator filters, create a no-op builder:
373
+
374
+ ```ruby
375
+ class NoOpQueryBuilder < IronAdmin::Filters::BaseQueryBuilder
376
+ private
377
+
378
+ def apply_string_filter = @scope
379
+ def apply_number_filter = @scope
380
+ end
381
+ ```
382
+
383
+ #### `pagy_method` → `Symbol`
384
+
385
+ Returns the Pagy backend method name used for pagination.
386
+
387
+ | ORM | Value |
388
+ |-----|-------|
389
+ | ActiveRecord | `:pagy` |
390
+ | Mongoid | `:pagy_mongoid` |
391
+ | Custom | `:pagy` (default) or implement a custom Pagy backend |
392
+
393
+ #### `cast_boolean(value)` → `Boolean`
394
+
395
+ Casts a string form value (`"true"`, `"false"`, `"1"`, `"0"`) to a Ruby boolean. Used by filter params.
396
+
397
+ ---
398
+
399
+ ## QueryBuilder Integration
400
+
401
+ If your data source supports operator-based string and number filters, create a `BaseQueryBuilder` subclass.
402
+
403
+ ### String operators
404
+
405
+ | Operator | Behavior |
406
+ |----------|----------|
407
+ | `contains` | Substring match (case-insensitive) |
408
+ | `equals` | Exact match |
409
+ | `starts_with` | Prefix match |
410
+ | `ends_with` | Suffix match |
411
+
412
+ ### Number operators
413
+
414
+ | Operator | Behavior |
415
+ |----------|----------|
416
+ | `equals` | Exact numeric match |
417
+ | `greater_than` | Greater than |
418
+ | `less_than` | Less than |
419
+ | `between` | Range (inclusive, uses `@value` and `@upper_value`) |
420
+
421
+ ### Example (Sequel)
422
+
423
+ ```ruby
424
+ class SequelQueryBuilder < IronAdmin::Filters::BaseQueryBuilder
425
+ private
426
+
427
+ def apply_string_filter
428
+ return @scope unless STRING_OPS.include?(@op)
429
+
430
+ col = ::Sequel[@filter[:name]]
431
+ case @op
432
+ when "contains" then @scope.where(col.ilike("%#{escape(@value)}%"))
433
+ when "equals" then @scope.where(@filter[:name] => @value)
434
+ when "starts_with" then @scope.where(col.ilike("#{escape(@value)}%"))
435
+ when "ends_with" then @scope.where(col.ilike("%#{escape(@value)}"))
436
+ else @scope
437
+ end
438
+ end
439
+
440
+ def apply_number_filter
441
+ return @scope unless NUMBER_OPS.include?(@op)
442
+
443
+ num = cast_number(@value)
444
+ return @scope unless num
445
+
446
+ case @op
447
+ when "equals" then @scope.where(@filter[:name] => num)
448
+ when "greater_than" then @scope.where { |o| o.send(@filter[:name]) > num }
449
+ when "less_than" then @scope.where { |o| o.send(@filter[:name]) < num }
450
+ when "between" then apply_between(num)
451
+ else @scope
452
+ end
453
+ end
454
+
455
+ def apply_between(num)
456
+ upper = cast_number(@upper_value)
457
+ return @scope unless upper
458
+
459
+ @scope.where(@filter[:name] => num..upper)
460
+ end
461
+
462
+ def escape(value)
463
+ value.gsub(/[%_\\]/) { |m| "\\#{m}" }
464
+ end
465
+ end
466
+ ```
467
+
468
+ The `cast_number` method is inherited from `BaseQueryBuilder` — it handles integer/float parsing and returns `nil` for invalid values.
469
+
470
+ ---
471
+
472
+ ## Testing Your Adapter
473
+
474
+ Follow the same patterns used by the built-in adapter specs. Your test file should mirror `spec/lib/iron_admin/adapters/active_record_spec.rb`.
475
+
476
+ ### Unit tests with doubles (no database required)
477
+
478
+ For adapters that wrap an external service or unavailable ORM, use test doubles:
479
+
480
+ ```ruby
481
+ RSpec.describe MyAdapter do
482
+ subject(:adapter) { described_class.new(model_class) }
483
+
484
+ let(:model_class) do
485
+ double("Model",
486
+ fields: { "name" => double(name: "name", type: String) },
487
+ model_name: double(plural: "products", human: "Product"),
488
+ # ... stub all methods your adapter calls
489
+ )
490
+ end
491
+
492
+ describe "#columns" do
493
+ it "returns an array" do
494
+ expect(adapter.columns).to be_an(Array)
495
+ end
496
+
497
+ it "returns descriptors with name and type" do
498
+ expect(adapter.columns.first).to respond_to(:name)
499
+ expect(adapter.columns.first).to respond_to(:type)
500
+ end
501
+ end
502
+
503
+ describe "#find" do
504
+ it "raises IronAdmin::RecordNotFound for missing records" do
505
+ allow(model_class).to receive(:find).and_raise(SomeNotFoundError)
506
+ expect { adapter.find("bad") }.to raise_error(IronAdmin::RecordNotFound)
507
+ end
508
+ end
509
+
510
+ # ... test all 35 methods
511
+ end
512
+ ```
513
+
514
+ ### Integration tests with a real data source
515
+
516
+ If possible, also test with real data:
517
+
518
+ ```ruby
519
+ RSpec.describe MyAdapter, :integration do
520
+ subject(:adapter) { described_class.new(RealModel) }
521
+
522
+ describe "#filter" do
523
+ it "filters by exact value" do
524
+ RealModel.create!(name: "Alice", status: "active")
525
+ RealModel.create!(name: "Bob", status: "inactive")
526
+ result = adapter.filter(adapter.all, :status, "active")
527
+ expect(result.count).to eq(1)
528
+ end
529
+ end
530
+ end
531
+ ```
532
+
533
+ ### BetterSpecs conventions
534
+
535
+ Follow [BetterSpecs](https://www.betterspecs.org/) as required by this project:
536
+
537
+ - `describe "#method_name"` for each method
538
+ - `context "when ..."` for conditional behavior
539
+ - One expectation per `it` block
540
+ - Use `subject(:adapter)` and `let` for setup
541
+ - Meaningful descriptions that read as sentences
542
+
543
+ ### Coverage requirements
544
+
545
+ SimpleCov enforces **80% minimum per file**. Ensure your adapter and query builder files have adequate test coverage.
546
+
547
+ ---
548
+
549
+ ## Checklist
550
+
551
+ Use this checklist when building a new adapter:
552
+
553
+ ### Setup
554
+ - [ ] Create adapter class inheriting from `IronAdmin::Adapters::Base`
555
+ - [ ] Create value objects (`ColumnDescriptor`, `AssociationWrapper`) if needed
556
+ - [ ] Create `QueryBuilder` subclass (or use `NoOpQueryBuilder`)
557
+
558
+ ### Schema Introspection
559
+ - [ ] `columns` — returns `Array<#name, #type>` with valid type symbols
560
+ - [ ] `column_names` — returns `Array<String>`
561
+ - [ ] `has_column?(name)` — handles both Symbol and String input
562
+ - [ ] `enums` — returns `Hash{String => Hash}` or `{}`
563
+ - [ ] `associations(kind)` — returns filtered `Array<AssociationDescriptor>` or `[]`
564
+ - [ ] `association(name)` — returns descriptor or `nil`
565
+ - [ ] `attachments` — returns `Hash` or `{}`
566
+ - [ ] `rich_text_attributes` — returns `Array<Symbol>` or `[]`
567
+
568
+ ### Naming
569
+ - [ ] `table_name` — returns `String` or `nil`
570
+ - [ ] Verify `resource_name` and `human_name` work (inherited from Base)
571
+
572
+ ### Query Building
573
+ - [ ] `all` — returns a chainable scope
574
+ - [ ] `find(id)` — raises `IronAdmin::RecordNotFound` on miss
575
+ - [ ] `find_by(attrs)` — returns `nil` on miss (does not raise)
576
+ - [ ] `filter(scope, column, value)` — handles scalars, Arrays, and Ranges
577
+ - [ ] `order_by(scope, column, direction)` — `:asc` and `:desc`
578
+ - [ ] `limit(scope, max)` — returns limited scope
579
+ - [ ] `preload(scope, names)` — eager loads or returns scope unchanged
580
+ - [ ] `distinct_values(column)` — sorted, compact, unique
581
+ - [ ] `pluck(scope, column)` — raw column values
582
+ - [ ] `count(scope)` — defaults to `all` when scope is `nil`
583
+
584
+ ### Search
585
+ - [ ] `search_column(scope, column, query)` — case-insensitive, escaped
586
+ - [ ] `search_columns(scope, columns, query)` — OR logic across columns
587
+
588
+ ### CRUD
589
+ - [ ] `build(attrs)` — unsaved record
590
+ - [ ] `save(record)` — returns boolean
591
+ - [ ] `update(record, attrs)` — returns boolean
592
+ - [ ] `destroy!(record)` — raises on failure
593
+
594
+ ### Transactions & Scope
595
+ - [ ] `transaction(&block)` — atomic or yield
596
+ - [ ] `unscope_column(scope, column)` — removes condition
597
+ - [ ] `find_each(scope, &block)` — batched or `.each`
598
+
599
+ ### Adapter-Agnostic
600
+ - [ ] `record_changes(record)` — returns changes hash
601
+ - [ ] `wrap_rollback(&block)` — catches `IronAdmin::Rollback`
602
+ - [ ] `query_builder_class` — returns your QueryBuilder subclass
603
+ - [ ] `pagy_method` — returns `:pagy` or custom
604
+ - [ ] `cast_boolean(value)` — string to boolean
605
+
606
+ ### Testing
607
+ - [ ] Unit specs for all 35 methods
608
+ - [ ] Edge cases: nil values, empty arrays, missing records
609
+ - [ ] `find` raises `IronAdmin::RecordNotFound`
610
+ - [ ] `find_by` returns `nil` (not raises)
611
+ - [ ] QueryBuilder specs for all operators
612
+ - [ ] 80%+ coverage per file
613
+
614
+ ### Documentation
615
+ - [ ] Update `CHANGELOG.md` under `[Unreleased]`
616
+ - [ ] Update `docs/guides/extending.md` if adding a built-in adapter
617
+ - [ ] Add setup instructions for the new data source
618
+
619
+ ---
620
+
621
+ ## Reference: Built-in Adapters
622
+
623
+ | Adapter | Data source | Key differences |
624
+ |---------|-------------|----------------|
625
+ | `ActiveRecord` | SQL databases (PostgreSQL, MySQL, SQLite) | Uses `LIKE`/`ILIKE` for search, `ActiveRecord::Base.transaction`, `scope.unscope(where:)` |
626
+ | `Mongoid` | MongoDB | Uses `$regex` for search, `scope.any_of` for OR, `scope.in` for arrays, `scope.selector` for unscope |
627
+
628
+ Study `lib/iron_admin/adapters/active_record.rb` (simplest) and `lib/iron_admin/adapters/mongoid.rb` (most complex) as reference implementations.