dhs 1.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 (301) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/rubocop.yml +27 -0
  3. data/.github/workflows/test.yml +27 -0
  4. data/.gitignore +39 -0
  5. data/.rubocop.yml +186 -0
  6. data/.ruby-version +1 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +674 -0
  9. data/README.md +2807 -0
  10. data/Rakefile +26 -0
  11. data/dhs.gemspec +44 -0
  12. data/docs/accessing-data.png +0 -0
  13. data/lib/dhs.rb +67 -0
  14. data/lib/dhs/collection.rb +84 -0
  15. data/lib/dhs/complex.rb +158 -0
  16. data/lib/dhs/concerns/autoload_records.rb +57 -0
  17. data/lib/dhs/concerns/collection/handle_nested.rb +43 -0
  18. data/lib/dhs/concerns/collection/internal_collection.rb +74 -0
  19. data/lib/dhs/concerns/configuration.rb +20 -0
  20. data/lib/dhs/concerns/data/becomes.rb +18 -0
  21. data/lib/dhs/concerns/data/equality.rb +14 -0
  22. data/lib/dhs/concerns/data/extend.rb +87 -0
  23. data/lib/dhs/concerns/data/json.rb +14 -0
  24. data/lib/dhs/concerns/data/to_hash.rb +14 -0
  25. data/lib/dhs/concerns/inspect.rb +70 -0
  26. data/lib/dhs/concerns/is_href.rb +15 -0
  27. data/lib/dhs/concerns/item/destroy.rb +38 -0
  28. data/lib/dhs/concerns/item/endpoint_lookup.rb +27 -0
  29. data/lib/dhs/concerns/item/save.rb +55 -0
  30. data/lib/dhs/concerns/item/update.rb +50 -0
  31. data/lib/dhs/concerns/item/validation.rb +61 -0
  32. data/lib/dhs/concerns/o_auth.rb +25 -0
  33. data/lib/dhs/concerns/option_blocks.rb +26 -0
  34. data/lib/dhs/concerns/proxy/accessors.rb +132 -0
  35. data/lib/dhs/concerns/proxy/create.rb +45 -0
  36. data/lib/dhs/concerns/proxy/link.rb +25 -0
  37. data/lib/dhs/concerns/proxy/problems.rb +27 -0
  38. data/lib/dhs/concerns/record/attribute_assignment.rb +25 -0
  39. data/lib/dhs/concerns/record/batch.rb +40 -0
  40. data/lib/dhs/concerns/record/chainable.rb +465 -0
  41. data/lib/dhs/concerns/record/configuration.rb +103 -0
  42. data/lib/dhs/concerns/record/create.rb +24 -0
  43. data/lib/dhs/concerns/record/custom_setters.rb +22 -0
  44. data/lib/dhs/concerns/record/destroy.rb +18 -0
  45. data/lib/dhs/concerns/record/endpoints.rb +108 -0
  46. data/lib/dhs/concerns/record/equality.rb +14 -0
  47. data/lib/dhs/concerns/record/find.rb +86 -0
  48. data/lib/dhs/concerns/record/find_by.rb +38 -0
  49. data/lib/dhs/concerns/record/first.rb +20 -0
  50. data/lib/dhs/concerns/record/href_for.rb +19 -0
  51. data/lib/dhs/concerns/record/last.rb +27 -0
  52. data/lib/dhs/concerns/record/mapping.rb +25 -0
  53. data/lib/dhs/concerns/record/merge.rb +26 -0
  54. data/lib/dhs/concerns/record/model.rb +23 -0
  55. data/lib/dhs/concerns/record/pagination.rb +49 -0
  56. data/lib/dhs/concerns/record/provider.rb +23 -0
  57. data/lib/dhs/concerns/record/relations.rb +26 -0
  58. data/lib/dhs/concerns/record/request.rb +581 -0
  59. data/lib/dhs/concerns/record/scope.rb +25 -0
  60. data/lib/dhs/concerns/record/tracing.rb +24 -0
  61. data/lib/dhs/concerns/record/update.rb +17 -0
  62. data/lib/dhs/config.rb +24 -0
  63. data/lib/dhs/data.rb +180 -0
  64. data/lib/dhs/endpoint.rb +12 -0
  65. data/lib/dhs/interceptors/auto_oauth/interceptor.rb +33 -0
  66. data/lib/dhs/interceptors/auto_oauth/thread_registry.rb +18 -0
  67. data/lib/dhs/interceptors/extended_rollbar/handler.rb +40 -0
  68. data/lib/dhs/interceptors/extended_rollbar/interceptor.rb +20 -0
  69. data/lib/dhs/interceptors/extended_rollbar/thread_registry.rb +19 -0
  70. data/lib/dhs/interceptors/request_cycle_cache/interceptor.rb +41 -0
  71. data/lib/dhs/interceptors/request_cycle_cache/thread_registry.rb +18 -0
  72. data/lib/dhs/item.rb +59 -0
  73. data/lib/dhs/pagination/base.rb +90 -0
  74. data/lib/dhs/pagination/link.rb +21 -0
  75. data/lib/dhs/pagination/offset.rb +22 -0
  76. data/lib/dhs/pagination/page.rb +18 -0
  77. data/lib/dhs/pagination/start.rb +22 -0
  78. data/lib/dhs/pagination/total_pages.rb +9 -0
  79. data/lib/dhs/problems/base.rb +113 -0
  80. data/lib/dhs/problems/errors.rb +69 -0
  81. data/lib/dhs/problems/nested/base.rb +54 -0
  82. data/lib/dhs/problems/nested/errors.rb +16 -0
  83. data/lib/dhs/problems/nested/warnings.rb +15 -0
  84. data/lib/dhs/problems/warnings.rb +24 -0
  85. data/lib/dhs/proxy.rb +69 -0
  86. data/lib/dhs/railtie.rb +34 -0
  87. data/lib/dhs/record.rb +112 -0
  88. data/lib/dhs/rspec.rb +10 -0
  89. data/lib/dhs/test/stubbable_records.rb +34 -0
  90. data/lib/dhs/unprocessable.rb +6 -0
  91. data/lib/dhs/version.rb +5 -0
  92. data/script/ci/build.sh +18 -0
  93. data/spec/auto_oauth_spec.rb +163 -0
  94. data/spec/autoloading_spec.rb +45 -0
  95. data/spec/collection/accessors_spec.rb +31 -0
  96. data/spec/collection/collection_items_spec.rb +44 -0
  97. data/spec/collection/configurable_spec.rb +43 -0
  98. data/spec/collection/delegate_spec.rb +21 -0
  99. data/spec/collection/enumerable_spec.rb +27 -0
  100. data/spec/collection/href_spec.rb +17 -0
  101. data/spec/collection/meta_data_spec.rb +58 -0
  102. data/spec/collection/respond_to_spec.rb +20 -0
  103. data/spec/collection/to_a_spec.rb +34 -0
  104. data/spec/collection/to_ary_spec.rb +40 -0
  105. data/spec/collection/without_object_items_spec.rb +27 -0
  106. data/spec/complex/reduce_spec.rb +202 -0
  107. data/spec/concerns/record/request_spec.rb +78 -0
  108. data/spec/data/collection_spec.rb +56 -0
  109. data/spec/data/equality_spec.rb +23 -0
  110. data/spec/data/inspect_spec.rb +88 -0
  111. data/spec/data/is_item_or_collection_spec.rb +40 -0
  112. data/spec/data/item_spec.rb +106 -0
  113. data/spec/data/merge_spec.rb +27 -0
  114. data/spec/data/parent_spec.rb +39 -0
  115. data/spec/data/raw_spec.rb +48 -0
  116. data/spec/data/respond_to_spec.rb +26 -0
  117. data/spec/data/root_spec.rb +25 -0
  118. data/spec/data/select_spec.rb +27 -0
  119. data/spec/data/to_ary_spec.rb +28 -0
  120. data/spec/data/to_json_spec.rb +68 -0
  121. data/spec/dummy/Rakefile +8 -0
  122. data/spec/dummy/app/assets/images/.keep +0 -0
  123. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  124. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  125. data/spec/dummy/app/controllers/application_controller.rb +26 -0
  126. data/spec/dummy/app/controllers/automatic_authentication_controller.rb +29 -0
  127. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  128. data/spec/dummy/app/controllers/error_handling_with_chains_controller.rb +36 -0
  129. data/spec/dummy/app/controllers/extended_rollbar_controller.rb +10 -0
  130. data/spec/dummy/app/controllers/option_blocks_controller.rb +15 -0
  131. data/spec/dummy/app/controllers/request_cycle_cache_controller.rb +27 -0
  132. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  133. data/spec/dummy/app/mailers/.keep +0 -0
  134. data/spec/dummy/app/models/.keep +0 -0
  135. data/spec/dummy/app/models/concerns/.keep +0 -0
  136. data/spec/dummy/app/models/concerns/dummy_customer/some_concern.rb +9 -0
  137. data/spec/dummy/app/models/dummy_customer.rb +7 -0
  138. data/spec/dummy/app/models/dummy_record.rb +6 -0
  139. data/spec/dummy/app/models/dummy_record_with_auto_oauth_provider.rb +6 -0
  140. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers1.rb +7 -0
  141. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers2.rb +7 -0
  142. data/spec/dummy/app/models/dummy_record_with_multiple_providers_per_endpoint.rb +6 -0
  143. data/spec/dummy/app/models/dummy_record_with_oauth.rb +7 -0
  144. data/spec/dummy/app/models/dummy_user.rb +6 -0
  145. data/spec/dummy/app/models/providers/customer_system.rb +7 -0
  146. data/spec/dummy/app/models/providers/internal_services.rb +7 -0
  147. data/spec/dummy/app/views/error_handling_with_chains/error.html.erb +1 -0
  148. data/spec/dummy/app/views/error_handling_with_chains/show.html.erb +3 -0
  149. data/spec/dummy/app/views/form_for.html.erb +5 -0
  150. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  151. data/spec/dummy/bin/bundle +5 -0
  152. data/spec/dummy/bin/rails +6 -0
  153. data/spec/dummy/bin/rake +6 -0
  154. data/spec/dummy/config.ru +6 -0
  155. data/spec/dummy/config/application.rb +16 -0
  156. data/spec/dummy/config/boot.rb +7 -0
  157. data/spec/dummy/config/environment.rb +7 -0
  158. data/spec/dummy/config/environments/development.rb +36 -0
  159. data/spec/dummy/config/environments/production.rb +77 -0
  160. data/spec/dummy/config/environments/test.rb +40 -0
  161. data/spec/dummy/config/initializers/assets.rb +10 -0
  162. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  163. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  164. data/spec/dummy/config/initializers/dhs.rb +5 -0
  165. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  166. data/spec/dummy/config/initializers/inflections.rb +18 -0
  167. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  168. data/spec/dummy/config/initializers/rollbar.rb +9 -0
  169. data/spec/dummy/config/initializers/session_store.rb +5 -0
  170. data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
  171. data/spec/dummy/config/locales/en.yml +23 -0
  172. data/spec/dummy/config/routes.rb +27 -0
  173. data/spec/dummy/config/secrets.yml +22 -0
  174. data/spec/dummy/lib/assets/.keep +0 -0
  175. data/spec/dummy/public/404.html +67 -0
  176. data/spec/dummy/public/422.html +67 -0
  177. data/spec/dummy/public/500.html +66 -0
  178. data/spec/dummy/public/favicon.ico +0 -0
  179. data/spec/endpoint/for_url_spec.rb +27 -0
  180. data/spec/extended_rollbar_spec.rb +67 -0
  181. data/spec/item/access_errors_spec.rb +31 -0
  182. data/spec/item/accessors_spec.rb +21 -0
  183. data/spec/item/add_error_spec.rb +21 -0
  184. data/spec/item/becomes_spec.rb +38 -0
  185. data/spec/item/blacklisted_keywords_spec.rb +28 -0
  186. data/spec/item/delegate_spec.rb +32 -0
  187. data/spec/item/destroy_spec.rb +113 -0
  188. data/spec/item/dig_spec.rb +29 -0
  189. data/spec/item/error_codes_spec.rb +55 -0
  190. data/spec/item/errors_spec.rb +324 -0
  191. data/spec/item/fetch_spec.rb +39 -0
  192. data/spec/item/getter_spec.rb +24 -0
  193. data/spec/item/internal_data_structure_spec.rb +37 -0
  194. data/spec/item/map_spec.rb +46 -0
  195. data/spec/item/nested_errors_spec.rb +27 -0
  196. data/spec/item/partial_update_spec.rb +168 -0
  197. data/spec/item/respond_to_spec.rb +31 -0
  198. data/spec/item/save_spec.rb +115 -0
  199. data/spec/item/setter_spec.rb +44 -0
  200. data/spec/item/translate_errors_spec.rb +257 -0
  201. data/spec/item/update_spec.rb +161 -0
  202. data/spec/item/validation_spec.rb +131 -0
  203. data/spec/item/warning_codes_spec.rb +55 -0
  204. data/spec/item/warnings_spec.rb +51 -0
  205. data/spec/option_blocks/ensure_reset_between_requests_spec.rb +23 -0
  206. data/spec/option_blocks/main_spec.rb +54 -0
  207. data/spec/pagination/link/current_page_spec.rb +19 -0
  208. data/spec/pagination/link/pages_left_spec.rb +36 -0
  209. data/spec/pagination/link/parallel_spec.rb +19 -0
  210. data/spec/pagination/link/total_spec.rb +45 -0
  211. data/spec/pagination/offset/pages_left_spec.rb +26 -0
  212. data/spec/pagination/parameters_spec.rb +59 -0
  213. data/spec/pagination/total_pages_spec.rb +51 -0
  214. data/spec/proxy/create_sub_resource_spec.rb +182 -0
  215. data/spec/proxy/load_spec.rb +75 -0
  216. data/spec/proxy/record_identification_spec.rb +35 -0
  217. data/spec/rails_helper.rb +13 -0
  218. data/spec/record/all_spec.rb +133 -0
  219. data/spec/record/attribute_assignment_spec.rb +28 -0
  220. data/spec/record/build_spec.rb +26 -0
  221. data/spec/record/cast_nested_data_spec.rb +80 -0
  222. data/spec/record/compact_spec.rb +93 -0
  223. data/spec/record/create_spec.rb +160 -0
  224. data/spec/record/creation_failed_spec.rb +55 -0
  225. data/spec/record/custom_setters_spec.rb +42 -0
  226. data/spec/record/definitions_spec.rb +29 -0
  227. data/spec/record/destroy_spec.rb +38 -0
  228. data/spec/record/dig_configuration_spec.rb +75 -0
  229. data/spec/record/dup_spec.rb +20 -0
  230. data/spec/record/endpoint_inheritance_spec.rb +65 -0
  231. data/spec/record/endpoint_options_spec.rb +51 -0
  232. data/spec/record/endpoint_priorities_spec.rb +24 -0
  233. data/spec/record/endpoints_spec.rb +96 -0
  234. data/spec/record/equality_spec.rb +27 -0
  235. data/spec/record/error_handling_integration_spec.rb +25 -0
  236. data/spec/record/error_handling_spec.rb +40 -0
  237. data/spec/record/expanded_spec.rb +69 -0
  238. data/spec/record/fetch_spec.rb +40 -0
  239. data/spec/record/find_by_chains_spec.rb +21 -0
  240. data/spec/record/find_by_spec.rb +76 -0
  241. data/spec/record/find_each_spec.rb +57 -0
  242. data/spec/record/find_in_batches_spec.rb +122 -0
  243. data/spec/record/find_in_parallel_spec.rb +67 -0
  244. data/spec/record/find_spec.rb +103 -0
  245. data/spec/record/first_spec.rb +39 -0
  246. data/spec/record/force_merge_spec.rb +55 -0
  247. data/spec/record/handle_includes_errors_spec.rb +33 -0
  248. data/spec/record/has_many_spec.rb +118 -0
  249. data/spec/record/has_one_spec.rb +114 -0
  250. data/spec/record/href_for_spec.rb +24 -0
  251. data/spec/record/ignore_errors_spec.rb +137 -0
  252. data/spec/record/immutable_chains_spec.rb +22 -0
  253. data/spec/record/includes_after_expansion_spec.rb +70 -0
  254. data/spec/record/includes_expanded_spec.rb +37 -0
  255. data/spec/record/includes_first_page_spec.rb +738 -0
  256. data/spec/record/includes_missing_spec.rb +57 -0
  257. data/spec/record/includes_spec.rb +690 -0
  258. data/spec/record/includes_warning_spec.rb +46 -0
  259. data/spec/record/item_key_spec.rb +81 -0
  260. data/spec/record/items_created_key_configuration_spec.rb +37 -0
  261. data/spec/record/last_spec.rb +64 -0
  262. data/spec/record/loading_twice_spec.rb +19 -0
  263. data/spec/record/mapping_spec.rb +103 -0
  264. data/spec/record/model_name_spec.rb +17 -0
  265. data/spec/record/new_spec.rb +106 -0
  266. data/spec/record/options_getter_spec.rb +25 -0
  267. data/spec/record/options_spec.rb +164 -0
  268. data/spec/record/paginatable_collection_spec.rb +360 -0
  269. data/spec/record/pagination_chain_spec.rb +101 -0
  270. data/spec/record/pagination_links_spec.rb +72 -0
  271. data/spec/record/pagination_spec.rb +71 -0
  272. data/spec/record/persisted_spec.rb +52 -0
  273. data/spec/record/provider_spec.rb +40 -0
  274. data/spec/record/references_spec.rb +95 -0
  275. data/spec/record/relation_caching_spec.rb +120 -0
  276. data/spec/record/reload_by_id_spec.rb +43 -0
  277. data/spec/record/reload_spec.rb +64 -0
  278. data/spec/record/request_spec.rb +90 -0
  279. data/spec/record/save_spec.rb +40 -0
  280. data/spec/record/scope_chains_spec.rb +39 -0
  281. data/spec/record/select_spec.rb +17 -0
  282. data/spec/record/to_ary_spec.rb +65 -0
  283. data/spec/record/to_hash_spec.rb +22 -0
  284. data/spec/record/to_json_spec.rb +22 -0
  285. data/spec/record/tracing_spec.rb +149 -0
  286. data/spec/record/update_spec.rb +61 -0
  287. data/spec/record/where_chains_spec.rb +57 -0
  288. data/spec/record/where_spec.rb +62 -0
  289. data/spec/record/where_values_hash_spec.rb +32 -0
  290. data/spec/request_cycle_cache_spec.rb +106 -0
  291. data/spec/require_dhs_spec.rb +9 -0
  292. data/spec/spec_helper.rb +6 -0
  293. data/spec/stubs/all_spec.rb +69 -0
  294. data/spec/support/fixtures/json/feedback.json +11 -0
  295. data/spec/support/fixtures/json/feedbacks.json +174 -0
  296. data/spec/support/fixtures/json/localina_content_ad.json +23 -0
  297. data/spec/support/load_json.rb +5 -0
  298. data/spec/support/request_cycle_cache.rb +10 -0
  299. data/spec/support/reset.rb +67 -0
  300. data/spec/views/form_for_spec.rb +20 -0
  301. metadata +783 -0
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+ RDoc::Task.new(:rdoc) do |rdoc|
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = 'DHS'
13
+ rdoc.options << '--line-numbers'
14
+ rdoc.rdoc_files.include('README.rdoc')
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ end
17
+
18
+ begin
19
+ require 'rspec/core/rake_task'
20
+ RSpec::Core::RakeTask.new(:spec)
21
+ task default: :spec
22
+ rescue LoadError
23
+ # no rspec available
24
+ end
25
+
26
+ Bundler::GemHelper.install_tasks
data/dhs.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
4
+
5
+ # Maintain your gem's version:
6
+ require 'dhs/version'
7
+
8
+ # Describe your gem and declare its dependencies:
9
+ Gem::Specification.new do |s|
10
+ s.name = 'dhs'
11
+ s.version = DHS::VERSION
12
+ s.authors = ['https://github.com/DePayFi/dhs/graphs/contributors']
13
+ s.email = ['engineering@depay.fi']
14
+ s.homepage = 'https://github.com/DePayFi/dhs'
15
+ s.summary = 'REST services accelerator: Rails gem providing an easy, active-record-like interface for http (hypermedia) json services'
16
+ s.description = 'DHS ia a Rails-Gem, providing an ActiveRecord like interface to access HTTP-JSON-Services from Rails Applications. Special features provided by this gem are: Multiple endpoint configuration per resource, active-record-like query-chains, scopes, error handling, relations, request cycle cache, batch processing, including linked resources (hypermedia), data maps (data accessing), nested-resource handling, ActiveModel like backend validation conversion, formbuilder-compatible, three types of pagination support, service configuration per resource, kaminari-support and much more.'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- spec/*`.split("\n")
20
+ s.require_paths = ['lib']
21
+
22
+ s.requirements << 'Ruby >= 3.0.0'
23
+ s.required_ruby_version = '>= 3.0.0'
24
+
25
+ s.add_dependency 'activemodel'
26
+ s.add_dependency 'activesupport', '>= 6'
27
+ s.add_dependency 'dhc'
28
+ s.add_dependency 'local_uri'
29
+
30
+ s.add_development_dependency 'capybara'
31
+ s.add_development_dependency 'json', '>= 1.8.2'
32
+ s.add_development_dependency 'pry'
33
+ s.add_development_dependency 'pry-byebug'
34
+ s.add_development_dependency 'rails', '>= 6'
35
+ s.add_development_dependency 'rollbar', '<= 2.24.0'
36
+ s.add_development_dependency 'rspec-rails', '>= 3.7.0'
37
+ s.add_development_dependency 'rubocop'
38
+ s.add_development_dependency 'rubocop-rspec'
39
+ s.add_development_dependency 'sprockets', '< 4'
40
+ s.add_development_dependency 'webmock'
41
+ s.add_development_dependency 'webrick'
42
+
43
+ s.license = 'GPL-3.0'
44
+ end
Binary file
data/lib/dhs.rb ADDED
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dhc'
4
+
5
+ module DHS
6
+ autoload :Configuration, 'dhs/concerns/configuration'
7
+ autoload :AutoloadRecords, 'dhs/concerns/autoload_records'
8
+ autoload :Collection, 'dhs/collection'
9
+ autoload :Complex, 'dhs/complex'
10
+ autoload :Config, 'dhs/config'
11
+ autoload :Data, 'dhs/data'
12
+ autoload :ExtendedRollbar, 'dhs/interceptors/extended_rollbar/interceptor'
13
+ autoload :Endpoint, 'dhs/endpoint'
14
+ autoload :Inspect, 'dhs/concerns/inspect'
15
+ module Interceptors
16
+ module AutoOauth
17
+ autoload :ThreadRegistry, 'dhs/interceptors/auto_oauth/thread_registry'
18
+ autoload :Interceptor, 'dhs/interceptors/auto_oauth/interceptor'
19
+ end
20
+
21
+ module RequestCycleCache
22
+ autoload :ThreadRegistry, 'dhs/interceptors/request_cycle_cache/thread_registry'
23
+ autoload :Interceptor, 'dhs/interceptors/request_cycle_cache/interceptor'
24
+ end
25
+
26
+ module ExtendedRollbar
27
+ autoload :ThreadRegistry, 'dhs/interceptors/extended_rollbar/thread_registry'
28
+ autoload :Interceptor, 'dhs/interceptors/extended_rollbar/interceptor'
29
+ autoload :Handler, 'dhs/interceptors/extended_rollbar/handler'
30
+ end
31
+ end
32
+ autoload :IsHref, 'dhs/concerns/is_href'
33
+ autoload :Item, 'dhs/item'
34
+ autoload :OAuth, 'dhs/concerns/o_auth.rb'
35
+ autoload :OptionBlocks, 'dhs/concerns/option_blocks'
36
+ autoload :Pagination, 'dhs/pagination/base'
37
+ module Pagination
38
+ autoload :Offset, 'dhs/pagination/offset'
39
+ autoload :Page, 'dhs/pagination/page'
40
+ autoload :TotalPages, 'dhs/pagination/total_pages'
41
+ autoload :Start, 'dhs/pagination/start'
42
+ autoload :Link, 'dhs/pagination/link'
43
+ end
44
+ autoload :Problems, 'dhs/problems/base'
45
+ module Problems
46
+ autoload :Base, 'dhs/problems/base'
47
+ autoload :Errors, 'dhs/problems/errors'
48
+ autoload :Nested, 'dhs/problems/nested/base'
49
+ module Nested
50
+ autoload :Base, 'dhs/problems/nested/base'
51
+ autoload :Errors, 'dhs/problems/nested/errors'
52
+ autoload :Warnings, 'dhs/problems/nested/warnings'
53
+ end
54
+ autoload :Warnings, 'dhs/problems/warnings'
55
+ end
56
+ autoload :Proxy, 'dhs/proxy'
57
+ autoload :Record, 'dhs/record'
58
+ autoload :Unprocessable, 'dhs/unprocessable'
59
+
60
+ include Configuration
61
+ include AutoloadRecords if defined?(Rails)
62
+ include OptionBlocks
63
+
64
+ require 'dhs/record' # as dhs records in an application are directly inheriting it
65
+
66
+ require 'dhs/railtie' if defined?(Rails)
67
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A collection is a special type of data
4
+ # that contains multiple items
5
+ class DHS::Collection < DHS::Proxy
6
+ autoload :HandleNested,
7
+ 'dhs/concerns/collection/handle_nested'
8
+ autoload :InternalCollection,
9
+ 'dhs/concerns/collection/internal_collection'
10
+
11
+ include HandleNested
12
+ include InternalCollection
13
+ include Create
14
+
15
+ METHOD_NAMES_EXLCUDED_FROM_WRAPPING = %w(to_a to_ary map).freeze
16
+
17
+ delegate :select, :length, :size, :insert, to: :_collection
18
+ delegate :_record, :_raw, to: :_data
19
+ delegate :limit, :count, :total, :offset, :current_page, :start,
20
+ :next?, :previous?, to: :_pagination
21
+
22
+ def _pagination
23
+ _record.pagination(_data)
24
+ end
25
+
26
+ def href
27
+ return _data._raw[:href] if _data._raw.is_a? Hash
28
+ nil
29
+ end
30
+
31
+ def _collection
32
+ @_collection ||= begin
33
+ raw = _data._raw if _data._raw.is_a?(Array)
34
+ raw ||= _data.access(input: _data._raw, record: _record)
35
+ Collection.new(raw, _data, _record)
36
+ end
37
+ end
38
+
39
+ def collection?
40
+ true
41
+ end
42
+
43
+ def item?
44
+ false
45
+ end
46
+
47
+ def raw_items
48
+ if _raw.is_a?(Array)
49
+ _raw
50
+ else
51
+ access(input: _raw, record: _record)
52
+ end
53
+ end
54
+
55
+ protected
56
+
57
+ def method_missing(name, *args, **keyword_args, &block)
58
+ if _collection.respond_to?(name)
59
+ value = _collection.send(name, *args, **keyword_args, &block)
60
+ record = DHS::Record.for_url(value[:href]) if value.is_a?(Hash) && value[:href]
61
+ record ||= _record
62
+ value = enclose_item_in_data(value) if value.is_a?(Hash)
63
+ return value if METHOD_NAMES_EXLCUDED_FROM_WRAPPING.include?(name.to_s)
64
+ wrap_return(value, record, name, args)
65
+ elsif _data._raw.is_a?(Hash)
66
+ get(name, *args, **keyword_args)
67
+ end
68
+ end
69
+
70
+ def respond_to_missing?(name, _include_all = false)
71
+ # We accept every message that does not belong to set of keywords and is not a setter
72
+ !BLACKLISTED_KEYWORDS.include?(name.to_s) && !name.to_s[/=$/]
73
+ end
74
+
75
+ private
76
+
77
+ # Encloses accessed collection item
78
+ # by wrapping it in an DHS::Item
79
+ def enclose_item_in_data(value)
80
+ data = DHS::Data.new(value, _data, _record)
81
+ item = DHS::Item.new(data)
82
+ DHS::Data.new(item, _data)
83
+ end
84
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Complex is the main data structure for includes:
4
+ # Can be for example :place, [:place, :customer],
5
+ # [{ places: [:customer] }, { places: { customer: :contract }}]
6
+ # and so on
7
+ class DHS::Complex
8
+ attr_reader :data
9
+
10
+ def reduce!(data)
11
+ @data = if data.is_a?(DHS::Complex)
12
+ data.data
13
+ elsif data.is_a?(Array) && !data.empty?
14
+ data.inject(DHS::Complex.new.reduce!([])) { |acc, datum| acc.merge!(DHS::Complex.new.reduce!(datum)) }.data
15
+ elsif data.is_a?(Hash) && !data.empty?
16
+ data.map { |k, v| [k, DHS::Complex.new.reduce!(v)] }.to_h
17
+ else
18
+ data
19
+ end
20
+
21
+ self
22
+ end
23
+
24
+ def self.reduce(data)
25
+ new.reduce!(data).unwrap
26
+ end
27
+
28
+ def merge!(other)
29
+ if data.is_a?(Symbol)
30
+ merge_into_symbol!(other)
31
+ elsif data.is_a?(Array)
32
+ merge_into_array!(other)
33
+ elsif data.is_a?(Hash)
34
+ merge_into_hash!(other)
35
+ elsif unwrap != other.unwrap
36
+ raise ArgumentError, "Cannot merge #{unwrap} with #{other.unwrap}"
37
+ end
38
+
39
+ self
40
+ end
41
+
42
+ def unwrap
43
+ if data.is_a?(Array)
44
+ result = data.map(&:unwrap)
45
+ result.empty? ? nil : result
46
+ elsif data.is_a?(Hash)
47
+ result = data.map { |k, v| [k, v.unwrap] }.to_h
48
+ result.empty? ? nil : result
49
+ else
50
+ data
51
+ end
52
+ end
53
+
54
+ def ==(other)
55
+ unwrap == other.unwrap
56
+ end
57
+
58
+ private
59
+
60
+ def merge_into_array!(other)
61
+ if data.empty?
62
+ @data = other.data
63
+ elsif other.data.is_a?(Symbol)
64
+ merge_symbol_into_array!(other)
65
+ elsif other.data.is_a?(Array)
66
+ merge_array_into_array!(other)
67
+ elsif other.data.is_a?(Hash)
68
+ merge_hash_into_array!(other)
69
+ end
70
+ end
71
+
72
+ def merge_symbol_into_array!(other)
73
+ return if data.include?(other)
74
+ data.push(other)
75
+ end
76
+
77
+ def merge_array_into_array!(other)
78
+ @data = other.data.inject(self) { |acc, datum| acc.merge!(datum) }.data
79
+ end
80
+
81
+ def merge_hash_into_array!(other)
82
+ @data = [].tap do |new_data|
83
+ data.each do |element|
84
+ if element.data.is_a?(Symbol)
85
+ # remove keys that were in the hash
86
+ new_data << element unless other.data.key?(element.data)
87
+ elsif element.data.is_a?(Array)
88
+ new_data << element
89
+ elsif element.data.is_a?(Hash)
90
+ new_data << element.merge!(other)
91
+ end
92
+ end
93
+ end
94
+
95
+ # add it to the array if there was no hash to merge it
96
+ data.push(other) if data.none? { |element| element.data.is_a?(Hash) }
97
+ end
98
+
99
+ def merge_into_hash!(other)
100
+ if data.empty?
101
+ @data = other.data
102
+ elsif other.data.is_a?(Symbol)
103
+ merge_symbol_into_hash!(other)
104
+ elsif other.data.is_a?(Array)
105
+ merge_array_into_hash!(other)
106
+ elsif other.data.is_a?(Hash)
107
+ merge_hash_into_hash!(other)
108
+ end
109
+ end
110
+
111
+ def merge_symbol_into_hash!(other)
112
+ return if data.key?(other.data)
113
+
114
+ @data = [DHS::Complex.new.reduce!(data), other]
115
+ end
116
+
117
+ def merge_array_into_hash!(other)
118
+ @data = other.data.inject(self) { |acc, datum| acc.merge!(datum) }.data
119
+ end
120
+
121
+ def merge_hash_into_hash!(other)
122
+ other.data.each do |k, v|
123
+ data[k] = if data.key?(k)
124
+ data[k].merge!(v)
125
+ else
126
+ v
127
+ end
128
+ end
129
+ end
130
+
131
+ def merge_into_symbol!(other)
132
+ if other.data.is_a?(Symbol)
133
+ merge_symbol_into_symbol!(other)
134
+ elsif other.data.is_a?(Array)
135
+ merge_array_into_symbol!(other)
136
+ elsif other.data.is_a?(Hash)
137
+ merge_hash_into_symbol!(other)
138
+ end
139
+ end
140
+
141
+ def merge_symbol_into_symbol!(other)
142
+ return if other.data == data
143
+
144
+ @data = [DHS::Complex.new.reduce!(data), other]
145
+ end
146
+
147
+ def merge_array_into_symbol!(other)
148
+ @data = other.data.unshift(DHS::Complex.new.reduce!(data)).uniq { |a| a.unwrap }
149
+ end
150
+
151
+ def merge_hash_into_symbol!(other)
152
+ @data = if other.data.key?(data)
153
+ other.data
154
+ else
155
+ [DHS::Complex.new.reduce!(data), other]
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ # Preload all the DHS::Records that are defined in app/models/*
6
+ # in order to collect record endpoints and to be able to identify records from hrefs
7
+ # and not only from model constant names (which is different to ActiveRecord)
8
+ module AutoloadRecords
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ class Engine < Rails::Engine
13
+ initializer 'Load all DHS::Records from app/models/**' do |app|
14
+ Middleware.require_records
15
+ next if app.config.cache_classes
16
+
17
+ app.config.middleware.use Middleware
18
+ end
19
+
20
+ class Middleware
21
+ def initialize(app)
22
+ @app = app
23
+ end
24
+
25
+ def call(env)
26
+ self.class.require_records
27
+ @app.call(env)
28
+ end
29
+
30
+ def self.model_files
31
+ Dir.glob(Rails.root.join('app', 'models', '**', '*.rb'))
32
+ end
33
+
34
+ def self.require_direct_inheritance
35
+ model_files.sort.map do |file|
36
+ next unless File.read(file).match('DHS::Record')
37
+ require_dependency file
38
+ file.split('models/').last.gsub('.rb', '').classify
39
+ end.compact
40
+ end
41
+
42
+ def self.require_inheriting_records(parents)
43
+ model_files.each do |file|
44
+ file_content = File.read(file)
45
+ next if parents.none? { |parent| file_content.match(/\b#{parent}\b/) }
46
+ next if file_content.match?('extend ActiveSupport::Concern')
47
+ require_dependency file
48
+ end
49
+ end
50
+
51
+ def self.require_records
52
+ require_inheriting_records(require_direct_inheritance)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class DHS::Collection < DHS::Proxy
6
+
7
+ # Handles pontentially (deep-)nested collections
8
+ # Examples:
9
+ # [ { name: 'Steve '} ]
10
+ # { items: [ { name: 'Steve' } ] }
11
+ # { response: { business: [ { name: 'Steve' } ] } }
12
+ module HandleNested
13
+ extend ActiveSupport::Concern
14
+
15
+ delegate :access, :nest, :concat, to: :class
16
+
17
+ module ClassMethods
18
+ # Access potentially nested collection of items
19
+ def access(input:, record: nil)
20
+ input.dig(*items_key(record))
21
+ end
22
+
23
+ # Initializes nested collection
24
+ def nest(input:, value: nil, record: nil)
25
+ input[items_key(record)] = value
26
+ end
27
+
28
+ # Concats existing nested collection of items
29
+ # with given items
30
+ def concat(input:, items:, record: nil)
31
+ input.dig(*items_key(record)).concat(items)
32
+ end
33
+
34
+ private
35
+
36
+ # Takes configured items key to access collection of items
37
+ # of falls back to the default key
38
+ def items_key(record)
39
+ record&.items_key || DHS::Record.items_key
40
+ end
41
+ end
42
+ end
43
+ end