mercury_banking 0.5.38 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd83aeec4085d1c49fe77f7ca68fbc0b7c215dc37deafba3b36a20da0e7f46c4
4
- data.tar.gz: 102b6b607353df20f4494c758935fe3f3517a2a4c95fca8440f9598b364d4f71
3
+ metadata.gz: 5c388af3fd079eacd9a2ba3fbc78a137ecb57352bd7deda55bcebbb372149126
4
+ data.tar.gz: 5665853a81cb64eb76128d70a60a3d830b732fa9ae9096ab2f6bae2d5ea858fd
5
5
  SHA512:
6
- metadata.gz: 06a9d7eea59264a65f07ef4073189c390b8b0d025bdbeced8af797d06f67066e14cd704db814ad61ebaedd03c0682171a5604b2be295463751808b2ddc2057b5
7
- data.tar.gz: b4898456ae09b4cceb0bf6a23c01fe7f860073fd76d069f1b70c0e5a4fc8c266ce52999351dfc319178730cea7779c9649076c43398604aaeadf9824ce9d8d49
6
+ metadata.gz: 8161c1c7afb40e33bb6dd29dbd843d01b969616be1ef4ed45983c2ea65dc2047344ca204e3043085351f8da86822e11ab015d56c4fa077400dd0982047186f75
7
+ data.tar.gz: 97f6fa41b322f94b53a4b69ee9255ceeb9083f3ec5934f35fc1b3952166aa9b0e16767a6460c688d0d7e4cd9aa6f43d65b1eeb3bac0eedb9f519c6679c11aab9
data/.rubocop.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.6
2
+ TargetRubyVersion: 3.0
3
3
  NewCops: enable
4
+ SuggestExtensions: false
4
5
 
5
6
  # Disable the rule that changes $? to $CHILD_STATUS
6
7
  Style/GlobalVars:
@@ -14,4 +15,47 @@ Style/SpecialGlobalVars:
14
15
 
15
16
  # Allow slightly longer methods since we've already done significant refactoring
16
17
  Metrics/MethodLength:
17
- Max: 25 # Default is 10, we're increasing it to 25
18
+ Max: 50 # Default is 10, we're increasing it to 50
19
+ Exclude:
20
+ - 'spec/**/*'
21
+
22
+ # Disable enforcement of single quotes over double quotes
23
+ Style/StringLiterals:
24
+ Enabled: false
25
+
26
+ # Increase line length limit
27
+ Layout/LineLength:
28
+ Max: 150
29
+ Exclude:
30
+ - 'spec/**/*'
31
+
32
+ # Increase block length limit for specs and complex methods
33
+ Metrics/BlockLength:
34
+ Max: 100
35
+ Exclude:
36
+ - 'spec/**/*'
37
+
38
+ # Increase class and module length limits
39
+ Metrics/ClassLength:
40
+ Max: 150
41
+
42
+ Metrics/ModuleLength:
43
+ Max: 500
44
+
45
+ # Increase complexity limits
46
+ Metrics/AbcSize:
47
+ Max: 75
48
+
49
+ Metrics/CyclomaticComplexity:
50
+ Max: 30
51
+
52
+ Metrics/PerceivedComplexity:
53
+ Max: 30
54
+
55
+ # Increase parameter list limit
56
+ Metrics/ParameterLists:
57
+ Max: 12
58
+
59
+ # Allow optional boolean parameters
60
+ Style/OptionalBooleanParameter:
61
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.0] - 2025-03-11
4
+ ### Changed
5
+ - Simplified gem functionality to focus on core features
6
+ - Removed balance sheet generation and only kept balance checking functionality
7
+ - Removed all reconciliation functionality (reconcile, reconcile_all, reconciliation_status, unreconcile)
8
+ - Added new `balancecheck` command that checks ledger balances against Mercury account balances
9
+ - Reduced dependencies by removing unnecessary modules and imports
10
+
11
+ ## [0.6.0] - 2025-03-10
12
+ ### Changed
13
+ - Major code refactoring to improve code quality and maintainability
14
+ - Extracted helper methods into separate modules
15
+ - Reduced method complexity and size
16
+ - Fixed various RuboCop issues
17
+ - Improved code organization by separating concerns
18
+
3
19
  ## [0.5.34] - 2025-03-10
4
20
  ### Fixed
5
21
  - Added missing `thor` gem dependency to fix LoadError when running CLI commands
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  ruby '3.3.5'
@@ -5,22 +7,21 @@ ruby '3.3.5'
5
7
  # Specify your gem's dependencies in mercury_banking.gemspec
6
8
  gemspec
7
9
 
8
- gem 'dotenv'
9
10
  gem 'activesupport'
10
- gem 'terminal-table'
11
- gem 'pry'
12
- gem 'webmock', '~> 3.18'
13
- gem 'thor'
14
- gem 'symmetric-encryption'
11
+ gem 'base64'
12
+ gem 'csv'
13
+ gem 'dead_end', require: false # Helps find syntax errors
14
+ gem 'debride', require: false # Finds unused code
15
+ gem 'dotenv'
15
16
  gem 'lockbox'
17
+ gem 'pry'
16
18
  gem 'rake', '~> 13.0'
17
19
  gem 'rspec', '~> 3.0'
18
20
  gem 'rubocop', require: false
19
21
  gem 'rubocop-performance', require: false
20
- gem 'rubocop-rspec', require: false
21
22
  gem 'rubocop-rake', require: false
22
- gem 'dead_end', require: false # Helps find syntax errors
23
- gem 'debride', require: false # Finds unused code
24
- gem 'csv'
25
- gem 'base64'
26
-
23
+ gem 'rubocop-rspec', require: false
24
+ gem 'symmetric-encryption'
25
+ gem 'terminal-table'
26
+ gem 'thor'
27
+ gem 'webmock', '~> 3.18'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mercury_banking (0.5.38)
4
+ mercury_banking (0.7.0)
5
5
  activesupport (~> 7.0.0)
6
6
  dotenv (~> 2.8)
7
7
  fiddle (~> 1.1)
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "mercury_banking"
data/bin/mercury CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'thor'
4
5
  require 'mercury_banking'
@@ -14,4 +15,4 @@ require 'mercury_banking/cli'
14
15
  require 'symmetric-encryption'
15
16
 
16
17
  # Start the CLI
17
- MercuryBanking::CLI::Main.start(ARGV)
18
+ MercuryBanking::CLI::Main.start(ARGV)
@@ -1,4 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MercuryBanking
4
+ # API client for Mercury Banking
5
+ # Handles all API requests to the Mercury Banking API
2
6
  class API
3
7
  def initialize(secret)
4
8
  @api_key = secret
@@ -40,10 +44,8 @@ module MercuryBanking
40
44
  end
41
45
 
42
46
  def validate_body(body, path)
43
- if body["errors"]
44
- raise "#{@api_key} access to #{path} errored with #{body["errors"]["message"]}"
45
- end
46
-
47
+ raise "#{@api_key} access to #{path} errored with #{body['errors']['message']}" if body["errors"]
48
+
47
49
  body
48
50
  end
49
51
 
@@ -86,6 +88,7 @@ module MercuryBanking
86
88
  def find_account_by_number(account_number)
87
89
  account = accounts.find { |a| a["accountNumber"] == account_number.to_s }
88
90
  raise "Account with number #{account_number} not found" unless account
91
+
89
92
  account
90
93
  end
91
94
 
@@ -93,37 +96,35 @@ module MercuryBanking
93
96
  account = get_account(account_id)
94
97
  account['currentBalance']
95
98
  end
96
-
99
+
97
100
  # /account/:id/transactions
98
101
  def get_transactions(account_id, start_date = nil)
99
102
  path = "account/#{account_id}/transactions"
100
103
  path += "?start=#{start_date}" if start_date
101
104
  get(path)["transactions"]
102
105
  end
103
-
106
+
104
107
  # Get transactions from all accounts
105
108
  def get_all_transactions(start_date = nil)
106
109
  all_transactions = []
107
-
110
+
108
111
  accounts.each do |account|
109
- begin
110
- account_transactions = get_transactions(account["id"], start_date)
111
- # Add account information to each transaction
112
- account_transactions.each do |transaction|
113
- transaction["accountName"] = account["name"]
114
- transaction["accountId"] = account["id"]
115
- transaction["accountNumber"] = account["accountNumber"]
116
- end
117
- all_transactions.concat(account_transactions)
118
- rescue StandardError => e
119
- puts "Warning: Could not fetch transactions for account #{account["name"]}: #{e.message}" unless ENV['MERCURY_SILENT']
112
+ account_transactions = get_transactions(account["id"], start_date)
113
+ # Add account information to each transaction
114
+ account_transactions.each do |transaction|
115
+ transaction["accountName"] = account["name"]
116
+ transaction["accountId"] = account["id"]
117
+ transaction["accountNumber"] = account["accountNumber"]
120
118
  end
119
+ all_transactions.concat(account_transactions)
120
+ rescue StandardError => e
121
+ puts "Warning: Could not fetch transactions for account #{account['name']}: #{e.message}" unless ENV['MERCURY_SILENT']
121
122
  end
122
-
123
+
123
124
  # Sort all transactions by date (newest first)
124
125
  all_transactions.sort_by { |t| t["createdAt"] || "" }.reverse
125
126
  end
126
-
127
+
127
128
  # /account/:id/transactions/:id
128
129
  def transaction(account_id, transaction_id)
129
130
  path = "account/#{account_id}/transaction/#{transaction_id}"
@@ -158,10 +159,12 @@ module MercuryBanking
158
159
  post(rec.json, 'recipients')
159
160
  new_recipient = find_recipient(name: name)
160
161
  raise "Couldn't add account #{name}" unless new_recipient
162
+
161
163
  puts "Successfully added #{new_recipient['name']}" if new_recipient
162
164
  end
163
165
 
164
- def update_recipient(name:, address:, email:, city:, region:, postal_code:, country:, account_number:, routing_number:, recipient_id:)
166
+ def update_recipient(name:, address:, email:, city:, region:, postal_code:, country:, account_number:,
167
+ routing_number:, recipient_id:)
165
168
  rec = MercuryBanking::Recipient.new(
166
169
  name: name,
167
170
  address: address,
@@ -179,7 +182,7 @@ module MercuryBanking
179
182
  def transfer(recipient_id:, amount:, account_id:, note: nil, external: nil)
180
183
  payload = { recipientId: recipient_id, amount: amount, paymentMethod: 'ach',
181
184
  note: note, externalMemo: external, idempotencyKey: "#{recipient_id}-#{amount}-#{note}-#{external}" }
182
- self.post(payload, "account/#{account_id}/transactions")
185
+ post(payload, "account/#{account_id}/transactions")
183
186
  end
184
187
  end
185
- end
188
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MercuryBanking
2
4
  module CLI
3
5
  # Module for account-related commands
@@ -6,51 +8,49 @@ module MercuryBanking
6
8
  def self.included(base)
7
9
  base.class_eval do
8
10
  desc 'accounts', 'Display all accounts'
9
- method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format (table or json)'
11
+ method_option :format, type: :string, default: 'table', enum: %w[table json],
12
+ desc: 'Output format (table or json)'
10
13
  def accounts
11
14
  with_api_client do |client|
12
15
  accounts = client.accounts
13
-
16
+
14
17
  if options[:json] || options[:format] == 'json'
15
18
  puts JSON.pretty_generate(accounts)
16
19
  else
17
20
  display_accounts_table(accounts)
18
21
  end
19
-
22
+
20
23
  puts "You have #{accounts.count} account/s" unless options[:json]
21
24
  end
22
25
  end
23
26
 
24
- desc 'balance ACCOUNT_ID_OR_NUMBER', "Display the current balance of an account (using account number from accounts table)"
27
+ desc 'balance ACCOUNT_ID_OR_NUMBER',
28
+ "Display the current balance of an account (using account number from accounts table)"
25
29
  def balance(account_identifier)
26
30
  with_api_client do |client|
27
31
  # Determine if we're dealing with an account ID or account number
28
32
  account_id = nil
29
- if account_identifier.match?(/^\d+$/) && !account_identifier.include?('-')
30
- begin
31
- account = client.find_account_by_number(account_identifier)
32
- account_id = account["id"]
33
- rescue => e
34
- # If not found by number, assume it's an ID
35
- account_id = account_identifier
36
- account = client.get_account(account_id)
37
- end
38
- else
33
+ begin
34
+ account = client.find_account_by_number(account_identifier)
35
+ account_id = account['id']
36
+ rescue StandardError
37
+ # If not found by number, assume it's an ID
39
38
  account_id = account_identifier
40
- account = client.get_account(account_id)
41
39
  end
42
-
40
+
41
+ account = client.get_account(account_id)
42
+
43
43
  balance = client.balance(account_id)
44
-
44
+
45
45
  if options[:json]
46
46
  puts JSON.pretty_generate({
47
- 'account_id' => account_id,
48
- 'account_number' => account['accountNumber'],
49
- 'name' => account['name'],
50
- 'balance' => balance
51
- })
47
+ 'account_id' => account_id,
48
+ 'account_number' => account['accountNumber'],
49
+ 'name' => account['name'],
50
+ 'balance' => balance
51
+ })
52
52
  else
53
- puts "#{account['name']} (#{account['accountNumber']}): $#{format("%.2f", balance)}"
53
+ puts "#{account['name']} (#{account['accountNumber']}): $#{format('%.2f', balance)}"
54
54
  end
55
55
  end
56
56
  end
@@ -58,4 +58,4 @@ module MercuryBanking
58
58
  end
59
59
  end
60
60
  end
61
- end
61
+ end
@@ -1,3 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'symmetric-encryption'
5
+ require 'json'
6
+ require 'base64'
7
+ require 'fileutils'
8
+
1
9
  module MercuryBanking
2
10
  module CLI
3
11
  # Base module for CLI functionality
@@ -6,7 +14,7 @@ module MercuryBanking
6
14
  def with_api_client
7
15
  api_key = get_api_key
8
16
  raise "API key not found. Please run 'mercury set_key' first." unless api_key
9
-
17
+
10
18
  client = MercuryBanking::API.new(api_key)
11
19
  yield client
12
20
  rescue StandardError => e
@@ -18,41 +26,55 @@ module MercuryBanking
18
26
  exit 1
19
27
  end
20
28
 
21
- # Get the API key from the encrypted storage
29
+ # Get API key by decrypting the stored encrypted key
22
30
  def get_api_key
23
- config_path = File.join(Dir.home, '.mercury-banking', 'api_key.enc')
24
- key_path = File.join(Dir.home, '.mercury-banking', 'key_config.json')
25
-
26
- unless File.exist?(config_path) && File.exist?(key_path)
27
- raise "API key not found. Please run 'mercury set_key' first."
31
+ config_dir = File.join(Dir.home, '.mercury-banking')
32
+ key_config_path = File.join(config_dir, 'key_config.json')
33
+ api_key_path = File.join(config_dir, 'api_key.enc')
34
+
35
+ unless File.exist?(key_config_path) && File.exist?(api_key_path)
36
+ return nil
28
37
  end
38
+
39
+ # Load the cipher configuration
40
+ cipher_config = JSON.parse(File.read(key_config_path))
41
+
42
+ # Recreate the cipher
43
+ cipher = SymmetricEncryption::Cipher.new(
44
+ key: Base64.strict_decode64(cipher_config['key']),
45
+ iv: Base64.strict_decode64(cipher_config['iv']),
46
+ cipher_name: cipher_config['cipher_name']
47
+ )
48
+
49
+ # Set the cipher as the primary one
50
+ SymmetricEncryption.cipher = cipher
29
51
 
30
- begin
31
- key_config = JSON.parse(File.read(key_path))
32
-
33
- # Initialize the SymmetricEncryption with the loaded cipher
34
- cipher = SymmetricEncryption::Cipher.new(
35
- key: Base64.strict_decode64(key_config['key']),
36
- iv: Base64.strict_decode64(key_config['iv']),
37
- cipher_name: key_config['cipher_name'] || 'aes-256-cbc'
38
- )
39
-
40
- # Set the cipher as the primary one
41
- SymmetricEncryption.cipher = cipher
42
-
43
- encrypted_key = File.read(config_path)
44
- cipher.decrypt(encrypted_key)
45
- rescue => e
46
- puts "Error decrypting API key: #{e.message}"
52
+ # Load and decrypt the API key
53
+ encrypted_key = File.read(api_key_path)
54
+ cipher.decrypt(encrypted_key)
55
+ rescue StandardError => e
56
+ puts "Error decrypting API key: #{e.message}" unless options[:json]
57
+ nil
58
+ end
59
+
60
+ # Get the API key from the configuration file (legacy method)
61
+ def api_key
62
+ config_path = File.join(Dir.home, '.mercury', 'config')
63
+ key_path = File.join(Dir.home, '.mercury', 'key')
64
+
65
+ unless File.exist?(config_path) && File.exist?(key_path)
66
+ puts "Error: Mercury API key not found. Please run 'mercury configure' first."
47
67
  exit 1
48
68
  end
69
+
70
+ File.read(key_path).strip
49
71
  end
50
72
 
51
73
  # Log transfer details
52
74
  def log_transfer(from, to, amount, note, status)
53
75
  log_dir = File.join(Dir.home, '.mercury-banking', 'logs')
54
76
  FileUtils.mkdir_p(log_dir)
55
-
77
+
56
78
  log_file = File.join(log_dir, 'transfers.log')
57
79
  File.open(log_file, 'a') do |f|
58
80
  f.puts "#{Time.now.iso8601},\"#{from}\",\"#{to}\",\"#{amount}\",\"#{note}\",\"#{status}\""
@@ -65,4 +87,4 @@ module MercuryBanking
65
87
  end
66
88
  end
67
89
  end
68
- end
90
+ end