gooddata-edge 0.6.27.edge
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/.document +5 -0
- data/.gitignore +36 -0
- data/.rspec +3 -0
- data/.rubocop.yml +89 -0
- data/.yardopts +22 -0
- data/CHANGELOG.md +196 -0
- data/CLI.md +439 -0
- data/DEPENDENCIES.md +817 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/LICENSE +22 -0
- data/LICENSE.rb +5 -0
- data/README.md +75 -0
- data/Rakefile +179 -0
- data/TODO.md +32 -0
- data/authors.sh +4 -0
- data/bin/gooddata +7 -0
- data/dependency_decisions.yml +104 -0
- data/gooddata +9 -0
- data/gooddata.gemspec +63 -0
- data/lib/gooddata.rb +31 -0
- data/lib/gooddata/app/app.rb +16 -0
- data/lib/gooddata/bricks/base_downloader.rb +86 -0
- data/lib/gooddata/bricks/brick.rb +38 -0
- data/lib/gooddata/bricks/bricks.rb +15 -0
- data/lib/gooddata/bricks/middleware/aws_middleware.rb +29 -0
- data/lib/gooddata/bricks/middleware/base_middleware.rb +56 -0
- data/lib/gooddata/bricks/middleware/bench_middleware.rb +24 -0
- data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +37 -0
- data/lib/gooddata/bricks/middleware/decode_params_middleware.rb +20 -0
- data/lib/gooddata/bricks/middleware/fs_download_middleware.rb +48 -0
- data/lib/gooddata/bricks/middleware/fs_upload_middleware.rb +36 -0
- data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +39 -0
- data/lib/gooddata/bricks/middleware/logger_middleware.rb +29 -0
- data/lib/gooddata/bricks/middleware/middleware.rb +12 -0
- data/lib/gooddata/bricks/middleware/restforce_middleware.rb +61 -0
- data/lib/gooddata/bricks/middleware/stdout_middleware.rb +23 -0
- data/lib/gooddata/bricks/middleware/twitter_middleware.rb +29 -0
- data/lib/gooddata/bricks/middleware/undot_params_middleware.rb +37 -0
- data/lib/gooddata/bricks/pipeline.rb +32 -0
- data/lib/gooddata/bricks/utils.rb +18 -0
- data/lib/gooddata/cli/cli.rb +27 -0
- data/lib/gooddata/cli/commands/auth_cmd.rb +29 -0
- data/lib/gooddata/cli/commands/domain_cmd.rb +28 -0
- data/lib/gooddata/cli/commands/project_cmd.rb +45 -0
- data/lib/gooddata/cli/hooks.rb +57 -0
- data/lib/gooddata/cli/shared.rb +61 -0
- data/lib/gooddata/cli/terminal.rb +20 -0
- data/lib/gooddata/client.rb +67 -0
- data/lib/gooddata/commands/api.rb +64 -0
- data/lib/gooddata/commands/auth.rb +107 -0
- data/lib/gooddata/commands/base.rb +12 -0
- data/lib/gooddata/commands/commands.rb +12 -0
- data/lib/gooddata/commands/datasets.rb +148 -0
- data/lib/gooddata/commands/datawarehouse.rb +20 -0
- data/lib/gooddata/commands/domain.rb +40 -0
- data/lib/gooddata/commands/process.rb +67 -0
- data/lib/gooddata/commands/project.rb +175 -0
- data/lib/gooddata/commands/projects.rb +20 -0
- data/lib/gooddata/commands/role.rb +36 -0
- data/lib/gooddata/commands/runners.rb +47 -0
- data/lib/gooddata/commands/scaffold.rb +69 -0
- data/lib/gooddata/commands/user.rb +39 -0
- data/lib/gooddata/connection.rb +127 -0
- data/lib/gooddata/core/core.rb +12 -0
- data/lib/gooddata/core/logging.rb +105 -0
- data/lib/gooddata/core/nil_logger.rb +23 -0
- data/lib/gooddata/core/project.rb +74 -0
- data/lib/gooddata/core/rest.rb +149 -0
- data/lib/gooddata/core/user.rb +20 -0
- data/lib/gooddata/data/data.rb +12 -0
- data/lib/gooddata/data/guesser.rb +122 -0
- data/lib/gooddata/exceptions/attr_element_not_found.rb +16 -0
- data/lib/gooddata/exceptions/command_failed.rb +11 -0
- data/lib/gooddata/exceptions/exceptions.rb +12 -0
- data/lib/gooddata/exceptions/execution_limit_exceeded.rb +13 -0
- data/lib/gooddata/exceptions/filter_maqlization.rb +16 -0
- data/lib/gooddata/exceptions/malformed_user.rb +15 -0
- data/lib/gooddata/exceptions/no_project_error.rb +15 -0
- data/lib/gooddata/exceptions/object_migration.rb +32 -0
- data/lib/gooddata/exceptions/project_not_found.rb +13 -0
- data/lib/gooddata/exceptions/segment_not_empty.rb +18 -0
- data/lib/gooddata/exceptions/uncomputable_report.rb +13 -0
- data/lib/gooddata/exceptions/user_in_different_domain.rb +15 -0
- data/lib/gooddata/exceptions/validation_error.rb +16 -0
- data/lib/gooddata/extensions/big_decimal.rb +17 -0
- data/lib/gooddata/extensions/enumerable.rb +39 -0
- data/lib/gooddata/extensions/extensions.rb +10 -0
- data/lib/gooddata/extensions/false.rb +15 -0
- data/lib/gooddata/extensions/hash.rb +38 -0
- data/lib/gooddata/extensions/nil.rb +15 -0
- data/lib/gooddata/extensions/numeric.rb +15 -0
- data/lib/gooddata/extensions/object.rb +27 -0
- data/lib/gooddata/extensions/symbol.rb +15 -0
- data/lib/gooddata/extensions/true.rb +15 -0
- data/lib/gooddata/extract.rb +21 -0
- data/lib/gooddata/goodzilla/goodzilla.rb +159 -0
- data/lib/gooddata/helpers/auth_helpers.rb +75 -0
- data/lib/gooddata/helpers/csv_helper.rb +61 -0
- data/lib/gooddata/helpers/data_helper.rb +116 -0
- data/lib/gooddata/helpers/global_helpers.rb +331 -0
- data/lib/gooddata/helpers/global_helpers_params.rb +172 -0
- data/lib/gooddata/helpers/helpers.rb +10 -0
- data/lib/gooddata/mixins/author.rb +26 -0
- data/lib/gooddata/mixins/content_getter.rb +15 -0
- data/lib/gooddata/mixins/content_property_reader.rb +17 -0
- data/lib/gooddata/mixins/content_property_writer.rb +17 -0
- data/lib/gooddata/mixins/contributor.rb +20 -0
- data/lib/gooddata/mixins/data_getter.rb +15 -0
- data/lib/gooddata/mixins/data_property_reader.rb +19 -0
- data/lib/gooddata/mixins/data_property_writer.rb +19 -0
- data/lib/gooddata/mixins/inspector.rb +53 -0
- data/lib/gooddata/mixins/is_attribute.rb +17 -0
- data/lib/gooddata/mixins/is_dimension.rb +17 -0
- data/lib/gooddata/mixins/is_fact.rb +17 -0
- data/lib/gooddata/mixins/is_label.rb +19 -0
- data/lib/gooddata/mixins/links.rb +15 -0
- data/lib/gooddata/mixins/md_finders.rb +77 -0
- data/lib/gooddata/mixins/md_grantees.rb +42 -0
- data/lib/gooddata/mixins/md_id_to_uri.rb +34 -0
- data/lib/gooddata/mixins/md_json.rb +15 -0
- data/lib/gooddata/mixins/md_lock.rb +87 -0
- data/lib/gooddata/mixins/md_object_id.rb +15 -0
- data/lib/gooddata/mixins/md_object_indexer.rb +64 -0
- data/lib/gooddata/mixins/md_object_query.rb +128 -0
- data/lib/gooddata/mixins/md_relations.rb +43 -0
- data/lib/gooddata/mixins/meta_getter.rb +17 -0
- data/lib/gooddata/mixins/meta_property_reader.rb +19 -0
- data/lib/gooddata/mixins/meta_property_writer.rb +19 -0
- data/lib/gooddata/mixins/mixins.rb +19 -0
- data/lib/gooddata/mixins/not_attribute.rb +17 -0
- data/lib/gooddata/mixins/not_exportable.rb +15 -0
- data/lib/gooddata/mixins/not_fact.rb +17 -0
- data/lib/gooddata/mixins/not_group.rb +17 -0
- data/lib/gooddata/mixins/not_label.rb +19 -0
- data/lib/gooddata/mixins/not_metric.rb +19 -0
- data/lib/gooddata/mixins/obj_id.rb +15 -0
- data/lib/gooddata/mixins/rest_getters.rb +17 -0
- data/lib/gooddata/mixins/rest_resource.rb +47 -0
- data/lib/gooddata/mixins/root_key_getter.rb +15 -0
- data/lib/gooddata/mixins/root_key_setter.rb +15 -0
- data/lib/gooddata/mixins/timestamps.rb +19 -0
- data/lib/gooddata/mixins/to_json.rb +11 -0
- data/lib/gooddata/mixins/uri_getter.rb +9 -0
- data/lib/gooddata/models/blueprint/anchor_field.rb +64 -0
- data/lib/gooddata/models/blueprint/attribute_field.rb +29 -0
- data/lib/gooddata/models/blueprint/blueprint.rb +11 -0
- data/lib/gooddata/models/blueprint/blueprint_field.rb +70 -0
- data/lib/gooddata/models/blueprint/dashboard_builder.rb +30 -0
- data/lib/gooddata/models/blueprint/dataset_blueprint.rb +449 -0
- data/lib/gooddata/models/blueprint/date_dimension.rb +14 -0
- data/lib/gooddata/models/blueprint/fact_field.rb +20 -0
- data/lib/gooddata/models/blueprint/label_field.rb +43 -0
- data/lib/gooddata/models/blueprint/project_blueprint.rb +746 -0
- data/lib/gooddata/models/blueprint/project_builder.rb +83 -0
- data/lib/gooddata/models/blueprint/reference_field.rb +43 -0
- data/lib/gooddata/models/blueprint/schema_blueprint.rb +160 -0
- data/lib/gooddata/models/blueprint/schema_builder.rb +89 -0
- data/lib/gooddata/models/blueprint/to_manifest.rb +181 -0
- data/lib/gooddata/models/blueprint/to_wire.rb +154 -0
- data/lib/gooddata/models/client.rb +182 -0
- data/lib/gooddata/models/client_synchronization_result.rb +31 -0
- data/lib/gooddata/models/client_synchronization_result_details.rb +41 -0
- data/lib/gooddata/models/datawarehouse.rb +92 -0
- data/lib/gooddata/models/domain.rb +479 -0
- data/lib/gooddata/models/execution.rb +115 -0
- data/lib/gooddata/models/execution_detail.rb +81 -0
- data/lib/gooddata/models/from_wire.rb +160 -0
- data/lib/gooddata/models/invitation.rb +75 -0
- data/lib/gooddata/models/links.rb +50 -0
- data/lib/gooddata/models/membership.rb +441 -0
- data/lib/gooddata/models/metadata.rb +272 -0
- data/lib/gooddata/models/metadata/attribute.rb +134 -0
- data/lib/gooddata/models/metadata/dashboard.rb +108 -0
- data/lib/gooddata/models/metadata/dashboard/dashboard_item.rb +76 -0
- data/lib/gooddata/models/metadata/dashboard/filter_apply_item.rb +37 -0
- data/lib/gooddata/models/metadata/dashboard/filter_item.rb +64 -0
- data/lib/gooddata/models/metadata/dashboard/geo_chart_item.rb +56 -0
- data/lib/gooddata/models/metadata/dashboard/headline_item.rb +56 -0
- data/lib/gooddata/models/metadata/dashboard/iframe_item.rb +46 -0
- data/lib/gooddata/models/metadata/dashboard/report_item.rb +92 -0
- data/lib/gooddata/models/metadata/dashboard/text_item.rb +55 -0
- data/lib/gooddata/models/metadata/dashboard_tab.rb +141 -0
- data/lib/gooddata/models/metadata/dataset.rb +64 -0
- data/lib/gooddata/models/metadata/dimension.rb +54 -0
- data/lib/gooddata/models/metadata/fact.rb +44 -0
- data/lib/gooddata/models/metadata/label.rb +128 -0
- data/lib/gooddata/models/metadata/metadata.rb +12 -0
- data/lib/gooddata/models/metadata/metric.rb +198 -0
- data/lib/gooddata/models/metadata/report.rb +247 -0
- data/lib/gooddata/models/metadata/report_definition.rb +264 -0
- data/lib/gooddata/models/metadata/scheduled_mail.rb +274 -0
- data/lib/gooddata/models/metadata/scheduled_mail/dashboard_attachment.rb +62 -0
- data/lib/gooddata/models/metadata/scheduled_mail/report_attachment.rb +64 -0
- data/lib/gooddata/models/metadata/variable.rb +91 -0
- data/lib/gooddata/models/model.rb +282 -0
- data/lib/gooddata/models/models.rb +12 -0
- data/lib/gooddata/models/module_constants.rb +31 -0
- data/lib/gooddata/models/process.rb +316 -0
- data/lib/gooddata/models/profile.rb +426 -0
- data/lib/gooddata/models/project.rb +1514 -0
- data/lib/gooddata/models/project_creator.rb +126 -0
- data/lib/gooddata/models/project_metadata.rb +67 -0
- data/lib/gooddata/models/project_role.rb +79 -0
- data/lib/gooddata/models/report_data_result.rb +266 -0
- data/lib/gooddata/models/schedule.rb +518 -0
- data/lib/gooddata/models/segment.rb +201 -0
- data/lib/gooddata/models/tab_builder.rb +27 -0
- data/lib/gooddata/models/user_filters/mandatory_user_filter.rb +76 -0
- data/lib/gooddata/models/user_filters/user_filter.rb +100 -0
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +512 -0
- data/lib/gooddata/models/user_filters/user_filters.rb +13 -0
- data/lib/gooddata/models/user_filters/variable_user_filter.rb +31 -0
- data/lib/gooddata/models/user_group.rb +241 -0
- data/lib/gooddata/rest/README.md +37 -0
- data/lib/gooddata/rest/client.rb +389 -0
- data/lib/gooddata/rest/connection.rb +765 -0
- data/lib/gooddata/rest/object.rb +69 -0
- data/lib/gooddata/rest/object_factory.rb +76 -0
- data/lib/gooddata/rest/resource.rb +27 -0
- data/lib/gooddata/rest/rest.rb +24 -0
- data/lib/gooddata/version.rb +23 -0
- data/lib/templates/bricks/brick.rb.erb +7 -0
- data/lib/templates/bricks/main.rb.erb +5 -0
- data/lib/templates/project/Goodfile.erb +4 -0
- data/lib/templates/project/data/commits.csv +4 -0
- data/lib/templates/project/data/devs.csv +4 -0
- data/lib/templates/project/data/repos.csv +3 -0
- data/lib/templates/project/model/model.rb.erb +20 -0
- data/spec/bricks/bricks_spec.rb +112 -0
- data/spec/bricks/default-config.json +8 -0
- data/spec/data/.gooddata +4 -0
- data/spec/data/blueprints/additional_dataset_module.json +32 -0
- data/spec/data/blueprints/big_blueprint_not_pruned.json +2079 -0
- data/spec/data/blueprints/invalid_blueprint.json +103 -0
- data/spec/data/blueprints/m_n_model.json +104 -0
- data/spec/data/blueprints/model_module.json +25 -0
- data/spec/data/blueprints/test_blueprint.json +38 -0
- data/spec/data/blueprints/test_project_model_spec.json +106 -0
- data/spec/data/cc/data/source/commits.csv +4 -0
- data/spec/data/cc/data/source/devs.csv +4 -0
- data/spec/data/cc/data/source/repos.csv +3 -0
- data/spec/data/cc/devel.prm +0 -0
- data/spec/data/cc/graph/graph.grf +11 -0
- data/spec/data/cc/workspace.prm +19 -0
- data/spec/data/column_based_permissions.csv +7 -0
- data/spec/data/column_based_permissions2.csv +6 -0
- data/spec/data/gd_gse_data_blueprint.json +1371 -0
- data/spec/data/gd_gse_data_manifest.json +1424 -0
- data/spec/data/gd_gse_data_model.json +1772 -0
- data/spec/data/gooddata_version_process/gooddata_version.rb +9 -0
- data/spec/data/gooddata_version_process/gooddata_version.zip +0 -0
- data/spec/data/hello_world_process/hello_world.rb +9 -0
- data/spec/data/hello_world_process/hello_world.zip +0 -0
- data/spec/data/line_based_permissions.csv +3 -0
- data/spec/data/manifests/test_blueprint.json +32 -0
- data/spec/data/manifests/test_project.json +107 -0
- data/spec/data/reports/left_attr_report.json +108 -0
- data/spec/data/reports/metric_only_one_line.json +83 -0
- data/spec/data/reports/report_1.json +197 -0
- data/spec/data/reports/top_attr_report.json +108 -0
- data/spec/data/ruby_params_process/ruby_params.rb +9 -0
- data/spec/data/ruby_process/deep_files/deep_stuff.txt +1 -0
- data/spec/data/ruby_process/process.rb +8 -0
- data/spec/data/ruby_process/stuff.txt +1 -0
- data/spec/data/superfluous_titles_view.json +81 -0
- data/spec/data/test-ci-data.csv +2 -0
- data/spec/data/users.csv +12 -0
- data/spec/data/wire_models/model_view.json +1775 -0
- data/spec/data/wire_models/nu_model.json +3046 -0
- data/spec/data/wire_models/test_blueprint.json +63 -0
- data/spec/data/wire_test_project.json +150 -0
- data/spec/environment/default.rb +41 -0
- data/spec/environment/develop.rb +31 -0
- data/spec/environment/environment.rb +18 -0
- data/spec/environment/hotfix.rb +21 -0
- data/spec/environment/production.rb +35 -0
- data/spec/environment/release.rb +21 -0
- data/spec/environment/staging.rb +30 -0
- data/spec/environment/staging_3.rb +36 -0
- data/spec/helpers/blueprint_helper.rb +26 -0
- data/spec/helpers/cli_helper.rb +36 -0
- data/spec/helpers/connection_helper.rb +41 -0
- data/spec/helpers/crypto_helper.rb +17 -0
- data/spec/helpers/csv_helper.rb +18 -0
- data/spec/helpers/process_helper.rb +33 -0
- data/spec/helpers/project_helper.rb +59 -0
- data/spec/helpers/schedule_helper.rb +31 -0
- data/spec/helpers/spec_helper.rb +15 -0
- data/spec/integration/blueprint_updates_spec.rb +101 -0
- data/spec/integration/blueprint_with_grain_spec.rb +72 -0
- data/spec/integration/clients_spec.rb +134 -0
- data/spec/integration/command_datawarehouse_spec.rb +39 -0
- data/spec/integration/command_projects_spec.rb +32 -0
- data/spec/integration/create_from_template_spec.rb +22 -0
- data/spec/integration/create_project_spec.rb +24 -0
- data/spec/integration/date_dim_switch_spec.rb +142 -0
- data/spec/integration/deprecated_load_spec.rb +58 -0
- data/spec/integration/full_process_schedule_spec.rb +298 -0
- data/spec/integration/full_project_spec.rb +569 -0
- data/spec/integration/over_to_user_filters_spec.rb +94 -0
- data/spec/integration/partial_md_export_import_spec.rb +42 -0
- data/spec/integration/project_spec.rb +264 -0
- data/spec/integration/rest_spec.rb +213 -0
- data/spec/integration/schedule_spec.rb +626 -0
- data/spec/integration/segments_spec.rb +141 -0
- data/spec/integration/user_filters_spec.rb +290 -0
- data/spec/integration/user_group_spec.rb +127 -0
- data/spec/integration/variables_spec.rb +188 -0
- data/spec/logging_in_logging_out_spec.rb +93 -0
- data/spec/spec_helper.rb +95 -0
- data/spec/unit/bricks/bricks_spec.rb +35 -0
- data/spec/unit/bricks/middleware/aws_middelware_spec.rb +51 -0
- data/spec/unit/bricks/middleware/bench_middleware_spec.rb +15 -0
- data/spec/unit/bricks/middleware/bulk_salesforce_middleware_spec.rb +15 -0
- data/spec/unit/bricks/middleware/gooddata_middleware_spec.rb +15 -0
- data/spec/unit/bricks/middleware/logger_middleware_spec.rb +15 -0
- data/spec/unit/bricks/middleware/restforce_middleware_spec.rb +15 -0
- data/spec/unit/bricks/middleware/stdout_middleware_spec.rb +15 -0
- data/spec/unit/bricks/middleware/twitter_middleware_spec.rb +15 -0
- data/spec/unit/cli/cli_spec.rb +17 -0
- data/spec/unit/cli/commands/cmd_auth_spec.rb +17 -0
- data/spec/unit/commands/command_projects_spec.rb +22 -0
- data/spec/unit/core/connection_spec.rb +57 -0
- data/spec/unit/core/logging_spec.rb +133 -0
- data/spec/unit/core/nil_logger_spec.rb +13 -0
- data/spec/unit/core/project_spec.rb +54 -0
- data/spec/unit/extensions/hash_spec.rb +23 -0
- data/spec/unit/godzilla/goodzilla_spec.rb +78 -0
- data/spec/unit/helpers/csv_helper_spec.rb +22 -0
- data/spec/unit/helpers/data_helper_spec.rb +61 -0
- data/spec/unit/helpers/global_helpers_spec.rb +111 -0
- data/spec/unit/helpers_spec.rb +86 -0
- data/spec/unit/models/blueprint/attributes_spec.rb +29 -0
- data/spec/unit/models/blueprint/dataset_spec.rb +121 -0
- data/spec/unit/models/blueprint/labels_spec.rb +44 -0
- data/spec/unit/models/blueprint/project_blueprint_spec.rb +648 -0
- data/spec/unit/models/blueprint/reference_spec.rb +29 -0
- data/spec/unit/models/blueprint/schema_builder_spec.rb +38 -0
- data/spec/unit/models/blueprint/to_wire_spec.rb +174 -0
- data/spec/unit/models/domain_spec.rb +144 -0
- data/spec/unit/models/execution_spec.rb +108 -0
- data/spec/unit/models/from_wire_spec.rb +296 -0
- data/spec/unit/models/invitation_spec.rb +17 -0
- data/spec/unit/models/membership_spec.rb +132 -0
- data/spec/unit/models/metadata_spec.rb +104 -0
- data/spec/unit/models/metric_spec.rb +117 -0
- data/spec/unit/models/model_spec.rb +82 -0
- data/spec/unit/models/params_spec.rb +118 -0
- data/spec/unit/models/profile_spec.rb +215 -0
- data/spec/unit/models/project_creator_spec.rb +127 -0
- data/spec/unit/models/project_role_spec.rb +94 -0
- data/spec/unit/models/project_spec.rb +162 -0
- data/spec/unit/models/report_result_data_spec.rb +199 -0
- data/spec/unit/models/schedule_spec.rb +418 -0
- data/spec/unit/models/to_manifest_spec.rb +63 -0
- data/spec/unit/models/unit_project_spec.rb +125 -0
- data/spec/unit/models/user_filters_spec.rb +95 -0
- data/spec/unit/models/variable_spec.rb +265 -0
- data/spec/unit/rest/polling_spec.rb +89 -0
- data/spec/unit/rest/resource_spec.rb +10 -0
- data/yard-server.sh +3 -0
- metadata +1125 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010-2015 GoodData Corporation. All rights reserved.
|
4
|
+
# This source code is licensed under the BSD-style license found in the
|
5
|
+
# LICENSE file in the root directory of this source tree.
|
6
|
+
|
7
|
+
require 'csv'
|
8
|
+
require 'digest'
|
9
|
+
require 'open-uri'
|
10
|
+
|
11
|
+
module GoodData
|
12
|
+
module Helpers
|
13
|
+
class DataSource
|
14
|
+
attr_reader :realized
|
15
|
+
|
16
|
+
def initialize(opts = {})
|
17
|
+
opts = opts.is_a?(String) ? { type: :staging, path: opts } : opts
|
18
|
+
opts = GoodData::Helpers.symbolize_keys(opts)
|
19
|
+
@source = opts[:type]
|
20
|
+
@options = opts
|
21
|
+
@realized = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def realize(params = {})
|
25
|
+
@realized = true
|
26
|
+
source = @source && @source.to_s
|
27
|
+
case source
|
28
|
+
when 'ads'
|
29
|
+
realize_query(params)
|
30
|
+
when 'staging'
|
31
|
+
realize_staging(params)
|
32
|
+
when 'web'
|
33
|
+
realize_link
|
34
|
+
when 's3'
|
35
|
+
realize_s3(params)
|
36
|
+
else
|
37
|
+
fail "DataSource does not support type \"#{source}\""
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def realized?
|
42
|
+
@realized == true
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def realize_query(params)
|
48
|
+
query = @options[:query]
|
49
|
+
dwh = params['ads_client']
|
50
|
+
fail "Data Source needs a client to ads to be able to query the storage but 'ads_client' is empty." unless dwh
|
51
|
+
filename = Digest::SHA256.new.hexdigest(query)
|
52
|
+
measure = Benchmark.measure do
|
53
|
+
CSV.open(filename, 'w') do |csv|
|
54
|
+
header_written = false
|
55
|
+
header = nil
|
56
|
+
|
57
|
+
dwh.execute_select(query) do |row|
|
58
|
+
unless header_written
|
59
|
+
header_written = true
|
60
|
+
header = row.keys
|
61
|
+
csv << header
|
62
|
+
end
|
63
|
+
csv << row.values_at(*header)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
puts "Realizing SQL query \"#{query}\" took #{measure.real}"
|
68
|
+
filename
|
69
|
+
end
|
70
|
+
|
71
|
+
def realize_staging(params)
|
72
|
+
path = @options[:path]
|
73
|
+
url = URI.parse(path)
|
74
|
+
filename = Digest::SHA256.new.hexdigest(path)
|
75
|
+
if url.relative?
|
76
|
+
params['gdc_project'].download_file(path, filename)
|
77
|
+
else
|
78
|
+
params['GDC_GD_CLIENT'].download_file(path, filename)
|
79
|
+
end
|
80
|
+
filename
|
81
|
+
end
|
82
|
+
|
83
|
+
def realize_link
|
84
|
+
link = @options[:url]
|
85
|
+
filename = Digest::SHA256.new.hexdigest(link)
|
86
|
+
measure = Benchmark.measure do
|
87
|
+
File.open(filename, 'w') do |f|
|
88
|
+
open(link) { |rf| f.write(rf.read) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
puts "Realizing web download from \"#{link}\" took #{measure.real}"
|
92
|
+
filename
|
93
|
+
end
|
94
|
+
|
95
|
+
def realize_s3(params)
|
96
|
+
s3_client = params['aws_client'] && params['aws_client']['s3_client']
|
97
|
+
fail 'AWS client not present. Perhaps S3Middleware is missing in the brick definition?' if !s3_client || !s3_client.respond_to?(:bucket)
|
98
|
+
bucket_name = @options[:bucket]
|
99
|
+
key = @options[:key]
|
100
|
+
fail "Key \"bucket\" is missing in S3 datasource" if bucket_name.blank?
|
101
|
+
fail "Key \"key\" is missing in S3 datasource" if key.blank?
|
102
|
+
puts "Realizing download from S3. Bucket #{bucket_name}, object with key #{key}."
|
103
|
+
filename = Digest::SHA256.new.hexdigest(@options.to_json)
|
104
|
+
bucket = s3_client.bucket(bucket_name)
|
105
|
+
obj = bucket.object(key)
|
106
|
+
File.open(filename, 'wb') do |file|
|
107
|
+
obj.read do |chunk|
|
108
|
+
file.write(chunk)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
puts 'Done downloading file.'
|
112
|
+
filename
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,331 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010-2015 GoodData Corporation. All rights reserved.
|
4
|
+
# This source code is licensed under the BSD-style license found in the
|
5
|
+
# LICENSE file in the root directory of this source tree.
|
6
|
+
|
7
|
+
require 'base64'
|
8
|
+
require 'pathname'
|
9
|
+
require 'hashie'
|
10
|
+
require 'openssl'
|
11
|
+
|
12
|
+
require_relative 'global_helpers_params'
|
13
|
+
|
14
|
+
module GoodData
|
15
|
+
module Helpers
|
16
|
+
class DeepMergeableHash < Hash
|
17
|
+
include Hashie::Extensions::DeepMerge
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def error(msg)
|
22
|
+
STDERR.puts(msg)
|
23
|
+
exit 1
|
24
|
+
end
|
25
|
+
|
26
|
+
# FIXME: Windows incompatible
|
27
|
+
def find_goodfile(pwd = `pwd`.strip!, options = {})
|
28
|
+
root = Pathname(options[:root] || '/')
|
29
|
+
pwd = Pathname(pwd).expand_path
|
30
|
+
loop do
|
31
|
+
gf = pwd + '.gooddata'
|
32
|
+
return gf if File.exist?(gf)
|
33
|
+
pwd = pwd.parent
|
34
|
+
break if root == pwd
|
35
|
+
end
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# It takes what should be mapped to what and creates a mapping that is suitable for other internal methods.
|
40
|
+
# This means looking up the objects and returning it as array of pairs.
|
41
|
+
# The input can be given in several ways
|
42
|
+
#
|
43
|
+
# 1. Hash. For example it could look like
|
44
|
+
# {'label.states.name' => 'label.state.id'}
|
45
|
+
#
|
46
|
+
# 2 Arrays. In such case the arrays are zipped together. First item will be swapped for the first item in the second array etc.
|
47
|
+
# ['label.states.name'], ['label.state.id']
|
48
|
+
#
|
49
|
+
# @param what [Hash | Array] List/Hash of objects to be swapped
|
50
|
+
# @param for_what [Array] List of objects to be swapped
|
51
|
+
# @return [Array<GoodData::MdObject>] List of pairs of objects
|
52
|
+
def prepare_mapping(what, for_what = nil, options = {})
|
53
|
+
project = options[:project] || (for_what.is_a?(Hash) && for_what[:project]) || fail('Project has to be provided')
|
54
|
+
mapping = if what.is_a?(Hash)
|
55
|
+
whats = what.keys
|
56
|
+
to_whats = what.values
|
57
|
+
whats.zip(to_whats)
|
58
|
+
elsif what.is_a?(Array) && for_what.is_a?(Array)
|
59
|
+
whats.zip(to_whats)
|
60
|
+
else
|
61
|
+
[[what, for_what]]
|
62
|
+
end
|
63
|
+
mapping.pmap { |f, t| [project.objects(f), project.objects(t)] }
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_path(an_object, path = [], default = nil)
|
67
|
+
return an_object if path.empty?
|
68
|
+
return default if an_object.nil?
|
69
|
+
|
70
|
+
path.reduce(an_object) do |a, e|
|
71
|
+
a && a.key?(e) ? a[e] : default
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def last_uri_part(uri)
|
76
|
+
uri.split('/').last
|
77
|
+
end
|
78
|
+
|
79
|
+
def home_directory
|
80
|
+
running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
|
81
|
+
end
|
82
|
+
|
83
|
+
def hash_dfs(thing, &block)
|
84
|
+
if !thing.is_a?(Hash) && !thing.is_a?(Array) # rubocop:disable Style/GuardClause
|
85
|
+
elsif thing.is_a?(Array)
|
86
|
+
thing.each do |child|
|
87
|
+
hash_dfs(child, &block)
|
88
|
+
end
|
89
|
+
else
|
90
|
+
thing.each do |key, val|
|
91
|
+
yield(thing, key)
|
92
|
+
hash_dfs(val, &block)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def titleize(str)
|
98
|
+
titleized = str.gsub(/[\.|_](.)/, &:upcase)
|
99
|
+
titleized = titleized.tr('_', ' ')
|
100
|
+
titleized[0] = titleized[0].upcase
|
101
|
+
titleized
|
102
|
+
end
|
103
|
+
|
104
|
+
def join(master, slave, on, on2, options = {})
|
105
|
+
full_outer = options[:full_outer]
|
106
|
+
inner = options[:inner]
|
107
|
+
|
108
|
+
lookup = create_lookup(slave, on2)
|
109
|
+
marked_lookup = {}
|
110
|
+
results = master.reduce([]) do |a, line|
|
111
|
+
matching_values = lookup[line.values_at(*on)] || []
|
112
|
+
marked_lookup[line.values_at(*on)] = 1
|
113
|
+
if matching_values.empty? && !inner
|
114
|
+
a << line.to_hash
|
115
|
+
else
|
116
|
+
matching_values.each do |matching_value|
|
117
|
+
a << matching_value.to_hash.merge(line.to_hash)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
a
|
121
|
+
end
|
122
|
+
|
123
|
+
if full_outer
|
124
|
+
(lookup.keys - marked_lookup.keys).each do |key|
|
125
|
+
puts lookup[key]
|
126
|
+
results << lookup[key].first.to_hash
|
127
|
+
end
|
128
|
+
end
|
129
|
+
results
|
130
|
+
end
|
131
|
+
|
132
|
+
def running_on_windows?
|
133
|
+
RUBY_PLATFORM =~ /mswin32|mingw32/
|
134
|
+
end
|
135
|
+
|
136
|
+
def running_on_a_mac?
|
137
|
+
RUBY_PLATFORM =~ /-darwin\d/
|
138
|
+
end
|
139
|
+
|
140
|
+
def underline(x)
|
141
|
+
'=' * x.size
|
142
|
+
end
|
143
|
+
|
144
|
+
def interpolate_error_messages(errors)
|
145
|
+
errors.map { |e| interpolate_error_message(e) }
|
146
|
+
end
|
147
|
+
|
148
|
+
def interpolate_error_message(error)
|
149
|
+
message = error['error']['message']
|
150
|
+
params = error['error']['parameters']
|
151
|
+
sprintf(message, *params)
|
152
|
+
end
|
153
|
+
|
154
|
+
def transform_keys!(an_object)
|
155
|
+
return enum_for(:transform_keys!) unless block_given?
|
156
|
+
an_object.keys.each do |key|
|
157
|
+
an_object[yield(key)] = an_object.delete(key)
|
158
|
+
end
|
159
|
+
an_object
|
160
|
+
end
|
161
|
+
|
162
|
+
def symbolize_keys!(an_object)
|
163
|
+
transform_keys!(an_object) do |key|
|
164
|
+
begin
|
165
|
+
key.to_sym
|
166
|
+
rescue
|
167
|
+
key
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def symbolize_keys(an_object)
|
173
|
+
transform_keys(an_object) do |key|
|
174
|
+
begin
|
175
|
+
key.to_sym
|
176
|
+
rescue
|
177
|
+
key
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def transform_keys(an_object)
|
183
|
+
return enum_for(:transform_keys) unless block_given?
|
184
|
+
result = an_object.class.new
|
185
|
+
an_object.each_key do |key|
|
186
|
+
result[yield(key)] = an_object[key]
|
187
|
+
end
|
188
|
+
result
|
189
|
+
end
|
190
|
+
|
191
|
+
def deep_symbolize_keys(an_object)
|
192
|
+
deep_transform_keys(an_object) do |key|
|
193
|
+
begin
|
194
|
+
key.to_sym
|
195
|
+
rescue
|
196
|
+
key
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def stringify_keys(an_object)
|
202
|
+
transform_keys(an_object, &:to_s)
|
203
|
+
end
|
204
|
+
|
205
|
+
def deep_stringify_keys(an_object)
|
206
|
+
deep_transform_keys(an_object, &:to_s)
|
207
|
+
end
|
208
|
+
|
209
|
+
def deep_transform_keys(an_object, &block)
|
210
|
+
_deep_transform_keys_in_object(an_object, &block)
|
211
|
+
end
|
212
|
+
|
213
|
+
def _deep_transform_keys_in_object(object, &block)
|
214
|
+
case object
|
215
|
+
when Hash
|
216
|
+
object.each_with_object({}) do |(key, value), result|
|
217
|
+
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
|
218
|
+
end
|
219
|
+
when Array
|
220
|
+
object.map { |e| _deep_transform_keys_in_object(e, &block) }
|
221
|
+
else
|
222
|
+
object
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def deep_dup(an_object)
|
227
|
+
case an_object
|
228
|
+
when Array
|
229
|
+
an_object.map { |it| GoodData::Helpers.deep_dup(it) }
|
230
|
+
when Hash
|
231
|
+
an_object.each_with_object(an_object.dup) do |(key, value), hash|
|
232
|
+
hash[GoodData::Helpers.deep_dup(key)] = GoodData::Helpers.deep_dup(value)
|
233
|
+
end
|
234
|
+
when Object
|
235
|
+
an_object.duplicable? ? an_object.dup : an_object
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def undot(params)
|
240
|
+
# for each key-value config given
|
241
|
+
params.map do |k, v|
|
242
|
+
# dot notation to hash
|
243
|
+
k.split('__').reverse.reduce(v) do |memo, obj|
|
244
|
+
GoodData::Helper.DeepMergeableHash[{ obj => memo }]
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def parse_http_exception(e)
|
250
|
+
JSON.parse(e.response)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Creates a matrix with zeroes in all places. It is implemented as an Array of Arrays. First rows then columns.
|
254
|
+
#
|
255
|
+
# @param [Integer] m Number of rows
|
256
|
+
# @param [Integer] n Number of cols
|
257
|
+
# @param [Integer] val Alternatively can fill in positions with different values than zeroes. Defualt is zero.
|
258
|
+
# @return [Array<Array>] Returns a matrix of zeroes
|
259
|
+
def zeroes(m, n, val = 0)
|
260
|
+
m.times.map { n.times.map { val } }
|
261
|
+
end
|
262
|
+
|
263
|
+
# Turns a boolean or string 'true' into boolean. Useful for bricks.
|
264
|
+
#
|
265
|
+
# @param [Object] Something
|
266
|
+
# @return [Boolean] Returns true or false if the input is 'true' or true
|
267
|
+
def to_boolean(param)
|
268
|
+
(param == 'true' || param == true) ? true : false
|
269
|
+
end
|
270
|
+
|
271
|
+
# encrypts data with the given key. returns a binary data with the
|
272
|
+
# unhashed random iv in the first 16 bytes
|
273
|
+
def encrypt(data, key)
|
274
|
+
cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
|
275
|
+
cipher.encrypt
|
276
|
+
cipher.key = key = Digest::SHA256.digest(key)
|
277
|
+
random_iv = cipher.random_iv
|
278
|
+
cipher.iv = Digest::SHA256.digest(random_iv + key)[0..15]
|
279
|
+
encrypted = cipher.update(data)
|
280
|
+
encrypted << cipher.final
|
281
|
+
# add unhashed iv to front of encrypted data
|
282
|
+
|
283
|
+
Base64.encode64(random_iv + encrypted)
|
284
|
+
end
|
285
|
+
|
286
|
+
def decrypt(data_base_64, key)
|
287
|
+
return '' if key.nil? || key.empty?
|
288
|
+
|
289
|
+
data = Base64.decode64(data_base_64)
|
290
|
+
|
291
|
+
cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
|
292
|
+
cipher.decrypt
|
293
|
+
cipher.key = cipher_key = Digest::SHA256.digest(key)
|
294
|
+
random_iv = data[0..15] # extract iv from first 16 bytes
|
295
|
+
data = data[16..data.size - 1]
|
296
|
+
cipher.iv = Digest::SHA256.digest(random_iv + cipher_key)[0..15]
|
297
|
+
begin
|
298
|
+
decrypted = cipher.update(data)
|
299
|
+
decrypted << cipher.final
|
300
|
+
rescue
|
301
|
+
puts 'Error'
|
302
|
+
return nil
|
303
|
+
end
|
304
|
+
|
305
|
+
decrypted
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
class << self
|
311
|
+
def get_client(opts)
|
312
|
+
client = opts[:client]
|
313
|
+
fail ArgumentError, 'No :client specified' if client.nil?
|
314
|
+
|
315
|
+
client
|
316
|
+
end
|
317
|
+
|
318
|
+
def get_client_and_project(opts)
|
319
|
+
client = opts[:client]
|
320
|
+
fail ArgumentError, 'No :client specified' if client.nil?
|
321
|
+
|
322
|
+
p = opts[:project]
|
323
|
+
fail ArgumentError, 'No :project specified' if p.nil?
|
324
|
+
|
325
|
+
project = GoodData::Project[p, opts]
|
326
|
+
fail ArgumentError, 'Wrong :project specified' if project.nil?
|
327
|
+
|
328
|
+
[client, project]
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010-2015 GoodData Corporation. All rights reserved.
|
4
|
+
# This source code is licensed under the BSD-style license found in the
|
5
|
+
# LICENSE file in the root directory of this source tree.
|
6
|
+
|
7
|
+
module GoodData
|
8
|
+
module Helpers
|
9
|
+
ENCODED_PARAMS_KEY = 'gd_encoded_params'
|
10
|
+
ENCODED_HIDDEN_PARAMS_KEY = 'gd_encoded_hidden_params'
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Encodes parameters for passing them to GD execution platform.
|
14
|
+
# Core types are kept and complex types (arrays, structures, etc) are JSON encoded into key hash "gd_encoded_params" or "gd_encoded_hidden_params", depending on the 'hidden' method param.
|
15
|
+
# The two different keys are used because the params and hidden params are merged by the platform and if we use the same key, the param would be overwritten.
|
16
|
+
#
|
17
|
+
# Core types are following:
|
18
|
+
# - Boolean (true, false)
|
19
|
+
# - Fixnum
|
20
|
+
# - Float
|
21
|
+
# - Nil
|
22
|
+
# - String
|
23
|
+
#
|
24
|
+
# @param [Hash] params Parameters to be encoded
|
25
|
+
# @return [Hash] Encoded parameters
|
26
|
+
def encode_params(params, data_key)
|
27
|
+
res = {}
|
28
|
+
nested = {}
|
29
|
+
core_types = [FalseClass, Fixnum, Float, NilClass, TrueClass, String]
|
30
|
+
params.each do |k, v|
|
31
|
+
if core_types.include?(v.class)
|
32
|
+
res[k] = v
|
33
|
+
else
|
34
|
+
nested[k] = v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
res[data_key] = nested.to_json unless nested.empty?
|
38
|
+
res
|
39
|
+
end
|
40
|
+
|
41
|
+
# Encodes public parameters for passing them to GD execution platform.
|
42
|
+
# @param [Hash] params Parameters to be encoded
|
43
|
+
# @return [Hash] Encoded parameters
|
44
|
+
def encode_public_params(params)
|
45
|
+
encode_params(params, ENCODED_PARAMS_KEY)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Encodes hidden parameters for passing them to GD execution platform.
|
49
|
+
# @param [Hash] params Parameters to be encoded
|
50
|
+
# @return [Hash] Encoded parameters
|
51
|
+
def encode_hidden_params(params)
|
52
|
+
encode_params(params, ENCODED_HIDDEN_PARAMS_KEY)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Decodes params as they came from the platform
|
56
|
+
# The "data" key is supposed to be json and it's parsed - if this
|
57
|
+
def decode_params(params)
|
58
|
+
key = ENCODED_PARAMS_KEY.to_s
|
59
|
+
hidden_key = ENCODED_HIDDEN_PARAMS_KEY.to_s
|
60
|
+
data_params = params[key] || '{}'
|
61
|
+
hidden_data_params = if params.key?(hidden_key) && params[hidden_key].nil?
|
62
|
+
"{\"#{hidden_key}\" : null}"
|
63
|
+
elsif params.key?(hidden_key)
|
64
|
+
params[hidden_key]
|
65
|
+
else
|
66
|
+
'{}'
|
67
|
+
end
|
68
|
+
|
69
|
+
begin
|
70
|
+
parsed_data_params = JSON.parse(data_params)
|
71
|
+
parsed_hidden_data_params = JSON.parse(hidden_data_params)
|
72
|
+
rescue JSON::ParserError => e
|
73
|
+
raise e.class, "Error reading json from '#{key}' or '#{hidden_key}' in params #{params}\n #{e.message}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Add the nil on ENCODED_HIDDEN_PARAMS_KEY
|
77
|
+
# if the data was retrieved from API You will not have the actual values so encode -> decode is not losless. The nil on the key prevents the server from deleting the key
|
78
|
+
parsed_hidden_data_params[ENCODED_HIDDEN_PARAMS_KEY] = nil unless parsed_hidden_data_params.empty?
|
79
|
+
params.delete(key)
|
80
|
+
params.delete(hidden_key)
|
81
|
+
params.merge(parsed_data_params).merge(parsed_hidden_data_params)
|
82
|
+
end
|
83
|
+
|
84
|
+
# A helper which allows you to diff two lists of objects. The objects
|
85
|
+
# can be arbitrary objects as long as they respond to to_hash because
|
86
|
+
# the diff is eventually done on hashes. It allows you to specify
|
87
|
+
# several options to allow you to limit on what the sameness test is done
|
88
|
+
#
|
89
|
+
# @param [Array<Object>] old_list List of objects that serves as a base for comparison
|
90
|
+
# @param [Array<Object>] new_list List of objects that is compared agianst the old_list
|
91
|
+
# @return [Hash] A structure that contains the result of the comparison. There are
|
92
|
+
# four keys.
|
93
|
+
# :added contains the list that are in new_list but were not in the old_list
|
94
|
+
# :added contains the list that are in old_list but were not in the new_list
|
95
|
+
# :same contains objects that are in both lists and they are the same
|
96
|
+
# :changed contains list of objects that changed along ith original, the new one
|
97
|
+
# and the list of changes
|
98
|
+
def diff(old_list, new_list, options = {})
|
99
|
+
old_list = old_list.map(&:to_hash)
|
100
|
+
new_list = new_list.map(&:to_hash)
|
101
|
+
|
102
|
+
fields = options[:fields]
|
103
|
+
lookup_key = options[:key]
|
104
|
+
|
105
|
+
old_lookup = Hash[old_list.map { |v| [v[lookup_key], v] }]
|
106
|
+
|
107
|
+
res = {
|
108
|
+
:added => [],
|
109
|
+
:removed => [],
|
110
|
+
:changed => [],
|
111
|
+
:same => []
|
112
|
+
}
|
113
|
+
|
114
|
+
new_list.each do |new_obj|
|
115
|
+
old_obj = old_lookup[new_obj[lookup_key]]
|
116
|
+
if old_obj.nil?
|
117
|
+
res[:added] << new_obj
|
118
|
+
next
|
119
|
+
end
|
120
|
+
|
121
|
+
if fields
|
122
|
+
sliced_old_obj = old_obj.slice(*fields)
|
123
|
+
sliced_new_obj = new_obj.slice(*fields)
|
124
|
+
else
|
125
|
+
sliced_old_obj = old_obj
|
126
|
+
sliced_new_obj = new_obj
|
127
|
+
end
|
128
|
+
if sliced_old_obj != sliced_new_obj
|
129
|
+
difference = sliced_new_obj.to_a - sliced_old_obj.to_a
|
130
|
+
differences = Hash[*difference.mapcat { |x| x }]
|
131
|
+
res[:changed] << {
|
132
|
+
old_obj: old_obj,
|
133
|
+
new_obj: new_obj,
|
134
|
+
diff: differences
|
135
|
+
}
|
136
|
+
else
|
137
|
+
res[:same] << old_obj
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
new_lookup = Hash[new_list.map { |v| [v[lookup_key], v] }]
|
142
|
+
old_list.each do |old_obj|
|
143
|
+
new_obj = new_lookup[old_obj[lookup_key]]
|
144
|
+
if new_obj.nil?
|
145
|
+
res[:removed] << old_obj
|
146
|
+
next
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
res
|
151
|
+
end
|
152
|
+
|
153
|
+
def create_lookup(collection, on)
|
154
|
+
lookup = {}
|
155
|
+
if on.is_a?(Array)
|
156
|
+
collection.each do |e|
|
157
|
+
key = e.values_at(*on)
|
158
|
+
lookup[key] = [] unless lookup.key?(key)
|
159
|
+
lookup[key] << e
|
160
|
+
end
|
161
|
+
else
|
162
|
+
collection.each do |e|
|
163
|
+
key = e[on]
|
164
|
+
lookup[key] = [] unless lookup.key?(key)
|
165
|
+
lookup[key] << e
|
166
|
+
end
|
167
|
+
end
|
168
|
+
lookup
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|