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 +4 -4
- data/README.md +10 -7
- data/examples/hello-world.rb +25 -0
- data/examples/server-falcon-app.rb +30 -0
- data/examples/server-puma-app.rb +30 -0
- data/examples/todos-api.rb +103 -0
- data/examples/todos-hotwire.rb +263 -0
- data/examples/todos-scaffold.rb +237 -0
- data/lib/uni_rails/app/css.rb +2 -6
- data/lib/uni_rails/app/javascript.rb +5 -14
- data/lib/uni_rails/app.rb +5 -3
- data/lib/uni_rails/helpers/css_helper.rb +1 -1
- data/lib/uni_rails/version.rb +1 -1
- data/lib/uni_rails.rb +44 -3
- metadata +37 -6
- data/examples/hotwire.rb +0 -0
- data/examples/json_api.rb +0 -0
- data/examples/todos.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3dbb41ea67dff2755cb5b77eae07f174a95bf99de2cf03a084739213c99033c9
|
4
|
+
data.tar.gz: 5c9414944b4616651a429657025c58444f330ceea500f164a92a8d7afcefc824
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 980ece2224b2c31320c749eb07676912f27bf5285d6dcefcacae4196f15061140185cb8b51a565d4605db0fd95a669ec543b1750fcdeb024fab4cc7d5a61d93a
|
7
|
+
data.tar.gz: 49e8aa1ff7ec06d23cfa5a07d8d9546e036fe6255c71f6766faa42416631d0fdaf9d572333ebd066f6265e693f731586f77e19f3858f929f351cd82f28c4ff99
|
data/README.md
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
# UniRails
|
2
2
|
|
3
|
-
UniRails
|
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
|
-
|
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
|
-
|
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
|
-
- [
|
14
|
-
- [
|
15
|
-
- [
|
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
|
-
- [
|
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)
|
data/lib/uni_rails/app/css.rb
CHANGED
@@ -3,20 +3,11 @@ module UniRails
|
|
3
3
|
class Javascript
|
4
4
|
include Singleton
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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 =
|
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
|
data/lib/uni_rails/version.rb
CHANGED
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
|
-
|
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
|
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.
|
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-
|
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/
|
39
|
-
- examples/
|
40
|
-
- examples/
|
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.
|
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
|