macaw_framework 1.3.22 → 1.4

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: 6884907c34b2719e0166f6a3c7fc5992e277126ec738a6a08e3fbaa71c427b0a
4
- data.tar.gz: 554b0671565902d395fa289ae970cb7b149f0b61f9dcebdfdd9947bc277807fa
3
+ metadata.gz: c18584ede9872a77b3976bd6685f99e4fb015082647e0dde824da268ce44b889
4
+ data.tar.gz: c36bf7deee533993408493517e2d8d46dd1a763276dafd3a8388b4a773c48af4
5
5
  SHA512:
6
- metadata.gz: bde5aa848d982a6cd112c9f8eb57b84c39aaf30463f0a21e8ce79dfee2b54bc6711598da4abd3c0287a32f502123a48715700c68f937118fe0cdbb2c045733cd
7
- data.tar.gz: 2248a9ea06a53f7602d366fdc2524a7c40866042102ec84fb6b1a724e9617175f2278bf2075fd6b9b7f9bc36626b64824f6cd0baee25ce6346f554bba9e76fd1
6
+ metadata.gz: 315cb13a54c7cdc82444747b1dd32da4a50dce5de756abd52a05a244eb2fe94b77bbe28aa0e56229a7a437d7be308118f67990779ad011fd965dec5001b31703
7
+ data.tar.gz: f8be92b1eb37fb9644f6bf8cb84ad02fa2ad0ba0da3244c888a5fd8b48e5adbfcda5a263f3bda73f29dfec6dd729bc5d72cbaa024e0fbe274aa988e5a025deaf
data/.rubocop.yml CHANGED
@@ -29,3 +29,9 @@ Metrics/ModuleLength:
29
29
 
30
30
  Naming/MemoizedInstanceVariableName:
31
31
  Enabled: false
32
+
33
+ Naming/MethodName:
34
+ Enabled: false
35
+
36
+ Style/ClassAndModuleChildren:
37
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -152,5 +152,8 @@
152
152
  - Fixing a bug where a HTTP call without client data broke the parser
153
153
  - Removing logs registering new HTTP connections to reduce log bloat
154
154
 
155
- ## [1.3.3]
155
+ ## [1.3.22]
156
156
  - Fixing error with tests on Ruby 3.4.x due to splash operator
157
+
158
+ ## [1.4]
159
+ - Add missing dependencies for Ruby 4.x
data/Gemfile CHANGED
@@ -4,11 +4,13 @@ source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
6
 
7
+ gem 'logger', '~> 1.7'
7
8
  gem 'openssl'
8
9
  gem 'prometheus-client', '~> 4.1'
9
10
 
10
11
  group :test do
11
12
  gem 'minitest', '~> 5.0'
13
+ gem 'ostruct', '~> 0.6.3'
12
14
  gem 'rake', '~> 13.0'
13
15
  gem 'rubocop', '~> 1.21'
14
16
  gem 'simplecov', '~> 0.21.2'
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # Main module for all Macaw classes
5
+ module MacawFramework; end
6
+
7
+ ##
8
+ # This singleton class allows to manually cache
9
+ # parameters and other data.
10
+ class MacawFramework::Cache
11
+ include Singleton
12
+
13
+ attr_accessor :invalidation_frequency
14
+
15
+ ##
16
+ # Write a value to Cache memory.
17
+ # Can be called statically or from an instance.
18
+ # @param {String} tag
19
+ # @param {Object} value
20
+ # @param {Integer} expires_in Defaults to 3600.
21
+ # @return nil
22
+ #
23
+ # @example
24
+ # MacawFramework::Cache.write("name", "Maria", expires_in: 7200)
25
+ def self.write(tag, value, expires_in: 3600)
26
+ MacawFramework::Cache.instance.write(tag, value, expires_in: expires_in)
27
+ end
28
+
29
+ ##
30
+ # Write a value to Cache memory.
31
+ # Can be called statically or from an instance.
32
+ # @param {String} tag
33
+ # @param {Object} value
34
+ # @param {Integer} expires_in Defaults to 3600.
35
+ # @return nil
36
+ #
37
+ # @example
38
+ # MacawFramework::Cache.write("name", "Maria", expires_in: 7200)
39
+ def write(tag, value, expires_in: 3600)
40
+ if read(tag).nil?
41
+ @mutex.synchronize do
42
+ @cache.store(tag, { value: value, expires_in: Time.now + expires_in })
43
+ end
44
+ else
45
+ @cache[tag][:value] = value
46
+ @cache[tag][:expires_in] = Time.now + expires_in
47
+ end
48
+ end
49
+
50
+ ##
51
+ # Read the value with the specified tag.
52
+ # Can be called statically or from an instance.
53
+ # @param {String} tag
54
+ # @return {String|nil}
55
+ #
56
+ # @example
57
+ # MacawFramework::Cache.read("name") # Maria
58
+ def self.read(tag) = MacawFramework::Cache.instance.read(tag)
59
+
60
+ ##
61
+ # Read the value with the specified tag.
62
+ # Can be called statically or from an instance.
63
+ # @param {String} tag
64
+ # @return {String|nil}
65
+ #
66
+ # @example
67
+ # MacawFramework::Cache.read("name") # Maria
68
+ def read(tag) = @cache.dig(tag, :value)
69
+
70
+ private
71
+
72
+ def initialize
73
+ @cache = {}
74
+ @mutex = Mutex.new
75
+ @invalidation_frequency = 60
76
+ invalidate_cache
77
+ end
78
+
79
+ def invalidate_cache
80
+ @invalidator = Thread.new(&method(:invalidation_process))
81
+ end
82
+
83
+ def invalidation_process
84
+ loop do
85
+ sleep @invalidation_frequency
86
+ @mutex.synchronize do
87
+ @cache.delete_if { |_, v| v[:expires_in] < Time.now }
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'errors/endpoint_not_mapped_error'
4
+ require_relative 'middlewares/prometheus_middleware'
5
+ require_relative 'data_filters/request_data_filtering'
6
+ require_relative 'middlewares/memory_invalidation_middleware'
7
+ require_relative 'core/cron_runner'
8
+ require_relative 'core/thread_server'
9
+ require_relative 'version'
10
+ require 'prometheus/client'
11
+ require 'securerandom'
12
+ require 'singleton'
13
+ require 'pathname'
14
+ require 'logger'
15
+ require 'socket'
16
+ require 'json'
17
+
18
+ ##
19
+ # Main module for all Macaw classes
20
+ module MacawFramework; end
21
+
22
+ ##
23
+ # Class responsible for creating endpoints and
24
+ # starting the web server.
25
+ class MacawFramework::Macaw
26
+ attr_reader :routes, :macaw_log, :config, :jobs, :cached_methods, :secure_header, :session
27
+ attr_accessor :port, :bind, :threads
28
+
29
+ ##
30
+ # Initialize Macaw Class
31
+ # @param {Logger} custom_log
32
+ # @param {ThreadServer} server
33
+ # @param {String?} dir
34
+ def initialize(custom_log: Logger.new($stdout), server: ThreadServer, dir: nil)
35
+ apply_options(custom_log)
36
+ create_endpoint_public_files(dir)
37
+ setup_default_configs
38
+ @server_class = server
39
+ end
40
+
41
+ ##
42
+ # Creates a GET endpoint associated
43
+ # with the respective path.
44
+ # @param {String} path
45
+ # @param {Proc} block
46
+ #
47
+ # @example
48
+ # macaw = MacawFramework::Macaw.new
49
+ # macaw.get("/hello") do |context|
50
+ # return "Hello World!", 200, { "Content-Type" => "text/plain" }
51
+ # end
52
+ ##
53
+ def get(path, cache: [], &block)
54
+ map_new_endpoint('get', cache, path, &block)
55
+ end
56
+
57
+ ##
58
+ # Creates a POST endpoint associated
59
+ # with the respective path.
60
+ # @param {String} path
61
+ # @param {Boolean} cache
62
+ # @param {Proc} block
63
+ # @example
64
+ #
65
+ # macaw = MacawFramework::Macaw.new
66
+ # macaw.post("/hello") do |context|
67
+ # return "Hello World!", 200, { "Content-Type" => "text/plain" }
68
+ # end
69
+ ##
70
+ def post(path, cache: [], &block)
71
+ map_new_endpoint('post', cache, path, &block)
72
+ end
73
+
74
+ ##
75
+ # Creates a PUT endpoint associated
76
+ # with the respective path.
77
+ # @param {String} path
78
+ # @param {Proc} block
79
+ # @example
80
+ #
81
+ # macaw = MacawFramework::Macaw.new
82
+ # macaw.put("/hello") do |context|
83
+ # return "Hello World!", 200, { "Content-Type" => "text/plain" }
84
+ # end
85
+ ##
86
+ def put(path, cache: [], &block)
87
+ map_new_endpoint('put', cache, path, &block)
88
+ end
89
+
90
+ ##
91
+ # Creates a PATCH endpoint associated
92
+ # with the respective path.
93
+ # @param {String} path
94
+ # @param {Proc} block
95
+ # @example
96
+ #
97
+ # macaw = MacawFramework::Macaw.new
98
+ # macaw.patch("/hello") do |context|
99
+ # return "Hello World!", 200, { "Content-Type" => "text/plain" }
100
+ # end
101
+ ##
102
+ def patch(path, cache: [], &block)
103
+ map_new_endpoint('patch', cache, path, &block)
104
+ end
105
+
106
+ ##
107
+ # Creates a DELETE endpoint associated
108
+ # with the respective path.
109
+ # @param {String} path
110
+ # @param {Proc} block
111
+ # @example
112
+ #
113
+ # macaw = MacawFramework::Macaw.new
114
+ # macaw.delete("/hello") do |context|
115
+ # return "Hello World!", 200, { "Content-Type" => "text/plain" }
116
+ # end
117
+ ##
118
+ def delete(path, cache: [], &block)
119
+ map_new_endpoint('delete', cache, path, &block)
120
+ end
121
+
122
+ ##
123
+ # Spawn and start a thread running the defined periodic job.
124
+ # @param {Integer} interval
125
+ # @param {Integer?} start_delay
126
+ # @param {String} job_name
127
+ # @param {Proc} block
128
+ # @example
129
+ #
130
+ # macaw = MacawFramework::Macaw.new
131
+ # macaw.setup_job(interval: 60, start_delay: 60, job_name: "job 1") do
132
+ # puts "I'm a periodic job that runs every minute"
133
+ # end
134
+ ##
135
+ def setup_job(interval: 60, start_delay: 0, job_name: "job_#{SecureRandom.uuid}", &block)
136
+ @cron_runner ||= CronRunner.new(self)
137
+ @jobs ||= []
138
+ @cron_runner.start_cron_job_thread(interval, start_delay, job_name, &block)
139
+ @jobs << job_name
140
+ end
141
+
142
+ ##
143
+ # Starts the web server
144
+ def start!
145
+ if @macaw_log.nil?
146
+ puts('---------------------------------')
147
+ puts("Starting server at port #{@port}")
148
+ puts("Number of threads: #{@threads}")
149
+ puts('---------------------------------')
150
+ else
151
+ @macaw_log.info('---------------------------------')
152
+ @macaw_log.info("Starting server at port #{@port}")
153
+ @macaw_log.info("Number of threads: #{@threads}")
154
+ @macaw_log.info('---------------------------------')
155
+ end
156
+ @server = @server_class.new(self, @endpoints_to_cache, @cache, @prometheus, @prometheus_middleware)
157
+ server_loop(@server)
158
+ rescue Interrupt
159
+ if @macaw_log.nil?
160
+ puts('Stopping server')
161
+ @server.shutdown
162
+ puts('Macaw stop flying for some seeds...')
163
+ else
164
+ @macaw_log.info('Stopping server')
165
+ @server.shutdown
166
+ @macaw_log.info('Macaw stop flying for some seeds...')
167
+ end
168
+ end
169
+
170
+ ##
171
+ # This method is intended to start the framework
172
+ # without an web server. This can be useful when
173
+ # you just want to keep cron jobs running, without
174
+ # mapping any HTTP endpoints.
175
+ def start_without_server!
176
+ @macaw_log.nil? ? puts('Application starting') : @macaw_log.info('Application starting')
177
+ loop { sleep(3600) }
178
+ rescue Interrupt
179
+ @macaw_log.nil? ? puts('Macaw stop flying for some seeds.') : @macaw_log.info('Macaw stop flying for some seeds.')
180
+ end
181
+
182
+ private
183
+
184
+ def setup_default_configs
185
+ @port ||= 8080
186
+ @bind ||= 'localhost'
187
+ @config ||= nil
188
+ @threads ||= 200
189
+ @endpoints_to_cache = []
190
+ @prometheus ||= nil
191
+ @prometheus_middleware ||= nil
192
+ end
193
+
194
+ def apply_options(custom_log)
195
+ setup_basic_config(custom_log)
196
+ setup_session
197
+ setup_cache
198
+ setup_prometheus
199
+ rescue StandardError => e
200
+ @macaw_log&.warn(e.message)
201
+ end
202
+
203
+ def setup_cache
204
+ return if @config['macaw']['cache'].nil?
205
+
206
+ @cache = MemoryInvalidationMiddleware.new(@config['macaw']['cache']['cache_invalidation'].to_i || 3_600)
207
+ end
208
+
209
+ def setup_session
210
+ @session = false
211
+ return if @config['macaw']['session'].nil?
212
+
213
+ @session = true
214
+ @secure_header = @config['macaw']['session']['secure_header'] || 'X-Session-ID'
215
+ end
216
+
217
+ def setup_basic_config(custom_log)
218
+ @routes = []
219
+ @cached_methods = {}
220
+ @macaw_log ||= custom_log
221
+ @config = JSON.parse(File.read('application.json'))
222
+ @port = @config['macaw']['port'] || 8080
223
+ @bind = @config['macaw']['bind'] || 'localhost'
224
+ @threads = @config['macaw']['threads'] || 200
225
+ end
226
+
227
+ def setup_prometheus
228
+ return unless @config['macaw']['prometheus']
229
+
230
+ @prometheus = Prometheus::Client::Registry.new
231
+ @prometheus_middleware = PrometheusMiddleware.new
232
+ @prometheus_middleware&.configure_prometheus(@prometheus, @config, self)
233
+ end
234
+
235
+ def server_loop(server)
236
+ server.run
237
+ end
238
+
239
+ def map_new_endpoint(prefix, cache, path, &block)
240
+ @endpoints_to_cache << "#{prefix}.#{RequestDataFiltering.sanitize_method_name(path)}" unless cache.empty?
241
+ @cached_methods["#{prefix}.#{RequestDataFiltering.sanitize_method_name(path)}"] = cache unless cache.empty?
242
+ path_clean = RequestDataFiltering.extract_path(path)
243
+ slash = path[0] == '/' ? '' : '/'
244
+ @macaw_log&.info("Defining #{prefix.upcase} endpoint at #{slash}#{path}")
245
+ define_singleton_method("#{prefix}.#{path_clean}", block || lambda {
246
+ |context = { headers: {}, body: '', params: {} }|
247
+ })
248
+ @routes << "#{prefix}.#{path_clean}"
249
+ end
250
+
251
+ def get_files_public_folder(dir)
252
+ return [] if dir.nil?
253
+
254
+ folder_path = Pathname.new(File.expand_path('public', dir))
255
+ file_paths = folder_path.glob('**/*').select(&:file?)
256
+ file_paths.map { |path| "public/#{path.relative_path_from(folder_path)}" }
257
+ end
258
+
259
+ def create_endpoint_public_files(dir)
260
+ get_files_public_folder(dir).each do |file|
261
+ get(file) { |_context| return File.read(file).to_s, 200, {} }
262
+ end
263
+ end
264
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MacawFramework
4
- VERSION = '1.3.22'
4
+ VERSION = '1.4'
5
5
  end
@@ -1,348 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'macaw_framework/errors/endpoint_not_mapped_error'
4
- require_relative 'macaw_framework/middlewares/prometheus_middleware'
5
- require_relative 'macaw_framework/data_filters/request_data_filtering'
6
- require_relative 'macaw_framework/middlewares/memory_invalidation_middleware'
7
- require_relative 'macaw_framework/core/cron_runner'
8
- require_relative 'macaw_framework/core/thread_server'
9
- require_relative 'macaw_framework/version'
10
- require 'prometheus/client'
11
- require 'securerandom'
12
- require 'singleton'
13
- require 'pathname'
14
- require 'logger'
15
- require 'socket'
16
- require 'json'
3
+ ##
4
+ # Main module for all Macaw classes
5
+ module MacawFramework; end
17
6
 
18
- module MacawFramework
19
- ##
20
- # Class responsible for creating endpoints and
21
- # starting the web server.
22
- class Macaw
23
- attr_reader :routes, :macaw_log, :config, :jobs, :cached_methods, :secure_header, :session
24
- attr_accessor :port, :bind, :threads
25
-
26
- ##
27
- # Initialize Macaw Class
28
- # @param {Logger} custom_log
29
- # @param {ThreadServer} server
30
- # @param {String?} dir
31
- def initialize(custom_log: Logger.new($stdout), server: ThreadServer, dir: nil)
32
- apply_options(custom_log)
33
- create_endpoint_public_files(dir)
34
- setup_default_configs
35
- @server_class = server
36
- end
37
-
38
- ##
39
- # Creates a GET endpoint associated
40
- # with the respective path.
41
- # @param {String} path
42
- # @param {Proc} block
43
- #
44
- # @example
45
- # macaw = MacawFramework::Macaw.new
46
- # macaw.get("/hello") do |context|
47
- # return "Hello World!", 200, { "Content-Type" => "text/plain" }
48
- # end
49
- ##
50
- def get(path, cache: [], &block)
51
- map_new_endpoint('get', cache, path, &block)
52
- end
53
-
54
- ##
55
- # Creates a POST endpoint associated
56
- # with the respective path.
57
- # @param {String} path
58
- # @param {Boolean} cache
59
- # @param {Proc} block
60
- # @example
61
- #
62
- # macaw = MacawFramework::Macaw.new
63
- # macaw.post("/hello") do |context|
64
- # return "Hello World!", 200, { "Content-Type" => "text/plain" }
65
- # end
66
- ##
67
- def post(path, cache: [], &block)
68
- map_new_endpoint('post', cache, path, &block)
69
- end
70
-
71
- ##
72
- # Creates a PUT endpoint associated
73
- # with the respective path.
74
- # @param {String} path
75
- # @param {Proc} block
76
- # @example
77
- #
78
- # macaw = MacawFramework::Macaw.new
79
- # macaw.put("/hello") do |context|
80
- # return "Hello World!", 200, { "Content-Type" => "text/plain" }
81
- # end
82
- ##
83
- def put(path, cache: [], &block)
84
- map_new_endpoint('put', cache, path, &block)
85
- end
86
-
87
- ##
88
- # Creates a PATCH endpoint associated
89
- # with the respective path.
90
- # @param {String} path
91
- # @param {Proc} block
92
- # @example
93
- #
94
- # macaw = MacawFramework::Macaw.new
95
- # macaw.patch("/hello") do |context|
96
- # return "Hello World!", 200, { "Content-Type" => "text/plain" }
97
- # end
98
- ##
99
- def patch(path, cache: [], &block)
100
- map_new_endpoint('patch', cache, path, &block)
101
- end
102
-
103
- ##
104
- # Creates a DELETE endpoint associated
105
- # with the respective path.
106
- # @param {String} path
107
- # @param {Proc} block
108
- # @example
109
- #
110
- # macaw = MacawFramework::Macaw.new
111
- # macaw.delete("/hello") do |context|
112
- # return "Hello World!", 200, { "Content-Type" => "text/plain" }
113
- # end
114
- ##
115
- def delete(path, cache: [], &block)
116
- map_new_endpoint('delete', cache, path, &block)
117
- end
118
-
119
- ##
120
- # Spawn and start a thread running the defined periodic job.
121
- # @param {Integer} interval
122
- # @param {Integer?} start_delay
123
- # @param {String} job_name
124
- # @param {Proc} block
125
- # @example
126
- #
127
- # macaw = MacawFramework::Macaw.new
128
- # macaw.setup_job(interval: 60, start_delay: 60, job_name: "job 1") do
129
- # puts "I'm a periodic job that runs every minute"
130
- # end
131
- ##
132
- def setup_job(interval: 60, start_delay: 0, job_name: "job_#{SecureRandom.uuid}", &block)
133
- @cron_runner ||= CronRunner.new(self)
134
- @jobs ||= []
135
- @cron_runner.start_cron_job_thread(interval, start_delay, job_name, &block)
136
- @jobs << job_name
137
- end
138
-
139
- ##
140
- # Starts the web server
141
- def start!
142
- if @macaw_log.nil?
143
- puts('---------------------------------')
144
- puts("Starting server at port #{@port}")
145
- puts("Number of threads: #{@threads}")
146
- puts('---------------------------------')
147
- else
148
- @macaw_log.info('---------------------------------')
149
- @macaw_log.info("Starting server at port #{@port}")
150
- @macaw_log.info("Number of threads: #{@threads}")
151
- @macaw_log.info('---------------------------------')
152
- end
153
- @server = @server_class.new(self, @endpoints_to_cache, @cache, @prometheus, @prometheus_middleware)
154
- server_loop(@server)
155
- rescue Interrupt
156
- if @macaw_log.nil?
157
- puts('Stopping server')
158
- @server.shutdown
159
- puts('Macaw stop flying for some seeds...')
160
- else
161
- @macaw_log.info('Stopping server')
162
- @server.shutdown
163
- @macaw_log.info('Macaw stop flying for some seeds...')
164
- end
165
- end
166
-
167
- ##
168
- # This method is intended to start the framework
169
- # without an web server. This can be useful when
170
- # you just want to keep cron jobs running, without
171
- # mapping any HTTP endpoints.
172
- def start_without_server!
173
- @macaw_log.nil? ? puts('Application starting') : @macaw_log.info('Application starting')
174
- loop { sleep(3600) }
175
- rescue Interrupt
176
- @macaw_log.nil? ? puts('Macaw stop flying for some seeds.') : @macaw_log.info('Macaw stop flying for some seeds.')
177
- end
178
-
179
- private
180
-
181
- def setup_default_configs
182
- @port ||= 8080
183
- @bind ||= 'localhost'
184
- @config ||= nil
185
- @threads ||= 200
186
- @endpoints_to_cache = []
187
- @prometheus ||= nil
188
- @prometheus_middleware ||= nil
189
- end
190
-
191
- def apply_options(custom_log)
192
- setup_basic_config(custom_log)
193
- setup_session
194
- setup_cache
195
- setup_prometheus
196
- rescue StandardError => e
197
- @macaw_log&.warn(e.message)
198
- end
199
-
200
- def setup_cache
201
- return if @config['macaw']['cache'].nil?
202
-
203
- @cache = MemoryInvalidationMiddleware.new(@config['macaw']['cache']['cache_invalidation'].to_i || 3_600)
204
- end
205
-
206
- def setup_session
207
- @session = false
208
- return if @config['macaw']['session'].nil?
209
-
210
- @session = true
211
- @secure_header = @config['macaw']['session']['secure_header'] || 'X-Session-ID'
212
- end
213
-
214
- def setup_basic_config(custom_log)
215
- @routes = []
216
- @cached_methods = {}
217
- @macaw_log ||= custom_log
218
- @config = JSON.parse(File.read('application.json'))
219
- @port = @config['macaw']['port'] || 8080
220
- @bind = @config['macaw']['bind'] || 'localhost'
221
- @threads = @config['macaw']['threads'] || 200
222
- end
223
-
224
- def setup_prometheus
225
- return unless @config['macaw']['prometheus']
226
-
227
- @prometheus = Prometheus::Client::Registry.new
228
- @prometheus_middleware = PrometheusMiddleware.new
229
- @prometheus_middleware&.configure_prometheus(@prometheus, @config, self)
230
- end
231
-
232
- def server_loop(server)
233
- server.run
234
- end
235
-
236
- def map_new_endpoint(prefix, cache, path, &block)
237
- @endpoints_to_cache << "#{prefix}.#{RequestDataFiltering.sanitize_method_name(path)}" unless cache.empty?
238
- @cached_methods["#{prefix}.#{RequestDataFiltering.sanitize_method_name(path)}"] = cache unless cache.empty?
239
- path_clean = RequestDataFiltering.extract_path(path)
240
- slash = path[0] == '/' ? '' : '/'
241
- @macaw_log&.info("Defining #{prefix.upcase} endpoint at #{slash}#{path}")
242
- define_singleton_method("#{prefix}.#{path_clean}", block || lambda {
243
- |context = { headers: {}, body: '', params: {} }|
244
- })
245
- @routes << "#{prefix}.#{path_clean}"
246
- end
247
-
248
- def get_files_public_folder(dir)
249
- return [] if dir.nil?
250
-
251
- folder_path = Pathname.new(File.expand_path('public', dir))
252
- file_paths = folder_path.glob('**/*').select(&:file?)
253
- file_paths.map { |path| "public/#{path.relative_path_from(folder_path)}" }
254
- end
255
-
256
- def create_endpoint_public_files(dir)
257
- get_files_public_folder(dir).each do |file|
258
- get(file) { |_context| return File.read(file).to_s, 200, {} }
259
- end
260
- end
261
- end
262
-
263
- ##
264
- # This singleton class allows to manually cache
265
- # parameters and other data.
266
- class Cache
267
- include Singleton
268
-
269
- attr_accessor :invalidation_frequency
270
-
271
- ##
272
- # Write a value to Cache memory.
273
- # Can be called statically or from an instance.
274
- # @param {String} tag
275
- # @param {Object} value
276
- # @param {Integer} expires_in Defaults to 3600.
277
- # @return nil
278
- #
279
- # @example
280
- # MacawFramework::Cache.write("name", "Maria", expires_in: 7200)
281
- def self.write(tag, value, expires_in: 3600)
282
- MacawFramework::Cache.instance.write(tag, value, expires_in: expires_in)
283
- end
284
-
285
- ##
286
- # Write a value to Cache memory.
287
- # Can be called statically or from an instance.
288
- # @param {String} tag
289
- # @param {Object} value
290
- # @param {Integer} expires_in Defaults to 3600.
291
- # @return nil
292
- #
293
- # @example
294
- # MacawFramework::Cache.write("name", "Maria", expires_in: 7200)
295
- def write(tag, value, expires_in: 3600)
296
- if read(tag).nil?
297
- @mutex.synchronize do
298
- @cache.store(tag, { value: value, expires_in: Time.now + expires_in })
299
- end
300
- else
301
- @cache[tag][:value] = value
302
- @cache[tag][:expires_in] = Time.now + expires_in
303
- end
304
- end
305
-
306
- ##
307
- # Read the value with the specified tag.
308
- # Can be called statically or from an instance.
309
- # @param {String} tag
310
- # @return {String|nil}
311
- #
312
- # @example
313
- # MacawFramework::Cache.read("name") # Maria
314
- def self.read(tag) = MacawFramework::Cache.instance.read(tag)
315
-
316
- ##
317
- # Read the value with the specified tag.
318
- # Can be called statically or from an instance.
319
- # @param {String} tag
320
- # @return {String|nil}
321
- #
322
- # @example
323
- # MacawFramework::Cache.read("name") # Maria
324
- def read(tag) = @cache.dig(tag, :value)
325
-
326
- private
327
-
328
- def initialize
329
- @cache = {}
330
- @mutex = Mutex.new
331
- @invalidation_frequency = 60
332
- invalidate_cache
333
- end
334
-
335
- def invalidate_cache
336
- @invalidator = Thread.new(&method(:invalidation_process))
337
- end
338
-
339
- def invalidation_process
340
- loop do
341
- sleep @invalidation_frequency
342
- @mutex.synchronize do
343
- @cache.delete_if { |_, v| v[:expires_in] < Time.now }
344
- end
345
- end
346
- end
347
- end
348
- end
7
+ require_relative 'macaw_framework/macaw'
8
+ require_relative 'macaw_framework/cache'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: macaw_framework
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.22
4
+ version: '1.4'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aria Diniz
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-04-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: prometheus-client
@@ -46,6 +45,7 @@ files:
46
45
  - lib/macaw_framework/aspects/cache_aspect.rb
47
46
  - lib/macaw_framework/aspects/logging_aspect.rb
48
47
  - lib/macaw_framework/aspects/prometheus_aspect.rb
48
+ - lib/macaw_framework/cache.rb
49
49
  - lib/macaw_framework/core/common/server_base.rb
50
50
  - lib/macaw_framework/core/cron_runner.rb
51
51
  - lib/macaw_framework/core/thread_server.rb
@@ -54,6 +54,7 @@ files:
54
54
  - lib/macaw_framework/data_filters/response_data_filter.rb
55
55
  - lib/macaw_framework/errors/endpoint_not_mapped_error.rb
56
56
  - lib/macaw_framework/errors/too_many_requests_error.rb
57
+ - lib/macaw_framework/macaw.rb
57
58
  - lib/macaw_framework/middlewares/memory_invalidation_middleware.rb
58
59
  - lib/macaw_framework/middlewares/prometheus_middleware.rb
59
60
  - lib/macaw_framework/middlewares/rate_limiter_middleware.rb
@@ -76,7 +77,6 @@ metadata:
76
77
  documentation_uri: https://rubydoc.info/gems/macaw_framework
77
78
  homepage_uri: https://github.com/ariasdiniz/macaw_framework
78
79
  source_code_uri: https://github.com/ariasdiniz/macaw_framework
79
- post_install_message:
80
80
  rdoc_options: []
81
81
  require_paths:
82
82
  - lib
@@ -91,8 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
91
  - !ruby/object:Gem::Version
92
92
  version: '0'
93
93
  requirements: []
94
- rubygems_version: 3.4.10
95
- signing_key:
94
+ rubygems_version: 4.0.3
96
95
  specification_version: 4
97
96
  summary: A lightweight back-end web framework
98
97
  test_files: []