greeenboii 0.1.5 → 0.1.7
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/.idea/dataSources.local.xml +1 -1
- data/.idea/greeenboii.iml +0 -2
- data/.idea/vcs.xml +1 -0
- data/.idea/workspace.xml +154 -123
- data/.rubocop.sorbet.yml +5 -0
- data/.rubocop.yml +36 -0
- data/README.md +5 -0
- data/exe/greeenboii +1 -1
- data/lib/greeenboii/version.rb +5 -5
- data/lib/greeenboii.rb +447 -234
- data/sig/greeenboii/todo_list.rbs +30 -0
- data/sorbet/rbi/gems/.gitattributes +1 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +585 -0
- data/sorbet/rbi/gems/benchmark@0.4.0.rbi +618 -0
- data/sorbet/rbi/gems/bigdecimal@3.1.9.rbi +9 -0
- data/sorbet/rbi/gems/cli-ui@2.3.0.rbi +3181 -0
- data/sorbet/rbi/gems/console_table@0.3.1.rbi +78 -0
- data/sorbet/rbi/gems/csv@3.3.2.rbi +9 -0
- data/sorbet/rbi/gems/erubi@1.13.1.rbi +155 -0
- data/sorbet/rbi/gems/httparty@0.22.0.rbi +2115 -0
- data/sorbet/rbi/gems/json@2.10.1.rbi +2120 -0
- data/sorbet/rbi/gems/language_server-protocol@3.17.0.4.rbi +9 -0
- data/sorbet/rbi/gems/lint_roller@1.1.0.rbi +86 -0
- data/sorbet/rbi/gems/mini_mime@1.1.5.rbi +9 -0
- data/sorbet/rbi/gems/minitest@5.25.4.rbi +1547 -0
- data/sorbet/rbi/gems/multi_xml@0.7.1.rbi +9 -0
- data/sorbet/rbi/gems/netrc@0.11.0.rbi +159 -0
- data/sorbet/rbi/gems/nokogiri@1.18.3.rbi +8205 -0
- data/sorbet/rbi/gems/parallel@1.26.3.rbi +291 -0
- data/sorbet/rbi/gems/parser@3.3.7.1.rbi +5525 -0
- data/sorbet/rbi/gems/prism@1.3.0.rbi +41403 -0
- data/sorbet/rbi/gems/racc@1.8.1.rbi +164 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +403 -0
- data/sorbet/rbi/gems/rake-compiler@1.2.9.rbi +9 -0
- data/sorbet/rbi/gems/rake@13.2.1.rbi +3028 -0
- data/sorbet/rbi/gems/rbi@0.2.4.rbi +4542 -0
- data/sorbet/rbi/gems/regexp_parser@2.10.0.rbi +3795 -0
- data/sorbet/rbi/gems/rubocop-ast@1.38.0.rbi +7654 -0
- data/sorbet/rbi/gems/rubocop@1.72.2.rbi +61026 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1318 -0
- data/sorbet/rbi/gems/spoom@1.5.4.rbi +5026 -0
- data/sorbet/rbi/gems/sqlite3@2.6.0.rbi +1895 -0
- data/sorbet/rbi/gems/tapioca@0.16.11.rbi +3656 -0
- data/sorbet/rbi/gems/thor@1.3.2.rbi +4378 -0
- data/sorbet/rbi/gems/unicode-display_width@3.1.4.rbi +132 -0
- data/sorbet/rbi/gems/unicode-emoji@4.0.4.rbi +251 -0
- data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +435 -0
- data/sorbet/rbi/gems/yard@0.9.37.rbi +18379 -0
- data/sorbet/tapioca/require.rb +6 -0
- metadata +47 -8
data/lib/greeenboii.rb
CHANGED
@@ -1,234 +1,447 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "greeenboii/version"
|
4
|
-
require "cli/ui"
|
5
|
-
require "date"
|
6
|
-
require "console_table"
|
7
|
-
|
8
|
-
require "nokogiri"
|
9
|
-
require "httparty"
|
10
|
-
|
11
|
-
require "sqlite3"
|
12
|
-
|
13
|
-
module Greeenboii
|
14
|
-
class Error < StandardError; end
|
15
|
-
|
16
|
-
class
|
17
|
-
|
18
|
-
"
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "greeenboii/version"
|
4
|
+
require "cli/ui"
|
5
|
+
require "date"
|
6
|
+
require "console_table"
|
7
|
+
|
8
|
+
require "nokogiri"
|
9
|
+
require "httparty"
|
10
|
+
|
11
|
+
require "sqlite3"
|
12
|
+
|
13
|
+
module Greeenboii
|
14
|
+
class Error < StandardError; end
|
15
|
+
|
16
|
+
class WebsiteBuilder
|
17
|
+
def self.build_website
|
18
|
+
CLI::UI::Frame.open("{{v}} Website Builder") do
|
19
|
+
CLI::UI::Prompt.ask("Choose a template:") do |handler|
|
20
|
+
handler.option("{{green:NextJs-CoolStack}}") { |selection| create_nextjs(selection) }
|
21
|
+
handler.option("{{yellow:Hono-API}}") { |selection| create_hono(selection) }
|
22
|
+
# handler.option("{{blue:Rails}}") { |selection| create_rails(selection) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.create_nextjs(_selection)
|
28
|
+
# Ask for a relative path instead
|
29
|
+
location = CLI::UI.ask("Where to install? (relative path)", default: "nextjs-project")
|
30
|
+
CLI::UI.puts("Checking prerequisites...")
|
31
|
+
|
32
|
+
# Check prerequisites
|
33
|
+
node_version = begin
|
34
|
+
`node -v`.strip
|
35
|
+
rescue StandardError
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
unless node_version
|
39
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Node.js not found. Please install Node.js first"))
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
git_version = begin
|
44
|
+
`git --version`.strip
|
45
|
+
rescue StandardError
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
unless git_version
|
49
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Git not found. Please install Git first"))
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
53
|
+
# Create a path relative to current directory
|
54
|
+
full_path = File.expand_path(location, Dir.pwd)
|
55
|
+
|
56
|
+
# Check if directory already exists
|
57
|
+
if Dir.exist?(full_path)
|
58
|
+
# If directory exists, check if it's empty
|
59
|
+
unless Dir.empty?(full_path) # . and .. entries
|
60
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Directory not empty: #{full_path}"))
|
61
|
+
return
|
62
|
+
end
|
63
|
+
else
|
64
|
+
# Try to create the directory
|
65
|
+
begin
|
66
|
+
FileUtils.mkdir_p(full_path)
|
67
|
+
rescue StandardError => e
|
68
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Cannot create directory: #{e.message}"))
|
69
|
+
return
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
success = false
|
74
|
+
CLI::UI::Spinner.spin("Setting up NextJS + Supabase template") do |spinner|
|
75
|
+
spinner.update_title("Cloning repository...")
|
76
|
+
|
77
|
+
# Clone directly into the target directory
|
78
|
+
clone_cmd = "git clone https://github.com/greeenboi/nextjs-supabase-template.git \"#{full_path}\""
|
79
|
+
result = system(clone_cmd)
|
80
|
+
|
81
|
+
unless result
|
82
|
+
# Try alternative approach if direct cloning fails
|
83
|
+
spinner.update_title("Direct clone failed, trying alternative...")
|
84
|
+
|
85
|
+
# Clone to a temporary directory first
|
86
|
+
temp_dir = "#{Dir.pwd}/temp_clone_#{Time.now.to_i}"
|
87
|
+
FileUtils.mkdir_p(temp_dir)
|
88
|
+
result = system("git clone https://github.com/greeenboi/nextjs-supabase-template.git \"#{temp_dir}\"")
|
89
|
+
|
90
|
+
if result
|
91
|
+
# Copy files to destination
|
92
|
+
FileUtils.cp_r(Dir.glob("#{temp_dir}/*"), full_path)
|
93
|
+
FileUtils.cp_r(Dir.glob("#{temp_dir}/.*").reject { |f| f =~ /\/\.\.?$/ }, full_path)
|
94
|
+
FileUtils.rm_rf(temp_dir)
|
95
|
+
else
|
96
|
+
spinner.update_title("Clone failed")
|
97
|
+
next
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
spinner.update_title("Installing dependencies...")
|
102
|
+
Dir.chdir(full_path) do
|
103
|
+
package_manager = if system("bun -v > /dev/null 2>&1")
|
104
|
+
"bun"
|
105
|
+
elsif system("pnpm -v > /dev/null 2>&1")
|
106
|
+
"pnpm"
|
107
|
+
else
|
108
|
+
"npm"
|
109
|
+
end
|
110
|
+
spinner.update_title("Installing dependencies with #{package_manager}...")
|
111
|
+
system("#{package_manager} install")
|
112
|
+
end
|
113
|
+
|
114
|
+
success = true
|
115
|
+
spinner.update_title("Setup complete!")
|
116
|
+
rescue StandardError => e
|
117
|
+
spinner.update_title("Error: #{e.message}")
|
118
|
+
end
|
119
|
+
|
120
|
+
if success
|
121
|
+
CLI::UI.puts(CLI::UI.fmt("{{v}} NextJS template installed successfully in #{full_path}"))
|
122
|
+
CLI::UI.puts(CLI::UI.fmt("{{*}} To start: cd \"#{location}\" && #{system} run dev"))
|
123
|
+
else
|
124
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Installation failed"))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.create_hono(_selection)
|
129
|
+
# Ask for a relative path instead
|
130
|
+
location = CLI::UI.ask("Where to install? (relative path)", default: "hono-api")
|
131
|
+
CLI::UI.puts("Checking prerequisites...")
|
132
|
+
|
133
|
+
# Check prerequisites
|
134
|
+
node_version = begin
|
135
|
+
`node -v`.strip
|
136
|
+
rescue StandardError
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
unless node_version
|
140
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Node.js not found. Please install Node.js first"))
|
141
|
+
return
|
142
|
+
end
|
143
|
+
|
144
|
+
git_version = begin
|
145
|
+
`git --version`.strip
|
146
|
+
rescue StandardError
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
unless git_version
|
150
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Git not found. Please install Git first"))
|
151
|
+
return
|
152
|
+
end
|
153
|
+
|
154
|
+
# Check for Deno
|
155
|
+
deno_version = begin
|
156
|
+
`deno --version`.strip
|
157
|
+
rescue StandardError
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
unless deno_version
|
161
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Deno not found. Please install Deno first"))
|
162
|
+
return
|
163
|
+
end
|
164
|
+
|
165
|
+
# Create a path relative to current directory
|
166
|
+
full_path = File.expand_path(location, Dir.pwd)
|
167
|
+
|
168
|
+
# Check if directory already exists
|
169
|
+
if Dir.exist?(full_path)
|
170
|
+
# If directory exists, check if it's empty
|
171
|
+
unless Dir.empty?(full_path) # . and .. entries
|
172
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Directory not empty: #{full_path}"))
|
173
|
+
return
|
174
|
+
end
|
175
|
+
else
|
176
|
+
# Try to create the directory
|
177
|
+
begin
|
178
|
+
FileUtils.mkdir_p(full_path)
|
179
|
+
rescue StandardError => e
|
180
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Cannot create directory: #{e.message}"))
|
181
|
+
return
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
success = false
|
186
|
+
CLI::UI::Spinner.spin("Setting up Hono Deno backend template") do |spinner|
|
187
|
+
spinner.update_title("Cloning repository...")
|
188
|
+
|
189
|
+
# Clone directly into the target directory
|
190
|
+
clone_cmd = "git clone https://github.com/greeenboi/hono-deno-backend-template.git \"#{full_path}\""
|
191
|
+
result = system(clone_cmd)
|
192
|
+
|
193
|
+
unless result
|
194
|
+
# Try alternative approach if direct cloning fails
|
195
|
+
spinner.update_title("Direct clone failed, trying alternative...")
|
196
|
+
|
197
|
+
# Clone to a temporary directory first
|
198
|
+
temp_dir = "#{Dir.pwd}/temp_clone_#{Time.now.to_i}"
|
199
|
+
FileUtils.mkdir_p(temp_dir)
|
200
|
+
result = system("git clone https://github.com/greeenboi/hono-deno-backend-template.git \"#{temp_dir}\"")
|
201
|
+
|
202
|
+
if result
|
203
|
+
# Copy files to destination
|
204
|
+
FileUtils.cp_r(Dir.glob("#{temp_dir}/*"), full_path)
|
205
|
+
FileUtils.cp_r(Dir.glob("#{temp_dir}/.*").reject { |f| f =~ /\/\.\.?$/ }, full_path)
|
206
|
+
FileUtils.rm_rf(temp_dir)
|
207
|
+
else
|
208
|
+
spinner.update_title("Clone failed")
|
209
|
+
next
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
success = true
|
214
|
+
spinner.update_title("Setup complete!")
|
215
|
+
rescue StandardError => e
|
216
|
+
spinner.update_title("Error: #{e.message}")
|
217
|
+
end
|
218
|
+
|
219
|
+
if success
|
220
|
+
CLI::UI.puts(CLI::UI.fmt("{{v}} Hono API template installed successfully in #{full_path}"))
|
221
|
+
CLI::UI.puts(CLI::UI.fmt("{{*}} To start: cd \"#{location}\" && deno task start"))
|
222
|
+
else
|
223
|
+
CLI::UI.puts(CLI::UI.fmt("{{x}} Installation failed"))
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
class Search
|
229
|
+
SEARCH_ENGINES = {
|
230
|
+
"Google" => "https://www.google.com/search?client=opera-gx&q= ",
|
231
|
+
"Bing" => "https://www.bing.com/search?q=",
|
232
|
+
"DuckDuckGo" => "https://duckduckgo.com/?q="
|
233
|
+
}.freeze
|
234
|
+
|
235
|
+
def self.perform_search
|
236
|
+
query = CLI::UI.ask("Enter your search query:", default: "")
|
237
|
+
return if query.empty?
|
238
|
+
|
239
|
+
results = []
|
240
|
+
CLI::UI::SpinGroup.new do |spin_group|
|
241
|
+
SEARCH_ENGINES.each do |engine, base_url|
|
242
|
+
spin_group.add("Searching #{engine}...") do |spinner|
|
243
|
+
suffix = case engine
|
244
|
+
when "Google"
|
245
|
+
"&sourceid=opera&ie=UTF-8&oe=UTF-8"
|
246
|
+
when "Bing"
|
247
|
+
"&sp=-1&pq=test&sc=6-4&qs=n&sk=&cvid=#{SecureRandom.hex(16)}"
|
248
|
+
when "DuckDuckGo"
|
249
|
+
"&t=h_&ia=web"
|
250
|
+
end
|
251
|
+
search_url = "#{base_url}#{URI.encode_www_form_component(query)}#{suffix}"
|
252
|
+
links = scrape_links(engine, search_url)
|
253
|
+
results << { engine: engine, links: links }
|
254
|
+
sleep 1.0 # Simulating search delay
|
255
|
+
spinner.update_title("#{engine} search complete!")
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
display_results(results)
|
261
|
+
end
|
262
|
+
|
263
|
+
def self.scrape_links(engine, url)
|
264
|
+
headers = {
|
265
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36",
|
266
|
+
"Accept-Language" => "en-US,en;q=0.5"
|
267
|
+
}
|
268
|
+
|
269
|
+
response = HTTParty.get(url, headers: headers)
|
270
|
+
puts "Debug: HTTP Status: #{response.code}"
|
271
|
+
|
272
|
+
doc = Nokogiri::HTML(response.body)
|
273
|
+
puts "Debug: Page title: #{doc.title}"
|
274
|
+
|
275
|
+
results = []
|
276
|
+
|
277
|
+
case engine
|
278
|
+
when "Google"
|
279
|
+
doc.css("div.g").each do |result|
|
280
|
+
link = result.css(".yuRUbf > a").first
|
281
|
+
next unless link
|
282
|
+
|
283
|
+
title = result.css("h3").text.strip
|
284
|
+
url = link["href"]
|
285
|
+
result.css(".VwiC3b").text.strip
|
286
|
+
|
287
|
+
puts "Debug: Found Google result - Title: #{title}"
|
288
|
+
results << url if url.start_with?("http")
|
289
|
+
end
|
290
|
+
|
291
|
+
when "Bing"
|
292
|
+
doc.css("#b_results li.b_algo").each do |result|
|
293
|
+
link = result.css("h2 a").first
|
294
|
+
next unless link
|
295
|
+
|
296
|
+
url = link["href"]
|
297
|
+
puts "Debug: Found Bing result - URL: #{url}"
|
298
|
+
results << url if url.start_with?("http")
|
299
|
+
end
|
300
|
+
|
301
|
+
else # DuckDuckGo
|
302
|
+
doc.css(".result__body").each do |result|
|
303
|
+
link = result.css(".result__title a").first
|
304
|
+
next unless link
|
305
|
+
|
306
|
+
url = link["href"]
|
307
|
+
puts "Debug: Found DuckDuckGo result - URL: #{url}"
|
308
|
+
results << url if url.start_with?("http")
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
puts "Debug: Total results found: #{results.length}"
|
313
|
+
results.take(8)
|
314
|
+
end
|
315
|
+
|
316
|
+
def self.display_results(results)
|
317
|
+
CLI::UI.frame_style = :bracket
|
318
|
+
CLI::UI::Frame.open(CLI::UI.fmt("{{green:Search Results}}")) do
|
319
|
+
results.each do |result|
|
320
|
+
puts CLI::UI.fmt("{{v}} {{cyan:#{result[:engine]}}}")
|
321
|
+
result[:links].each_with_index do |link, idx|
|
322
|
+
puts CLI::UI.fmt(" {{*}} ##{idx + 1}: #{link}")
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
class TodoList
|
330
|
+
def initialize
|
331
|
+
@db = setup_database
|
332
|
+
end
|
333
|
+
|
334
|
+
private def setup_database
|
335
|
+
db = SQLite3::Database.new("greeenboii_todo.db")
|
336
|
+
db.execute <<~SQL
|
337
|
+
CREATE TABLE IF NOT EXISTS todos (
|
338
|
+
id INTEGER PRIMARY KEY,
|
339
|
+
title TEXT NOT NULL,
|
340
|
+
completed BOOLEAN DEFAULT 0,
|
341
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
342
|
+
)
|
343
|
+
SQL
|
344
|
+
db
|
345
|
+
end
|
346
|
+
|
347
|
+
def add_task
|
348
|
+
title = CLI::UI.ask("Enter task title:")
|
349
|
+
return if title.empty?
|
350
|
+
|
351
|
+
@db.execute("INSERT INTO todos (title) VALUES (?)", [title])
|
352
|
+
puts CLI::UI.fmt "{{green:✓}} Task added successfully!"
|
353
|
+
end
|
354
|
+
|
355
|
+
def list_tasks
|
356
|
+
tasks = @db.execute("SELECT id, title, completed, created_at FROM todos ORDER BY created_at DESC")
|
357
|
+
if tasks.empty?
|
358
|
+
puts CLI::UI.fmt "{{yellow:⚠}} No tasks found"
|
359
|
+
return
|
360
|
+
end
|
361
|
+
|
362
|
+
ConsoleTable.define(%w[ID Title Status Created]) do |table|
|
363
|
+
tasks.each do |id, title, completed, created_at|
|
364
|
+
status = completed == 1 ? "{{green:✓}} Done" : "{{red:✗}} Pending"
|
365
|
+
table << [id, title, CLI::UI.fmt(status), created_at]
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def mark_done
|
371
|
+
list_tasks
|
372
|
+
id = CLI::UI.ask("Enter task ID to mark as done:")
|
373
|
+
return if id.empty?
|
374
|
+
|
375
|
+
@db.execute("UPDATE todos SET completed = 1 WHERE id = ?", [id])
|
376
|
+
puts CLI::UI.fmt "{{green:✓}} Task marked as done!"
|
377
|
+
end
|
378
|
+
|
379
|
+
def delete_task
|
380
|
+
list_tasks
|
381
|
+
id = CLI::UI.ask("Enter task ID to delete:")
|
382
|
+
return if id.empty?
|
383
|
+
|
384
|
+
@db.execute("DELETE FROM todos WHERE id = ?", [id])
|
385
|
+
puts CLI::UI.fmt "{{green:✓}} Task deleted!"
|
386
|
+
end
|
387
|
+
|
388
|
+
def update_task
|
389
|
+
list_tasks
|
390
|
+
id = CLI::UI.ask("Enter task ID to update:")
|
391
|
+
return if id.empty?
|
392
|
+
|
393
|
+
title = CLI::UI.ask("Enter new title:")
|
394
|
+
return if title.empty?
|
395
|
+
|
396
|
+
@db.execute("UPDATE todos SET title = ? WHERE id = ?", [title, id])
|
397
|
+
puts CLI::UI.fmt "{{green:✓}} Task updated!"
|
398
|
+
end
|
399
|
+
|
400
|
+
def show_menu
|
401
|
+
CLI::UI::Frame.divider("{{v}} Todo List")
|
402
|
+
loop do
|
403
|
+
CLI::UI::Prompt.ask("Todo List Options:") do |handler|
|
404
|
+
handler.option("List Tasks") { list_tasks }
|
405
|
+
handler.option("Add Task") { add_task }
|
406
|
+
handler.option("Mark Done") { mark_done }
|
407
|
+
handler.option("Update Task") { update_task }
|
408
|
+
handler.option("Delete Task") { delete_task }
|
409
|
+
handler.option("Exit") { return }
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
class Options
|
416
|
+
def self.show_options
|
417
|
+
CLI::UI::Prompt.instructions_color = CLI::UI::Color::GRAY
|
418
|
+
|
419
|
+
CLI::UI::Prompt.ask("Choose an option:") do |handler|
|
420
|
+
handler.option("{{gray:Search Files}}") { |selection| puts "Placeholder, Replaced soon. #{selection}" }
|
421
|
+
handler.option("{{gray:Search Directory}}") { |selection| puts "Placeholder, Replaced soon. #{selection}" }
|
422
|
+
handler.option("{{green:Website Builder}}") { |_selection| WebsiteBuilder.build_website }
|
423
|
+
handler.option("{{yellow:Todo List}}") { |_selection| TodoList.new.show_menu }
|
424
|
+
handler.option("{{cyan:Search Engine}}") { |_selection| Search.perform_search }
|
425
|
+
handler.option("{{red:Exit}}") { |_selection| exit }
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
class Main
|
431
|
+
CLI::UI::StdoutRouter.enable
|
432
|
+
current_time = DateTime.now.strftime("%d-%m-%Y %H:%M:%S")
|
433
|
+
CLI::UI::Frame.open("{{v}} Greeenboi : #{current_time}") do
|
434
|
+
puts "Welcome to Greeenboii"
|
435
|
+
puts "Lets do some magic!"
|
436
|
+
CLI::UI.frame_style = :bracket
|
437
|
+
CLI::UI::Frame.open(CLI::UI.fmt("{{green:Welcome to Greeenboii CLI}}")) do
|
438
|
+
puts CLI::UI.fmt("{{cyan:Version}}: #{Greeenboii::VERSION}")
|
439
|
+
# ConsoleTable.define(%w[Name Version]) do |table|
|
440
|
+
# table << ["Greeenboii", Greeenboii::VERSION]
|
441
|
+
# end
|
442
|
+
puts CLI::UI.fmt("{{yellow:Type 'help' to see available commands}}")
|
443
|
+
Options.show_options
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|