lhs 24.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 (306) hide show
  1. checksums.yaml +7 -0
  2. data/.bundler-version +1 -0
  3. data/.gitignore +39 -0
  4. data/.rubocop.localch.yml +325 -0
  5. data/.rubocop.yml +52 -0
  6. data/.ruby-version +1 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.activesupport4 +5 -0
  9. data/Gemfile.activesupport5 +4 -0
  10. data/LICENSE +674 -0
  11. data/README.md +2836 -0
  12. data/Rakefile +25 -0
  13. data/cider-ci.yml +6 -0
  14. data/cider-ci/bin/bundle +51 -0
  15. data/cider-ci/bin/ruby_install +8 -0
  16. data/cider-ci/bin/ruby_version +25 -0
  17. data/cider-ci/jobs/rspec-activesupport-4.yml +27 -0
  18. data/cider-ci/jobs/rspec-activesupport-5.yml +26 -0
  19. data/cider-ci/jobs/rspec-activesupport-latest.yml +24 -0
  20. data/cider-ci/jobs/rubocop.yml +18 -0
  21. data/cider-ci/task_components/bundle.yml +22 -0
  22. data/cider-ci/task_components/rspec.yml +37 -0
  23. data/cider-ci/task_components/rubocop.yml +29 -0
  24. data/cider-ci/task_components/ruby.yml +15 -0
  25. data/friday.yml +2 -0
  26. data/lhs.gemspec +43 -0
  27. data/lib/lhs.rb +100 -0
  28. data/lib/lhs/collection.rb +84 -0
  29. data/lib/lhs/complex.rb +158 -0
  30. data/lib/lhs/concerns/autoload_records.rb +55 -0
  31. data/lib/lhs/concerns/collection/handle_nested.rb +43 -0
  32. data/lib/lhs/concerns/collection/internal_collection.rb +49 -0
  33. data/lib/lhs/concerns/configuration.rb +20 -0
  34. data/lib/lhs/concerns/data/becomes.rb +18 -0
  35. data/lib/lhs/concerns/data/equality.rb +14 -0
  36. data/lib/lhs/concerns/data/json.rb +14 -0
  37. data/lib/lhs/concerns/data/to_hash.rb +14 -0
  38. data/lib/lhs/concerns/inspect.rb +70 -0
  39. data/lib/lhs/concerns/is_href.rb +15 -0
  40. data/lib/lhs/concerns/item/destroy.rb +38 -0
  41. data/lib/lhs/concerns/item/endpoint_lookup.rb +27 -0
  42. data/lib/lhs/concerns/item/save.rb +55 -0
  43. data/lib/lhs/concerns/item/update.rb +50 -0
  44. data/lib/lhs/concerns/item/validation.rb +61 -0
  45. data/lib/lhs/concerns/o_auth.rb +25 -0
  46. data/lib/lhs/concerns/option_blocks.rb +26 -0
  47. data/lib/lhs/concerns/proxy/accessors.rb +132 -0
  48. data/lib/lhs/concerns/proxy/create.rb +45 -0
  49. data/lib/lhs/concerns/proxy/link.rb +25 -0
  50. data/lib/lhs/concerns/proxy/problems.rb +27 -0
  51. data/lib/lhs/concerns/record/attribute_assignment.rb +25 -0
  52. data/lib/lhs/concerns/record/batch.rb +40 -0
  53. data/lib/lhs/concerns/record/chainable.rb +465 -0
  54. data/lib/lhs/concerns/record/configuration.rb +103 -0
  55. data/lib/lhs/concerns/record/create.rb +24 -0
  56. data/lib/lhs/concerns/record/custom_setters.rb +22 -0
  57. data/lib/lhs/concerns/record/destroy.rb +18 -0
  58. data/lib/lhs/concerns/record/endpoints.rb +108 -0
  59. data/lib/lhs/concerns/record/equality.rb +14 -0
  60. data/lib/lhs/concerns/record/find.rb +86 -0
  61. data/lib/lhs/concerns/record/find_by.rb +38 -0
  62. data/lib/lhs/concerns/record/first.rb +20 -0
  63. data/lib/lhs/concerns/record/href_for.rb +19 -0
  64. data/lib/lhs/concerns/record/last.rb +27 -0
  65. data/lib/lhs/concerns/record/mapping.rb +25 -0
  66. data/lib/lhs/concerns/record/merge.rb +26 -0
  67. data/lib/lhs/concerns/record/model.rb +23 -0
  68. data/lib/lhs/concerns/record/pagination.rb +47 -0
  69. data/lib/lhs/concerns/record/provider.rb +23 -0
  70. data/lib/lhs/concerns/record/relations.rb +26 -0
  71. data/lib/lhs/concerns/record/request.rb +620 -0
  72. data/lib/lhs/concerns/record/scope.rb +25 -0
  73. data/lib/lhs/concerns/record/tracing.rb +24 -0
  74. data/lib/lhs/concerns/record/update.rb +17 -0
  75. data/lib/lhs/config.rb +24 -0
  76. data/lib/lhs/data.rb +165 -0
  77. data/lib/lhs/endpoint.rb +12 -0
  78. data/lib/lhs/interceptors/auto_oauth/interceptor.rb +33 -0
  79. data/lib/lhs/interceptors/auto_oauth/thread_registry.rb +18 -0
  80. data/lib/lhs/interceptors/extended_rollbar/handler.rb +40 -0
  81. data/lib/lhs/interceptors/extended_rollbar/interceptor.rb +20 -0
  82. data/lib/lhs/interceptors/extended_rollbar/thread_registry.rb +19 -0
  83. data/lib/lhs/interceptors/request_cycle_cache/interceptor.rb +41 -0
  84. data/lib/lhs/interceptors/request_cycle_cache/thread_registry.rb +18 -0
  85. data/lib/lhs/item.rb +59 -0
  86. data/lib/lhs/pagination/base.rb +90 -0
  87. data/lib/lhs/pagination/link.rb +21 -0
  88. data/lib/lhs/pagination/offset.rb +22 -0
  89. data/lib/lhs/pagination/page.rb +18 -0
  90. data/lib/lhs/pagination/start.rb +22 -0
  91. data/lib/lhs/problems/base.rb +113 -0
  92. data/lib/lhs/problems/errors.rb +69 -0
  93. data/lib/lhs/problems/nested/base.rb +54 -0
  94. data/lib/lhs/problems/nested/errors.rb +16 -0
  95. data/lib/lhs/problems/nested/warnings.rb +15 -0
  96. data/lib/lhs/problems/warnings.rb +24 -0
  97. data/lib/lhs/proxy.rb +68 -0
  98. data/lib/lhs/railtie.rb +34 -0
  99. data/lib/lhs/record.rb +112 -0
  100. data/lib/lhs/rspec.rb +10 -0
  101. data/lib/lhs/test/stubbable_records.rb +34 -0
  102. data/lib/lhs/unprocessable.rb +6 -0
  103. data/lib/lhs/version.rb +5 -0
  104. data/script/ci/build.sh +18 -0
  105. data/spec/auto_oauth_spec.rb +169 -0
  106. data/spec/autoloading_spec.rb +48 -0
  107. data/spec/collection/accessors_spec.rb +31 -0
  108. data/spec/collection/collection_items_spec.rb +44 -0
  109. data/spec/collection/configurable_spec.rb +43 -0
  110. data/spec/collection/delegate_spec.rb +21 -0
  111. data/spec/collection/enumerable_spec.rb +27 -0
  112. data/spec/collection/href_spec.rb +17 -0
  113. data/spec/collection/meta_data_spec.rb +57 -0
  114. data/spec/collection/respond_to_spec.rb +20 -0
  115. data/spec/collection/to_a_spec.rb +34 -0
  116. data/spec/collection/to_ary_spec.rb +40 -0
  117. data/spec/collection/without_object_items_spec.rb +27 -0
  118. data/spec/complex/reduce_spec.rb +202 -0
  119. data/spec/concerns/record/request_spec.rb +78 -0
  120. data/spec/data/collection_spec.rb +56 -0
  121. data/spec/data/equality_spec.rb +23 -0
  122. data/spec/data/inspect_spec.rb +88 -0
  123. data/spec/data/is_item_or_collection_spec.rb +40 -0
  124. data/spec/data/item_spec.rb +106 -0
  125. data/spec/data/merge_spec.rb +27 -0
  126. data/spec/data/parent_spec.rb +39 -0
  127. data/spec/data/raw_spec.rb +48 -0
  128. data/spec/data/respond_to_spec.rb +26 -0
  129. data/spec/data/root_spec.rb +25 -0
  130. data/spec/data/select_spec.rb +27 -0
  131. data/spec/data/to_ary_spec.rb +28 -0
  132. data/spec/data/to_json_spec.rb +68 -0
  133. data/spec/dummy/Rakefile +8 -0
  134. data/spec/dummy/app/assets/images/.keep +0 -0
  135. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  136. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  137. data/spec/dummy/app/controllers/application_controller.rb +26 -0
  138. data/spec/dummy/app/controllers/automatic_authentication_controller.rb +29 -0
  139. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  140. data/spec/dummy/app/controllers/error_handling_with_chains_controller.rb +36 -0
  141. data/spec/dummy/app/controllers/extended_rollbar_controller.rb +10 -0
  142. data/spec/dummy/app/controllers/option_blocks_controller.rb +15 -0
  143. data/spec/dummy/app/controllers/request_cycle_cache_controller.rb +27 -0
  144. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  145. data/spec/dummy/app/mailers/.keep +0 -0
  146. data/spec/dummy/app/models/.keep +0 -0
  147. data/spec/dummy/app/models/concerns/.keep +0 -0
  148. data/spec/dummy/app/models/dummy_customer.rb +6 -0
  149. data/spec/dummy/app/models/dummy_record.rb +6 -0
  150. data/spec/dummy/app/models/dummy_record_with_auto_oauth_provider.rb +6 -0
  151. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers1.rb +7 -0
  152. data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers2.rb +7 -0
  153. data/spec/dummy/app/models/dummy_record_with_multiple_providers_per_endpoint.rb +6 -0
  154. data/spec/dummy/app/models/dummy_record_with_oauth.rb +7 -0
  155. data/spec/dummy/app/models/dummy_user.rb +6 -0
  156. data/spec/dummy/app/models/providers/customer_system.rb +7 -0
  157. data/spec/dummy/app/models/providers/internal_services.rb +7 -0
  158. data/spec/dummy/app/views/error_handling_with_chains/error.html.erb +1 -0
  159. data/spec/dummy/app/views/error_handling_with_chains/show.html.erb +3 -0
  160. data/spec/dummy/app/views/form_for.html.erb +5 -0
  161. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  162. data/spec/dummy/bin/bundle +5 -0
  163. data/spec/dummy/bin/rails +6 -0
  164. data/spec/dummy/bin/rake +6 -0
  165. data/spec/dummy/config.ru +6 -0
  166. data/spec/dummy/config/application.rb +16 -0
  167. data/spec/dummy/config/boot.rb +7 -0
  168. data/spec/dummy/config/environment.rb +7 -0
  169. data/spec/dummy/config/environments/development.rb +36 -0
  170. data/spec/dummy/config/environments/production.rb +77 -0
  171. data/spec/dummy/config/environments/test.rb +40 -0
  172. data/spec/dummy/config/initializers/assets.rb +10 -0
  173. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  174. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  175. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  176. data/spec/dummy/config/initializers/inflections.rb +18 -0
  177. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  178. data/spec/dummy/config/initializers/rollbar.rb +9 -0
  179. data/spec/dummy/config/initializers/session_store.rb +5 -0
  180. data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
  181. data/spec/dummy/config/locales/en.yml +23 -0
  182. data/spec/dummy/config/routes.rb +27 -0
  183. data/spec/dummy/config/secrets.yml +22 -0
  184. data/spec/dummy/lib/assets/.keep +0 -0
  185. data/spec/dummy/public/404.html +67 -0
  186. data/spec/dummy/public/422.html +67 -0
  187. data/spec/dummy/public/500.html +66 -0
  188. data/spec/dummy/public/favicon.ico +0 -0
  189. data/spec/endpoint/for_url_spec.rb +27 -0
  190. data/spec/extended_rollbar_spec.rb +67 -0
  191. data/spec/item/access_errors_spec.rb +33 -0
  192. data/spec/item/accessors_spec.rb +21 -0
  193. data/spec/item/add_error_spec.rb +21 -0
  194. data/spec/item/becomes_spec.rb +38 -0
  195. data/spec/item/blacklisted_keywords_spec.rb +30 -0
  196. data/spec/item/delegate_spec.rb +32 -0
  197. data/spec/item/destroy_spec.rb +113 -0
  198. data/spec/item/dig_spec.rb +29 -0
  199. data/spec/item/error_codes_spec.rb +55 -0
  200. data/spec/item/errors_spec.rb +324 -0
  201. data/spec/item/fetch_spec.rb +39 -0
  202. data/spec/item/getter_spec.rb +24 -0
  203. data/spec/item/internal_data_structure_spec.rb +37 -0
  204. data/spec/item/map_spec.rb +46 -0
  205. data/spec/item/nested_errors_spec.rb +28 -0
  206. data/spec/item/partial_update_spec.rb +170 -0
  207. data/spec/item/respond_to_spec.rb +31 -0
  208. data/spec/item/save_spec.rb +115 -0
  209. data/spec/item/setter_spec.rb +44 -0
  210. data/spec/item/translate_errors_spec.rb +257 -0
  211. data/spec/item/update_spec.rb +161 -0
  212. data/spec/item/validation_spec.rb +131 -0
  213. data/spec/item/warning_codes_spec.rb +55 -0
  214. data/spec/item/warnings_spec.rb +52 -0
  215. data/spec/option_blocks/ensure_reset_between_requests_spec.rb +23 -0
  216. data/spec/option_blocks/main_spec.rb +55 -0
  217. data/spec/pagination/link/current_page_spec.rb +19 -0
  218. data/spec/pagination/link/pages_left_spec.rb +36 -0
  219. data/spec/pagination/link/parallel_spec.rb +19 -0
  220. data/spec/pagination/link/total_spec.rb +46 -0
  221. data/spec/pagination/offset/pages_left_spec.rb +26 -0
  222. data/spec/pagination/parameters_spec.rb +61 -0
  223. data/spec/proxy/create_sub_resource_spec.rb +182 -0
  224. data/spec/proxy/load_spec.rb +75 -0
  225. data/spec/proxy/record_identification_spec.rb +35 -0
  226. data/spec/rails_helper.rb +13 -0
  227. data/spec/record/all_spec.rb +133 -0
  228. data/spec/record/attribute_assignment_spec.rb +28 -0
  229. data/spec/record/build_spec.rb +26 -0
  230. data/spec/record/cast_nested_data_spec.rb +78 -0
  231. data/spec/record/create_spec.rb +160 -0
  232. data/spec/record/creation_failed_spec.rb +55 -0
  233. data/spec/record/custom_setters_spec.rb +45 -0
  234. data/spec/record/definitions_spec.rb +29 -0
  235. data/spec/record/destroy_spec.rb +38 -0
  236. data/spec/record/dig_configuration_spec.rb +75 -0
  237. data/spec/record/dup_spec.rb +20 -0
  238. data/spec/record/endpoint_inheritance_spec.rb +65 -0
  239. data/spec/record/endpoint_options_spec.rb +52 -0
  240. data/spec/record/endpoint_priorities_spec.rb +26 -0
  241. data/spec/record/endpoints_spec.rb +96 -0
  242. data/spec/record/equality_spec.rb +27 -0
  243. data/spec/record/error_handling_integration_spec.rb +25 -0
  244. data/spec/record/error_handling_spec.rb +40 -0
  245. data/spec/record/expanded_spec.rb +69 -0
  246. data/spec/record/fetch_spec.rb +41 -0
  247. data/spec/record/find_by_chains_spec.rb +21 -0
  248. data/spec/record/find_by_spec.rb +76 -0
  249. data/spec/record/find_each_spec.rb +57 -0
  250. data/spec/record/find_in_batches_spec.rb +122 -0
  251. data/spec/record/find_in_parallel_spec.rb +67 -0
  252. data/spec/record/find_spec.rb +104 -0
  253. data/spec/record/first_spec.rb +39 -0
  254. data/spec/record/force_merge_spec.rb +56 -0
  255. data/spec/record/handle_includes_errors_spec.rb +32 -0
  256. data/spec/record/has_many_spec.rb +120 -0
  257. data/spec/record/has_one_spec.rb +116 -0
  258. data/spec/record/href_for_spec.rb +25 -0
  259. data/spec/record/ignore_errors_spec.rb +139 -0
  260. data/spec/record/immutable_chains_spec.rb +22 -0
  261. data/spec/record/includes_first_page_spec.rb +737 -0
  262. data/spec/record/includes_spec.rb +693 -0
  263. data/spec/record/includes_warning_spec.rb +46 -0
  264. data/spec/record/item_key_spec.rb +81 -0
  265. data/spec/record/items_created_key_configuration_spec.rb +37 -0
  266. data/spec/record/last_spec.rb +68 -0
  267. data/spec/record/loading_twice_spec.rb +19 -0
  268. data/spec/record/mapping_spec.rb +102 -0
  269. data/spec/record/model_name_spec.rb +17 -0
  270. data/spec/record/new_spec.rb +106 -0
  271. data/spec/record/options_getter_spec.rb +26 -0
  272. data/spec/record/options_spec.rb +164 -0
  273. data/spec/record/paginatable_collection_spec.rb +360 -0
  274. data/spec/record/pagination_chain_spec.rb +101 -0
  275. data/spec/record/pagination_links_spec.rb +72 -0
  276. data/spec/record/pagination_spec.rb +71 -0
  277. data/spec/record/persisted_spec.rb +52 -0
  278. data/spec/record/provider_spec.rb +41 -0
  279. data/spec/record/references_spec.rb +53 -0
  280. data/spec/record/relation_caching_spec.rb +121 -0
  281. data/spec/record/reload_by_id_spec.rb +43 -0
  282. data/spec/record/reload_spec.rb +65 -0
  283. data/spec/record/request_spec.rb +90 -0
  284. data/spec/record/save_spec.rb +40 -0
  285. data/spec/record/scope_chains_spec.rb +39 -0
  286. data/spec/record/select_spec.rb +17 -0
  287. data/spec/record/to_ary_spec.rb +65 -0
  288. data/spec/record/to_hash_spec.rb +22 -0
  289. data/spec/record/to_json_spec.rb +22 -0
  290. data/spec/record/tracing_spec.rb +154 -0
  291. data/spec/record/update_spec.rb +62 -0
  292. data/spec/record/where_chains_spec.rb +57 -0
  293. data/spec/record/where_spec.rb +62 -0
  294. data/spec/record/where_values_hash_spec.rb +32 -0
  295. data/spec/request_cycle_cache_spec.rb +106 -0
  296. data/spec/require_lhs_spec.rb +9 -0
  297. data/spec/spec_helper.rb +6 -0
  298. data/spec/stubs/all_spec.rb +72 -0
  299. data/spec/support/fixtures/json/feedback.json +11 -0
  300. data/spec/support/fixtures/json/feedbacks.json +174 -0
  301. data/spec/support/fixtures/json/localina_content_ad.json +23 -0
  302. data/spec/support/load_json.rb +5 -0
  303. data/spec/support/request_cycle_cache.rb +10 -0
  304. data/spec/support/reset.rb +67 -0
  305. data/spec/views/form_for_spec.rb +20 -0
  306. metadata +776 -0
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class LHS::Data
6
+
7
+ module Json
8
+ extend ActiveSupport::Concern
9
+
10
+ def as_json(options = {})
11
+ _data._raw.as_json(options)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class LHS::Data
6
+
7
+ module ToHash
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ delegate :to_h, :to_hash, to: :_raw, allow_nil: true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ module LHS
6
+ module Inspect
7
+ extend ActiveSupport::Concern
8
+
9
+ def inspect
10
+ [
11
+ [
12
+ [
13
+ [
14
+ to_s.match('LHS::Data') ? 'Data of ' : nil,
15
+ self.class
16
+ ].join,
17
+ _inspect_id
18
+ ].join(' '),
19
+ _inspect_path
20
+ ].compact.join("\n"),
21
+ pretty_raw
22
+ ].compact.join("\n")
23
+ end
24
+
25
+ private
26
+
27
+ def _inspect_id
28
+ _root.href || (_root.item? ? _root.id : nil) || object_id
29
+ end
30
+
31
+ def _inspect_path
32
+ current = self
33
+ path = []
34
+ _collect_parents_for_inspect!(path, current)
35
+ return if path.blank?
36
+ "> #{path.reverse.join(' > ')}"
37
+ end
38
+
39
+ def _collect_parents_for_inspect!(path, current)
40
+ return unless current.parent
41
+ parent_raw = current.parent._raw
42
+ if parent_raw.is_a?(Array)
43
+ parent_raw.each_with_index do |element, index|
44
+ path.push(index) if element == current._raw
45
+ end
46
+ elsif parent_raw.is_a?(Hash)
47
+ parent_raw.each do |key, value|
48
+ path.push(key) if value == current._raw
49
+ end
50
+ end
51
+ _collect_parents_for_inspect!(path, current.parent)
52
+ end
53
+
54
+ def pretty_raw
55
+ return if _raw.blank?
56
+ if _raw.is_a?(Array)
57
+ _raw
58
+ else
59
+ _raw.to_a.map do |key, value|
60
+ ":#{key} => " +
61
+ if value.is_a? String
62
+ "\"#{value}\""
63
+ else
64
+ value.to_s
65
+ end
66
+ end
67
+ end.join("\n")
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ module LHS
6
+ module IsHref
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ def href?(input)
11
+ input.is_a?(String) && %r{^https?://}.match(input).present?
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class LHS::Item < LHS::Proxy
6
+
7
+ module Destroy
8
+ extend ActiveSupport::Concern
9
+
10
+ def destroy(options = {})
11
+ options ||= {}
12
+ options = options.merge(method: :delete)
13
+ data = _data._raw.dup
14
+ url = url_for_deletion!(options, data)
15
+ options = options.merge(url: url)
16
+ _data._request = _data.class.request(options)._request
17
+ _data
18
+ end
19
+
20
+ private
21
+
22
+ def url_for_deletion!(options, data)
23
+ return href if href.present?
24
+ endpoint = endpoint_for_deletion(data, options)
25
+ endpoint.compile(
26
+ merge_data_with_options(data, options)
27
+ ).tap do
28
+ endpoint.remove_interpolated_params!(data)
29
+ endpoint.remove_interpolated_params!(options.fetch(:params, {}))
30
+ options.merge!(endpoint.options.merge(options)) if endpoint.options
31
+ end
32
+ end
33
+
34
+ def endpoint_for_deletion(data, options)
35
+ record.find_endpoint(merge_data_with_options(data, options))
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class LHS::Item < LHS::Proxy
6
+ module EndpointLookup
7
+ extend ActiveSupport::Concern
8
+
9
+ def url_for_persistance!(data, options)
10
+ return href if href.present?
11
+ endpoint = endpoint_for_persistance!(data, options)
12
+ endpoint.compile(
13
+ merge_data_with_options(data, options)
14
+ ).tap do
15
+ endpoint.remove_interpolated_params!(data)
16
+ endpoint.remove_interpolated_params!(options.fetch(:params, {}))
17
+ options.merge!(endpoint.options.merge(options)) if endpoint.options
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def endpoint_for_persistance!(data, options)
24
+ record.find_endpoint(merge_data_with_options(data, options))
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class LHS::Item < LHS::Proxy
6
+ autoload :EndpointLookup,
7
+ 'lhs/concerns/item/endpoint_lookup'
8
+
9
+ module Save
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ include EndpointLookup
14
+ end
15
+
16
+ def save(options = nil)
17
+ save!(options)
18
+ rescue LHC::Error
19
+ false
20
+ end
21
+
22
+ def save!(options = {})
23
+ options = options.present? ? options.dup : {}
24
+ data = _data._raw.dup
25
+ url = url_for_persistance!(data, options)
26
+ create_and_merge_data!(
27
+ apply_default_creation_options(options, url, data)
28
+ )
29
+ rescue LHC::Error => e
30
+ self.errors = LHS::Problems::Errors.new(e.response, record)
31
+ raise e
32
+ end
33
+
34
+ private
35
+
36
+ def apply_default_creation_options(options, url, data)
37
+ options = options.merge(method: :post, url: url, body: data)
38
+ options[:headers] ||= {}
39
+ options
40
+ end
41
+
42
+ def create_and_merge_data!(options)
43
+ response_data = record.request(options)
44
+ if response_data.present?
45
+ _data.merge_raw!(response_data.unwrap(:item_created_key))
46
+ response_headers = response_data._request.response.headers
47
+ end
48
+ if response_headers && response_headers['Location']
49
+ location_data = record.request(options.merge(url: response_headers['Location'], method: :get, body: nil))
50
+ _data.merge_raw!(location_data.unwrap(:item_created_key))
51
+ end
52
+ true
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+
5
+ class LHS::Item < LHS::Proxy
6
+ autoload :EndpointLookup,
7
+ 'lhs/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 LHC::Error => e
19
+ self.errors = LHS::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(LHS::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 LHS::Item < LHS::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/local-ch/lhs#validation' unless endpoint
15
+ record = LHS::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 LHC::Error => e
21
+ self.errors = LHS::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 LHS
6
+ module OAuth
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ prepend_before_action :lhs_store_oauth_access_token
11
+ end
12
+
13
+ private
14
+
15
+ def lhs_store_oauth_access_token
16
+ lhs_check_auto_oauth_enabled!
17
+ LHS::Interceptors::AutoOauth::ThreadRegistry.access_token = instance_exec(&LHS.config.auto_oauth)
18
+ end
19
+
20
+ def lhs_check_auto_oauth_enabled!
21
+ return if LHS.config.auto_oauth.present? && LHS.config.auto_oauth.is_a?(Proc)
22
+ raise 'You have to enable LHS.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 LHS
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 LHS::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)
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 = LHS::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 = LHS::Data.new(value, _data)
80
+ collection = LHS::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?(LHS::Data) || value.is_a?(LHS::Record)) ? value : LHS::Data.new(value, _data, record, _request)
91
+ data.errors = LHS::Problems::Nested::Errors.new(errors, name) if errors.any?
92
+ data.warnings = LHS::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?(LHS::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?(LHS::Proxy) ||
106
+ value.is_a?(LHS::Data) ||
107
+ value.is_a?(LHS::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