uni_rails 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32de6ce8c47b9fc788b686f9ccd709341c543f3e8f97b93383980b8814ff941e
4
- data.tar.gz: 52f9a0d16c28d7e5f3a326cd58fccea70687eb7fef1560d6e0dbf8c700fe61b4
3
+ metadata.gz: 3dbb41ea67dff2755cb5b77eae07f174a95bf99de2cf03a084739213c99033c9
4
+ data.tar.gz: 5c9414944b4616651a429657025c58444f330ceea500f164a92a8d7afcefc824
5
5
  SHA512:
6
- metadata.gz: 3695f3707ec840bc876d891da128bc3ab385b0327915ac5d714ae4b0e1cfc2f083b939fd9c8ab7199b85a9db0b8678627c67f1d8ced5ebf19142f19973a49ced
7
- data.tar.gz: ae3b6b9530ade148bca88cc8329dc3e71aca7f24829330ae9ac55c641710ed4551e2ca9e4b5532886fad5b285f1dd7dc2181cce9ced90a30051eec1dd3a4f7ce
6
+ metadata.gz: 980ece2224b2c31320c749eb07676912f27bf5285d6dcefcacae4196f15061140185cb8b51a565d4605db0fd95a669ec543b1750fcdeb024fab4cc7d5a61d93a
7
+ data.tar.gz: 49e8aa1ff7ec06d23cfa5a07d8d9546e036fe6255c71f6766faa42416631d0fdaf9d572333ebd066f6265e693f731586f77e19f3858f929f351cd82f28c4ff99
data/README.md CHANGED
@@ -1,18 +1,21 @@
1
1
  # UniRails
2
2
 
3
- UniRails is designed to streamline the process of creating Ruby on Rails applications within a single Ruby file. This approach is ideal for small personal CRUD (Create, Read, Update, Delete) applications or educational purposes, offering a simplified method for demonstrating Rails concepts.
3
+ UniRails makes it easy to build Ruby on Rails apps all within one Ruby file. It's perfect for small personal projects or teaching, as everything is one scroll away.
4
4
 
5
- Our goal is to facilitate educators and writers in crafting concise, illustrative examples for articles or books. Traditional Rails applications require a complex structure even for demonstrating basic concepts, which can be cumbersome and unnecessary. Our library addresses this issue by enabling the quick replication of a Ruby file to instantiate a fully operational Rails app, consolidating all essential components into one file.
5
+ We aim to help educators and writers create clear examples for articles or books. Rails requires a full folder structure to show the basics, which can be a hassle. UniRails cuts through that by letting you spin up a complete Rails app from just one Ruby file everything you need in one place.
6
6
 
7
- Embrace the convenience of single-file Rails applications with our library, where simplicity meets functionality.
7
+ Check out our [examples](/examples) to understand how the library creates Rails apps from a single file.
8
8
 
9
9
  ## Installation & Usage
10
10
 
11
11
  See some examples of how the UniRails library can be used
12
12
 
13
- - [json api](/examples/json_api.rb)
14
- - [todo app](/examples/todos.rb)
15
- - [hotwire](/examples/hotwire.rb)
13
+ - [Hello world](/examples/hello-world.rb)
14
+ - [Todos app (JSON API)](/examples/todos-api.rb)
15
+ - [Todos app (Rails scaffold)](/examples/todos-scaffold.rb) based off `bin/rails g scaffold todo name completed_at:datetime`
16
+ - [Todos app (Hotwire)](/examples/todos-hotwire.rb) based off online article: [turbo-rails-101-todo-list](https://www.colby.so/posts/turbo-rails-101-todo-list) by David Colby
17
+ - [App using Puma server](/examples/server-puma-app.rb)
18
+ - [App using Falcon server](/examples/server-falcon-app.rb)
16
19
 
17
20
  ## Development
18
21
 
@@ -30,7 +33,7 @@ We would like to support all railties and engines. Please help us support more o
30
33
 
31
34
  - [X] action_controller
32
35
  - [X] active_record
33
- - [ ] active_model/railtie
36
+ - [X] active_model/railtie
34
37
  - [ ] active_job/railtie
35
38
  - [ ] active_storage/engine
36
39
  - [ ] action_mailer/railtie
@@ -0,0 +1,25 @@
1
+ # Run the application
2
+ # $ ruby hello-world.rb
3
+
4
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
5
+
6
+ require "bundler/inline"
7
+
8
+ gemfile(true) do
9
+ source "https://rubygems.org"
10
+ gem 'uni_rails'
11
+ end
12
+
13
+ require 'uni_rails'
14
+
15
+ UniRails::App.routes.append do
16
+ root 'pages#index'
17
+ end
18
+
19
+ class PagesController < ActionController::Base
20
+ def index
21
+ render plain: 'Hello world'
22
+ end
23
+ end
24
+
25
+ UniRails.run(Port: 3000)
@@ -0,0 +1,30 @@
1
+ # Run the application
2
+ # $ ruby server-falcon-app.rb
3
+
4
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
5
+
6
+ require "bundler/inline"
7
+
8
+ gemfile(true) do
9
+ source "https://rubygems.org"
10
+
11
+ gem 'uni_rails'
12
+ gem 'falcon'
13
+ end
14
+
15
+ require 'uni_rails'
16
+ require 'rackup/handler/falcon'
17
+
18
+ UniRails.rackup_handler = Rackup::Handler::Falcon
19
+
20
+ UniRails::App.routes.append do
21
+ root 'pages#index'
22
+ end
23
+
24
+ class PagesController < ActionController::Base
25
+ def index
26
+ render plain: 'Application served by Falcon'
27
+ end
28
+ end
29
+
30
+ UniRails.run(Port: 3000)
@@ -0,0 +1,30 @@
1
+ # Run the application
2
+ # $ ruby server-puma-app.rb
3
+
4
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
5
+
6
+ require "bundler/inline"
7
+
8
+ gemfile(true) do
9
+ source "https://rubygems.org"
10
+
11
+ gem 'uni_rails'
12
+ gem 'puma'
13
+ end
14
+
15
+ require 'uni_rails'
16
+ require 'rack/handler/puma'
17
+
18
+ UniRails.rackup_handler = Rack::Handler::Puma
19
+
20
+ UniRails::App.routes.append do
21
+ root 'pages#index'
22
+ end
23
+
24
+ class PagesController < ActionController::Base
25
+ def index
26
+ render plain: 'Application served by Puma'
27
+ end
28
+ end
29
+
30
+ UniRails.run(Port: 3000)
@@ -0,0 +1,103 @@
1
+ # Run the application
2
+ # $ ruby todos-api.rb
3
+
4
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
5
+ ENV['DATABASE_URL'] = "sqlite3:///#{Dir.pwd}/todos-api.sqlite"
6
+
7
+ require "bundler/inline"
8
+
9
+ gemfile(true) do
10
+ source "https://rubygems.org"
11
+
12
+ gem 'uni_rails'
13
+ gem 'sqlite3', '~> 1.7'
14
+ end
15
+
16
+ require 'uni_rails'
17
+ require 'sqlite3'
18
+
19
+ ActiveRecord::Base.establish_connection
20
+ ActiveRecord::Schema.define do
21
+ create_table :todos, force: :cascade do |t|
22
+ t.string :name
23
+ t.datetime :completed_at
24
+ t.timestamps
25
+ end
26
+ end
27
+
28
+ UniRails::App.routes.append do
29
+ resources :todos do
30
+ put :complete, on: :member
31
+ end
32
+ end
33
+
34
+ # MODELS
35
+
36
+ class Todo < ActiveRecord::Base
37
+ validates :name, presence: true
38
+
39
+ def complete(at: Time.zone.now)
40
+ update(completed_at: at)
41
+ end
42
+ end
43
+
44
+ # CONTROLLERS
45
+
46
+ class ApplicationController < ActionController::Base
47
+ protect_from_forgery with: :null_session
48
+ end
49
+
50
+ class TodosController < ApplicationController
51
+ before_action :set_todo, only: [:show, :update, :destroy, :complete]
52
+
53
+ def index
54
+ @todos = Todo.all
55
+ render json: @todos
56
+ end
57
+
58
+ def show
59
+ render json: @todo
60
+ end
61
+
62
+ def create
63
+ @todo = Todo.new(todo_params)
64
+ if @todo.save
65
+ render json: @todo, status: :created, location: @todo
66
+ else
67
+ render json: @todo.errors, status: :unprocessable_entity
68
+ end
69
+ end
70
+
71
+ def update
72
+ if @todo.update(todo_params)
73
+ render json: @todo
74
+ else
75
+ render json: @todo.errors, status: :unprocessable_entity
76
+ end
77
+ end
78
+
79
+ def complete
80
+ if @todo.complete
81
+ render json: @todo
82
+ else
83
+ render json: @todo.errors, status: :unprocessable_entity
84
+ end
85
+ end
86
+
87
+ def destroy
88
+ @todo.destroy
89
+ head :no_content
90
+ end
91
+
92
+ private
93
+
94
+ def set_todo
95
+ @todo = Todo.find(params[:id])
96
+ end
97
+
98
+ def todo_params
99
+ params.require(:todo).permit(:name, :status)
100
+ end
101
+ end
102
+
103
+ UniRails.run(Port: 3000)
@@ -0,0 +1,263 @@
1
+ # Based on the Hotwire tutorial: https://www.colby.so/posts/turbo-rails-101-todo-list
2
+ # Run the application
3
+ # $ ruby todos-hotwire.rb
4
+
5
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
6
+ ENV['DATABASE_URL'] = "sqlite3:///#{Dir.pwd}/todos-hotwire.sqlite"
7
+
8
+ require "bundler/inline"
9
+
10
+ gemfile(true) do
11
+ source "https://rubygems.org"
12
+
13
+ gem 'uni_rails'
14
+ gem 'sqlite3', '~> 1.7'
15
+ gem 'debug'
16
+ end
17
+
18
+ require 'uni_rails'
19
+ require 'sqlite3'
20
+
21
+ ActiveRecord::Base.establish_connection
22
+ ActiveRecord::Schema.define do
23
+ create_table :todos, force: :cascade do |t|
24
+ t.string :name
25
+ t.integer :status, default: 0
26
+ t.timestamps
27
+ end
28
+ end
29
+
30
+ UniRails.enable_turbo_rails!
31
+
32
+ UniRails::App.routes.append do
33
+ root 'todos#index'
34
+ resources :todos do
35
+ patch :change_status, on: :member
36
+ end
37
+ end
38
+
39
+ # MODELS
40
+
41
+ class Todo < ActiveRecord::Base
42
+ enum status: {
43
+ incomplete: 0,
44
+ complete: 1
45
+ }
46
+
47
+ validates :name, presence: true
48
+ end
49
+
50
+
51
+ # CONTROLLERS
52
+
53
+ class TodosController < ActionController::Base
54
+ layout 'application'
55
+
56
+ before_action :set_todo, only: [:edit, :update, :destroy, :change_status]
57
+
58
+ def index
59
+ @todos = Todo.where(status: params[:status].presence || 'incomplete')
60
+ end
61
+
62
+ def new
63
+ @todo = Todo.new
64
+ end
65
+
66
+ def edit
67
+ @todo = Todo.find(params[:id])
68
+ end
69
+
70
+ def create
71
+ @todo = Todo.new(todo_params)
72
+
73
+ respond_to do |format|
74
+ if @todo.save
75
+ format.turbo_stream
76
+ format.html { redirect_to todo_url(@todo), notice: "Todo was successfully created." }
77
+ else
78
+ format.turbo_stream { render turbo_stream: turbo_stream.replace("#{helpers.dom_id(@todo)}_form", partial: "form", locals: { todo: @todo }) }
79
+ format.html { render :new, status: :unprocessable_entity }
80
+ end
81
+ end
82
+ end
83
+
84
+ def update
85
+ respond_to do |format|
86
+ if @todo.update(todo_params)
87
+ format.turbo_stream
88
+ format.html { redirect_to todo_url(@todo), notice: "Todo was successfully updated." }
89
+ format.json { render :show, status: :ok, location: @todo }
90
+ else
91
+ format.turbo_stream { render turbo_stream: turbo_stream.replace("#{helpers.dom_id(@todo)}_form", partial: "form", locals: { todo: @todo }) }
92
+ format.html { render :edit, status: :unprocessable_entity }
93
+ format.json { render json: @todo.errors, status: :unprocessable_entity }
94
+ end
95
+ end
96
+ end
97
+
98
+ def destroy
99
+ @todo.destroy
100
+
101
+ respond_to do |format|
102
+ format.turbo_stream { render turbo_stream: turbo_stream.remove("#{helpers.dom_id(@todo)}_container") }
103
+ format.html { redirect_to todos_url, notice: "Todo was successfully destroyed." }
104
+ format.json { head :no_content }
105
+ end
106
+ end
107
+
108
+ def change_status
109
+ @todo.update(status: todo_params[:status])
110
+ respond_to do |format|
111
+ format.turbo_stream { render turbo_stream: turbo_stream.remove("#{helpers.dom_id(@todo)}_container") }
112
+ format.html { redirect_to todos_path, notice: "Updated todo status." }
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ def set_todo
119
+ @todo = Todo.find(params[:id])
120
+ end
121
+
122
+ def todo_params
123
+ params.require(:todo).permit(:name, :status)
124
+ end
125
+ end
126
+
127
+ # VIEWS
128
+
129
+ UniRails.javascript <<~JAVASCRIPT
130
+ import * as Turbo from "turbo"
131
+ JAVASCRIPT
132
+
133
+ UniRails.register_view "layouts/application.html.erb", <<~'HTML'
134
+ <!DOCTYPE html>
135
+ <html>
136
+ <head>
137
+ <title>Template</title>
138
+ <meta name="viewport" content="width=device-width,initial-scale=1">
139
+ <%= csrf_meta_tags %>
140
+ <%= csp_meta_tag %>
141
+ <script src="https://cdn.tailwindcss.com"></script>
142
+ <%= uni_rails_css_stylesheet %>
143
+ <%= uni_rails_import_map_tag %>
144
+ <%= uni_rails_javascript_script %>
145
+ </head>
146
+
147
+ <body>
148
+ <%= yield %>
149
+ </body>
150
+ </html>
151
+ HTML
152
+
153
+ UniRails.register_view "todos/index.html.erb", <<~'HTML'
154
+ <div class="mx-auto w-1/2">
155
+ <h2 class="text-2xl text-gray-900">
156
+ Your todos
157
+ </h2>
158
+ <%= turbo_frame_tag "todos-container", class: "block max-w-2xl w-full bg-gray-100 py-8 px-4 border border-gray-200 rounded shadow-sm" do %>
159
+ <div class="border-b border-gray-200 w-full">
160
+ <ul class="flex space-x-2 justify-center">
161
+ <li>
162
+ <%= link_to "Incomplete",
163
+ todos_path(status: "incomplete"),
164
+ class: "inline-block py-4 px-4 text-sm font-medium text-center text-gray-500 border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300"
165
+ %>
166
+ <li>
167
+ <%= link_to "Complete",
168
+ todos_path(status: "complete"),
169
+ class: "inline-block py-4 px-4 text-sm font-medium text-center text-gray-500 border-b-2 border-transparent hover:text-gray-600 hover:border-gray-300"
170
+ %>
171
+ </li>
172
+ </ul>
173
+ </div>
174
+ <% unless params[:status] == "complete" %>
175
+ <div class="py-2 px-4">
176
+ <%= render "form", todo: Todo.new %>
177
+ </div>
178
+ <% end %>
179
+ <ul id="todos">
180
+ <%= render @todos %>
181
+ </ul>
182
+ <% end %>
183
+ </div>
184
+ HTML
185
+
186
+ UniRails.register_view "todos/_form.html.erb", <<~'HTML'
187
+ <%= form_with(model: todo, id: "#{dom_id(todo)}_form") do |form| %>
188
+ <% if todo.errors.any? %>
189
+ <div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
190
+ <h2><%= pluralize(todo.errors.count, "error") %> prohibited this todo from being saved:</h2>
191
+
192
+ <ul>
193
+ <% todo.errors.each do |error| %>
194
+ <li><%= error.full_message %></li>
195
+ <% end %>
196
+ </ul>
197
+ </div>
198
+ <% end %>
199
+ <div class="flex items-stretch flex-grow">
200
+ <%= form.label :name, class: "sr-only" %>
201
+ <%= form.text_field :name, class: "block w-full rounded-none rounded-l-md sm:text-sm border-gray-300", placeholder: "Add a new todo..." %>
202
+ <%= form.submit class: "-ml-px relative px-4 py-2 border border-blue-600 text-sm font-medium rounded-r-md text-white bg-blue-600 hover:bg-blue-700" %>
203
+ </div>
204
+ <% end %>
205
+ HTML
206
+
207
+ UniRails.register_view "todos/_todo.html.erb", <<~'HTML'
208
+ <li id="<%= "#{dom_id(todo)}_container" %>" class="py-2 px-4 border-b border-gray-300">
209
+ <%= turbo_frame_tag dom_id(todo) do %>
210
+ <div class="flex justify-between items-center space-x-2">
211
+ <%= link_to todo.name, edit_todo_path(todo), class: todo.complete? ? 'line-through' : '' %>
212
+
213
+ <div class="flex justify-end space-x-3">
214
+ <% if todo.complete? %>
215
+ <%= button_to change_status_todo_path(todo, todo: { status: 'incomplete' }), class: "bg-green-600 px-4 py-2 rounded hover:bg-green-700", method: :patch do %>
216
+ <span class="sr-only">Mark as incomplete</span>
217
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor">
218
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
219
+ </svg>
220
+ <% end %>
221
+ <% else %>
222
+ <%= button_to change_status_todo_path(todo, todo: { status: 'complete' }), class: "bg-gray-400 px-4 py-2 rounded hover:bg-green-700", method: :patch do %>
223
+ <span class="sr-only">Mark as complete</span>
224
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor">
225
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
226
+ </svg>
227
+ <% end %>
228
+ <% end %>
229
+
230
+ <%= button_to todo_path(todo), class: "bg-red-600 px-4 py-2 rounded hover:bg-red-700", method: :delete do %>
231
+ <span class="sr-only">Delete</span>
232
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor" title="Delete todo">
233
+ <path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
234
+ </svg>
235
+ <% end %>
236
+ </div>
237
+ </div>
238
+ <% end %>
239
+ </li>
240
+ HTML
241
+
242
+ UniRails.register_view "todos/edit.html.erb", <<~'HTML'
243
+ <%= turbo_frame_tag dom_id(@todo) do %>
244
+ <%= render "form", todo: @todo %>
245
+ <% end %>
246
+ HTML
247
+
248
+ UniRails.register_view "todos/create.turbo_stream.erb", <<~'HTML'
249
+ <%= turbo_stream.prepend "todos" do %>
250
+ <%= render "todo", todo: @todo %>
251
+ <% end %>
252
+ <%= turbo_stream.replace "#{dom_id(Todo.new)}_form" do %>
253
+ <%= render "form", todo: Todo.new %>
254
+ <% end %>
255
+ HTML
256
+
257
+ UniRails.register_view "todos/update.turbo_stream.erb", <<~'HTML'
258
+ <%= turbo_stream.replace "#{dom_id(@todo)}_container" do %>
259
+ <%= render "todo", todo: @todo %>
260
+ <% end %>
261
+ HTML
262
+
263
+ UniRails.run(Port: 3000)
@@ -0,0 +1,237 @@
1
+ # Run the application
2
+ # $ ruby todos-scaffold.rb
3
+
4
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
5
+ ENV['DATABASE_URL'] = "sqlite3:///#{Dir.pwd}/todos-scaffold.sqlite"
6
+
7
+ require "bundler/inline"
8
+
9
+ gemfile(true) do
10
+ source "https://rubygems.org"
11
+
12
+ gem 'uni_rails'
13
+ gem 'sqlite3', '~> 1.7'
14
+ gem 'jbuilder' # jbuilder allows the .jbuilder extension for views
15
+ end
16
+
17
+ require 'uni_rails'
18
+ require 'sqlite3'
19
+
20
+ ActiveRecord::Base.establish_connection
21
+ ActiveRecord::Schema.define do
22
+ create_table :todos, force: :cascade do |t|
23
+ t.string :name
24
+ t.datetime :completed_at
25
+ t.timestamps
26
+ end
27
+ end
28
+
29
+ UniRails::App.routes.append do
30
+ root 'todos#index'
31
+ resources :todos
32
+ end
33
+
34
+ # MODELS
35
+
36
+ class Todo < ActiveRecord::Base
37
+ validates :name, presence: true
38
+
39
+ def complete(at: Time.zone.now)
40
+ update(completed_at: at)
41
+ end
42
+ end
43
+
44
+
45
+ # CONTROLLERS
46
+ class ApplicationController < ActionController::Base
47
+ end
48
+
49
+ class TodosController < ApplicationController
50
+ before_action :set_todo, only: %i[ show edit update destroy ]
51
+
52
+ # GET /todos or /todos.json
53
+ def index
54
+ @todos = Todo.all
55
+ end
56
+
57
+ # GET /todos/1 or /todos/1.json
58
+ def show
59
+ end
60
+
61
+ # GET /todos/new
62
+ def new
63
+ @todo = Todo.new
64
+ end
65
+
66
+ # GET /todos/1/edit
67
+ def edit
68
+ end
69
+
70
+ # POST /todos or /todos.json
71
+ def create
72
+ @todo = Todo.new(todo_params)
73
+
74
+ respond_to do |format|
75
+ if @todo.save
76
+ format.html { redirect_to todo_url(@todo), notice: "Todo was successfully created." }
77
+ format.json { render :show, status: :created, location: @todo }
78
+ else
79
+ format.html { render :new, status: :unprocessable_entity }
80
+ format.json { render json: @todo.errors, status: :unprocessable_entity }
81
+ end
82
+ end
83
+ end
84
+
85
+ # PATCH/PUT /todos/1 or /todos/1.json
86
+ def update
87
+ respond_to do |format|
88
+ if @todo.update(todo_params)
89
+ format.html { redirect_to todo_url(@todo), notice: "Todo was successfully updated." }
90
+ format.json { render :show, status: :ok, location: @todo }
91
+ else
92
+ format.html { render :edit, status: :unprocessable_entity }
93
+ format.json { render json: @todo.errors, status: :unprocessable_entity }
94
+ end
95
+ end
96
+ end
97
+
98
+ # DELETE /todos/1 or /todos/1.json
99
+ def destroy
100
+ @todo.destroy!
101
+
102
+ respond_to do |format|
103
+ format.html { redirect_to todos_url, notice: "Todo was successfully destroyed." }
104
+ format.json { head :no_content }
105
+ end
106
+ end
107
+
108
+ private
109
+ # Use callbacks to share common setup or constraints between actions.
110
+ def set_todo
111
+ @todo = Todo.find(params[:id])
112
+ end
113
+
114
+ # Only allow a list of trusted parameters through.
115
+ def todo_params
116
+ params.require(:todo).permit(:name, :completed_at)
117
+ end
118
+ end
119
+
120
+ # VIEWS - HTML
121
+
122
+ UniRails.register_view "todos/_form.html.erb", <<~HTML
123
+ <%= form_with(model: todo) do |form| %>
124
+ <% if todo.errors.any? %>
125
+ <div style="color: red">
126
+ <h2><%= pluralize(todo.errors.count, "error") %> prohibited this todo from being saved:</h2>
127
+
128
+ <ul>
129
+ <% todo.errors.each do |error| %>
130
+ <li><%= error.full_message %></li>
131
+ <% end %>
132
+ </ul>
133
+ </div>
134
+ <% end %>
135
+
136
+ <div>
137
+ <%= form.label :name, style: "display: block" %>
138
+ <%= form.text_field :name %>
139
+ </div>
140
+
141
+ <div>
142
+ <%= form.label :completed_at, style: "display: block" %>
143
+ <%= form.datetime_field :completed_at %>
144
+ </div>
145
+
146
+ <div>
147
+ <%= form.submit %>
148
+ </div>
149
+ <% end %>
150
+ HTML
151
+
152
+ UniRails.register_view "todos/_todo.html.erb", <<~HTML
153
+ <div id="<%= dom_id todo %>">
154
+ <p>
155
+ <strong>Name:</strong>
156
+ <%= todo.name %>
157
+ </p>
158
+
159
+ <p>
160
+ <strong>Completed at:</strong>
161
+ <%= todo.completed_at %>
162
+ </p>
163
+
164
+ </div>
165
+ HTML
166
+
167
+ UniRails.register_view "todos/edit.html.erb", <<~HTML
168
+ <h1>Editing todo</h1>
169
+
170
+ <%= render "form", todo: @todo %>
171
+
172
+ <br>
173
+
174
+ <div>
175
+ <%= link_to "Show this todo", @todo %> |
176
+ <%= link_to "Back to todos", todos_path %>
177
+ </div>
178
+ HTML
179
+
180
+ UniRails.register_view "todos/index.html.erb", <<~HTML
181
+ <p style="color: green"><%= notice %></p>
182
+
183
+ <h1>Todos</h1>
184
+
185
+ <div id="todos">
186
+ <% @todos.each do |todo| %>
187
+ <%= render todo %>
188
+ <p>
189
+ <%= link_to "Show this todo", todo %>
190
+ </p>
191
+ <% end %>
192
+ </div>
193
+
194
+ <%= link_to "New todo", new_todo_path %>
195
+ HTML
196
+
197
+ UniRails.register_view "todos/new.html.erb", <<~HTML
198
+ <h1>New todo</h1>
199
+
200
+ <%= render "form", todo: @todo %>
201
+
202
+ <br>
203
+
204
+ <div>
205
+ <%= link_to "Back to todos", todos_path %>
206
+ </div>
207
+ HTML
208
+
209
+ UniRails.register_view "todos/show.html.erb", <<~HTML
210
+ <p style="color: green"><%= notice %></p>
211
+
212
+ <%= render @todo %>
213
+
214
+ <div>
215
+ <%= link_to "Edit this todo", edit_todo_path(@todo) %> |
216
+ <%= link_to "Back to todos", todos_path %>
217
+
218
+ <%= button_to "Destroy this todo", @todo, method: :delete %>
219
+ </div>
220
+ HTML
221
+
222
+ # VIEWS - JSON
223
+
224
+ UniRails.register_view "todos/_todo.json.jbuilder", <<~RUBY
225
+ json.extract! todo, :id, :name, :completed_at, :created_at, :updated_at
226
+ json.url todo_url(todo, format: :json)
227
+ RUBY
228
+
229
+ UniRails.register_view "todos/index.json.jbuilder", <<~RUBY
230
+ json.array! @todos, partial: "todos/todo", as: :todo
231
+ RUBY
232
+
233
+ UniRails.register_view "todos/show.json.jbuilder", <<~RUBY
234
+ json.partial! "todos/todo", todo: @todo
235
+ RUBY
236
+
237
+ UniRails.run(Port: 3000)
@@ -3,12 +3,8 @@ module UniRails
3
3
  class CSS
4
4
  include Singleton
5
5
 
6
- def self.css=(content)
7
- instance.css = content
8
- end
9
-
10
- def self.css
11
- instance.css
6
+ class << self
7
+ delegate :css, :css=, to: :instance
12
8
  end
13
9
 
14
10
  attr_accessor :css
@@ -3,20 +3,11 @@ module UniRails
3
3
  class Javascript
4
4
  include Singleton
5
5
 
6
- def self.imports
7
- instance.imports
8
- end
9
-
10
- def self.dependencies=(dependencies)
11
- instance.dependencies = dependencies
12
- end
13
-
14
- def self.javascript
15
- instance.javascript
16
- end
17
-
18
- def self.javascript=(content)
19
- instance.javascript = content
6
+ class << self
7
+ delegate :imports,
8
+ :dependencies, :dependencies=,
9
+ :javascript, :javascript=,
10
+ to: :instance
20
11
  end
21
12
 
22
13
  attr_accessor :dependencies, :javascript
data/lib/uni_rails/app.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # Pick the frameworks you want:
2
2
 
3
- # require "active_model/railtie"
3
+ require "active_model/railtie"
4
4
  # require "active_job/railtie"
5
- require "active_record/railtie"
5
+ require "active_record/railtie" unless ENV['DATABASE_URL'].nil?
6
6
  # require "active_storage/engine"
7
7
  require "action_controller/railtie"
8
8
  # require "action_mailer/railtie"
@@ -15,10 +15,12 @@ require "action_view/testing/resolvers"
15
15
 
16
16
  module UniRails
17
17
  class App < Rails::Application
18
+ config.load_defaults 7.1
19
+
18
20
  config.eager_load = true
19
21
  config.logger = Logger.new(STDOUT)
20
22
  config.log_level = :debug
21
- config.secret_key_base = "whatever"
23
+ config.secret_key_base = ENV.fetch('SECRET_KEY_BASE')
22
24
 
23
25
  config.after_initialize do
24
26
  ActionController::Base.view_paths = Views.view_paths
@@ -1,7 +1,7 @@
1
1
  module UniRails
2
2
  module Helpers
3
3
  module CSSHelper
4
- def mono_rails_css_stylesheet
4
+ def uni_rails_css_stylesheet
5
5
  content_tag(:style) do
6
6
  raw App::CSS.css
7
7
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UniRails
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/uni_rails.rb CHANGED
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ if ENV['SECRET_KEY_BASE'].nil?
4
+ raise StandardError, <<~ERROR
5
+
6
+ SECRET_KEY_BASE environment variable is required
7
+ Provide ENV['SECRET_KEY_BASE'] in your file or export the variable to your profile
8
+ ERROR
9
+ end
10
+
3
11
  require "rails"
4
12
  require_relative "uni_rails/version"
5
13
  require_relative "uni_rails/helpers"
@@ -13,19 +21,52 @@ require_relative "uni_rails/app/views"
13
21
  module UniRails
14
22
  class Error < StandardError; end
15
23
 
24
+ def self.enable_turbo_rails!
25
+ # # We currently do not support ActionCable and ActiveJob
26
+ require "turbo-rails"
27
+
28
+ App.configure do
29
+ initializer "turbo.no_action_cable", before: :set_eager_load_paths do
30
+ unless defined?(ActionCable)
31
+ Rails.autoloaders.once.do_not_eager_load("#{Turbo::Engine.root}/app/channels")
32
+ end
33
+
34
+ unless defined?(ActiveJob)
35
+ Rails.autoloaders.once.do_not_eager_load("#{Turbo::Engine.root}/app/jobs")
36
+ end
37
+ end
38
+ end
39
+
40
+ App::Javascript.dependencies.merge!(
41
+ "turbo" => "https://unpkg.com/@hotwired/turbo@8.0.4/dist/turbo.es2017-umd.js"
42
+ )
43
+ end
44
+
45
+ def self.rackup_handler=(handler)
46
+ @@rackup_handler = handler
47
+ end
48
+
49
+ def self.rackup_handler
50
+ @@rackup_handler ||= begin
51
+ require 'rackup'
52
+ Rackup::Handler::WEBrick
53
+ end
54
+ end
55
+
16
56
  def self.register_view(action, view)
17
57
  UniRails::App::Views.instance.views[action] = view
18
58
  end
19
59
 
20
60
  def self.run(**webrick_options)
21
61
  App.initialize!
22
- # Rackup::Handler::WEBrick.run App, **webrick_options
62
+ rackup_handler.run App, **webrick_options
23
63
  # Rackup::Handler::Falcon.run App, **webrick_options
24
- Rack::Handler::Puma.run App, **webrick_options
64
+ # Rack::Handler::Puma.run App, **webrick_options
65
+ # Rackup::Handler::WEBrick.run App, **webrick_options
25
66
  end
26
67
 
27
68
  def self.import_maps(dependencies)
28
- UniRails::App::Javascript.dependencies = dependencies
69
+ UniRails::App::Javascript.dependencies.merge! dependencies
29
70
  end
30
71
 
31
72
  def self.javascript(content)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uni_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Barret
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-30 00:00:00.000000000 Z
11
+ date: 2024-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '7.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rackup
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: turbo-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
27
55
  description:
28
56
  email:
29
57
  - barret.alx@gmail.com
@@ -35,9 +63,12 @@ files:
35
63
  - LICENSE.txt
36
64
  - README.md
37
65
  - Rakefile
38
- - examples/hotwire.rb
39
- - examples/json_api.rb
40
- - examples/todos.rb
66
+ - examples/hello-world.rb
67
+ - examples/server-falcon-app.rb
68
+ - examples/server-puma-app.rb
69
+ - examples/todos-api.rb
70
+ - examples/todos-hotwire.rb
71
+ - examples/todos-scaffold.rb
41
72
  - lib/uni_rails.rb
42
73
  - lib/uni_rails/app.rb
43
74
  - lib/uni_rails/app/css.rb
@@ -69,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
100
  - !ruby/object:Gem::Version
70
101
  version: '0'
71
102
  requirements: []
72
- rubygems_version: 3.4.21
103
+ rubygems_version: 3.5.3
73
104
  signing_key:
74
105
  specification_version: 4
75
106
  summary: A Ruby library to create single-file Rails application
data/examples/hotwire.rb DELETED
File without changes
data/examples/json_api.rb DELETED
File without changes
data/examples/todos.rb DELETED
File without changes