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
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class DHS::Item < DHS::Proxy
6
+ autoload :EndpointLookup,
7
+ 'dhs/concerns/item/endpoint_lookup'
8
+
9
+ module Update
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ include EndpointLookup
14
+ end
15
+
16
+ def update(params, options = nil, partial_update = false)
17
+ update!(params, options, partial_update)
18
+ rescue DHC::Error => e
19
+ self.errors = DHS::Problems::Errors.new(e.response, record)
20
+ false
21
+ end
22
+
23
+ def partial_update(params, options = nil)
24
+ update(params, options, true)
25
+ end
26
+
27
+ def partial_update!(params, options = nil)
28
+ update!(params, options, true)
29
+ end
30
+
31
+ def update!(params, options = {}, partial_update = false)
32
+ options = options.present? ? options.dup : {}
33
+ partial_record = _record.new(DHS::Data.new(params, _data.parent, _record))
34
+ _data.merge_raw!(partial_record._data)
35
+ data = _data._raw.dup
36
+ partial_data = partial_record._data._raw.dup
37
+ url = url_for_persistance!(data, options)
38
+ data_sent = partial_update ? partial_data.extract!(*data.keys) : data
39
+ response_data = record.request(
40
+ options.merge(
41
+ method: options.fetch(:method, :post),
42
+ url: url,
43
+ body: data_sent
44
+ )
45
+ )
46
+ _data.merge_raw!(response_data.unwrap(:item_created_key))
47
+ true
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class DHS::Item < DHS::Proxy
6
+
7
+ module Validation
8
+ extend ActiveSupport::Concern
9
+
10
+ def valid?(options = {})
11
+ options ||= {}
12
+ errors.clear
13
+ endpoint = validation_endpoint
14
+ raise 'No endpoint found to perform validations! See here: https://github.com/DePayFi/dhs#validation' unless endpoint
15
+ record = DHS::Record.for_url(endpoint.url)
16
+ params = merge_validation_params!(endpoint).merge options.fetch(:params, {})
17
+ url = validation_url(endpoint)
18
+ run_validation!(record, options, url, params)
19
+ true
20
+ rescue DHC::Error => e
21
+ self.errors = DHS::Problems::Errors.new(e.response, record)
22
+ false
23
+ end
24
+ alias validate valid?
25
+
26
+ private
27
+
28
+ def validation_url(endpoint)
29
+ url = endpoint.url
30
+ action = endpoint.options[:validates][:path].presence
31
+ url = "#{url}/#{action}" if action.present?
32
+ url
33
+ end
34
+
35
+ def merge_validation_params!(endpoint)
36
+ validates_params = endpoint.options[:validates].reject { |key, _| key.to_sym == :path }
37
+ params = endpoint.options.fetch(:params, {}).merge(params_from_link)
38
+ params = params.merge(validates_params) if validates_params.is_a?(Hash)
39
+ params
40
+ end
41
+
42
+ def run_validation!(record, options, url, params)
43
+ record.request(
44
+ options.merge(
45
+ url: url,
46
+ method: :post,
47
+ params: params,
48
+ body: _data
49
+ )
50
+ )
51
+ end
52
+
53
+ def validation_endpoint
54
+ endpoint = endpoint_from_link if _data.href # take embeded first
55
+ endpoint ||= record.find_endpoint(_data._raw)
56
+ validates = endpoint.options&.fetch(:validates, false)
57
+ raise 'Endpoint does not support validations!' unless validates
58
+ endpoint
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ module DHS
6
+ module OAuth
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ prepend_before_action :dhs_store_oauth_access_token
11
+ end
12
+
13
+ private
14
+
15
+ def dhs_store_oauth_access_token
16
+ dhs_check_auto_oauth_enabled!
17
+ DHS::Interceptors::AutoOauth::ThreadRegistry.access_token = instance_exec(&DHS.config.auto_oauth)
18
+ end
19
+
20
+ def dhs_check_auto_oauth_enabled!
21
+ return if DHS.config.auto_oauth.present? && DHS.config.auto_oauth.is_a?(Proc)
22
+ raise 'You have to enable DHS.config.auto_oauth by passing a proc returning an access token!'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/per_thread_registry'
5
+
6
+ module DHS
7
+ module OptionBlocks
8
+ extend ActiveSupport::Concern
9
+
10
+ class CurrentOptionBlock
11
+ # Using ActiveSupports PerThreadRegistry to be able to support Active Support v4.
12
+ # Will switch to thread_mattr_accessor (which comes with Activesupport) when we dropping support for Active Support v4.
13
+ extend ActiveSupport::PerThreadRegistry
14
+ attr_accessor :options
15
+ end
16
+
17
+ module ClassMethods
18
+ def options(options, &block)
19
+ CurrentOptionBlock.options = options
20
+ block.call
21
+ ensure
22
+ CurrentOptionBlock.options = nil
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class DHS::Proxy
6
+ module Accessors
7
+ extend ActiveSupport::Concern
8
+
9
+ # Keywords that would not be forwarded via method missing
10
+ # FIXME: Extend the set of keywords
11
+ BLACKLISTED_KEYWORDS = %w(new proxy_association).freeze
12
+
13
+ delegate :dig, :fetch, to: :_raw, allow_nil: true
14
+
15
+ def clear_cache!
16
+ @cache = nil
17
+ end
18
+
19
+ private
20
+
21
+ def set(name, args)
22
+ clear_cache!
23
+ return set_attribute_directly(name, args.try(:first)) if name != :[]=
24
+ key = args[0]
25
+ value = args[1]
26
+ _data._raw[key.to_sym] = value
27
+ end
28
+
29
+ def set_attribute_directly(name, value)
30
+ key = name.to_s.gsub(/=$/, '')
31
+ _data._raw[key.to_sym] = value
32
+ end
33
+
34
+ def get(name, *args)
35
+ name = args.first if name == :[]
36
+ value = _data._raw[name.to_s] if _data._raw
37
+ if value.nil? && _data._raw.present? && _data._raw.is_a?(Hash)
38
+ value = _data._raw[name.to_sym]
39
+ value = _data._raw[name.to_s.classify.to_sym] if value.nil?
40
+ end
41
+
42
+ record = DHS::Record.for_url(value[:href]) if value.is_a?(Hash) && value[:href]
43
+ access_item(value, record, name) ||
44
+ access_collection(value, record, name) ||
45
+ convert(value)
46
+ end
47
+
48
+ def accessing_item?(value, record)
49
+ return false unless value.is_a?(Hash)
50
+ return false if record && access(input: value, record: record).present?
51
+ return false if !record && value[:items].present?
52
+ true
53
+ end
54
+
55
+ def accessing_collection?(value, record)
56
+ return true if value.is_a?(Array)
57
+ return true if value.is_a?(Hash) && record && access(input: value, record: record).present?
58
+ return true if value.is_a?(Hash) && !record && value[:items].present?
59
+ end
60
+
61
+ def convert(value)
62
+ return value unless value.is_a?(String)
63
+ if date_time?(value)
64
+ Time.zone.parse(value)
65
+ elsif date?(value)
66
+ Date.parse(value)
67
+ else
68
+ value
69
+ end
70
+ end
71
+
72
+ def access_item(value, record, name)
73
+ return unless accessing_item?(value, record)
74
+ wrap_return(value, record, name)
75
+ end
76
+
77
+ def access_collection(value, record, name)
78
+ return unless accessing_collection?(value, record)
79
+ collection_data = DHS::Data.new(value, _data)
80
+ collection = DHS::Collection.new(collection_data)
81
+ wrap_return(collection, record, name)
82
+ end
83
+
84
+ # Wraps the return value with a record class.
85
+ # Adds errors and warnings if existing.
86
+ # Returns plain data if no record class was found.
87
+ def wrap_return(value, record, name, args = nil)
88
+ name = args.first if name == :[]
89
+ return value unless worth_wrapping?(value)
90
+ data = (value.is_a?(DHS::Data) || value.is_a?(DHS::Record)) ? value : DHS::Data.new(value, _data, record, _request)
91
+ data.errors = DHS::Problems::Nested::Errors.new(errors, name) if errors.any?
92
+ data.warnings = DHS::Problems::Nested::Warnings.new(warnings, name) if warnings.any?
93
+ if _record && _record._relations[name]
94
+ klass = _record._relations[name][:record_class_name].constantize
95
+ return cache.compute_if_absent(klass) do
96
+ data.becomes(klass, errors: data.errors, warnings: data.warnings)
97
+ end
98
+ elsif record && !value.is_a?(DHS::Record)
99
+ return data.becomes(record, errors: data.errors, warnings: data.warnings)
100
+ end
101
+ data
102
+ end
103
+
104
+ def worth_wrapping?(value)
105
+ value.is_a?(DHS::Proxy) ||
106
+ value.is_a?(DHS::Data) ||
107
+ value.is_a?(DHS::Record) ||
108
+ value.is_a?(Hash) ||
109
+ value.is_a?(Array)
110
+ end
111
+
112
+ def date?(value)
113
+ value[date_time_regex, :date].presence
114
+ end
115
+
116
+ def time?(value)
117
+ value[date_time_regex, :time].presence
118
+ end
119
+
120
+ def date_time?(value)
121
+ date?(value) && time?(value)
122
+ end
123
+
124
+ def date_time_regex
125
+ /(?<date>\d{4}-\d{2}-\d{2})?(?<time>T\d{2}:\d{2}(:\d{2}(\.\d*.\d{2}:\d{2})*)?)?/
126
+ end
127
+
128
+ def cache
129
+ @cache ||= Concurrent::Map.new
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class DHS::Proxy
6
+
7
+ module Create
8
+ extend ActiveSupport::Concern
9
+
10
+ def create(data = {}, options = nil)
11
+ record_creation!(options) do
12
+ record_from_link.create(
13
+ data,
14
+ options_for_creation(options)
15
+ )
16
+ end
17
+ end
18
+
19
+ def create!(data = {}, options = nil)
20
+ record_creation!(options) do
21
+ record_from_link.create!(
22
+ data,
23
+ options_for_creation(options)
24
+ )
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def record_creation!(options)
31
+ raise(ArgumentError, 'Record already exists') if _raw.keys != [:href] && item?
32
+
33
+ record = yield
34
+ # Needed to handle unexpanded collection which looks the same as item
35
+ reload!(options) if record.errors.empty?
36
+ record
37
+ end
38
+
39
+ def options_for_creation(options)
40
+ return options if params_from_link.blank?
41
+ options = {} if options.blank?
42
+ options.deep_merge(params: params_from_link)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class DHS::Proxy
6
+
7
+ module Link
8
+ extend ActiveSupport::Concern
9
+
10
+ private
11
+
12
+ def record_from_link
13
+ DHS::Record.for_url(_data.href)
14
+ end
15
+
16
+ def endpoint_from_link
17
+ DHS::Endpoint.for_url(_data.href)
18
+ end
19
+
20
+ def params_from_link
21
+ return {} if !_data.href || !endpoint_from_link
22
+ DHC::Endpoint.values_as_params(endpoint_from_link.url, _data.href)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class DHS::Proxy
6
+
7
+ module Problems
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ attr_writer :errors, :warnings
12
+ end
13
+
14
+ def initialize(data)
15
+ super(data)
16
+ end
17
+
18
+ def errors
19
+ response = (_raw.present? && _raw.is_a?(Hash) && _raw[:field_errors]) ? OpenStruct.new(body: _raw.to_json) : nil
20
+ @errors ||= DHS::Problems::Errors.new(response, record)
21
+ end
22
+
23
+ def warnings
24
+ @warnings ||= DHS::Problems::Warnings.new(_raw, record)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DHS::Record
4
+
5
+ module AttributeAssignment
6
+ extend ActiveSupport::Concern
7
+
8
+ def assign_attributes(new_attributes)
9
+ unless new_attributes.respond_to?(:stringify_keys)
10
+ raise ArgumentError, "When assigning attributes, you must pass a hash as an argument, #{new_attributes.class} passed."
11
+ end
12
+ return if new_attributes.empty?
13
+
14
+ _assign_attributes(new_attributes)
15
+ end
16
+
17
+ private
18
+
19
+ def _assign_attributes(attributes)
20
+ attributes.each do |key, value|
21
+ public_send(:"#{key}=", value)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class DHS::Record
6
+
7
+ module Batch
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+ # Process single entries fetched in batches
12
+ def find_each(options = {})
13
+ find_in_batches(options) do |records|
14
+ records.each do |record|
15
+ item = DHS::Item.new(record)
16
+ yield new(DHS::Data.new(item, records._data, self))
17
+ end
18
+ end
19
+ end
20
+
21
+ # Process batches of entries
22
+ def find_in_batches(options = {})
23
+ raise 'No block given' unless block_given?
24
+ options = options.dup
25
+ start = options.delete(:start) || 1
26
+ batch_size = options.delete(:batch_size) || DHS::Pagination::Base::DEFAULT_LIMIT
27
+ loop do # as suggested by Matz
28
+ options = options.dup
29
+ options[:params] = (options[:params] || {}).merge(limit_key(:parameter) => batch_size, pagination_key(:parameter) => start)
30
+ data = request(options)
31
+ batch_size = data._raw.dig(*limit_key(:body))
32
+ left = data._raw.dig(*total_key).to_i - data._raw.dig(*pagination_key(:body)).to_i - data._raw.dig(*limit_key(:body)).to_i
33
+ yield new(data)
34
+ break if left <= 0
35
+ start += batch_size
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end