c80_shared 0.1.67 → 0.1.68
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 +4 -4
- data/app/getboat/schemas/base.rb +67 -0
- data/app/getboat/schemas/base_boat_schema.rb +233 -0
- data/app/getboat/schemas/central_agent/save_boat_schema.rb +55 -0
- data/app/getboat/schemas/site/lease/create_personal_inquiry_schema.rb +187 -0
- data/app/repositories/accounts/central_agent_account_repository.rb +46 -0
- data/app/repositories/accounts/client_account_repository.rb +43 -0
- data/app/services/abstract_prices_service.rb +70 -0
- data/app/services/boats/boat_prices_save_service.rb +79 -0
- data/app/services/boats/boat_sale_prices_save_service.rb +58 -0
- data/app/services/central_agent/save_boat_service.rb +117 -0
- data/app/services/dimension_service.rb +42 -0
- data/app/services/lease/create_broadcast_inquiry_service.rb +68 -0
- data/config/initializers/core_ext/string.rb +62 -0
- data/lib/c80_shared/version.rb +1 -1
- data/lib/c80_shared.rb +27 -0
- data/lib/dry/errors.rb +77 -0
- data/lib/dry/rule.rb +69 -0
- data/lib/dry/rules/and.rb +11 -0
- data/lib/dry/rules/between.rb +18 -0
- data/lib/dry/rules/binary.rb +16 -0
- data/lib/dry/rules/collection.rb +16 -0
- data/lib/dry/rules/composite.rb +19 -0
- data/lib/dry/rules/equal.rb +18 -0
- data/lib/dry/rules/format.rb +18 -0
- data/lib/dry/rules/greater_than.rb +18 -0
- data/lib/dry/rules/greater_than_or_equal.rb +18 -0
- data/lib/dry/rules/included.rb +18 -0
- data/lib/dry/rules/length_between.rb +18 -0
- data/lib/dry/rules/length_equal.rb +18 -0
- data/lib/dry/rules/less_than.rb +18 -0
- data/lib/dry/rules/less_than_or_equal.rb +18 -0
- data/lib/dry/rules/max_length.rb +18 -0
- data/lib/dry/rules/min_length.rb +18 -0
- data/lib/dry/rules/not_equal.rb +18 -0
- data/lib/dry/rules/or.rb +13 -0
- data/lib/dry/rules/present.rb +21 -0
- data/lib/dry/rules/then.rb +13 -0
- data/lib/dry/rules_factory.rb +118 -0
- data/lib/dry/schema.rb +148 -0
- metadata +39 -2
@@ -0,0 +1,70 @@
|
|
1
|
+
class AbstractPricesService
|
2
|
+
|
3
|
+
attr_reader :built_prices
|
4
|
+
|
5
|
+
def initialize(all_currencies = ::Dicts::Currency::ALL2) # all_currencies - список валют, в которых надо сохранить цены
|
6
|
+
@all_currencies = all_currencies
|
7
|
+
_reset_ivars
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def _model
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def _fields_for_create
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
def _save(prices) # помещаем в базу всё одним запросом
|
21
|
+
filtered = prices.select { |el| !el[:_delete] && el[:value].to_i != 0 } # игнорируя цены, которые были помечены "к удалению" или те, у которых значение 0 (0 можно подавать, если хотим "удалить" цену продажи)
|
22
|
+
return if filtered.size.zero?
|
23
|
+
|
24
|
+
values = filtered.map do |p|
|
25
|
+
_fields_for_create.map do |field|
|
26
|
+
p[field.to_sym]
|
27
|
+
end.compact * ','
|
28
|
+
end * '),('
|
29
|
+
values = '(%s)' % values
|
30
|
+
sql = 'INSERT INTO %s(%s) VALUES %s' % [_model.table_name, _fields_for_create * ',', values]
|
31
|
+
ActiveRecord::Base.connection.execute sql
|
32
|
+
|
33
|
+
filtered
|
34
|
+
end
|
35
|
+
|
36
|
+
# из базы удаляем все записи, принадлежащие данной лодке
|
37
|
+
# т.к. считаем, что с формы приходит полная картина по ценам
|
38
|
+
# кроме того, юзер может просто изменить, например, период в уже
|
39
|
+
# существующей строке с ценами, которую он видит на экране
|
40
|
+
def _delete_existing_records(boat_id)
|
41
|
+
sql = 'DELETE FROM %s WHERE boat_id = %s' % [_model.table_name, boat_id]
|
42
|
+
ActiveRecord::Base.connection.execute sql
|
43
|
+
end
|
44
|
+
|
45
|
+
def _build_other(price) # отталкиваясь от валюты входных данных, рассчитываем "те же" цены в других валютах
|
46
|
+
return if price[:_delete] || price[:value].to_i == 0 # игнорируя цены, которые были помечены "к удалению" или те, у которых значение 0 (0 можно подавать, если хотим "удалить" цену продажи)
|
47
|
+
|
48
|
+
incoming_curr = ::Dicts::Currency.find(price[:currency_id])
|
49
|
+
currs_to_build = @all_currencies - [incoming_curr]
|
50
|
+
rates = Currency.fresh(incoming_curr.index).rates
|
51
|
+
rates = JSON.parse(rates)['rates']
|
52
|
+
|
53
|
+
currs_to_build.each do |curr|
|
54
|
+
rate = rates[curr.index]
|
55
|
+
res = price.clone
|
56
|
+
res[:currency_id] = curr.id
|
57
|
+
res[:value] = res[:value].to_f * rate
|
58
|
+
res[:is_orig] = false
|
59
|
+
@built_prices << res
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def _delete_bad_values(prices)
|
64
|
+
prices.delete_if { |price| price[:value].nil? || price[:value].to_f.zero? }
|
65
|
+
end
|
66
|
+
|
67
|
+
def _reset_ivars
|
68
|
+
@built_prices = []
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Boats
|
2
|
+
#
|
3
|
+
# spec/services/boat/boat_prices_save_service_spec.rb
|
4
|
+
#
|
5
|
+
class BoatPricesSaveService < ::AbstractPricesService
|
6
|
+
|
7
|
+
attr_reader :is_for_rent
|
8
|
+
|
9
|
+
def self.perform(boat, params)
|
10
|
+
new.perform(boat, params)
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform(boat, prices = nil)
|
14
|
+
_reset_ivars
|
15
|
+
return false if prices.nil?
|
16
|
+
|
17
|
+
boat_prices_params = prices.deep_dup # Rails deep_dup https://apidock.com/rails/Hash/deep_dup
|
18
|
+
boat_prices_params = boat_prices_params.map { |bp| bp.symbolize_keys }
|
19
|
+
filtered = _delete_bad_values boat_prices_params
|
20
|
+
return false if filtered.size.zero? # взаимодействия с базой не будет, если все цены с "плохими" значениями
|
21
|
+
|
22
|
+
filtered.each do |bp| # bp = {uom_id, season_id, currency_id, duration, value, discount}
|
23
|
+
bp[:boat_id] = boat.id
|
24
|
+
bp[:is_orig] = true
|
25
|
+
bp[:value] = bp[:value].is_a?(String) ? bp[:value].split(/[, ]/).join : bp[:value] # с формы может прийти строка, сгруппированная по разрадям, вида "100,100,222"
|
26
|
+
bp[:duration] = format('%.1f', bp[:duration])
|
27
|
+
bp[:discount] = bp[:discount].to_f # с формы может прийти пустая строка ''
|
28
|
+
bp[:created_at] = '\'%s\'' % Time.now.to_s(:db)
|
29
|
+
_build_other bp
|
30
|
+
end
|
31
|
+
|
32
|
+
filtered2 = _del_duplicates(filtered + @built_prices)
|
33
|
+
return false if filtered2.size.zero?
|
34
|
+
|
35
|
+
saved = nil
|
36
|
+
ActiveRecord::Base.transaction do
|
37
|
+
_delete_existing_records boat.id
|
38
|
+
saved = _save filtered2
|
39
|
+
end
|
40
|
+
|
41
|
+
@is_for_rent = _detect_for_rent saved
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def _model
|
48
|
+
BoatPrice
|
49
|
+
end
|
50
|
+
|
51
|
+
def _fields_for_create
|
52
|
+
@fields_for_create ||= %w'boat_id uom_id duration season_id is_orig discount currency_id value created_at'
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def _delete_bad_values(prices)
|
57
|
+
prices.delete_if do |price|
|
58
|
+
price[:value].nil? || price[:value].to_f.zero? || format('%.1f', price[:duration].to_f) == '0.0'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def _del_duplicates(params) # подстраховываемся, вдруг форма прислала что-то не то
|
63
|
+
grouped = params.group_by { |row| [row[:duration].to_f, row[:uom_id], row[:season_id], row[:currency_id]] } # убираем из params массива дубликаты согласно уникальному индексу в db
|
64
|
+
grouped.map {|_, v| v.first }
|
65
|
+
end
|
66
|
+
|
67
|
+
def _reset_ivars
|
68
|
+
@is_for_rent = nil
|
69
|
+
super
|
70
|
+
end
|
71
|
+
|
72
|
+
def _detect_for_rent(saved_prices)
|
73
|
+
return 0 if saved_prices.nil?
|
74
|
+
# noinspection RubySimplifyBooleanInspection
|
75
|
+
!!saved_prices.select { |bp| !bp[:_delete] }.size.nonzero? ? 1 : 0
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Boats
|
2
|
+
#
|
3
|
+
# [spec/services/boat/boat_sale_price_save_service_spec.rb]
|
4
|
+
#
|
5
|
+
class BoatSalePricesSaveService < ::AbstractPricesService
|
6
|
+
|
7
|
+
attr_reader :is_for_sale
|
8
|
+
|
9
|
+
def self.perform(boat_id, price)
|
10
|
+
new.perform boat_id, price
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform(boat_id, price = nil)
|
14
|
+
_reset_ivars
|
15
|
+
return false if price.nil?
|
16
|
+
|
17
|
+
sp = price.dup
|
18
|
+
sp[:boat_id] = boat_id
|
19
|
+
sp[:is_orig] = true
|
20
|
+
sp[:value] = sp[:value].is_a?(String) ? sp[:value].split(/[, ]/).join : sp[:value].to_i # с формы может прийти строка, сгруппированная по разрадям, вида "100,100,222" + тут же превращаем nil в 0
|
21
|
+
sp[:discount] = sp[:discount].present? ? sp[:discount] : 0
|
22
|
+
sp[:created_at] = '\'%s\'' % Time.now.to_s(:db)
|
23
|
+
_build_other sp
|
24
|
+
|
25
|
+
filtered2 = @built_prices + [sp]
|
26
|
+
saved = nil
|
27
|
+
ActiveRecord::Base.transaction do
|
28
|
+
_delete_existing_records boat_id
|
29
|
+
saved = _save filtered2
|
30
|
+
end
|
31
|
+
|
32
|
+
@is_for_sale = _detect_for_sale saved
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def _model
|
39
|
+
BoatSalePrice
|
40
|
+
end
|
41
|
+
|
42
|
+
def _fields_for_create
|
43
|
+
@fields_for_create ||= %w'boat_id currency_id value discount is_orig created_at'
|
44
|
+
end
|
45
|
+
|
46
|
+
def _reset_ivars
|
47
|
+
@is_for_sale = nil
|
48
|
+
super
|
49
|
+
end
|
50
|
+
|
51
|
+
def _detect_for_sale(saved_prices)
|
52
|
+
return 0 if saved_prices.nil?
|
53
|
+
# noinspection RubySimplifyBooleanInspection
|
54
|
+
!!saved_prices.select { |bp| bp[:value].to_i != 0 }.size.nonzero? ? 1 : 0
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module CentralAgent
|
2
|
+
class SaveBoatService
|
3
|
+
|
4
|
+
attr_reader :errors, :boat
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
_reset_ivars
|
8
|
+
end
|
9
|
+
|
10
|
+
# @param [Hash] boat_params
|
11
|
+
# @param [Hash] cookies example: {... "currency"=>"USD", "dimension"=>"ft", "volume"=>"liters", "business"=>"rent", "language"=>"en"}
|
12
|
+
#
|
13
|
+
def perform(boat_params, cookies, current_user, state: Boat::STATE_PREMODERATED)
|
14
|
+
_reset_ivars
|
15
|
+
|
16
|
+
@schema = ::Schemas::CentralAgent::SaveBoatSchema.new boat_params.dup # проверяем параметры, пришедшие с формы
|
17
|
+
unless @schema.valid?
|
18
|
+
@errors = @schema.errors.messages
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
|
22
|
+
if boat_params[:id].present?
|
23
|
+
boat = _update_boat(@schema.attributes, cookies)
|
24
|
+
else
|
25
|
+
boat = _create_boat(@schema.attributes, cookies, current_user, state)
|
26
|
+
end
|
27
|
+
|
28
|
+
@boat = boat
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def _create_boat(boat_params, cookies, current_user, state) # TODO:: проверить и доработать при необходимости (ref /home/scout/git/get-the-boat/app/services/boat_register_service.rb)
|
35
|
+
prms = _fuck_params boat_params
|
36
|
+
|
37
|
+
boat = Boat.new
|
38
|
+
boat.assign_attributes prms
|
39
|
+
boat.state = state
|
40
|
+
|
41
|
+
_handle_boat_attributes(boat, cookies)
|
42
|
+
|
43
|
+
ActiveRecord::Base.transaction do
|
44
|
+
boat.users << current_user
|
45
|
+
|
46
|
+
boat.send :set_slug
|
47
|
+
boat.save validate: false
|
48
|
+
boat.update_attribute(:boat_photo_id, boat.boat_photos.first.id) # TODO:: реализовать назначение главного фото (пока же на скорую руку делаем первую фотку главной)
|
49
|
+
_save_prices boat
|
50
|
+
end
|
51
|
+
|
52
|
+
boat
|
53
|
+
end
|
54
|
+
|
55
|
+
def _update_boat(boat_params, cookies) # TODO:: реализовать назначение главного фото (в админке, например, приходит такое: "boat"=>{"boat_photo_id"=>"41434"})
|
56
|
+
# _fuck_params - подгоняем под active_record (перегоняем массив в хэш)
|
57
|
+
#boat_locs2 = boat_params[:boat_locations_attributes].compact.map { |attr| [attr[:id], attr] }.to_h
|
58
|
+
#prms = boat_params.except(:rent_prices, :sale_price, :boat_locations_attributes) # с формы из кабинета, в отличии от админки, приходит длина в виде "boat_length_metrics_ft" => "12.0"
|
59
|
+
#prms[:boat_locations_attributes] = boat_locs2
|
60
|
+
prms = _fuck_params boat_params
|
61
|
+
|
62
|
+
boat = Boat.find boat_params[:id]
|
63
|
+
boat.assign_attributes prms
|
64
|
+
|
65
|
+
_handle_boat_attributes(boat, cookies)
|
66
|
+
|
67
|
+
ActiveRecord::Base.transaction do
|
68
|
+
boat.send :set_slug
|
69
|
+
boat.save validate: false
|
70
|
+
_save_prices boat
|
71
|
+
boat.cancel if boat.state == ::Boat::STATE_APPROVED.to_s # кто знает, что там наизменял агент в своей лодке, вдруг фотки с телефонами загрузил?
|
72
|
+
end
|
73
|
+
|
74
|
+
boat
|
75
|
+
end
|
76
|
+
|
77
|
+
# boat_params - это атрибуты схемы
|
78
|
+
def _fuck_params(boat_params)
|
79
|
+
# подгоняем под active_record (перегоняем массив в хэш)
|
80
|
+
boat_locs2 = boat_params[:boat_locations_attributes].compact.map { |attr| [(attr[:id] || -(rand*1000).to_i), attr] }.to_h
|
81
|
+
prms = boat_params.except(:rent_prices, :sale_price, :boat_locations_attributes, :main_boat_photo_id) # с формы из кабинета, в отличии от админки, приходит длина в виде "boat_length_metrics_ft" => "12.0"
|
82
|
+
prms[:boat_photo_id] = boat_params[:main_boat_photo_id]
|
83
|
+
prms[:boat_locations_attributes] = boat_locs2
|
84
|
+
prms
|
85
|
+
end
|
86
|
+
|
87
|
+
def _handle_boat_attributes(boat, cookies)
|
88
|
+
dimension_service = ::DimensionService.new(boat) # присланные измерения переведём в другие единицы измерения
|
89
|
+
dimension_service.calculate_boat_attributes(cookies[:dimension])
|
90
|
+
|
91
|
+
# volume_service = VolumeService.new(boat)
|
92
|
+
# volume_service.calculate_boat_attributes(cookies[:volume])
|
93
|
+
end
|
94
|
+
|
95
|
+
# сохраним цены аренды и цену продажи
|
96
|
+
def _save_prices(boat)
|
97
|
+
service = ::Boats::BoatPricesSaveService.new
|
98
|
+
result = service.perform boat, @schema.attributes[:rent_prices]
|
99
|
+
|
100
|
+
if result && boat.for_rent != service.is_for_rent # result = true - обязательное условие
|
101
|
+
boat.update_columns for_rent: service.is_for_rent
|
102
|
+
end
|
103
|
+
|
104
|
+
service = ::Boats::BoatSalePricesSaveService.new
|
105
|
+
result = service.perform boat.id, @schema.attributes[:sale_price]
|
106
|
+
|
107
|
+
if result && boat.for_sale != service.is_for_sale # result = true - обязательное условие
|
108
|
+
boat.update_columns for_sale: service.is_for_sale
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def _reset_ivars
|
113
|
+
@errors = { }
|
114
|
+
@boat = nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class DimensionService
|
2
|
+
|
3
|
+
def initialize model
|
4
|
+
@model = model
|
5
|
+
end
|
6
|
+
|
7
|
+
def calculate_boat_attributes default_dimension
|
8
|
+
|
9
|
+
attributes = [:boat_length_metrics, :boat_beam_metrics, :boat_draft_metrics, :boat_gross_tonage]
|
10
|
+
|
11
|
+
available_dimensions(default_dimension).each do |available_dimension|
|
12
|
+
|
13
|
+
# puts "#{available_dimension} #{default_dimension}"
|
14
|
+
dimension_value = Dimension::COMPARISON[default_dimension.to_sym][available_dimension.to_sym]
|
15
|
+
|
16
|
+
attributes.each do |attribute|
|
17
|
+
attribute_dimension = attribute.to_s + "_#{available_dimension}"
|
18
|
+
attribute_default_dimension = attribute.to_s + "_#{default_dimension}"
|
19
|
+
default_dimension_value = @model.attributes[attribute_default_dimension.downcase]
|
20
|
+
|
21
|
+
if default_dimension_value
|
22
|
+
calculated_dimension = default_dimension_value * dimension_value
|
23
|
+
@model.send("#{attribute_dimension}=", calculated_dimension)
|
24
|
+
# puts "#{default_dimension_value} #{default_dimension} is #{calculated_dimension} #{available_dimension}"
|
25
|
+
else
|
26
|
+
@model.send("#{attribute_dimension}=", nil)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def available_dimensions default_dimension
|
34
|
+
available_dimensions = []
|
35
|
+
I18n::t('static.dimensions').select do |dimension|
|
36
|
+
next if dimension[:id] == default_dimension
|
37
|
+
available_dimensions << dimension[:id]
|
38
|
+
end
|
39
|
+
available_dimensions
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Lease
|
2
|
+
#
|
3
|
+
# Если заявка создаётся НЕ со страницы лодки - используется этот сервис
|
4
|
+
#
|
5
|
+
class CreateBroadcastInquiryService
|
6
|
+
|
7
|
+
attr_reader :inquiry, :errors
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
_reset_instance_variables
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform(client_account, params)
|
14
|
+
_reset_instance_variables
|
15
|
+
|
16
|
+
@inquiry = ::Lease::Inquiry.new
|
17
|
+
_assign_attributes(client_account, params)
|
18
|
+
return false unless @inquiry.valid?
|
19
|
+
|
20
|
+
inquiry_profile = ::Lease::InquiryProfile.new inquiry: @inquiry
|
21
|
+
|
22
|
+
@inquiry.transaction do
|
23
|
+
@inquiry.save!
|
24
|
+
inquiry_profile.save!
|
25
|
+
end
|
26
|
+
|
27
|
+
if params[:uploaded_license_file].present?
|
28
|
+
::Lease::LicenseService.new.process_license_file(client_account, params[:uploaded_license_file])
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def _assign_attributes(client_account, params)
|
37
|
+
@inquiry.client_account = client_account
|
38
|
+
@inquiry.address = params[:address]
|
39
|
+
@inquiry.latitude = params[:latitude]
|
40
|
+
@inquiry.longitude = params[:longitude]
|
41
|
+
@inquiry.from = params[:from]
|
42
|
+
@inquiry.to = params[:to]
|
43
|
+
@inquiry.name = params[:name]
|
44
|
+
@inquiry.phone_number = params[:phone_number]
|
45
|
+
@inquiry.is_skippered = params[:is_skippered]
|
46
|
+
@inquiry.comments = params[:comments]
|
47
|
+
@inquiry.need_transfer = params[:need_transfer]
|
48
|
+
@inquiry.watersports = params[:watersports]
|
49
|
+
@inquiry.guests = params[:guests]
|
50
|
+
@inquiry.kids = params[:kids]
|
51
|
+
@inquiry.boat_type_ids = params[:boat_types] # fuckin' rent_form.rb
|
52
|
+
@inquiry.staying_overnight = params[:staying_overnight]
|
53
|
+
@inquiry.hours_per_day = params[:hours_per_day]&.join(',')
|
54
|
+
|
55
|
+
if params[:my_price].present? && !params[:my_price].to_i.zero?
|
56
|
+
@inquiry.my_price = params[:my_price]
|
57
|
+
@inquiry.my_price_currency = params[:my_price_currency]
|
58
|
+
@inquiry.is_my_price = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def _reset_instance_variables
|
63
|
+
@errors = { }
|
64
|
+
@inquiry = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
def clear_query
|
4
|
+
dup.clear_query!
|
5
|
+
end
|
6
|
+
|
7
|
+
def clear_query!
|
8
|
+
gsub!(/\s+/, ' ')
|
9
|
+
strip!
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def nums_only
|
14
|
+
scan(/\d/).join
|
15
|
+
end
|
16
|
+
|
17
|
+
def numeric?
|
18
|
+
true if Float(self) rescue false
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_bool
|
22
|
+
%w[t true yes on 1].include? self
|
23
|
+
end
|
24
|
+
|
25
|
+
def sanitize(options = {})
|
26
|
+
ActionController::Base.helpers.sanitize(self, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def nl2br
|
30
|
+
dup.nl2br!
|
31
|
+
end
|
32
|
+
|
33
|
+
def nl2br!
|
34
|
+
gsub!(/\r/, '')
|
35
|
+
gsub!(/\n/, '<br>')
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def strip_leading_zeros
|
40
|
+
dup.strip_leading_zeros!
|
41
|
+
end
|
42
|
+
|
43
|
+
def strip_leading_zeros!
|
44
|
+
gsub!(/^0+/, '')
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def try_to_integer
|
49
|
+
Integer( try_to_big_decimal ) rescue self
|
50
|
+
end
|
51
|
+
|
52
|
+
def try_to_big_decimal
|
53
|
+
gsub!(/\s/, '')
|
54
|
+
begin
|
55
|
+
Float(self)
|
56
|
+
rescue
|
57
|
+
return self
|
58
|
+
end
|
59
|
+
BigDecimal(self)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
data/lib/c80_shared/version.rb
CHANGED
data/lib/c80_shared.rb
CHANGED
@@ -3,6 +3,33 @@ lambda do
|
|
3
3
|
lib_path = '%s/**/*.rb' % File.expand_path('../c80_shared', __FILE__)
|
4
4
|
# noinspection RubyResolve
|
5
5
|
Dir[lib_path].each { |f| require_relative f }
|
6
|
+
|
7
|
+
require_relative 'dry/errors'
|
8
|
+
require_relative 'dry/schema'
|
9
|
+
require_relative 'dry/rule'
|
10
|
+
|
11
|
+
require_relative 'dry/rules/binary'
|
12
|
+
require_relative 'dry/rules/composite'
|
13
|
+
require_relative 'dry/rules/between'
|
14
|
+
require_relative 'dry/rules/min_length'
|
15
|
+
require_relative 'dry/rules/greater_than'
|
16
|
+
require_relative 'dry/rules/not_equal'
|
17
|
+
require_relative 'dry/rules/less_than'
|
18
|
+
require_relative 'dry/rules/greater_than_or_equal'
|
19
|
+
require_relative 'dry/rules/collection'
|
20
|
+
require_relative 'dry/rules/length_between'
|
21
|
+
require_relative 'dry/rules/or'
|
22
|
+
require_relative 'dry/rules/and'
|
23
|
+
require_relative 'dry/rules/less_than_or_equal'
|
24
|
+
require_relative 'dry/rules/then'
|
25
|
+
require_relative 'dry/rules/format'
|
26
|
+
require_relative 'dry/rules/present'
|
27
|
+
require_relative 'dry/rules/length_equal'
|
28
|
+
require_relative 'dry/rules/included'
|
29
|
+
require_relative 'dry/rules/equal'
|
30
|
+
require_relative 'dry/rules/max_length'
|
31
|
+
|
32
|
+
require_relative 'dry/rules_factory'
|
6
33
|
end.call
|
7
34
|
|
8
35
|
# загружаем все классы из директории app
|
data/lib/dry/errors.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
module Dry
|
2
|
+
class Errors
|
3
|
+
|
4
|
+
attr_reader :messages
|
5
|
+
|
6
|
+
def initialize(messages = {})
|
7
|
+
@messages = messages
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def add(key, message)
|
12
|
+
keys = key.to_s.split('.').map!(&:to_sym)
|
13
|
+
old = messages.dig(*keys[0...-1]) rescue {}
|
14
|
+
old = {} unless old.is_a?(Hash)
|
15
|
+
new = keys[0...-1].inject(messages) { |h, k| h[k] ||= {} rescue h = {}; h[k] = {} }
|
16
|
+
new[keys.last] = [] unless new.is_a?(Array)
|
17
|
+
new[keys.last] << message
|
18
|
+
new.merge!(old)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def merge!(error, parent_key = nil)
|
23
|
+
hash_to_dots(error.messages, {}, parent_key).each do |key, messages|
|
24
|
+
messages.each { |message| add(key, message) }
|
25
|
+
end
|
26
|
+
messages
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def any?
|
31
|
+
messages.any?
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def has_key?(key)
|
36
|
+
keys = key.to_s.split('.').map!(&:to_sym)
|
37
|
+
keys.size == 1 ? messages[keys.first].present? : (messages.dig(*keys[0...-1])[keys.last].present? rescue false)
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
def first_message
|
42
|
+
fetch_messages(messages.values.first).first
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def clone
|
47
|
+
self.class.new(messages.clone)
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
|
54
|
+
def hash_to_dots(hash, results = {}, start_key = '')
|
55
|
+
hash.each do |key, value|
|
56
|
+
key = key.to_s
|
57
|
+
key_value = start_key.present? ? sprintf('%s.%s', start_key, key) : key
|
58
|
+
if value.is_a?(Hash)
|
59
|
+
results.merge!(hash_to_dots(value, results, key_value))
|
60
|
+
else
|
61
|
+
results[key_value] = value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
results
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def fetch_messages(value)
|
69
|
+
if value.is_a?(Hash)
|
70
|
+
fetch_messages(value.values.first)
|
71
|
+
else
|
72
|
+
value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
data/lib/dry/rule.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Dry
|
2
|
+
class Rule
|
3
|
+
|
4
|
+
attr_reader :value, :errors, :args
|
5
|
+
|
6
|
+
def initialize(value, errors = Dry::Errors.new, **args)
|
7
|
+
@value = value
|
8
|
+
@errors = errors
|
9
|
+
@args = args.symbolize_keys
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def name
|
14
|
+
args[:name]
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def add_error
|
19
|
+
errors.add(key, messages[name.to_s] || 'invalid')
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def clone
|
24
|
+
self.class.new(value, errors.clone, args)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def and(right)
|
29
|
+
Dry::Rules::And.new(self, errors, args.merge(right: right))
|
30
|
+
end
|
31
|
+
alias :& :and
|
32
|
+
|
33
|
+
|
34
|
+
def then(right)
|
35
|
+
Dry::Rules::Then.new(self, errors, args.merge(right: right))
|
36
|
+
end
|
37
|
+
alias :> :then
|
38
|
+
|
39
|
+
|
40
|
+
def or(right)
|
41
|
+
Dry::Rules::Or.new(self, errors, args.merge(right: right))
|
42
|
+
end
|
43
|
+
alias :| :or
|
44
|
+
|
45
|
+
|
46
|
+
def +(right)
|
47
|
+
Dry::Rules::Collection.new(self, errors, args.merge(right: right))
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def valid?
|
52
|
+
raise NotImplementedError
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
|
59
|
+
def messages
|
60
|
+
@messages ||= (args[:messages] || {}).deep_stringify_keys
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def key
|
65
|
+
@key ||= args[:key] || (raise 'Missing required param "key"')
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|