jetski 0.4.8 → 0.5.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/bin/jetski +5 -0
  3. data/bin/jetski_cli_helpers/base.rb +11 -0
  4. data/bin/jetski_cli_helpers/database.rb +16 -12
  5. data/bin/jetski_cli_helpers/destroy.rb +11 -10
  6. data/bin/jetski_cli_helpers/destroyers/controller.rb +13 -0
  7. data/bin/jetski_cli_helpers/destroyers/model.rb +13 -0
  8. data/bin/jetski_cli_helpers/generate.rb +12 -42
  9. data/bin/jetski_cli_helpers/generators/controller.rb +40 -0
  10. data/bin/jetski_cli_helpers/generators/model.rb +31 -0
  11. data/lib/jetski/autoloader.rb +2 -6
  12. data/lib/jetski/base_controller.rb +2 -4
  13. data/lib/jetski/database/base.rb +8 -30
  14. data/lib/jetski/database/interface.rb +31 -0
  15. data/lib/jetski/database/sqlite3.rb +6 -0
  16. data/lib/jetski/frontend/javascript_helpers.rb +12 -0
  17. data/lib/jetski/helpers/delegatable.rb +1 -1
  18. data/lib/jetski/helpers/generic.rb +14 -0
  19. data/lib/jetski/helpers/route_helpers.rb +1 -2
  20. data/lib/jetski/helpers/view_helpers.rb +53 -8
  21. data/lib/jetski/model/attributes.rb +46 -0
  22. data/lib/jetski/model/crud_helpers.rb +76 -0
  23. data/lib/jetski/model.rb +14 -96
  24. data/lib/jetski/router/{shared_methods.rb → file_path_helper.rb} +2 -2
  25. data/lib/jetski/router/host/base.rb +58 -0
  26. data/lib/jetski/router/host/controller.rb +59 -0
  27. data/lib/jetski/router/host/crud.rb +72 -0
  28. data/lib/jetski/router/parser.rb +8 -8
  29. data/lib/jetski/router.rb +33 -57
  30. data/lib/jetski/server.rb +1 -1
  31. data/lib/jetski/version.rb +2 -2
  32. data/lib/jetski/view_renderer.rb +23 -8
  33. data/lib/jetski.rb +13 -5
  34. data/templates/controllers/controller.rb.erb +56 -0
  35. data/templates/controllers/views/default.html.erb +2 -0
  36. data/templates/controllers/views/edit.html.erb +7 -0
  37. data/templates/controllers/views/index.html.erb +12 -0
  38. data/templates/controllers/views/new.html.erb +7 -0
  39. data/templates/controllers/views/show.html.erb +8 -0
  40. data/templates/model/template.rb.erb +5 -0
  41. metadata +30 -10
  42. data/lib/jetski/frontend/reactive_form.rb +0 -8
  43. /data/lib/jetski/frontend/{reactive_form.js → javascript_helpers.js} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 718959913722c396f5ada24d19c6997aee009d7a0aca229156a877def8dba9e4
4
- data.tar.gz: 8ad9d4179950762af88e4f4df0a02ff4d8510050a5f7417af440b04f758a78dd
3
+ metadata.gz: 0fba85e1a50746a7e376738a0ec5648ce22edcc7874ea0d9ee9b3be681d7364d
4
+ data.tar.gz: b3ac634c65fd946fb1dc43c62702b620b46dd17e1369ef0716eedfa6885e373d
5
5
  SHA512:
6
- metadata.gz: c1c685e092ab8da1cf08805874d24f3e2319982f11c1a17ce14e92f0342b8ef5622a24cdd4db89e325c36e0a8c842e547057468b592d9028fdbff1fff84963bf
7
- data.tar.gz: 847df89c32ce237683bd953da15d589236fe7884707e088e09d0512cc7554a3df09b05a493dd537586e11ac7e094ffc1142e443ea5649d5f1311aea329a669a4
6
+ metadata.gz: 9740af48073b1a0c8c069d834e2da63c7b351b5a84c5c77502348854c1164105a1002282c8036fa2eaa040cbf085b9e8e8a1ffb98c4b16a87208ca33dccd544c
7
+ data.tar.gz: cc288a4e32d8dff6c78c14baad4fb317049b2329d30a05bb3132ad1e054db0bf7a0924fec502720b7ae80a7e99d047ee2c364f1e1c5327d81244ddbb2734c6e1
data/bin/jetski CHANGED
@@ -5,7 +5,12 @@ require 'optparse'
5
5
  require 'thor'
6
6
  require "pry"
7
7
 
8
+ require_relative './jetski_cli_helpers/generators/controller.rb'
9
+ require_relative './jetski_cli_helpers/generators/model.rb'
10
+ require_relative './jetski_cli_helpers/destroyers/controller.rb'
11
+ require_relative './jetski_cli_helpers/destroyers/model.rb'
8
12
  require_relative './jetski_cli_helpers/shared_methods.rb'
13
+ require_relative './jetski_cli_helpers/base.rb'
9
14
  require_relative './jetski_cli_helpers/generate.rb'
10
15
  require_relative './jetski_cli_helpers/destroy.rb'
11
16
  require_relative './jetski_cli_helpers/database.rb'
@@ -0,0 +1,11 @@
1
+ # Reason for this is to dry up code and includes between classes
2
+ module JetskiCLIHelpers
3
+ class Base < Thor
4
+ include Jetski::Helpers::Generic, Thor::Actions, JetskiCLIHelpers::SharedMethods,
5
+ Jetski::Database::Base, Jetski::Database::Interface
6
+
7
+ def self.source_root
8
+ File.join(File.dirname(__FILE__), '..', '..', 'templates')
9
+ end
10
+ end
11
+ end
@@ -1,16 +1,20 @@
1
- require 'thor'
2
1
  module JetskiCLIHelpers
3
- class Database < Thor
4
- include Thor::Actions, JetskiCLIHelpers::SharedMethods,
5
- Jetski::Database::Base
6
- desc "create", "Creates a database for your app"
7
- def create
8
- say "🌊 Database was created successfully!"
9
- end
10
-
11
- desc "create_table NAME COLUMN_NAMES", "Creates a new table in your database"
12
- def create_table(name, *fields)
13
- db.execute create_table_sql(table_name: name, field_names: fields)
2
+ class Database < Base
3
+ desc "migrate", "Create, load, and patch DB from models"
4
+ def migrate
5
+ Jetski::Autoloader.call
6
+ # Get all models defined in users app
7
+ Jetski::Model.subclasses.each do |model|
8
+ table_name = model.pluralized_table_name
9
+ # Create table if it doesnt exist
10
+ create_table(table_name) if !table_exists?(table_name)
11
+ # Get model attributes
12
+ model.db_attribute_values.each do |obj|
13
+ name = obj[:name]
14
+ type = obj[:type]
15
+ add_column_unless_exists(table_name, name, type)
16
+ end
17
+ end
14
18
  end
15
19
 
16
20
  desc "seed", "Seeds the database with records created from seed file"
@@ -1,20 +1,21 @@
1
- require 'thor'
2
1
  module JetskiCLIHelpers
3
- class Destroy < Thor
4
- include Thor::Actions
2
+ class Destroy < Base
3
+ include JetskiCLIHelpers::Destroyers::Controller,
4
+ JetskiCLIHelpers::Destroyers::Model
5
5
  desc "controller NAME ACTION_NAMES", "Destroys a controller"
6
6
  def controller(name, *actions)
7
- controller_file_path = "app/controllers/#{name}_controller.rb"
8
- remove_file(controller_file_path)
9
- view_folder = "app/views/#{name}"
10
- remove_dir(view_folder)
7
+ destroy_controller(name)
11
8
  end
12
9
 
13
10
  desc "model NAME ACTION_NAMES", "Destroys a model"
14
11
  def model(name, *actions)
15
- model_file_path = "app/models/#{name}.rb"
16
- remove_file(controller_file_path)
17
- # TODO: Remove from db.
12
+ destroy_model(name)
13
+ end
14
+
15
+ desc "resource NAME ACTION_NAMES", "Destroys a resource"
16
+ def resource(name, *actions)
17
+ destroy_controller(name)
18
+ destroy_model(name)
18
19
  end
19
20
  end
20
21
  end
@@ -0,0 +1,13 @@
1
+ module JetskiCLIHelpers
2
+ module Destroyers
3
+ module Controller
4
+ def destroy_controller(name)
5
+ controller_name = pluralize_string(name)
6
+ controller_file_path = "app/controllers/#{controller_name}_controller.rb"
7
+ remove_file(controller_file_path)
8
+ view_folder = "app/views/#{controller_name}"
9
+ remove_dir(view_folder)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module JetskiCLIHelpers
2
+ module Destroyers
3
+ module Model
4
+ def destroy_model(name)
5
+ model_file_path = "app/models/#{name}.rb"
6
+ remove_file(model_file_path)
7
+ table_name = pluralize_string(name)
8
+ remove_table_sql = "DROP TABLE #{table_name}"
9
+ db.execute(remove_table_sql)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,52 +1,22 @@
1
- require 'thor'
2
1
  module JetskiCLIHelpers
3
- class Generate < Thor
4
- include Thor::Actions, JetskiCLIHelpers::SharedMethods,
5
- Jetski::Database::Base
2
+ class Generate < Base
3
+ include JetskiCLIHelpers::Generators::Controller,
4
+ JetskiCLIHelpers::Generators::Model
6
5
  desc "controller NAME ACTION_NAMES", "Create a controller with matching actions"
7
6
  def controller(name, *actions)
8
- controller_file_path = "app/controllers/#{name}_controller.rb"
9
- create_file controller_file_path
10
- append_to_file controller_file_path, <<~CONTROLLER
11
- class #{name.capitalize}Controller < Jetski::BaseController
12
-
13
- end
14
- CONTROLLER
15
-
16
- actions.each.with_index do |action_name, idx|
17
- action_content = <<~ACTION_CONTENT
18
- def #{action_name}
19
-
20
- end
21
- ACTION_CONTENT
22
- action_nl_seperator = ((idx + 1) != actions.size) ? "\n\n" : ""
23
- insert_into_file(controller_file_path, "#{indent_code(action_content, 1)}#{action_nl_seperator}", before: "\nend")
24
-
25
- if !["create", "create", "update", "destroy"].include?(action_name)
26
- path_to_view = "app/views/#{name}/#{action_name}.html.erb"
27
-
28
- empty_directory("app/views/#{name}")
29
- create_file(path_to_view)
30
- append_to_file path_to_view, <<~EXAMPLEFILE
31
- <h1> #{name}##{action_name} </h1>
32
- <p> edit the content of this page at app/views/#{path_to_view}/#{action_name}.html.erb </p>
33
- EXAMPLEFILE
34
-
35
- say "🌊 View your new page at http://localhost:8000/#{name}/#{action_name}"
36
- end
37
- end
7
+ generate_controller(name, *actions)
38
8
  end
39
9
 
40
10
  desc "model NAME FIELD_NAMES", "Creates a model with matching fields"
41
11
  def model(name, *field_names)
42
- db.execute create_table_sql(table_name: name, field_names: field_names)
43
- model_file_path = "app/models/#{name}.rb"
44
- create_file model_file_path
45
- append_to_file model_file_path, <<~MODEL
46
- class #{name.capitalize} < Jetski::Model
47
-
48
- end
49
- MODEL
12
+ generate_model(name, *field_names)
13
+ end
14
+
15
+ desc "resource NAME FIELD_NAMES", "Creates a resource with model and controller and crud routes"
16
+ def resource(name, *field_names)
17
+ crud_actions = %w(new create show index edit update destroy)
18
+ generate_model(name, *field_names)
19
+ generate_controller(name, *crud_actions, field_names: field_names)
50
20
  end
51
21
  end
52
22
  end
@@ -0,0 +1,40 @@
1
+ module JetskiCLIHelpers
2
+ module Generators
3
+ module Controller
4
+ def generate_controller(name, *actions, **extra_options)
5
+ field_names = extra_options[:field_names]
6
+ @name = name
7
+ @controller_name = pluralize_string(name)
8
+
9
+ controller_file_path = "app/controllers/#{@controller_name}_controller.rb"
10
+
11
+ @controller_class_name = @controller_name.split("_").map(&:capitalize).join
12
+ @model_class_name = @name.capitalize # post -> Post
13
+ @field_names = field_names
14
+ @actions = actions
15
+
16
+ # Create controller from template
17
+ template "controllers/controller.rb.erb", controller_file_path
18
+
19
+ actions.each do |action_name|
20
+ # create views
21
+ if !["create", "create", "update", "destroy"].include?(action_name)
22
+ empty_directory("app/views/#{@controller_name}")
23
+
24
+ # For new, show, edit, index actions
25
+ @path_to_view = "app/views/#{@controller_name}/#{action_name}.html.erb"
26
+
27
+ template_name = if ["new", "show", "edit", "index"].include?(action_name)
28
+ action_name
29
+ else
30
+ "default"
31
+ end
32
+ @action_name = action_name
33
+ template "controllers/views/#{template_name}.html.erb", @path_to_view
34
+ say "🌊 View your new page at http://localhost:8000/#{@controller_name}/#{action_name}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ module JetskiCLIHelpers
2
+ module Generators
3
+ module Model
4
+ def generate_model(name, *field_names)
5
+ # TODO: Instead of creating table here we should have a db:migrate method
6
+ # this will automatically build and patch db tables. Add/remove columns
7
+ #db.execute create_table_sql(table_name: name, field_names: field_names)
8
+ @model_name = name
9
+
10
+
11
+ if field_names
12
+ @model_attributes = []
13
+ custom_fields = field_names.filter { |f| f.include?(":") }
14
+ string_fields = field_names - custom_fields
15
+ string_fields.each do |field|
16
+ @model_attributes << ":#{field}"
17
+ end
18
+
19
+ custom_fields.each do |custom_field|
20
+ field, type = custom_field.split(":")
21
+ @model_attributes << "#{field}: :#{type}"
22
+ end
23
+ end
24
+
25
+ model_file_path = "app/models/#{name}.rb"
26
+ template "model/template.rb.erb", model_file_path
27
+ end
28
+ end
29
+ end
30
+ end
31
+
@@ -1,6 +1,6 @@
1
- module Jetski
1
+ class Jetski
2
2
  module Autoloader
3
- include Jetski::Router::SharedMethods
3
+ include Jetski::Router::FilePathHelper
4
4
  extend self
5
5
  # Responsibility is to load all models in app.
6
6
  def call
@@ -12,10 +12,6 @@ module Jetski
12
12
  .split("/")
13
13
  .map(&:capitalize)
14
14
  .join("::")
15
- # posts/comment
16
- # Post/comment
17
- model_class = Object.const_get(model_name)
18
- model_class.define_attribute_methods
19
15
  end
20
16
  end
21
17
 
@@ -1,12 +1,12 @@
1
1
  # This is the base controller of the library
2
- module Jetski
2
+ class Jetski
3
3
  class BaseController
4
4
  RESERVED_INSTANCE_VARIABLES = [
5
5
  :@res, :@performed_render, :@action_name,
6
6
  :@controller_name, :@controller_path, :@cookies
7
7
  ]
8
8
 
9
- include ReactiveForm
9
+ include Jetski::Frontend::JavascriptHelpers
10
10
  extend Jetski::Helpers::RouteHelpers
11
11
  attr_accessor :action_name, :controller_name, :controller_path,
12
12
  :params, :cookies
@@ -19,8 +19,6 @@ module Jetski
19
19
  end
20
20
 
21
21
  # Method to render matching view with controller_name/action_name
22
- # TODO: Make render available in view as seperate method.
23
-
24
22
  def render(**args)
25
23
  @performed_render = true
26
24
  request_status = args[:status] || 200
@@ -1,4 +1,4 @@
1
- module Jetski
1
+ class Jetski
2
2
  module Database
3
3
  module Base
4
4
  extend self
@@ -7,35 +7,13 @@ module Jetski
7
7
  @_db ||= SQLite3::Database.new "test.db"
8
8
  end
9
9
 
10
- def create_table_sql(table_name:, field_names:)
11
- pluralized_table_name = if table_name.chars.last == "s"
12
- table_name
13
- else
14
- table_name + "s"
15
- end
16
-
17
- _gen_sql = ""
18
- _gen_sql += "create table #{pluralized_table_name} (\n"
19
-
20
- # Default fields on all models
21
- _gen_sql += " created_at datetime,\n"
22
- _gen_sql += " updated_at datetime,\n"
23
- _gen_sql += " id integer,\n"
24
-
25
- field_names.each.with_index do |field_name, idx|
26
- field, field_data_type = if field_name.include?(":")
27
- field_name.split(":")
28
- else
29
- [field_name, ""]
30
- end
31
- data_type = sql_data_type(field_data_type)
32
- _gen_sql += " #{field} #{data_type}"
33
- if (idx + 1) < field_names.size
34
- _gen_sql += ",\n"
35
- else
36
- _gen_sql += "\n"
37
- end
38
- end
10
+ def create_table_sql(table_name:, field_names: [])
11
+ table_name = pluralize_string(table_name)
12
+ default_fields = [["created_at", "datetime"], ["updated_at", "datetime"], ["id", "integer"]]
13
+ fields = field_names.map { |f| f.split(":") }
14
+ all_fields = fields + default_fields
15
+ _gen_sql = "create table #{table_name} (\n"
16
+ _gen_sql += all_fields.map { |field, data_type| " #{field} #{data_type}" }.join(",\n")
39
17
  _gen_sql += ");\n"
40
18
  _gen_sql
41
19
  end
@@ -0,0 +1,31 @@
1
+ # Responsibility is to wrap calls to db and make it more clean and readable.
2
+
3
+ class Jetski
4
+ module Database
5
+ module Interface
6
+ extend self
7
+
8
+ # expects an array of fields with type seperated by colon :
9
+ def create_table(table_name, *fields)
10
+ db.execute create_table_sql(table_name: table_name, field_names: fields)
11
+ end
12
+
13
+ def add_column(table_name, field_name, data_type = "string")
14
+ db.execute("ALTER TABLE #{table_name} ADD #{field_name} #{sql_data_type(data_type)}")
15
+ end
16
+
17
+ def add_column_unless_exists(table_name, field_name, data_type = nil)
18
+ add_column(table_name, field_name, data_type) if !field_exists?(table_name, field_name)
19
+ end
20
+
21
+ def table_exists?(table_name)
22
+ db.get_first_value("SELECT name FROM sqlite_master WHERE type='table' AND name=?", table_name)
23
+ end
24
+
25
+ def field_exists?(table_name, field_name)
26
+ table_info = db.execute("PRAGMA table_info(#{table_name})")
27
+ table_info.find { |d| d[1] == field_name } != nil
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,6 @@
1
+ class Jetski
2
+ module Database
3
+ module Sqlite3
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ class Jetski
2
+ module Frontend
3
+ module JavascriptHelpers
4
+ def reactive_text_area(path, **opts)
5
+ css_classes = opts[:class]
6
+ value = opts[:value]
7
+ # Text area that auto saves to url.
8
+ "<textarea reactive-form-path='#{path}' class='#{css_classes}'>#{value}</textarea>"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,4 +1,4 @@
1
- module Jetski
1
+ class Jetski
2
2
  module Helpers
3
3
  module Delegatable
4
4
  # delegate(:method, to: :class)
@@ -0,0 +1,14 @@
1
+ class Jetski
2
+ module Helpers
3
+ module Generic
4
+ # Helpers for use all over codebase for simple grammatics etc..
5
+ def pluralize_string(string)
6
+ if string[-1] == 's'
7
+ string
8
+ else
9
+ string + 's'
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,10 +1,9 @@
1
1
  # This is responsible for methods for routing in base_controller like the route class method
2
2
 
3
- module Jetski
3
+ class Jetski
4
4
  module Helpers
5
5
  module RouteHelpers
6
6
  def route(method_name, root: false, path: nil, request_method: nil)
7
- # TODO: write code to set route
8
7
  @custom_route_opts ||= {}
9
8
  @custom_route_opts[method_name] = {
10
9
  method_name: method_name,
@@ -1,4 +1,4 @@
1
- module Jetski
1
+ class Jetski
2
2
  module Helpers
3
3
  module ViewHelpers
4
4
  # Expecting a relative path.
@@ -21,24 +21,60 @@ module Jetski
21
21
  end
22
22
 
23
23
  # button_to("Click me", class: "cool-btn")
24
- def button_to(button_text, **html_opts)
25
- "<button #{format_html_options(**html_opts)}>#{button_text}</button>"
24
+ def button_to(button_text, url = nil, **html_opts)
25
+ if url
26
+ _method = html_opts[:method] || 'POST'
27
+ # Create form with button inside lol
28
+ _form = "<form action='#{url}' method='#{_method}'>"
29
+ _form += "<button type='submit' #{format_html_options(**html_opts)}>#{button_text}</button>"
30
+ _form += "</form>"
31
+ _form
32
+ else
33
+ _button = "<button #{format_html_options(**html_opts)}>#{button_text}</button>"
34
+ end
26
35
  end
27
36
 
28
- def input_tag(**html_opts)
37
+ def input_tag(input_name = nil, **html_opts)
38
+ html_opts[:name] ||= input_name
29
39
  "<input #{format_html_options(**html_opts)}>"
30
40
  end
31
41
 
32
- def textarea_tag(**html_opts)
33
- "<textarea #{format_html_options(**html_opts.except(:value))}>#{html_opts[:value]}</textarea>"
42
+ def label_tag(text, **html_opts)
43
+ "<label #{format_html_options(**html_opts)}>#{text}</label>"
44
+ end
45
+
46
+ def textarea_tag(name = nil, value = nil, **html_opts)
47
+ html_opts[:name] ||= name
48
+ value ||= html_opts[:value]
49
+ "<textarea #{format_html_options(**html_opts.except(:value))}>#{value}</textarea>"
34
50
  end
35
51
 
36
52
  def image_tag(image_path, **html_opts)
37
- "<img src='#{image_path}' #{format_html_options(**html_opts)}></img>"
53
+ abs_url = process_url(image_path)
54
+ "<img src='#{abs_url}' #{format_html_options(**html_opts)}></img>"
38
55
  end
39
56
 
40
57
  def favicon_tag(url, **html_opts)
41
- "<link rel='icon' type='image/x-icon' #{format_html_options(**html_opts)} href='#{url}'>"
58
+ abs_url = process_url(url)
59
+ "<link rel='icon' type='image/x-icon' #{format_html_options(**html_opts)} href='#{abs_url}'>"
60
+ end
61
+
62
+ def stylesheet_tag(url, **html_opts)
63
+ abs_url = process_url(url)
64
+ "<link rel='stylesheet' #{format_html_options(**html_opts)} href='#{abs_url}'>"
65
+ end
66
+
67
+ def javascript_include_tag(url, **html_opts)
68
+ abs_url = process_url(url)
69
+ "<script src='#{abs_url}' #{format_html_options(**html_opts)}></script>"
70
+ end
71
+
72
+ def javascript_tag(**html_opts)
73
+ "<script #{format_html_options(**html_opts)}>#{yield}</script>"
74
+ end
75
+
76
+ def truncate(text, limit)
77
+ text[0..limit]
42
78
  end
43
79
 
44
80
  def format_html_options(**html_opts)
@@ -47,6 +83,15 @@ module Jetski
47
83
  "#{formatted_key}='#{v}'"
48
84
  end.join(" ")
49
85
  end
86
+ private
87
+ # TODO: could move this to a sharable method.
88
+ def process_url(url)
89
+ if url[0] == '/'
90
+ url
91
+ else
92
+ "/#{url}"
93
+ end
94
+ end
50
95
  end
51
96
  end
52
97
  end
@@ -0,0 +1,46 @@
1
+ class Jetski
2
+ class Model
3
+ module Attributes
4
+ # class method for user to configure add/remove attributes on model
5
+ def attributes(*string_attributes, **custom_attributes)
6
+ @_db_string_attributes = string_attributes
7
+ @_db_custom_attributes = custom_attributes
8
+ combined_attributes = string_attributes + custom_attributes.map { |k, _v| k }
9
+ @_attribute_names ||= []
10
+ @_attribute_names.concat(combined_attributes)
11
+ @_attribute_names.concat([:created_at, :updated_at, :id]) # defaults
12
+ @_attribute_names = @_attribute_names.uniq
13
+ @_attribute_names.each do |attribute|
14
+ define_method attribute do
15
+ @virtual_attributes[attribute]
16
+ end
17
+ end
18
+ end
19
+
20
+ def attribute_names
21
+ @_attribute_names || []
22
+ end
23
+
24
+ def db_attribute_values
25
+ # Returns the formatted objects with types for each attribute
26
+ _db_attributes = []
27
+
28
+ @_db_string_attributes.each do |value|
29
+ _db_attributes << {
30
+ name: value,
31
+ type: :string
32
+ }
33
+ end
34
+
35
+ @_db_custom_attributes.each do |key, value|
36
+ _db_attributes << {
37
+ name: key,
38
+ type: value
39
+ }
40
+ end
41
+
42
+ _db_attributes
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,76 @@
1
+ class Jetski
2
+ class Model
3
+ module CrudHelpers
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ def destroy!
9
+ # Destroy record: remove from db
10
+ delete_sql = <<~SQL
11
+ DELETE from #{self.class.pluralized_table_name} WHERE id=?
12
+ SQL
13
+ self.class.db.execute(delete_sql, id)
14
+ nil
15
+ end
16
+
17
+ module ClassMethods
18
+ def create(hash_args = nil, **key_args)
19
+ args = if hash_args && hash_args.is_a?(Hash)
20
+ hash_args
21
+ else
22
+ key_args
23
+ end
24
+
25
+ return puts "#{table_name.capitalize}.create was called with no args" if args.size == 0
26
+ data_values = args.map { |k,v| v }
27
+ key_names = args.map { |k, v| k }
28
+
29
+ # Set default values on create
30
+
31
+ key_names.append "created_at"
32
+ data_values.append Time.now.to_s
33
+
34
+ current_record_count = (count || 0)
35
+ record_id = current_record_count + 1
36
+ key_names.append "id"
37
+ data_values.append(record_id)
38
+
39
+ sql_command = <<~SQL
40
+ INSERT INTO #{pluralized_table_name} (#{key_names.join(", ")})
41
+ VALUES (#{(1..key_names.size).map { |n| "?" }.join(", ")})
42
+ SQL
43
+
44
+ db.execute(sql_command, data_values)
45
+
46
+ record_attributes = {}
47
+ key_names.each.with_index do |k, i|
48
+ record_attributes[k.to_sym] = data_values[i]
49
+ end
50
+
51
+ new(**record_attributes)
52
+ end
53
+
54
+ def all
55
+ columns, *rows = db.execute2( "select * from #{pluralized_table_name}" )
56
+ _all = []
57
+ rows.map do |row|
58
+ _all << format_model_obj(row, columns)
59
+ end
60
+ _all
61
+ end
62
+
63
+ def find(id)
64
+ id_as_integer = id.to_i
65
+ columns, *rows = db.execute2( "select * from #{pluralized_table_name} WHERE id=?", id_as_integer)
66
+ format_model_obj(rows.last, columns)
67
+ end
68
+
69
+
70
+ def destroy_all!
71
+ db.execute("DELETE from #{pluralized_table_name}")
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end