ru.Bee 1.4.0 → 1.5.1
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/bin/rubee +1 -3
- data/lib/app/views/app.tsx +1 -0
- data/lib/app/views/apples_.erb +1 -0
- data/lib/app/views/index.html +1 -36
- data/lib/app/views/layout.erb +1 -35
- data/lib/app/views/s_.erb +1 -0
- data/lib/css/app.css +33 -0
- data/lib/esbuild.config.js +3 -0
- data/lib/inits/charged_string.rb +35 -0
- data/lib/inits/print_colors.rb +4 -2
- data/lib/package.json +1 -1
- data/lib/rubee/controllers/base_controller.rb +12 -0
- data/lib/rubee/models/database_objectable.rb +3 -23
- data/lib/rubee/models/sequel_object.rb +5 -3
- data/lib/rubee.rb +68 -24
- data/lib/tests/rubee_generator_test.rb +190 -0
- data/lib/tests/rubeeapp_test.rb +3 -8
- data/lib/tests/test.db +0 -0
- data/lib/tests/user_model_test.rb +5 -1
- data/readme.md +188 -65
- metadata +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 209e21bbf6b7d2a4986fd92968f33b4c53b3f9699577eadcd2e35674f7767e3c
|
4
|
+
data.tar.gz: 9a9d73b4b2822a0e423371cf7cef7596aac3e7ee9574d0e722e7885b42959a4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e32baee79be70d8259d1c6643d35ccb6ef31cadad1c53d5a085fd15eeea521a394a97b72044b68f7ef62ab9e8bb90b14356a9e38f219797b404f2a02d061a676
|
7
|
+
data.tar.gz: a9b7e464b0e4bf11f262a70cd593c0aa65f0eaf9a8e836ece33256df533feab5f341d0db3077d64c234535bb7f8635b1e034735003044d37ae8f40740d8ea54e
|
data/bin/rubee
CHANGED
@@ -5,7 +5,6 @@ require 'fileutils'
|
|
5
5
|
require_relative '../lib/inits/print_colors'
|
6
6
|
require_relative '../lib/rubee'
|
7
7
|
|
8
|
-
|
9
8
|
ENV['RACK_ENV'] ||= 'development'
|
10
9
|
|
11
10
|
LIB_ROOT = File.expand_path('../lib', File.dirname(__FILE__))
|
@@ -165,7 +164,7 @@ elsif command == 'project'
|
|
165
164
|
color_puts "Project #{project_name} created successfully at #{target_dir}", color: :green
|
166
165
|
|
167
166
|
elsif command == 'version'
|
168
|
-
color_puts "
|
167
|
+
color_puts "ruBee v#{Rubee::VERSION}", color: :yellow
|
169
168
|
elsif command == 'routes'
|
170
169
|
file = Rubee::PROJECT_NAME == 'rubee' ? File.join('/lib', 'config/routes.rb') : 'config/routes.rb'
|
171
170
|
routes = eval(File.read(file))
|
@@ -190,7 +189,6 @@ elsif %w[generate gen].include?(command)
|
|
190
189
|
routes = eval(File.read(file))
|
191
190
|
route = routes.find { |route| route[:path] == path.to_s && route[:method] == method.to_sym }
|
192
191
|
color_puts("Route not found with path: #{path} and method: #{method}", color: :red) unless route
|
193
|
-
|
194
192
|
Rubee::Generator.new(
|
195
193
|
route[:model]&.[](:name),
|
196
194
|
route[:model]&.[](:attributes),
|
data/lib/app/views/app.tsx
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
<h1>apples_ View</h1>
|
data/lib/app/views/index.html
CHANGED
@@ -4,44 +4,9 @@
|
|
4
4
|
<meta charset="UTF-8" />
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6
6
|
<title>React App</title>
|
7
|
-
<style>
|
8
|
-
body {
|
9
|
-
display: flex;
|
10
|
-
justify-content: center;
|
11
|
-
align-items: center;
|
12
|
-
height: 100vh;
|
13
|
-
margin: 0;
|
14
|
-
background-color: #fdf6a5;
|
15
|
-
text-align: center;
|
16
|
-
font-family: Arial, sans-serif;
|
17
|
-
}
|
18
|
-
.container {
|
19
|
-
padding: 20px;
|
20
|
-
background: white;
|
21
|
-
border-radius: 10px;
|
22
|
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
23
|
-
}
|
24
|
-
img {
|
25
|
-
width: 50px;
|
26
|
-
height: auto;
|
27
|
-
margin-top: 10px;
|
28
|
-
}
|
29
|
-
h1 {
|
30
|
-
font-size: 1.8rem;
|
31
|
-
margin: 10px 0;
|
32
|
-
}
|
33
|
-
@media (max-width: 600px) {
|
34
|
-
h1 {
|
35
|
-
font-size: 1.5rem;
|
36
|
-
}
|
37
|
-
img {
|
38
|
-
width: 60px;
|
39
|
-
}
|
40
|
-
}
|
41
|
-
</style>
|
42
7
|
</head>
|
43
8
|
<body>
|
44
9
|
<div id="app"></div>
|
45
|
-
<script type="module" src="
|
10
|
+
<script type="module" src="/js/bundle.js"></script>
|
46
11
|
</body>
|
47
12
|
</html>
|
data/lib/app/views/layout.erb
CHANGED
@@ -4,41 +4,7 @@
|
|
4
4
|
<meta charset="UTF-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
6
|
<title><%= @title || '' %></title>
|
7
|
-
<
|
8
|
-
body {
|
9
|
-
display: flex;
|
10
|
-
justify-content: center;
|
11
|
-
align-items: center;
|
12
|
-
height: 100vh;
|
13
|
-
margin: 0;
|
14
|
-
background-color: #fdf6a5;
|
15
|
-
text-align: center;
|
16
|
-
font-family: Arial, sans-serif;
|
17
|
-
}
|
18
|
-
.container {
|
19
|
-
padding: 20px;
|
20
|
-
background: white;
|
21
|
-
border-radius: 10px;
|
22
|
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
23
|
-
}
|
24
|
-
img {
|
25
|
-
width: 50px;
|
26
|
-
height: auto;
|
27
|
-
margin-top: 10px;
|
28
|
-
}
|
29
|
-
h1 {
|
30
|
-
font-size: 1.8rem;
|
31
|
-
margin: 10px 0;
|
32
|
-
}
|
33
|
-
@media (max-width: 600px) {
|
34
|
-
h1 {
|
35
|
-
font-size: 1.5rem;
|
36
|
-
}
|
37
|
-
img {
|
38
|
-
width: 60px;
|
39
|
-
}
|
40
|
-
}
|
41
|
-
</style>
|
7
|
+
<link rel="stylesheet" href="/css/app.css">
|
42
8
|
</head>
|
43
9
|
<body>
|
44
10
|
<%= _yield_template %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>s_ View</h1>
|
data/lib/css/app.css
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
body {
|
2
|
+
display: flex;
|
3
|
+
justify-content: center;
|
4
|
+
align-items: center;
|
5
|
+
height: 100vh;
|
6
|
+
margin: 0;
|
7
|
+
background-color: #fdf6a5;
|
8
|
+
text-align: center;
|
9
|
+
font-family: Arial, sans-serif;
|
10
|
+
}
|
11
|
+
.container {
|
12
|
+
padding: 20px;
|
13
|
+
background: white;
|
14
|
+
border-radius: 10px;
|
15
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
16
|
+
}
|
17
|
+
img {
|
18
|
+
width: 50px;
|
19
|
+
height: auto;
|
20
|
+
margin-top: 10px;
|
21
|
+
}
|
22
|
+
h1 {
|
23
|
+
font-size: 1.8rem;
|
24
|
+
margin: 10px 0;
|
25
|
+
}
|
26
|
+
@media (max-width: 600px) {
|
27
|
+
h1 {
|
28
|
+
font-size: 1.5rem;
|
29
|
+
}
|
30
|
+
img {
|
31
|
+
width: 60px;
|
32
|
+
}
|
33
|
+
}
|
data/lib/esbuild.config.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
// esbuild.config.js (CommonJS style)
|
2
2
|
const esbuild = require("esbuild");
|
3
|
+
const inlineCss = require("esbuild-plugin-inline-css");
|
3
4
|
|
4
5
|
const buildOptions = {
|
5
6
|
entryPoints: ["js/app.js"], // Can be .ts or .tsx too
|
@@ -11,7 +12,9 @@ const buildOptions = {
|
|
11
12
|
".jsx": "jsx",
|
12
13
|
".ts": "ts",
|
13
14
|
".tsx": "tsx",
|
15
|
+
".css": "css",
|
14
16
|
},
|
17
|
+
plugins: [inlineCss()],
|
15
18
|
allowOverwrite: true,
|
16
19
|
sourcemap: true,
|
17
20
|
};
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ChargedString
|
2
|
+
refine String do
|
3
|
+
def pluralize
|
4
|
+
if self.end_with?('y') && !%w[a e i o u].include?(self[-2])
|
5
|
+
"#{self[0..-2]}ies" # Replace "y" with "ies"
|
6
|
+
elsif self.end_with?('s', 'x', 'z', 'ch', 'sh')
|
7
|
+
"#{self}es" # Add "es" for certain endings
|
8
|
+
else
|
9
|
+
"#{self}s" # Default to adding "s"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def plural?
|
14
|
+
return true if self.end_with?('s') && !self.end_with?('ss')
|
15
|
+
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def singular?
|
20
|
+
!plural?
|
21
|
+
end
|
22
|
+
|
23
|
+
def singularize
|
24
|
+
if self.end_with?('ies') && self.length > 3
|
25
|
+
"#{self[0..-4]}y" # Convert "ies" to "y"
|
26
|
+
elsif self.end_with?('es') && %w[s x z ch sh].any? { |ending| self[-(ending.length + 2)..-3] == ending }
|
27
|
+
self[0..-3] # Remove "es" for selfs like "foxes", "buses"
|
28
|
+
elsif self.end_with?('s') && self.length > 1
|
29
|
+
self[0..-2] # Remove "s" for regular plurals
|
30
|
+
else
|
31
|
+
self # Return as-is if no plural form is detected
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/inits/print_colors.rb
CHANGED
@@ -4,12 +4,14 @@
|
|
4
4
|
def color_puts(text, color: :nil, background: :nil, style: :normal)
|
5
5
|
colors = {
|
6
6
|
black: 30, red: 31, green: 32, yellow: 33,
|
7
|
-
blue: 34, magenta: 35, cyan: 36, white: 37
|
7
|
+
blue: 34, magenta: 35, cyan: 36, white: 37,
|
8
|
+
gray: 90
|
8
9
|
}
|
9
10
|
|
10
11
|
backgrounds = {
|
11
12
|
black: 40, red: 41, green: 42, yellow: 43,
|
12
|
-
blue: 44, magenta: 45, cyan: 46, white: 47
|
13
|
+
blue: 44, magenta: 45, cyan: 46, white: 47,
|
14
|
+
gray: 100
|
13
15
|
}
|
14
16
|
|
15
17
|
styles = {
|
data/lib/package.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"scripts": {
|
3
3
|
"build": "node esbuild.config.js",
|
4
4
|
"watch": "node esbuild.config.js --watch",
|
5
|
-
"prepare": "npm install react react-dom react-router-dom"
|
5
|
+
"prepare": "npm install react react-dom react-router-dom esbuild-plugin-inline-css"
|
6
6
|
},
|
7
7
|
"dependencies": {
|
8
8
|
"react": "^18.3.1",
|
@@ -28,6 +28,16 @@ module Rubee
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
def css
|
32
|
+
css_path = File.join(CSS_DIR, @request.path.sub('/css/', ''))
|
33
|
+
|
34
|
+
if File.exist?(css_path) && File.file?(css_path)
|
35
|
+
response_with(object: File.read(css_path), type: :css)
|
36
|
+
else
|
37
|
+
response_with(object: 'Css file is not found', type: :text)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
31
41
|
def response_with type: nil, object: nil, status: 200, mime_type: nil, render_view: nil, headers: {}, to: nil,
|
32
42
|
file: nil, filename: nil, **options
|
33
43
|
case type&.to_sym
|
@@ -38,6 +48,8 @@ module Rubee
|
|
38
48
|
[status, headers.merge('content-type' => mime_type), [object]]
|
39
49
|
in :js
|
40
50
|
[status, headers.merge('content-type' => 'application/javascript'), [object]]
|
51
|
+
in :css
|
52
|
+
[status, headers.merge('content-type' => 'text/css'), [object]]
|
41
53
|
in :file
|
42
54
|
[
|
43
55
|
status,
|
@@ -1,3 +1,5 @@
|
|
1
|
+
using ChargedString
|
2
|
+
|
1
3
|
module Rubee
|
2
4
|
module DatabaseObjectable
|
3
5
|
def self.included(base)
|
@@ -11,29 +13,7 @@ module Rubee
|
|
11
13
|
|
12
14
|
module ClassMethods
|
13
15
|
def pluralize_class_name
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
def pluralize(word)
|
18
|
-
if word.end_with?('y') && !%w[a e i o u].include?(word[-2])
|
19
|
-
"#{word[0..-2]}ies" # Replace "y" with "ies"
|
20
|
-
elsif word.end_with?('s', 'x', 'z', 'ch', 'sh')
|
21
|
-
"#{word}es" # Add "es" for certain endings
|
22
|
-
else
|
23
|
-
"#{word}s" # Default to adding "s"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def singularize(word)
|
28
|
-
if word.end_with?('ies') && word.length > 3
|
29
|
-
"#{word[0..-4]}y" # Convert "ies" to "y"
|
30
|
-
elsif word.end_with?('es') && %w[s x z ch sh].any? { |ending| word[-(ending.length + 2)..-3] == ending }
|
31
|
-
word[0..-3] # Remove "es" for words like "foxes", "buses"
|
32
|
-
elsif word.end_with?('s') && word.length > 1
|
33
|
-
word[0..-2] # Remove "s" for regular plurals
|
34
|
-
else
|
35
|
-
word # Return as-is if no plural form is detected
|
36
|
-
end
|
16
|
+
name.pluralize.downcase
|
37
17
|
end
|
38
18
|
|
39
19
|
def accessor_names
|
@@ -1,3 +1,5 @@
|
|
1
|
+
using ChargedString
|
2
|
+
|
1
3
|
module Rubee
|
2
4
|
class SequelObject
|
3
5
|
include Rubee::DatabaseObjectable
|
@@ -11,7 +13,7 @@ module Rubee
|
|
11
13
|
# destroy related records
|
12
14
|
tables_with_fk.each do |table|
|
13
15
|
fk_name ||= "#{self.class.name.to_s.downcase}_id".to_sym
|
14
|
-
target_klass = Object.const_get(
|
16
|
+
target_klass = Object.const_get(table.to_s.singularize.capitalize)
|
15
17
|
target_klass.where(fk_name => id).map(&:destroy)
|
16
18
|
end
|
17
19
|
end
|
@@ -81,7 +83,7 @@ module Rubee
|
|
81
83
|
# > user.comments
|
82
84
|
# > [<comment1>, <comment2>]
|
83
85
|
def owns_many(assoc, fk_name: nil, over: nil, **_options)
|
84
|
-
singularized_assoc_name =
|
86
|
+
singularized_assoc_name = assoc.to_s.singularize
|
85
87
|
fk_name ||= "#{name.to_s.downcase}_id"
|
86
88
|
|
87
89
|
define_method(assoc) do
|
@@ -175,7 +177,7 @@ module Rubee
|
|
175
177
|
def serialize(suquel_dataset, klass = nil)
|
176
178
|
klass ||= self
|
177
179
|
suquel_dataset.map do |record_hash|
|
178
|
-
target_klass_fields = DB[
|
180
|
+
target_klass_fields = DB[klass.name.pluralize.downcase.to_sym].columns
|
179
181
|
klass_attributes = record_hash.filter { target_klass_fields.include?(_1) }
|
180
182
|
klass.new(**klass_attributes)
|
181
183
|
end
|
data/lib/rubee.rb
CHANGED
@@ -14,7 +14,8 @@ module Rubee
|
|
14
14
|
LIB = PROJECT_NAME == 'rubee' ? 'lib/' : '' unless defined?(LIB)
|
15
15
|
IMAGE_DIR = File.join(APP_ROOT, LIB, 'images') unless defined?(IMAGE_DIR)
|
16
16
|
JS_DIR = File.join(APP_ROOT, LIB, 'js') unless defined?(JS_DIR)
|
17
|
-
|
17
|
+
CSS_DIR = File.join(APP_ROOT, LIB, 'css') unless defined?(CSS_DIR)
|
18
|
+
VERSION = '1.5.1'
|
18
19
|
|
19
20
|
class Application
|
20
21
|
include Singleton
|
@@ -34,7 +35,7 @@ module Rubee
|
|
34
35
|
route = Router.route_for(request)
|
35
36
|
# if react is the view so we would like to delegate not cauth by rubee routes to it.
|
36
37
|
if Rubee::Configuration.react[:on] && !route
|
37
|
-
index = File.read
|
38
|
+
index = File.read(File.join(Rubee::APP_ROOT, Rubee::LIB, 'app/views', 'index.html'))
|
38
39
|
return [200, { 'content-type' => 'text/html' }, [index]]
|
39
40
|
end
|
40
41
|
# if not found return 404
|
@@ -213,20 +214,23 @@ module Rubee
|
|
213
214
|
end
|
214
215
|
|
215
216
|
class Generator
|
216
|
-
|
217
|
+
require_relative 'inits/charged_string'
|
218
|
+
using ChargedString
|
219
|
+
def initialize(model_name, model_attributes, controller_name, action_name, **options)
|
217
220
|
@model_name = model_name&.downcase
|
218
|
-
@
|
219
|
-
@
|
221
|
+
@model_attributes = model_attributes || []
|
222
|
+
@base_name = controller_name.to_s.gsub('Controller', '').downcase.to_s
|
223
|
+
color_puts("base_name: #{@base_name}", color: :gray)
|
224
|
+
@plural_name = @base_name.plural? ? @base_name : @base_name.pluralize
|
220
225
|
@action_name = action_name
|
221
|
-
@controller_name = controller_name
|
222
226
|
@react = options[:react] || {}
|
223
227
|
end
|
224
228
|
|
225
229
|
def call
|
226
230
|
generate_model if @model_name
|
227
231
|
generate_db_file if @model_name
|
228
|
-
generate_controller if @
|
229
|
-
generate_view if @
|
232
|
+
generate_controller if @base_name && @action_name
|
233
|
+
generate_view if @base_name
|
230
234
|
end
|
231
235
|
|
232
236
|
private
|
@@ -240,7 +244,7 @@ module Rubee
|
|
240
244
|
|
241
245
|
content = <<~RUBY
|
242
246
|
class #{@model_name.capitalize} < Rubee::SequelObject
|
243
|
-
attr_accessor
|
247
|
+
#{'attr_accessor ' + @model_attributes.map { |hash| ":#{hash[:name]}" }.join(', ') unless @model_attributes.empty?}
|
244
248
|
end
|
245
249
|
RUBY
|
246
250
|
|
@@ -249,14 +253,14 @@ module Rubee
|
|
249
253
|
end
|
250
254
|
|
251
255
|
def generate_controller
|
252
|
-
controller_file = File.join(Rubee::APP_ROOT, Rubee::LIB, "app/controllers/#{@
|
256
|
+
controller_file = File.join(Rubee::APP_ROOT, Rubee::LIB, "app/controllers/#{@base_name}_controller.rb")
|
253
257
|
if File.exist?(controller_file)
|
254
|
-
puts "Controller #{@
|
258
|
+
puts "Controller #{@base_name} already exists. Remove it if you want to regenerate"
|
255
259
|
return
|
256
260
|
end
|
257
261
|
|
258
262
|
content = <<~RUBY
|
259
|
-
class #{@
|
263
|
+
class #{@base_name.capitalize}Controller < Rubee::BaseController
|
260
264
|
def #{@action_name}
|
261
265
|
response_with
|
262
266
|
end
|
@@ -264,24 +268,24 @@ module Rubee
|
|
264
268
|
RUBY
|
265
269
|
|
266
270
|
File.open(controller_file, 'w') { |file| file.write(content) }
|
267
|
-
color_puts("Controller #{@
|
271
|
+
color_puts("Controller #{@base_name} created", color: :green)
|
268
272
|
end
|
269
273
|
|
270
274
|
def generate_view
|
271
275
|
if @react[:view_name]
|
272
276
|
view_file = File.join(Rubee::APP_ROOT, Rubee::LIB, "app/views/#{@react[:view_name]}")
|
273
277
|
content = <<~JS
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
278
|
+
import React, { useEffect, useState } from "react";
|
279
|
+
// 1. Add your logic that fetches data
|
280
|
+
// 2. Do not forget to add respective react route
|
281
|
+
export function #{@react[:view_name].gsub(/\.(.*)+$/, '').capitalize}() {
|
282
|
+
|
283
|
+
return (
|
284
|
+
<div>
|
285
|
+
<h2>#{@react[:view_name]} view</h2>
|
286
|
+
</div>
|
287
|
+
);
|
288
|
+
}
|
285
289
|
JS
|
286
290
|
else
|
287
291
|
view_file = File.join(Rubee::APP_ROOT, Rubee::LIB, "app/views/#{@plural_name}_#{@action_name}.erb")
|
@@ -311,6 +315,11 @@ module Rubee
|
|
311
315
|
content = <<~RUBY
|
312
316
|
class Create#{@plural_name.capitalize}
|
313
317
|
def call
|
318
|
+
return if Rubee::SequelObject::DB.tables.include?(:#{@plural_name})
|
319
|
+
|
320
|
+
Rubee::SequelObject::DB.create_table(:#{@plural_name}) do
|
321
|
+
#{@model_attributes.map { |attribute| generate_sequel_schema(attribute) }.join("\n\t\t\t")}
|
322
|
+
end
|
314
323
|
end
|
315
324
|
end
|
316
325
|
RUBY
|
@@ -318,5 +327,40 @@ module Rubee
|
|
318
327
|
File.open(db_file, 'w') { |file| file.write(content) }
|
319
328
|
color_puts("DB file for #{@plural_name} created", color: :green)
|
320
329
|
end
|
330
|
+
|
331
|
+
def generate_sequel_schema(attribute)
|
332
|
+
type = attribute[:type]
|
333
|
+
name = if attribute[:name].is_a?(Array)
|
334
|
+
attribute[:name].map { |nom| ":#{nom}" }.join(", ").prepend('[') + ']'
|
335
|
+
else
|
336
|
+
":#{attribute[:name]}"
|
337
|
+
end
|
338
|
+
table = attribute[:table] || 'replace_with_table_name'
|
339
|
+
options = attribute[:options] || {}
|
340
|
+
|
341
|
+
lookup_hash = {
|
342
|
+
primary: "primary_key #{name}",
|
343
|
+
string: "String #{name}",
|
344
|
+
text: "String #{name}, text: true",
|
345
|
+
integer: "Integer #{name}",
|
346
|
+
date: "Date #{name}",
|
347
|
+
datetime: "DateTime #{name}",
|
348
|
+
time: "Time #{name}",
|
349
|
+
boolean: "TrueClass #{name}",
|
350
|
+
bigint: "Bignum #{name}",
|
351
|
+
decimal: "BigDecimal #{name}",
|
352
|
+
foreign_key: "foreign_key #{name}, :#{table}",
|
353
|
+
index: "index #{name}",
|
354
|
+
unique: "unique #",
|
355
|
+
}
|
356
|
+
|
357
|
+
statement = lookup_hash[type.to_sym]
|
358
|
+
|
359
|
+
options.keys.each do |key|
|
360
|
+
statement += ", #{key}: '#{options[key]}'"
|
361
|
+
end
|
362
|
+
|
363
|
+
statement
|
364
|
+
end
|
321
365
|
end
|
322
366
|
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe 'Rubee::Generator' do
|
4
|
+
describe 'generates Sequel schema lines' do
|
5
|
+
after do
|
6
|
+
File.delete('lib/app/models/apple.rb') if File.exist?('lib/app/models/apple.rb')
|
7
|
+
File.delete('lib/db/create_apples.rb') if File.exist?('lib/db/create_apples.rb')
|
8
|
+
end
|
9
|
+
it 'for string with just name' do
|
10
|
+
generator = Rubee::Generator.new(nil, nil, nil, nil)
|
11
|
+
|
12
|
+
attribute = { name: 'something', type: :string }
|
13
|
+
|
14
|
+
text = generator.send(:generate_sequel_schema, attribute)
|
15
|
+
|
16
|
+
_(text).must_equal('String :something')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'for string name and options' do
|
20
|
+
generator = Rubee::Generator.new(nil, nil, nil, nil)
|
21
|
+
|
22
|
+
attribute = { name: 'something', type: :string, options: { curse: 'squirrel' } }
|
23
|
+
|
24
|
+
text = generator.send(:generate_sequel_schema, attribute)
|
25
|
+
|
26
|
+
_(text).must_equal("String :something, curse: 'squirrel'")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'generates Sequel file' do
|
31
|
+
after do
|
32
|
+
File.delete('lib/app/models/apple.rb') if File.exist?('lib/app/models/apple.rb')
|
33
|
+
File.delete('lib/db/create_apples.rb') if File.exist?('lib/db/create_apples.rb')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'not without a model' do
|
37
|
+
generator = Rubee::Generator.new(nil, nil, nil, nil)
|
38
|
+
generator.call
|
39
|
+
|
40
|
+
_(File.exist?('lib/db/create_apples.rb')).must_equal false
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'with a model only' do
|
44
|
+
generator = Rubee::Generator.new('apple', nil, 'apples', nil)
|
45
|
+
generator.call
|
46
|
+
|
47
|
+
_(File.exist?('lib/db/create_apples.rb')).must_equal true
|
48
|
+
|
49
|
+
lines = File.readlines('lib/db/create_apples.rb').map(&:chomp).join("\n")
|
50
|
+
|
51
|
+
_(lines.include?('class CreateApples')).must_equal true
|
52
|
+
_(lines.include?('def call')).must_equal true
|
53
|
+
_(lines.include?('return if Rubee::SequelObject::DB.tables.include?(:apples)')).must_equal true
|
54
|
+
_(lines.include?('Rubee::SequelObject::DB.create_table(:apples) do')).must_equal true
|
55
|
+
_(lines.include?('String')).must_equal false
|
56
|
+
_(lines.include?('end')).must_equal true
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'with a model with attributes' do
|
60
|
+
generator = Rubee::Generator.new('apple', [{ name: 'title', type: :string }, {name: 'content', type: :text }], 'apples', nil)
|
61
|
+
generator.call
|
62
|
+
|
63
|
+
_(File.exist?('lib/db/create_apples.rb')).must_equal true
|
64
|
+
|
65
|
+
lines = File.readlines('lib/db/create_apples.rb').map(&:chomp).join("\n")
|
66
|
+
|
67
|
+
_(lines.include?('class CreateApples')).must_equal true
|
68
|
+
_(lines.include?('def call')).must_equal true
|
69
|
+
_(lines.include?('return if Rubee::SequelObject::DB.tables.include?(:apples)')).must_equal true
|
70
|
+
_(lines.include?('Rubee::SequelObject::DB.create_table(:apples) do')).must_equal true
|
71
|
+
_(lines.include?('String :title')).must_equal true
|
72
|
+
_(lines.include?('String :content, text: true')).must_equal true
|
73
|
+
_(lines.include?('end')).must_equal true
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'with a model with different attributes' do
|
77
|
+
generator = Rubee::Generator.new('apple', [{name: 'id', type: :bigint}, {name: 'colour', type: :string }, {name: 'weight', type: :integer }], 'apples', nil)
|
78
|
+
generator.call
|
79
|
+
|
80
|
+
_(File.exist?('lib/db/create_apples.rb')).must_equal true
|
81
|
+
|
82
|
+
lines = File.readlines('lib/db/create_apples.rb').map(&:chomp).join("\n")
|
83
|
+
|
84
|
+
_(lines.include?('class CreateApples')).must_equal true
|
85
|
+
_(lines.include?('def call')).must_equal true
|
86
|
+
_(lines.include?('return if Rubee::SequelObject::DB.tables.include?(:apples)')).must_equal true
|
87
|
+
_(lines.include?('Rubee::SequelObject::DB.create_table(:apples) do')).must_equal true
|
88
|
+
_(lines.include?('Bignum :id')).must_equal true
|
89
|
+
_(lines.include?('String :colour')).must_equal true
|
90
|
+
_(lines.include?('Integer :weight')).must_equal true
|
91
|
+
_(lines.include?('end')).must_equal true
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'with a model with an attribute with multiple names' do
|
95
|
+
generator = Rubee::Generator.new('apple', [{ name: ['blue_id', 'shoe_id'], type: :foreign_key, table: 'blue_and_shoe_join_tb' }], 'apples', nil)
|
96
|
+
generator.call
|
97
|
+
|
98
|
+
_(File.exist?('lib/db/create_apples.rb')).must_equal true
|
99
|
+
|
100
|
+
lines = File.readlines('lib/db/create_apples.rb').map(&:chomp).join("\n")
|
101
|
+
|
102
|
+
_(lines.include?('class CreateApples')).must_equal true
|
103
|
+
_(lines.include?('def call')).must_equal true
|
104
|
+
_(lines.include?('return if Rubee::SequelObject::DB.tables.include?(:apples)')).must_equal true
|
105
|
+
_(lines.include?('Rubee::SequelObject::DB.create_table(:apples) do')).must_equal true
|
106
|
+
_(lines.include?('foreign_key [:blue_id, :shoe_id]')).must_equal true
|
107
|
+
_(lines.include?(':blue_and_shoe_join_tb')).must_equal true
|
108
|
+
_(lines.include?('end')).must_equal true
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'with a model with a foreign_key without table' do
|
112
|
+
generator = Rubee::Generator.new('apple', [{ name: 'blue_id', type: :foreign_key }], 'apples', nil)
|
113
|
+
generator.call
|
114
|
+
|
115
|
+
_(File.exist?('lib/db/create_apples.rb')).must_equal true
|
116
|
+
|
117
|
+
lines = File.readlines('lib/db/create_apples.rb').map(&:chomp).join("\n")
|
118
|
+
|
119
|
+
_(lines.include?('class CreateApples')).must_equal true
|
120
|
+
_(lines.include?('def call')).must_equal true
|
121
|
+
_(lines.include?('return if Rubee::SequelObject::DB.tables.include?(:apples)')).must_equal true
|
122
|
+
_(lines.include?('Rubee::SequelObject::DB.create_table(:apples) do')).must_equal true
|
123
|
+
_(lines.include?('foreign_key :blue_id')).must_equal true
|
124
|
+
_(lines.include?(':replace_with_table_name')).must_equal true
|
125
|
+
_(lines.include?('end')).must_equal true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe 'generates Model file' do
|
130
|
+
after do
|
131
|
+
File.delete('lib/app/models/apple.rb') if File.exist?('lib/app/models/apple.rb')
|
132
|
+
File.delete('lib/db/create_apples.rb') if File.exist?('lib/db/create_apples.rb')
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'not without a model' do
|
136
|
+
generator = Rubee::Generator.new(nil, nil, nil, nil)
|
137
|
+
generator.call
|
138
|
+
|
139
|
+
_(File.exist?('lib/app/models/apple.rb')).must_equal false
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'with a model only' do
|
143
|
+
generator = Rubee::Generator.new('apple', nil, 'apples', nil)
|
144
|
+
generator.call
|
145
|
+
|
146
|
+
_(File.exist?('lib/app/models/apple.rb')).must_equal true
|
147
|
+
|
148
|
+
lines = File.readlines('lib/app/models/apple.rb').map(&:chomp).join("\n")
|
149
|
+
|
150
|
+
_(lines.include?('class Apple < Rubee::SequelObject')).must_equal true
|
151
|
+
_(lines.include?('attr_accessor')).must_equal false
|
152
|
+
_(lines.include?('end')).must_equal true
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'with a model with attributes' do
|
156
|
+
generator = Rubee::Generator.new('apple', [{ name: 'title', type: :string }, {name: 'content', type: :text }], 'apples', nil)
|
157
|
+
generator.call
|
158
|
+
|
159
|
+
_(File.exist?('lib/app/models/apple.rb')).must_equal true
|
160
|
+
|
161
|
+
lines = File.readlines('lib/app/models/apple.rb').map(&:chomp).join("\n")
|
162
|
+
|
163
|
+
_(lines.include?('class Apple < Rubee::SequelObject')).must_equal true
|
164
|
+
_(lines.include?('attr_accessor')).must_equal true
|
165
|
+
_(lines.include?(':title, :content')).must_equal true
|
166
|
+
_(lines.include?('end')).must_equal true
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'with a model with different attributes' do
|
170
|
+
generator = Rubee::Generator.new('apple', [{name: 'id', type: :bigInt}, {name: 'colour', type: :string }, {name: 'weight', type: :integer }], 'apples', nil)
|
171
|
+
generator.call
|
172
|
+
|
173
|
+
_(File.exist?('lib/app/models/apple.rb')).must_equal true
|
174
|
+
|
175
|
+
lines = File.readlines('lib/app/models/apple.rb').map(&:chomp).join("\n")
|
176
|
+
|
177
|
+
_(lines.include?('class Apple < Rubee::SequelObject')).must_equal true
|
178
|
+
_(lines.include?('attr_accessor')).must_equal true
|
179
|
+
_(lines.include?(':id, :colour, :weight')).must_equal true
|
180
|
+
_(lines.include?('end')).must_equal true
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe 'generates View file' do
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
describe 'generates Controller file' do
|
189
|
+
end
|
190
|
+
end
|
data/lib/tests/rubeeapp_test.rb
CHANGED
@@ -7,14 +7,6 @@ class RubeeAppTest < Minitest::Test
|
|
7
7
|
Rubee::Application.instance
|
8
8
|
end
|
9
9
|
|
10
|
-
def setup
|
11
|
-
Rubee::Configuration.setup(env = :test) { _1.react = { on: false, env: } }
|
12
|
-
end
|
13
|
-
|
14
|
-
def teardown
|
15
|
-
Rubee::Configuration.setup(env = :test) { _1.react = { on: false, env: } }
|
16
|
-
end
|
17
|
-
|
18
10
|
def test_welcome_route
|
19
11
|
get('/')
|
20
12
|
|
@@ -30,10 +22,13 @@ class RubeeAppTest < Minitest::Test
|
|
30
22
|
|
31
23
|
def test_react_home
|
32
24
|
Rubee::Configuration.setup(env = :test) { _1.react = { on: true, env: } }
|
25
|
+
|
33
26
|
get('/home')
|
34
27
|
|
35
28
|
assert_equal(200, last_response.status)
|
36
29
|
assert_includes(last_response.body, '<div id="app">')
|
37
30
|
assert_includes(last_response.body, 'bundle.js')
|
31
|
+
|
32
|
+
Rubee::Configuration.setup(env = :test) { _1.react = { on: false, env: } }
|
38
33
|
end
|
39
34
|
end
|
data/lib/tests/test.db
CHANGED
Binary file
|
@@ -153,9 +153,13 @@ describe 'User model' do
|
|
153
153
|
User.destroy_all(cascade: true)
|
154
154
|
end
|
155
155
|
|
156
|
+
before do
|
157
|
+
User.destroy_all(cascade: true)
|
158
|
+
end
|
159
|
+
|
156
160
|
describe 'when there are records' do
|
157
161
|
it 'returns all records' do
|
158
|
-
skip "This is Flaky test that blcoks dev, need to be fixed"
|
162
|
+
# skip "This is Flaky test that blcoks dev, need to be fixed"
|
159
163
|
user = User.new(email: 'ok-test@test.com', password: '123')
|
160
164
|
user2 = User.new(email: 'ok-test2@test.com', password: '123')
|
161
165
|
user.save
|
data/readme.md
CHANGED
@@ -24,10 +24,9 @@ All greaet features are yet to come!
|
|
24
24
|
- **Contract driven**: Define your API contracts in a simple, declarative manner. And generate the files for you.
|
25
25
|
- **Fast**: Optimized for speed, providing a quick response to requests. Everything is relative, I know!
|
26
26
|
- **Rack**: Rack backed. All Rack api is available for integration.
|
27
|
-
- **Router**: Router driven - generates all required files from the routes.
|
28
27
|
- **Databases**: Sqlite3, Postgres, Mysql and many more supported by sequel gem.
|
29
28
|
- **Views**: Json, ERB and plain HTML and ..
|
30
|
-
- **React**
|
29
|
+
- **React** is supported out of the box as a rubee view
|
31
30
|
- **Bundlable** Charge your ruBee with any gem you need and update your project with bundle.
|
32
31
|
- **ORM** All models are natively ORM objects, however you can use it as a blueurpint for any datasources.
|
33
32
|
- **Authentificatable** Add JWT authentification easily to any controller action.
|
@@ -51,8 +50,7 @@ cd my_project
|
|
51
50
|
3. Install dependencies
|
52
51
|
|
53
52
|
***Prerequisites***<br />
|
54
|
-
|
55
|
-
Aside that, make sure:
|
53
|
+
Make sure:
|
56
54
|
**Ruby** language (3+) is installed
|
57
55
|
**Bundler** is installed
|
58
56
|
|
@@ -67,35 +65,46 @@ rubee start
|
|
67
65
|
|
68
66
|
5. Open your browser and go to http://localhost:7000
|
69
67
|
|
70
|
-
##
|
71
|
-
1. Add the routes to the routes.rb
|
72
|
-
```bash
|
73
|
-
Rubee::Router.draw do |router|
|
74
|
-
...
|
75
|
-
# draw the contract
|
76
|
-
router.get "/apples", to: "apples#index",
|
77
|
-
model: {
|
78
|
-
name: "apple",
|
79
|
-
attributes: [
|
80
|
-
{ name: 'id', type: :integer },
|
81
|
-
{ name: 'colour', type: :string },
|
82
|
-
{ name: 'weight', type: :integer }
|
83
|
-
]
|
84
|
-
}
|
85
|
-
end
|
86
|
-
```
|
87
|
-
2. genrate the files
|
88
|
-
```bash
|
89
|
-
rubee generate get /apples
|
90
|
-
```
|
91
|
-
3. This will generate the following files
|
68
|
+
## Run the tests
|
92
69
|
```bash
|
93
|
-
|
94
|
-
./app/models/apple.rb # Model that acts as ORM
|
95
|
-
./app/views/apples_index.erb # ERB view that is rendered by the controller right away
|
96
|
-
./db/create_items.rb # Database migration file needed for creating repsective table
|
70
|
+
rubee test
|
97
71
|
```
|
98
|
-
|
72
|
+
|
73
|
+
## Create API contract and generate files from the routes
|
74
|
+
1. Add the routes to the routes.rb
|
75
|
+
```ruby
|
76
|
+
Rubee::Router.draw do |router|
|
77
|
+
...
|
78
|
+
# draw the contract
|
79
|
+
router.get "/apples", to: "apples#index",
|
80
|
+
model: {
|
81
|
+
name: "apple",
|
82
|
+
attributes: [
|
83
|
+
{ name: 'id', type: :primary },
|
84
|
+
{ name: 'colour', type: :string },
|
85
|
+
{ name: 'weight', type: :integer }
|
86
|
+
]
|
87
|
+
}
|
88
|
+
end
|
89
|
+
```
|
90
|
+
2. generate the files
|
91
|
+
```bash
|
92
|
+
rubee generate get /apples
|
93
|
+
```
|
94
|
+
- This will generate the following files
|
95
|
+
```bash
|
96
|
+
./app/controllers/apples_controller.rb # Controller with respective action
|
97
|
+
./app/views/apples_index.erb # ERB view that is rendered by the controller right away
|
98
|
+
./app/models/apple.rb # Model that acts as ORM
|
99
|
+
./db/create_apples.rb # Database migration file needed for creating repsective table
|
100
|
+
```
|
101
|
+
|
102
|
+
3. Run the initial db migration
|
103
|
+
```bash
|
104
|
+
rubee db run:all
|
105
|
+
```
|
106
|
+
|
107
|
+
5. Fill the generated files with the logic you need and run the server again!
|
99
108
|
|
100
109
|
## Model
|
101
110
|
Model in ruBee is just simple ruby object that can be serilalized in the view
|
@@ -107,8 +116,8 @@ Here below is a simple example on how it can be used by rendering json from in m
|
|
107
116
|
#ApplesController
|
108
117
|
|
109
118
|
def show
|
110
|
-
#
|
111
|
-
apples = [Apple.new(colour: 'red', weight: '1lb'), Apple.new(colour: 'green', weight: '1lb')]
|
119
|
+
# In memory example
|
120
|
+
apples = [Apple.new(colour: 'red', weight: '1lb'), Apple.new(colour: 'green', weight: '1lb')]
|
112
121
|
apple = apples.find { |apple| apple.colour = params[:colour] }
|
113
122
|
|
114
123
|
response_with object: apple, type: :json
|
@@ -122,8 +131,8 @@ Just make sure Serializable module included in the target class.
|
|
122
131
|
attr_accessor :id, :colour, :weight
|
123
132
|
end
|
124
133
|
```
|
125
|
-
However, you can simply turn it to ORM object by extending database class.
|
126
|
-
|
134
|
+
However, you can simply turn it to ORM object by extending database class Rubee::SequelObject.
|
135
|
+
This one is already serializable and charged with hooks.
|
127
136
|
```Ruby
|
128
137
|
class Apple < Rubee::SequelObject
|
129
138
|
attr_accessor :id, :colour, :weight
|
@@ -131,7 +140,6 @@ However, you can simply turn it to ORM object by extending database class.
|
|
131
140
|
```
|
132
141
|
|
133
142
|
So in the controller you would need to query your target object now.
|
134
|
-
|
135
143
|
```ruby
|
136
144
|
#ApplesController
|
137
145
|
|
@@ -276,10 +284,139 @@ irb(main):010> .then { |dataset| Comment.serialize(dataset) }
|
|
276
284
|
This is recommended when you want to run one query and serialize it back to Rubee object only once.
|
277
285
|
So it may safe some resources.
|
278
286
|
|
287
|
+
## Routing
|
288
|
+
Rubee uses explicit routes. In the routes.rb yout can define routes for any of the main HTTP methods. You can also add any matched parameter denoted by a pair of `{ }` in the path of the route. Eg. `/path/to/{a_key}/somewhere`
|
289
|
+
|
290
|
+
### Routing methods
|
291
|
+
``` ruby
|
292
|
+
Rubee::Router.draw do |router|
|
293
|
+
router.get '/posts', to: 'posts#index'
|
294
|
+
router.post '/posts', to: 'posts#create'
|
295
|
+
router.patch '/posts/{id}', to: 'posts#update'
|
296
|
+
router.put '/posts/{id}', to: 'posts#update'
|
297
|
+
router.delete '/posts/{id}', to: 'posts#delete'
|
298
|
+
router.head '/posts', to: 'posts#index'
|
299
|
+
router.connect '/posts', to: 'posts#index'
|
300
|
+
router.options '/posts', to: 'posts#index'
|
301
|
+
router.trace '/posts', to: 'posts#index'
|
302
|
+
end
|
303
|
+
```
|
304
|
+
|
305
|
+
As you see above every route is set up as:\
|
306
|
+
`route.http_method path, to: "controller#action", model { ...optional }`
|
307
|
+
|
308
|
+
### Defining Model attributes in routes
|
309
|
+
One of Rubee's unique traits is where we can define our models for generation. You've seen above one possible way you can set up.
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
Rubee::Router.draw do |router|
|
313
|
+
...
|
314
|
+
# draw the contract
|
315
|
+
router.get "/apples", to: "apples#index",
|
316
|
+
model: {
|
317
|
+
name: "apple",
|
318
|
+
attributes: [
|
319
|
+
{ name: 'id', type: :primary },
|
320
|
+
{ name: 'colour', type: :string },
|
321
|
+
{ name: 'weight', type: :integer }
|
322
|
+
]
|
323
|
+
}
|
324
|
+
end
|
325
|
+
```
|
326
|
+
|
327
|
+
There are many other keys supported by us and Sequel to help generate your initial db files. Other supported attribute key types are:
|
328
|
+
``` ruby
|
329
|
+
[
|
330
|
+
{ name: 'key1', type: :primary},
|
331
|
+
{ name: 'key2', type: :string },
|
332
|
+
{ name: 'key3', type: :text },
|
333
|
+
{ name: 'key4', type: :integer },
|
334
|
+
{ name: 'key5', type: :date },
|
335
|
+
{ name: 'key6', type: :datetime },
|
336
|
+
{ name: 'key7', type: :time },
|
337
|
+
{ name: 'key8', type: :boolean },
|
338
|
+
{ name: 'key9', type: :bigint },
|
339
|
+
{ name: 'key10', type: :decimal },
|
340
|
+
{ name: 'key11', type: :foreign_key },
|
341
|
+
{ name: 'key12', type: :index },
|
342
|
+
{ name: 'key13', type: :unique }
|
343
|
+
]
|
344
|
+
```
|
345
|
+
Every attribute can have a set of options passed based on their related [Sequel schema definition](https://github.com/jeremyevans/sequel/blob/master/doc/schema_modification.rdoc).
|
346
|
+
|
347
|
+
An example of this would be for the type string: \
|
348
|
+
`{name: 'key', type: :string, options: { size: 50, fixed: true } }`
|
349
|
+
|
350
|
+
Gets translated to:\
|
351
|
+
`String :key, size: 50, fixed: true`
|
352
|
+
|
353
|
+
### Generation from routes
|
354
|
+
As long as you have a `{ model: 'something' }` passed to your given route you can use it to generate your initial model files. If only a `path` and a `to:` are defined will only generate a controller and a corresponding view.
|
355
|
+
|
356
|
+
To generate based on a get route for the path /apples:\
|
357
|
+
`rubee generate get /apples` or `rubee gen get /apples`\
|
358
|
+
|
359
|
+
To generate base on a patch request for the path /apples/{id}:\
|
360
|
+
`rubee generate patch /apples/{id}` or `rubee gen patch /apples/{id}`
|
361
|
+
|
279
362
|
|
363
|
+
Example:
|
364
|
+
```ruby
|
365
|
+
Rubee::Router.draw do |router|
|
366
|
+
...
|
367
|
+
# draw the contract
|
368
|
+
router.get "/apples", to: "apples#index"
|
369
|
+
end
|
370
|
+
```
|
371
|
+
Will Generate:
|
372
|
+
```bash
|
373
|
+
./app/controllers/apples_controller.rb # Controller with respective action
|
374
|
+
./app/views/apples_index.erb # ERB view that is rendered by the controller right away
|
375
|
+
```
|
376
|
+
|
377
|
+
Example 2:
|
378
|
+
```ruby
|
379
|
+
Rubee::Router.draw do |router|
|
380
|
+
...
|
381
|
+
# draw the contract
|
382
|
+
router.get "/apples", to: "apples#index", model: { name: 'apple' }
|
383
|
+
end
|
384
|
+
```
|
385
|
+
Will generate:
|
386
|
+
```bash
|
387
|
+
./app/controllers/apples_controller.rb # Controller with respective action
|
388
|
+
./app/views/apples_index.erb # ERB view that is rendered by the controller right away
|
389
|
+
./app/models/apple.rb # Model that acts as ORM
|
390
|
+
./db/create_apples.rb # Database migration file needed for creating repsective table
|
391
|
+
```
|
392
|
+
|
393
|
+
Example 3:
|
394
|
+
```ruby
|
395
|
+
Rubee::Router.draw do |router|
|
396
|
+
...
|
397
|
+
# draw the contract
|
398
|
+
router.get "/apples", to: "apples#index",
|
399
|
+
model: {
|
400
|
+
name: 'apple',
|
401
|
+
attributes: [
|
402
|
+
{ name: 'id', type: :primary },
|
403
|
+
{ name: 'colour', type: :string },
|
404
|
+
{ name: 'weight', type: :integer }
|
405
|
+
]
|
406
|
+
}
|
407
|
+
end
|
408
|
+
```
|
409
|
+
|
410
|
+
Will generate:
|
411
|
+
```bash
|
412
|
+
./app/controllers/apples_controller.rb # Controller with respective action
|
413
|
+
./app/models/apple.rb # Model that acts as ORM
|
414
|
+
./app/views/apples_index.erb # ERB view that is rendered by the controller right away
|
415
|
+
./db/create_apples.rb # Database migration file needed for creating repsective table
|
416
|
+
```
|
280
417
|
|
281
418
|
## Views
|
282
|
-
View in ruBee is just a plain html/erb file that can be rendered from the controller.
|
419
|
+
View in ruBee is just a plain html/erb/react file that can be rendered from the controller.
|
283
420
|
|
284
421
|
## Templates over erb
|
285
422
|
|
@@ -369,7 +506,7 @@ end
|
|
369
506
|
# app/controllers/api/user_controller.rb
|
370
507
|
class Api::UserController < Rubee::BaseController
|
371
508
|
def index
|
372
|
-
response_with object: User.all
|
509
|
+
response_with object: User.all, type: :json
|
373
510
|
end
|
374
511
|
end
|
375
512
|
```
|
@@ -379,7 +516,7 @@ end
|
|
379
516
|
// app/views/app.tsx
|
380
517
|
<Router>
|
381
518
|
<Routes>
|
382
|
-
<Route path="/users" element={<
|
519
|
+
<Route path="/users" element={<Users />} />
|
383
520
|
<Route path="*" element={<NotFound />} />
|
384
521
|
</Routes>
|
385
522
|
</Router>
|
@@ -508,6 +645,8 @@ end
|
|
508
645
|
```bash
|
509
646
|
rubee start # start the server
|
510
647
|
rubee start_dev # start the server in dev mode, which restart server on changes
|
648
|
+
rubee react prepare # install react dependencies
|
649
|
+
rubee react watch # dev mode for react, works together with start_dev
|
511
650
|
rubee stop # stop the server
|
512
651
|
rubee restart # restart the server
|
513
652
|
```
|
@@ -519,6 +658,7 @@ rubee generate get /apples # generate controller view, model and migration if se
|
|
519
658
|
|
520
659
|
## Migraiton commands
|
521
660
|
```bash
|
661
|
+
rubee db run:all # run all migrations
|
522
662
|
rubee db run:create_apples # where create_apples is the name of the migration file, located in /db folder
|
523
663
|
rubee db structure # generate migration file for the database structure
|
524
664
|
```
|
@@ -526,6 +666,7 @@ rubee db structure # generate migration file for the database structure
|
|
526
666
|
## Rubee console
|
527
667
|
```bash
|
528
668
|
rubee console # start the console
|
669
|
+
# you can reload the console by typing reload, so it will pick up latest changes
|
529
670
|
```
|
530
671
|
|
531
672
|
## Testing
|
@@ -533,6 +674,7 @@ rubee console # start the console
|
|
533
674
|
rubee test # run all tests
|
534
675
|
rubee test auth_tokenable_test.rb # run specific tests
|
535
676
|
```
|
677
|
+
|
536
678
|
If you want to run any ruBee command within a specific ENV make sure you added it before a command.
|
537
679
|
For instance if you want to run console in test environment you need to run the following command
|
538
680
|
|
@@ -616,30 +758,11 @@ TestAsyncRunnner.new.perform_async(options: {"email"=> "new@new.com", "password"
|
|
616
758
|
|
617
759
|
### Contributing
|
618
760
|
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
```bash
|
625
|
-
git clone https://github.com/your-username/rubee.git
|
626
|
-
```
|
627
|
-
|
628
|
-
3. Create a new branch for your feature or bug fix:
|
629
|
-
```bash
|
630
|
-
git checkout -b feature/your-feature-name
|
631
|
-
```
|
632
|
-
|
633
|
-
4. Make your changes and commit them with descriptive messages:
|
634
|
-
```bash
|
635
|
-
git commit -m "Add feature: [brief description of feature]"
|
636
|
-
```
|
637
|
-
|
638
|
-
5. Push your changes to your fork:
|
639
|
-
```bash
|
640
|
-
git push origin feature/your-feature-name
|
641
|
-
```
|
642
|
-
|
643
|
-
6. Submit a pull request to the main branch of the original repository.
|
761
|
+
If you are interested in contributing to ruBee,
|
762
|
+
please read the [Contributing](https://github.com/nucleom42/rubee/blob/main/CONTRIBUTING.md) guide.
|
763
|
+
Also feel free to open an [issue](https://github.com/nucleom42/rubee/issues) if you apot one.
|
764
|
+
Have an idea or you wnat to discuss something?
|
765
|
+
Please open a [discussion](https://github.com/nucleom42/rubee/discussions)
|
644
766
|
|
645
|
-
|
767
|
+
## License
|
768
|
+
This project is released under the MIT License.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ru.Bee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleg Saltykov
|
@@ -44,14 +44,17 @@ files:
|
|
44
44
|
- lib/app/controllers/welcome_controller.rb
|
45
45
|
- lib/app/models/user.rb
|
46
46
|
- lib/app/views/app.tsx
|
47
|
+
- lib/app/views/apples_.erb
|
47
48
|
- lib/app/views/index.html
|
48
49
|
- lib/app/views/layout.erb
|
50
|
+
- lib/app/views/s_.erb
|
49
51
|
- lib/app/views/utils/redirectToBackend.tsx
|
50
52
|
- lib/app/views/welcome_header.erb
|
51
53
|
- lib/app/views/welcome_show.erb
|
52
54
|
- lib/config.ru
|
53
55
|
- lib/config/base_configuration.rb
|
54
56
|
- lib/config/routes.rb
|
57
|
+
- lib/css/app.css
|
55
58
|
- lib/db/create_accounts.rb
|
56
59
|
- lib/db/create_comments.rb
|
57
60
|
- lib/db/create_posts.rb
|
@@ -59,6 +62,7 @@ files:
|
|
59
62
|
- lib/db/structure.rb
|
60
63
|
- lib/esbuild.config.js
|
61
64
|
- lib/images/rubee.svg
|
65
|
+
- lib/inits/charged_string.rb
|
62
66
|
- lib/inits/print_colors.rb
|
63
67
|
- lib/js/app.js
|
64
68
|
- lib/js/app.js.map
|
@@ -243,6 +247,7 @@ files:
|
|
243
247
|
- lib/tests/example_models/comment.rb
|
244
248
|
- lib/tests/example_models/post.rb
|
245
249
|
- lib/tests/example_models/user.rb
|
250
|
+
- lib/tests/rubee_generator_test.rb
|
246
251
|
- lib/tests/rubeeapp_test.rb
|
247
252
|
- lib/tests/test.db
|
248
253
|
- lib/tests/test_helper.rb
|