gera 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +67 -0
  4. data/Rakefile +43 -0
  5. data/app/assets/config/gera_manifest.js +2 -0
  6. data/app/assets/javascripts/gera/application.js +14 -0
  7. data/app/assets/stylesheets/gera/application.css +15 -0
  8. data/app/authorizers/gera/application_authorizer.rb +6 -0
  9. data/app/authorizers/gera/currency_rate_authorizer.rb +9 -0
  10. data/app/authorizers/gera/currency_rate_mode_authorizer.rb +7 -0
  11. data/app/authorizers/gera/direction_rate_authorizer.rb +8 -0
  12. data/app/authorizers/gera/exchange_rate_authorizer.rb +8 -0
  13. data/app/authorizers/gera/payment_system_authorizer.rb +13 -0
  14. data/app/authorizers/gera/rate_source_authorizer.rb +7 -0
  15. data/app/controllers/gera/application_controller.rb +30 -0
  16. data/app/controllers/gera/currencies_controller.rb +8 -0
  17. data/app/controllers/gera/currency_rate_history_intervals_controller.rb +53 -0
  18. data/app/controllers/gera/currency_rate_mode_snapshots_controller.rb +83 -0
  19. data/app/controllers/gera/currency_rate_modes_controller.rb +60 -0
  20. data/app/controllers/gera/currency_rates_controller.rb +70 -0
  21. data/app/controllers/gera/direction_rate_history_intervals_controller.rb +62 -0
  22. data/app/controllers/gera/direction_rates_controller.rb +28 -0
  23. data/app/controllers/gera/exchange_rates_controller.rb +25 -0
  24. data/app/controllers/gera/external_rate_snapshots_controller.rb +35 -0
  25. data/app/controllers/gera/external_rates_controller.rb +28 -0
  26. data/app/controllers/gera/payment_systems_controller.rb +78 -0
  27. data/app/controllers/gera/rate_sources_controller.rb +8 -0
  28. data/app/decorators/gera/currency_rate_decorator.rb +148 -0
  29. data/app/decorators/gera/direction_rate_decorator.rb +47 -0
  30. data/app/decorators/gera/payment_system_decorator.rb +19 -0
  31. data/app/helpers/gera/application_helper.rb +110 -0
  32. data/app/helpers/gera/currency_rate_helper.rb +19 -0
  33. data/app/helpers/gera/currency_rate_modes_helper.rb +73 -0
  34. data/app/helpers/gera/direction_rate_helper.rb +36 -0
  35. data/app/helpers/gera/exchange_rates_helper.rb +21 -0
  36. data/app/jobs/gera/application_job.rb +6 -0
  37. data/app/models/concerns/gera/currency_pair_generator.rb +14 -0
  38. data/app/models/concerns/gera/currency_pair_support.rb +42 -0
  39. data/app/models/concerns/gera/currency_rate_mode_builder_support.rb +35 -0
  40. data/app/models/concerns/gera/direction_support.rb +14 -0
  41. data/app/models/concerns/gera/history_interval_concern.rb +46 -0
  42. data/app/models/gera/application_record.rb +8 -0
  43. data/app/models/gera/cbr_external_rate.rb +13 -0
  44. data/app/models/gera/cross_rate_mode.rb +13 -0
  45. data/app/models/gera/currency_rate.rb +78 -0
  46. data/app/models/gera/currency_rate_history_interval.rb +25 -0
  47. data/app/models/gera/currency_rate_history_interval_filter.rb +30 -0
  48. data/app/models/gera/currency_rate_mode.rb +20 -0
  49. data/app/models/gera/currency_rate_mode_snapshot.rb +26 -0
  50. data/app/models/gera/currency_rate_snapshot.rb +12 -0
  51. data/app/models/gera/direction.rb +44 -0
  52. data/app/models/gera/direction_rate.rb +136 -0
  53. data/app/models/gera/direction_rate_history_interval.rb +33 -0
  54. data/app/models/gera/direction_rate_history_interval_filter.rb +30 -0
  55. data/app/models/gera/direction_rate_snapshot.rb +7 -0
  56. data/app/models/gera/exchange_rate.rb +130 -0
  57. data/app/models/gera/external_rate.rb +38 -0
  58. data/app/models/gera/external_rate_snapshot.rb +20 -0
  59. data/app/models/gera/payment_system.rb +98 -0
  60. data/app/models/gera/rate_source.rb +77 -0
  61. data/app/models/gera/rate_source_auto.rb +25 -0
  62. data/app/models/gera/rate_source_bitfinex.rb +9 -0
  63. data/app/models/gera/rate_source_cbr.rb +13 -0
  64. data/app/models/gera/rate_source_cbr_avg.rb +6 -0
  65. data/app/models/gera/rate_source_exmo.rb +9 -0
  66. data/app/models/gera/rate_source_manual.rb +13 -0
  67. data/app/views/application/_exchange_rate_monitor.slim +1 -0
  68. data/app/views/gera/application/_attributes_table.slim +5 -0
  69. data/app/views/gera/application/_currencies_table.slim +18 -0
  70. data/app/views/gera/application/_currency_rate_mode_snapshots.slim +6 -0
  71. data/app/views/gera/application/_direction_rate.slim +6 -0
  72. data/app/views/gera/application/_directions_submenu.slim +11 -0
  73. data/app/views/gera/application/_directions_table.slim +15 -0
  74. data/app/views/gera/application/_exchange_rate.slim +6 -0
  75. data/app/views/gera/application/_external_rate.slim +6 -0
  76. data/app/views/gera/application/_header.slim +12 -0
  77. data/app/views/gera/application/_table.slim +28 -0
  78. data/app/views/gera/application/_topnav.slim +21 -0
  79. data/app/views/gera/application/direction_details.slim +75 -0
  80. data/app/views/gera/application/page.html.slim +1 -0
  81. data/app/views/gera/currencies/index.html.slim +20 -0
  82. data/app/views/gera/currency_rate_history_intervals/_filter.slim +11 -0
  83. data/app/views/gera/currency_rate_history_intervals/index.slim +39 -0
  84. data/app/views/gera/currency_rate_mode_snapshots/_snapshot.slim +20 -0
  85. data/app/views/gera/currency_rate_mode_snapshots/edit.html.slim +30 -0
  86. data/app/views/gera/currency_rate_modes/_cross_rate_mode_fields.slim +21 -0
  87. data/app/views/gera/currency_rate_modes/_currency_rate_mode.slim +7 -0
  88. data/app/views/gera/currency_rate_modes/_form.slim +29 -0
  89. data/app/views/gera/currency_rate_modes/edit.slim +11 -0
  90. data/app/views/gera/currency_rate_modes/index.html.slim +12 -0
  91. data/app/views/gera/currency_rates/_currency_rate.slim +22 -0
  92. data/app/views/gera/currency_rates/_currency_rate_humanized.slim +35 -0
  93. data/app/views/gera/currency_rates/diff.slim +12 -0
  94. data/app/views/gera/currency_rates/index.slim +27 -0
  95. data/app/views/gera/currency_rates/modes.slim +14 -0
  96. data/app/views/gera/currency_rates/show.slim +4 -0
  97. data/app/views/gera/direction_rate_history_intervals/_filter.slim +11 -0
  98. data/app/views/gera/direction_rate_history_intervals/index.slim +41 -0
  99. data/app/views/gera/direction_rates/_direction_rate_example_calculator.slim +28 -0
  100. data/app/views/gera/direction_rates/diff.slim +19 -0
  101. data/app/views/gera/direction_rates/index.slim +26 -0
  102. data/app/views/gera/direction_rates/legacy.html.slim +5 -0
  103. data/app/views/gera/direction_rates/minimals.slim +5 -0
  104. data/app/views/gera/direction_rates/show.slim +14 -0
  105. data/app/views/gera/external_rate_snapshots/index.slim +25 -0
  106. data/app/views/gera/external_rate_snapshots/show.slim +22 -0
  107. data/app/views/gera/external_rates/_flat_list.slim +22 -0
  108. data/app/views/gera/external_rates/index.html.slim +28 -0
  109. data/app/views/gera/external_rates/show.slim +1 -0
  110. data/app/views/gera/payment_systems/_fields.slim +4 -0
  111. data/app/views/gera/payment_systems/_fields_table.slim +18 -0
  112. data/app/views/gera/payment_systems/_form.slim +9 -0
  113. data/app/views/gera/payment_systems/edit.slim +7 -0
  114. data/app/views/gera/payment_systems/index.slim +3 -0
  115. data/app/views/gera/payment_systems/new.slim +9 -0
  116. data/app/views/gera/payment_systems/show.slim +1 -0
  117. data/app/views/gera/rate_sources/_list.slim +13 -0
  118. data/app/views/gera/rate_sources/_rate_source.slim +23 -0
  119. data/app/views/gera/rate_sources/index.html.slim +6 -0
  120. data/app/views/layouts/application.html.slim +15 -0
  121. data/app/views/layouts/gera/blank.html.slim +10 -0
  122. data/app/workers/concerns/gera/rates_worker.rb +87 -0
  123. data/app/workers/gera/bitfinex_rates_worker.rb +65 -0
  124. data/app/workers/gera/cbr_avg_rates_worker.rb +42 -0
  125. data/app/workers/gera/cbr_rates_worker.rb +192 -0
  126. data/app/workers/gera/create_history_intervals_worker.rb +38 -0
  127. data/app/workers/gera/currency_rates_worker.rb +64 -0
  128. data/app/workers/gera/directions_rates_worker.rb +51 -0
  129. data/app/workers/gera/exmo_rates_worker.rb +62 -0
  130. data/app/workers/gera/purge_currency_rates_worker.rb +21 -0
  131. data/app/workers/gera/purge_direction_rates_worker.rb +30 -0
  132. data/config/currencies.yml +559 -0
  133. data/config/routes.rb +30 -0
  134. data/db/migrate/20180912130000_setup.rb +228 -0
  135. data/db/migrate/20190314114844_add_is_available_to_payment_systems.rb +7 -0
  136. data/db/migrate/20190315113046_add_icon_url_to_payment_systems.rb +7 -0
  137. data/db/migrate/20190315132137_add_commission_to_payment_systems.rb +7 -0
  138. data/lib/banks/currency_exchange.rb +12 -0
  139. data/lib/builders/currency_rate_auto_builder.rb +38 -0
  140. data/lib/builders/currency_rate_builder.rb +60 -0
  141. data/lib/builders/currency_rate_cross_builder.rb +70 -0
  142. data/lib/builders/currency_rate_direct_builder.rb +26 -0
  143. data/lib/gera.rb +32 -0
  144. data/lib/gera/bitfinex_fetcher.rb +36 -0
  145. data/lib/gera/configuration.rb +47 -0
  146. data/lib/gera/currencies_purger.rb +28 -0
  147. data/lib/gera/currency_pair.rb +93 -0
  148. data/lib/gera/engine.rb +14 -0
  149. data/lib/gera/mathematic.rb +156 -0
  150. data/lib/gera/money_support.rb +89 -0
  151. data/lib/gera/numeric.rb +25 -0
  152. data/lib/gera/railtie.rb +8 -0
  153. data/lib/gera/rate.rb +22 -0
  154. data/lib/gera/rate_from_multiplicator.rb +54 -0
  155. data/lib/gera/repositories/currency_rate_modes_repository.rb +37 -0
  156. data/lib/gera/repositories/currency_rates_repository.rb +25 -0
  157. data/lib/gera/repositories/direction_rates_repository.rb +46 -0
  158. data/lib/gera/repositories/exchange_rates_repository.rb +23 -0
  159. data/lib/gera/repositories/payment_systems_repository.rb +21 -0
  160. data/lib/gera/repositories/universe.rb +47 -0
  161. data/lib/gera/version.rb +3 -0
  162. data/lib/tasks/auto_generate_diagram.rake +6 -0
  163. data/lib/tasks/gera_tasks.rake +4 -0
  164. metadata +765 -0
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ class DirectionRateDecorator < ApplicationDecorator
5
+ include Mathematic
6
+ delegate :id
7
+
8
+ def inverse_direction
9
+ h.link_to h.direction_rate_path(object.inverse_direction_rate), class: 'text-muted' do
10
+ dd = DirectionRateDecorator.new(object.inverse_direction_rate)
11
+ buffer = dd.direction
12
+ buffer << ' '
13
+ buffer << dd.rate_value
14
+ end
15
+ end
16
+
17
+ def direction
18
+ "#{h.ps_icon object.ps_from}(#{object.ps_from.currency}) &rarr; #{h.ps_icon object.ps_to}(#{object.ps_to.currency})".html_safe
19
+ end
20
+
21
+ def title
22
+ [object.ps_from, object.ps_to].join('->')
23
+ end
24
+
25
+ def ps_comission
26
+ object.ps_comission.to_percent
27
+ end
28
+
29
+ def created_at
30
+ h.link_to h.direction_rate_path(object) do
31
+ I18n.l object.created_at, format: :long
32
+ end
33
+ end
34
+
35
+ def base_rate_value
36
+ h.currency_rate_mode_detailed object.currency_rate
37
+ end
38
+
39
+ def rate_percent
40
+ object.rate_percent.to_percent
41
+ end
42
+
43
+ def rate_value
44
+ h.rate_humanized_description(object.rate_value, object.ps_from.currency, object.ps_to.currency)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ class PaymentSystemDecorator < ApplicationDecorator
5
+ delegate_all
6
+
7
+ def icon
8
+ h.ps_icon object, size: 32
9
+ end
10
+
11
+ def actions
12
+ h.link_to t('.edit'), h.edit_payment_system_path(object), class: 'btn btn-outline btn-default btn-sm' if object.updatable_by? current_user
13
+ end
14
+
15
+ def self.decorated_class
16
+ PaymentSystem
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ module ApplicationHelper
5
+ MINIMAL_EPSILON = 0.001
6
+
7
+ def humanized_currency_rate(rate_value, pair, currency)
8
+ rate = if rate_value < 1
9
+ format('%.9f', rate_value.to_f)
10
+ else
11
+ format('%.3f', rate_value.to_f)
12
+ end
13
+
14
+ if pair.first == currency
15
+ "#{rate} #{currency}" # покупка
16
+ elsif pair.second == currency
17
+ "#{rate} #{currency}" # продажа
18
+ else
19
+ raise
20
+ end
21
+ end
22
+
23
+ def rate_source(currency_rate, level = 0)
24
+ if currency_rate.mode_cross?
25
+ buffer = currency_rate_mode_detailed currency_rate, level
26
+ "⟮ <span class='text-muted'>#{buffer}</span> ⟯".html_safe
27
+ else
28
+ buffer = currency_rate.rate_source.presence || currency_rate.mode
29
+ "(#{buffer})".html_safe
30
+ end
31
+ end
32
+
33
+ def currency_rate_cell_data_attr(rate)
34
+ {
35
+ toggle: :popover,
36
+ container: :body,
37
+ content: "Calculation method: #{currency_rate_mode_detailed rate}",
38
+ trigger: :hover,
39
+ html: 'true',
40
+ placement: :bottom,
41
+ animation: false,
42
+ delay: 0,
43
+ href: currency_rate_path(rate)
44
+ }
45
+ end
46
+
47
+ def rate_diff(rv1, rv2, _reverse = false)
48
+ return rate_diff(1.0 / rv1, 1.0 / rv2, true) if rv1 < 1
49
+
50
+ diff = rv2 - rv1
51
+ return 0 if diff.abs.round(3) == 0
52
+
53
+ p = 100 * diff / rv2
54
+
55
+ buffer = "#{format('%.2f', p.round(3))}%"
56
+ content_tag :span, buffer, data: { toggle: :tooltip, title: "#{rv2} - #{rv1}" }
57
+ end
58
+
59
+ def rate_with_currency(rate, currency)
60
+ rate = format('%.12f', rate) if rate.is_a?(Float) && rate < MINIMAL_EPSILON
61
+ "#{rate} <span class=text-muted>#{currency}</span>".html_safe
62
+ end
63
+
64
+ def rate_cell_with_currency(rate, currency)
65
+ buffer = [rate_with_currency(rate, currency)]
66
+ buffer << rate_with_currency(rate_inversed(rate), currency) if rate < 1
67
+ [buffer].join('<br/>').html_safe
68
+ end
69
+
70
+ # = '%.12f' % humanized_money rate.buy_money
71
+ def humanized_rate(rate, _currency = nil)
72
+ rate = rate.to_f if rate.is_a? Gera::Rate
73
+ if rate.is_a? Money
74
+ raise 'Dont use Money, it has bad round'
75
+ currency = rate.currency
76
+ rate = rate.to_f
77
+ end
78
+ if rate < 1
79
+ rate1 = format('%.3f', (1.0 / rate))
80
+ "<span class=text-muted>1/</span>#{rate1}".html_safe
81
+ else
82
+ format('%.3f', rate)
83
+ # = '%.12f' % humanized_money rate.buy_money
84
+ # humanized_money Money.from_amount(rate, currency)
85
+ end
86
+ end
87
+
88
+ def humanized_rate_text(rate, _currency = nil)
89
+ if rate < 1
90
+ rate1 = format('%.3f', (1.0 / rate))
91
+ "1/#{rate1}".html_safe
92
+ else
93
+ format('%.3f', rate)
94
+ end
95
+ end
96
+
97
+ def rate_inversed(rate)
98
+ "<span class='text-muted'>(1/#{1.0 / rate.to_f})</span>".html_safe
99
+ end
100
+
101
+ def humanized_rate_detailed(rate, separator = ' ')
102
+ buffer = if rate < 1
103
+ "#{rate}#{separator}#{rate_inversed(rate)}".html_safe
104
+ else
105
+ rate
106
+ end
107
+ content_tag :span, buffer.to_s, class: 'text-nowrap'
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera::CurrencyRateHelper
4
+ def currency_rate_mode_autoshow_style(crm)
5
+ 'display: none' unless crm.mode_cross?
6
+ end
7
+
8
+ def currency_rate_columns(cer)
9
+ if cer.external_rate_id.present?
10
+ %i[id created_at rate_value mode external_rate tooltip metadata]
11
+ else
12
+ %i[id created_at rate_value mode tooltip metadata]
13
+ end
14
+ end
15
+
16
+ def currency_rate_class(cer)
17
+ "cer-mode-#{cer.mode}"
18
+ end
19
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera::CurrencyRateModesHelper
4
+ ICON_CLASS = {
5
+ 'draft' => 'battery-half',
6
+ 'active' => 'battery-charging',
7
+ 'deactive' => 'battery-empty'
8
+ }.freeze
9
+
10
+ STATUS_CLASS = {
11
+ 'draft' => 'label-info',
12
+ 'active' => 'label-success',
13
+ 'deactive' => 'label-default'
14
+ }.freeze
15
+
16
+ def currency_rate_mode_detailed(currency_rate, level = 0)
17
+ buffer = []
18
+ if currency_rate.mode_cross?
19
+ currency_rate.external_rates.each do |er|
20
+ buffer << "#{er.currency_pair}(#{er.source})<sup>#{humanized_rate er.rate_value}</sup>"
21
+ end
22
+ elsif currency_rate.mode_same?
23
+ buffer << t('.same_currency')
24
+ else
25
+ buffer << "#{currency_rate.currency_pair}(#{currency_rate.rate_source})"
26
+ end
27
+
28
+ buffer = buffer.join(' × ')
29
+ buffer << " =&nbsp;#{humanized_rate currency_rate.rate_value}&nbsp;#{currency_rate.currency_pair.last}" if level == 0
30
+ buffer.html_safe
31
+ end
32
+
33
+ def currency_rate_mode_build_result_details(build_result)
34
+ return build_result.error.message if build_result.error?
35
+
36
+ raw t('.calculation_method', rate: currency_rate_mode_detailed(build_result.currency_rate))
37
+ end
38
+
39
+ def crms_cell_data_attr(crm)
40
+ url = crm.persisted? ? edit_currency_rate_mode_path(crm, back: request.url) :
41
+ new_currency_rate_mode_path(
42
+ currency_rate_mode: crm.attributes.slice('currency_rate_mode_snapshot_id', 'cur_from', 'cur_to'),
43
+ back: request.url
44
+ )
45
+ {
46
+ toggle: :popover,
47
+ container: :body,
48
+ content: currency_rate_mode_build_result_details(crm.build_result),
49
+ trigger: :hover,
50
+ html: 'true',
51
+ placement: :bottom,
52
+ animation: false,
53
+ delay: 0,
54
+ href: url
55
+ }
56
+ end
57
+
58
+ def crms_status_label(status)
59
+ content_tag :span, status, class: "label #{STATUS_CLASS[status]}"
60
+ end
61
+
62
+ def currency_rate_mode_snapshot_icon(crms)
63
+ ion_icon ICON_CLASS[crms.status]
64
+ end
65
+
66
+ def currency_rate_modes_enum
67
+ Gera::CurrencyRateMode.modes.keys
68
+ end
69
+
70
+ def currencies_enum
71
+ Money::Currency.all.map(&:to_s)
72
+ end
73
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ module DirectionRateHelper
5
+ def exchange_rate_cell_data_attr(direction)
6
+ rate_cell_data_attr(direction).merge href: operator_exchange_rate_path(direction.exchange_rate)
7
+ end
8
+
9
+ def direction_rate_cell_data_attr(direction)
10
+ rate_cell_data_attr(direction).merge href: direction_rate_path(direction.direction_rate.try(:id) || 0)
11
+ end
12
+
13
+ def rate_cell_data_attr(direction)
14
+ return {} unless show_direction_popover?
15
+
16
+ {
17
+ toggle: 'ajax-popover',
18
+ container: :body,
19
+ popover_content_url: details_exchange_rate_path(direction.exchange_rate),
20
+ trigger: :hover,
21
+ html: 'true',
22
+ placement: :bottom,
23
+ animation: false,
24
+ delay: 0
25
+ }
26
+ end
27
+
28
+ def rate_humanized_description(rate_value, cur_from, cur_to)
29
+ if rate_value < 1
30
+ t '.sell_for', cur_to: cur_to, value: (1.0 / rate_value).round(3), cur_from: cur_from
31
+ else
32
+ t '.buy_for', cur_to: cur_to, value: rate_value.round(3), cur_from: cur_from
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ module ExchangeRatesHelper
5
+ def exchange_rate_cell_class(er)
6
+ return unless er
7
+
8
+ classes = []
9
+
10
+ classes << 'rate-popover' if show_direction_popover?
11
+
12
+ classes << if er.comission_percents <= 0
13
+ 'text-danger'
14
+ else
15
+ 'text-success'
16
+ end
17
+
18
+ classes.join ' '
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ class ApplicationJob < ActiveJob::Base
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ module CurrencyPairGenerator
5
+ def generate_pairs_from_currencies(currencies)
6
+ currencies = currencies.map { |c| Money::Currency.find c }
7
+
8
+ currencies
9
+ .map { |c1| currencies.reject { |c2| c2 == c1 }.map { |c2| [c1, c2].join('/') } }
10
+ .flatten.compact
11
+ .map { |cp| Gera::CurrencyPair.new cp }.uniq
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ module CurrencyPairSupport
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ if ancestors.include? ActiveRecord::Base
9
+ scope :by_currency_pair, ->(pair) { where cur_from: pair.cur_from.to_s, cur_to: pair.cur_to.to_s }
10
+
11
+ def self.find_by_currency_pair(pair)
12
+ by_currency_pair(pair).take
13
+ end
14
+
15
+ def self.find_or_build_by_currency_pair(pair)
16
+ by_currency_pair(pair).take || new(currency_pair: pair)
17
+ end
18
+ end
19
+ end
20
+
21
+ def currency_pair=(value)
22
+ self.cur_from = value.cur_from
23
+ self.cur_to = value.cur_to
24
+ @currency_pair = nil
25
+ @currency_from = nil
26
+ @currency_to = nil
27
+ value
28
+ end
29
+
30
+ def currency_pair
31
+ @currency_pair ||= Gera::CurrencyPair.new currency_from, currency_to
32
+ end
33
+
34
+ def currency_from
35
+ @currency_from ||= cur_from.is_a?(Money::Currency) ? cur_from : Money::Currency.find!(cur_from)
36
+ end
37
+
38
+ def currency_to
39
+ @currency_to ||= cur_to.is_a?(Money::Currency) ? cur_to : Money::Currency.find!(cur_to)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ module CurrencyRateModeBuilderSupport
5
+ def build_currency_rate
6
+ @currency_rate ||= build_currency_rate!
7
+ rescue CurrencyRateBuilder::Error
8
+ nil
9
+ end
10
+
11
+ def build_result
12
+ @result ||= builder.build_currency_rate
13
+ end
14
+
15
+ def build_currency_rate!
16
+ raise build_result.error if build_result.error?
17
+
18
+ build_result.currency_rate
19
+ end
20
+
21
+ def builder
22
+ case mode
23
+ when 'auto'
24
+ CurrencyRateAutoBuilder.new currency_pair: currency_pair
25
+ when 'cross'
26
+ CurrencyRateCrossBuilder.new currency_pair: currency_pair, cross_rate_modes: cross_rate_modes
27
+ else
28
+ source = RateSource.find_by_key(mode)
29
+ raise "not supported mode #{mode} for #{currency_pair}" unless source.present?
30
+
31
+ CurrencyRateDirectBuilder.new currency_pair: currency_pair, source: source
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ module DirectionSupport
5
+ def direction=(value)
6
+ self.payment_system_from = value.payment_system_from
7
+ self.payment_system_to = value.payment_system_to
8
+ end
9
+
10
+ def direction
11
+ ::Gera::Direction.new(ps_from: payment_system_from, ps_to: payment_system_to).freeze
12
+ end
13
+ end
14
+ end