one-for-all-framework 4.3.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.env.example +5 -0
- data/Gemfile +2 -0
- data/README.md +6 -1
- data/app/mailers/application_mailer.rb +42 -0
- data/app/views/docs.erb +61 -6
- data/app/views/index.erb +1 -1
- data/bin/ofa +157 -10
- data/config/boot.rb +1 -1
- data/db/data.sqlite3 +0 -0
- metadata +2 -2
- data/db/target.sqlite3 +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 345583a2af3a1c7ee4eb6d923c1d87ea769a5f6e70c98537a36575d1ee89dc03
|
|
4
|
+
data.tar.gz: 596e7f2abe8bcf277d180e5044e9b5fb77fbfa894c894c92639d7cda3fc4136a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b5c07c96b28fc73548e35ecbb81113361d6b32575c4a681bf7bf0b916e2c39e4b18e34032f252c83176116a89d4377d8f1f58ee16acab90cdafb4d0ce467b03
|
|
7
|
+
data.tar.gz: bcb67f5526ebca56809ec9fa8009199b0911c2668445b8eb15ecd7f5995edde4fd8b33271b4168a416fc04aa87f768947d8109a1e0816a97b2620571cd1f911d
|
data/.env.example
CHANGED
|
@@ -2,3 +2,8 @@ DATABASE_URL="sqlite://db/development.sqlite3"
|
|
|
2
2
|
CLOUDINARY_URL="cloudinary://API_KEY:API_SECRET@CLOUD_NAME"
|
|
3
3
|
EKS_CENT_SECRET_KEY_BASE="your_secret_key"
|
|
4
4
|
EKS_ENV="development"
|
|
5
|
+
MAILER_FROM="admin@example.com"
|
|
6
|
+
SMTP_ADDRESS="smtp.gmail.com"
|
|
7
|
+
SMTP_PORT=587
|
|
8
|
+
SMTP_USER_NAME="your_email@gmail.com"
|
|
9
|
+
SMTP_PASSWORD="your_app_password"
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<img src="public/images/logo.png" width="500" height="500" alt="OFA Framework Logo">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
# ⚡ One-For-All (OFA) Framework v4.
|
|
5
|
+
# ⚡ One-For-All (OFA) Framework v4.4.0
|
|
6
6
|
|
|
7
7
|
[](https://www.ruby-lang.org/)
|
|
8
8
|
[](LICENSE)
|
|
@@ -141,6 +141,8 @@ Securely manages admin credentials.
|
|
|
141
141
|
| `ofa doctor` | **System Health Check.** Validates `.env` config, database connectivity (SQL/MongoDB), Ruby version, and dependencies. |
|
|
142
142
|
| `ofa routes` | **Route Inspection.** Lists all registered routes in your application in a clean tabular format. |
|
|
143
143
|
| `ofa swagger` | **OpenAPI Generation.** Auto-generates `openapi.json` for your entire application. |
|
|
144
|
+
| `ofa task NAME` | **Run Background Task.** Executes a task defined in `lib/tasks/`. |
|
|
145
|
+
| `ofa test` | **Run Test Suite.** Executes all unit tests in the `test/` directory using Minitest. |
|
|
144
146
|
| `ofa deploy` | **Production Deployment.** Automatically detects deployment targets (Railway/Docker/Git). |
|
|
145
147
|
|
|
146
148
|
---
|
|
@@ -154,6 +156,9 @@ Automate the creation of boilerplate code with the generator command.
|
|
|
154
156
|
| `ofa g api NAME` | Creates a JSON-based controller in `app/controllers/{name}_controller.rb` inheriting from `ApiController`. |
|
|
155
157
|
| `ofa g model NAME` | Generates a database model in `app/models/{name}.rb` integrated with the Sequel ORM. |
|
|
156
158
|
| `ofa g migration NAME` | Creates a timestamped migration file in `db/migrations/`. Use this to define your schema changes. |
|
|
159
|
+
| `ofa g mailer NAME ACTION` | Generates a new mailer in `app/mailers/` and an ERB template in `app/views/mailers/`. |
|
|
160
|
+
| `ofa g task NAME` | Creates a new background task file in `lib/tasks/`. |
|
|
161
|
+
| `ofa g test NAME` | Generates a new Minitest unit test in `test/`. |
|
|
157
162
|
| `ofa g post TITLE` | Creates a new Markdown/ERB post in `app/views/posts/`. <br> *Args:* `--category`, `--author`, `--image`. <br> *Example:* `./ofa g post "My First Journey" --category Tech --author "John Doe"` |
|
|
158
163
|
|
|
159
164
|
---
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'mail'
|
|
2
|
+
require 'erb'
|
|
3
|
+
|
|
4
|
+
class ApplicationMailer
|
|
5
|
+
# Configure default settings if needed
|
|
6
|
+
def self.mail(to:, subject:, template:, locals: {})
|
|
7
|
+
# Template name expected: "welcome_mailer/welcome"
|
|
8
|
+
template_path = File.join(APP_ROOT, 'app', 'views', 'mailers', "#{template}.erb")
|
|
9
|
+
|
|
10
|
+
unless File.exist?(template_path)
|
|
11
|
+
puts "❌ Mailer Error: Template not found at #{template_path}"
|
|
12
|
+
return
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Create rendering context
|
|
16
|
+
context = Object.new
|
|
17
|
+
locals.each { |k, v| context.instance_variable_set("@#{k}", v) }
|
|
18
|
+
|
|
19
|
+
body_content = ERB.new(File.read(template_path)).result(context.instance_eval { binding })
|
|
20
|
+
|
|
21
|
+
mail = Mail.new do
|
|
22
|
+
from ENV['MAILER_FROM'] || 'admin@example.com'
|
|
23
|
+
to to
|
|
24
|
+
subject subject
|
|
25
|
+
body body_content
|
|
26
|
+
|
|
27
|
+
# Optional: set content type to HTML if needed
|
|
28
|
+
# content_type 'text/html; charset=UTF-8'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# In development/test, we might want to just log it
|
|
32
|
+
if ENV['EKS_ENV'] == 'production'
|
|
33
|
+
mail.deliver!
|
|
34
|
+
else
|
|
35
|
+
puts "--- 📧 EMAIL SENT ---"
|
|
36
|
+
puts "To: #{to}"
|
|
37
|
+
puts "Subject: #{subject}"
|
|
38
|
+
puts "Body:\n#{body_content}"
|
|
39
|
+
puts "----------------------"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/app/views/docs.erb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div class="space-y-12 pb-20">
|
|
2
2
|
<div class="text-left">
|
|
3
|
-
<div class="badge-premium">Documentation v4.
|
|
3
|
+
<div class="badge-premium">Documentation v4.4.0</div>
|
|
4
4
|
<h1 class="text-5xl font-black tracking-tighter mb-4 text-slate-700 dark:text-white">Framework <span class="text-primary">Guide</span></h1>
|
|
5
5
|
<p class="text-xl text-slate-500 max-w-2xl leading-relaxed">Everything you need to know about building premium web applications with the One-For-All framework.</p>
|
|
6
6
|
</div>
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
<a href="#cms" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">CMS & Features</a>
|
|
18
18
|
<a href="#ecommerce" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">E-Commerce</a>
|
|
19
19
|
<a href="#api" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">API & Modern Web</a>
|
|
20
|
-
<a href="#
|
|
20
|
+
<a href="#advanced" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">Mailers & Background Jobs</a>
|
|
21
|
+
<a href="#testing" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">Automated Testing</a>
|
|
21
22
|
<a href="#deploy" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">Deployment</a>
|
|
22
23
|
</nav>
|
|
23
24
|
</aside>
|
|
@@ -98,10 +99,22 @@
|
|
|
98
99
|
<td class="px-6 py-4 font-mono text-primary">./ofa g model NAME</td>
|
|
99
100
|
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Creates model & migration linked to Sequel/NoSQL.</td>
|
|
100
101
|
</tr>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
<tr>
|
|
103
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g migration NAME</td>
|
|
104
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">Creates timestamped migration in <code>db/migrations/</code>.</td>
|
|
105
|
+
</tr>
|
|
106
|
+
<tr>
|
|
107
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g mailer NAME</td>
|
|
108
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">Generates mailer class and ERB templates.</td>
|
|
109
|
+
</tr>
|
|
110
|
+
<tr>
|
|
111
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g task NAME</td>
|
|
112
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">Creates a background task in <code>lib/tasks/</code>.</td>
|
|
113
|
+
</tr>
|
|
114
|
+
<tr>
|
|
115
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g test NAME</td>
|
|
116
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">Generates a Minitest unit test file.</td>
|
|
117
|
+
</tr>
|
|
105
118
|
|
|
106
119
|
<tr class="bg-slate-500/5"><td colspan="2" class="px-6 py-2 text-[10px] font-black uppercase tracking-tighter text-slate-400">Customization</td></tr>
|
|
107
120
|
<tr>
|
|
@@ -350,6 +363,48 @@ resources :posts
|
|
|
350
363
|
</div>
|
|
351
364
|
</section>
|
|
352
365
|
|
|
366
|
+
<!-- Advanced Features -->
|
|
367
|
+
<section id="advanced" class="glass-panel p-8 md:p-12 text-left bg-gradient-to-br from-primary/10 to-transparent">
|
|
368
|
+
<h2 class="text-3xl font-black tracking-tight mb-6 flex items-center gap-3">
|
|
369
|
+
<i class="fas fa-envelope text-primary"></i> Mailers & Tasks
|
|
370
|
+
</h2>
|
|
371
|
+
<div class="markdown-content space-y-8">
|
|
372
|
+
<div>
|
|
373
|
+
<h4 class="font-bold text-lg mb-2">📧 Email Mailers</h4>
|
|
374
|
+
<p class="text-sm text-slate-600 dark:text-slate-400">Send transactional emails with ease using ERB templates. Mailers are stored in <code>app/mailers</code> and templates in <code>app/views/mailers</code>.</p>
|
|
375
|
+
<div class="p-4 bg-white/5 border border-white/10 rounded-2xl mt-4 font-mono text-sm">
|
|
376
|
+
./ofa g mailer welcome signup
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
|
|
380
|
+
<div>
|
|
381
|
+
<h4 class="font-bold text-lg mb-2">⚙️ Background Tasks</h4>
|
|
382
|
+
<p class="text-sm text-slate-600 dark:text-slate-400">Run long-running processes or scheduled jobs using the Task runner. Defined in <code>lib/tasks/</code>.</p>
|
|
383
|
+
<div class="p-4 bg-white/5 border border-white/10 rounded-2xl mt-4 font-mono text-sm">
|
|
384
|
+
./ofa g task sync_data<br>
|
|
385
|
+
./ofa task sync_data
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
</section>
|
|
390
|
+
|
|
391
|
+
<!-- Testing -->
|
|
392
|
+
<section id="testing" class="glass-panel p-8 md:p-12 text-left">
|
|
393
|
+
<h2 class="text-3xl font-black tracking-tight mb-6 flex items-center gap-3">
|
|
394
|
+
<i class="fas fa-vial text-primary"></i> Automated Testing
|
|
395
|
+
</h2>
|
|
396
|
+
<div class="markdown-content">
|
|
397
|
+
<p class="text-slate-600 dark:text-slate-400 mb-6">OFA integrates <strong>Minitest</strong> out-of-the-box for reliable application testing. Run all your tests with a single command.</p>
|
|
398
|
+
<div class="p-4 bg-slate-900 rounded-2xl font-mono text-sm text-green-400 mb-6">
|
|
399
|
+
./ofa test
|
|
400
|
+
</div>
|
|
401
|
+
<p class="text-sm text-slate-500">To create a new test file:</p>
|
|
402
|
+
<div class="p-4 bg-white/5 border border-white/10 rounded-2xl mt-2 font-mono text-sm">
|
|
403
|
+
./ofa g test user_profile
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
</section>
|
|
407
|
+
|
|
353
408
|
<!-- Deployment -->
|
|
354
409
|
<section id="deploy" class="glass-panel p-8 md:p-12 text-left border-primary/20">
|
|
355
410
|
<h2 class="text-3xl font-black tracking-tight mb-6 flex items-center gap-3">
|
data/app/views/index.erb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div class="text-center space-y-6 max-w-2xl mx-auto py-12">
|
|
2
2
|
<div class="inline-flex items-center px-4 py-1.5 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold uppercase tracking-wider animate-pulse">
|
|
3
|
-
Framework Version 4.
|
|
3
|
+
Framework Version 4.4.0
|
|
4
4
|
</div>
|
|
5
5
|
|
|
6
6
|
<h1 class="text-5xl md:text-7xl font-black tracking-tight leading-tight">
|
data/bin/ofa
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
+
# --- Bundler Setup ---
|
|
4
|
+
# This ensures that if we are in a project with a Gemfile, we use the correct
|
|
5
|
+
# gem versions specified in Gemfile.lock, preventing conflicts with system gems.
|
|
6
|
+
if File.exist?(File.join(Dir.pwd, 'Gemfile'))
|
|
7
|
+
begin
|
|
8
|
+
require 'bundler/setup'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
# Bundler not installed, proceed without it
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
3
14
|
require 'fileutils'
|
|
4
15
|
require 'optparse'
|
|
5
16
|
require 'json'
|
|
@@ -22,7 +33,7 @@ def help
|
|
|
22
33
|
puts " / __ \\/ ____/ / | "
|
|
23
34
|
puts " / / / / /_ / /| | Framework "
|
|
24
35
|
puts "/ /_/ / __/ / ___ | Premium MVC "
|
|
25
|
-
puts "\\____/_/ /_/ |_| v4.
|
|
36
|
+
puts "\\____/_/ /_/ |_| v4.4.0 "
|
|
26
37
|
puts " "
|
|
27
38
|
puts "✨ One-For-All Framework CLI ✨"
|
|
28
39
|
puts "-----------------------------"
|
|
@@ -33,6 +44,9 @@ def help
|
|
|
33
44
|
puts " ofa g api NAME - Generate a new API controller (JSON)"
|
|
34
45
|
puts " ofa g model NAME - Generate a new model"
|
|
35
46
|
puts " ofa g migration NAME - Generate a new migration"
|
|
47
|
+
puts " ofa g mailer NAME ACTION - Generate a new mailer and template"
|
|
48
|
+
puts " ofa g task NAME - Generate a new background task"
|
|
49
|
+
puts " ofa g test NAME - Generate a new unit test"
|
|
36
50
|
puts " ofa g post TITLE - Create a new post [args: --category, --author, --image]"
|
|
37
51
|
puts " ofa feature ACTION F - Toggle features: action=enable/disable, F=cms/auth/rich_text"
|
|
38
52
|
puts " ofa type NAME - Set application type: portfolio, blog, landing_page, e_commerce"
|
|
@@ -47,6 +61,8 @@ def help
|
|
|
47
61
|
puts " ofa doctor - Check system health (DB, config, gems)"
|
|
48
62
|
puts " ofa routes - List all registered routes"
|
|
49
63
|
puts " ofa swagger - Auto-generate OpenAPI/Swagger documentation"
|
|
64
|
+
puts " ofa task NAME - Run a background task"
|
|
65
|
+
puts " ofa test - Run all application tests"
|
|
50
66
|
puts " ofa run - Start the application server"
|
|
51
67
|
puts " ofa deploy - Deploy project to production"
|
|
52
68
|
puts "-----------------------------"
|
|
@@ -231,7 +247,7 @@ when 'init'
|
|
|
231
247
|
puts " / __ \\/ ____/ / | "
|
|
232
248
|
puts " / / / / /_ / /| | Framework "
|
|
233
249
|
puts "/ /_/ / __/ / ___ | Premium MVC "
|
|
234
|
-
puts "\\____/_/ /_/ |_| v4.
|
|
250
|
+
puts "\\____/_/ /_/ |_| v4.4.0 "
|
|
235
251
|
puts " "
|
|
236
252
|
puts "Initializing One-For-All project as '#{app_type}' in #{PROJECT_ROOT}..."
|
|
237
253
|
|
|
@@ -262,7 +278,7 @@ when 'init'
|
|
|
262
278
|
puts "-----------------------------\n"
|
|
263
279
|
|
|
264
280
|
# Create directories
|
|
265
|
-
dirs = ['app/controllers', 'app/models', 'app/views', 'config', 'db/migrations', 'public/css', 'public/js', 'public/images', 'public/images/uploads']
|
|
281
|
+
dirs = ['app/controllers', 'app/models', 'app/views', 'app/mailers', 'lib/tasks', 'test', 'config', 'db/migrations', 'public/css', 'public/js', 'public/images', 'public/images/uploads']
|
|
266
282
|
dirs.each { |dir| FileUtils.mkdir_p(File.join(PROJECT_ROOT, dir)) }
|
|
267
283
|
|
|
268
284
|
# Copy framework core migrations
|
|
@@ -274,6 +290,21 @@ when 'init'
|
|
|
274
290
|
end
|
|
275
291
|
end
|
|
276
292
|
|
|
293
|
+
# Copy framework core helpers and test assets
|
|
294
|
+
['lib/task_helper.rb', 'app/mailers/application_mailer.rb', 'test/test_helper.rb'].each do |file|
|
|
295
|
+
src = File.join(FRAMEWORK_ROOT, file)
|
|
296
|
+
dest = File.join(PROJECT_ROOT, file)
|
|
297
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
298
|
+
if File.exist?(src)
|
|
299
|
+
content = File.read(src)
|
|
300
|
+
# Inject FRAMEWORK_ROOT into test_helper.rb
|
|
301
|
+
if file == 'test/test_helper.rb'
|
|
302
|
+
content.gsub!("require_relative '../config/boot'", "$LOAD_PATH.unshift '#{FRAMEWORK_ROOT}'\nrequire 'config/boot'")
|
|
303
|
+
end
|
|
304
|
+
File.write(dest, content)
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
277
308
|
# Create .env
|
|
278
309
|
env_content = []
|
|
279
310
|
env_content << "EKS_ENV=development"
|
|
@@ -321,6 +352,8 @@ when 'init'
|
|
|
321
352
|
gem 'mysql2'
|
|
322
353
|
gem 'kramdown'
|
|
323
354
|
gem 'kramdown-parser-gfm'
|
|
355
|
+
gem 'mail'
|
|
356
|
+
gem 'minitest'
|
|
324
357
|
RUBY
|
|
325
358
|
File.write(File.join(PROJECT_ROOT, 'Gemfile'), gemfile_content)
|
|
326
359
|
|
|
@@ -499,6 +532,58 @@ when 'g'
|
|
|
499
532
|
File.write(file_name, content)
|
|
500
533
|
puts "Created API Controller: #{file_name}"
|
|
501
534
|
puts "💡 Don't forget to register it in config/routes.rb"
|
|
535
|
+
elsif sub == 'mailer' && name
|
|
536
|
+
action = ARGV.shift || 'welcome'
|
|
537
|
+
class_name = name.capitalize + "Mailer"
|
|
538
|
+
file_name = File.join(PROJECT_ROOT, "app/mailers/#{name.downcase}_mailer.rb")
|
|
539
|
+
content = <<~RUBY
|
|
540
|
+
class #{class_name} < ApplicationMailer
|
|
541
|
+
def #{action}(user)
|
|
542
|
+
mail(
|
|
543
|
+
to: user.email,
|
|
544
|
+
subject: "Welcome to our platform!",
|
|
545
|
+
template: "#{name.downcase}_mailer/#{action}",
|
|
546
|
+
locals: { user: user }
|
|
547
|
+
)
|
|
548
|
+
end
|
|
549
|
+
end
|
|
550
|
+
RUBY
|
|
551
|
+
File.write(file_name, content)
|
|
552
|
+
|
|
553
|
+
# Create template
|
|
554
|
+
template_dir = File.join(PROJECT_ROOT, "app/views/mailers/#{name.downcase}_mailer")
|
|
555
|
+
FileUtils.mkdir_p(template_dir)
|
|
556
|
+
template_file = File.join(template_dir, "#{action}.erb")
|
|
557
|
+
File.write(template_file, "<h1>Hello <%= @user.username %></h1>\n<p>Welcome to One-For-All Framework!</p>")
|
|
558
|
+
|
|
559
|
+
puts "Created Mailer: #{file_name}"
|
|
560
|
+
puts "Created Template: #{template_file}"
|
|
561
|
+
elsif sub == 'task' && name
|
|
562
|
+
dir_path = File.join(PROJECT_ROOT, "lib/tasks")
|
|
563
|
+
FileUtils.mkdir_p(dir_path)
|
|
564
|
+
file_name = File.join(dir_path, "#{name.downcase}.rb")
|
|
565
|
+
content = <<~RUBY
|
|
566
|
+
desc "Description for #{name} task"
|
|
567
|
+
task :#{name.downcase} do
|
|
568
|
+
puts "Running task: #{name}"
|
|
569
|
+
# Your background job logic here
|
|
570
|
+
end
|
|
571
|
+
RUBY
|
|
572
|
+
File.write(file_name, content)
|
|
573
|
+
puts "Created Task: #{file_name}"
|
|
574
|
+
elsif sub == 'test' && name
|
|
575
|
+
file_name = File.join(PROJECT_ROOT, "test/#{name.downcase}_test.rb")
|
|
576
|
+
content = <<~RUBY
|
|
577
|
+
require_relative 'test_helper'
|
|
578
|
+
|
|
579
|
+
class #{name.capitalize}Test < Minitest::Test
|
|
580
|
+
def test_example
|
|
581
|
+
assert true
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
RUBY
|
|
585
|
+
File.write(file_name, content)
|
|
586
|
+
puts "Created Test: #{file_name}"
|
|
502
587
|
elsif sub == 'model' && name
|
|
503
588
|
class_name = name.capitalize
|
|
504
589
|
file_name = File.join(PROJECT_ROOT, "app/models/#{name.downcase}.rb")
|
|
@@ -535,7 +620,7 @@ when 'g'
|
|
|
535
620
|
File.write(file_name, content)
|
|
536
621
|
puts "Created post: #{file_name}"
|
|
537
622
|
else
|
|
538
|
-
puts "Usage: ofa g [controller|model|migration|post] NAME"
|
|
623
|
+
puts "Usage: ofa g [controller|api|model|migration|mailer|task|test|post] NAME"
|
|
539
624
|
end
|
|
540
625
|
|
|
541
626
|
when 'feature'
|
|
@@ -545,7 +630,8 @@ when 'feature'
|
|
|
545
630
|
config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
|
|
546
631
|
config = JSON.parse(File.read(config_path))
|
|
547
632
|
config[feature] = (action == 'enable')
|
|
548
|
-
File.
|
|
633
|
+
File.open("#{config_path}.tmp", 'w') { |f| f.write(JSON.pretty_generate(config)) }
|
|
634
|
+
File.rename("#{config_path}.tmp", config_path)
|
|
549
635
|
puts "Feature '#{feature}' has been #{action}d."
|
|
550
636
|
|
|
551
637
|
if action == 'enable' && feature == 'cms' && !config['auth']
|
|
@@ -564,7 +650,8 @@ when 'type'
|
|
|
564
650
|
config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
|
|
565
651
|
config = JSON.parse(File.read(config_path))
|
|
566
652
|
config['type'] = name
|
|
567
|
-
File.
|
|
653
|
+
File.open("#{config_path}.tmp", 'w') { |f| f.write(JSON.pretty_generate(config)) }
|
|
654
|
+
File.rename("#{config_path}.tmp", config_path)
|
|
568
655
|
puts "Application type set to '#{name}'."
|
|
569
656
|
|
|
570
657
|
when 'theme'
|
|
@@ -577,7 +664,8 @@ when 'theme'
|
|
|
577
664
|
config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
|
|
578
665
|
config = JSON.parse(File.read(config_path))
|
|
579
666
|
config['theme'] = name
|
|
580
|
-
File.
|
|
667
|
+
File.open("#{config_path}.tmp", 'w') { |f| f.write(JSON.pretty_generate(config)) }
|
|
668
|
+
File.rename("#{config_path}.tmp", config_path)
|
|
581
669
|
puts "Application theme set to '#{name}'."
|
|
582
670
|
|
|
583
671
|
when 'storage'
|
|
@@ -640,7 +728,8 @@ when 'db'
|
|
|
640
728
|
env_lines = File.exist?(env_path) ? File.readlines(env_path) : []
|
|
641
729
|
env_lines.reject! { |l| l.start_with?('DATABASE_URL=') }
|
|
642
730
|
env_lines << "DATABASE_URL=#{db_name}\n"
|
|
643
|
-
File.
|
|
731
|
+
File.open("#{env_path}.tmp", 'w') { |f| f.write(env_lines.join) }
|
|
732
|
+
File.rename("#{env_path}.tmp", env_path)
|
|
644
733
|
puts "✅ Connection string saved to .env"
|
|
645
734
|
end
|
|
646
735
|
when 'sqlite'
|
|
@@ -648,7 +737,8 @@ when 'db'
|
|
|
648
737
|
else
|
|
649
738
|
config = { "adapter" => type, "host" => "localhost", "user" => "root", "password" => "", "database" => db_name || "one_for_all" }
|
|
650
739
|
end
|
|
651
|
-
File.
|
|
740
|
+
File.open("#{config_path}.tmp", 'w') { |f| f.write(JSON.pretty_generate(config)) }
|
|
741
|
+
File.rename("#{config_path}.tmp", config_path)
|
|
652
742
|
puts "Switched to #{type} mode."
|
|
653
743
|
when 'migrate'
|
|
654
744
|
run_migrations
|
|
@@ -733,7 +823,7 @@ when 'console'
|
|
|
733
823
|
puts " / __ \\/ ____/ / | "
|
|
734
824
|
puts " / / / / /_ / /| | Framework "
|
|
735
825
|
puts "/ /_/ / __/ / ___ | Console (REPL) "
|
|
736
|
-
puts "\\____/_/ /_/ |_| v4.
|
|
826
|
+
puts "\\____/_/ /_/ |_| v4.4.0 "
|
|
737
827
|
puts " "
|
|
738
828
|
puts "✨ Loading environment... (Type 'exit' to quit)"
|
|
739
829
|
|
|
@@ -845,6 +935,63 @@ when 'deploy'
|
|
|
845
935
|
puts " This framework supports deployment via Docker or Git Push."
|
|
846
936
|
end
|
|
847
937
|
|
|
938
|
+
when 'task'
|
|
939
|
+
ensure_initialized!
|
|
940
|
+
name = ARGV.shift
|
|
941
|
+
if name.nil?
|
|
942
|
+
puts "Usage: ofa task NAME"
|
|
943
|
+
# List available tasks
|
|
944
|
+
tasks_dir = File.join(PROJECT_ROOT, 'lib/tasks')
|
|
945
|
+
if Dir.exist?(tasks_dir)
|
|
946
|
+
puts "\nAvailable tasks:"
|
|
947
|
+
Dir.glob(File.join(tasks_dir, '*.rb')).each do |f|
|
|
948
|
+
puts " - #{File.basename(f, '.rb')}"
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
exit 1
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
# Load framework & app
|
|
955
|
+
Object.const_set(:APP_ROOT, PROJECT_ROOT) unless defined?(APP_ROOT)
|
|
956
|
+
require File.join(FRAMEWORK_ROOT, 'config', 'boot')
|
|
957
|
+
require File.join(FRAMEWORK_ROOT, 'lib', 'task_helper')
|
|
958
|
+
|
|
959
|
+
# Load all tasks in lib/tasks
|
|
960
|
+
Dir.glob(File.join(PROJECT_ROOT, 'lib/tasks/*.rb')).each { |f| require f }
|
|
961
|
+
|
|
962
|
+
run_task(name)
|
|
963
|
+
|
|
964
|
+
when 'test'
|
|
965
|
+
ensure_initialized!
|
|
966
|
+
puts "🧪 Running tests..."
|
|
967
|
+
puts "------------------------------------------"
|
|
968
|
+
|
|
969
|
+
# Set environment to test
|
|
970
|
+
ENV['EKS_ENV'] = 'test'
|
|
971
|
+
Object.const_set(:APP_ROOT, PROJECT_ROOT) unless defined?(APP_ROOT)
|
|
972
|
+
|
|
973
|
+
# Add project root to load path
|
|
974
|
+
$LOAD_PATH.unshift PROJECT_ROOT
|
|
975
|
+
$LOAD_PATH.unshift File.join(PROJECT_ROOT, 'test')
|
|
976
|
+
|
|
977
|
+
# Find all tests (support both _test.rb and _spec.rb)
|
|
978
|
+
if ARGV.empty?
|
|
979
|
+
test_files = Dir.glob(File.join(PROJECT_ROOT, 'test/**/{*_test.rb,*_spec.rb}'))
|
|
980
|
+
else
|
|
981
|
+
test_files = ARGV.map { |arg| File.expand_path(arg, PROJECT_ROOT) }
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
if test_files.empty?
|
|
985
|
+
puts "❌ No tests found in test/ directory."
|
|
986
|
+
exit 1
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
# Run them by requiring
|
|
990
|
+
test_files.each { |f| require f }
|
|
991
|
+
|
|
992
|
+
# Trigger EksaMination execution if available
|
|
993
|
+
EksaMination.run if defined?(EksaMination)
|
|
994
|
+
|
|
848
995
|
else
|
|
849
996
|
help
|
|
850
997
|
end
|
data/config/boot.rb
CHANGED
|
@@ -93,7 +93,7 @@ require_relative 'database'
|
|
|
93
93
|
|
|
94
94
|
# Autoloading (Framework Core first, then APP_ROOT)
|
|
95
95
|
framework_app = File.expand_path('../app', __dir__)
|
|
96
|
-
['controllers', 'models', 'middleware', 'helpers'].each do |folder|
|
|
96
|
+
['controllers', 'models', 'middleware', 'helpers', 'mailers'].each do |folder|
|
|
97
97
|
next if folder == 'models' && ENV['SKIP_MODELS']
|
|
98
98
|
loaded = []
|
|
99
99
|
# Load framework core
|
data/db/data.sqlite3
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: one-for-all-framework
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ishikawa Uta
|
|
@@ -210,6 +210,7 @@ files:
|
|
|
210
210
|
- app/controllers/products_controller.rb
|
|
211
211
|
- app/controllers/projects_controller.rb
|
|
212
212
|
- app/helpers/cloudinary_helper.rb
|
|
213
|
+
- app/mailers/application_mailer.rb
|
|
213
214
|
- app/middleware/auth_middleware.rb
|
|
214
215
|
- app/middleware/csrf_middleware.rb
|
|
215
216
|
- app/middleware/jwt_middleware.rb
|
|
@@ -254,7 +255,6 @@ files:
|
|
|
254
255
|
- db/data.sqlite3
|
|
255
256
|
- db/development.sqlite3
|
|
256
257
|
- db/migrations/20260502000000_create_products.rb
|
|
257
|
-
- db/target.sqlite3
|
|
258
258
|
- ofa
|
|
259
259
|
- public/css/cms.css
|
|
260
260
|
- public/images/logo.jpg
|
data/db/target.sqlite3
DELETED
|
Binary file
|