api_wrapper 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +7 -0
  3. data/CHANGELOG.md +18 -0
  4. data/CODE_OF_CONDUCT.md +84 -0
  5. data/Gemfile +22 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +153 -0
  8. data/Rakefile +31 -0
  9. data/api_wrapper.gemspec +34 -0
  10. data/doc/ApiWrapper/ApiManager.html +807 -0
  11. data/doc/ApiWrapper/Cache/CachePolicy.html +907 -0
  12. data/doc/ApiWrapper/Cache/CacheStore.html +674 -0
  13. data/doc/ApiWrapper/Cache.html +117 -0
  14. data/doc/ApiWrapper/Configuration.html +366 -0
  15. data/doc/ApiWrapper/HttpClient/BaseClient.html +349 -0
  16. data/doc/ApiWrapper/HttpClient/FaradayClient.html +299 -0
  17. data/doc/ApiWrapper/HttpClient.html +117 -0
  18. data/doc/ApiWrapper.html +543 -0
  19. data/doc/_index.html +195 -0
  20. data/doc/class_list.html +54 -0
  21. data/doc/css/common.css +1 -0
  22. data/doc/css/full_list.css +58 -0
  23. data/doc/css/style.css +503 -0
  24. data/doc/file.README.html +248 -0
  25. data/doc/file_list.html +59 -0
  26. data/doc/frames.html +22 -0
  27. data/doc/index.html +248 -0
  28. data/doc/js/app.js +344 -0
  29. data/doc/js/full_list.js +242 -0
  30. data/doc/js/jquery.js +4 -0
  31. data/doc/method_list.html +286 -0
  32. data/doc/top-level-namespace.html +110 -0
  33. data/lib/api_wrapper/api_manager.rb +86 -0
  34. data/lib/api_wrapper/cache/README.md +78 -0
  35. data/lib/api_wrapper/cache/cache_policy.rb +115 -0
  36. data/lib/api_wrapper/cache/cache_store.rb +84 -0
  37. data/lib/api_wrapper/cache/redis_cache_store.rb +3 -0
  38. data/lib/api_wrapper/http_client/base_client.rb +26 -0
  39. data/lib/api_wrapper/http_client/faraday_client.rb +81 -0
  40. data/lib/api_wrapper/version.rb +5 -0
  41. data/lib/api_wrapper.rb +83 -0
  42. metadata +121 -0
@@ -0,0 +1,110 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.9.37
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+
41
+
42
+ <span class="title">Top Level Namespace</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Top Level Namespace
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ </div>
80
+
81
+ <h2>Defined Under Namespace</h2>
82
+ <p class="children">
83
+
84
+
85
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="ApiWrapper.html" title="ApiWrapper (module)">ApiWrapper</a></span>
86
+
87
+
88
+
89
+
90
+ </p>
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ </div>
101
+
102
+ <div id="footer">
103
+ Generated on Mon Sep 16 23:22:30 2024 by
104
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
+ 0.9.37 (ruby-3.2.2).
106
+ </div>
107
+
108
+ </div>
109
+ </body>
110
+ </html>
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require_relative 'http_client/faraday_client'
5
+ require_relative 'cache/cache_policy'
6
+ require_relative 'cache/cache_store'
7
+
8
+ module ApiWrapper
9
+ # ApiManager is responsible for managing API interactions, including fetching data from endpoints
10
+ # and handling caching behavior.
11
+ #
12
+ # It supports configuration of caching policies, including specifying endpoints that should bypass
13
+ # the cache and setting custom TTL (time-to-live) values for individual endpoints. The class
14
+ # uses a Faraday client for making HTTP requests and integrates with a cache store for storing
15
+ # and retrieving cached data.
16
+ #
17
+ # Usage:
18
+ # api_manager = ApiWrapper::ApiManager.new('path/to/api_configuration.yml')
19
+ #
20
+ # response = api_manager.fetch_data('endpoint_key')
21
+ # puts response.body
22
+ #
23
+ # @attr_reader [Hash] endpoints The endpoints configuration loaded from the API configuration file.
24
+ # @attr_reader [String] base_url The base URL for API requests.
25
+ # @attr_reader [CachePolicy] cache_policy The cache policy used for caching data.
26
+ # @attr_reader [FaradayClient] client The Faraday client used for making HTTP requests.
27
+ class ApiManager
28
+ # Initializes a new instance of the ApiManager class.
29
+ #
30
+ # @param api_configuration_path [String] Path to the API configuration file.
31
+ # @param cache_store [CacheStore, RedisCacheStore, nil] The cache store to use for caching.
32
+ # Defaults to in-memory cache if nil.
33
+ def initialize(api_configuration_path, cache_store: nil)
34
+ load_api_configuration(api_configuration_path)
35
+
36
+ # Initialize cache policy
37
+ @cache_policy = ApiWrapper::Cache::CachePolicy.new(cache_store || ApiWrapper::Cache::CacheStore.new)
38
+
39
+ # Configure cache policy
40
+ configure_cache_policy
41
+
42
+ # Initialize Faraday client
43
+ @client = ApiWrapper::HttpClient::FaradayClient.new(@base_url, @cache_policy)
44
+ end
45
+
46
+ # Fetches data from the specified API endpoint.
47
+ #
48
+ # @param endpoint_key [String] The key of the API endpoint to fetch data from.
49
+ # @param force_refresh [Boolean] Whether to force refresh the data, bypassing the cache.
50
+ # @return [Faraday::Response] The response object containing the fetched data.
51
+ # @raise [ArgumentError] If the provided endpoint key is invalid.
52
+ def fetch_data(endpoint_key, force_refresh: false)
53
+ endpoint = @endpoints[endpoint_key]
54
+ raise ArgumentError, "Invalid endpoint key: #{endpoint_key}" unless endpoint
55
+
56
+ @cache_policy.fetch(endpoint['path'], force_refresh:) do
57
+ @client.get(endpoint['path'])
58
+ end
59
+ end
60
+
61
+ attr_reader :endpoints, :base_url
62
+
63
+ private
64
+
65
+ # Loads the API configuration from the configuration file.
66
+ #
67
+ # @raise [RuntimeError] If the configuration file is missing or has syntax errors.
68
+ def load_api_configuration(api_configuration_path)
69
+ yaml_content = YAML.load_file(api_configuration_path)
70
+ @endpoints = yaml_content['apis']
71
+ @base_url = yaml_content['base_url']
72
+ rescue Errno::ENOENT => e
73
+ raise "Configuration file not found: #{e.message}"
74
+ rescue Psych::SyntaxError => e
75
+ raise "YAML syntax error: #{e.message}"
76
+ end
77
+
78
+ # Configures the cache policy with settings from the configuration file.
79
+ def configure_cache_policy
80
+ @endpoints.each_value do |endpoint|
81
+ @cache_policy.add_no_cache_endpoint(endpoint['path']) if endpoint['no_cache']
82
+ @cache_policy.add_custom_ttl(endpoint['path'], endpoint['ttl']) if endpoint['ttl']
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,78 @@
1
+ <!-- Note: This readme is simply moved from nse_data, so reflect on this doc for any minor mistakes. -->
2
+ # Caching Mechanism Usage and Customization
3
+
4
+ ## Overview
5
+
6
+ The **ApiWrapper** gem includes a flexible caching mechanism to improve performance and reduce redundant API calls. This documentation provides an overview of how to use and customize the caching mechanism.
7
+
8
+ ## Cache Policy
9
+
10
+ The `CachePolicy` class is responsible for managing caching behavior, including setting global TTLs, custom TTLs for specific endpoints, and controlling which endpoints should bypass the cache.
11
+
12
+ ### Initializing CachePolicy
13
+
14
+ To use caching, you need to initialize the `CachePolicy` with a cache store and optional global TTL:
15
+
16
+ ```ruby
17
+ require 'api_wrapper/cache/cache_policy'
18
+ require 'api_wrapper/cache/cache_store'
19
+
20
+ # Initialize the cache store (e.g., in-memory cache store)
21
+ cache_store = ApiWrapper::Cache::CacheStore.new
22
+
23
+ # Initialize the CachePolicy with the cache store and a global TTL of 300 seconds (5 minutes)
24
+ cache_policy = ApiWrapper::Cache::CachePolicy.new(cache_store, global_ttl: 300)
25
+ ```
26
+
27
+ ### Configuring Cache Policy
28
+ #### Adding No-Cache Endpoints
29
+ You can specify endpoints that should bypass the cache:
30
+ ```ruby
31
+ # Add an endpoint to the no-cache list
32
+ cache_policy.add_no_cache_endpoint('/no-cache')
33
+ ```
34
+
35
+ #### Adding Custom TTLs
36
+ You can define custom TTLs for specific endpoints:
37
+
38
+ ```ruby
39
+ # Set a custom TTL of 600 seconds (10 minutes) for a specific endpoint
40
+ cache_policy.add_custom_ttl('/custom-ttl', ttl: 600)
41
+ ```
42
+
43
+ #### Fetching Data with Cache
44
+ Use the fetch method to retrieve data with caching applied:
45
+
46
+ ```ruby
47
+ # Fetch data for an endpoint with optional cache
48
+ data = cache_policy.fetch('/some-endpoint') do
49
+ # The block should fetch fresh data if cache is not used or is stale
50
+ # e.g., perform an API call or other data retrieval operation
51
+ Faraday::Response.new(body: 'fresh data')
52
+ end
53
+ ```
54
+ ## Custom Cache Stores
55
+ You can extend the caching mechanism to support different types of cache stores. Implement a custom cache store by inheriting from the CacheStore base class and overriding the read and write methods.
56
+
57
+ ### Example Custom Cache Store
58
+ ```ruby
59
+ class CustomCacheStore < ApiWrapper::Cache::CacheStore
60
+ def read(key)
61
+ # Implement custom read logic
62
+ end
63
+
64
+ def write(key, value, ttl)
65
+ # Implement custom write logic
66
+ end
67
+ end
68
+ ```
69
+ ### Using a Custom Cache Store
70
+ To use a custom cache store, initialize CachePolicy with an instance of your custom cache store:
71
+
72
+ ```ruby
73
+ # Initialize the custom cache store
74
+ custom_cache_store = CustomCacheStore.new
75
+
76
+ # Initialize CachePolicy with the custom cache store
77
+ cache_policy = ApiWrapper::Cache::CachePolicy.new(custom_cache_store, global_ttl: 300)
78
+ ```
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiWrapper
4
+ module Cache
5
+ # CachePolicy manages caching behavior, including cache storage and time-to-live (TTL) settings.
6
+ #
7
+ # It allows setting global TTLs, custom TTLs for specific endpoints, and controlling which
8
+ # endpoints should not use the cache.
9
+ #
10
+ # @attr_reader [CacheStore] cache_store The cache store used for storing cached data.
11
+ class CachePolicy
12
+ attr_reader :cache_store
13
+
14
+ # Initializes the CachePolicy with a cache store and global TTL.
15
+ #
16
+ # @param cache_store [CacheStore, RedisCacheStore] The cache store to use for caching.
17
+ # @param global_ttl [Integer] The default TTL (in seconds) for caching.
18
+ def initialize(cache_store, global_ttl = 300)
19
+ @cache_store = cache_store
20
+ @global_ttl = global_ttl
21
+ @custom_ttls = {}
22
+ @no_cache_endpoints = []
23
+ end
24
+
25
+ # Adds an endpoint that should bypass the cache.
26
+ #
27
+ # @param endpoint [String] The endpoint to exclude from caching.
28
+ def add_no_cache_endpoint(endpoint)
29
+ @no_cache_endpoints << endpoint
30
+ end
31
+
32
+ # Adds a custom TTL for a specific endpoint.
33
+ #
34
+ # @param endpoint [String] The endpoint to apply a custom TTL to.
35
+ # @param ttl [Integer] The custom TTL value in seconds.
36
+ def add_custom_ttl(endpoint, ttl = 300)
37
+ @custom_ttls[endpoint] = ttl
38
+ end
39
+
40
+ # Returns the TTL for a specific endpoint. Defaults to the global TTL if no custom TTL is set.
41
+ #
42
+ # @param endpoint [String] The endpoint to fetch the TTL for.
43
+ # @return [Integer] The TTL in seconds.
44
+ def ttl_for(endpoint)
45
+ @custom_ttls.fetch(endpoint, @global_ttl)
46
+ end
47
+
48
+ # Determines if caching should be used for the given endpoint.
49
+ #
50
+ # @param endpoint [String] The endpoint to check.
51
+ # @return [Boolean] True if caching is enabled for the endpoint, false otherwise.
52
+ def use_cache?(endpoint)
53
+ !@no_cache_endpoints.include?(endpoint)
54
+ end
55
+
56
+ # Fetches the data for the given endpoint, using cache if applicable.
57
+ #
58
+ # @param endpoint [String] The endpoint to fetch data for.
59
+ # @param force_refresh [Boolean] Whether to force refresh the data, bypassing the cache.
60
+ # @yield The block that fetches fresh data if cache is not used or is stale.
61
+ # @return [Object] The data fetched from cache or fresh data.
62
+ def fetch(endpoint, force_refresh: false, &block)
63
+ if force_refresh || !use_cache?(endpoint)
64
+ fetch_fresh_data(endpoint, &block)
65
+ else
66
+ fetch_cached_or_fresh_data(endpoint, &block)
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ # Fetches fresh data and writes it to the cache if applicable.
73
+ #
74
+ # @param endpoint [String] The endpoint to fetch fresh data for.
75
+ # @yield The block that fetches fresh data.
76
+ # @return [Object] The fresh data.
77
+ def fetch_fresh_data(endpoint)
78
+ fresh_data = yield
79
+ cache_fresh_data(endpoint, fresh_data)
80
+ fresh_data
81
+ end
82
+
83
+ # Fetches cached data or fresh data if not available in the cache.
84
+ #
85
+ # @param endpoint [String] The endpoint to fetch data for.
86
+ # @yield The block that fetches fresh data if cache is not used or is stale.
87
+ # @return [Object] The cached or fresh data.
88
+ def fetch_cached_or_fresh_data(endpoint, &block)
89
+ cached_data = @cache_store.read(endpoint)
90
+ if cached_data
91
+ Faraday::Response.new(body: cached_data)
92
+ else
93
+ fetch_fresh_data(endpoint, &block)
94
+ end
95
+ end
96
+
97
+ # Writes fresh data to the cache.
98
+ #
99
+ # @param endpoint [String] The endpoint for which to store the data.
100
+ # @param fresh_data [Object] The data to be stored in the cache.
101
+ def cache_fresh_data(endpoint, fresh_data)
102
+ ttl = determine_ttl(endpoint)
103
+ @cache_store.write(endpoint, fresh_data.body, ttl) if fresh_data.is_a?(Faraday::Response)
104
+ end
105
+
106
+ # Determines the TTL value for the given endpoint.
107
+ #
108
+ # @param endpoint [String] The endpoint to fetch the TTL for.
109
+ # @return [Integer] The TTL value in seconds.
110
+ def determine_ttl(endpoint)
111
+ @custom_ttls.fetch(endpoint, @global_ttl)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiWrapper
4
+ module Cache
5
+ # CacheStore class provides an in-memory caching mechanism.
6
+ class CacheStore
7
+ def initialize
8
+ @store = {}
9
+ end
10
+
11
+ # Retrieves the cached data for the given key, or fetches fresh data if not cached or expired.
12
+ #
13
+ # @param key [String] The cache key.
14
+ # @param ttl [Integer] The time-to-live in seconds.
15
+ # @yield Fetches fresh data if cache is expired or not present.
16
+ # @return [Object] The cached data or the result of the block if not cached or expired.
17
+ def fetch(key, ttl)
18
+ if cached?(key, ttl)
19
+ @store[key][:data]
20
+ else
21
+ fresh_data = yield
22
+ store(key, fresh_data, ttl)
23
+ fresh_data
24
+ end
25
+ end
26
+
27
+ # Reads data from the cache.
28
+ #
29
+ # @param key [String] The cache key.
30
+ # @return [Object, nil] The cached data, or nil if not present.
31
+ def read(key)
32
+ cached?(key) ? @store[key][:data] : nil
33
+ end
34
+
35
+ # Writes data to the cache with an expiration time.
36
+ #
37
+ # @param key [String] The cache key.
38
+ # @param data [Object] The data to cache.
39
+ # @param ttl [Integer] The time-to-live in seconds.
40
+ def write(key, data, ttl)
41
+ store(key, data, ttl)
42
+ end
43
+
44
+ # Deletes data from the cache.
45
+ #
46
+ # @param key [String] The cache key.
47
+ def delete(key)
48
+ @store.delete(key)
49
+ end
50
+
51
+ private
52
+
53
+ # Checks if the data for the given key is cached and not expired.
54
+ #
55
+ # @param key [String] The cache key.
56
+ # @param ttl [Integer] The time-to-live in seconds.
57
+ # @return [Boolean] Whether the data is cached and valid.
58
+ def cached?(key, ttl = nil)
59
+ return false unless @store.key?(key)
60
+
61
+ !expired?(key, ttl)
62
+ end
63
+
64
+ # Checks if the cached data for the given key has expired.
65
+ #
66
+ # @param key [String] The cache key.
67
+ # @param ttl [Integer] The time-to-live in seconds.
68
+ # @return [Boolean] Whether the cached data has expired.
69
+ def expired?(key, ttl)
70
+ stored_time = @store[key][:timestamp]
71
+ ttl && (Time.now - stored_time) >= ttl
72
+ end
73
+
74
+ # Stores the data in the cache.
75
+ #
76
+ # @param key [String] The cache key.
77
+ # @param data [Object] The data to cache.
78
+ # @param ttl [Integer] The time-to-live in seconds.
79
+ def store(key, data, ttl)
80
+ @store[key] = { data:, timestamp: Time.now, ttl: }
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TODO: Implement in near future :)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiWrapper
4
+ module HttpClient
5
+ # Base class for HTTP clients
6
+ class BaseClient
7
+ # Initializes a new instance of the BaseClient class.
8
+ #
9
+ # @param base_url [String] The base URL for the HTTP client.
10
+ def initialize(base_url, cache_policy)
11
+ @base_url = base_url
12
+ @cache_policy = cache_policy
13
+ end
14
+
15
+ # Sends a GET request to the specified endpoint.
16
+ #
17
+ # @param endpoint [String] The endpoint to send the GET request to.
18
+ # @raise [NotImplementedError] If the method is not implemented by subclasses.
19
+ def get(endpoint)
20
+ raise NotImplementedError, 'Subclasses must implement the get method'
21
+ end
22
+
23
+ # TODO: Other HTTP methods like post, put, delete can be added here if needed
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday-http-cache'
5
+ require 'json'
6
+ require_relative 'base_client'
7
+
8
+ module ApiWrapper
9
+ module HttpClient
10
+ # FaradayClient class is responsible for making HTTP requests using Faraday.
11
+ class FaradayClient < BaseClient
12
+ # Sends a GET request to the specified endpoint.
13
+ #
14
+ # @param endpoint [String] The endpoint to send the request to.
15
+ # @param force_refresh [Boolean] Whether to force a cache refresh.
16
+ # @return [Faraday::Response] The response object.
17
+ def get(endpoint, force_refresh: false)
18
+ # Use the cache policy to determine whether to fetch from cache or refresh.
19
+ @cache_policy.fetch(endpoint, force_refresh:) do
20
+ handle_connection(endpoint) do |connection|
21
+ connection.get(endpoint)
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ # Handles the connection to the base URL.
29
+ #
30
+ # @yield [connection] The Faraday connection object.
31
+ # @yieldparam connection [Faraday::Connection] The Faraday connection object.
32
+ # @return [Object] The result of the block execution.
33
+ # @raise [Faraday::Error] If there is an error with the connection.
34
+ def handle_connection(endpoint)
35
+ connection = build_faraday_connection(endpoint)
36
+ yield(connection)
37
+ rescue Faraday::Error => e
38
+ handle_faraday_error(e)
39
+ end
40
+
41
+ # Builds the Faraday connection with proper configuration.
42
+ #
43
+ # @param endpoint [String] The endpoint for the connection.
44
+ # @return [Faraday::Connection] The configured Faraday connection.
45
+ def build_faraday_connection(endpoint)
46
+ Faraday.new(url: @base_url) do |faraday|
47
+ configure_faraday(faraday)
48
+ apply_cache_policy(faraday, endpoint) if @cache_policy.use_cache?(endpoint)
49
+ faraday.adapter Faraday.default_adapter
50
+ end
51
+ end
52
+
53
+ # Configures Faraday with default headers and request options.
54
+ #
55
+ # @param faraday [Faraday::Connection] The Faraday connection object.
56
+ def configure_faraday(faraday)
57
+ faraday.request :json
58
+ faraday.headers['User-Agent'] = 'ApiWrapperClient/1.0'
59
+ faraday.response :json
60
+ faraday.headers['Accept'] = 'application/json'
61
+ end
62
+
63
+ # Applies the cache policy to the Faraday connection if caching is enabled.
64
+ #
65
+ # @param faraday [Faraday::Connection] The Faraday connection object.
66
+ # @param endpoint [String] The endpoint to be cached.
67
+ def apply_cache_policy(faraday, endpoint)
68
+ ttl = @cache_policy.ttl_for(endpoint)
69
+ faraday.use :http_cache, store: @cache_policy.cache_store, expire_after: ttl
70
+ end
71
+
72
+ # Handles any Faraday errors.
73
+ #
74
+ # @param error [Faraday::Error] The error raised by Faraday.
75
+ # @raise [StandardError] Raises the original error with full context.
76
+ def handle_faraday_error(error)
77
+ raise StandardError, "Faraday error: #{error.message}"
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiWrapper
4
+ VERSION = '0.1.6'
5
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'api_wrapper/version'
4
+ require_relative 'api_wrapper/api_manager'
5
+
6
+ # ApiWrapper provides a unified interface for managing API interactions and caching.
7
+ #
8
+ # It simplifies API management and caching with sensible defaults and customization options.
9
+ #
10
+ # Usage:
11
+ # # Fetch data using default configuration
12
+ # response = ApiWrapper.fetch_data('endpoint_key')
13
+ # puts response.body
14
+ #
15
+ # # Configure with custom settings
16
+ # ApiWrapper.configure do |config|
17
+ # config.api_configuration_path = 'custom/path/to/config.yml'
18
+ # config.cache_store = CustomCacheStore.new
19
+ # end
20
+ # response = ApiWrapper.fetch_data('endpoint_key')
21
+ # puts response.body
22
+ module ApiWrapper
23
+ # Configuration class for managing settings
24
+ class Configuration
25
+ attr_accessor :api_configuration_path, :cache_store
26
+
27
+ def initialize
28
+ @api_configuration_path = default_api_configuration_path
29
+ @cache_store = default_cache_store
30
+ end
31
+
32
+ private
33
+
34
+ # Default API configuration path
35
+ def default_api_configuration_path
36
+ ENV['API_CONFIGURATION_PATH'] || File.join(Dir.pwd, 'config', 'api_endpoints.yml')
37
+ end
38
+
39
+ # Default cache store
40
+ def default_cache_store
41
+ cache_type = ENV['CACHE_STORE_TYPE'] || 'in_memory'
42
+ case cache_type
43
+ when 'redis'
44
+ ApiWrapper::Cache::RedisCacheStore.new # TODO: implement RedisCacheStore later
45
+ else
46
+ ApiWrapper::Cache::CacheStore.new
47
+ end
48
+ end
49
+ end
50
+
51
+ class << self
52
+ # Accesses the configuration instance, initializing if needed
53
+ def configuration
54
+ @configuration ||= Configuration.new
55
+ end
56
+
57
+ # Configures ApiWrapper with a block
58
+ def configure
59
+ raise ArgumentError, 'Configuration block required' unless block_given?
60
+
61
+ yield(configuration)
62
+ reset_api_manager!
63
+ end
64
+
65
+ # Returns the singleton ApiManager instance
66
+ def api_manager
67
+ @api_manager ||= begin
68
+ config = configuration
69
+ ApiManager.new(config.api_configuration_path, cache_store: config.cache_store)
70
+ end
71
+ end
72
+
73
+ # Fetches data from the specified endpoint
74
+ def fetch_data(endpoint_key, force_refresh: false)
75
+ api_manager.fetch_data(endpoint_key, force_refresh:)
76
+ end
77
+
78
+ # Resets the ApiManager instance
79
+ def reset_api_manager!
80
+ @api_manager = nil
81
+ end
82
+ end
83
+ end