api_wrapper 0.1.6

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 (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