genesis_ruby 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 (132) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +158 -0
  5. data/LICENSE +21 -0
  6. data/README.md +343 -0
  7. data/Rakefile +12 -0
  8. data/VERSION +1 -0
  9. data/genesis_ruby.gemspec +43 -0
  10. data/lib/genesis_ruby/api/constants/currencies/iso4217.rb +1106 -0
  11. data/lib/genesis_ruby/api/constants/date_time_formats.rb +45 -0
  12. data/lib/genesis_ruby/api/constants/endpoints.rb +18 -0
  13. data/lib/genesis_ruby/api/constants/environments.rb +17 -0
  14. data/lib/genesis_ruby/api/constants/states/state.rb +43 -0
  15. data/lib/genesis_ruby/api/constants/states.rb +86 -0
  16. data/lib/genesis_ruby/api/constants/transactions/parameters/business/payment_types.rb +24 -0
  17. data/lib/genesis_ruby/api/constants/transactions/parameters/recurring/categories.rb +24 -0
  18. data/lib/genesis_ruby/api/constants/transactions/parameters/recurring/types.rb +28 -0
  19. data/lib/genesis_ruby/api/constants/transactions/parameters/sca_exemptions.rb +34 -0
  20. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/browser/color_depths.rb +46 -0
  21. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/card_holder_account/password_change_indicators.rb +37 -0
  22. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/card_holder_account/registration_indicators.rb +37 -0
  23. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/card_holder_account/shipping_address_usage_indicators.rb +34 -0
  24. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/card_holder_account/suspicious_activity_indicators.rb +28 -0
  25. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/card_holder_account/update_indicators.rb +34 -0
  26. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/control/challenge_indicators.rb +34 -0
  27. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/control/challenge_window_sizes.rb +37 -0
  28. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/control/device_types.rb +28 -0
  29. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/merchant_risk/delivery_timeframes.rb +34 -0
  30. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/merchant_risk/pre_order_purchase_indicators.rb +28 -0
  31. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/merchant_risk/reorder_item_indicators.rb +28 -0
  32. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/merchant_risk/shipping_indicators.rb +46 -0
  33. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/purchase/categories.rb +43 -0
  34. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/sdk/interfaces.rb +31 -0
  35. data/lib/genesis_ruby/api/constants/transactions/parameters/threeds/version2/sdk/ui_types.rb +37 -0
  36. data/lib/genesis_ruby/api/constants/transactions.rb +371 -0
  37. data/lib/genesis_ruby/api/mixins/constants/common.rb +27 -0
  38. data/lib/genesis_ruby/api/mixins/requests/address_info_attributes.rb +20 -0
  39. data/lib/genesis_ruby/api/mixins/requests/customer_address/billing_info_attributes.rb +33 -0
  40. data/lib/genesis_ruby/api/mixins/requests/customer_address/customer_info_attributes.rb +26 -0
  41. data/lib/genesis_ruby/api/mixins/requests/customer_address/shipping_info_attributes.rb +33 -0
  42. data/lib/genesis_ruby/api/mixins/requests/document_attributes.rb +14 -0
  43. data/lib/genesis_ruby/api/mixins/requests/financial/async_attributes.rb +34 -0
  44. data/lib/genesis_ruby/api/mixins/requests/financial/base_attributes.rb +16 -0
  45. data/lib/genesis_ruby/api/mixins/requests/financial/business/airlines_air_carriers_attributes.rb +60 -0
  46. data/lib/genesis_ruby/api/mixins/requests/financial/business/business_attributes.rb +56 -0
  47. data/lib/genesis_ruby/api/mixins/requests/financial/business/car_plane_and_boat_rentals_attributes.rb +53 -0
  48. data/lib/genesis_ruby/api/mixins/requests/financial/business/cruise_lines_attributes.rb +50 -0
  49. data/lib/genesis_ruby/api/mixins/requests/financial/business/event_management_attributes.rb +54 -0
  50. data/lib/genesis_ruby/api/mixins/requests/financial/business/furniture_attributes.rb +53 -0
  51. data/lib/genesis_ruby/api/mixins/requests/financial/business/hotels_and_real_estate_rentals_attributes.rb +53 -0
  52. data/lib/genesis_ruby/api/mixins/requests/financial/business/travel_agencies_attributes.rb +88 -0
  53. data/lib/genesis_ruby/api/mixins/requests/financial/cards/credit_card_attributes.rb +31 -0
  54. data/lib/genesis_ruby/api/mixins/requests/financial/cards/fx_rate_attributes.rb +18 -0
  55. data/lib/genesis_ruby/api/mixins/requests/financial/cards/recurring/recurring_category_attributes.rb +31 -0
  56. data/lib/genesis_ruby/api/mixins/requests/financial/cards/recurring/recurring_type_attributes.rb +31 -0
  57. data/lib/genesis_ruby/api/mixins/requests/financial/cards/tokenization_attributes.rb +39 -0
  58. data/lib/genesis_ruby/api/mixins/requests/financial/crypto_attributes.rb +25 -0
  59. data/lib/genesis_ruby/api/mixins/requests/financial/dynamic_descriptor_attributes.rb +114 -0
  60. data/lib/genesis_ruby/api/mixins/requests/financial/gaming_attributes.rb +25 -0
  61. data/lib/genesis_ruby/api/mixins/requests/financial/moto_attributes.rb +25 -0
  62. data/lib/genesis_ruby/api/mixins/requests/financial/notification_attributes.rb +25 -0
  63. data/lib/genesis_ruby/api/mixins/requests/financial/payment_attributes.rb +32 -0
  64. data/lib/genesis_ruby/api/mixins/requests/financial/pending_payment_attributes.rb +25 -0
  65. data/lib/genesis_ruby/api/mixins/requests/financial/reference_attributes.rb +16 -0
  66. data/lib/genesis_ruby/api/mixins/requests/financial/risk_attributes.rb +47 -0
  67. data/lib/genesis_ruby/api/mixins/requests/financial/sca_attributes.rb +37 -0
  68. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/browser.rb +92 -0
  69. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/card_holder_account.rb +183 -0
  70. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/common_attributes.rb +52 -0
  71. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/control.rb +61 -0
  72. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/merchant_risk.rb +114 -0
  73. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/method.rb +44 -0
  74. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/purchase.rb +42 -0
  75. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/recurring.rb +49 -0
  76. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/sdk.rb +107 -0
  77. data/lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/wpf_attributes.rb +61 -0
  78. data/lib/genesis_ruby/api/mixins/requests/restricted_setter.rb +75 -0
  79. data/lib/genesis_ruby/api/request.rb +171 -0
  80. data/lib/genesis_ruby/api/requests/base/financial.rb +44 -0
  81. data/lib/genesis_ruby/api/requests/base/financials/credit_card.rb +27 -0
  82. data/lib/genesis_ruby/api/requests/financial/cards/authorize.rb +60 -0
  83. data/lib/genesis_ruby/api/requests/financial/cards/authorize3d.rb +76 -0
  84. data/lib/genesis_ruby/api/requests/financial/cards/sale.rb +59 -0
  85. data/lib/genesis_ruby/api/requests/financial/cards/sale3d.rb +75 -0
  86. data/lib/genesis_ruby/api/requests/wpf/create.rb +142 -0
  87. data/lib/genesis_ruby/api/response.rb +91 -0
  88. data/lib/genesis_ruby/builder.rb +42 -0
  89. data/lib/genesis_ruby/builders/base.rb +18 -0
  90. data/lib/genesis_ruby/builders/xml.rb +95 -0
  91. data/lib/genesis_ruby/configuration.rb +177 -0
  92. data/lib/genesis_ruby/connection.rb +20 -0
  93. data/lib/genesis_ruby/dependencies.rb +19 -0
  94. data/lib/genesis_ruby/errors/builder_error.rb +14 -0
  95. data/lib/genesis_ruby/errors/endpoint_not_set_error.rb +14 -0
  96. data/lib/genesis_ruby/errors/error.rb +18 -0
  97. data/lib/genesis_ruby/errors/invalid_argument_error.rb +14 -0
  98. data/lib/genesis_ruby/errors/network_error.rb +14 -0
  99. data/lib/genesis_ruby/errors/object_formatter_error.rb +14 -0
  100. data/lib/genesis_ruby/errors/parameter_error.rb +12 -0
  101. data/lib/genesis_ruby/errors/parser_error.rb +14 -0
  102. data/lib/genesis_ruby/errors/response_error.rb +14 -0
  103. data/lib/genesis_ruby/network/adapter/base_adapter.rb +31 -0
  104. data/lib/genesis_ruby/network/adapter/net_http_adapter.rb +92 -0
  105. data/lib/genesis_ruby/network/base_network.rb +83 -0
  106. data/lib/genesis_ruby/network/net_http.rb +28 -0
  107. data/lib/genesis_ruby/parser.rb +44 -0
  108. data/lib/genesis_ruby/parsers/base.rb +25 -0
  109. data/lib/genesis_ruby/parsers/xml.rb +120 -0
  110. data/lib/genesis_ruby/utils/common.rb +108 -0
  111. data/lib/genesis_ruby/utils/formatters/base.rb +40 -0
  112. data/lib/genesis_ruby/utils/formatters/response/definitions.rb +21 -0
  113. data/lib/genesis_ruby/utils/formatters/response/formats/amount.rb +34 -0
  114. data/lib/genesis_ruby/utils/formatters/response/formats/timestamp.rb +29 -0
  115. data/lib/genesis_ruby/utils/formatters/response/loader.rb +42 -0
  116. data/lib/genesis_ruby/utils/money/base/strategy.rb +38 -0
  117. data/lib/genesis_ruby/utils/money/conversions/amount_to_exponent.rb +22 -0
  118. data/lib/genesis_ruby/utils/money/conversions/exponent_to_amount.rb +22 -0
  119. data/lib/genesis_ruby/utils/money/format.rb +34 -0
  120. data/lib/genesis_ruby/utils/money_format.rb +39 -0
  121. data/lib/genesis_ruby/utils/object_formatter.rb +67 -0
  122. data/lib/genesis_ruby/utils/options/api_config.rb +36 -0
  123. data/lib/genesis_ruby/utils/options/base.rb +35 -0
  124. data/lib/genesis_ruby/utils/options/network_adapter_config.rb +41 -0
  125. data/lib/genesis_ruby/utils/transactions/financial_types.rb +50 -0
  126. data/lib/genesis_ruby/utils/transactions/references/capturable_types.rb +52 -0
  127. data/lib/genesis_ruby/utils/transactions/references/refundable_types.rb +70 -0
  128. data/lib/genesis_ruby/utils/transactions/references/voidable_types.rb +49 -0
  129. data/lib/genesis_ruby/utils/transactions/wpf_types.rb +43 -0
  130. data/lib/genesis_ruby/version.rb +5 -0
  131. data/lib/genesis_ruby.rb +57 -0
  132. metadata +372 -0
@@ -0,0 +1,14 @@
1
+ require 'genesis_ruby/errors/error'
2
+
3
+ module GenesisRuby
4
+ # Invalid Argument Custom Error
5
+ class ObjectFormatterError < Error
6
+
7
+ def custom_message
8
+ return 'Invalid format given!' unless @message
9
+
10
+ @message
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ require 'genesis_ruby/errors/error'
2
+
3
+ module GenesisRuby
4
+ # Invalid Argument Custom Error
5
+ class ParameterError < Error
6
+
7
+ def custom_message
8
+ "Please verify the following transaction parameters:\n#{@message}"
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ require 'genesis_ruby/errors/error'
2
+
3
+ module GenesisRuby
4
+ # Invalid Argument Custom Error
5
+ class ParserError < Error
6
+
7
+ def custom_message
8
+ return 'Unknown error during parsing the response!' unless @message
9
+
10
+ @message
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'genesis_ruby/errors/error'
2
+
3
+ module GenesisRuby
4
+ # Response Custom Error
5
+ class ResponseError < Error
6
+
7
+ def custom_message
8
+ return 'Invalid/Unexpected format given! ' unless @message
9
+
10
+ @message
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,31 @@
1
+ module GenesisRuby
2
+ module Network
3
+ module Adapter
4
+ # Base Network Adapter Class
5
+ class BaseAdapter
6
+
7
+ # Response body is plain string received from the server
8
+ # Response header is hash with the received headers from the server
9
+ attr_accessor :response_body, :response_headers
10
+
11
+ # Retrieve HTTP Response Status Code
12
+ def status
13
+ raise NotImplementedError, 'Status method must be implemented'
14
+ end
15
+
16
+ # Prepare the Request
17
+ # Return the adapter library request object
18
+ def prepare_request(data)
19
+ raise NotImplementedError, 'Prepare Request Body method must be implemented'
20
+ end
21
+
22
+ # Execute the request
23
+ # Return the adapter library response object
24
+ def execute
25
+ raise NotImplementedError, 'Execute method must be implemented'
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,92 @@
1
+ require 'genesis_ruby/network/adapter/base_adapter'
2
+ require 'genesis_ruby/errors/network_error'
3
+ require 'net/http'
4
+
5
+ module GenesisRuby
6
+ module Network
7
+ module Adapter
8
+ # Net-HTTP Adapter implementation
9
+ class NetHttpAdapter < BaseAdapter
10
+
11
+ # HTTP Response Status Code
12
+ def status
13
+ @response&.code
14
+ end
15
+
16
+ # Prepare the request from the given data
17
+ def prepare_request(data)
18
+ @request_data = data
19
+ @uri = parse_uri
20
+ # Force connection re-creation
21
+ @request = Net::HTTP.start(@uri.hostname ||= '', ssl_options)
22
+ end
23
+
24
+ # Send the request
25
+ def execute
26
+ raise NetworkError, 'Request is not initialized' unless @request
27
+
28
+ begin
29
+ # TODO: Use GenesisRuby::Api::Request::METHOD_XXX constants for sending GET and PUT request_data.type
30
+ @response = @request.post(path, request_data.body, headers)
31
+ rescue StandardError => e
32
+ raise NetworkError, "Network error raised by #{e.class.name}: #{e.message}"
33
+ ensure
34
+ # Close the request
35
+ @request.finish
36
+ end
37
+ end
38
+
39
+ # Response body
40
+ def response_body
41
+ @response_body ||= @response ? @response.body : ''
42
+ end
43
+
44
+ # Response headers
45
+ def response_headers
46
+ @response_headers ||= @response ? @response.each_header.to_h : {}
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :uri, :request, :response, :request_data
52
+
53
+ # Path accessor that secure always string return value
54
+ def path
55
+ path = @uri&.path
56
+
57
+ path.empty? ? '/' : path
58
+ end
59
+
60
+ # Override URI parsing errors
61
+ def parse_uri
62
+ URI.parse(request_data.url)
63
+ rescue URI::Error => e
64
+ raise NetworkError, "Error during URL parsing: #{e.message}"
65
+ end
66
+
67
+ # Define SSL requirements
68
+ def ssl_options
69
+ {
70
+ use_ssl: true,
71
+ verify_mode: OpenSSL::SSL::VERIFY_PEER,
72
+ verify_depth: 10,
73
+ verify_hostname: true,
74
+ read_timeout: request_data.timeout,
75
+ min_version: OpenSSL::SSL::TLS1_VERSION
76
+ }
77
+ end
78
+
79
+ # Define Headers
80
+ def headers
81
+ {
82
+ 'Content-Type' => request_data.format,
83
+ 'Content-Length' => request_data.body&.length.to_s,
84
+ 'Authorization' => "Basic #{request_data.user_login}",
85
+ 'User-Agent' => request_data.user_agent
86
+ }
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,83 @@
1
+ require 'genesis_ruby/errors/invalid_argument_error'
2
+ require 'genesis_ruby/utils/options/network_adapter_config'
3
+ require 'base64'
4
+
5
+ module GenesisRuby
6
+ module Network
7
+ # Base Network factory class
8
+ class BaseNetwork
9
+
10
+ XML_HEADER = 'application/xml'.freeze
11
+ JSON_HEADER = 'application/json'.freeze
12
+
13
+ # Base constructor
14
+ def initialize(configuration)
15
+ @context = factory
16
+ @configuration = configuration
17
+ @network_adapter_config = GenesisRuby::Utils::Options::NetworkAdapterConfig.new
18
+ end
19
+
20
+ # Return the Response Body
21
+ def response_body
22
+ @context.response_body
23
+ end
24
+
25
+ # Return the Response Headers
26
+ def response_headers
27
+ @context.response_headers
28
+ end
29
+
30
+ # Send the request
31
+ def send_request
32
+ @context.execute
33
+ end
34
+
35
+ # Initialize the Adapter
36
+ def init_api_data(request)
37
+ @context.prepare_request(adapter_data_mapper(request))
38
+ end
39
+
40
+ # Every child defines is XML Response Type
41
+ def xml?
42
+ raise NotImplementedError, 'Is XML method must be implemented'
43
+ end
44
+
45
+ # Every child defines is JSON Response Type
46
+ def json?
47
+ raise NotImplementedError, 'Is JSON method must be implemented'
48
+ end
49
+
50
+ protected
51
+
52
+ # GenesisRuby::Configuration, Adapter Context, Network Adapter Config mapper
53
+ attr_reader :configuration, :context, :network_adapter_config
54
+
55
+ # Every child override and initialize its instance
56
+ def factory
57
+ raise NotImplementedError, 'Factory method must be implemented'
58
+ end
59
+
60
+ # Map the Request to the Network Adapter object
61
+ def adapter_data_mapper(request)
62
+ network_adapter_config.map_from_request(request).merge(
63
+ {
64
+ user_login: build_user_login,
65
+ user_agent: network_user_agent,
66
+ timeout: configuration.timeout
67
+ }
68
+ )
69
+ end
70
+
71
+ # Build the User login string used for the Authorization Header
72
+ def build_user_login
73
+ Base64.urlsafe_encode64("#{configuration.username}:#{configuration.password}")
74
+ end
75
+
76
+ # The default user agent for every Network
77
+ def network_user_agent
78
+ "Genesis Ruby Client #{GenesisRuby::VERSION}. Network: #{self.class.name}"
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,28 @@
1
+ require 'genesis_ruby/network/base_network'
2
+ require 'genesis_ruby/network/adapter/net_http_adapter'
3
+
4
+ module GenesisRuby
5
+ module Network
6
+ # Net HTTP Network module
7
+ class NetHttp < BaseNetwork
8
+
9
+ # JSON Response Type
10
+ def xml?
11
+ response_headers['content-type'].downcase.include?(BaseNetwork::XML_HEADER)
12
+ end
13
+
14
+ # JSON Response Type
15
+ def json?
16
+ response_headers['content-type'].downcase.include?(BaseNetwork::JSON_HEADER)
17
+ end
18
+
19
+ protected
20
+
21
+ # Adapter Initialization
22
+ def factory
23
+ GenesisRuby::Network::Adapter::NetHttpAdapter.new
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ require 'genesis_ruby/parsers/xml'
2
+ require 'genesis_ruby/errors/parser_error'
3
+
4
+ module GenesisRuby
5
+ # Parser handler
6
+ class Parser
7
+
8
+ # XML Parser
9
+ XML = 'xml'.freeze
10
+
11
+ # JSON Parser
12
+ JSON = 'json'.freeze
13
+
14
+ # Initialize the Parser Interface based on the Response requirements
15
+ def initialize(response_interface)
16
+ case response_interface
17
+ when XML
18
+ @parser_context = GenesisRuby::Parsers::Xml.new
19
+ else
20
+ raise GenesisRuby::ParserError, 'Invalid Parser interface!'
21
+ end
22
+ end
23
+
24
+ # Parsed document
25
+ def structure
26
+ parser_context.structure
27
+ end
28
+
29
+ # Parse tree-structure into Builder document
30
+ def parse_document(document)
31
+ parser_context.parse_document(document)
32
+ end
33
+
34
+ # Indicates that the Root element will not be included in the generated Object
35
+ def skip_root_node
36
+ parser_context.skip_root_node
37
+ end
38
+
39
+ private
40
+
41
+ attr_accessor :parser_context
42
+
43
+ end
44
+ end
@@ -0,0 +1,25 @@
1
+ module GenesisRuby
2
+ module Parsers
3
+ # Contract for every parser abstraction
4
+ class Base
5
+
6
+ attr_reader :structure, :root_node_skipped
7
+
8
+ def initialize
9
+ @structure = {}
10
+ @root_node_skipped = false
11
+ end
12
+
13
+ # Parse given document to Hash Structuce
14
+ def parse_document(document)
15
+ raise NotImplementedError, 'Parse Document must be implemented'
16
+ end
17
+
18
+ # Indicates that the Root element will not be included in the generated Object
19
+ def skip_root_node
20
+ @root_node_skipped = true
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,120 @@
1
+ require 'genesis_ruby/parsers/base'
2
+ require 'nokogiri'
3
+
4
+ module GenesisRuby
5
+ module Parsers
6
+ # XML Document Parser
7
+ class Xml < Base
8
+
9
+ def parse_document(document)
10
+ load_document(document)
11
+
12
+ @structure = if root_node_skipped
13
+ collect_nodes(@document.root)
14
+ else
15
+ { @document.root.name.to_sym => collect_attributes(@document.root).merge(
16
+ collect_nodes(@document.root)
17
+ ) }
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :document
24
+
25
+ # Nokogiri::XML.parse
26
+ def load_document(document)
27
+ @document = Nokogiri::XML.parse document, &:strict
28
+ rescue Nokogiri::SyntaxError
29
+ raise ParserError, 'Given Document is invalid'
30
+ end
31
+
32
+ # Loop the given Nokogiri::XML::Document
33
+ def collect_nodes(nodes, tree = {})
34
+ nodes.children.each do |child|
35
+ next if skip_element?(child)
36
+
37
+ tree[child.name.to_sym] = [tree[child.name.to_sym]] if duplicated_keys?(tree, child.name.to_sym)
38
+
39
+ process_node(child, tree)
40
+ end
41
+
42
+ tree
43
+ end
44
+
45
+ # Process every node by checking the type of the given tree and take decision for further processing
46
+ def process_node(node, tree)
47
+ key = node.name.to_sym
48
+
49
+ # The node has children of type Nokogiri::XML::Node.element?
50
+ return tree[key] = collect_children(node, tree) if children_elements?(node)
51
+
52
+ # The node is Element type and the tree has duplicated keys
53
+ return tree[key].push format_element(node) if tree[key].is_a? Array
54
+
55
+ # Simple Element formatting
56
+ tree[key] = format_element(node)
57
+ end
58
+
59
+ # Handle Nokogiri::XML::Element and take decision for further processing
60
+ def collect_children(child, tree)
61
+ key = child.name.to_sym
62
+ tree[key] = collect_attributes(child) unless tree[key]
63
+
64
+ case tree[key]
65
+ when Array
66
+ tree[key].push collect_attributes(child).merge collect_nodes(child, {})
67
+ when Hash
68
+ collect_nodes(child, tree[key])
69
+ end
70
+ end
71
+
72
+ # Format Nokogiri::XML::Element element
73
+ def format_element(element)
74
+ attributes = collect_attributes(element)
75
+
76
+ return element.content.to_s if attributes.empty? && !children_elements?(element)
77
+
78
+ attributes.merge(
79
+ element.content.to_s.empty? ? {} : { element.name.to_sym => element.content.to_s }
80
+ )
81
+ end
82
+
83
+ # Collect attributes of the given Nokogiri::XML::Node
84
+ def collect_attributes(element)
85
+ attributes = {}
86
+
87
+ element.attributes.each_value { |attribute| attributes.merge! format_attributes attribute }
88
+
89
+ attributes
90
+ end
91
+
92
+ # Format Nokogiri::XML:Element.attributes
93
+ def format_attributes(element)
94
+ { element.name.to_sym => element.value.to_s }
95
+ end
96
+
97
+ # Check if the given tree already has definition of the given key
98
+ def duplicated_keys?(tree, key)
99
+ tree.is_a?(Hash) && tree.key?(key) && !tree[key].is_a?(Array)
100
+ end
101
+
102
+ # Check if the given element should be skipped
103
+ def skip_element?(element)
104
+ blank_element?(element) || element.comment? || element.cdata? || element.xml? || element.html?
105
+ end
106
+
107
+ # Check if the given Nokogiri::XML::Node has blank element (ex. "\n ")
108
+ # Doesn't remove <element/> as Genesis has such output (ex Chargeback cnn)
109
+ def blank_element?(element)
110
+ element.text? && element.content.strip.empty?
111
+ end
112
+
113
+ # Check if the given element has at least one child with Nokogiri::XML::Node.children of Element type
114
+ def children_elements?(element)
115
+ !element.children.find(&:element?).nil?
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,108 @@
1
+ require 'uri'
2
+ require 'date'
3
+
4
+ module GenesisRuby
5
+ module Utils
6
+ # Common Utils used all across the project
7
+ class Common
8
+
9
+ class << self
10
+
11
+ # Deep compact over the given nested structure
12
+ def deep_compact!(structure)
13
+ structure.each do |element_key, element_value|
14
+ deep_compact!(element_key) if element_key.is_a? Hash
15
+ deep_compact!(element_value) if element_value.is_a?(Hash) || element_value.is_a?(Array)
16
+ end
17
+
18
+ compact_array! structure
19
+ compact_hash! structure
20
+ end
21
+
22
+ # Compact over given Array
23
+ def compact_array!(structure)
24
+ return unless structure.is_a? Array
25
+
26
+ structure.reject! do |element|
27
+ element.is_a?(Array) || element.is_a?(Hash) ? element.empty? : element.nil?
28
+ end
29
+ end
30
+
31
+ # Compact over given Hash
32
+ def compact_hash!(structure)
33
+ return unless structure.is_a? Hash
34
+
35
+ structure.reject! do |_key, value|
36
+ value.is_a?(Array) || value.is_a?(Hash) ? value.empty? : value.nil?
37
+ end
38
+ end
39
+
40
+ # Deep compact empty over the given nested hash
41
+ def deep_compact_empty!(structure)
42
+ structure.each do |element_key, element_value|
43
+ deep_compact_empty!(element_key) if element_key.is_a? Hash
44
+ deep_compact_empty!(element_value) if element_value.is_a?(Hash) || element_value.is_a?(Array)
45
+ end
46
+
47
+ empty_compact_array!(structure)
48
+ empty_compact_hash!(structure)
49
+ end
50
+
51
+ # Remove empty elements from an Array
52
+ def empty_compact_array!(structure)
53
+ return unless structure.is_a? Array
54
+
55
+ structure.reject! do |element|
56
+ element.is_a?(Array) || element.is_a?(Hash) || element.is_a?(String) ? element.empty? : element.nil?
57
+ end
58
+ end
59
+
60
+ # Remove empty elements from an Hash
61
+ def empty_compact_hash!(structure)
62
+ return unless structure.is_a? Hash
63
+
64
+ structure.reject! do |_key, value|
65
+ value.is_a?(Array) || value.is_a?(Hash) || value.is_a?(String) ? value.empty? : value.nil?
66
+ end
67
+ end
68
+
69
+ # Retrieve all constant values defined in the given module or class
70
+ def constant_values(reference)
71
+ constant_values = []
72
+
73
+ reference.constants(false).each do |constant_value|
74
+ value = reference.const_get constant_value
75
+
76
+ constant_values.push value unless value.is_a?(Class) || value.is_a?(Module)
77
+ end
78
+
79
+ constant_values.sort
80
+ end
81
+
82
+ # Validates URL
83
+ def valid_url?(url)
84
+ uri = URI.parse(url)
85
+
86
+ %w(http https).include?(uri.scheme) && !uri.host.nil? && !uri.host.to_s.empty?
87
+ rescue StandardError
88
+ false
89
+ end
90
+
91
+ # Parses string to DateTime
92
+ def parse_date(date, formats)
93
+ parsed_date = nil
94
+
95
+ formats.each do |format|
96
+ return Date.strptime(date, format)
97
+ rescue StandardError
98
+ parsed_date = nil
99
+ end
100
+
101
+ parsed_date
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,40 @@
1
+ module GenesisRuby
2
+ module Utils
3
+ module Formatters
4
+ # Object Formatter Contract
5
+ # ObjectFormatter iterate over each formatter with the given structure
6
+ class Base
7
+
8
+ # The Response keys that the formatter will be applied to
9
+ def formatting_keys
10
+ []
11
+ end
12
+
13
+ # The keys required for formatting the specific response key
14
+ def required_keys
15
+ []
16
+ end
17
+
18
+ # Format the given Response Key value
19
+ def format_key(*_args)
20
+ raise NotImplementedError, 'Format method is not implemented'
21
+ end
22
+
23
+ # Collect required key's values requires for the Response Object formatting
24
+ # Ex. Amount to Major requires Currency
25
+ def collect_required_key_values(response_object)
26
+ required_key_values = []
27
+
28
+ required_keys.each do |required_key|
29
+ next unless response_object.include?(required_key.to_sym)
30
+
31
+ required_key_values.push response_object[required_key.to_sym]
32
+ end
33
+
34
+ required_key_values
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+ require 'genesis_ruby/utils/formatters/response/formats/amount'
2
+ require 'genesis_ruby/utils/formatters/response/formats/timestamp'
3
+
4
+ module GenesisRuby
5
+ module Utils
6
+ module Formatters
7
+ # Definition of all available Response Formatters
8
+ module Response
9
+
10
+ # Array of string of all available Response Formatters
11
+ def available_formatters
12
+ [
13
+ GenesisRuby::Utils::Formatters::Response::Formats::Amount,
14
+ GenesisRuby::Utils::Formatters::Response::Formats::Timestamp
15
+ ]
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ require 'genesis_ruby/utils/formatters/base'
2
+ require 'genesis_ruby/utils/money_format'
3
+
4
+ module GenesisRuby
5
+ module Utils
6
+ module Formatters
7
+ module Response
8
+ module Formats
9
+ # Response Amount Formatter. Converts Minor Currency to Major currency format
10
+ class Amount < Base
11
+
12
+ # The Response keys that the formatter will be applied to
13
+ def formatting_keys
14
+ %w(amount leftover_amount)
15
+ end
16
+
17
+ # The keys required for formatting the specific response key
18
+ def required_keys
19
+ %w(currency)
20
+ end
21
+
22
+ # Format the given amount response key and its currency to major currency format
23
+ def format_key(amount, currency)
24
+ GenesisRuby::Utils::MoneyFormat.exponent_to_amount(amount, currency)
25
+ rescue StandardError
26
+ amount
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end