From da85aa4f9c9a676e917198f89299f8e962227168 Mon Sep 17 00:00:00 2001 From: Udalov Igor Date: Mon, 28 Aug 2023 17:10:16 +0300 Subject: [PATCH] [UCMS-7655] Add EntitiesLogDump, FlexibleBoolean, UrlFormatter, CiFormatter; other --- Gemfile | 4 +- Gemfile.lock | 48 +++++--- README.md | 56 +++++++++ lib/unity/config/locales/ru.yml | 8 ++ lib/unity/modules/cli_modeable.rb | 15 +++ lib/unity/modules/entities_log_dump.rb | 81 +++++++++++++ lib/unity/modules/flexible_boolean.rb | 29 +++++ lib/unity/modules/{logable.rb => loggable.rb} | 2 +- lib/unity/utils.rb | 9 +- lib/unity/utils/ci_formatter.rb | 93 +++++++++++++++ lib/unity/utils/retrier.rb | 4 +- lib/unity/utils/url_formatter.rb | 59 +++++++++ lib/unity/utils/url_validator.rb | 112 ++++++++++++++++++ spec/spec_helper.rb | 1 + spec/unity/utils/retrier_spec.rb | 2 + spec/unity/utils/url_validator_spec.rb | 112 ++++++++++++++++++ unity-utils.gemspec | 8 +- 17 files changed, 615 insertions(+), 28 deletions(-) create mode 100644 lib/unity/config/locales/ru.yml create mode 100644 lib/unity/modules/entities_log_dump.rb create mode 100644 lib/unity/modules/flexible_boolean.rb rename lib/unity/modules/{logable.rb => loggable.rb} (96%) create mode 100644 lib/unity/utils/ci_formatter.rb create mode 100644 lib/unity/utils/url_formatter.rb create mode 100644 lib/unity/utils/url_validator.rb create mode 100644 spec/unity/utils/url_validator_spec.rb diff --git a/Gemfile b/Gemfile index e282e96..7341a45 100644 --- a/Gemfile +++ b/Gemfile @@ -5,10 +5,8 @@ source 'https://rubygems.org' # Specify your gem's dependencies in unity-utils.gemspec gemspec -gem 'pry', '~> 3.0' +gem 'pry', '>= 0.14.2' gem 'rspec', '~> 3.0' gem 'rubocop', '~> 1.21.0' - -gem 'ruby-progressbar', '~> 1.11.0' diff --git a/Gemfile.lock b/Gemfile.lock index 8194d31..257f222 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,25 +1,37 @@ PATH remote: . specs: - unity-utils (0.1.0) - ruby-progressbar + unity-utils (0.1.2) + activesupport (>= 4.2) + ruby-progressbar (>= 1.11.0) GEM remote: https://rubygems.org/ specs: + activesupport (7.0.7.2) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) ast (2.4.2) coderay (1.1.3) + concurrent-ruby (1.2.2) diff-lcs (1.4.4) + i18n (1.14.1) + concurrent-ruby (~> 1.0) method_source (1.0.0) - parallel (1.20.1) - parser (3.0.2.0) + minitest (5.19.0) + parallel (1.23.0) + parser (3.2.2.3) ast (~> 2.4.1) - pry (0.14.0) + racc + pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) - rainbow (3.0.0) - regexp_parser (2.1.1) - rexml (3.2.5) + racc (1.7.1) + rainbow (3.1.1) + regexp_parser (2.8.1) + rexml (3.2.6) rspec (3.10.0) rspec-core (~> 3.10.0) rspec-expectations (~> 3.10.0) @@ -33,7 +45,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.2) - rubocop (1.20.0) + rubocop (1.21.0) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) @@ -42,20 +54,22 @@ GEM rubocop-ast (>= 1.9.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.11.0) - parser (>= 3.0.1.1) + rubocop-ast (1.29.0) + parser (>= 3.2.1.0) ruby-progressbar (1.11.0) - unicode-display_width (2.0.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.4.2) PLATFORMS x86_64-darwin-19 + x86_64-darwin-21 DEPENDENCIES - pry - rspec - rubocop - ruby-progressbar + pry (>= 0.14.2) + rspec (~> 3.0) + rubocop (~> 1.21.0) unity-utils! BUNDLED WITH - 2.2.12 + 2.4.19 diff --git a/README.md b/README.md index 34c70ce..f34d385 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,24 @@ Gem with utilities used by Unity team # Documentation - ### Utils: + - **CiFormatter** - Custom rspec formatter: collapses pending tests and adds some time analytics. + + ``` + rspec --formatter CiFormatter + ...bash + Rubric publishing using Muppet::PublishJob + updating rubric + enqueues Muppet::PublishJob | Duration: 0.03981s + ... + Groups: + 7.20884s ./spec/services/topics_views_count/update_spec.rb (right after midnight) + 5.45071s ./spec/commands/ugc/approve_topic_spec.rb (approve logic) + ... + Single examples: + 5.45071s ./spec/commands/ugc/approve_topic_spec.rb (approve logic / creates a topic) + 4.91609s ./spec/commands/topic/update/reviews_of_published_spec.rb (has correct state value / is expected to be need approval) + ``` + - **Retrier** - Restarting passed block. ```ruby Unity::Utils::Retrier.call { 5 / 0 } @@ -20,6 +38,20 @@ Gem with utilities used by Unity team pool.run! ``` + - **UrlFormatter** - Formats url. + ```ruby + formatter = ::Unity::Utils::UrlFormatter.new('blog/post/nykotyn-bez-syharet-hde-on-soderzytsia/') + formatter.trailing_slash(enabled: false).build #=> "blog/post/nykotyn-bez-syharet-hde-on-soderzytsia" + formatter.params({'erid' => 'test'}).build #=> "blog/post/nykotyn-bez-syharet-hde-on-soderzytsia?erid=test" + ``` + + - **UrlValidator** - Validates url. + ```ruby + response = UrlValidator.new('ftp://motor.ru/reports/videoparis.htm').call + response.valid? #=> false + response.errors #=> ['Ссылка должна начинаться с http(s)'] + ``` + - ### Modules: - **CliModeable** - Wrapper over the ruby-progressbar. ```ruby @@ -35,6 +67,30 @@ Gem with utilities used by Unity team end ``` + - **EntitiesLogDump** - Module for service objects do dump entities. + ```ruby + dump_entities(Topic.first, attributes: [:id, :headline]) # => [{ "id" => 1, "headline" => "foo" }, { "id" => 2, "headline" => "bar" }] + dump_entities(Topic.first(2), attributes: [:id, :headline]) #=> { "id" => 1, "headline" => "foo" } + ``` + Will work both with ApplicationRecord or Mongoid::Document models. + + - **FlexibleBoolean** - Concern to make easier work with virtual boolean attributes. + ```ruby + class MyKlass + include IsReactable + + flexible_boolean :is_reactable + + attr_reader :is_reactable + + def intiailize(is_reactable) + @is_reactable = is_reactable + end + end + MyKlass.new(true).is_reactable # => true + MyKlass.new(nil).is_reactable # => false + ``` + - **Loggable** - Wrapper over logger. ```ruby @log_file = 'some_file.log' diff --git a/lib/unity/config/locales/ru.yml b/lib/unity/config/locales/ru.yml new file mode 100644 index 0000000..6694364 --- /dev/null +++ b/lib/unity/config/locales/ru.yml @@ -0,0 +1,8 @@ +--- +ru: + unity_utils: + errors: + blank_url: Ссылка не может быть пустой + invalid_url_format: Неправильный формат ссылки + blank_host: Хост не может быть пустым + invalid_protocol: Ссылка должна начинаться с http(s) diff --git a/lib/unity/modules/cli_modeable.rb b/lib/unity/modules/cli_modeable.rb index c762c4c..e537023 100644 --- a/lib/unity/modules/cli_modeable.rb +++ b/lib/unity/modules/cli_modeable.rb @@ -7,10 +7,14 @@ module Modules module CliModeable private + # @return [Boolean] def cli_mode? @cli_mode == true end + # @param amount [Fixnum] + # @param title [String] + # @return [void] def init_progressbar(amount, title = self.class.to_s) return unless cli_mode? @@ -18,9 +22,20 @@ def init_progressbar(amount, title = self.class.to_s) @progressbar.total = amount end + # Increment value of progressbar + # @return [void] def incr_progressbar @progressbar.increment if cli_mode? end + + # @param amount [Fixnum] + # @return [void] + def add_to_progress(amount = 0) + return unless cli_mode? + + new_progress = @progressbar.progress + amount + @progressbar.progress = new_progress if new_progress <= @progressbar.total + end end end end diff --git a/lib/unity/modules/entities_log_dump.rb b/lib/unity/modules/entities_log_dump.rb new file mode 100644 index 0000000..1543d99 --- /dev/null +++ b/lib/unity/modules/entities_log_dump.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require 'active_support/concern' + +module Unity + module Modules + # You could use this module for service objects + # class YourServiceObject + # include Helpers::EntitiesLogDump + # + # You can get array of logged entities by custom attributes like that: + # def topic_query + # @topic_query ||= Topic.first(2) + # end + # + # def foo + # dump_entities(topic_query, attributes: [:id, :headline]) + # end + # Result: [{ "id" => 1, "headline" => "foo" }, { "id" => 2, "headline" => "bar" }] + # + module EntitiesLogDump + extend ::ActiveSupport::Concern + + # @param entities [ActiveRecord::Relation,Array] + # @param [Hash] options + # @option options [Array] :attributes + # @option options [Integer] :batch_size + # @return [Array] + def dump_entities(entities, options = {}) + attributes = options[:attributes] || all_attributes(entities.first) + batch_size = options[:batch_size] || 100 + + dump_entities = [] + entities.each_slice(batch_size) do |sliced_entities| + sliced_entities.each do |entity| + dump_entities.append(dump_entity(entity, attributes: attributes)) + end + end + + dump_entities + end + + # @param entity [ApplicationRecord,Mongoid::Document] + # @param [Hash] options + # @option options [Array] :attributes + # @return [Hash] + def dump_entity(entity, options = {}) + attributes = options[:attributes] || all_attributes(entity) + attributes.map { |a| { a => try_array(entity.send(a)) } }.inject(:merge).deep_stringify_keys + end + + # @param entity [ApplicationRecord,Mongoid::Document] + # @return [Array] + def all_attributes(entity) + return entity.class.attribute_names if object_is_a?(entity, 'ApplicationRecord') + return entity.fields.keys if object_is_a?(entity, 'Mongoid::Document') + + raise ArgumentError, "Can't determine full attributes list for this kind of entity." + end + + private + + # @item [Object] + # @return + def try_array(item) + return item.map { |i| i.try(:attributes) || i } if item.is_a?(Array) + return item.map(&:attributes) if item.try(:any?) { |obj| object_is_a?(obj, 'ActiveRecord::Base') } + + item + end + + # @param obj [Object] + # @klass_name [String] + # @return [Boolean] + def object_is_a?(obj, klass_name) + ancestors = obj.class.ancestors.map(&:to_s) + ancestors.include?(klass_name) + end + end + end +end diff --git a/lib/unity/modules/flexible_boolean.rb b/lib/unity/modules/flexible_boolean.rb new file mode 100644 index 0000000..646a603 --- /dev/null +++ b/lib/unity/modules/flexible_boolean.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'active_support/concern' + +module Unity + module Modules + # Concern to make easier work with virtual boolean attributes. + # Allows always store a boolean value in a pseudo attribute + # (pass string or boolean, attribute will boolean value type always). + # + # Overrides target attribute setter method by adding a + # comparison of the boolean attribute value with the 'true' string. + # + # Usage example: + # flexible_boolean :is_reactable + # + module FlexibleBoolean + extend ::ActiveSupport::Concern + + class_methods do + # @param attribute [Symbol, String] + # @return [Symbol] defined method name + def flexible_boolean(attribute) + define_method(:"#{attribute}=") { |v| super(v.to_s == 'true') } + end + end + end + end +end diff --git a/lib/unity/modules/logable.rb b/lib/unity/modules/loggable.rb similarity index 96% rename from lib/unity/modules/logable.rb rename to lib/unity/modules/loggable.rb index 6d60408..b972dc1 100644 --- a/lib/unity/modules/logable.rb +++ b/lib/unity/modules/loggable.rb @@ -4,7 +4,7 @@ module Unity module Modules - module Logable + module Loggable attr_reader :log_file, :logger def clean_logfile diff --git a/lib/unity/utils.rb b/lib/unity/utils.rb index d4f15bf..95b3298 100644 --- a/lib/unity/utils.rb +++ b/lib/unity/utils.rb @@ -1,12 +1,19 @@ # frozen_string_literal: true # Modules -require_relative 'modules/logable' require_relative 'modules/cli_modeable' +require_relative 'modules/entities_log_dump' +require_relative 'modules/flexible_boolean' +require_relative 'modules/loggable' # Utils +require_relative 'utils/ci_formatter' require_relative 'utils/retrier' require_relative 'utils/thread_pool' +require_relative 'utils/url_formatter' +require_relative 'utils/url_validator' + +I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'config/locales/*.yml')] module Unity module Utils diff --git a/lib/unity/utils/ci_formatter.rb b/lib/unity/utils/ci_formatter.rb new file mode 100644 index 0000000..460aa03 --- /dev/null +++ b/lib/unity/utils/ci_formatter.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'rspec/core' +require 'rspec/core/formatters/console_codes' + +RSpec::Support.require_rspec_core 'formatters/documentation_formatter' + +class CiFormatter < RSpec::Core::Formatters::DocumentationFormatter + RSpec::Core::Formatters.register self + + DURATION_LONG_FORMAT = '%-80s | Duration: %7.5fs' + DURATION_SHORT_FORMAT = '%.5fs' + SHOW_TOP = 20 + + def initialize(*args) + super + @example_times = [] + end + + def example_started(*_args) + @time = Time.zone.now + end + + def example_passed(notification) + super + + @example_times << [ + notification.example.file_path, + notification.example.example_group.description, + notification.example.description, + Time.zone.now - @time + ] + end + + def failure_output(example) + RSpec::Core::Formatters::ConsoleCodes.wrap( + format(DURATION_LONG_FORMAT, + "#{current_indentation}#{example.description.strip} (FAILED - #{next_failure_index})", + example.execution_result[:run_time]), + :failure + ) + end + + def passed_output(example) + RSpec::Core::Formatters::ConsoleCodes.wrap( + format(DURATION_LONG_FORMAT, + "#{current_indentation}#{example.description.strip}", + Time.zone.now - @time), + :success + ) + end + + def dump_summary(summary_notification) + dump_group_times + dump_example_times + + @output.flush + + super + end + + def example_pending(_); end + + def dump_pending(_); end + + private + + def dump_example_times + @output.puts "\n\nSingle examples:\n" + + sorted_by_time(@example_times)[0..SHOW_TOP].each do |file_path, group, example, time| + @output.puts "#{format(DURATION_SHORT_FORMAT, time)} #{file_path} (#{group} / #{example})" + end + end + + def dump_group_times + @output.puts "\n\nGroups:\n" + + group_times = Hash.new(0) + @example_times.each do |file_path, group, _example, time| + group_times["#{file_path} (#{group})"] += time + end + + sorted_by_time(group_times)[0..SHOW_TOP].each do |group, time| + @output.puts "#{format(DURATION_SHORT_FORMAT, time)} #{group}" + end + end + + # sorted by last ascending + def sorted_by_time(times) + times.to_a.sort_by(&:last).reverse + end +end diff --git a/lib/unity/utils/retrier.rb b/lib/unity/utils/retrier.rb index a0cccbb..084ce50 100644 --- a/lib/unity/utils/retrier.rb +++ b/lib/unity/utils/retrier.rb @@ -5,8 +5,8 @@ module Utils class Retrier attr_reader :errors, :max_retries, :sleep_factor - def self.call - new.call(&Proc.new) if block_given? + def self.call(&block) + new.call(&block) if block_given? end def initialize(errors = nil, max_retries = 5, sleep_factor = 0.3) diff --git a/lib/unity/utils/url_formatter.rb b/lib/unity/utils/url_formatter.rb new file mode 100644 index 0000000..f5a9b69 --- /dev/null +++ b/lib/unity/utils/url_formatter.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module Unity + module Utils + # Class for formatting link. + # + # Usage: + # ::Unity::Utils::UrlFormatter.new('blog/post/nykotyn-bez-syharet-hde-on-soderzytsia').params({'erid' => 'test'}).build + class UrlFormatter + # @param link [String] + def initialize(link) + @link = link + end + + # @param params [Hash, Array] + # @return [Self] + def params(params) + @query = build_query(params) + + self + end + + # @param enabled [Boolean] + # @return [Self] + def trailing_slash(enabled: true) + @trailing_slash = enabled + + self + end + + # @return [String] + def build + uri.query = @query if @query + + uri.to_s + end + + private + + # @return [URI::Generic] + def uri + @uri ||= begin + link = @link + link = @link.chomp('/') unless @trailing_slash + + URI.parse(link) + end + end + + def build_query(params) + new_query = URI.decode_www_form(uri.query || '') + + params.each { |k, v| new_query << [k, v] } + + URI.encode_www_form(new_query) + end + end + end +end diff --git a/lib/unity/utils/url_validator.rb b/lib/unity/utils/url_validator.rb new file mode 100644 index 0000000..1603071 --- /dev/null +++ b/lib/unity/utils/url_validator.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'uri' +require 'active_support' +require 'active_support/core_ext' + +module Unity + module Utils + class UrlValidator + # @!method url + # @return [String] + # @!method validation_errors + # @return [Array] + # @!method response + # @return [Response] + attr_reader :url, :validation_errors, :response + + DEFAULT_LOCALE_PREFIX = 'unity_utils.errors' + VALID_PROTOCOLS = [::URI::HTTP, ::URI::HTTPS].freeze + VALIDATION_METHODS = %i[blank_url! parse_uri! blank_host! valid_protocol!].freeze + + # @param url [String] + # @param custom_validation_errors [Hash{Symbol->String}] + def initialize(url, custom_validation_errors = {}) + @url = url # krang or site url + @validation_errors = default_validation_errors.merge(custom_validation_errors).with_indifferent_access + @response = Response.new + end + + # @return [Response] + def call + VALIDATION_METHODS.each do |method| + return response if response.invalid? + + send(method) + end + response + end + + private + + # @return [Hash{Symbol->Sting}] + def default_validation_errors + { + blank_url!: "#{DEFAULT_LOCALE_PREFIX}.blank_url", + parse_uri!: "#{DEFAULT_LOCALE_PREFIX}.invalid_url_format", + blank_host!: "#{DEFAULT_LOCALE_PREFIX}.blank_host", + valid_protocol!: "#{DEFAULT_LOCALE_PREFIX}.invalid_protocol" + } + end + + # @param method_name [Symbol] + # @return [NilClass] + def add_error(method_name) + error_message = I18n.t(validation_errors[method_name]) + + response.add_error(error_message) + end + + # @return [NilClass, Array] + def blank_url! + add_error(__method__) if url.blank? + end + + # @return [URI::HTTPS, Array] + def parse_uri! + @uri = ::URI.parse(url) + rescue ::URI::InvalidURIError + add_error(__method__) + end + + # @return [NilClass, Array] + def blank_host! + add_error(__method__) if @uri.host.nil? + end + + # @return [NilClass, Array] + def valid_protocol! + return unless VALID_PROTOCOLS.none? { |protocol| @uri.is_a?(protocol) } + + add_error(__method__) + end + + class Response + def initialize + @errors = [] + end + + # @return [Boolean] + def valid? + errors.empty? + end + + # @return [Boolean] + def invalid? + !valid? + end + + # @return [Array] + def errors + @errors.compact + end + + # @param error [String] + # @return [Array] + def add_error(error) + @errors << error + end + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 576f68d..984197e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true Dir['./lib/unity/**/**.rb'].sort.each { |file_path| require(file_path) } +Dir['./spec/support/**/**.rb'].sort.each { |file_path| require(file_path) } RSpec.configure do |config| config.disable_monkey_patching! diff --git a/spec/unity/utils/retrier_spec.rb b/spec/unity/utils/retrier_spec.rb index 68932d5..c9ffe2d 100644 --- a/spec/unity/utils/retrier_spec.rb +++ b/spec/unity/utils/retrier_spec.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'spec_helper' + RSpec.describe Unity::Utils::Retrier do let(:errors) { nil } let(:action) { 5 + 5 } diff --git a/spec/unity/utils/url_validator_spec.rb b/spec/unity/utils/url_validator_spec.rb new file mode 100644 index 0000000..6560e1f --- /dev/null +++ b/spec/unity/utils/url_validator_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Unity::Utils::UrlValidator, type: :validator do + subject { described_class.new(url).call } + + let(:locale_prefix) { 'unity_utils.errors' } + + before { I18n.locale = :ru } + + context 'with valid url' do + context 'with site url' do + let(:url) { 'https://motor.ru/reports/videoparis.htm' } + + it 'returns true and empty errors array' do + expect(subject).to be_valid + expect(subject.errors).to be_empty + end + end + + context 'with krang url' do + let(:topic_id) { 1 } + let(:url) { "http://localhost:8090/#/topic/#{topic_id}?_k=7mwqub" } + + it 'returns true and empty errors array' do + expect(subject).to be_valid + expect(subject.errors).to be_empty + end + end + end + + context 'without top level domain' do + subject { described_class.new(url).call } + + let(:url) { 'http://rambler' } + + it 'returns true and empty errors array' do + expect(subject).to be_valid + expect(subject.errors).to be_empty + end + end + + context 'with invalid url' do + context 'with blank url' do + let(:error_message) { I18n.t!("#{locale_prefix}.blank_url") } + + context 'with nil url' do + let(:url) { nil } + + it 'returns false and blank error' do + expect(subject).to be_invalid + expect(subject.errors).to eq([error_message]) + end + end + + context 'with empty string url' do + let(:url) { '' } + + it 'returns false and blank error' do + expect(subject).to be_invalid + expect(subject.errors).to eq([error_message]) + end + end + end + + context 'with invalid protocol' do + let(:url) { 'ftp://motor.ru/reports/videoparis.htm' } + + it 'returns false and protocol error' do + expect(subject).to be_invalid + expect(subject.errors).to eq([I18n.t!("#{locale_prefix}.invalid_protocol")]) + end + end + + context 'with blank host' do + let(:url) { 'secretmag.ru' } + + it 'returns false and host error' do + expect(subject).to be_invalid + expect(subject.errors).to eq([I18n.t!("#{locale_prefix}.blank_host")]) + end + end + + context 'with custom validation errors' do + subject { described_class.new(url, custom_errors).call } + + let(:url) { 'http://s1.shredder.unity.rambler tech' } + let(:locale) { 'errors.price_ru.bad_response' } + let(:custom_errors) { { parse_uri!: locale } } + let(:error_message) { 'Error message' } + + before { allow(I18n).to receive(:t).with(locale).and_return(error_message) } + + it 'returns false and custom format error' do + expect(subject).to be_invalid + expect(subject.errors).to eq([error_message]) + end + end + + context 'with two fragments' do + subject { described_class.new(url).call } + + let(:url) { 'http://rambler.ru#first#second' } + + it 'returns false and format error' do + expect(subject).to be_invalid + expect(subject.errors).to eq([I18n.t!("#{locale_prefix}.invalid_url_format")]) + end + end + end +end diff --git a/unity-utils.gemspec b/unity-utils.gemspec index 5612315..8fc5f0c 100644 --- a/unity-utils.gemspec +++ b/unity-utils.gemspec @@ -1,10 +1,8 @@ # frozen_string_literal: true -require_relative 'lib/unity-utils' - Gem::Specification.new do |spec| spec.name = 'unity-utils' - spec.version = '0.1.1' + spec.version = '0.1.2' spec.authors = ['Mikhail Georgievskiy'] spec.email = ['m.georgievskiy@rambler-co.ru'] @@ -19,5 +17,7 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'ruby-progressbar', '~> 1.11.0' + spec.add_dependency 'activesupport', '>= 4.2' + spec.add_dependency 'rspec-core', '>= 3.9' + spec.add_dependency 'ruby-progressbar', '>= 1.11.0' end