uni_rails 0.2.2 → 0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1033c5128ac63cace79cbe7cec088e1017561c8c31c2a86d78adefd574dd1c4d
4
- data.tar.gz: 43ce3852658188d1a20189e8c0950e7cc70ad6f5ffd4056caba92f0a0fb75785
3
+ metadata.gz: c4f9cd3df989797cabd98e67117c2767bf2aa19d8d586b86e62e8db1bc5962b4
4
+ data.tar.gz: 77c6a558483c47a3f08f8acbc1139290a90e2313f9f39cb48a17efb6996542d3
5
5
  SHA512:
6
- metadata.gz: c1d94d2e32d53ec78056169347e89b94c67d23805ddd5c6084f50929b2735e06e1d94d000697d5414cec954c5e5ffc0839162c90d052a36029c8d8e863d92423
7
- data.tar.gz: 2868ac060f8e8f33fbdee31e18eadd2466aa0e20dfbe864d92722fa6286e03a1dc2c6cb980a9d312b851dc45016a4612e962a41a9de1d5270031892127c23edc
6
+ metadata.gz: c10ef57227cf6742fed28dd95c35d221dc3307a126244da83b34c7dc7fdd7b2d022ef63a92f607a873e276538e8bb665a863b3828e85d95f64647eb395f7394d
7
+ data.tar.gz: 2d7db16654876db5b7f28fd10d0ca5019a1070f8e90c2e6167f96e242d8b7c9dc535194a3d77635f1fa77b405f8dafb3f5860daf140265f6956c1cc14970f565
data/README.md CHANGED
@@ -1,18 +1,22 @@
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
- See some examples of how the UniRails library can be used
11
+ See some examples of how the UniRails library can be used. Running an example is a easy as `ruby filename.rb`
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 StimulusJS](/examples/stimulus-app.rb)
18
+ - [App using Puma server](/examples/server-puma-app.rb)
19
+ - [App using Falcon server](/examples/server-falcon-app.rb)
16
20
 
17
21
  ## Development
18
22
 
@@ -30,7 +34,7 @@ We would like to support all railties and engines. Please help us support more o
30
34
 
31
35
  - [X] action_controller
32
36
  - [X] active_record
33
- - [ ] active_model/railtie
37
+ - [X] active_model/railtie
34
38
  - [ ] active_job/railtie
35
39
  - [ ] active_storage/engine
36
40
  - [ ] action_mailer/railtie
@@ -0,0 +1,88 @@
1
+ ENV["SECRET_KEY_BASE"] = "1212312313"
2
+ ENV["DATABASE_URL"] = "sqlite3:///#{__dir__}/database.sqlite"
3
+
4
+ require "bundler/inline"
5
+
6
+ gemfile do
7
+ source "https://www.rubygems.org"
8
+ gem "uni_rails"
9
+ gem "sqlite3", "~> 1.7"
10
+ gem "byebug"
11
+ end
12
+
13
+ require "uni_rails"
14
+ require "sqlite3"
15
+ require "byebug"
16
+
17
+ # ==== ROUTES ====
18
+ UniRails::App.routes.append do
19
+ root "resource_names#new"
20
+ resources :resource_names
21
+ end
22
+
23
+ # ==== DB SCHEMA ====
24
+ ActiveRecord::Base.establish_connection
25
+ ActiveRecord::Schema.define do
26
+ create_table :resources, force: :cascade do |t|
27
+ t.string :title
28
+ end
29
+ end
30
+
31
+ # ==== MODELS ====
32
+ class Resource < ActiveRecord::Base
33
+ end
34
+
35
+ # ==== SEEDS ====
36
+ # Create your existing records here
37
+
38
+ # ==== CONTROLLERS ====
39
+ class ResourceNamesController < ActionController::Base
40
+ layout 'application'
41
+
42
+ def new
43
+ end
44
+
45
+ def create
46
+ end
47
+ end
48
+
49
+ # ==== IMPORT MAPS ====
50
+ UniRails.import_maps(
51
+ 'stimulus' => 'https://unpkg.com/@hotwired/stimulus/dist/stimulus.js'
52
+ )
53
+
54
+
55
+ # ==== JAVASCRTIP ====
56
+ UniRails.javascript <<~JAVASCRIPT
57
+ import { Application, Controller } from "stimulus"
58
+ window.Stimulus = Application.start()
59
+
60
+ Stimulus.register("hello", class extends Controller {
61
+ connect() {
62
+ console.log("hello world")
63
+ }
64
+ })
65
+
66
+ JAVASCRIPT
67
+
68
+ # ==== CSS ====
69
+ UniRails.css <<~CSS
70
+ html { background-color:#EEE; }
71
+ body { width:500px; height:700px; margin:auto;
72
+ background-color:white; padding:1rem;
73
+ }
74
+ form {
75
+ label { display: block; }
76
+ input[type="submit"] { display: block; margin-top:1rem; }
77
+ .field_with_errors { color:red; display:inline; }
78
+ }
79
+ CSS
80
+
81
+ # ==== VIEWS ====
82
+ UniRails.register_view "resource_names/new.html.erb", <<~HTML
83
+ HTML
84
+
85
+ UniRails.register_view "resource_names/show.html.erb", <<~HTML
86
+ HTML
87
+
88
+ UniRails.run(Port: 3000)
@@ -0,0 +1,23 @@
1
+
2
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
3
+
4
+ require "bundler/inline"
5
+
6
+ gemfile(true) do
7
+ source "https://rubygems.org"
8
+ gem 'uni_rails'
9
+ end
10
+
11
+ require 'uni_rails'
12
+
13
+ UniRails::App.routes.append do
14
+ root 'pages#index'
15
+ end
16
+
17
+ class PagesController < ActionController::Base
18
+ def index
19
+ render plain: 'Hello world'
20
+ end
21
+ end
22
+
23
+ UniRails.run(Port: 3000)
@@ -0,0 +1,28 @@
1
+
2
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
3
+
4
+ require "bundler/inline"
5
+
6
+ gemfile(true) do
7
+ source "https://rubygems.org"
8
+
9
+ gem 'uni_rails'
10
+ gem 'falcon'
11
+ end
12
+
13
+ require 'uni_rails'
14
+ require 'rackup/handler/falcon'
15
+
16
+ UniRails.rackup_handler = Rackup::Handler::Falcon
17
+
18
+ UniRails::App.routes.append do
19
+ root 'pages#index'
20
+ end
21
+
22
+ class PagesController < ActionController::Base
23
+ def index
24
+ render plain: 'Application served by Falcon'
25
+ end
26
+ end
27
+
28
+ UniRails.run(Port: 3000)
@@ -0,0 +1,28 @@
1
+
2
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
3
+
4
+ require "bundler/inline"
5
+
6
+ gemfile(true) do
7
+ source "https://rubygems.org"
8
+
9
+ gem 'uni_rails'
10
+ gem 'puma'
11
+ end
12
+
13
+ require 'uni_rails'
14
+ require 'rack/handler/puma'
15
+
16
+ UniRails.rackup_handler = Rack::Handler::Puma
17
+
18
+ UniRails::App.routes.append do
19
+ root 'pages#index'
20
+ end
21
+
22
+ class PagesController < ActionController::Base
23
+ def index
24
+ render plain: 'Application served by Puma'
25
+ end
26
+ end
27
+
28
+ UniRails.run(Port: 3000)
@@ -0,0 +1,41 @@
1
+
2
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
3
+
4
+ require "bundler/inline"
5
+
6
+ gemfile(true) do
7
+ source "https://rubygems.org"
8
+ gem 'uni_rails'
9
+ end
10
+
11
+ require 'uni_rails'
12
+
13
+ UniRails::App.routes.append do
14
+ root 'pages#index'
15
+ end
16
+
17
+ UniRails.import_maps(
18
+ 'stimulus' => 'https://unpkg.com/@hotwired/stimulus/dist/stimulus.js'
19
+ )
20
+
21
+ UniRails.javascript <<~JAVASCRIPT
22
+ import { Application, Controller } from "stimulus"
23
+ window.Stimulus = Application.start()
24
+
25
+ Stimulus.register("hello", class extends Controller {
26
+ connect() {
27
+ console.log("hello world")
28
+ }
29
+ })
30
+ JAVASCRIPT
31
+
32
+ class PagesController < ActionController::Base
33
+ layout 'application'
34
+ def index;end
35
+ end
36
+
37
+ UniRails.register_view "pages/index.html.erb", <<~HTML
38
+ <div data-controller="hello">Check out the dev console to see "hello world"</div>
39
+ HTML
40
+
41
+ UniRails.run(Port: 3000)
@@ -0,0 +1,101 @@
1
+
2
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
3
+ ENV['DATABASE_URL'] = "sqlite3:///#{Dir.pwd}/todos-api.sqlite"
4
+
5
+ require "bundler/inline"
6
+
7
+ gemfile(true) do
8
+ source "https://rubygems.org"
9
+
10
+ gem 'uni_rails'
11
+ gem 'sqlite3', '~> 1.7'
12
+ end
13
+
14
+ require 'uni_rails'
15
+ require 'sqlite3'
16
+
17
+ ActiveRecord::Base.establish_connection
18
+ ActiveRecord::Schema.define do
19
+ create_table :todos, force: :cascade do |t|
20
+ t.string :name
21
+ t.datetime :completed_at
22
+ t.timestamps
23
+ end
24
+ end
25
+
26
+ UniRails::App.routes.append do
27
+ resources :todos do
28
+ put :complete, on: :member
29
+ end
30
+ end
31
+
32
+ # MODELS
33
+
34
+ class Todo < ActiveRecord::Base
35
+ validates :name, presence: true
36
+
37
+ def complete(at: Time.zone.now)
38
+ update(completed_at: at)
39
+ end
40
+ end
41
+
42
+ # CONTROLLERS
43
+
44
+ class ApplicationController < ActionController::Base
45
+ protect_from_forgery with: :null_session
46
+ end
47
+
48
+ class TodosController < ApplicationController
49
+ before_action :set_todo, only: [:show, :update, :destroy, :complete]
50
+
51
+ def index
52
+ @todos = Todo.all
53
+ render json: @todos
54
+ end
55
+
56
+ def show
57
+ render json: @todo
58
+ end
59
+
60
+ def create
61
+ @todo = Todo.new(todo_params)
62
+ if @todo.save
63
+ render json: @todo, status: :created, location: @todo
64
+ else
65
+ render json: @todo.errors, status: :unprocessable_entity
66
+ end
67
+ end
68
+
69
+ def update
70
+ if @todo.update(todo_params)
71
+ render json: @todo
72
+ else
73
+ render json: @todo.errors, status: :unprocessable_entity
74
+ end
75
+ end
76
+
77
+ def complete
78
+ if @todo.complete
79
+ render json: @todo
80
+ else
81
+ render json: @todo.errors, status: :unprocessable_entity
82
+ end
83
+ end
84
+
85
+ def destroy
86
+ @todo.destroy
87
+ head :no_content
88
+ end
89
+
90
+ private
91
+
92
+ def set_todo
93
+ @todo = Todo.find(params[:id])
94
+ end
95
+
96
+ def todo_params
97
+ params.require(:todo).permit(:name, :status)
98
+ end
99
+ end
100
+
101
+ UniRails.run(Port: 3000)
@@ -0,0 +1,261 @@
1
+ # Based on the Hotwire tutorial: https://www.colby.so/posts/turbo-rails-101-todo-list
2
+
3
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
4
+ ENV['DATABASE_URL'] = "sqlite3:///#{Dir.pwd}/todos-hotwire.sqlite"
5
+
6
+ require "bundler/inline"
7
+
8
+ gemfile(true) do
9
+ source "https://rubygems.org"
10
+
11
+ gem 'uni_rails'
12
+ gem 'sqlite3', '~> 1.7'
13
+ gem 'debug'
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.integer :status, default: 0
24
+ t.timestamps
25
+ end
26
+ end
27
+
28
+ UniRails.enable_turbo_rails!
29
+
30
+ UniRails::App.routes.append do
31
+ root 'todos#index'
32
+ resources :todos do
33
+ patch :change_status, on: :member
34
+ end
35
+ end
36
+
37
+ # MODELS
38
+
39
+ class Todo < ActiveRecord::Base
40
+ enum status: {
41
+ incomplete: 0,
42
+ complete: 1
43
+ }
44
+
45
+ validates :name, presence: true
46
+ end
47
+
48
+
49
+ # CONTROLLERS
50
+
51
+ class TodosController < ActionController::Base
52
+ layout 'application'
53
+
54
+ before_action :set_todo, only: [:edit, :update, :destroy, :change_status]
55
+
56
+ def index
57
+ @todos = Todo.where(status: params[:status].presence || 'incomplete')
58
+ end
59
+
60
+ def new
61
+ @todo = Todo.new
62
+ end
63
+
64
+ def edit
65
+ @todo = Todo.find(params[:id])
66
+ end
67
+
68
+ def create
69
+ @todo = Todo.new(todo_params)
70
+
71
+ respond_to do |format|
72
+ if @todo.save
73
+ format.turbo_stream
74
+ format.html { redirect_to todo_url(@todo), notice: "Todo was successfully created." }
75
+ else
76
+ format.turbo_stream { render turbo_stream: turbo_stream.replace("#{helpers.dom_id(@todo)}_form", partial: "form", locals: { todo: @todo }) }
77
+ format.html { render :new, status: :unprocessable_entity }
78
+ end
79
+ end
80
+ end
81
+
82
+ def update
83
+ respond_to do |format|
84
+ if @todo.update(todo_params)
85
+ format.turbo_stream
86
+ format.html { redirect_to todo_url(@todo), notice: "Todo was successfully updated." }
87
+ format.json { render :show, status: :ok, location: @todo }
88
+ else
89
+ format.turbo_stream { render turbo_stream: turbo_stream.replace("#{helpers.dom_id(@todo)}_form", partial: "form", locals: { todo: @todo }) }
90
+ format.html { render :edit, status: :unprocessable_entity }
91
+ format.json { render json: @todo.errors, status: :unprocessable_entity }
92
+ end
93
+ end
94
+ end
95
+
96
+ def destroy
97
+ @todo.destroy
98
+
99
+ respond_to do |format|
100
+ format.turbo_stream { render turbo_stream: turbo_stream.remove("#{helpers.dom_id(@todo)}_container") }
101
+ format.html { redirect_to todos_url, notice: "Todo was successfully destroyed." }
102
+ format.json { head :no_content }
103
+ end
104
+ end
105
+
106
+ def change_status
107
+ @todo.update(status: todo_params[:status])
108
+ respond_to do |format|
109
+ format.turbo_stream { render turbo_stream: turbo_stream.remove("#{helpers.dom_id(@todo)}_container") }
110
+ format.html { redirect_to todos_path, notice: "Updated todo status." }
111
+ end
112
+ end
113
+
114
+ private
115
+
116
+ def set_todo
117
+ @todo = Todo.find(params[:id])
118
+ end
119
+
120
+ def todo_params
121
+ params.require(:todo).permit(:name, :status)
122
+ end
123
+ end
124
+
125
+ # VIEWS
126
+
127
+ UniRails.javascript <<~JAVASCRIPT
128
+ import * as Turbo from "turbo"
129
+ JAVASCRIPT
130
+
131
+ UniRails.register_view "layouts/application.html.erb", <<~'HTML'
132
+ <!DOCTYPE html>
133
+ <html>
134
+ <head>
135
+ <title>Template</title>
136
+ <meta name="viewport" content="width=device-width,initial-scale=1">
137
+ <%= csrf_meta_tags %>
138
+ <%= csp_meta_tag %>
139
+ <script src="https://cdn.tailwindcss.com"></script>
140
+ <%= uni_rails_css_stylesheet %>
141
+ <%= uni_rails_import_map_tag %>
142
+ <%= uni_rails_javascript_script %>
143
+ </head>
144
+
145
+ <body>
146
+ <%= yield %>
147
+ </body>
148
+ </html>
149
+ HTML
150
+
151
+ UniRails.register_view "todos/index.html.erb", <<~'HTML'
152
+ <div class="mx-auto w-1/2">
153
+ <h2 class="text-2xl text-gray-900">
154
+ Your todos
155
+ </h2>
156
+ <%= 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 %>
157
+ <div class="border-b border-gray-200 w-full">
158
+ <ul class="flex space-x-2 justify-center">
159
+ <li>
160
+ <%= link_to "Incomplete",
161
+ todos_path(status: "incomplete"),
162
+ 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"
163
+ %>
164
+ <li>
165
+ <%= link_to "Complete",
166
+ todos_path(status: "complete"),
167
+ 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"
168
+ %>
169
+ </li>
170
+ </ul>
171
+ </div>
172
+ <% unless params[:status] == "complete" %>
173
+ <div class="py-2 px-4">
174
+ <%= render "form", todo: Todo.new %>
175
+ </div>
176
+ <% end %>
177
+ <ul id="todos">
178
+ <%= render @todos %>
179
+ </ul>
180
+ <% end %>
181
+ </div>
182
+ HTML
183
+
184
+ UniRails.register_view "todos/_form.html.erb", <<~'HTML'
185
+ <%= form_with(model: todo, id: "#{dom_id(todo)}_form") do |form| %>
186
+ <% if todo.errors.any? %>
187
+ <div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
188
+ <h2><%= pluralize(todo.errors.count, "error") %> prohibited this todo from being saved:</h2>
189
+
190
+ <ul>
191
+ <% todo.errors.each do |error| %>
192
+ <li><%= error.full_message %></li>
193
+ <% end %>
194
+ </ul>
195
+ </div>
196
+ <% end %>
197
+ <div class="flex items-stretch flex-grow">
198
+ <%= form.label :name, class: "sr-only" %>
199
+ <%= form.text_field :name, class: "block w-full rounded-none rounded-l-md sm:text-sm border-gray-300", placeholder: "Add a new todo..." %>
200
+ <%= 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" %>
201
+ </div>
202
+ <% end %>
203
+ HTML
204
+
205
+ UniRails.register_view "todos/_todo.html.erb", <<~'HTML'
206
+ <li id="<%= "#{dom_id(todo)}_container" %>" class="py-2 px-4 border-b border-gray-300">
207
+ <%= turbo_frame_tag dom_id(todo) do %>
208
+ <div class="flex justify-between items-center space-x-2">
209
+ <%= link_to todo.name, edit_todo_path(todo), class: todo.complete? ? 'line-through' : '' %>
210
+
211
+ <div class="flex justify-end space-x-3">
212
+ <% if todo.complete? %>
213
+ <%= 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 %>
214
+ <span class="sr-only">Mark as incomplete</span>
215
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor">
216
+ <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" />
217
+ </svg>
218
+ <% end %>
219
+ <% else %>
220
+ <%= 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 %>
221
+ <span class="sr-only">Mark as complete</span>
222
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor">
223
+ <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" />
224
+ </svg>
225
+ <% end %>
226
+ <% end %>
227
+
228
+ <%= button_to todo_path(todo), class: "bg-red-600 px-4 py-2 rounded hover:bg-red-700", method: :delete do %>
229
+ <span class="sr-only">Delete</span>
230
+ <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">
231
+ <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" />
232
+ </svg>
233
+ <% end %>
234
+ </div>
235
+ </div>
236
+ <% end %>
237
+ </li>
238
+ HTML
239
+
240
+ UniRails.register_view "todos/edit.html.erb", <<~'HTML'
241
+ <%= turbo_frame_tag dom_id(@todo) do %>
242
+ <%= render "form", todo: @todo %>
243
+ <% end %>
244
+ HTML
245
+
246
+ UniRails.register_view "todos/create.turbo_stream.erb", <<~'HTML'
247
+ <%= turbo_stream.prepend "todos" do %>
248
+ <%= render "todo", todo: @todo %>
249
+ <% end %>
250
+ <%= turbo_stream.replace "#{dom_id(Todo.new)}_form" do %>
251
+ <%= render "form", todo: Todo.new %>
252
+ <% end %>
253
+ HTML
254
+
255
+ UniRails.register_view "todos/update.turbo_stream.erb", <<~'HTML'
256
+ <%= turbo_stream.replace "#{dom_id(@todo)}_container" do %>
257
+ <%= render "todo", todo: @todo %>
258
+ <% end %>
259
+ HTML
260
+
261
+ UniRails.run(Port: 3000)
@@ -0,0 +1,235 @@
1
+
2
+ ENV['SECRET_KEY_BASE'] = 'my_secret_key_base'
3
+ ENV['DATABASE_URL'] = "sqlite3:///#{Dir.pwd}/todos-scaffold.sqlite"
4
+
5
+ require "bundler/inline"
6
+
7
+ gemfile(true) do
8
+ source "https://rubygems.org"
9
+
10
+ gem 'uni_rails'
11
+ gem 'sqlite3', '~> 1.7'
12
+ gem 'jbuilder' # jbuilder allows the .jbuilder extension for views
13
+ end
14
+
15
+ require 'uni_rails'
16
+ require 'sqlite3'
17
+
18
+ ActiveRecord::Base.establish_connection
19
+ ActiveRecord::Schema.define do
20
+ create_table :todos, force: :cascade do |t|
21
+ t.string :name
22
+ t.datetime :completed_at
23
+ t.timestamps
24
+ end
25
+ end
26
+
27
+ UniRails::App.routes.append do
28
+ root 'todos#index'
29
+ resources :todos
30
+ end
31
+
32
+ # MODELS
33
+
34
+ class Todo < ActiveRecord::Base
35
+ validates :name, presence: true
36
+
37
+ def complete(at: Time.zone.now)
38
+ update(completed_at: at)
39
+ end
40
+ end
41
+
42
+
43
+ # CONTROLLERS
44
+ class ApplicationController < ActionController::Base
45
+ end
46
+
47
+ class TodosController < ApplicationController
48
+ before_action :set_todo, only: %i[ show edit update destroy ]
49
+
50
+ # GET /todos or /todos.json
51
+ def index
52
+ @todos = Todo.all
53
+ end
54
+
55
+ # GET /todos/1 or /todos/1.json
56
+ def show
57
+ end
58
+
59
+ # GET /todos/new
60
+ def new
61
+ @todo = Todo.new
62
+ end
63
+
64
+ # GET /todos/1/edit
65
+ def edit
66
+ end
67
+
68
+ # POST /todos or /todos.json
69
+ def create
70
+ @todo = Todo.new(todo_params)
71
+
72
+ respond_to do |format|
73
+ if @todo.save
74
+ format.html { redirect_to todo_url(@todo), notice: "Todo was successfully created." }
75
+ format.json { render :show, status: :created, location: @todo }
76
+ else
77
+ format.html { render :new, status: :unprocessable_entity }
78
+ format.json { render json: @todo.errors, status: :unprocessable_entity }
79
+ end
80
+ end
81
+ end
82
+
83
+ # PATCH/PUT /todos/1 or /todos/1.json
84
+ def update
85
+ respond_to do |format|
86
+ if @todo.update(todo_params)
87
+ format.html { redirect_to todo_url(@todo), notice: "Todo was successfully updated." }
88
+ format.json { render :show, status: :ok, location: @todo }
89
+ else
90
+ format.html { render :edit, status: :unprocessable_entity }
91
+ format.json { render json: @todo.errors, status: :unprocessable_entity }
92
+ end
93
+ end
94
+ end
95
+
96
+ # DELETE /todos/1 or /todos/1.json
97
+ def destroy
98
+ @todo.destroy!
99
+
100
+ respond_to do |format|
101
+ format.html { redirect_to todos_url, notice: "Todo was successfully destroyed." }
102
+ format.json { head :no_content }
103
+ end
104
+ end
105
+
106
+ private
107
+ # Use callbacks to share common setup or constraints between actions.
108
+ def set_todo
109
+ @todo = Todo.find(params[:id])
110
+ end
111
+
112
+ # Only allow a list of trusted parameters through.
113
+ def todo_params
114
+ params.require(:todo).permit(:name, :completed_at)
115
+ end
116
+ end
117
+
118
+ # VIEWS - HTML
119
+
120
+ UniRails.register_view "todos/_form.html.erb", <<~HTML
121
+ <%= form_with(model: todo) do |form| %>
122
+ <% if todo.errors.any? %>
123
+ <div style="color: red">
124
+ <h2><%= pluralize(todo.errors.count, "error") %> prohibited this todo from being saved:</h2>
125
+
126
+ <ul>
127
+ <% todo.errors.each do |error| %>
128
+ <li><%= error.full_message %></li>
129
+ <% end %>
130
+ </ul>
131
+ </div>
132
+ <% end %>
133
+
134
+ <div>
135
+ <%= form.label :name, style: "display: block" %>
136
+ <%= form.text_field :name %>
137
+ </div>
138
+
139
+ <div>
140
+ <%= form.label :completed_at, style: "display: block" %>
141
+ <%= form.datetime_field :completed_at %>
142
+ </div>
143
+
144
+ <div>
145
+ <%= form.submit %>
146
+ </div>
147
+ <% end %>
148
+ HTML
149
+
150
+ UniRails.register_view "todos/_todo.html.erb", <<~HTML
151
+ <div id="<%= dom_id todo %>">
152
+ <p>
153
+ <strong>Name:</strong>
154
+ <%= todo.name %>
155
+ </p>
156
+
157
+ <p>
158
+ <strong>Completed at:</strong>
159
+ <%= todo.completed_at %>
160
+ </p>
161
+
162
+ </div>
163
+ HTML
164
+
165
+ UniRails.register_view "todos/edit.html.erb", <<~HTML
166
+ <h1>Editing todo</h1>
167
+
168
+ <%= render "form", todo: @todo %>
169
+
170
+ <br>
171
+
172
+ <div>
173
+ <%= link_to "Show this todo", @todo %> |
174
+ <%= link_to "Back to todos", todos_path %>
175
+ </div>
176
+ HTML
177
+
178
+ UniRails.register_view "todos/index.html.erb", <<~HTML
179
+ <p style="color: green"><%= notice %></p>
180
+
181
+ <h1>Todos</h1>
182
+
183
+ <div id="todos">
184
+ <% @todos.each do |todo| %>
185
+ <%= render todo %>
186
+ <p>
187
+ <%= link_to "Show this todo", todo %>
188
+ </p>
189
+ <% end %>
190
+ </div>
191
+
192
+ <%= link_to "New todo", new_todo_path %>
193
+ HTML
194
+
195
+ UniRails.register_view "todos/new.html.erb", <<~HTML
196
+ <h1>New todo</h1>
197
+
198
+ <%= render "form", todo: @todo %>
199
+
200
+ <br>
201
+
202
+ <div>
203
+ <%= link_to "Back to todos", todos_path %>
204
+ </div>
205
+ HTML
206
+
207
+ UniRails.register_view "todos/show.html.erb", <<~HTML
208
+ <p style="color: green"><%= notice %></p>
209
+
210
+ <%= render @todo %>
211
+
212
+ <div>
213
+ <%= link_to "Edit this todo", edit_todo_path(@todo) %> |
214
+ <%= link_to "Back to todos", todos_path %>
215
+
216
+ <%= button_to "Destroy this todo", @todo, method: :delete %>
217
+ </div>
218
+ HTML
219
+
220
+ # VIEWS - JSON
221
+
222
+ UniRails.register_view "todos/_todo.json.jbuilder", <<~RUBY
223
+ json.extract! todo, :id, :name, :completed_at, :created_at, :updated_at
224
+ json.url todo_url(todo, format: :json)
225
+ RUBY
226
+
227
+ UniRails.register_view "todos/index.json.jbuilder", <<~RUBY
228
+ json.array! @todos, partial: "todos/todo", as: :todo
229
+ RUBY
230
+
231
+ UniRails.register_view "todos/show.json.jbuilder", <<~RUBY
232
+ json.partial! "todos/todo", todo: @todo
233
+ RUBY
234
+
235
+ UniRails.run(Port: 3000)
data/exe/unirails ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # require "byebug"
4
+ # byebug
5
+
6
+ assets_path = File.expand_path('../assets', __dir__)
7
+
8
+ options = {}
9
+ case ARGV[0]
10
+ when "new"
11
+ filename = ARGV[1]
12
+ File.open(filename, 'wb+') do |f|
13
+ f.write File.read File.join(assets_path, 'templates', 'default.txt')
14
+ end
15
+ else
16
+ puts "unknown command"
17
+ puts "Usage: unirails new <filename>"
18
+ end
@@ -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,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UniRails
4
- VERSION = "0.2.2"
4
+ VERSION = "0.4.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.2
4
+ version: 0.4.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-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,10 +24,39 @@ 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
30
- executables: []
58
+ executables:
59
+ - unirails
31
60
  extensions: []
32
61
  extra_rdoc_files: []
33
62
  files:
@@ -35,9 +64,15 @@ files:
35
64
  - LICENSE.txt
36
65
  - README.md
37
66
  - Rakefile
38
- - examples/hotwire.rb
39
- - examples/json_api.rb
40
- - examples/todos.rb
67
+ - assets/templates/default.txt
68
+ - examples/hello-world.rb
69
+ - examples/server-falcon-app.rb
70
+ - examples/server-puma-app.rb
71
+ - examples/stimulus-app.rb
72
+ - examples/todos-api.rb
73
+ - examples/todos-hotwire.rb
74
+ - examples/todos-scaffold.rb
75
+ - exe/unirails
41
76
  - lib/uni_rails.rb
42
77
  - lib/uni_rails/app.rb
43
78
  - lib/uni_rails/app/css.rb
@@ -69,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
104
  - !ruby/object:Gem::Version
70
105
  version: '0'
71
106
  requirements: []
72
- rubygems_version: 3.4.21
107
+ rubygems_version: 3.5.3
73
108
  signing_key:
74
109
  specification_version: 4
75
110
  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