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.
- checksums.yaml +7 -0
- data/.bundler-version +1 -0
- data/.gitignore +39 -0
- data/.rubocop.localch.yml +325 -0
- data/.rubocop.yml +52 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.activesupport4 +5 -0
- data/Gemfile.activesupport5 +4 -0
- data/LICENSE +674 -0
- data/README.md +2836 -0
- data/Rakefile +25 -0
- data/cider-ci.yml +6 -0
- data/cider-ci/bin/bundle +51 -0
- data/cider-ci/bin/ruby_install +8 -0
- data/cider-ci/bin/ruby_version +25 -0
- data/cider-ci/jobs/rspec-activesupport-4.yml +27 -0
- data/cider-ci/jobs/rspec-activesupport-5.yml +26 -0
- data/cider-ci/jobs/rspec-activesupport-latest.yml +24 -0
- data/cider-ci/jobs/rubocop.yml +18 -0
- data/cider-ci/task_components/bundle.yml +22 -0
- data/cider-ci/task_components/rspec.yml +37 -0
- data/cider-ci/task_components/rubocop.yml +29 -0
- data/cider-ci/task_components/ruby.yml +15 -0
- data/friday.yml +2 -0
- data/lhs.gemspec +43 -0
- data/lib/lhs.rb +100 -0
- data/lib/lhs/collection.rb +84 -0
- data/lib/lhs/complex.rb +158 -0
- data/lib/lhs/concerns/autoload_records.rb +55 -0
- data/lib/lhs/concerns/collection/handle_nested.rb +43 -0
- data/lib/lhs/concerns/collection/internal_collection.rb +49 -0
- data/lib/lhs/concerns/configuration.rb +20 -0
- data/lib/lhs/concerns/data/becomes.rb +18 -0
- data/lib/lhs/concerns/data/equality.rb +14 -0
- data/lib/lhs/concerns/data/json.rb +14 -0
- data/lib/lhs/concerns/data/to_hash.rb +14 -0
- data/lib/lhs/concerns/inspect.rb +70 -0
- data/lib/lhs/concerns/is_href.rb +15 -0
- data/lib/lhs/concerns/item/destroy.rb +38 -0
- data/lib/lhs/concerns/item/endpoint_lookup.rb +27 -0
- data/lib/lhs/concerns/item/save.rb +55 -0
- data/lib/lhs/concerns/item/update.rb +50 -0
- data/lib/lhs/concerns/item/validation.rb +61 -0
- data/lib/lhs/concerns/o_auth.rb +25 -0
- data/lib/lhs/concerns/option_blocks.rb +26 -0
- data/lib/lhs/concerns/proxy/accessors.rb +132 -0
- data/lib/lhs/concerns/proxy/create.rb +45 -0
- data/lib/lhs/concerns/proxy/link.rb +25 -0
- data/lib/lhs/concerns/proxy/problems.rb +27 -0
- data/lib/lhs/concerns/record/attribute_assignment.rb +25 -0
- data/lib/lhs/concerns/record/batch.rb +40 -0
- data/lib/lhs/concerns/record/chainable.rb +465 -0
- data/lib/lhs/concerns/record/configuration.rb +103 -0
- data/lib/lhs/concerns/record/create.rb +24 -0
- data/lib/lhs/concerns/record/custom_setters.rb +22 -0
- data/lib/lhs/concerns/record/destroy.rb +18 -0
- data/lib/lhs/concerns/record/endpoints.rb +108 -0
- data/lib/lhs/concerns/record/equality.rb +14 -0
- data/lib/lhs/concerns/record/find.rb +86 -0
- data/lib/lhs/concerns/record/find_by.rb +38 -0
- data/lib/lhs/concerns/record/first.rb +20 -0
- data/lib/lhs/concerns/record/href_for.rb +19 -0
- data/lib/lhs/concerns/record/last.rb +27 -0
- data/lib/lhs/concerns/record/mapping.rb +25 -0
- data/lib/lhs/concerns/record/merge.rb +26 -0
- data/lib/lhs/concerns/record/model.rb +23 -0
- data/lib/lhs/concerns/record/pagination.rb +47 -0
- data/lib/lhs/concerns/record/provider.rb +23 -0
- data/lib/lhs/concerns/record/relations.rb +26 -0
- data/lib/lhs/concerns/record/request.rb +620 -0
- data/lib/lhs/concerns/record/scope.rb +25 -0
- data/lib/lhs/concerns/record/tracing.rb +24 -0
- data/lib/lhs/concerns/record/update.rb +17 -0
- data/lib/lhs/config.rb +24 -0
- data/lib/lhs/data.rb +165 -0
- data/lib/lhs/endpoint.rb +12 -0
- data/lib/lhs/interceptors/auto_oauth/interceptor.rb +33 -0
- data/lib/lhs/interceptors/auto_oauth/thread_registry.rb +18 -0
- data/lib/lhs/interceptors/extended_rollbar/handler.rb +40 -0
- data/lib/lhs/interceptors/extended_rollbar/interceptor.rb +20 -0
- data/lib/lhs/interceptors/extended_rollbar/thread_registry.rb +19 -0
- data/lib/lhs/interceptors/request_cycle_cache/interceptor.rb +41 -0
- data/lib/lhs/interceptors/request_cycle_cache/thread_registry.rb +18 -0
- data/lib/lhs/item.rb +59 -0
- data/lib/lhs/pagination/base.rb +90 -0
- data/lib/lhs/pagination/link.rb +21 -0
- data/lib/lhs/pagination/offset.rb +22 -0
- data/lib/lhs/pagination/page.rb +18 -0
- data/lib/lhs/pagination/start.rb +22 -0
- data/lib/lhs/problems/base.rb +113 -0
- data/lib/lhs/problems/errors.rb +69 -0
- data/lib/lhs/problems/nested/base.rb +54 -0
- data/lib/lhs/problems/nested/errors.rb +16 -0
- data/lib/lhs/problems/nested/warnings.rb +15 -0
- data/lib/lhs/problems/warnings.rb +24 -0
- data/lib/lhs/proxy.rb +68 -0
- data/lib/lhs/railtie.rb +34 -0
- data/lib/lhs/record.rb +112 -0
- data/lib/lhs/rspec.rb +10 -0
- data/lib/lhs/test/stubbable_records.rb +34 -0
- data/lib/lhs/unprocessable.rb +6 -0
- data/lib/lhs/version.rb +5 -0
- data/script/ci/build.sh +18 -0
- data/spec/auto_oauth_spec.rb +169 -0
- data/spec/autoloading_spec.rb +48 -0
- data/spec/collection/accessors_spec.rb +31 -0
- data/spec/collection/collection_items_spec.rb +44 -0
- data/spec/collection/configurable_spec.rb +43 -0
- data/spec/collection/delegate_spec.rb +21 -0
- data/spec/collection/enumerable_spec.rb +27 -0
- data/spec/collection/href_spec.rb +17 -0
- data/spec/collection/meta_data_spec.rb +57 -0
- data/spec/collection/respond_to_spec.rb +20 -0
- data/spec/collection/to_a_spec.rb +34 -0
- data/spec/collection/to_ary_spec.rb +40 -0
- data/spec/collection/without_object_items_spec.rb +27 -0
- data/spec/complex/reduce_spec.rb +202 -0
- data/spec/concerns/record/request_spec.rb +78 -0
- data/spec/data/collection_spec.rb +56 -0
- data/spec/data/equality_spec.rb +23 -0
- data/spec/data/inspect_spec.rb +88 -0
- data/spec/data/is_item_or_collection_spec.rb +40 -0
- data/spec/data/item_spec.rb +106 -0
- data/spec/data/merge_spec.rb +27 -0
- data/spec/data/parent_spec.rb +39 -0
- data/spec/data/raw_spec.rb +48 -0
- data/spec/data/respond_to_spec.rb +26 -0
- data/spec/data/root_spec.rb +25 -0
- data/spec/data/select_spec.rb +27 -0
- data/spec/data/to_ary_spec.rb +28 -0
- data/spec/data/to_json_spec.rb +68 -0
- data/spec/dummy/Rakefile +8 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +26 -0
- data/spec/dummy/app/controllers/automatic_authentication_controller.rb +29 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/controllers/error_handling_with_chains_controller.rb +36 -0
- data/spec/dummy/app/controllers/extended_rollbar_controller.rb +10 -0
- data/spec/dummy/app/controllers/option_blocks_controller.rb +15 -0
- data/spec/dummy/app/controllers/request_cycle_cache_controller.rb +27 -0
- data/spec/dummy/app/helpers/application_helper.rb +4 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/dummy_customer.rb +6 -0
- data/spec/dummy/app/models/dummy_record.rb +6 -0
- data/spec/dummy/app/models/dummy_record_with_auto_oauth_provider.rb +6 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers1.rb +7 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_oauth_providers2.rb +7 -0
- data/spec/dummy/app/models/dummy_record_with_multiple_providers_per_endpoint.rb +6 -0
- data/spec/dummy/app/models/dummy_record_with_oauth.rb +7 -0
- data/spec/dummy/app/models/dummy_user.rb +6 -0
- data/spec/dummy/app/models/providers/customer_system.rb +7 -0
- data/spec/dummy/app/models/providers/internal_services.rb +7 -0
- data/spec/dummy/app/views/error_handling_with_chains/error.html.erb +1 -0
- data/spec/dummy/app/views/error_handling_with_chains/show.html.erb +3 -0
- data/spec/dummy/app/views/form_for.html.erb +5 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +5 -0
- data/spec/dummy/bin/rails +6 -0
- data/spec/dummy/bin/rake +6 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/config/application.rb +16 -0
- data/spec/dummy/config/boot.rb +7 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config/environments/development.rb +36 -0
- data/spec/dummy/config/environments/production.rb +77 -0
- data/spec/dummy/config/environments/test.rb +40 -0
- data/spec/dummy/config/initializers/assets.rb +10 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +18 -0
- data/spec/dummy/config/initializers/mime_types.rb +6 -0
- data/spec/dummy/config/initializers/rollbar.rb +9 -0
- data/spec/dummy/config/initializers/session_store.rb +5 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +27 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/endpoint/for_url_spec.rb +27 -0
- data/spec/extended_rollbar_spec.rb +67 -0
- data/spec/item/access_errors_spec.rb +33 -0
- data/spec/item/accessors_spec.rb +21 -0
- data/spec/item/add_error_spec.rb +21 -0
- data/spec/item/becomes_spec.rb +38 -0
- data/spec/item/blacklisted_keywords_spec.rb +30 -0
- data/spec/item/delegate_spec.rb +32 -0
- data/spec/item/destroy_spec.rb +113 -0
- data/spec/item/dig_spec.rb +29 -0
- data/spec/item/error_codes_spec.rb +55 -0
- data/spec/item/errors_spec.rb +324 -0
- data/spec/item/fetch_spec.rb +39 -0
- data/spec/item/getter_spec.rb +24 -0
- data/spec/item/internal_data_structure_spec.rb +37 -0
- data/spec/item/map_spec.rb +46 -0
- data/spec/item/nested_errors_spec.rb +28 -0
- data/spec/item/partial_update_spec.rb +170 -0
- data/spec/item/respond_to_spec.rb +31 -0
- data/spec/item/save_spec.rb +115 -0
- data/spec/item/setter_spec.rb +44 -0
- data/spec/item/translate_errors_spec.rb +257 -0
- data/spec/item/update_spec.rb +161 -0
- data/spec/item/validation_spec.rb +131 -0
- data/spec/item/warning_codes_spec.rb +55 -0
- data/spec/item/warnings_spec.rb +52 -0
- data/spec/option_blocks/ensure_reset_between_requests_spec.rb +23 -0
- data/spec/option_blocks/main_spec.rb +55 -0
- data/spec/pagination/link/current_page_spec.rb +19 -0
- data/spec/pagination/link/pages_left_spec.rb +36 -0
- data/spec/pagination/link/parallel_spec.rb +19 -0
- data/spec/pagination/link/total_spec.rb +46 -0
- data/spec/pagination/offset/pages_left_spec.rb +26 -0
- data/spec/pagination/parameters_spec.rb +61 -0
- data/spec/proxy/create_sub_resource_spec.rb +182 -0
- data/spec/proxy/load_spec.rb +75 -0
- data/spec/proxy/record_identification_spec.rb +35 -0
- data/spec/rails_helper.rb +13 -0
- data/spec/record/all_spec.rb +133 -0
- data/spec/record/attribute_assignment_spec.rb +28 -0
- data/spec/record/build_spec.rb +26 -0
- data/spec/record/cast_nested_data_spec.rb +78 -0
- data/spec/record/create_spec.rb +160 -0
- data/spec/record/creation_failed_spec.rb +55 -0
- data/spec/record/custom_setters_spec.rb +45 -0
- data/spec/record/definitions_spec.rb +29 -0
- data/spec/record/destroy_spec.rb +38 -0
- data/spec/record/dig_configuration_spec.rb +75 -0
- data/spec/record/dup_spec.rb +20 -0
- data/spec/record/endpoint_inheritance_spec.rb +65 -0
- data/spec/record/endpoint_options_spec.rb +52 -0
- data/spec/record/endpoint_priorities_spec.rb +26 -0
- data/spec/record/endpoints_spec.rb +96 -0
- data/spec/record/equality_spec.rb +27 -0
- data/spec/record/error_handling_integration_spec.rb +25 -0
- data/spec/record/error_handling_spec.rb +40 -0
- data/spec/record/expanded_spec.rb +69 -0
- data/spec/record/fetch_spec.rb +41 -0
- data/spec/record/find_by_chains_spec.rb +21 -0
- data/spec/record/find_by_spec.rb +76 -0
- data/spec/record/find_each_spec.rb +57 -0
- data/spec/record/find_in_batches_spec.rb +122 -0
- data/spec/record/find_in_parallel_spec.rb +67 -0
- data/spec/record/find_spec.rb +104 -0
- data/spec/record/first_spec.rb +39 -0
- data/spec/record/force_merge_spec.rb +56 -0
- data/spec/record/handle_includes_errors_spec.rb +32 -0
- data/spec/record/has_many_spec.rb +120 -0
- data/spec/record/has_one_spec.rb +116 -0
- data/spec/record/href_for_spec.rb +25 -0
- data/spec/record/ignore_errors_spec.rb +139 -0
- data/spec/record/immutable_chains_spec.rb +22 -0
- data/spec/record/includes_first_page_spec.rb +737 -0
- data/spec/record/includes_spec.rb +693 -0
- data/spec/record/includes_warning_spec.rb +46 -0
- data/spec/record/item_key_spec.rb +81 -0
- data/spec/record/items_created_key_configuration_spec.rb +37 -0
- data/spec/record/last_spec.rb +68 -0
- data/spec/record/loading_twice_spec.rb +19 -0
- data/spec/record/mapping_spec.rb +102 -0
- data/spec/record/model_name_spec.rb +17 -0
- data/spec/record/new_spec.rb +106 -0
- data/spec/record/options_getter_spec.rb +26 -0
- data/spec/record/options_spec.rb +164 -0
- data/spec/record/paginatable_collection_spec.rb +360 -0
- data/spec/record/pagination_chain_spec.rb +101 -0
- data/spec/record/pagination_links_spec.rb +72 -0
- data/spec/record/pagination_spec.rb +71 -0
- data/spec/record/persisted_spec.rb +52 -0
- data/spec/record/provider_spec.rb +41 -0
- data/spec/record/references_spec.rb +53 -0
- data/spec/record/relation_caching_spec.rb +121 -0
- data/spec/record/reload_by_id_spec.rb +43 -0
- data/spec/record/reload_spec.rb +65 -0
- data/spec/record/request_spec.rb +90 -0
- data/spec/record/save_spec.rb +40 -0
- data/spec/record/scope_chains_spec.rb +39 -0
- data/spec/record/select_spec.rb +17 -0
- data/spec/record/to_ary_spec.rb +65 -0
- data/spec/record/to_hash_spec.rb +22 -0
- data/spec/record/to_json_spec.rb +22 -0
- data/spec/record/tracing_spec.rb +154 -0
- data/spec/record/update_spec.rb +62 -0
- data/spec/record/where_chains_spec.rb +57 -0
- data/spec/record/where_spec.rb +62 -0
- data/spec/record/where_values_hash_spec.rb +32 -0
- data/spec/request_cycle_cache_spec.rb +106 -0
- data/spec/require_lhs_spec.rb +9 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/stubs/all_spec.rb +72 -0
- data/spec/support/fixtures/json/feedback.json +11 -0
- data/spec/support/fixtures/json/feedbacks.json +174 -0
- data/spec/support/fixtures/json/localina_content_ad.json +23 -0
- data/spec/support/load_json.rb +5 -0
- data/spec/support/request_cycle_cache.rb +10 -0
- data/spec/support/reset.rb +67 -0
- data/spec/views/form_for_spec.rb +20 -0
- metadata +776 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
|
|
5
|
+
class LHS::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 LHS::Proxy
|
|
6
|
+
|
|
7
|
+
module Link
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def record_from_link
|
|
13
|
+
LHS::Record.for_url(_data.href)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def endpoint_from_link
|
|
17
|
+
LHS::Endpoint.for_url(_data.href)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def params_from_link
|
|
21
|
+
return {} if !_data.href || !endpoint_from_link
|
|
22
|
+
LHC::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 LHS::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 ||= LHS::Problems::Errors.new(response, record)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def warnings
|
|
24
|
+
@warnings ||= LHS::Problems::Warnings.new(_raw, record)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class LHS::Record
|
|
4
|
+
|
|
5
|
+
module AttributeAssignment
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
def assign_attributes(new_attributes)
|
|
9
|
+
if !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 LHS::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 = LHS::Item.new(record)
|
|
16
|
+
yield new(LHS::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) || LHS::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
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
|
|
5
|
+
class LHS::Record
|
|
6
|
+
|
|
7
|
+
module Chainable
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
# You can start new option chains for already fetched records (needed for save, update, valid etc.)
|
|
11
|
+
def options(hash = nil)
|
|
12
|
+
return method_missing(:options) if hash.nil?
|
|
13
|
+
Chain.new(self.class, Option.new(hash), self)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ClassMethods
|
|
17
|
+
def where(args = nil)
|
|
18
|
+
if href?(args)
|
|
19
|
+
Chain.new(self, Option.new(url: args))
|
|
20
|
+
else
|
|
21
|
+
Chain.new(self, Parameter.new(args))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def fetch
|
|
26
|
+
Chain.new(self, nil).fetch
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def all(hash = nil)
|
|
30
|
+
chain = Chain.new(self, Parameter.new(hash))
|
|
31
|
+
chain._links.push(Option.new(all: true))
|
|
32
|
+
chain
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def expanded(options = nil)
|
|
36
|
+
Chain.new(self, Option.new(expanded: options || true))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def options(hash = nil)
|
|
40
|
+
Chain.new(self, Option.new(hash))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def page(page)
|
|
44
|
+
Chain.new(self, Pagination.new(page: page))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def per(limit)
|
|
48
|
+
Chain.new(self, Pagination.new(per: limit))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def limit(argument = nil)
|
|
52
|
+
Chain.new(self, Pagination.new(per: argument))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def rescue(error_class, handler)
|
|
56
|
+
Chain.new(self, ErrorHandling.new(error_class => handler))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def ignore(*error_classes)
|
|
60
|
+
chain = Chain.new(self, IgnoredError.new(error_classes.shift))
|
|
61
|
+
error_classes.each do |error_class|
|
|
62
|
+
chain._links.push(IgnoredError.new(error_class))
|
|
63
|
+
end
|
|
64
|
+
chain
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def includes_first_page(*args)
|
|
68
|
+
Chain.new(self, Include.new(Chain.unfold(args)))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def includes(*args)
|
|
72
|
+
chain = Chain.new(self, Include.new(Chain.unfold(args)))
|
|
73
|
+
chain.include_all!(args)
|
|
74
|
+
chain
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def references(*args)
|
|
78
|
+
Chain.new(self, Reference.new(Chain.unfold(args)))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Link: A part of a chain
|
|
83
|
+
class Link
|
|
84
|
+
|
|
85
|
+
attr_reader :data
|
|
86
|
+
|
|
87
|
+
def initialize(data = nil)
|
|
88
|
+
@data = data
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def [](parameter)
|
|
92
|
+
@data[parameter]
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Parameter: Part of the chain that will be forwarded to the endpoint as parameter
|
|
97
|
+
class Parameter < Link
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Option: Part of the chain that will be used to configure the communication with the endpoint
|
|
101
|
+
class Option < Link
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Pagination: Part of the chain that will be used to controll pagination
|
|
105
|
+
class Pagination < Link
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Include: Part of the chain that will be used to include linked resources
|
|
109
|
+
class Include < Link
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Reference: Part of the chain that will be used to pass options to requests
|
|
113
|
+
# made to include linked resources
|
|
114
|
+
class Reference < Link
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# ErrorHandling: Catch and resolve errors when resolving the chain
|
|
118
|
+
class ErrorHandling < Link
|
|
119
|
+
delegate :call, to: :handler
|
|
120
|
+
|
|
121
|
+
def handler
|
|
122
|
+
@data.values.first
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def class
|
|
126
|
+
@data.keys.first
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def to_a
|
|
130
|
+
[self.class, handler]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# IgnoredError: Ignore certain LHC errors when resolving the chain
|
|
135
|
+
class IgnoredError < Link
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# A sequence of links
|
|
139
|
+
class Chain
|
|
140
|
+
|
|
141
|
+
excluded_methods = []
|
|
142
|
+
# Instance exec is required for scope chains
|
|
143
|
+
excluded_methods += [:instance_exec]
|
|
144
|
+
# Clone is required to have immutable
|
|
145
|
+
excluded_methods += [:clone]
|
|
146
|
+
# We are asked from ruby to not delegate __send__ and object_id
|
|
147
|
+
excluded_methods += [:__send__, :object_id]
|
|
148
|
+
delegated_methods = Object.instance_methods - excluded_methods
|
|
149
|
+
delegate(*delegated_methods, to: :resolve)
|
|
150
|
+
|
|
151
|
+
attr_accessor :_links
|
|
152
|
+
|
|
153
|
+
def self.unfold(args)
|
|
154
|
+
(args.size == 1) ? args[0] : args
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def initialize(record_class, link, record = nil)
|
|
158
|
+
@record_class = record_class
|
|
159
|
+
@record = record
|
|
160
|
+
self._links = [link].compact
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def create(data = {})
|
|
164
|
+
@record_class.create(data, resolved_options)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def create!(data = {})
|
|
168
|
+
@record_class.create!(data, resolved_options)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def save!(options = nil)
|
|
172
|
+
options ||= {}
|
|
173
|
+
@record.save!(resolved_options.merge(options))
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def save(options = nil)
|
|
177
|
+
options ||= {}
|
|
178
|
+
@record.save(resolved_options.merge(options))
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def destroy(options = nil)
|
|
182
|
+
options ||= {}
|
|
183
|
+
options = options.respond_to?(:to_h) ? options : { id: options }
|
|
184
|
+
if @record
|
|
185
|
+
@record.destroy(resolved_options.merge(options))
|
|
186
|
+
else
|
|
187
|
+
@record_class.destroy(options, resolved_options)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def update(data = {}, options = nil)
|
|
192
|
+
options ||= {}
|
|
193
|
+
@record.update(data, resolved_options.merge(options))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def update!(data = {}, options = nil)
|
|
197
|
+
options ||= {}
|
|
198
|
+
@record.update!(data, resolved_options.merge(options))
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def partial_update(data = {}, options = nil)
|
|
202
|
+
options ||= {}
|
|
203
|
+
@record.update(data, resolved_options.merge(options), true)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def partial_update!(data = {}, options = nil)
|
|
207
|
+
options ||= {}
|
|
208
|
+
@record.update!(data, resolved_options.merge(options), true)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def valid?(options = nil)
|
|
212
|
+
options ||= {}
|
|
213
|
+
@record.valid?(resolved_options.merge(options))
|
|
214
|
+
end
|
|
215
|
+
alias validate valid?
|
|
216
|
+
|
|
217
|
+
def where(args = nil)
|
|
218
|
+
if LHS::Record.href?(args)
|
|
219
|
+
push(Option.new(url: args))
|
|
220
|
+
else
|
|
221
|
+
push(Parameter.new(args))
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def all(hash = nil)
|
|
226
|
+
push([Parameter.new(hash), Option.new(all: true)])
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def expanded(options = nil)
|
|
230
|
+
push(Option.new(expanded: options || true))
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def options(hash = nil)
|
|
234
|
+
push(Option.new(hash))
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def ignore(*error_classes)
|
|
238
|
+
chain = push(IgnoredError.new(error_classes.shift))
|
|
239
|
+
error_classes.each do |error_class|
|
|
240
|
+
chain._links.push(IgnoredError.new(error_class))
|
|
241
|
+
end
|
|
242
|
+
chain
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def page(page)
|
|
246
|
+
push(Pagination.new(page: page))
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def per(per)
|
|
250
|
+
push(Pagination.new(per: per))
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def limit(argument = nil)
|
|
254
|
+
return resolve.limit if argument.blank?
|
|
255
|
+
push(Pagination.new(per: argument))
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def rescue(error_class, handler)
|
|
259
|
+
push(ErrorHandling.new(error_class => handler))
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def includes_first_page(*args)
|
|
263
|
+
push(Include.new(Chain.unfold(args)))
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def includes(*args)
|
|
267
|
+
chain = push(Include.new(Chain.unfold(args)))
|
|
268
|
+
chain.include_all!(args)
|
|
269
|
+
chain
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def references(*args)
|
|
273
|
+
push(Reference.new(Chain.unfold(args)))
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def find(*args)
|
|
277
|
+
@record_class.find(*args.push(resolved_options))
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def find_by(params = {})
|
|
281
|
+
@record_class.find_by(params, resolved_options)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def find_by!(params = {})
|
|
285
|
+
@record_class.find_by!(params, resolved_options)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def first!
|
|
289
|
+
@record_class.first!(resolved_options)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Returns a hash of where conditions
|
|
293
|
+
def where_values_hash
|
|
294
|
+
chain_parameters
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Returns a hash of options
|
|
298
|
+
def option_values_hash
|
|
299
|
+
chain_options
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Returns a hash of pagination values
|
|
303
|
+
def pagination_values_hash
|
|
304
|
+
chain_pagination
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Returns a hash of include conditions
|
|
308
|
+
def includes_values
|
|
309
|
+
chain_includes
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Returns a hash of reference options
|
|
313
|
+
def references_values
|
|
314
|
+
chain_references
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Adds additional .references(name_of_linked_resource: { all: true })
|
|
318
|
+
# to all linked resources included with includes_all
|
|
319
|
+
def include_all!(args)
|
|
320
|
+
includes_all_to_references(args).each do |reference|
|
|
321
|
+
_links.push(reference)
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def fetch
|
|
326
|
+
resolve
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
protected
|
|
330
|
+
|
|
331
|
+
def method_missing(name, *args, &block)
|
|
332
|
+
scope = @record_class.scopes[name]
|
|
333
|
+
return instance_exec(*args, &scope) if scope
|
|
334
|
+
resolve.send(name, *args, &block)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def respond_to_missing?(name, include_all = false)
|
|
338
|
+
@record_class.scopes[name] ||
|
|
339
|
+
resolve.respond_to?(name, include_all)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def resolve
|
|
343
|
+
return @resolved if defined?(@resolved)
|
|
344
|
+
response_data = @record_class.request(resolved_options)
|
|
345
|
+
@resolved = response_data ? @record_class.new(response_data) : nil
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def resolved_options
|
|
349
|
+
options = chain_options
|
|
350
|
+
options = options.deep_merge(params: chain_parameters.merge(chain_pagination))
|
|
351
|
+
options = options.merge(rescue: chain_error_handler) if chain_error_handler.present?
|
|
352
|
+
options = options.merge(ignore: chain_ignored_errors) if chain_ignored_errors.present?
|
|
353
|
+
options = options.merge(including: chain_includes) if chain_includes.present?
|
|
354
|
+
options = options.merge(referencing: chain_references) if chain_references.present?
|
|
355
|
+
options
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
private
|
|
359
|
+
|
|
360
|
+
# Translates includes_all(resources:) to the internal datastructure
|
|
361
|
+
# references(resource: { all: true })
|
|
362
|
+
def includes_all_to_references(args, parent = nil)
|
|
363
|
+
references = []
|
|
364
|
+
if args.is_a?(Array)
|
|
365
|
+
includes_all_to_references_for_arrays!(references, args, parent)
|
|
366
|
+
elsif args.is_a?(Hash)
|
|
367
|
+
includes_all_to_references_for_hash!(references, args, parent)
|
|
368
|
+
elsif args.is_a?(Symbol)
|
|
369
|
+
includes_all_to_references_for_symbol!(references, args, parent)
|
|
370
|
+
end
|
|
371
|
+
references
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def includes_all_to_references_for_arrays!(references, args, parent)
|
|
375
|
+
args.each do |part|
|
|
376
|
+
references.concat(includes_all_to_references(part, parent))
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def includes_all_to_references_for_hash!(references, args, parent)
|
|
381
|
+
args.each do |key, value|
|
|
382
|
+
parent ||= { all: true }
|
|
383
|
+
references.concat([Reference.new(key => parent)])
|
|
384
|
+
references.concat(includes_all_to_references(value, parent))
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def includes_all_to_references_for_symbol!(references, args, parent)
|
|
389
|
+
if parent.present?
|
|
390
|
+
parent[args] = { all: true }
|
|
391
|
+
else
|
|
392
|
+
references.concat([Reference.new(args => { all: true })])
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def push(link)
|
|
397
|
+
clone = self.clone
|
|
398
|
+
clone._links = _links + [link].flatten.compact
|
|
399
|
+
clone
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def chain_parameters
|
|
403
|
+
@chain_parameters ||= merge_links(_links.select { |link| link.is_a? Parameter })
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def chain_options
|
|
407
|
+
@chain_options ||= merge_links(_links.select { |link| link.is_a? Option })
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def chain_error_handler
|
|
411
|
+
@chain_error_handler ||= _links.select { |link| link.is_a? ErrorHandling }
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
def chain_ignored_errors
|
|
415
|
+
@chain_ignored_errors ||= _links
|
|
416
|
+
.select { |link| link.is_a? IgnoredError }
|
|
417
|
+
.map { |link| link.data }
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def chain_pagination
|
|
421
|
+
@chain_pagination ||= resolve_pagination(_links.select { |link| link.is_a? Pagination })
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def chain_includes
|
|
425
|
+
@chain_includes ||= LHS::Complex.reduce(
|
|
426
|
+
_links
|
|
427
|
+
.select { |link| link.is_a?(Include) && link.data.present? }
|
|
428
|
+
.map { |link| link.data }
|
|
429
|
+
)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def chain_references
|
|
433
|
+
@chain_references ||= LHS::Complex.reduce(
|
|
434
|
+
_links
|
|
435
|
+
.select { |link| link.is_a?(Reference) && link.data.present? }
|
|
436
|
+
.map { |link| link.data }
|
|
437
|
+
)
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def resolve_pagination(links)
|
|
441
|
+
return {} if links.empty?
|
|
442
|
+
page = 1
|
|
443
|
+
per = LHS::Pagination::Base::DEFAULT_LIMIT
|
|
444
|
+
links.each do |link|
|
|
445
|
+
page = link[:page] if link[:page].present?
|
|
446
|
+
per = link[:per] if link[:per].present?
|
|
447
|
+
end
|
|
448
|
+
pagination = @record_class.pagination_class
|
|
449
|
+
{
|
|
450
|
+
@record_class.pagination_key(:parameter) => pagination.page_to_offset(page, per),
|
|
451
|
+
@record_class.limit_key(:parameter) => per
|
|
452
|
+
}
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def merge_links(links)
|
|
456
|
+
hash = {}
|
|
457
|
+
links.each do |link|
|
|
458
|
+
next if link.data.blank?
|
|
459
|
+
hash.deep_merge!(link.data)
|
|
460
|
+
end
|
|
461
|
+
hash
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
end
|