ru.Bee 1.1.32 → 1.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/bin/rubee +30 -12
- data/lib/app/views/layout.erb +46 -0
- data/lib/app/views/welcome_header.erb +1 -0
- data/lib/app/views/welcome_show.erb +5 -43
- data/lib/db/create_accounts.rb +14 -0
- data/lib/db/create_comments.rb +13 -0
- data/lib/db/create_posts.rb +14 -0
- data/lib/db/structure.rb +92 -0
- data/lib/rubee/async/asyncable.rb +1 -1
- data/lib/rubee/controllers/base_controller.rb +22 -4
- data/lib/rubee/models/database_objectable.rb +52 -0
- data/lib/rubee/models/sequel_object.rb +103 -15
- data/lib/rubee.rb +13 -2
- data/lib/tests/account_model_test.rb +19 -0
- data/lib/tests/comment_model_test.rb +35 -0
- data/lib/tests/example_models/account.rb +4 -0
- data/lib/tests/example_models/comment.rb +4 -0
- data/lib/tests/example_models/post.rb +5 -0
- data/lib/tests/example_models/user.rb +4 -0
- data/lib/{db → tests}/test.db +0 -0
- data/lib/tests/test_helper.rb +1 -1
- data/lib/tests/user_model_test.rb +172 -2
- data/readme.md +173 -17
- metadata +15 -4
- data/lib/rubee/models/database_object.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a01285afa9a2c3b5f9a08cd5cbd58ddbd300181a1cc4c5b823710c873e84300a
|
4
|
+
data.tar.gz: 241e42bbccbfe0731d83b80d58f5d40f0969c003b067660e53ace470294f2896
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2dbdeed01d5187c61ebbc3bc5773ce9d6150f6952fe634e76d4c8f22718b3c14091f591b879e9ecf94954672d8f9719ce1994f490966cc2d79aa899baca8e994
|
7
|
+
data.tar.gz: 91eaf06432d1f35f1c1450ea0af9bac800194bfc448bd055e7455294170682965f9e25122e2a6e64d9587049dbe6b91c6b062c6a345381cd11c3f22bb98fd60d
|
data/bin/rubee
CHANGED
@@ -160,7 +160,7 @@ elsif command == "test"
|
|
160
160
|
color_puts "Running all tests ...", color: :yellow
|
161
161
|
exec("ruby -Itest -e \"Dir.glob('.#{lib}/tests/**/*_test.rb').each { |file| require file }\"")
|
162
162
|
end
|
163
|
-
elsif ['generate', '
|
163
|
+
elsif ['generate', 'gen'].include? command
|
164
164
|
method, path = ARGV[1..2]
|
165
165
|
ENV['RACK_ENV'] ||= 'development'
|
166
166
|
|
@@ -181,9 +181,9 @@ elsif command == "db"
|
|
181
181
|
command, file_name = ARGV[1]&.split(':')
|
182
182
|
if Rubee::PROJECT_NAME == 'rubee'
|
183
183
|
Rubee::Configuration.setup(env=:test) do |config|
|
184
|
-
config.database_url = { url: "sqlite://test.db", env: }
|
184
|
+
config.database_url = { url: "sqlite://lib/tests/test.db", env: }
|
185
185
|
end
|
186
|
-
Rubee::SequelObject.reconnect!
|
186
|
+
Rubee::SequelObject.reconnect! unless command == 'init'
|
187
187
|
end
|
188
188
|
|
189
189
|
def ensure_database_exists(db_url)
|
@@ -193,7 +193,7 @@ elsif command == "db"
|
|
193
193
|
begin
|
194
194
|
Sequel.connect(db_url)
|
195
195
|
color_puts "Database #{ENV['RACK_ENV']} exists", color: :cyan
|
196
|
-
rescue =>
|
196
|
+
rescue Exception => e
|
197
197
|
if File.exist?(db_path = db_url.sub(/^sqlite:\/\//, ''))
|
198
198
|
color_puts "Database #{ENV['RACK_ENV']} exists", color: :cyan
|
199
199
|
else
|
@@ -237,14 +237,22 @@ elsif command == "db"
|
|
237
237
|
end
|
238
238
|
|
239
239
|
|
240
|
-
|
240
|
+
if command == 'run'
|
241
241
|
Rubee::Autoload.call
|
242
|
+
file_names = if file_name == 'all'
|
243
|
+
lib = Rubee::PROJECT_NAME == 'rubee' ? '/lib' : ''
|
244
|
+
Dir.glob(".#{lib}/db/*.rb").map { |file| File.basename(file, '.rb') }.select { |file| file != 'structure' }
|
245
|
+
else
|
246
|
+
[file_name]
|
247
|
+
end
|
242
248
|
Rubee::Configuration.envs.each do |env|
|
243
249
|
ENV['RACK_ENV'] = env.to_s
|
244
|
-
|
245
|
-
|
250
|
+
file_names.each do |file_name|
|
251
|
+
color_puts "Run #{file_name} file for #{env} env", color: :cyan
|
252
|
+
Object.const_get(file_name.split('_').map(&:capitalize).join).new.call
|
253
|
+
end
|
246
254
|
end
|
247
|
-
color_puts "Migration #{file_name} completed", color: :green
|
255
|
+
color_puts "Migration for #{file_name} completed", color: :green
|
248
256
|
unless Rubee::PROJECT_NAME == 'rubee'
|
249
257
|
color_puts "Regenerate schema file", color: :cyan
|
250
258
|
generate_structure
|
@@ -261,15 +269,25 @@ elsif ['console'].include? command
|
|
261
269
|
ENV['RACK_ENV'] ||= 'development'
|
262
270
|
|
263
271
|
Rubee::Autoload.call
|
272
|
+
if Rubee::PROJECT_NAME == 'rubee'
|
273
|
+
Rubee::Configuration.setup(env=:test) do |config|
|
274
|
+
config.database_url = { url: "sqlite://lib/tests/test.db", env: }
|
275
|
+
end
|
276
|
+
Rubee::Autoload.call
|
277
|
+
Rubee::SequelObject.reconnect!
|
278
|
+
end
|
264
279
|
|
265
280
|
def reload
|
266
|
-
app_files = Dir["./#{APP_ROOT}/**/*.rb"]
|
281
|
+
app_files = Dir["./#{Rubee::APP_ROOT}/**/*.rb"]
|
267
282
|
app_files.each { |file| load file }
|
268
283
|
color_puts "Reloaded ..", color: :green
|
269
284
|
end
|
270
|
-
|
271
|
-
|
272
|
-
|
285
|
+
begin
|
286
|
+
# Start IRB
|
287
|
+
IRB.start
|
288
|
+
rescue Exception => e
|
289
|
+
IRB.start
|
290
|
+
end
|
273
291
|
else
|
274
292
|
color_puts "Unknown command: #{command}", color: :red
|
275
293
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title><%= @title || '' %></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
|
+
</head>
|
43
|
+
<body>
|
44
|
+
<%= _yield_template %>
|
45
|
+
</body>
|
46
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>All set up and running!</h1>
|
@@ -1,52 +1,14 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html lang="en">
|
3
3
|
<head>
|
4
|
-
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
-
<title>Setup Complete</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
|
-
</head>
|
43
|
-
<body>
|
4
|
+
|
44
5
|
<div class="container">
|
45
|
-
|
6
|
+
<%= render_template :welcome_header %>
|
46
7
|
<br/>
|
47
8
|
<img src="images/rubee.svg" alt="ruBee">
|
48
9
|
<br/>
|
49
10
|
<p>rubee homepage: <a href="https://github.com/nucleom42/rubee"><br/>https://github.com/nucleom42/rubee</a></p>
|
11
|
+
<br/>
|
12
|
+
<p>Version: <%= Rubee::VERSION %></p>
|
50
13
|
</div>
|
51
|
-
|
52
|
-
</html>
|
14
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateAccounts
|
2
|
+
def call
|
3
|
+
unless Rubee::SequelObject::DB.tables.include?(:accounts)
|
4
|
+
Rubee::SequelObject::DB.create_table :accounts do
|
5
|
+
primary_key :id
|
6
|
+
String :addres
|
7
|
+
foreign_key :user_id, :users
|
8
|
+
end
|
9
|
+
|
10
|
+
Account.create(addres: "13th Ave, NY", user_id: User.all.first.id)
|
11
|
+
Account.create(addres: "14th Ave, NY", user_id: User.all.last.id)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateComments
|
2
|
+
def call
|
3
|
+
unless Rubee::SequelObject::DB.tables.include?(:comments)
|
4
|
+
Rubee::SequelObject::DB.create_table :comments do
|
5
|
+
primary_key :id
|
6
|
+
String :text
|
7
|
+
Integer :user_id
|
8
|
+
end
|
9
|
+
|
10
|
+
User.create(email: "ok@ok.com", password: "password")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreatePosts
|
2
|
+
def call
|
3
|
+
unless Rubee::SequelObject::DB.tables.include?(:posts)
|
4
|
+
Rubee::SequelObject::DB.create_table :posts do
|
5
|
+
primary_key :id
|
6
|
+
foreign_key :user_id, :users
|
7
|
+
foreign_key :comment_id, :comments
|
8
|
+
end
|
9
|
+
|
10
|
+
Post.create(user_id: User.all.first.id, comment_id: Comment.all.first.id)
|
11
|
+
Post.create(user_id: User.all.last.id, comment_id: Comment.all.last.id)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/db/structure.rb
CHANGED
@@ -30,5 +30,97 @@ STRUCTURE = {
|
|
30
30
|
ruby_default: nil,
|
31
31
|
max_length: 255
|
32
32
|
}
|
33
|
+
},
|
34
|
+
accounts: {
|
35
|
+
id: {
|
36
|
+
generated: false,
|
37
|
+
allow_null: false,
|
38
|
+
default: nil,
|
39
|
+
db_type: "INTEGER",
|
40
|
+
primary_key: true,
|
41
|
+
auto_increment: true,
|
42
|
+
type: "integer",
|
43
|
+
ruby_default: nil
|
44
|
+
},
|
45
|
+
addres: {
|
46
|
+
generated: false,
|
47
|
+
allow_null: true,
|
48
|
+
default: nil,
|
49
|
+
db_type: "varchar(255)",
|
50
|
+
primary_key: false,
|
51
|
+
type: "string",
|
52
|
+
ruby_default: nil,
|
53
|
+
max_length: 255
|
54
|
+
},
|
55
|
+
user_id: {
|
56
|
+
generated: false,
|
57
|
+
allow_null: true,
|
58
|
+
default: nil,
|
59
|
+
db_type: "INTEGER",
|
60
|
+
primary_key: false,
|
61
|
+
type: "integer",
|
62
|
+
ruby_default: nil
|
63
|
+
}
|
64
|
+
},
|
65
|
+
posts: {
|
66
|
+
id: {
|
67
|
+
generated: false,
|
68
|
+
allow_null: false,
|
69
|
+
default: nil,
|
70
|
+
db_type: "INTEGER",
|
71
|
+
primary_key: true,
|
72
|
+
auto_increment: true,
|
73
|
+
type: "integer",
|
74
|
+
ruby_default: nil
|
75
|
+
},
|
76
|
+
user_id: {
|
77
|
+
generated: false,
|
78
|
+
allow_null: true,
|
79
|
+
default: nil,
|
80
|
+
db_type: "INTEGER",
|
81
|
+
primary_key: false,
|
82
|
+
type: "integer",
|
83
|
+
ruby_default: nil
|
84
|
+
},
|
85
|
+
comment_id: {
|
86
|
+
generated: false,
|
87
|
+
allow_null: true,
|
88
|
+
default: nil,
|
89
|
+
db_type: "INTEGER",
|
90
|
+
primary_key: false,
|
91
|
+
type: "integer",
|
92
|
+
ruby_default: nil
|
93
|
+
}
|
94
|
+
},
|
95
|
+
comments: {
|
96
|
+
id: {
|
97
|
+
generated: false,
|
98
|
+
allow_null: false,
|
99
|
+
default: nil,
|
100
|
+
db_type: "INTEGER",
|
101
|
+
primary_key: true,
|
102
|
+
auto_increment: true,
|
103
|
+
type: "integer",
|
104
|
+
ruby_default: nil
|
105
|
+
},
|
106
|
+
text: {
|
107
|
+
generated: false,
|
108
|
+
allow_null: true,
|
109
|
+
default: nil,
|
110
|
+
db_type: "varchar(255)",
|
111
|
+
primary_key: false,
|
112
|
+
type: "string",
|
113
|
+
ruby_default: nil,
|
114
|
+
max_length: 255
|
115
|
+
},
|
116
|
+
user_id: {
|
117
|
+
generated: false,
|
118
|
+
allow_null: true,
|
119
|
+
default: nil,
|
120
|
+
db_type: "INTEGER",
|
121
|
+
primary_key: false,
|
122
|
+
type: "integer",
|
123
|
+
ruby_default: nil
|
124
|
+
}
|
33
125
|
}
|
34
126
|
}
|
@@ -18,7 +18,7 @@ module Rubee
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def response_with type: nil, object: nil, status: 200, mime_type: nil, render_view: nil, headers: {}, to: nil, file: nil, filename: nil
|
21
|
+
def response_with type: nil, object: nil, status: 200, mime_type: nil, render_view: nil, headers: {}, to: nil, file: nil, filename: nil, **options
|
22
22
|
case type&.to_sym
|
23
23
|
in :json
|
24
24
|
rendered_json = object.is_a?(Array) ? object&.map(&:to_h).to_json : object.to_json
|
@@ -42,13 +42,31 @@ module Rubee
|
|
42
42
|
return [302, headers.merge("location" => "#{to}"), ["Unauthentificated"]]
|
43
43
|
else # rendering erb view is a default behavior
|
44
44
|
view_file_name = self.class.name.split("Controller").first.downcase
|
45
|
-
erb_file = render_view ? "#{render_view}
|
45
|
+
erb_file = render_view ? "#{render_view}" : "#{view_file_name}_#{@route[:action]}"
|
46
46
|
lib = Rubee::PROJECT_NAME == 'rubee' ? 'lib/' : ''
|
47
|
-
|
48
|
-
|
47
|
+
view = render_template(erb_file, { object:, **(options[:locals] || {}) })
|
48
|
+
|
49
|
+
whole_erb = if File.exist?(layout_path = "#{lib}app/views/#{options[:layout] || 'layout'}.erb")
|
50
|
+
context = Object.new
|
51
|
+
context.define_singleton_method(:_yield_template) { view }
|
52
|
+
layout = File.read(layout_path)
|
53
|
+
ERB.new(layout).result(context.instance_eval { binding })
|
54
|
+
else
|
55
|
+
ERB.new(view).result(binding)
|
56
|
+
end
|
57
|
+
|
58
|
+
return [status, headers.merge("content-type" => "text/html"), [whole_erb]]
|
49
59
|
end
|
50
60
|
end
|
51
61
|
|
62
|
+
def render_template(file_name, locals = {})
|
63
|
+
lib = Rubee::PROJECT_NAME == 'rubee' ? 'lib/' : ''
|
64
|
+
path = "#{lib}app/views/#{file_name}.erb"
|
65
|
+
erb_template = ERB.new(File.read(path))
|
66
|
+
|
67
|
+
erb_template.result(binding)
|
68
|
+
end
|
69
|
+
|
52
70
|
def params
|
53
71
|
inputs = @request.env['rack.input'].read
|
54
72
|
body = JSON.parse(@request.body.read.strip) rescue body = {}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Rubee
|
2
|
+
module DatabaseObjectable
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
base.include InstanceMethods
|
6
|
+
base.prepend Initializer
|
7
|
+
|
8
|
+
base.include Rubee::Hookable
|
9
|
+
base.include Rubee::Serializable
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def pluralize_class_name
|
14
|
+
pluralize(self.name.downcase)
|
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
|
37
|
+
end
|
38
|
+
|
39
|
+
def accessor_names
|
40
|
+
instance_methods(false)
|
41
|
+
.select { |m| method_defined?("#{m}=") } # Check if setter exists
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module InstanceMethods
|
46
|
+
end
|
47
|
+
|
48
|
+
module Initializer
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -1,9 +1,21 @@
|
|
1
1
|
module Rubee
|
2
|
-
class SequelObject
|
3
|
-
|
4
|
-
|
5
|
-
def destroy
|
6
|
-
|
2
|
+
class SequelObject
|
3
|
+
include Rubee::DatabaseObjectable
|
4
|
+
|
5
|
+
def destroy(cascade: false, **options)
|
6
|
+
if cascade
|
7
|
+
# find all tables with foreign key
|
8
|
+
tables_with_fk = DB.tables.select do |table|
|
9
|
+
DB.foreign_key_list(table).any? { |fk| fk[:table] == self.class.pluralize_class_name.to_sym }
|
10
|
+
end
|
11
|
+
# destroy related records
|
12
|
+
tables_with_fk.each do |table|
|
13
|
+
fk_name ||= "#{self.class.name.to_s.downcase}_id".to_sym
|
14
|
+
target_klass = Object.const_get(self.class.singularize(table.to_s).capitalize)
|
15
|
+
target_klass.where(fk_name => id).map(&:destroy)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
self.class.dataset.where(id:).delete
|
7
19
|
end
|
8
20
|
|
9
21
|
def save
|
@@ -28,7 +40,7 @@ module Rubee
|
|
28
40
|
|
29
41
|
def update(args = {})
|
30
42
|
assign_attributes(args)
|
31
|
-
found_hash = self.class.
|
43
|
+
found_hash = self.class.dataset.where(id:)
|
32
44
|
return self.class.find(id) if found_hash&.update(**args)
|
33
45
|
|
34
46
|
false
|
@@ -44,46 +56,122 @@ module Rubee
|
|
44
56
|
|
45
57
|
class << self
|
46
58
|
def last
|
47
|
-
found_hash =
|
59
|
+
found_hash = dataset.order(:id).last
|
60
|
+
return self.new(**found_hash) if found_hash
|
61
|
+
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def first
|
66
|
+
found_hash = dataset.order(:id).first
|
48
67
|
return self.new(**found_hash) if found_hash
|
49
68
|
|
50
69
|
nil
|
51
70
|
end
|
52
71
|
|
72
|
+
# ## User
|
73
|
+
# owns_many :comments
|
74
|
+
# > user.comments
|
75
|
+
# > [<comment1>, <comment2>]
|
76
|
+
def owns_many(assoc, fk_name: nil, over: nil, **options)
|
77
|
+
singularized_assoc_name = singularize(assoc.to_s)
|
78
|
+
fk_name ||= "#{self.name.to_s.downcase}_id"
|
79
|
+
|
80
|
+
define_method(assoc) do
|
81
|
+
klass = Object.const_get(singularized_assoc_name.capitalize)
|
82
|
+
if over
|
83
|
+
sequel_dataset = klass
|
84
|
+
.join(over.to_sym, "#{singularized_assoc_name}_id".to_sym => :id)
|
85
|
+
.where(fk_name.to_sym => id)
|
86
|
+
self.class.serialize(sequel_dataset, klass)
|
87
|
+
else
|
88
|
+
klass.where(fk_name.to_sym => id)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# ## Comment
|
94
|
+
# owns_one :user
|
95
|
+
# > comment.user
|
96
|
+
# > <user>
|
97
|
+
def owns_one(assoc, options={})
|
98
|
+
Sequel::Model.one_to_one(assoc, **options)
|
99
|
+
fk_name ||= "#{self.name.to_s.downcase}_id"
|
100
|
+
define_method(assoc) do
|
101
|
+
Object.const_get(assoc.capitalize).where(fk_name.to_sym => id)&.first
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# ## Account
|
106
|
+
# holds :user
|
107
|
+
# > account.user
|
108
|
+
# > <user>
|
109
|
+
def holds(assoc, fk_name: nil, **options)
|
110
|
+
fk_name ||= "#{assoc.to_s.downcase}_id"
|
111
|
+
define_method(assoc) do
|
112
|
+
target_klass = Object.const_get(assoc.capitalize)
|
113
|
+
target_klass.find(self.send(fk_name))
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
53
117
|
def reconnect!
|
118
|
+
return if defined?(DB) && !DB.nil?
|
119
|
+
|
54
120
|
const_set(:DB, Sequel.connect(Rubee::Configuration.get_database_url))
|
55
121
|
end
|
56
122
|
|
57
|
-
def
|
58
|
-
@
|
123
|
+
def dataset
|
124
|
+
@dataset ||= DB[pluralize_class_name.to_sym]
|
125
|
+
rescue Exception => _
|
126
|
+
reconnect!
|
127
|
+
retry
|
59
128
|
end
|
60
129
|
|
61
130
|
def all
|
62
|
-
|
131
|
+
dataset.map do |record_hash|
|
63
132
|
self.new(**record_hash)
|
64
133
|
end
|
65
134
|
end
|
66
135
|
|
67
136
|
def find(id)
|
68
|
-
found_hash =
|
137
|
+
found_hash = dataset.where(id:)&.first
|
69
138
|
return self.new(**found_hash) if found_hash
|
70
139
|
|
71
140
|
nil
|
72
141
|
end
|
73
142
|
|
74
143
|
def where(args)
|
75
|
-
|
144
|
+
dataset.where(**args).map do |record_hash|
|
145
|
+
self.new(**record_hash)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def order(*args)
|
150
|
+
dataset.order(*args).map do |record_hash|
|
76
151
|
self.new(**record_hash)
|
77
152
|
end
|
78
153
|
end
|
79
154
|
|
155
|
+
def join(assoc, args)
|
156
|
+
dataset.join(assoc, **args)
|
157
|
+
end
|
158
|
+
|
80
159
|
def create(attrs)
|
81
|
-
out_id =
|
160
|
+
out_id = dataset.insert(**attrs)
|
82
161
|
self.new(**(attrs.merge(id: out_id)))
|
83
162
|
end
|
84
163
|
|
85
|
-
def destroy_all
|
86
|
-
all.each(
|
164
|
+
def destroy_all(cascade: false)
|
165
|
+
all.each{ |record| record.destroy(cascade:) }
|
166
|
+
end
|
167
|
+
|
168
|
+
def serialize(suquel_dataset, klass = nil)
|
169
|
+
klass ||= self
|
170
|
+
suquel_dataset.map do |record_hash|
|
171
|
+
target_klass_fields = DB[pluralize(klass.name.downcase).to_sym].columns
|
172
|
+
klass_attributes = record_hash.filter{ target_klass_fields.include? _1 }
|
173
|
+
klass.new(**klass_attributes)
|
174
|
+
end
|
87
175
|
end
|
88
176
|
end
|
89
177
|
end
|
data/lib/rubee.rb
CHANGED
@@ -8,7 +8,7 @@ module Rubee
|
|
8
8
|
APP_ROOT = File.expand_path(Dir.pwd) unless defined?(APP_ROOT)
|
9
9
|
IMAGE_DIR = File.join(APP_ROOT, 'images') unless defined?(IMAGE_DIR)
|
10
10
|
PROJECT_NAME = File.basename(APP_ROOT) unless defined?(PROJECT_NAME)
|
11
|
-
VERSION = '1.
|
11
|
+
VERSION = '1.2.0'
|
12
12
|
|
13
13
|
class Application
|
14
14
|
include Singleton
|
@@ -123,6 +123,8 @@ module Rubee
|
|
123
123
|
# autoload all rbs
|
124
124
|
root_directory = File.dirname(__FILE__)
|
125
125
|
priority_order_require(root_directory, black_list)
|
126
|
+
# ensure sequel object is connected
|
127
|
+
Rubee::SequelObject.reconnect!
|
126
128
|
|
127
129
|
Dir.glob(File.join(APP_ROOT, '**', '*.rb')).sort.each do |file|
|
128
130
|
base_name = File.basename(file)
|
@@ -149,6 +151,15 @@ module Rubee
|
|
149
151
|
# app config and routes
|
150
152
|
lib = PROJECT_NAME == 'rubee' ? 'lib/' : ''
|
151
153
|
require_relative File.join(APP_ROOT, lib, "config/base_configuration") unless black_list.include?('base_configuration.rb')
|
154
|
+
# This is necessary prerequisitedb init step
|
155
|
+
unless defined?(Rubee::SequelObject::DB)
|
156
|
+
if PROJECT_NAME == 'rubee'
|
157
|
+
Rubee::Configuration.setup(env=:test) do |config|
|
158
|
+
config.database_url = { url: "sqlite://lib/tests/test.db", env: }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
152
163
|
require_relative File.join(APP_ROOT, lib, "config/routes") unless black_list.include?('routes.rb')
|
153
164
|
# rubee extensions
|
154
165
|
Dir[File.join(root_directory, "rubee/extensions/**", '*.rb')].each do |file|
|
@@ -163,7 +174,7 @@ module Rubee
|
|
163
174
|
end
|
164
175
|
require_relative File.join(root_directory, "rubee/controllers/base_controller") unless black_list.include?('base_controller.rb')
|
165
176
|
# rubee models
|
166
|
-
require_relative File.join(root_directory, "rubee/models/
|
177
|
+
require_relative File.join(root_directory, "rubee/models/database_objectable") unless black_list.include?('database_objectable.rb')
|
167
178
|
require_relative File.join(root_directory, "rubee/models/sequel_object") unless black_list.include?('sequel_object.rb')
|
168
179
|
end
|
169
180
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe 'Account model' do
|
4
|
+
describe 'holds :user' do
|
5
|
+
after do
|
6
|
+
Account.destroy_all cascade: true
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'when it holds user_id' do
|
10
|
+
it 'returns associated User record' do
|
11
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
12
|
+
user.save
|
13
|
+
account = Account.new(user_id: user.id, addres: "test")
|
14
|
+
account.save
|
15
|
+
_(account.user.id).must_equal user.id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe 'Comment model' do
|
4
|
+
describe 'owns_many :users, over: :posts' do
|
5
|
+
before do
|
6
|
+
comment = Comment.new(text: "test")
|
7
|
+
comment.save
|
8
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
9
|
+
user.save
|
10
|
+
post = Post.new(user_id: user.id, comment_id: comment.id)
|
11
|
+
post.save
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
Comment.destroy_all cascade: true
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'when there are associated comment records' do
|
19
|
+
it 'returns all records' do
|
20
|
+
_(Comment.where(text: "test").last.users.count).must_equal 1
|
21
|
+
_(Comment.where(text: "test").last.users.first.email).must_equal "ok-test@test.com"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'sequel dataset query' do
|
26
|
+
it 'returns all records' do
|
27
|
+
result = Comment.dataset.join(:posts, comment_id: :id)
|
28
|
+
.where(comment_id: Comment.where(text: "test").last.id)
|
29
|
+
.then { |dataset| Comment.serialize(dataset) }
|
30
|
+
|
31
|
+
_(result.first.text).must_equal "test"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/{db → tests}/test.db
RENAMED
Binary file
|
data/lib/tests/test_helper.rb
CHANGED
@@ -7,7 +7,7 @@ require_relative '../../lib/rubee'
|
|
7
7
|
|
8
8
|
Rubee::Autoload.call
|
9
9
|
Rubee::Configuration.setup(env=:test) do |config|
|
10
|
-
config.database_url = { url: "sqlite://test.db", env: }
|
10
|
+
config.database_url = { url: "sqlite://lib/tests/test.db", env: }
|
11
11
|
end
|
12
12
|
Rubee::SequelObject.reconnect!
|
13
13
|
|
@@ -3,7 +3,7 @@ require_relative 'test_helper'
|
|
3
3
|
describe 'User model' do
|
4
4
|
describe ".create" do
|
5
5
|
after do
|
6
|
-
User.destroy_all
|
6
|
+
User.destroy_all cascade: true
|
7
7
|
end
|
8
8
|
|
9
9
|
describe 'when data is valid' do
|
@@ -26,7 +26,7 @@ describe 'User model' do
|
|
26
26
|
|
27
27
|
describe '.save' do
|
28
28
|
after do
|
29
|
-
User.destroy_all
|
29
|
+
User.destroy_all cascade: true
|
30
30
|
end
|
31
31
|
|
32
32
|
describe 'when data is valid' do
|
@@ -57,4 +57,174 @@ describe 'User model' do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
60
|
+
|
61
|
+
describe '.update' do
|
62
|
+
after do
|
63
|
+
User.destroy_all cascade: true
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'when data is valid' do
|
67
|
+
it 'persists to db' do
|
68
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
69
|
+
user.save
|
70
|
+
user.update(password: "1234")
|
71
|
+
|
72
|
+
_(user.reload.password).must_equal "1234"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '.destroy' do
|
78
|
+
after do
|
79
|
+
User.destroy_all cascade: true
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'when there is no related recrods' do
|
83
|
+
it 'delete the record' do
|
84
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
85
|
+
user.save
|
86
|
+
user.destroy
|
87
|
+
|
88
|
+
assert_nil user.reload
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'when there are related recrods' do
|
93
|
+
it 'does not delete the record' do
|
94
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
95
|
+
user.save
|
96
|
+
Account.new(user_id: user.id, addres: "test").save
|
97
|
+
user.destroy rescue nil
|
98
|
+
|
99
|
+
_(user.reload.id).must_equal user.id
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'when there are related recrods but passed cascade=true' do
|
104
|
+
it 'deletes the record' do
|
105
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
106
|
+
user.save
|
107
|
+
Account.new(user_id: user.id, addres: "test").save
|
108
|
+
user.destroy cascade: true
|
109
|
+
|
110
|
+
assert_nil user.reload
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '.find' do
|
116
|
+
after do
|
117
|
+
User.destroy_all cascade: true
|
118
|
+
end
|
119
|
+
|
120
|
+
describe 'when trhere is a record' do
|
121
|
+
it 'returns a record' do
|
122
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
123
|
+
user.save
|
124
|
+
_(User.find(user.id).email).must_equal user.email
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'when there is no record' do
|
129
|
+
it 'returns nil' do
|
130
|
+
assert_nil User.find(1)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '.all' do
|
136
|
+
after do
|
137
|
+
User.destroy_all cascade: true
|
138
|
+
end
|
139
|
+
|
140
|
+
describe 'when there are records' do
|
141
|
+
it 'returns all records' do
|
142
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
143
|
+
user2 = User.new(email: "ok-test2@test.com", password: "123")
|
144
|
+
user.save
|
145
|
+
user2.save
|
146
|
+
_(User.all.count).must_equal 2
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '.where' do
|
152
|
+
after do
|
153
|
+
User.destroy_all cascade: true
|
154
|
+
end
|
155
|
+
|
156
|
+
describe 'when there are records' do
|
157
|
+
it 'returns all records' do
|
158
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
159
|
+
user2 = User.new(email: "ok-test2@test.com", password: "123")
|
160
|
+
user.save
|
161
|
+
user2.save
|
162
|
+
_(User.where(email: "ok-test2@test.com").count).must_equal 1
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '.first' do
|
168
|
+
after do
|
169
|
+
User.destroy_all cascade: true
|
170
|
+
end
|
171
|
+
|
172
|
+
describe 'when there are records' do
|
173
|
+
it 'returns first record' do
|
174
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
175
|
+
user2 = User.new(email: "ok-test2@test.com", password: "123")
|
176
|
+
user.save
|
177
|
+
user2.save
|
178
|
+
_(User.first.email).must_equal user.email
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '.last' do
|
184
|
+
after do
|
185
|
+
User.destroy_all cascade: true
|
186
|
+
end
|
187
|
+
|
188
|
+
describe 'when there are records' do
|
189
|
+
it 'returns last record' do
|
190
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
191
|
+
user2 = User.new(email: "ok-test2@test.com", password: "123")
|
192
|
+
user.save
|
193
|
+
user2.save
|
194
|
+
_(User.last.email).must_equal user2.email
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe '.order' do
|
200
|
+
after do
|
201
|
+
User.destroy_all cascade: true
|
202
|
+
end
|
203
|
+
|
204
|
+
describe 'when there are records' do
|
205
|
+
it 'returns ordered records' do
|
206
|
+
user = User.new(email: "abc@test.com", password: "123")
|
207
|
+
user2 = User.new(email: "defg@test.com", password: "123")
|
208
|
+
user.save
|
209
|
+
user2.save
|
210
|
+
_(User.order(:email).first.email).must_equal user.email
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe 'owns_many' do
|
216
|
+
after do
|
217
|
+
User.destroy_all cascade: true
|
218
|
+
end
|
219
|
+
|
220
|
+
describe 'when there are associated account records' do
|
221
|
+
it 'returns all records' do
|
222
|
+
user = User.new(email: "ok-test@test.com", password: "123")
|
223
|
+
user.save
|
224
|
+
account = Account.new(user_id: user.id, addres: "test")
|
225
|
+
account.save
|
226
|
+
_(user.accounts.count).must_equal 1
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
60
230
|
end
|
data/readme.md
CHANGED
@@ -6,11 +6,11 @@
|
|
6
6
|

|
7
7
|
|
8
8
|
|
9
|
-
# <img src="lib/images/rubee.svg" alt="
|
9
|
+
# <img src="lib/images/rubee.svg" alt="ruBee" height="40"> ... ruBee
|
10
10
|
|
11
|
-
|
11
|
+
ruBee is a fast and lightweight Ruby application server designed for minimalism and flexibility .
|
12
12
|
|
13
|
-
The main philosophy of
|
13
|
+
The main philosophy of ruBee is to focus on Ruby language explicit implementation of the MVC web application.
|
14
14
|
|
15
15
|
Want to get a quick API server up and runing? You can do it for real quick!
|
16
16
|
<br />
|
@@ -27,7 +27,7 @@ All greaet features are yet to come!
|
|
27
27
|
- **Router**: Router driven - generates all required files from the routes.
|
28
28
|
- **Databases**: Sqlite3, Postgres, Mysql and many more supported by sequel gem.
|
29
29
|
- **Views**: Json, ERB and plain HTML
|
30
|
-
- **Bundlable** Charge your
|
30
|
+
- **Bundlable** Charge your ruBee with any gem you need and update your project with bundle.
|
31
31
|
- **ORM** All models are natively ORM objects, however you can use it as a blueurpint for any datasources.
|
32
32
|
- **Authentificatable** Add JWT authentification easily to any controller action.
|
33
33
|
- **Hooks** Add logic before, after and around any action.
|
@@ -36,7 +36,7 @@ All greaet features are yet to come!
|
|
36
36
|
|
37
37
|
## Installation
|
38
38
|
|
39
|
-
1. Install
|
39
|
+
1. Install ruBee
|
40
40
|
```bash
|
41
41
|
gem install ru.Bee
|
42
42
|
```
|
@@ -50,7 +50,7 @@ cd my_project
|
|
50
50
|
3. Install dependencies
|
51
51
|
|
52
52
|
***Prerequisites***<br />
|
53
|
-
**
|
53
|
+
**ruBee** is using **Sqlite** as a default database. However you can pick up any other database supported by sequel gem.
|
54
54
|
Aside that, make sure:
|
55
55
|
**Ruby** language (3+) is installed
|
56
56
|
**Bundler** is installed
|
@@ -59,7 +59,7 @@ Aside that, make sure:
|
|
59
59
|
bundle install
|
60
60
|
```
|
61
61
|
|
62
|
-
4. Run
|
62
|
+
4. Run ruBee server. Default port is 7000
|
63
63
|
```bash
|
64
64
|
rubee start
|
65
65
|
```
|
@@ -97,7 +97,7 @@ rubee generate get /apples
|
|
97
97
|
4. Fill those files with the logic you need and run the server again!
|
98
98
|
|
99
99
|
## Model
|
100
|
-
Model in
|
100
|
+
Model in ruBee is just simple ruby object that can be serilalized in the view
|
101
101
|
in the way it required (ie json).
|
102
102
|
|
103
103
|
Here below is a simple example on how it can be used by rendering json from in memory object
|
@@ -107,7 +107,7 @@ Here below is a simple example on how it can be used by rendering json from in m
|
|
107
107
|
|
108
108
|
def show
|
109
109
|
# in memory example
|
110
|
-
|
110
|
+
apples = [Apple.new(colour: 'red', weight: '1lb'), Apple.new(colour: 'green', weight: '1lb')]
|
111
111
|
apple = apples.find { |apple| apple.colour = params[:colour] }
|
112
112
|
|
113
113
|
response_with object: apple, type: :json
|
@@ -145,12 +145,144 @@ So in the controller you would need to query your target object now.
|
|
145
145
|
end
|
146
146
|
```
|
147
147
|
|
148
|
+
#### Rubee::SequelObject base methods:
|
149
|
+
|
150
|
+
Initiate new record in memory
|
151
|
+
```Ruby
|
152
|
+
irb(main):015> user = User.new(email: "llo@ok.com", password: 543)
|
153
|
+
=> #<User:0x000000010cda23b8 @email="llo@ok.com", @password=543>
|
154
|
+
```
|
155
|
+
|
156
|
+
Save record in db
|
157
|
+
```Ruby
|
158
|
+
=> #<User:0x000000010cda23b8 @email="llo@ok.com", @password=543>
|
159
|
+
irb(main):018> user.save
|
160
|
+
=> true
|
161
|
+
```
|
162
|
+
|
163
|
+
Update record with new value
|
164
|
+
```Ruby
|
165
|
+
irb(main):019> user.update(email: "update@email.com")
|
166
|
+
=> #<User:0x000000010c39b298 @email="update@email.com", @id=3, @password="543">
|
167
|
+
```
|
168
|
+
|
169
|
+
Check whether it includes id
|
170
|
+
```Ruby
|
171
|
+
irb(main):015> user = User.new(email: "llo@ok.com", password: 543)
|
172
|
+
=> #<User:0x000000010cda23b8 @email="llo@ok.com", @password=543>
|
173
|
+
irb(main):016> user.persisted?
|
174
|
+
=> false
|
175
|
+
```
|
176
|
+
|
177
|
+
Get the record from the database
|
178
|
+
```Ruby
|
179
|
+
irb(main):011> user = User.last
|
180
|
+
=> #<User:0x000000010ccea178 @email="ok23@ok.com", @id=2, @password="123">
|
181
|
+
irb(main):012> user.email = "new@ok.com"
|
182
|
+
=> "new@ok.com"
|
183
|
+
irb(main):013> user
|
184
|
+
=> #<User:0x000000010ccea178 @email="new@ok.com", @id=2, @password="123">
|
185
|
+
irb(main):014> user.reload
|
186
|
+
=> #<User:0x000000010c488548 @email="ok23@ok.com", @id=2, @password="123"> # not persited data was updated from db
|
187
|
+
```
|
188
|
+
|
189
|
+
Assign attributes without persisiting it to db
|
190
|
+
```Ruby
|
191
|
+
irb(main):008> User.last.assign_attributes(email: "bb@ok.com")
|
192
|
+
=> {"id" => 2, "email" => "ok23@ok.com", "password" => "123"
|
193
|
+
```
|
194
|
+
|
195
|
+
Get all records scoped by field
|
196
|
+
```Ruby
|
197
|
+
irb(main):005> User.where(email: "ok23@ok.com")
|
198
|
+
=> [#<User:0x000000010cfaa5c0 @email="ok23@ok.com", @id=2, @password="123">]
|
199
|
+
```
|
200
|
+
|
201
|
+
Get all record
|
202
|
+
```Ruby
|
203
|
+
irb(main):001> User.all
|
204
|
+
=> [#<User:0x000000010c239a30 @email="ok@ok.com", @id=1, @password="password">]
|
205
|
+
```
|
206
|
+
Find by id
|
207
|
+
```Ruby
|
208
|
+
irb(main):002> user = User.find 1
|
209
|
+
=> #<User:0x000000010c2f7cd8 @email="ok@ok.com", @id=1, @password="password">
|
210
|
+
```
|
211
|
+
|
212
|
+
Get last record
|
213
|
+
```Ruby
|
214
|
+
irb(main):003> User.last
|
215
|
+
=> #<User:0x000000010c2f7cd8 @email="ok@ok.com", @id=1, @password="password">
|
216
|
+
```
|
217
|
+
|
218
|
+
Create new persited record
|
219
|
+
```Ruby
|
220
|
+
irb(main):004> User.create(email: "ok23@ok.com", password: 123)
|
221
|
+
=> #<User:0x000000010c393818 @email="ok23@ok.com", @id=2, @password=123>
|
222
|
+
```
|
223
|
+
|
224
|
+
Destroy record and all related records
|
225
|
+
```Ruby
|
226
|
+
irb(main):021> user.destroy(cascade: true)
|
227
|
+
=> 1
|
228
|
+
```
|
229
|
+
|
230
|
+
Destroy all records one by one
|
231
|
+
```Ruby
|
232
|
+
irb(main):022> User.destroy_all
|
233
|
+
=> [#<User:0x000000010d42df98 @email="ok@ok.com", @id=1, @password="password">, #<User:0x000000010d42de80 @email="ok23@ok.com", @id=2, @password="123">
|
234
|
+
irb(main):023> User.all
|
235
|
+
=> []
|
236
|
+
```
|
237
|
+
|
238
|
+
Use complex queries chains and when ready serialize it back to Rubee object.
|
239
|
+
```Ruby
|
240
|
+
# user model
|
241
|
+
class User < Rubee::SequelObject
|
242
|
+
attr_accessor :id, :email, :password
|
243
|
+
owns_many :comments, over: :posts
|
244
|
+
end
|
245
|
+
|
246
|
+
# comment model
|
247
|
+
class Comment < Rubee::SequelObject
|
248
|
+
attr_accessor :id, :text, :user_id
|
249
|
+
owns_many :users, over: :posts
|
250
|
+
end
|
251
|
+
|
252
|
+
# join post model
|
253
|
+
class Post < Rubee::SequelObject
|
254
|
+
attr_accessor :id, :user_id, :comment_id
|
255
|
+
holds :comment
|
256
|
+
holds :user
|
257
|
+
end
|
258
|
+
```
|
259
|
+
|
260
|
+
```Ruby
|
261
|
+
irb(main):001> comment = Comment.new(text: "test")
|
262
|
+
irb(main):002> comment.save
|
263
|
+
irb(main):003> user = User.new(email: "ok-test@test.com", password: "123")
|
264
|
+
irb(main):004> user.save
|
265
|
+
irb(main):005> post = Post.new(user_id: user.id, comment_id: comment.id)
|
266
|
+
irb(main):006> post.save
|
267
|
+
=> true
|
268
|
+
irb(main):007> comment
|
269
|
+
=> #<Comment:0x000000012281a650 @id=21, @text="test">
|
270
|
+
irb(main):008> result = Comment.dataset.join(:posts, comment_id: :id)
|
271
|
+
irb(main):009> .where(comment_id: Comment.where(text: "test").last.id)
|
272
|
+
irb(main):010> .then { |dataset| Comment.serialize(dataset) }
|
273
|
+
=> [#<Comment:0x0000000121889998 @id=30, @text="test", @user_id=702>]
|
274
|
+
```
|
275
|
+
This is recommended when you want to run one query and serialize it back to Rubee object only once.
|
276
|
+
So it may safe some resources.
|
277
|
+
|
278
|
+
|
279
|
+
|
148
280
|
## Views
|
149
|
-
View in
|
281
|
+
View in ruBee is just a plain html/erb file that can be rendered from the controller.
|
150
282
|
|
151
283
|
## Object hooks
|
152
284
|
|
153
|
-
In
|
285
|
+
In ruBee by extending Hookable module any Ruby object can be charged with hooks (logic),
|
154
286
|
that can be executed before, after and around a specific method execution.
|
155
287
|
|
156
288
|
Here below a controller example. However it can be used in any Ruby object, like Model etc.
|
@@ -268,7 +400,7 @@ rubee console # start the console
|
|
268
400
|
rubee test # run all tests
|
269
401
|
rubee test auth_tokenable_test.rb # run specific tests
|
270
402
|
```
|
271
|
-
If you want to run any
|
403
|
+
If you want to run any ruBee command within a specific ENV make sure you added it before a command.
|
272
404
|
For instance if you want to run console in test environment you need to run the following command
|
273
405
|
|
274
406
|
```bash
|
@@ -349,8 +481,32 @@ end
|
|
349
481
|
TestAsyncRunnner.new.perform_async(options: {"email"=> "new@new.com", "password"=> "123"})
|
350
482
|
```
|
351
483
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
484
|
+
### Contributing
|
485
|
+
|
486
|
+
You are more than welcome to contribute to ruBee! To do so, please follow these steps:
|
487
|
+
|
488
|
+
1. Fork the repository by clicking the "Fork" button on the GitHub page.
|
489
|
+
|
490
|
+
2. Clone your fork:
|
491
|
+
```bash
|
492
|
+
git clone https://github.com/your-username/rubee.git
|
493
|
+
```
|
494
|
+
|
495
|
+
3. Create a new branch for your feature or bug fix:
|
496
|
+
```bash
|
497
|
+
git checkout -b feature/your-feature-name
|
498
|
+
```
|
499
|
+
|
500
|
+
4. Make your changes and commit them with descriptive messages:
|
501
|
+
```bash
|
502
|
+
git commit -m "Add feature: [brief description of feature]"
|
503
|
+
```
|
504
|
+
|
505
|
+
5. Push your changes to your fork:
|
506
|
+
```bash
|
507
|
+
git push origin feature/your-feature-name
|
508
|
+
```
|
509
|
+
|
510
|
+
6. Submit a pull request to the main branch of the original repository.
|
511
|
+
|
512
|
+
Let's make it shine even brighter!
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ru.Bee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleg Saltykov
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-04-06 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: bundler
|
@@ -43,13 +43,17 @@ files:
|
|
43
43
|
- lib/Dockerfile
|
44
44
|
- lib/app/controllers/welcome_controller.rb
|
45
45
|
- lib/app/models/user.rb
|
46
|
+
- lib/app/views/layout.erb
|
47
|
+
- lib/app/views/welcome_header.erb
|
46
48
|
- lib/app/views/welcome_show.erb
|
47
49
|
- lib/config.ru
|
48
50
|
- lib/config/base_configuration.rb
|
49
51
|
- lib/config/routes.rb
|
52
|
+
- lib/db/create_accounts.rb
|
53
|
+
- lib/db/create_comments.rb
|
54
|
+
- lib/db/create_posts.rb
|
50
55
|
- lib/db/create_users.rb
|
51
56
|
- lib/db/structure.rb
|
52
|
-
- lib/db/test.db
|
53
57
|
- lib/images/rubee.svg
|
54
58
|
- lib/inits/print_colors.rb
|
55
59
|
- lib/rubee.rb
|
@@ -63,10 +67,17 @@ files:
|
|
63
67
|
- lib/rubee/controllers/middlewares/auth_token_middleware.rb
|
64
68
|
- lib/rubee/extensions/hookable.rb
|
65
69
|
- lib/rubee/extensions/serializable.rb
|
66
|
-
- lib/rubee/models/
|
70
|
+
- lib/rubee/models/database_objectable.rb
|
67
71
|
- lib/rubee/models/sequel_object.rb
|
72
|
+
- lib/tests/account_model_test.rb
|
68
73
|
- lib/tests/auth_tokenable_test.rb
|
74
|
+
- lib/tests/comment_model_test.rb
|
75
|
+
- lib/tests/example_models/account.rb
|
76
|
+
- lib/tests/example_models/comment.rb
|
77
|
+
- lib/tests/example_models/post.rb
|
78
|
+
- lib/tests/example_models/user.rb
|
69
79
|
- lib/tests/rubeeapp_test.rb
|
80
|
+
- lib/tests/test.db
|
70
81
|
- lib/tests/test_helper.rb
|
71
82
|
- lib/tests/user_model_test.rb
|
72
83
|
- readme.md
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module Rubee
|
2
|
-
class DatabaseObject
|
3
|
-
include Serializable
|
4
|
-
include Hookable
|
5
|
-
|
6
|
-
def destroy
|
7
|
-
end
|
8
|
-
|
9
|
-
def save
|
10
|
-
end
|
11
|
-
|
12
|
-
def update(args = {})
|
13
|
-
end
|
14
|
-
|
15
|
-
def reload
|
16
|
-
end
|
17
|
-
|
18
|
-
class << self
|
19
|
-
def last
|
20
|
-
end
|
21
|
-
|
22
|
-
def connection
|
23
|
-
end
|
24
|
-
|
25
|
-
def all
|
26
|
-
end
|
27
|
-
|
28
|
-
def find(id)
|
29
|
-
end
|
30
|
-
|
31
|
-
def where(args)
|
32
|
-
end
|
33
|
-
|
34
|
-
def create(attrs)
|
35
|
-
end
|
36
|
-
|
37
|
-
def pluralize_class_name
|
38
|
-
word = self.name.downcase
|
39
|
-
# Basic pluralization rules
|
40
|
-
if word.end_with?('y') && !%w[a e i o u].include?(word[-2])
|
41
|
-
word[0..-2] + 'ies' # Replace "y" with "ies"
|
42
|
-
elsif word.end_with?('s', 'x', 'z', 'ch', 'sh')
|
43
|
-
word + 'es' # Add "es" for certain endings
|
44
|
-
else
|
45
|
-
word + 's' # Default to adding "s"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|