lunchmoney 1.4.1 → 1.5.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build_and_publish_yard_docs.yml +2 -2
  3. data/.github/workflows/ci.yml +9 -10
  4. data/.github/workflows/rbi-updater.yml +1 -1
  5. data/.github/workflows/release_pipeline.yml +1 -1
  6. data/.rubocop.yml +1 -1
  7. data/.ruby-version +1 -1
  8. data/.toys/.toys.rb +8 -0
  9. data/Gemfile +1 -1
  10. data/Gemfile.lock +76 -60
  11. data/README.md +0 -2
  12. data/SECURITY.md +151 -0
  13. data/bin/check_vcr_version +94 -0
  14. data/lib/lunchmoney/api.rb +25 -38
  15. data/lib/lunchmoney/calls/assets.rb +10 -13
  16. data/lib/lunchmoney/calls/base.rb +59 -7
  17. data/lib/lunchmoney/calls/budgets.rb +22 -25
  18. data/lib/lunchmoney/calls/categories.rb +28 -36
  19. data/lib/lunchmoney/calls/crypto.rb +7 -9
  20. data/lib/lunchmoney/calls/plaid_accounts.rb +7 -9
  21. data/lib/lunchmoney/calls/recurring_expenses.rb +4 -5
  22. data/lib/lunchmoney/calls/tags.rb +3 -4
  23. data/lib/lunchmoney/calls/transactions.rb +28 -36
  24. data/lib/lunchmoney/calls/users.rb +3 -4
  25. data/lib/lunchmoney/configuration.rb +20 -0
  26. data/lib/lunchmoney/objects/object.rb +4 -9
  27. data/lib/lunchmoney/validators.rb +8 -6
  28. data/lib/lunchmoney/version.rb +1 -1
  29. data/lib/lunchmoney.rb +3 -3
  30. data/lunchmoney.gemspec +1 -1
  31. data/sorbet/rbi/annotations/activesupport.rbi +35 -0
  32. data/sorbet/rbi/dsl/active_support/callbacks.rbi +0 -2
  33. data/sorbet/rbi/gems/{activesupport@7.2.2.1.rbi → activesupport@8.0.2.1.rbi} +1369 -989
  34. data/sorbet/rbi/gems/{ast@2.4.2.rbi → ast@2.4.3.rbi} +4 -3
  35. data/sorbet/rbi/gems/{base64@0.2.0.rbi → base64@0.3.0.rbi} +76 -39
  36. data/sorbet/rbi/gems/{benchmark@0.4.0.rbi → benchmark@0.4.1.rbi} +50 -49
  37. data/sorbet/rbi/gems/bigdecimal@3.2.2.rbi +275 -0
  38. data/sorbet/rbi/gems/{dotenv@3.1.7.rbi → dotenv@3.1.8.rbi} +9 -9
  39. data/sorbet/rbi/gems/{drb@2.2.1.rbi → drb@2.2.3.rbi} +503 -188
  40. data/sorbet/rbi/gems/{faraday-net_http@3.4.0.rbi → faraday-net_http@3.4.1.rbi} +23 -23
  41. data/sorbet/rbi/gems/{faraday@2.12.2.rbi → faraday@2.13.4.rbi} +468 -141
  42. data/sorbet/rbi/gems/{hashdiff@1.1.2.rbi → hashdiff@1.2.0.rbi} +5 -3
  43. data/sorbet/rbi/gems/{json@2.10.2.rbi → json@2.13.2.rbi} +386 -175
  44. data/sorbet/rbi/gems/{kramdown@2.4.0.rbi → kramdown@2.5.1.rbi} +316 -234
  45. data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +240 -0
  46. data/sorbet/rbi/gems/{logger@1.6.6.rbi → logger@1.7.0.rbi} +63 -40
  47. data/sorbet/rbi/gems/{parser@3.3.7.1.rbi → parser@3.3.9.0.rbi} +303 -291
  48. data/sorbet/rbi/gems/{rack@3.1.9.rbi → rack@3.2.1.rbi} +712 -547
  49. data/sorbet/rbi/gems/{rake@13.2.1.rbi → rake@13.3.0.rbi} +238 -227
  50. data/sorbet/rbi/gems/{rbi@0.3.1.rbi → rbi@0.3.6.rbi} +1084 -2521
  51. data/sorbet/rbi/gems/{rbs@3.9.0.rbi → rbs@4.0.0.dev.4.rbi} +1336 -419
  52. data/sorbet/rbi/gems/{regexp_parser@2.10.0.rbi → regexp_parser@2.11.2.rbi} +1044 -956
  53. data/sorbet/rbi/gems/require-hooks@0.2.2.rbi +110 -0
  54. data/sorbet/rbi/gems/{rexml@3.4.1.rbi → rexml@3.4.2.rbi} +302 -292
  55. data/sorbet/rbi/gems/{rubocop-ast@1.38.0.rbi → rubocop-ast@1.46.0.rbi} +802 -1016
  56. data/sorbet/rbi/gems/{rubocop-minitest@0.36.0.rbi → rubocop-minitest@0.38.2.rbi} +129 -93
  57. data/sorbet/rbi/gems/{rubocop-rails@2.29.1.rbi → rubocop-rails@2.33.3.rbi} +575 -230
  58. data/sorbet/rbi/gems/{rubocop-shopify@2.15.1.rbi → rubocop-shopify@2.17.1.rbi} +1 -0
  59. data/sorbet/rbi/gems/{rubocop-sorbet@0.8.7.rbi → rubocop-sorbet@0.10.5.rbi} +761 -83
  60. data/sorbet/rbi/gems/{rubocop@1.71.2.rbi → rubocop@1.80.1.rbi} +6543 -3097
  61. data/sorbet/rbi/gems/{spoom@1.6.1.rbi → spoom@1.7.6.rbi} +1179 -2621
  62. data/sorbet/rbi/gems/{tapioca@0.16.11.rbi → tapioca@0.17.7.rbi} +623 -745
  63. data/sorbet/rbi/gems/{thor@1.3.2.rbi → thor@1.4.0.rbi} +88 -67
  64. data/sorbet/rbi/gems/{unicode-display_width@3.1.4.rbi → unicode-display_width@3.1.5.rbi} +5 -5
  65. data/sorbet/rbi/gems/{vcr@6.3.1.rbi → vcr@6.3.1-ce35c236fe48899f02ddf780973b44cdb756c0ee.rbi} +140 -123
  66. metadata +42 -38
  67. data/sorbet/rbi/gems/bigdecimal@3.1.9.rbi +0 -78
  68. /data/sorbet/rbi/gems/{connection_pool@2.5.0.rbi → connection_pool@2.5.3.rbi} +0 -0
  69. /data/sorbet/rbi/gems/{language_server-protocol@3.17.0.4.rbi → language_server-protocol@3.17.0.5.rbi} +0 -0
  70. /data/sorbet/rbi/gems/{parallel@1.26.3.rbi → parallel@1.27.0.rbi} +0 -0
@@ -30,7 +30,7 @@ module LunchMoney
30
30
 
31
31
  sig { params(api_key: T.nilable(String)).void }
32
32
  def initialize(api_key: nil)
33
- @api_key = T.let((api_key || LunchMoney.configuration.api_key), T.nilable(String))
33
+ @api_key = T.let(api_key || LunchMoney.configuration.api_key, T.nilable(String))
34
34
  end
35
35
 
36
36
  delegate :me, to: :user_calls
@@ -41,9 +41,7 @@ module LunchMoney
41
41
  # api.me
42
42
  sig { returns(LunchMoney::Calls::Base) }
43
43
  def user_calls
44
- with_valid_api_key do
45
- @user_calls ||= T.let(LunchMoney::Calls::Users.new(api_key:), T.nilable(LunchMoney::Calls::Users))
46
- end
44
+ memoized_call_instance(:@user_calls, LunchMoney::Calls::Users)
47
45
  end
48
46
 
49
47
  delegate :categories,
@@ -84,9 +82,7 @@ module LunchMoney
84
82
  # api.force_delete_category(1234567)
85
83
  sig { returns(LunchMoney::Calls::Base) }
86
84
  def category_calls
87
- with_valid_api_key do
88
- @category_calls ||= T.let(LunchMoney::Calls::Categories.new(api_key:), T.nilable(LunchMoney::Calls::Categories))
89
- end
85
+ memoized_call_instance(:@category_calls, LunchMoney::Calls::Categories)
90
86
  end
91
87
 
92
88
  delegate :tags, to: :tag_calls
@@ -97,9 +93,7 @@ module LunchMoney
97
93
  # api.tags
98
94
  sig { returns(LunchMoney::Calls::Base) }
99
95
  def tag_calls
100
- with_valid_api_key do
101
- @tag_calls ||= T.let(LunchMoney::Calls::Tags.new(api_key:), T.nilable(LunchMoney::Calls::Tags))
102
- end
96
+ memoized_call_instance(:@tag_calls, LunchMoney::Calls::Tags)
103
97
  end
104
98
 
105
99
  delegate :transactions,
@@ -160,12 +154,7 @@ module LunchMoney
160
154
  # api.delete_transaction_group(905483362)
161
155
  sig { returns(LunchMoney::Calls::Base) }
162
156
  def transaction_calls
163
- with_valid_api_key do
164
- @transaction_calls ||= T.let(
165
- LunchMoney::Calls::Transactions.new(api_key:),
166
- T.nilable(LunchMoney::Calls::Transactions),
167
- )
168
- end
157
+ memoized_call_instance(:@transaction_calls, LunchMoney::Calls::Transactions)
169
158
  end
170
159
 
171
160
  delegate :recurring_expenses, to: :recurring_expense_calls
@@ -176,12 +165,7 @@ module LunchMoney
176
165
  # api.recurring_expenses
177
166
  sig { returns(LunchMoney::Calls::Base) }
178
167
  def recurring_expense_calls
179
- with_valid_api_key do
180
- @recurring_expense_calls ||= T.let(
181
- LunchMoney::Calls::RecurringExpenses.new(api_key:),
182
- T.nilable(LunchMoney::Calls::RecurringExpenses),
183
- )
184
- end
168
+ memoized_call_instance(:@recurring_expense_calls, LunchMoney::Calls::RecurringExpenses)
185
169
  end
186
170
 
187
171
  delegate :budgets, :upsert_budget, :remove_budget, to: :budget_calls
@@ -193,14 +177,12 @@ module LunchMoney
193
177
  # @example [Upsert Budget](https://lunchmoney.dev/#upsert-budget)
194
178
  # api = LunchMoney::Api.new
195
179
  # api.upsert_budget(start_date: "2023-01-01", category_id: 777052, amount: 400.99)
196
- # @example [Remove Budget(https://lunchmoney.dev/#remove-budget)
180
+ # @example [Remove Budget](https://lunchmoney.dev/#remove-budget)
197
181
  # api = LunchMoney::Api.new
198
182
  # api.remove_budget(start_date: "2023-01-01", category_id: 777052)
199
183
  sig { returns(LunchMoney::Calls::Base) }
200
184
  def budget_calls
201
- with_valid_api_key do
202
- @budget_calls ||= T.let(LunchMoney::Calls::Budgets.new(api_key:), T.nilable(LunchMoney::Calls::Budgets))
203
- end
185
+ memoized_call_instance(:@budget_calls, LunchMoney::Calls::Budgets)
204
186
  end
205
187
 
206
188
  delegate :assets, :create_asset, :update_asset, to: :asset_calls
@@ -221,9 +203,7 @@ module LunchMoney
221
203
  # api.update_asset(93746, balance: "99.99")
222
204
  sig { returns(LunchMoney::Calls::Base) }
223
205
  def asset_calls
224
- with_valid_api_key do
225
- @asset_calls ||= T.let(LunchMoney::Calls::Assets.new(api_key:), T.nilable(LunchMoney::Calls::Assets))
226
- end
206
+ memoized_call_instance(:@asset_calls, LunchMoney::Calls::Assets)
227
207
  end
228
208
 
229
209
  delegate :plaid_accounts, :plaid_accounts_fetch, to: :plaid_account_calls
@@ -237,12 +217,7 @@ module LunchMoney
237
217
  # api.plaid_accounts_fetch
238
218
  sig { returns(LunchMoney::Calls::Base) }
239
219
  def plaid_account_calls
240
- with_valid_api_key do
241
- @plaid_account_calls ||= T.let(
242
- LunchMoney::Calls::PlaidAccounts.new(api_key:),
243
- T.nilable(LunchMoney::Calls::PlaidAccounts),
244
- )
245
- end
220
+ memoized_call_instance(:@plaid_account_calls, LunchMoney::Calls::PlaidAccounts)
246
221
  end
247
222
 
248
223
  delegate :crypto, :update_crypto, to: :crypto_calls
@@ -256,9 +231,7 @@ module LunchMoney
256
231
  # api.update_crypto(1234567, name: "New Crypto Name")
257
232
  sig { returns(LunchMoney::Calls::Base) }
258
233
  def crypto_calls
259
- with_valid_api_key do
260
- @crypto_calls ||= T.let(LunchMoney::Calls::Crypto.new(api_key:), T.nilable(LunchMoney::Calls::Crypto))
261
- end
234
+ memoized_call_instance(:@crypto_calls, LunchMoney::Calls::Crypto)
262
235
  end
263
236
 
264
237
  private
@@ -269,5 +242,19 @@ module LunchMoney
269
242
 
270
243
  yield
271
244
  end
245
+
246
+ sig do
247
+ type_parameters(:T)
248
+ .params(
249
+ ivar_name: Symbol,
250
+ klass: T.class_of(LunchMoney::Calls::Base),
251
+ )
252
+ .returns(LunchMoney::Calls::Base)
253
+ end
254
+ def memoized_call_instance(ivar_name, klass)
255
+ with_valid_api_key do
256
+ instance_variable_get(ivar_name) || instance_variable_set(ivar_name, klass.new(api_key:))
257
+ end
258
+ end
272
259
  end
273
260
  end
@@ -11,11 +11,10 @@ module LunchMoney
11
11
  def assets
12
12
  response = get("assets")
13
13
 
14
- api_errors = errors(response)
15
- return api_errors if api_errors.present?
16
-
17
- response.body[:assets].map do |asset|
18
- LunchMoney::Objects::Asset.new(**asset)
14
+ handle_api_response(response) do |body|
15
+ body[:assets].map do |asset|
16
+ LunchMoney::Objects::Asset.new(**asset)
17
+ end
19
18
  end
20
19
  end
21
20
 
@@ -50,10 +49,9 @@ module LunchMoney
50
49
 
51
50
  response = post("assets", params)
52
51
 
53
- api_errors = errors(response)
54
- return api_errors if api_errors.present?
55
-
56
- LunchMoney::Objects::Asset.new(**response.body)
52
+ handle_api_response(response) do |body|
53
+ LunchMoney::Objects::Asset.new(**body)
54
+ end
57
55
  end
58
56
 
59
57
  sig do
@@ -88,10 +86,9 @@ module LunchMoney
88
86
 
89
87
  response = put("assets/#{asset_id}", params)
90
88
 
91
- api_errors = errors(response)
92
- return api_errors if api_errors.present?
93
-
94
- LunchMoney::Objects::Asset.new(**response.body)
89
+ handle_api_response(response) do |body|
90
+ LunchMoney::Objects::Asset.new(**body)
91
+ end
95
92
  end
96
93
  end
97
94
  end
@@ -22,14 +22,15 @@ module LunchMoney
22
22
 
23
23
  sig { params(api_key: T.nilable(String)).void }
24
24
  def initialize(api_key: nil)
25
- @api_key = T.let((api_key || LunchMoney.configuration.api_key), T.nilable(String))
25
+ @api_key = T.let(api_key || LunchMoney.configuration.api_key, T.nilable(String))
26
+ @connections = T.let({}, T::Hash[Symbol, Faraday::Connection])
26
27
  end
27
28
 
28
29
  private
29
30
 
30
31
  sig { params(endpoint: String, query_params: T.nilable(T::Hash[Symbol, T.untyped])).returns(Faraday::Response) }
31
32
  def get(endpoint, query_params: nil)
32
- connection = request(flat_params: true)
33
+ connection = connection_for(:flat_params)
33
34
 
34
35
  if query_params.present?
35
36
  connection.get(BASE_URL + endpoint, query_params)
@@ -40,19 +41,19 @@ module LunchMoney
40
41
 
41
42
  sig { params(endpoint: String, params: T.nilable(T::Hash[Symbol, T.untyped])).returns(Faraday::Response) }
42
43
  def post(endpoint, params)
43
- request(json_request: true).post(BASE_URL + endpoint, params)
44
+ connection_for(:json).post(BASE_URL + endpoint, params)
44
45
  end
45
46
 
46
47
  sig { params(endpoint: String, body: T::Hash[Symbol, T.untyped]).returns(Faraday::Response) }
47
48
  def put(endpoint, body)
48
- request(json_request: true).put(BASE_URL + endpoint) do |req|
49
+ connection_for(:json).put(BASE_URL + endpoint) do |req|
49
50
  req.body = body
50
51
  end
51
52
  end
52
53
 
53
54
  sig { params(endpoint: String, query_params: T.nilable(T::Hash[Symbol, T.untyped])).returns(Faraday::Response) }
54
55
  def delete(endpoint, query_params: nil)
55
- connection = request(flat_params: true)
56
+ connection = connection_for(:flat_params)
56
57
 
57
58
  if query_params.present?
58
59
  connection.delete(BASE_URL + endpoint, query_params)
@@ -61,12 +62,24 @@ module LunchMoney
61
62
  end
62
63
  end
63
64
 
65
+ sig { params(connection_type: Symbol).returns(Faraday::Connection) }
66
+ def connection_for(connection_type)
67
+ @connections[connection_type] ||= case connection_type
68
+ when :json
69
+ build_connection(json_request: true)
70
+ when :flat_params
71
+ build_connection(flat_params: true)
72
+ else
73
+ build_connection
74
+ end
75
+ end
76
+
64
77
  sig { params(json_request: T::Boolean, flat_params: T::Boolean).returns(Faraday::Connection) }
65
- def request(json_request: false, flat_params: false)
78
+ def build_connection(json_request: false, flat_params: false)
66
79
  Faraday.new do |conn|
67
80
  conn.request(:authorization, "Bearer", @api_key)
68
81
  conn.request(:json) if json_request
69
- # conn.options.params_encoder = Faraday::FlatParamsEncoder if flat_params
82
+ conn.options.params_encoder = Faraday::FlatParamsEncoder if flat_params
70
83
  conn.response(:json, content_type: /json$/, parser_options: { symbolize_names: true })
71
84
  end
72
85
  end
@@ -113,6 +126,45 @@ module LunchMoney
113
126
  def clean_params(params)
114
127
  params.reject! { |_key, value| value.nil? }
115
128
  end
129
+
130
+ sig do
131
+ type_parameters(:T)
132
+ .params(
133
+ response: Faraday::Response,
134
+ block: T.proc.params(body: T.untyped).returns(T.type_parameter(:T)),
135
+ )
136
+ .returns(T.any(T.type_parameter(:T), LunchMoney::Errors))
137
+ end
138
+ def handle_api_response(response, &block)
139
+ api_errors = errors(response)
140
+ return api_errors if api_errors.present?
141
+
142
+ yield(response.body)
143
+ end
144
+
145
+ sig do
146
+ type_parameters(:T)
147
+ .params(
148
+ response: Faraday::Response,
149
+ collection_key: Symbol,
150
+ lazy: T::Boolean,
151
+ block: T.proc.params(item: T.untyped).returns(T.type_parameter(:T)),
152
+ )
153
+ .returns(T.any(T::Enumerable[T.type_parameter(:T)], T::Array[T.type_parameter(:T)], LunchMoney::Errors))
154
+ end
155
+ def handle_collection_response(response, collection_key, lazy: false, &block)
156
+ api_errors = errors(response)
157
+ return api_errors if api_errors.present?
158
+
159
+ collection = response.body[collection_key]
160
+ return [] unless collection
161
+
162
+ if lazy
163
+ collection.lazy.map(&block)
164
+ else
165
+ collection.map(&block)
166
+ end
167
+ end
116
168
  end
117
169
  end
118
170
  end
@@ -18,29 +18,28 @@ module LunchMoney
18
18
  params = clean_params({ start_date:, end_date:, currency: })
19
19
  response = get("budgets", query_params: params)
20
20
 
21
- api_errors = errors(response)
22
- return api_errors if api_errors.present?
21
+ handle_api_response(response) do |body|
22
+ body.map do |budget|
23
+ if budget[:data]
24
+ data_keys = budget[:data].keys
25
+ data_keys.each do |data_key|
26
+ budget[:data][data_key] = LunchMoney::Objects::Data.new(**budget[:data][data_key])
27
+ end
28
+ end
23
29
 
24
- response.body.map do |budget|
25
- if budget[:data]
26
- data_keys = budget[:data].keys
27
- data_keys.each do |data_key|
28
- budget[:data][data_key] = LunchMoney::Objects::Data.new(**budget[:data][data_key])
30
+ if budget[:config]
31
+ config_keys = budget[:config].keys
32
+ config_keys.each do |config_key|
33
+ budget[:config][config_key] = LunchMoney::Objects::Data.new(**budget[:config][config_key])
34
+ end
29
35
  end
30
- end
31
36
 
32
- if budget[:config]
33
- config_keys = budget[:config].keys
34
- config_keys.each do |config_key|
35
- budget[:config][config_key] = LunchMoney::Objects::Data.new(**budget[:config][config_key])
37
+ if budget[:recurring]
38
+ budget[:recurring][:list]&.map! { |recurring| LunchMoney::Objects::RecurringExpenseBase.new(**recurring) }
36
39
  end
37
- end
38
40
 
39
- if budget[:recurring]
40
- budget[:recurring][:list]&.map! { |recurring| LunchMoney::Objects::RecurringExpenseBase.new(**recurring) }
41
+ LunchMoney::Objects::Budget.new(**budget)
41
42
  end
42
-
43
- LunchMoney::Objects::Budget.new(**budget)
44
43
  end
45
44
  end
46
45
 
@@ -59,10 +58,9 @@ module LunchMoney
59
58
  params = clean_params({ start_date:, category_id:, amount:, currency: })
60
59
  response = put("budgets", params)
61
60
 
62
- api_errors = errors(response)
63
- return api_errors if api_errors.present?
64
-
65
- response.body
61
+ handle_api_response(response) do |body|
62
+ body
63
+ end
66
64
  end
67
65
 
68
66
  sig { params(start_date: String, category_id: Integer).returns(T.any(T::Boolean, LunchMoney::Errors)) }
@@ -74,10 +72,9 @@ module LunchMoney
74
72
 
75
73
  response = delete("budgets", query_params: params)
76
74
 
77
- api_errors = errors(response)
78
- return api_errors if api_errors.present?
79
-
80
- response.body
75
+ handle_api_response(response) do |body|
76
+ body
77
+ end
81
78
  end
82
79
  end
83
80
  end
@@ -7,7 +7,7 @@ module LunchMoney
7
7
  module Calls
8
8
  # https://lunchmoney.dev/#categories
9
9
  class Categories < LunchMoney::Calls::Base
10
- # Valid query parameter formets for categories
10
+ # Valid query parameter formats for categories
11
11
  VALID_FORMATS = T.let(
12
12
  [
13
13
  "flattened",
@@ -24,13 +24,12 @@ module LunchMoney
24
24
  def categories(format: nil)
25
25
  response = get("categories", query_params: categories_params(format:))
26
26
 
27
- api_errors = errors(response)
28
- return api_errors if api_errors.present?
27
+ handle_api_response(response) do |body|
28
+ body[:categories].map do |category|
29
+ category[:children]&.map! { |child_category| LunchMoney::Objects::Category.new(**child_category) }
29
30
 
30
- response.body[:categories].map do |category|
31
- category[:children]&.map! { |child_category| LunchMoney::Objects::Category.new(**child_category) }
32
-
33
- LunchMoney::Objects::Category.new(**category)
31
+ LunchMoney::Objects::Category.new(**category)
32
+ end
34
33
  end
35
34
  end
36
35
 
@@ -38,12 +37,11 @@ module LunchMoney
38
37
  def category(category_id)
39
38
  response = get("categories/#{category_id}")
40
39
 
41
- api_errors = errors(response)
42
- return api_errors if api_errors.present?
43
-
44
- response.body[:children]&.map! { |child_category| LunchMoney::Objects::ChildCategory.new(**child_category) }
40
+ handle_api_response(response) do |body|
41
+ body[:children]&.map! { |child_category| LunchMoney::Objects::ChildCategory.new(**child_category) }
45
42
 
46
- LunchMoney::Objects::Category.new(**response.body)
43
+ LunchMoney::Objects::Category.new(**body)
44
+ end
47
45
  end
48
46
 
49
47
  sig do
@@ -70,10 +68,9 @@ module LunchMoney
70
68
  })
71
69
  response = post("categories", params)
72
70
 
73
- api_errors = errors(response)
74
- return api_errors if api_errors.present?
75
-
76
- response.body
71
+ handle_api_response(response) do |body|
72
+ body
73
+ end
77
74
  end
78
75
 
79
76
  sig do
@@ -101,10 +98,9 @@ module LunchMoney
101
98
 
102
99
  response = post("categories/group", params)
103
100
 
104
- api_errors = errors(response)
105
- return api_errors if api_errors.present?
106
-
107
- response.body
101
+ handle_api_response(response) do |body|
102
+ body
103
+ end
108
104
  end
109
105
 
110
106
  sig do
@@ -132,10 +128,9 @@ module LunchMoney
132
128
  })
133
129
  response = put("categories/#{category_id}", params)
134
130
 
135
- api_errors = errors(response)
136
- return api_errors if api_errors.present?
137
-
138
- response.body
131
+ handle_api_response(response) do |body|
132
+ body
133
+ end
139
134
  end
140
135
 
141
136
  sig do
@@ -153,30 +148,27 @@ module LunchMoney
153
148
 
154
149
  response = post("categories/group/#{group_id}/add", params)
155
150
 
156
- api_errors = errors(response)
157
- return api_errors if api_errors.present?
158
-
159
- LunchMoney::Objects::Category.new(**response.body)
151
+ handle_api_response(response) do |body|
152
+ LunchMoney::Objects::Category.new(**body)
153
+ end
160
154
  end
161
155
 
162
156
  sig { params(category_id: Integer).returns(T.any(T::Boolean, LunchMoney::Errors)) }
163
157
  def delete_category(category_id)
164
158
  response = delete("categories/#{category_id}")
165
159
 
166
- api_errors = errors(response)
167
- return api_errors if api_errors.present?
168
-
169
- response.body
160
+ handle_api_response(response) do |body|
161
+ body
162
+ end
170
163
  end
171
164
 
172
165
  sig { params(category_id: Integer).returns(T.any(T::Boolean, LunchMoney::Errors)) }
173
166
  def force_delete_category(category_id)
174
167
  response = delete("categories/#{category_id}/force")
175
168
 
176
- api_errors = errors(response)
177
- return api_errors if api_errors.present?
178
-
179
- response.body
169
+ handle_api_response(response) do |body|
170
+ body
171
+ end
180
172
  end
181
173
 
182
174
  private
@@ -11,11 +11,10 @@ module LunchMoney
11
11
  def crypto
12
12
  response = get("crypto")
13
13
 
14
- api_errors = errors(response)
15
- return api_errors if api_errors.present?
16
-
17
- response.body[:crypto].map do |crypto|
18
- LunchMoney::Objects::Crypto.new(**crypto)
14
+ handle_api_response(response) do |body|
15
+ body[:crypto].map do |crypto|
16
+ LunchMoney::Objects::Crypto.new(**crypto)
17
+ end
19
18
  end
20
19
  end
21
20
 
@@ -40,10 +39,9 @@ module LunchMoney
40
39
 
41
40
  response = put("crypto/manual/#{crypto_id}", params)
42
41
 
43
- api_errors = errors(response)
44
- return api_errors if api_errors.present?
45
-
46
- LunchMoney::Objects::CryptoBase.new(**response.body)
42
+ handle_api_response(response) do |body|
43
+ LunchMoney::Objects::CryptoBase.new(**body)
44
+ end
47
45
  end
48
46
  end
49
47
  end
@@ -11,11 +11,10 @@ module LunchMoney
11
11
  def plaid_accounts
12
12
  response = get("plaid_accounts")
13
13
 
14
- api_errors = errors(response)
15
- return api_errors if api_errors.present?
16
-
17
- response.body[:plaid_accounts].map do |plaid_account|
18
- LunchMoney::Objects::PlaidAccount.new(**plaid_account)
14
+ handle_api_response(response) do |body|
15
+ body[:plaid_accounts].map do |plaid_account|
16
+ LunchMoney::Objects::PlaidAccount.new(**plaid_account)
17
+ end
19
18
  end
20
19
  end
21
20
 
@@ -30,10 +29,9 @@ module LunchMoney
30
29
  params = clean_params({ start_date:, end_date:, plaid_account_id: })
31
30
  response = post("plaid_accounts/fetch", params)
32
31
 
33
- api_errors = errors(response)
34
- return api_errors if api_errors.present?
35
-
36
- response.body
32
+ handle_api_response(response) do |body|
33
+ body
34
+ end
37
35
  end
38
36
  end
39
37
  end
@@ -17,11 +17,10 @@ module LunchMoney
17
17
  params = clean_params({ start_date:, end_date: })
18
18
  response = get("recurring_expenses", query_params: params)
19
19
 
20
- api_errors = errors(response)
21
- return api_errors if api_errors.present?
22
-
23
- response.body[:recurring_expenses].map do |recurring_expense|
24
- LunchMoney::Objects::RecurringExpense.new(**recurring_expense)
20
+ handle_api_response(response) do |body|
21
+ body[:recurring_expenses].map do |recurring_expense|
22
+ LunchMoney::Objects::RecurringExpense.new(**recurring_expense)
23
+ end
25
24
  end
26
25
  end
27
26
  end
@@ -11,10 +11,9 @@ module LunchMoney
11
11
  def tags
12
12
  response = get("tags")
13
13
 
14
- api_errors = errors(response)
15
- return api_errors if api_errors.present?
16
-
17
- response.body.map { |tag| LunchMoney::Objects::Tag.new(**tag) }
14
+ handle_api_response(response) do |body|
15
+ body.map { |tag| LunchMoney::Objects::Tag.new(**tag) }
16
+ end
18
17
  end
19
18
  end
20
19
  end