From c4ed888eed561fd65c8ed326302ef87e2688e6e2 Mon Sep 17 00:00:00 2001 From: Andy Leap <104936100+andrewleap-optimizely@users.noreply.github.com> Date: Thu, 4 Aug 2022 09:24:01 -0400 Subject: [PATCH] feat: update ruby version (#305) * update supported ruby versions --- .github/workflows/lint_markdown.yml | 2 +- .github/workflows/ruby.yml | 4 +- .rubocop.yml | 6 +- .rubocop_todo.yml | 4 +- CHANGELOG.md | 6 ++ lib/optimizely.rb | 39 ++++------- lib/optimizely/condition_tree_evaluator.rb | 4 +- .../config/datafile_project_config.rb | 54 ++++----------- .../http_project_config_manager.rb | 1 + .../static_project_config_manager.rb | 1 + lib/optimizely/event/batch_event_processor.rb | 7 +- .../event/entity/conversion_event.rb | 3 +- .../event/entity/impression_event.rb | 3 +- lib/optimizely/event/entity/visitor.rb | 3 +- .../event/entity/visitor_attribute.rb | 3 +- lib/optimizely/event/event_factory.rb | 7 +- .../event/forwarding_event_processor.rb | 3 +- lib/optimizely/event_builder.rb | 15 ++--- lib/optimizely/event_dispatcher.rb | 4 +- lib/optimizely/exceptions.rb | 6 +- lib/optimizely/helpers/http_utils.rb | 9 +-- lib/optimizely/helpers/validator.rb | 4 +- lib/optimizely/logger.rb | 5 +- lib/optimizely/notification_center.rb | 16 ++--- lib/optimizely/optimizely_config.rb | 21 +++--- lib/optimizely/optimizely_factory.rb | 6 +- lib/optimizely/optimizely_user_context.rb | 6 +- lib/optimizely/semantic_version.rb | 4 +- lib/optimizely/user_condition_evaluator.rb | 6 +- optimizely-sdk.gemspec | 3 +- spec/audience_spec.rb | 6 +- spec/benchmarking/benchmark.rb | 12 ++-- .../http_project_config_manager_spec.rb | 24 +++---- spec/event_dispatcher_spec.rb | 10 +-- spec/notification_center_spec.rb | 16 ++--- spec/optimizely_factory_spec.rb | 66 +++++++++---------- spec/optimizely_user_context_spec.rb | 22 +++---- spec/project_spec.rb | 40 +++++------ 38 files changed, 203 insertions(+), 248 deletions(-) diff --git a/.github/workflows/lint_markdown.yml b/.github/workflows/lint_markdown.yml index af23e15a..9089b508 100644 --- a/.github/workflows/lint_markdown.yml +++ b/.github/workflows/lint_markdown.yml @@ -10,7 +10,7 @@ jobs: - name: Set up Ruby uses: ruby/setup-ruby@v1 with: - ruby-version: '2.6' + ruby-version: '3.1' bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Install gem run: | diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 1dfaf2a8..6c99cefd 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -11,7 +11,7 @@ jobs: uses: optimizely/ruby-sdk/.github/workflows/lint_markdown.yml@master integration_tests: - uses: optimizely/ruby-sdk/.github/workflows/integration_test.yml@uzair/with-fsc-git-action + uses: optimizely/ruby-sdk/.github/workflows/integration_test.yml@master secrets: CI_USER_TOKEN: ${{ secrets.CI_USER_TOKEN }} TRAVIS_COM_TOKEN: ${{ secrets.TRAVIS_COM_TOKEN }} @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby: [ '2.3.7', '2.4.4', '2.5.1', '2.6.0' ] + ruby: [ '2.7.0', '3.0.0', '3.1.0' ] steps: - uses: actions/checkout@v3 - name: Set up Ruby ${{ matrix.ruby }} diff --git a/.rubocop.yml b/.rubocop.yml index 65bf129f..ea105dd6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,7 +1,7 @@ inherit_from: .rubocop_todo.yml AllCops: - TargetRubyVersion: 2.3 + TargetRubyVersion: 2.7 Layout/SpaceInsideHashLiteralBraces: EnforcedStyle: no_space @@ -21,7 +21,7 @@ Metrics/ClassLength: Metrics/CyclomaticComplexity: Enabled: false -Metrics/LineLength: +Layout/LineLength: Enabled: false Metrics/MethodLength: @@ -49,4 +49,4 @@ Lint/RescueException: Enabled: true Layout/EndOfLine: - EnforcedStyle: lf + Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 6da46802..07dc3b1c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -7,7 +7,7 @@ # versions of RuboCop, may require this file to be generated again. # Offense count: 1 -Lint/HandleExceptions: +Lint/SuppressedException: Exclude: - 'Rakefile' @@ -44,5 +44,5 @@ Style/TrivialAccessors: # Offense count: 2465 # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https -Metrics/LineLength: +Layout/LineLength: Max: 215 diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a4f88c..8be5beab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Optimizely Ruby SDK Changelog + +## Unreleased + +### Changes: +* Breaking change: Changed official supported versions of Ruby to 2.7, 3.0 and 3.1 + ## 3.10.1 February 2, 2022 diff --git a/lib/optimizely.rb b/lib/optimizely.rb index e2e23320..22d44548 100644 --- a/lib/optimizely.rb +++ b/lib/optimizely.rb @@ -63,12 +63,12 @@ class Project # @param notification_center - Optional Instance of NotificationCenter. # @param event_processor - Optional Responds to process. - def initialize( + def initialize( # rubocop:disable Metrics/ParameterLists datafile = nil, event_dispatcher = nil, logger = nil, error_handler = nil, - skip_json_validation = false, + skip_json_validation = false, # rubocop:disable Style/OptionalBooleanParameter user_profile_service = nil, sdk_key = nil, config_manager = nil, @@ -146,8 +146,7 @@ def create_user_context(user_id, attributes = nil) # validate attributes return nil unless user_inputs_valid?(attributes) - user_context = OptimizelyUserContext.new(self, user_id, attributes) - user_context + OptimizelyUserContext.new(self, user_id, attributes) end def decide(user_context, key, decide_options = []) @@ -219,11 +218,9 @@ def decide(user_context, key, decide_options = []) decision_source = decision.source end - unless decide_options.include? OptimizelyDecideOption::DISABLE_DECISION_EVENT - if decision_source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] || config.send_flag_decisions - send_impression(config, experiment, variation_key || '', flag_key, rule_key || '', feature_enabled, decision_source, user_id, attributes) - decision_event_dispatched = true - end + if !decide_options.include?(OptimizelyDecideOption::DISABLE_DECISION_EVENT) && (decision_source == Optimizely::DecisionService::DECISION_SOURCES['FEATURE_TEST'] || config.send_flag_decisions) + send_impression(config, experiment, variation_key || '', flag_key, rule_key || '', feature_enabled, decision_source, user_id, attributes) + decision_event_dispatched = true end # Generate all variables map if decide options doesn't include excludeVariables @@ -610,15 +607,13 @@ def get_feature_variable(feature_flag_key, variable_key, user_id, attributes = n @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable').message) return nil end - variable_value = get_feature_variable_for_type( + get_feature_variable_for_type( feature_flag_key, variable_key, nil, user_id, attributes ) - - variable_value end # Get the String value of the specified variable in the feature flag. @@ -636,15 +631,13 @@ def get_feature_variable_string(feature_flag_key, variable_key, user_id, attribu @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_string').message) return nil end - variable_value = get_feature_variable_for_type( + get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['STRING'], user_id, attributes ) - - variable_value end # Get the Json value of the specified variable in the feature flag in a Dict. @@ -662,15 +655,13 @@ def get_feature_variable_json(feature_flag_key, variable_key, user_id, attribute @logger.log(Logger::ERROR, InvalidProjectConfigError.new('get_feature_variable_json').message) return nil end - variable_value = get_feature_variable_for_type( + get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['JSON'], user_id, attributes ) - - variable_value end # Get the Boolean value of the specified variable in the feature flag. @@ -689,15 +680,13 @@ def get_feature_variable_boolean(feature_flag_key, variable_key, user_id, attrib return nil end - variable_value = get_feature_variable_for_type( + get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['BOOLEAN'], user_id, attributes ) - - variable_value end # Get the Double value of the specified variable in the feature flag. @@ -716,15 +705,13 @@ def get_feature_variable_double(feature_flag_key, variable_key, user_id, attribu return nil end - variable_value = get_feature_variable_for_type( + get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['DOUBLE'], user_id, attributes ) - - variable_value end # Get values of all the variables in the feature flag and returns them in a Dict @@ -809,15 +796,13 @@ def get_feature_variable_integer(feature_flag_key, variable_key, user_id, attrib return nil end - variable_value = get_feature_variable_for_type( + get_feature_variable_for_type( feature_flag_key, variable_key, Optimizely::Helpers::Constants::VARIABLE_TYPES['INTEGER'], user_id, attributes ) - - variable_value end def is_valid diff --git a/lib/optimizely/condition_tree_evaluator.rb b/lib/optimizely/condition_tree_evaluator.rb index 724bc2d5..aa4fee33 100644 --- a/lib/optimizely/condition_tree_evaluator.rb +++ b/lib/optimizely/condition_tree_evaluator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019, Optimizely and contributors +# Copyright 2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ def evaluate(conditions, leaf_evaluator) if conditions.is_a? Array first_operator = conditions[0] - rest_of_conditions = conditions[1..-1] + rest_of_conditions = conditions[1..] # Operator to apply is not explicit - assume 'or' unless EVALUATORS_BY_OPERATOR_TYPE.include?(conditions[0]) diff --git a/lib/optimizely/config/datafile_project_config.rb b/lib/optimizely/config/datafile_project_config.rb index d6cb29c9..5d939ea4 100644 --- a/lib/optimizely/config/datafile_project_config.rb +++ b/lib/optimizely/config/datafile_project_config.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright 2019-2021, Optimizely and contributors +# Copyright 2019-2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,53 +24,23 @@ class DatafileProjectConfig < ProjectConfig RUNNING_EXPERIMENT_STATUS = ['Running'].freeze RESERVED_ATTRIBUTE_PREFIX = '$opt_' - attr_reader :datafile - attr_reader :account_id - attr_reader :attributes - attr_reader :audiences - attr_reader :typed_audiences - attr_reader :events - attr_reader :experiments - attr_reader :feature_flags - attr_reader :groups - attr_reader :project_id + attr_reader :datafile, :account_id, :attributes, :audiences, :typed_audiences, :events, + :experiments, :feature_flags, :groups, :project_id, :bot_filtering, :revision, + :sdk_key, :environment_key, :rollouts, :version, :send_flag_decisions, + :attribute_key_map, :audience_id_map, :event_key_map, :experiment_feature_map, + :experiment_id_map, :experiment_key_map, :feature_flag_key_map, :feature_variable_key_map, + :group_id_map, :rollout_id_map, :rollout_experiment_id_map, :variation_id_map, + :variation_id_to_variable_usage_map, :variation_key_map, :variation_id_map_by_experiment_id, + :variation_key_map_by_experiment_id, :flag_variation_map, :integration_key_map, :integrations, + :public_key_for_odp, :host_for_odp, :all_segments # Boolean - denotes if Optimizely should remove the last block of visitors' IP address before storing event data attr_reader :anonymize_ip - attr_reader :bot_filtering - attr_reader :revision - attr_reader :sdk_key - attr_reader :environment_key - attr_reader :rollouts - attr_reader :version - attr_reader :send_flag_decisions - attr_reader :integrations - attr_reader :public_key_for_odp - attr_reader :host_for_odp - attr_reader :all_segments - - attr_reader :attribute_key_map - attr_reader :audience_id_map - attr_reader :event_key_map - attr_reader :experiment_feature_map - attr_reader :experiment_id_map - attr_reader :experiment_key_map - attr_reader :feature_flag_key_map - attr_reader :feature_variable_key_map - attr_reader :group_id_map - attr_reader :rollout_id_map - attr_reader :rollout_experiment_id_map - attr_reader :variation_id_map - attr_reader :variation_id_to_variable_usage_map - attr_reader :variation_key_map - attr_reader :variation_id_map_by_experiment_id - attr_reader :variation_key_map_by_experiment_id - attr_reader :flag_variation_map - attr_reader :integration_key_map def initialize(datafile, logger, error_handler) # ProjectConfig init method to fetch and set project config data # # datafile - JSON string representing the project + super() config = JSON.parse(datafile) @@ -224,7 +194,7 @@ def self.create(datafile, logger, error_handler, skip_json_validation) config = new(datafile, logger, error_handler) rescue StandardError => e default_logger = SimpleLogger.new - error_to_handle = e.class == InvalidDatafileVersionError ? e : InvalidInputError.new('datafile') + error_to_handle = e.instance_of?(InvalidDatafileVersionError) ? e : InvalidInputError.new('datafile') error_msg = error_to_handle.message default_logger.log(Logger::ERROR, error_msg) diff --git a/lib/optimizely/config_manager/http_project_config_manager.rb b/lib/optimizely/config_manager/http_project_config_manager.rb index 7eceed76..790353ab 100644 --- a/lib/optimizely/config_manager/http_project_config_manager.rb +++ b/lib/optimizely/config_manager/http_project_config_manager.rb @@ -69,6 +69,7 @@ def initialize( datafile_access_token: nil, proxy_config: nil ) + super() @logger = logger || NoOpLogger.new @error_handler = error_handler || NoOpErrorHandler.new @access_token = datafile_access_token diff --git a/lib/optimizely/config_manager/static_project_config_manager.rb b/lib/optimizely/config_manager/static_project_config_manager.rb index e5c3600f..281beb3d 100644 --- a/lib/optimizely/config_manager/static_project_config_manager.rb +++ b/lib/optimizely/config_manager/static_project_config_manager.rb @@ -34,6 +34,7 @@ def initialize(datafile, logger, error_handler, skip_json_validation) # skip_json_validation - Optional boolean param which allows skipping JSON schema # validation upon object invocation. By default JSON schema validation will be performed. # Returns instance of DatafileProjectConfig, nil otherwise. + super() @config = DatafileProjectConfig.create( datafile, logger, diff --git a/lib/optimizely/event/batch_event_processor.rb b/lib/optimizely/event/batch_event_processor.rb index 76e49293..52ec0533 100644 --- a/lib/optimizely/event/batch_event_processor.rb +++ b/lib/optimizely/event/batch_event_processor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019, Optimizely and contributors +# Copyright 2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ def initialize( logger: NoOpLogger.new, notification_center: nil ) + super() @event_queue = event_queue @logger = logger @event_dispatcher = event_dispatcher || EventDispatcher.new(logger: @logger) @@ -101,8 +102,8 @@ def process(user_event) @event_queue.push(user_event, true) @wait_mutex.synchronize { @resource.signal } rescue => e - @logger.log(Logger::WARN, 'Payload not accepted by the queue: ' + e.message) - return + @logger.log(Logger::WARN, "Payload not accepted by the queue: #{e.message}") + nil end end diff --git a/lib/optimizely/event/entity/conversion_event.rb b/lib/optimizely/event/entity/conversion_event.rb index 8859f932..4eed4610 100644 --- a/lib/optimizely/event/entity/conversion_event.rb +++ b/lib/optimizely/event/entity/conversion_event.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019, Optimizely and contributors +# Copyright 2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ def initialize( tags:, bot_filtering: ) + super() @event_context = event_context @uuid = SecureRandom.uuid @timestamp = Helpers::DateTimeUtils.create_timestamp diff --git a/lib/optimizely/event/entity/impression_event.rb b/lib/optimizely/event/entity/impression_event.rb index 4a27ef39..e3458d9b 100644 --- a/lib/optimizely/event/entity/impression_event.rb +++ b/lib/optimizely/event/entity/impression_event.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019-2020, Optimizely and contributors +# Copyright 2019-2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ def initialize( visitor_attributes:, bot_filtering: ) + super() @event_context = event_context @uuid = SecureRandom.uuid @timestamp = Helpers::DateTimeUtils.create_timestamp diff --git a/lib/optimizely/event/entity/visitor.rb b/lib/optimizely/event/entity/visitor.rb index ff6cac48..c0338555 100644 --- a/lib/optimizely/event/entity/visitor.rb +++ b/lib/optimizely/event/entity/visitor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019, Optimizely and contributors +# Copyright 2019, 2022. Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ module Optimizely class Visitor attr_reader :snapshots, :visitor_id, :attributes + def initialize(snapshots:, visitor_id:, attributes:) @snapshots = snapshots @visitor_id = visitor_id diff --git a/lib/optimizely/event/entity/visitor_attribute.rb b/lib/optimizely/event/entity/visitor_attribute.rb index 2ac9df8e..1a578d3a 100644 --- a/lib/optimizely/event/entity/visitor_attribute.rb +++ b/lib/optimizely/event/entity/visitor_attribute.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019, Optimizely and contributors +# Copyright 2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ module Optimizely class VisitorAttribute attr_reader :entity_id, :key, :type, :value + def initialize(entity_id:, key:, type:, value:) @entity_id = entity_id @key = key diff --git a/lib/optimizely/event/event_factory.rb b/lib/optimizely/event/event_factory.rb index a416ff66..d8d5062e 100644 --- a/lib/optimizely/event/event_factory.rb +++ b/lib/optimizely/event/event_factory.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019-2020, Optimizely and contributors +# Copyright 2019-2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -41,10 +41,11 @@ def create_log_event(user_events, logger) visitors = [] user_context = nil user_events.each do |user_event| - if user_event.is_a? Optimizely::ImpressionEvent + case user_event + when Optimizely::ImpressionEvent visitor = create_impression_event_visitor(user_event) visitors.push(visitor) - elsif user_event.is_a? Optimizely::ConversionEvent + when Optimizely::ConversionEvent visitor = create_conversion_event_visitor(user_event) visitors.push(visitor) else diff --git a/lib/optimizely/event/forwarding_event_processor.rb b/lib/optimizely/event/forwarding_event_processor.rb index 8970f301..c3fd6de1 100644 --- a/lib/optimizely/event/forwarding_event_processor.rb +++ b/lib/optimizely/event/forwarding_event_processor.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019, Optimizely and contributors +# Copyright 2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ class ForwardingEventProcessor < EventProcessor # ForwardingEventProcessor is a basic transformation stage for converting # the event batch into a LogEvent to be dispatched. def initialize(event_dispatcher, logger = nil, notification_center = nil) + super() @event_dispatcher = event_dispatcher @logger = logger || NoOpLogger.new @notification_center = notification_center diff --git a/lib/optimizely/event_builder.rb b/lib/optimizely/event_builder.rb index b9746bfc..9b87413e 100644 --- a/lib/optimizely/event_builder.rb +++ b/lib/optimizely/event_builder.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2016-2019, Optimizely and contributors +# Copyright 2016-2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,10 +27,7 @@ module Optimizely class Event # Representation of an event which can be sent to the Optimizely logging endpoint. - attr_reader :http_verb - attr_reader :params - attr_reader :url - attr_reader :headers + attr_reader :http_verb, :params, :url, :headers def initialize(http_verb, url, params, headers) @http_verb = http_verb @@ -90,7 +87,7 @@ def get_common_params(project_config, user_id, attributes) ) end - common_params = { + { account_id: project_config.account_id, project_id: project_config.project_id, visitors: [ @@ -106,8 +103,6 @@ def get_common_params(project_config, user_id, attributes) enrich_decisions: true, client_version: VERSION } - - common_params end end @@ -166,7 +161,7 @@ def get_impression_params(project_config, experiment, variation_id) experiment_key = experiment['key'] experiment_id = experiment['id'] - impression_event_params = { + { decisions: [{ campaign_id: project_config.experiment_key_map[experiment_key]['layerId'], experiment_id: experiment_id, @@ -179,8 +174,6 @@ def get_impression_params(project_config, experiment, variation_id) uuid: create_uuid }] } - - impression_event_params end def get_conversion_params(event, event_tags) diff --git a/lib/optimizely/event_dispatcher.rb b/lib/optimizely/event_dispatcher.rb index 18e4e57c..aaa0b593 100644 --- a/lib/optimizely/event_dispatcher.rb +++ b/lib/optimizely/event_dispatcher.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2016-2017, 2019-2020 Optimizely and contributors +# Copyright 2016-2017, 2019-2020, 2022 Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ def dispatch_event(event) @logger.log(Logger::ERROR, error_msg) @error_handler.handle_error(HTTPCallError.new("HTTP Server Error: #{response.code}")) else - @logger.log(Logger::DEBUG, 'event successfully sent with response code ' + response.code.to_s) + @logger.log(Logger::DEBUG, "event successfully sent with response code #{response.code}") end rescue Timeout::Error => e @logger.log(Logger::ERROR, "Request Timed out. Error: #{e}") diff --git a/lib/optimizely/exceptions.rb b/lib/optimizely/exceptions.rb index be35f4d2..fef0f829 100644 --- a/lib/optimizely/exceptions.rb +++ b/lib/optimizely/exceptions.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2016-2020, Optimizely and contributors +# Copyright 2016-2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -107,10 +107,6 @@ def initialize(msg = 'Provided notification type is invalid.') class InvalidInputsError < Error # Raised when an invalid inputs are provided during Project instantiation - - def initialize(msg) - super msg - end end class InvalidProjectConfigError < Error diff --git a/lib/optimizely/helpers/http_utils.rb b/lib/optimizely/helpers/http_utils.rb index fae0aa20..3530bb6e 100644 --- a/lib/optimizely/helpers/http_utils.rb +++ b/lib/optimizely/helpers/http_utils.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2020, Optimizely and contributors +# Copyright 2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,14 +23,15 @@ module Helpers module HttpUtils module_function - def make_request(url, http_method, request_body = nil, headers = {}, read_timeout = nil, proxy_config = nil) + def make_request(url, http_method, request_body = nil, headers = {}, read_timeout = nil, proxy_config = nil) # rubocop:disable Metrics/ParameterLists # makes http/https GET/POST request and returns response # uri = URI.parse(url) - if http_method == :get + case http_method + when :get request = Net::HTTP::Get.new(uri.request_uri) - elsif http_method == :post + when :post request = Net::HTTP::Post.new(uri.request_uri) request.body = request_body if request_body else diff --git a/lib/optimizely/helpers/validator.rb b/lib/optimizely/helpers/validator.rb index a920c166..fb901f39 100644 --- a/lib/optimizely/helpers/validator.rb +++ b/lib/optimizely/helpers/validator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2016-2019, Optimizely and contributors +# Copyright 2016-2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -168,7 +168,7 @@ def same_types?(value_1, value_2) return true if boolean?(value_1) && boolean?(value_2) - value_1.class == value_2.class + value_1.instance_of?(value_2.class) end def finite_number?(value) diff --git a/lib/optimizely/logger.rb b/lib/optimizely/logger.rb index 7a8bf9a8..b2917587 100644 --- a/lib/optimizely/logger.rb +++ b/lib/optimizely/logger.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2016-2017, Optimizely and contributors +# Copyright 2016-2017, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -34,7 +34,8 @@ class SimpleLogger < BaseLogger # Simple wrapper around Logger. def initialize(min_level = Logger::INFO) - @logger = Logger.new(STDOUT) + super() + @logger = Logger.new($stdout) @logger.level = min_level end diff --git a/lib/optimizely/notification_center.rb b/lib/optimizely/notification_center.rb index 6f4eda99..e4054595 100644 --- a/lib/optimizely/notification_center.rb +++ b/lib/optimizely/notification_center.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2017-2019, Optimizely and contributors +# Copyright 2017-2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -135,14 +135,12 @@ def send_notifications(notification_type, *args) return nil unless notification_type_valid?(notification_type) @notifications[notification_type].each do |notification| - begin - notification_callback = notification[:callback] - notification_callback.call(*args) - @logger.log Logger::INFO, "Notification #{notification_type} sent successfully." - rescue => e - @logger.log(Logger::ERROR, "Problem calling notify callback. Error: #{e}") - return nil - end + notification_callback = notification[:callback] + notification_callback.call(*args) + @logger.log Logger::INFO, "Notification #{notification_type} sent successfully." + rescue => e + @logger.log(Logger::ERROR, "Problem calling notify callback. Error: #{e}") + return nil end end diff --git a/lib/optimizely/optimizely_config.rb b/lib/optimizely/optimizely_config.rb index f3337752..7b53a07c 100644 --- a/lib/optimizely/optimizely_config.rb +++ b/lib/optimizely/optimizely_config.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright 2019-2021, Optimizely and contributors +# Copyright 2019-2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ def initialize(project_config) def config experiments_map_object = experiments_map features_map = get_features_map(experiments_id_map) - config = { + { 'sdkKey' => @project_config.sdk_key, 'datafile' => @project_config.datafile, # This experimentsMap is for experiments of legacy projects only. @@ -63,7 +63,6 @@ def config 'events' => get_events_list(@project_config.events), 'environmentKey' => @project_config.environment_key } - config end private @@ -204,13 +203,13 @@ def stringify_conditions(conditions, audiences_map) conditions_str = '' length = conditions.length() return '' if length.zero? - return '"' + lookup_name_from_id(conditions[0], audiences_map) + '"' if length == 1 && !OPERATORS.include?(conditions[0]) + return "\"#{lookup_name_from_id(conditions[0], audiences_map)}\"" if length == 1 && !OPERATORS.include?(conditions[0]) # Edge cases for lengths 0, 1 or 2 if length == 2 && OPERATORS.include?(conditions[0]) && !conditions[1].is_a?(Array) && !OPERATORS.include?(conditions[1]) - return '"' + lookup_name_from_id(conditions[1], audiences_map) + '"' if conditions[0] != 'not' + return "\"#{lookup_name_from_id(conditions[1], audiences_map)}\"" if conditions[0] != 'not' - return conditions[0].upcase + ' "' + lookup_name_from_id(conditions[1], audiences_map) + '"' + return "#{conditions[0].upcase} \"#{lookup_name_from_id(conditions[1], audiences_map)}\"" end if length > 1 @@ -223,9 +222,9 @@ def stringify_conditions(conditions, audiences_map) # Check if at the end or not to determine where to add the operand # Recursive call to call stringify on embedded list conditions_str += if n + 1 < length - '(' + stringify_conditions(conditions[n], audiences_map) + ') ' + "(#{stringify_conditions(conditions[n], audiences_map)}) " else - operand + ' (' + stringify_conditions(conditions[n], audiences_map) + ')' + "#{operand} (#{stringify_conditions(conditions[n], audiences_map)})" end # If the item is not a list, we process as an audience ID and retrieve the name else @@ -233,11 +232,11 @@ def stringify_conditions(conditions, audiences_map) unless audience_name.nil? # Below handles all cases for one ID or greater conditions_str += if n + 1 < length - 1 - '"' + audience_name + '" ' + operand + ' ' + "\"#{audience_name}\" #{operand} " elsif n + 1 == length - operand + ' "' + audience_name + '"' + "#{operand} \"#{audience_name}\"" else - '"' + audience_name + '" ' + "\"#{audience_name}\" " end end end diff --git a/lib/optimizely/optimizely_factory.rb b/lib/optimizely/optimizely_factory.rb index 65b5ee59..99ea733f 100644 --- a/lib/optimizely/optimizely_factory.rb +++ b/lib/optimizely/optimizely_factory.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019, Optimizely and contributors +# Copyright 2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -129,13 +129,13 @@ def self.default_instance_with_config_manager(config_manager) # # if @max_event_batch_size and @max_event_flush_interval are nil then default batchsize and flush_interval # will be used to setup batchEventProcessor. - def self.custom_instance( + def self.custom_instance( # rubocop:disable Metrics/ParameterLists sdk_key, datafile = nil, event_dispatcher = nil, logger = nil, error_handler = nil, - skip_json_validation = false, + skip_json_validation = false, # rubocop:disable Style/OptionalBooleanParameter user_profile_service = nil, config_manager = nil, notification_center = nil diff --git a/lib/optimizely/optimizely_user_context.rb b/lib/optimizely/optimizely_user_context.rb index f00f78c9..1298fb7d 100644 --- a/lib/optimizely/optimizely_user_context.rb +++ b/lib/optimizely/optimizely_user_context.rb @@ -22,11 +22,7 @@ module Optimizely class OptimizelyUserContext # Representation of an Optimizely User Context using which APIs are to be called. - attr_reader :user_id - attr_reader :forced_decisions - attr_reader :OptimizelyDecisionContext - attr_reader :OptimizelyForcedDecision - attr_reader :optimizely_client + attr_reader :user_id, :forced_decisions, :optimizely_client OptimizelyDecisionContext = Struct.new(:flag_key, :rule_key) OptimizelyForcedDecision = Struct.new(:variation_key) diff --git a/lib/optimizely/semantic_version.rb b/lib/optimizely/semantic_version.rb index 4a7cad0d..0b3c0cb1 100644 --- a/lib/optimizely/semantic_version.rb +++ b/lib/optimizely/semantic_version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2020, Optimizely and contributors +# Copyright 2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -88,7 +88,7 @@ def split_semantic_version(target) unless target_parts.empty? target_prefix = target_parts[0].to_s - target_suffix = target_parts[1..-1] + target_suffix = target_parts[1..] end # expect a version string of the form x.y.z diff --git a/lib/optimizely/user_condition_evaluator.rb b/lib/optimizely/user_condition_evaluator.rb index 9f4556cf..ced8ebf7 100644 --- a/lib/optimizely/user_condition_evaluator.rb +++ b/lib/optimizely/user_condition_evaluator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019-2020, Optimizely and contributors +# Copyright 2019-2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -129,7 +129,7 @@ def evaluate(leaf_condition) condition_name ) ) - return nil + nil rescue InvalidSemanticVersion condition_name = leaf_condition['name'] @@ -141,7 +141,7 @@ def evaluate(leaf_condition) condition_name ) ) - return nil + nil end end diff --git a/optimizely-sdk.gemspec b/optimizely-sdk.gemspec index c8b91b4e..dd90d70b 100644 --- a/optimizely-sdk.gemspec +++ b/optimizely-sdk.gemspec @@ -7,6 +7,7 @@ Gem::Specification.new do |spec| spec.version = Optimizely::VERSION spec.authors = ['Optimizely'] spec.email = ['developers@optimizely.com'] + spec.required_ruby_version = '>= 2.7' spec.summary = "Ruby SDK for Optimizely's testing framework" spec.description = "A Ruby SDK for Optimizely's Full Stack product." @@ -20,7 +21,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'coveralls_reborn' spec.add_development_dependency 'rake' spec.add_development_dependency 'rspec' - spec.add_development_dependency 'rubocop', '0.73.0' + spec.add_development_dependency 'rubocop' spec.add_development_dependency 'webmock' spec.add_runtime_dependency 'json-schema', '~> 2.6' diff --git a/spec/audience_spec.rb b/spec/audience_spec.rb index ddbd6101..a531d8f9 100644 --- a/spec/audience_spec.rb +++ b/spec/audience_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2016-2017, 2019-2020, Optimizely and contributors +# Copyright 2016-2017, 2019-2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -223,7 +223,7 @@ expect(spy_logger).to have_received(:log).once.with( Logger::DEBUG, - "Evaluating audiences for experiment 'test_experiment_with_audience': " + '["11110"].' + "Evaluating audiences for experiment 'test_experiment_with_audience': [\"11110\"]." ) expect(spy_logger).to have_received(:log).once.with( @@ -250,7 +250,7 @@ expect(spy_logger).to have_received(:log).once.with( Logger::DEBUG, - "Evaluating audiences for experiment 'test_experiment_with_audience': " + '["11154", "11155"].' + "Evaluating audiences for experiment 'test_experiment_with_audience': [\"11154\", \"11155\"]." ) # audience_11154 diff --git a/spec/benchmarking/benchmark.rb b/spec/benchmarking/benchmark.rb index 403a0e53..f2cf7c90 100644 --- a/spec/benchmarking/benchmark.rb +++ b/spec/benchmarking/benchmark.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2016-2017, Optimizely and contributors +# Copyright 2016-2017, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ # require 'optimizely' require 'benchmark' -require_relative 'data.rb' +require_relative 'data' module OptimizelyBenchmark ITERATIONS = 10 @@ -157,10 +157,10 @@ def run_tests trim_max_min(tms25) trim_max_min(tms50) - puts test, ' ' + Benchmark::CAPTION - puts '10 exp:' + ((tms10.reduce(:+) / tms10.size) * 1000).format(Benchmark::FORMAT) - puts '25 exp:' + ((tms25.reduce(:+) / tms25.size) * 1000).format(Benchmark::FORMAT) - puts '50 exp:' + ((tms50.reduce(:+) / tms50.size) * 1000).format(Benchmark::FORMAT) + puts test, " #{Benchmark::CAPTION}" + puts "10 exp:#{((tms10.reduce(:+) / tms10.size) * 1000).format(Benchmark::FORMAT)}" + puts "25 exp:#{((tms25.reduce(:+) / tms25.size) * 1000).format(Benchmark::FORMAT)}" + puts "50 exp:#{((tms50.reduce(:+) / tms50.size) * 1000).format(Benchmark::FORMAT)}" puts '' end end diff --git a/spec/config_manager/http_project_config_manager_spec.rb b/spec/config_manager/http_project_config_manager_spec.rb index 038efe14..c786f38e 100644 --- a/spec/config_manager/http_project_config_manager_spec.rb +++ b/spec/config_manager/http_project_config_manager_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019-2020, Optimizely and contributors +# Copyright 2019-2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,10 +23,10 @@ let(:spy_logger) { spy('logger') } before(:context) do - VALID_SDK_KEY_CONFIG = OptimizelySpec::VALID_CONFIG_BODY.dup + VALID_SDK_KEY_CONFIG = OptimizelySpec::VALID_CONFIG_BODY.dup # rubocop:disable Lint/ConstantDefinitionInBlock VALID_SDK_KEY_CONFIG['accountId'] = '12002' VALID_SDK_KEY_CONFIG['revision'] = '81' - VALID_SDK_KEY_CONFIG_JSON = JSON.dump(VALID_SDK_KEY_CONFIG) + VALID_SDK_KEY_CONFIG_JSON = JSON.dump(VALID_SDK_KEY_CONFIG) # rubocop:disable Lint/ConstantDefinitionInBlock end before(:example) do @@ -60,7 +60,7 @@ url: 'https://cdn.optimizely.com/datafiles/valid_sdk_key.json' ) - until @http_project_config_manager.ready?; end + sleep 0.1 until @http_project_config_manager.ready? expect(@http_project_config_manager.config).to be_a Optimizely::ProjectConfig end @@ -78,7 +78,7 @@ url: 'http://cdn.optimizely.com/datafiles/valid_sdk_key.json' ) - until @http_project_config_manager.ready?; end + sleep 0.1 until @http_project_config_manager.ready? expect(@http_project_config_manager.config).to be_a Optimizely::ProjectConfig end @@ -87,7 +87,7 @@ sdk_key: 'valid_sdk_key' ) - until @http_project_config_manager.ready?; end + sleep 0.1 until @http_project_config_manager.ready? expect(@http_project_config_manager.config).to be_a Optimizely::ProjectConfig end @@ -96,7 +96,7 @@ sdk_key: 'valid_sdk_key', url_template: 'https://cdn.optimizely.com/datafiles/%s.json' ) - until @http_project_config_manager.ready?; end + sleep 0.1 until @http_project_config_manager.ready? expect(@http_project_config_manager.config).to be_a Optimizely::ProjectConfig end @@ -157,7 +157,7 @@ notification_center: notification_center ) - until @http_project_config_manager.ready?; end + sleep 0.1 until @http_project_config_manager.ready? end it 'should not send config update notification when datafile is provided' do @@ -171,7 +171,7 @@ start_by_default: false, notification_center: notification_center ) - until @http_project_config_manager.ready?; end + sleep 0.1 until @http_project_config_manager.ready? end end @@ -301,7 +301,7 @@ @http_project_config_manager = Optimizely::HTTPProjectConfigManager.new( sdk_key: 'valid_sdk_key' ) - until @http_project_config_manager.ready?; end + sleep 0.1 until @http_project_config_manager.ready? expect(@http_project_config_manager.config).to be_a Optimizely::ProjectConfig end @@ -315,7 +315,7 @@ @http_project_config_manager.start! - until @http_project_config_manager.ready?; end + sleep 0.1 until @http_project_config_manager.ready? expect(@http_project_config_manager.ready?).to be true end @@ -470,7 +470,7 @@ polling_interval: 0.1 ) expect(@http_project_config_manager.optimizely_config['revision']).to eq('42') - sleep 0.3 + sleep 0.5 expect(@http_project_config_manager.optimizely_config['revision']).to eq('81') end end diff --git a/spec/event_dispatcher_spec.rb b/spec/event_dispatcher_spec.rb index 151f31ab..499e8f09 100644 --- a/spec/event_dispatcher_spec.rb +++ b/spec/event_dispatcher_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2016-2017, 2019-2020 Optimizely and contributors +# Copyright 2016-2017, 2019-2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -89,7 +89,7 @@ end it 'should properly dispatch V2 (GET) events' do - get_url = @url + '?a=111001&g=111028&n=test_event&u=test_user' + get_url = "#{@url}?a=111001&g=111028&n=test_event&u=test_user" stub_request(:get, get_url) event = Optimizely::Event.new(:get, get_url, @params, @post_headers) @event_dispatcher.dispatch_event(event) @@ -98,7 +98,7 @@ end it 'should properly dispatch V2 (GET) events with timeout exception' do - get_url = @url + '?a=111001&g=111028&n=test_event&u=test_user' + get_url = "#{@url}?a=111001&g=111028&n=test_event&u=test_user" event = Optimizely::Event.new(:get, get_url, @params, @post_headers) timeout_error = Timeout::Error.new stub_request(:get, get_url).to_raise(timeout_error) @@ -109,7 +109,7 @@ end it 'should log and handle Timeout error' do - get_url = @url + '?a=111001&g=111028&n=test_event&u=test_user' + get_url = "#{@url}?a=111001&g=111028&n=test_event&u=test_user" event = Optimizely::Event.new(:post, get_url, @params, @post_headers) timeout_error = Timeout::Error.new stub_request(:post, get_url).to_raise(timeout_error) @@ -125,7 +125,7 @@ end it 'should log and handle any standard error' do - get_url = @url + '?a=111001&g=111028&n=test_event&u=test_user' + get_url = "#{@url}?a=111001&g=111028&n=test_event&u=test_user" event = Optimizely::Event.new(:post, get_url, @params, @post_headers) stub_request(:post, get_url).to_raise(ArgumentError.new) diff --git a/spec/notification_center_spec.rb b/spec/notification_center_spec.rb index 6c254bc8..978de5ac 100644 --- a/spec/notification_center_spec.rb +++ b/spec/notification_center_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2017-2019, Optimizely and contributors +# Copyright 2017-2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ let(:notification_center) { Optimizely::NotificationCenter.new(spy_logger, error_handler) } before(:context) do - class CallBack + class CallBack # rubocop:disable Lint/ConstantDefinitionInBlock attr_reader :args def call(*args) @@ -117,7 +117,7 @@ def call(*args) it 'should add and return notification ID when multiple valid callbacks are added for a single notification type' do - class CallBackSecond + class CallBackSecond # rubocop:disable Lint/ConstantDefinitionInBlock def call; end end @@ -335,7 +335,7 @@ def call; end @callback_second = CallBackSecond.new @callback_reference_second = @callback_second.method(:call) - class CallBackThird + class CallBackThird # rubocop:disable Lint/ConstantDefinitionInBlock def call; end end @@ -404,7 +404,7 @@ def call; end end describe '.send_notifications' do - class Invitation + class Invitation # rubocop:disable Lint/ConstantDefinitionInBlock def initialize(logger) @logger = logger end @@ -507,7 +507,7 @@ def deliver_three; end notification_center.send_notifications(Optimizely::NotificationCenter::NOTIFICATION_TYPES[:TRACK], :arg1, 'arg2', arg3: 4) - expect(actual_args).to eq([:arg1, 'arg2', arg3: 4]) + expect(actual_args).to eq([:arg1, 'arg2', {arg3: 4}]) end it 'should send notifications to lambdas' do @@ -518,7 +518,7 @@ def deliver_three; end notification_center.send_notifications(Optimizely::NotificationCenter::NOTIFICATION_TYPES[:TRACK], :arg1, 'arg2', arg3: 4) - expect(actual_args).to eq([:arg1, 'arg2', arg3: 4]) + expect(actual_args).to eq([:arg1, 'arg2', {arg3: 4}]) end it 'should send notifications to callables' do @@ -528,7 +528,7 @@ def deliver_three; end notification_center.send_notifications(Optimizely::NotificationCenter::NOTIFICATION_TYPES[:TRACK], :arg1, 'arg2', arg3: 4) - expect(callback.args).to eq([:arg1, 'arg2', arg3: 4]) + expect(callback.args).to eq([:arg1, 'arg2', {arg3: 4}]) end end diff --git a/spec/optimizely_factory_spec.rb b/spec/optimizely_factory_spec.rb index 502be4e2..c293f5da 100644 --- a/spec/optimizely_factory_spec.rb +++ b/spec/optimizely_factory_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2019, Optimizely and contributors +# Copyright 2019, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ describe '.default_instance' do it 'should create http config manager when sdk_key is given' do optimizely_instance = Optimizely::OptimizelyFactory.default_instance('sdk_key', datafile) - expect(optimizely_instance.config_manager). to be_instance_of(Optimizely::HTTPProjectConfigManager) + expect(optimizely_instance.config_manager).to be_instance_of(Optimizely::HTTPProjectConfigManager) end it 'should create http config manager when polling interval and blocking timeout are set' do @@ -53,27 +53,27 @@ optimizely_instance = Optimizely::OptimizelyFactory.default_instance('sdk_key', datafile) # Verify that values set in OptimizelyFactory are being used inside config manager. - expect(optimizely_instance.config_manager.instance_variable_get(:@polling_interval)). to eq(40) - expect(optimizely_instance.config_manager.instance_variable_get(:@blocking_timeout)). to eq(5) + expect(optimizely_instance.config_manager.instance_variable_get(:@polling_interval)).to eq(40) + expect(optimizely_instance.config_manager.instance_variable_get(:@blocking_timeout)).to eq(5) end it 'should create http config manager with the same components as the instance' do optimizely_instance = Optimizely::OptimizelyFactory.default_instance('sdk_key', datafile) - expect(optimizely_instance.error_handler). to be(optimizely_instance.config_manager.instance_variable_get(:@error_handler)) - expect(optimizely_instance.logger). to be(optimizely_instance.config_manager.instance_variable_get(:@logger)) - expect(optimizely_instance.notification_center). to be(optimizely_instance.config_manager.instance_variable_get(:@notification_center)) + expect(optimizely_instance.error_handler).to be(optimizely_instance.config_manager.instance_variable_get(:@error_handler)) + expect(optimizely_instance.logger).to be(optimizely_instance.config_manager.instance_variable_get(:@logger)) + expect(optimizely_instance.notification_center).to be(optimizely_instance.config_manager.instance_variable_get(:@notification_center)) end end describe '.default_instance_with_manager' do it 'should take provided custom config manager' do - class CustomConfigManager + class CustomConfigManager # rubocop:disable Lint/ConstantDefinitionInBlock attr_reader :config end custom_config_manager = CustomConfigManager.new optimizely_instance = Optimizely::OptimizelyFactory.default_instance_with_config_manager(custom_config_manager) - expect(optimizely_instance.config_manager). to be(custom_config_manager) + expect(optimizely_instance.config_manager).to be(custom_config_manager) end end @@ -94,8 +94,8 @@ class CustomConfigManager ) # Verify that values set in OptimizelyFactory are being used inside config manager. - expect(optimizely_instance.config_manager.instance_variable_get(:@polling_interval)). to eq(50) - expect(optimizely_instance.config_manager.instance_variable_get(:@blocking_timeout)). to eq(10) + expect(optimizely_instance.config_manager.instance_variable_get(:@polling_interval)).to eq(50) + expect(optimizely_instance.config_manager.instance_variable_get(:@blocking_timeout)).to eq(10) end it 'should take event processor when flush interval and batch size are set' do @@ -123,50 +123,50 @@ class CustomConfigManager notification_center ) - expect(error_handler). to be(optimizely_instance.config_manager.instance_variable_get(:@error_handler)) - expect(logger). to be(optimizely_instance.config_manager.instance_variable_get(:@logger)) - expect(notification_center). to be(optimizely_instance.config_manager.instance_variable_get(:@notification_center)) + expect(error_handler).to be(optimizely_instance.config_manager.instance_variable_get(:@error_handler)) + expect(logger).to be(optimizely_instance.config_manager.instance_variable_get(:@logger)) + expect(notification_center).to be(optimizely_instance.config_manager.instance_variable_get(:@notification_center)) - expect(error_handler). to be(optimizely_instance.error_handler) - expect(logger). to be(optimizely_instance.logger) - expect(notification_center). to be(optimizely_instance.notification_center) + expect(error_handler).to be(optimizely_instance.error_handler) + expect(logger).to be(optimizely_instance.logger) + expect(notification_center).to be(optimizely_instance.notification_center) end end describe '.max_event_batch_size' do it 'should log error message and return nil when invalid batch size provided' do - expect(Optimizely::OptimizelyFactory.max_event_batch_size([], spy_logger)). to eq(nil) - expect(Optimizely::OptimizelyFactory.max_event_batch_size(true, spy_logger)). to eq(nil) - expect(Optimizely::OptimizelyFactory.max_event_batch_size('test', spy_logger)). to eq(nil) - expect(Optimizely::OptimizelyFactory.max_event_batch_size(5.2, spy_logger)). to eq(nil) - expect(Optimizely::OptimizelyFactory.max_event_batch_size(nil, spy_logger)). to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_batch_size([], spy_logger)).to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_batch_size(true, spy_logger)).to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_batch_size('test', spy_logger)).to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_batch_size(5.2, spy_logger)).to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_batch_size(nil, spy_logger)).to eq(nil) expect(spy_logger).to have_received(:log).with(Logger::ERROR, 'Batch size is invalid, setting to default batch size 10.').exactly(5).times - expect(Optimizely::OptimizelyFactory.max_event_batch_size(0, spy_logger)). to eq(nil) - expect(Optimizely::OptimizelyFactory.max_event_batch_size(-2, spy_logger)). to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_batch_size(0, spy_logger)).to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_batch_size(-2, spy_logger)).to eq(nil) expect(spy_logger).to have_received(:log).with(Logger::ERROR, 'Batch size is negative, setting to default batch size 10.').twice end it 'should not log error and return batch size and when valid batch size provided' do - expect(Optimizely::OptimizelyFactory.max_event_batch_size(5, spy_logger)). to eq(5) + expect(Optimizely::OptimizelyFactory.max_event_batch_size(5, spy_logger)).to eq(5) expect(spy_logger).not_to have_received(:log) end end describe '.max_event_flush_interval' do it 'should log error message and return nil when invalid flush interval provided' do - expect(Optimizely::OptimizelyFactory.max_event_flush_interval([], spy_logger)). to eq(nil) - expect(Optimizely::OptimizelyFactory.max_event_flush_interval(true, spy_logger)). to eq(nil) - expect(Optimizely::OptimizelyFactory.max_event_flush_interval('test', spy_logger)). to eq(nil) - expect(Optimizely::OptimizelyFactory.max_event_flush_interval(nil, spy_logger)). to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_flush_interval([], spy_logger)).to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_flush_interval(true, spy_logger)).to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_flush_interval('test', spy_logger)).to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_flush_interval(nil, spy_logger)).to eq(nil) expect(spy_logger).to have_received(:log).with(Logger::ERROR, 'Flush interval is invalid, setting to default flush interval 30000.').exactly(4).times - expect(Optimizely::OptimizelyFactory.max_event_flush_interval(0, spy_logger)). to eq(nil) - expect(Optimizely::OptimizelyFactory.max_event_flush_interval(-2, spy_logger)). to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_flush_interval(0, spy_logger)).to eq(nil) + expect(Optimizely::OptimizelyFactory.max_event_flush_interval(-2, spy_logger)).to eq(nil) expect(spy_logger).to have_received(:log).with(Logger::ERROR, 'Flush interval is negative, setting to default flush interval 30000.').twice end it 'should not log error and return batch size and when valid flush interval provided' do - expect(Optimizely::OptimizelyFactory.max_event_flush_interval(5, spy_logger)). to eq(5) - expect(Optimizely::OptimizelyFactory.max_event_flush_interval(5.5, spy_logger)). to eq(5.5) + expect(Optimizely::OptimizelyFactory.max_event_flush_interval(5, spy_logger)).to eq(5) + expect(Optimizely::OptimizelyFactory.max_event_flush_interval(5.5, spy_logger)).to eq(5.5) expect(spy_logger).not_to have_received(:log) end end diff --git a/spec/optimizely_user_context_spec.rb b/spec/optimizely_user_context_spec.rb index 78011068..3c3be2e4 100644 --- a/spec/optimizely_user_context_spec.rb +++ b/spec/optimizely_user_context_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2020, Optimizely and contributors +# Copyright 2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -38,14 +38,14 @@ attributes = {' browser' => 'firefox'} user_context_obj = Optimizely::OptimizelyUserContext.new(project_instance, user_id, attributes) - expect(user_context_obj.instance_variable_get(:@optimizely_client)). to eq(project_instance) - expect(user_context_obj.instance_variable_get(:@user_id)). to eq(user_id) - expect(user_context_obj.instance_variable_get(:@user_attributes)). to eq(attributes) + expect(user_context_obj.instance_variable_get(:@optimizely_client)).to eq(project_instance) + expect(user_context_obj.instance_variable_get(:@user_id)).to eq(user_id) + expect(user_context_obj.instance_variable_get(:@user_attributes)).to eq(attributes) end it 'should set user attributes to empty hash when passed nil' do user_context_obj = Optimizely::OptimizelyUserContext.new(project_instance, 'test_user', nil) - expect(user_context_obj.instance_variable_get(:@user_attributes)). to eq({}) + expect(user_context_obj.instance_variable_get(:@user_attributes)).to eq({}) end end @@ -58,7 +58,7 @@ expected_attributes = attributes expected_attributes['id'] = 49 - expect(user_context_obj.instance_variable_get(:@user_attributes)). to eq(expected_attributes) + expect(user_context_obj.instance_variable_get(:@user_attributes)).to eq(expected_attributes) end it 'should override attribute value if key already exists in hash' do @@ -70,7 +70,7 @@ expected_attributes = attributes expected_attributes['browser'] = 'chrome' - expect(user_context_obj.instance_variable_get(:@user_attributes)). to eq(expected_attributes) + expect(user_context_obj.instance_variable_get(:@user_attributes)).to eq(expected_attributes) end it 'should not alter original attributes object when attrubute is modified in the user context' do @@ -78,7 +78,7 @@ original_attributes = {'browser' => 'firefox'} user_context_obj = Optimizely::OptimizelyUserContext.new(project_instance, user_id, original_attributes) user_context_obj.set_attribute('id', 49) - expect(user_context_obj.instance_variable_get(:@user_attributes)). to eq( + expect(user_context_obj.instance_variable_get(:@user_attributes)).to eq( 'browser' => 'firefox', 'id' => 49 ) @@ -117,7 +117,7 @@ project_id: '10431130345', revision: '241', client_name: 'ruby-sdk', - client_version: '3.10.1', + client_version: Optimizely::VERSION, anonymize_ip: true, enrich_decisions: true, visitors: [{ @@ -210,7 +210,7 @@ project_id: '10431130345', revision: '241', client_name: 'ruby-sdk', - client_version: '3.10.1', + client_version: Optimizely::VERSION, anonymize_ip: true, enrich_decisions: true, visitors: [{ @@ -323,7 +323,7 @@ project_id: '10431130345', revision: '241', client_name: 'ruby-sdk', - client_version: '3.10.1', + client_version: Optimizely::VERSION, anonymize_ip: true, enrich_decisions: true, visitors: [{ diff --git a/spec/project_spec.rb b/spec/project_spec.rb index e30d0490..c9232099 100644 --- a/spec/project_spec.rb +++ b/spec/project_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # -# Copyright 2016-2020, Optimizely and contributors +# Copyright 2016-2020, 2022, Optimizely and contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ describe '.initialize' do it 'should take in a custom logger when instantiating Project class' do - class CustomLogger + class CustomLogger # rubocop:disable Lint/ConstantDefinitionInBlock def log(log_message) log_message end @@ -63,7 +63,7 @@ def log(log_message) end it 'should take in a custom error handler when instantiating Project class' do - class CustomErrorHandler + class CustomErrorHandler # rubocop:disable Lint/ConstantDefinitionInBlock def handle_error(error) error end @@ -71,7 +71,7 @@ def handle_error(error) error_handler = CustomErrorHandler.new instance_with_error_handler = Optimizely::Project.new(config_body_JSON, nil, nil, error_handler) - expect(instance_with_error_handler.error_handler.handle_error('test_message')). to eq('test_message') + expect(instance_with_error_handler.error_handler.handle_error('test_message')).to eq('test_message') end it 'should log an error when datafile is null' do @@ -92,21 +92,21 @@ def handle_error(error) it 'should log an error when given an invalid logger' do expect_any_instance_of(Optimizely::SimpleLogger).to receive(:log).once.with(Logger::ERROR, 'Provided logger is in an invalid format.') - class InvalidLogger; end + class InvalidLogger; end # rubocop:disable Lint/ConstantDefinitionInBlock Optimizely::Project.new(config_body_JSON, nil, InvalidLogger.new) end it 'should log an error when given an invalid event_dispatcher' do expect_any_instance_of(Optimizely::SimpleLogger).to receive(:log).once.with(Logger::ERROR, 'Provided event_dispatcher is in an invalid format.') - class InvalidEventDispatcher; end + class InvalidEventDispatcher; end # rubocop:disable Lint/ConstantDefinitionInBlock Optimizely::Project.new(config_body_JSON, InvalidEventDispatcher.new) end it 'should log an error when given an invalid error_handler' do expect_any_instance_of(Optimizely::SimpleLogger).to receive(:log).once.with(Logger::ERROR, 'Provided error_handler is in an invalid format.') - class InvalidErrorHandler; end + class InvalidErrorHandler; end # rubocop:disable Lint/ConstantDefinitionInBlock Optimizely::Project.new(config_body_JSON, nil, nil, InvalidErrorHandler.new) end @@ -881,7 +881,7 @@ def callback(_args); end false, nil, nil, http_project_config_manager, notification_center ) - until http_project_config_manager.ready?; end + sleep 0.1 until http_project_config_manager.ready? expect(http_project_config_manager.config).not_to eq(nil) expect(project_instance.activate('test_experiment', 'test_user')).not_to eq(nil) @@ -907,7 +907,7 @@ def callback(_args); end false, nil, nil, http_project_config_manager, notification_center ) - until http_project_config_manager.ready?; end + sleep 0.1 until http_project_config_manager.ready? expect(http_project_config_manager.config).not_to eq(nil) expect(project_instance.activate('test_experiment', 'test_user')).not_to eq(nil) @@ -940,7 +940,7 @@ def callback(_args); end false, nil, 'valid_sdk_key', nil, notification_center ) - until project_instance.config_manager.ready?; end + sleep 0.1 until project_instance.config_manager.ready? expect(project_instance.is_valid).to be true expect(project_instance.activate('test_experiment', 'test_user')).not_to eq(nil) @@ -1893,7 +1893,7 @@ def callback(_args); end disabled_features = features_keys.map { |x| x[:key] if x[:value] == false }.compact features_keys.each do |feature| - allow(project_instance).to receive(:is_feature_enabled).with(feature[:key], 'test_user', 'browser_type' => 'chrome').and_return(feature[:value]) + allow(project_instance).to receive(:is_feature_enabled).with(feature[:key], 'test_user', {'browser_type' => 'chrome'}).and_return(feature[:value]) end # Checks enabled features are returned @@ -2565,7 +2565,7 @@ def callback(_args); end describe 'when the feature flag is enabled for the user' do describe 'and a variable usage instance is not found' do it 'should return the default variable value' do - Decision = Struct.new(:experiment, :variation, :source) + Decision = Struct.new(:experiment, :variation, :source) # rubocop:disable Lint/ConstantDefinitionInBlock variation_to_return = project_config.rollout_id_map['166661']['experiments'][0]['variations'][0] decision_to_return = Decision.new({'key' => 'test-exp'}, variation_to_return, 'feature-test') allow(project_instance.decision_service).to receive(:get_variation_for_feature).and_return(decision_to_return) @@ -3249,26 +3249,26 @@ def callback(_args); end # setForcedVariation on a paused experiment and then call getVariation. it 'should return null when getVariation is called on a paused experiment after setForcedVariation' do project_instance.set_forced_variation('test_experiment_not_started', 'test_user', 'control_not_started') - expect(project_instance.get_variation('test_experiment_not_started', 'test_user')). to eq(nil) + expect(project_instance.get_variation('test_experiment_not_started', 'test_user')).to eq(nil) end # setForcedVariation on a running experiment and then call getVariation. it 'should return expected variation id when getVariation is called on a running experiment after setForcedVariation' do project_instance.set_forced_variation('test_experiment', 'test_user', 'variation') - expect(project_instance.get_variation('test_experiment', 'test_user')). to eq('variation') + expect(project_instance.get_variation('test_experiment', 'test_user')).to eq('variation') end # setForcedVariation on a whitelisted user on the variation that they are not forced into and then call getVariation on the user. it 'should return expected forced variation id when getVariation is called on a running experiment after setForcedVariation is called on a whitelisted user' do project_instance.set_forced_variation('test_experiment', 'forced_user1', 'variation') - expect(project_instance.get_variation('test_experiment', 'forced_user1')). to eq('variation') + expect(project_instance.get_variation('test_experiment', 'forced_user1')).to eq('variation') end # setForcedVariation on a running experiment with a previously set variation (different from the one set by setForcedVariation) and then call getVariation. it 'should return latest set variation when different variations are set on the same experiment' do project_instance.set_forced_variation('test_experiment', 'test_user', 'control') project_instance.set_forced_variation('test_experiment', 'test_user', 'variation') - expect(project_instance.get_variation('test_experiment', 'test_user')). to eq('variation') + expect(project_instance.get_variation('test_experiment', 'test_user')).to eq('variation') end # setForcedVariation on a running experiment with audience enabled and then call getVariation on that same experiment with invalid attributes. @@ -3285,7 +3285,7 @@ def callback(_args); end # getForceVariation on a running experiment after setforcevariation it 'should return expected variation id when get_forced_variation is called on a running experiment after setForcedVariation' do project_instance.set_forced_variation('test_experiment', 'test_user', 'variation') - expect(project_instance.get_forced_variation('test_experiment', 'test_user')). to eq('variation') + expect(project_instance.get_forced_variation('test_experiment', 'test_user')).to eq('variation') end end @@ -3467,7 +3467,7 @@ def callback(_args); end false, nil, nil, http_project_config_manager ) - until http_project_config_manager.ready?; end + sleep 0.1 until http_project_config_manager.ready? expect(project_instance.activate('test_experiment', 'test_user')).not_to eq(nil) expect(project_instance.is_valid).to be true @@ -3655,7 +3655,7 @@ def callback(_args); end project_id: '111001', revision: '42', client_name: 'ruby-sdk', - client_version: '3.10.1', + client_version: Optimizely::VERSION, anonymize_ip: false, enrich_decisions: true, visitors: [{ @@ -3801,7 +3801,7 @@ def callback(_args); end project_id: '111001', revision: '42', client_name: 'ruby-sdk', - client_version: '3.10.1', + client_version: Optimizely::VERSION, anonymize_ip: false, enrich_decisions: true, visitors: [{