mtk_framework 0.1.0

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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.github/workflows/test.yml +21 -0
  4. data/.rspec +1 -0
  5. data/Gemfile +22 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.rdoc +18 -0
  8. data/Rakefile +50 -0
  9. data/VERSION +1 -0
  10. data/lib/mtk_framework.rb +50 -0
  11. data/lib/mtk_framework/active_interaction_concerns/i18nable.rb +18 -0
  12. data/lib/mtk_framework/active_interaction_concerns/interruptable.rb +32 -0
  13. data/lib/mtk_framework/active_interaction_concerns/loggable.rb +84 -0
  14. data/lib/mtk_framework/active_interaction_concerns/rescuable.rb +27 -0
  15. data/lib/mtk_framework/active_interaction_concerns/updatable_object.rb +32 -0
  16. data/lib/mtk_framework/active_interaction_mocks/interaction_group_mocks_helper.rb +32 -0
  17. data/lib/mtk_framework/active_interaction_mocks/interaction_mocks_helper.rb +61 -0
  18. data/lib/mtk_framework/active_interaction_mocks/mocks_loader.rb +53 -0
  19. data/lib/mtk_framework/active_interaction_params.rb +16 -0
  20. data/lib/mtk_framework/active_interaction_params/parametrize_filter.rb +108 -0
  21. data/lib/mtk_framework/core_extensions/array/except.rb +7 -0
  22. data/lib/mtk_framework/core_extensions/class/virtual_method.rb +11 -0
  23. data/lib/mtk_framework/core_extensions/hash/rename_keys.rb +11 -0
  24. data/lib/mtk_framework/gem_extensions/active_interaction/base.rb +20 -0
  25. data/lib/mtk_framework/gem_extensions/active_interaction/filters/abstract_tz_filter.rb +28 -0
  26. data/lib/mtk_framework/gem_extensions/active_interaction/filters/hash_filter.rb +38 -0
  27. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_account_filter.rb +23 -0
  28. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_contract_address_filter.rb +23 -0
  29. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_operation_filter.rb +23 -0
  30. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_public_key_filter.rb +23 -0
  31. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_secret_key_filter.rb +23 -0
  32. data/lib/mtk_framework/gem_extensions/active_interaction/filters/tz_signature_filter.rb +23 -0
  33. data/lib/mtk_framework/locale/active_interaction.fr.yml +8 -0
  34. data/lib/mtk_framework/locale/fr.yml +219 -0
  35. data/lib/mtk_framework/railtie.rb +13 -0
  36. data/mtk_framework.gemspec +114 -0
  37. data/spec/active_interaction_concerns/interruptable_spec.rb +127 -0
  38. data/spec/active_interaction_concerns/loggable_spec.rb +195 -0
  39. data/spec/application_interaction_spec.rb +17 -0
  40. data/spec/fake_app.rb +14 -0
  41. data/spec/gem_extensions/active_interaction/filters/tz_account_filter_spec.rb +7 -0
  42. data/spec/gem_extensions/active_interaction/filters/tz_contract_address_filter_spec.rb +7 -0
  43. data/spec/gem_extensions/active_interaction/filters/tz_operation_filter_spec.rb +7 -0
  44. data/spec/gem_extensions/active_interaction/filters/tz_public_key_filter_spec.rb +7 -0
  45. data/spec/gem_extensions/active_interaction/filters/tz_secret_key_filter_spec.rb +7 -0
  46. data/spec/gem_extensions/active_interaction/filters/tz_signature_filter_spec.rb +7 -0
  47. data/spec/gem_extensions/active_interaction/filters_shared.rb +253 -0
  48. data/spec/gem_extensions/active_interaction/tz_filters_shared.rb +60 -0
  49. data/spec/spec_helper.rb +24 -0
  50. metadata +232 -0
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MtkFramework
4
+ module ActiveInteractionMocks
5
+ module InteractionMocksHelper
6
+ def mock_interaction(
7
+ klass,
8
+ methods: nil,
9
+ failing: false,
10
+ mandatory: true,
11
+ expected_params: nil,
12
+ additional_params: nil,
13
+ return_proc: nil,
14
+ call_original: false
15
+ )
16
+ stubbed_methods = methods || (mandatory ? [:run] : %i[run run!])
17
+
18
+ spec_method = mandatory ? :expect : :allow
19
+ const_name = failing ? 'Fail' : 'Mocked'
20
+ mocking_klass = klass.const_get(const_name)
21
+
22
+ if block_given? || return_proc.present?
23
+ mocking_klass = Class.new(mocking_klass)
24
+ test_scope = self
25
+
26
+ mocking_klass.define_method :execute do
27
+ return_value = yield self if block_given?
28
+ if return_proc.present?
29
+ return_value = test_scope.instance_exec(&return_proc)
30
+ end
31
+
32
+ return_value
33
+ end
34
+
35
+ mocking_klass.define_singleton_method :name do
36
+ "#{klass.name}::#{const_name}"
37
+ end
38
+ end
39
+
40
+ stubbed_methods.map do |stubbed_method|
41
+ block = lambda { |*params|
42
+ mocking_klass.send(stubbed_method, *params, &block)
43
+ }
44
+
45
+ rspec_sentence = receive(stubbed_method, &block)
46
+
47
+ if expected_params
48
+ with = expected_params.respond_to?(:call) ? instance_eval(&expected_params) : expected_params
49
+ rspec_sentence.with(with, &block)
50
+ end
51
+
52
+ additional_params&.call(rspec_sentence)
53
+
54
+ rspec_sentence.and_call_original if call_original
55
+
56
+ send(spec_method, klass).to rspec_sentence
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MtkFramework
4
+ module ActiveInteractionMocks
5
+ class MocksLoader
6
+ def run
7
+ # force require of all interactions
8
+ Dir['app/interactions/**/*.rb'].each do |x|
9
+ require Rails.root.join(x)
10
+ rescue StandardError
11
+ false
12
+ end
13
+
14
+ ApplicationInteraction.descendants.each do |klass|
15
+ mocked = Class.new(ApplicationInteraction) do
16
+ import_filters klass
17
+
18
+ # delegate i18n translation to mocked class
19
+ define_singleton_method :human_attribute_name do |*args|
20
+ klass.human_attribute_name(*args)
21
+ end
22
+
23
+ define_singleton_method :model_name do |*args|
24
+ klass.model_name(*args)
25
+ end
26
+
27
+ define_method :to_model do
28
+ klass.to_s.split('::').first.singularize.safe_constantize&.new
29
+ end
30
+
31
+ private
32
+
33
+ def execute; end
34
+ end
35
+
36
+ failing = Class.new(mocked) do
37
+ private
38
+
39
+ def execute
40
+ errors.add(:base, 'shit happens')
41
+ end
42
+ end
43
+
44
+ klass.const_set('Mocked', mocked)
45
+ klass.const_set('Fail', failing)
46
+ end
47
+
48
+ require_relative 'interaction_mocks_helper.rb'
49
+ require_relative 'interaction_group_mocks_helper.rb'
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'active_interaction_params/parametrize_filter.rb'
4
+
5
+ module MtkFramework
6
+ module ActiveInteractionParams
7
+ def params_from_interaction(klass, except: [])
8
+ params do
9
+ klass.filters.except(*except).each do |_, filter|
10
+ ParametrizeFilter.call(filter, self)
11
+ yield self if block_given?
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MtkFramework
4
+ module ActiveInteractionParams
5
+ class ParametrizeFilter
6
+ TYPES_MAPPING = {
7
+ 'Integer' => Integer,
8
+ 'Float' => Float,
9
+ 'Boolean' => Grape::API::Boolean,
10
+ 'String' => String,
11
+ 'Symbol' => Symbol,
12
+ 'Date' => Date,
13
+ 'DateTime' => DateTime,
14
+ 'Time' => Time,
15
+ 'File' => File,
16
+ 'Hash' => Hash,
17
+ 'Array' => Array,
18
+ 'TzSecretKey' => String,
19
+ 'TzAccount' => String,
20
+ 'TzContractAddress' => String,
21
+ 'TzPublicKey' => String,
22
+ 'TzSignature' => String
23
+ }.freeze
24
+
25
+ class <<self
26
+ # TODO: handle the case of Object, Record, Interface(?)
27
+ def call(filter, params_scope)
28
+ unless nestable_filter?(filter)
29
+ return params_scope.send(grape_method(filter), filter.name,
30
+ **grape_options(filter))
31
+ end
32
+
33
+ parametrize_nestable(filter, params_scope)
34
+ end
35
+
36
+ def parametrize_nestable(filter, params_scope)
37
+ current_filter = filter
38
+
39
+ params_scope.send(grape_method(current_filter), current_filter.name,
40
+ **grape_options(current_filter)) do
41
+ current_filter.filters.each do |_, child_filter|
42
+ unless ParametrizeFilter.array_hash_filters?(current_filter, child_filter)
43
+ ParametrizeFilter.call(child_filter, self)
44
+ end
45
+
46
+ # skip the hash require in the case of an array of hashes (AI and grape handle this case differently)
47
+ child_filter.filters.each do |_, small_child_filter|
48
+ ParametrizeFilter.call(small_child_filter, self)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def grape_filter_type(filter)
55
+ if filter.is_a? ActiveInteraction::ArrayFilter
56
+ return array_grape_filter_type(filter)
57
+ end
58
+
59
+ filter_name = filter.class.name.split('::').second.split('Filter').first
60
+
61
+ TYPES_MAPPING.fetch(filter_name)
62
+ end
63
+
64
+ # returns Array[<File | Integer ...>] if not array of hashes
65
+ # else return Array, because in this case we use nested params with a block
66
+ def array_grape_filter_type(filter)
67
+ child_filter = filter.filters.values.first
68
+
69
+ unless child_filter.is_a? ActiveInteraction::HashFilter
70
+ return Array[grape_filter_type(child_filter)]
71
+ end
72
+
73
+ Array
74
+ end
75
+
76
+ def grape_options(filter)
77
+ grape_options = { desc: filter.options.fetch(:desc, ''), type: grape_filter_type(filter) }
78
+ if filter.options[:default]
79
+ grape_options[:default] = filter.options[:default]
80
+ end
81
+
82
+ grape_options
83
+ end
84
+
85
+ def grape_method(filter)
86
+ filter.options.key?(:default) ? :optional : :requires
87
+ end
88
+
89
+ def array_hash_filters?(filter, child_filter)
90
+ filter.is_a?(ActiveInteraction::ArrayFilter) && child_filter.is_a?(ActiveInteraction::HashFilter)
91
+ end
92
+
93
+ def nestable_filter?(filter)
94
+ unless filter.is_a? ActiveInteraction::ArrayFilter
95
+ return filter.filters.any?
96
+ end
97
+
98
+ # this case is not supposed to happen since ActiveInteraction only allows one type in the case of an Array
99
+ raise NotImplementedError unless filter.filters.count == 1
100
+
101
+ # if it is an array of hashes => we use nested params with a block
102
+ # else we use types like Array[String] => not nestable
103
+ filter.filters.values.first.is_a? ActiveInteraction::HashFilter
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Array
4
+ def except(*fields)
5
+ self - fields
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Class
4
+ def virtual_method(*args)
5
+ args.each do |method_name|
6
+ define_method(method_name) do
7
+ raise NotImplementedError, "#{self.class.name}##{method_name} is a virtual method!"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ def rename_keys(keys_replacement_hash)
5
+ transform_keys do |key|
6
+ next key unless keys_replacement_hash.key?(key)
7
+
8
+ keys_replacement_hash[key]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteraction
4
+ class Base
5
+ def self.import_filters_optionally(klass, options = {})
6
+ only = options[:only]
7
+ except = options[:except]
8
+ default_value = options[:default_value]
9
+
10
+ other_filters = klass.filters.dup
11
+ other_filters.select! { |k, _| [*only].include?(k) } if only
12
+ other_filters.reject! { |k, _| [*except].include?(k) } if except
13
+
14
+ other_filters.each_value do |filter|
15
+ filter.options[:default] = default_value
16
+ initialize_filter(filter)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteraction
4
+ # @abstract
5
+ #
6
+ # Common logic for filters that handle numeric objects.
7
+ #
8
+ # @private
9
+ class AbstractTzFilter < Filter
10
+ private
11
+
12
+ def matches?(value)
13
+ value.is_a?(String) && valid_tz_entry?(value)
14
+ rescue NoMethodError # BasicObject
15
+ false
16
+ end
17
+
18
+ def valid_tz_entry?(value)
19
+ TezosClient.new.decode_tz(value) do |prefix, _payload|
20
+ return false unless prefixes.include? prefix
21
+ end
22
+
23
+ true
24
+ rescue ArgumentError
25
+ false
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_interaction'
4
+
5
+ # credits to : https://github.com/formigarafa/active_interaction/pull/2
6
+ # This allows to import_filters inside hash. Example :
7
+ # hash :user do
8
+ # import_filters UserSignup
9
+ # end
10
+
11
+ module ActiveInteraction
12
+ class HashFilter < Filter
13
+ # Import filters from another interaction.
14
+ #
15
+ # @param klass [Class] The other interaction.
16
+ # @param options [Hash]
17
+ #
18
+ # @option options [Array<Symbol>, nil] :only Import only these filters.
19
+ # @option options [Array<Symbol>, nil] :except Import all filters except
20
+ # for these.
21
+ #
22
+ # @!visibility public
23
+ def import_filters(klass, options = {}) # rubocop:disable Metrics/AbcSize
24
+ only = options[:only]
25
+ except = options[:except]
26
+ groups = options[:groups] || [klass.to_s.demodulize.underscore.to_sym]
27
+
28
+ klass.filters.each do |name, filter|
29
+ next if only && ![*only].include?(name)
30
+ next if except && [*except].include?(name)
31
+
32
+ options = filter.options.merge(groups: groups)
33
+ filter_copy = filter.class.new(name, options)
34
+ filters[name] = filter_copy
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteraction
4
+ class Base
5
+ # @!method self.tz_public_key(*attributes, options = {})
6
+ # Creates accessors for the attributes and ensures that values passed to
7
+ # the attributes are Strings.
8
+ #
9
+ # @example
10
+ # tz_public_key :public_key
11
+ end
12
+
13
+ # @private
14
+ class TzAccountFilter < AbstractTzFilter
15
+ register :tz_account
16
+
17
+ private
18
+
19
+ def prefixes
20
+ %i[tz1 tz2 tz3].freeze
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteraction
4
+ class Base
5
+ # @!method self.tz_public_key(*attributes, options = {})
6
+ # Creates accessors for the attributes and ensures that values passed to
7
+ # the attributes are Strings.
8
+ #
9
+ # @example
10
+ # tz_public_key :public_key
11
+ end
12
+
13
+ # @private
14
+ class TzContractAddressFilter < AbstractTzFilter
15
+ register :tz_contract_address
16
+
17
+ private
18
+
19
+ def prefixes
20
+ %i[KT].freeze
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteraction
4
+ class Base
5
+ # @!method self.tz_operation(*attributes, options = {})
6
+ # Creates accessors for the attributes and ensures that values passed to
7
+ # the attributes are Strings.
8
+ #
9
+ # @example
10
+ # tz_operation :operation
11
+ end
12
+
13
+ # @private
14
+ class TzOperationFilter < AbstractTzFilter
15
+ register :tz_operation
16
+
17
+ private
18
+
19
+ def prefixes
20
+ %i[o].freeze
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveInteraction
4
+ class Base
5
+ # @!method self.tz_public_key(*attributes, options = {})
6
+ # Creates accessors for the attributes and ensures that values passed to
7
+ # the attributes are Strings.
8
+ #
9
+ # @example
10
+ # tz_public_key :public_key
11
+ end
12
+
13
+ # @private
14
+ class TzPublicKeyFilter < AbstractTzFilter
15
+ register :tz_public_key
16
+
17
+ private
18
+
19
+ def prefixes
20
+ %i[edpk].freeze
21
+ end
22
+ end
23
+ end