Skip to content

Commit 4feae22

Browse files
authored
Merge pull request #6 from binlabs/feature/sequelize-model
Typescript Sequelize Model bundle
2 parents 91258c5 + 02463e8 commit 4feae22

File tree

8 files changed

+432
-5
lines changed

8 files changed

+432
-5
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,17 @@
22

33
A collection of bundles for Sequel Ace meant to aide in development of Typescript projects.
44

5-
The first bundle allows you to generate Typescript interfaces from the currently selected tables. An additional bundle is in the works that will generate a Sequelize model from the selected tables.
5+
## Bundles
6+
7+
There are currently two Typescript-related bundles whose descriptions you can see below.
8+
9+
### Typescript Interface
10+
11+
This bundle allows you to generate a Typescript interface from the table(s) that you have selected.
12+
13+
### Typescript Sequelize Model with Interface
14+
15+
This bunle allows you to generate a [Typescript Sequelize](https://github.com/RobinBuschmann/sequelize-typescript) model and an accompanying interface, which will be used to instantiate a class for the model.
616

717
## Installation and Usage
818

Typescript Interface.saBundle/Support/footer.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@
4747
</script>
4848
</div>
4949
</body>
50-
</html>
50+
</html>

Typescript Interface.saBundle/Support/header.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@
4848
max-width: 1320px;
4949
}
5050
}
51+
.h3, h3 {
52+
font-size: calc(1.3rem + .6vw);
53+
margin-top: 0;
54+
margin-bottom: .5rem;
55+
font-weight: 500;
56+
line-height: 1.2;
57+
}
5158
.alert {
5259
position: relative;
5360
padding: 1rem 1rem;
@@ -64,6 +71,13 @@
6471
background-color: #f8d7da;
6572
border-color: #f5c2c7;
6673
}
74+
.highlight {
75+
border: 1px solid #afc2c7;
76+
border-radius: 5px;
77+
color: #323230;
78+
margin-bottom: 1.5em;
79+
padding: 0 1em;
80+
}
6781
pre {
6882
display: block;
6983
margin-top: 0;

Typescript Interface.saBundle/Support/parseTableData.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
require 'csv'
55

66
interface_name = table_name.split('_').collect(&:capitalize).join
7+
puts "<h3>Typescript Interface for #{table_name}</h3>"
78
puts "<div class='clipboard'><button type='button' class='btn-clipboard' title='Copy to clipboard' data-target-id='#{interface_name}'>Copy</button></div>"
8-
puts "<div class='hightlight'><pre><code id='#{interface_name}'>"
9-
puts "interface #{interface_name} {"
9+
puts "<div class='highlight'><pre><code id='#{interface_name}'>"
10+
puts "interface #{interface_name}Iface {"
1011
CSV.parse(STDIN.read, headers: true) do |row|
12+
# TODO: Lots of if statements, try to find a better way to do this
1113
fieldType = "unknown"
12-
# Lots of if statements, couldn't get a shorthand OR to work
1314
fieldType = "string" if row['Type'].include? "text"
1415
fieldType = "string" if row['Type'].include? "char"
16+
fieldType = "string" if row['Type'].include? "json"
1517
fieldType = "Date" if row['Type'].include? "date"
1618
fieldType = "DateTime" if row['Type'].include? "datetime"
1719
fieldType = "number" if row['Type'].include? "fixed"
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<script type="text/javascript">
2+
// Copy to Clipboard code from https://stackoverflow.com/a/30810322
3+
function fallbackCopyTextToClipboard(text) {
4+
var textArea = document.createElement("textarea");
5+
textArea.value = text;
6+
7+
// Avoid scrolling to bottom
8+
textArea.style.top = "0";
9+
textArea.style.left = "0";
10+
textArea.style.position = "fixed";
11+
12+
document.body.appendChild(textArea);
13+
textArea.focus();
14+
textArea.select();
15+
16+
try {
17+
var successful = document.execCommand('copy');
18+
var msg = successful ? 'successful' : 'unsuccessful';
19+
console.log('Fallback: Copying text command was ' + msg);
20+
} catch (err) {
21+
console.error('Fallback: Oops, unable to copy', err);
22+
}
23+
24+
document.body.removeChild(textArea);
25+
}
26+
27+
function copyTextToClipboard(text) {
28+
if (!navigator.clipboard) {
29+
fallbackCopyTextToClipboard(text);
30+
return;
31+
}
32+
navigator.clipboard.writeText(text).then(function() {
33+
console.log('Async: Copying to clipboard was successful!');
34+
}, function(err) {
35+
console.error('Async: Could not copy text: ', err);
36+
});
37+
}
38+
39+
var btnCopy = document.querySelector('.btn-clipboard');
40+
btnCopy.addEventListener('click', function(event) {
41+
var btnClicked = event.target || event.srcElement;
42+
var targetId = btnClicked.getAttribute('data-target-id');
43+
var elCode = document.getElementById(targetId);
44+
var text = elCode.innerText || elCode.textContent;
45+
copyTextToClipboard(text);
46+
});
47+
</script>
48+
</div>
49+
</body>
50+
</html>
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<html>
2+
<head>
3+
<title>Typescript Sequelize Models</title>
4+
<meta charset="UTF-8">
5+
<!-- Some basic styling from Bootstrap and Reboot -->
6+
<style>
7+
*, ::after, ::before {
8+
box-sizing: border-box;
9+
}
10+
body {
11+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
12+
font-size: 1rem;
13+
font-weight: 400;
14+
line-height: 1.5;
15+
color: #212529;
16+
text-size-adjust: 100%;
17+
-webkit-text-size-adjust: 100%;
18+
}
19+
.container, .container-fluid, .container-lg, .container-md, .container-sm, .container-xl, .container-xxl {
20+
width: 100%;
21+
padding-right: 0.75rem;
22+
padding-left: 0.75rem;
23+
margin-right: auto;
24+
margin-left: auto;
25+
}
26+
@media (min-width: 576px) {
27+
.container, .container-sm {
28+
max-width: 540px;
29+
}
30+
}
31+
@media (min-width: 768px) {
32+
.container, .container-md, .container-sm {
33+
max-width: 720px;
34+
}
35+
}
36+
@media (min-width: 992px) {
37+
.container, .container-lg, .container-md, .container-sm {
38+
max-width: 960px;
39+
}
40+
}
41+
@media (min-width: 1200px) {
42+
.container, .container-lg, .container-md, .container-sm, .container-xl {
43+
max-width: 1140px;
44+
}
45+
}
46+
@media (min-width: 1400px) {
47+
.container, .container-lg, .container-md, .container-sm, .container-xl, .container-xxl {
48+
max-width: 1320px;
49+
}
50+
}
51+
.h3, h3 {
52+
font-size: calc(1.3rem + .6vw);
53+
margin-top: 0;
54+
margin-bottom: .5rem;
55+
font-weight: 500;
56+
line-height: 1.2;
57+
}
58+
.alert {
59+
position: relative;
60+
padding: 1rem 1rem;
61+
margin-bottom: 1rem;
62+
border: 1px solid transparent;
63+
border-top-color: transparent;
64+
border-right-color: transparent;
65+
border-bottom-color: transparent;
66+
border-left-color: transparent;
67+
border-radius: .25rem;
68+
}
69+
.alert-danger {
70+
color: #842029;
71+
background-color: #f8d7da;
72+
border-color: #f5c2c7;
73+
}
74+
.highlight {
75+
border: 1px solid #afc2c7;
76+
border-radius: 5px;
77+
color: #323230;
78+
margin-bottom: 1.5em;
79+
padding: 0 1em;
80+
}
81+
pre {
82+
display: block;
83+
margin-top: 0;
84+
margin-bottom: 1rem;
85+
overflow: auto;
86+
font-size: .875em;
87+
}
88+
pre code {
89+
font-size: inherit;
90+
color: inherit;
91+
word-break: normal;
92+
}
93+
code {
94+
font-size: .875em;
95+
color: #d63384;
96+
word-wrap: break-word;
97+
}
98+
code, pre {
99+
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace, sans-serif;
100+
font-size: 1em;
101+
direction: ltr;
102+
unicode-bidi: bidi-override;
103+
}
104+
.clipboard {
105+
position: relative;
106+
display: block;
107+
float: right;
108+
}
109+
button {
110+
border-radius: 0;
111+
margin: 0;
112+
font-family: inherit;
113+
font-size: inherit;
114+
line-height: inherit;
115+
text-transform: none;
116+
}
117+
[type="button"], button {
118+
-webkit-appearance: button;
119+
}
120+
.btn-clipboard {
121+
position: absolute;
122+
top: .65rem;
123+
right: .65rem;
124+
z-index: 10;
125+
display: block;
126+
padding: .25rem .5rem;
127+
font-size: .65em;
128+
color: #0d6efd;
129+
background-color: #fff;
130+
border: 1px solid;
131+
border-radius: .25rem;
132+
}
133+
.btn-clipboard:hover, .btn-clipboard:focus {
134+
color: #fff;
135+
background-color: #0d6efd;
136+
}
137+
[type="button"]:not(:disabled), button:not(:disabled) {
138+
cursor: pointer;
139+
}
140+
</style>
141+
</head>
142+
<body>
143+
<div class="container">
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env ruby
2+
require 'csv'
3+
4+
# General Variables
5+
table_name = ARGV[0]
6+
csv_data = STDIN.read
7+
class_name = table_name.split('_').collect(&:capitalize).join
8+
interface_name = table_name.split('_').collect(&:capitalize).join + 'Iface'
9+
single_return = "\n"
10+
# Model Variables
11+
allow_null_false = "allowNull: false"
12+
allow_null_true = "allowNull: true"
13+
data_type_boolean = "type: DataTypes.BOOLEAN"
14+
data_type_date = "type: DataTypes.DATE"
15+
data_type_decimal = "type: DataTypes.DECIMAL"
16+
data_type_double = "type: DataTypes.DOUBLE"
17+
data_type_fixed = "type: DataTypes.FIXED"
18+
data_type_float = "type: DataTypes.FLOAT"
19+
data_type_integer = "type: DataTypes.INTEGER"
20+
data_type_interger_unsigned = "type: DataTypes.INTEGER.UNSIGNED"
21+
data_type_json = "type: DataTypes.JSON"
22+
data_type_numeric = "type: DataTypes.NUMBER"
23+
data_type_string = "type: DataTypes.STRING"
24+
default_value_date_now = "defaultValue: literal('NOW()')"
25+
default_value_empty_string = "defaultValue: ''"
26+
default_value_false = "defaultValue: false"
27+
default_value_null = "defaultValue: null"
28+
default_value_true = "defaultValue: true"
29+
default_value_zero = "defaultValue: 0"
30+
31+
# Print the heading and opening of the HTML code wrapper
32+
puts "<h3>Sequelize Model and Interface for #{class_name}</h3>"
33+
puts "<div class='clipboard'><button type='button' class='btn-clipboard' title='Copy to clipboard' data-target-id='#{class_name}'>Copy</button></div>"
34+
puts "<div class='highlight'><pre><code id='#{class_name}'>"
35+
36+
# Print the imports
37+
puts "import { DataTypes, Model, InitOptions, ModelAttributes, literal } from 'sequelize'"
38+
puts "// Uncomment the following line and update the import location"
39+
puts "// import sequelize from 'location_of_your_sequelize_database_instance'"
40+
puts single_return
41+
42+
# Print the interface
43+
puts "interface #{interface_name} {"
44+
CSV.parse(csv_data, headers: true) do |row|
45+
# TODO: Lots of if statements, try to find a better way to do this
46+
# Field Types
47+
fieldType = "unknown"
48+
fieldType = "string" if row['Type'].include? "text"
49+
fieldType = "string" if row['Type'].include? "char"
50+
fieldType = "string" if row['Type'].include? "json"
51+
fieldType = "Date" if row['Type'].include? "date"
52+
fieldType = "DateTime" if row['Type'].include? "datetime"
53+
fieldType = "number" if row['Type'].include? "fixed"
54+
fieldType = "number" if row['Type'].include? "float"
55+
fieldType = "number" if row['Type'].include? "int"
56+
fieldType = "number" if row['Type'].include? "dec"
57+
fieldType = "number" if row['Type'].include? "doub"
58+
fieldType = "number" if row['Type'].include? "numeric"
59+
fieldType = "number" if row['Type'].include? "year"
60+
fieldType = "boolean" if row['Type'].include? "tinyint"
61+
fieldType = "boolean" if row['Type'].include? "bool"
62+
puts " #{row['Field']}: #{fieldType}"
63+
end
64+
puts "}"
65+
puts single_return
66+
67+
# Print the model
68+
puts "const tableName = '#{table_name}'"
69+
puts single_return
70+
puts "const columns: ModelAttributes = {"
71+
CSV.parse(csv_data, headers: true) do |row|
72+
dataType = "unknown"
73+
# Lots of if statements, couldn't get a shorthand OR to work
74+
dataType = data_type_string if row['Type'].include? "text"
75+
dataType = data_type_string if row['Type'].include? "char"
76+
dataType = data_type_json if row['Type'].include? "json"
77+
dataType = data_type_date if row['Type'].include? "date"
78+
dataType = data_type_date if row['Type'].include? "datetime"
79+
dataType = data_type_fixed if row['Type'].include? "fixed"
80+
dataType = data_type_float if row['Type'].include? "float"
81+
dataType = data_type_integer if row['Type'].include? "int"
82+
dataType = data_type_interger_unsigned if row['Type'] =~ /int.*unsigned/
83+
dataType = data_type_interger_unsigned if row['Type'].include? "dec"
84+
dataType = data_type_double if row['Type'].include? "doub"
85+
dataType = data_type_numeric if row['Type'].include? "numeric"
86+
dataType = data_type_date if row['Type'].include? "year"
87+
dataType = data_type_boolean if row['Type'].include? "tinyint"
88+
dataType = data_type_boolean if row['Type'].include? "bool"
89+
# Allow Null
90+
allowNull = allow_null_true
91+
allowNull = allow_null_true if row['Null'] == "NO"
92+
# Default Value
93+
defaultValue = default_value_empty_string
94+
defaultValue = default_value_date_now if row['Type'] == "CURRENT_TIMESTAMP"
95+
defaultValue = default_value_null if row['Default'] == "NULL"
96+
defaultValue = default_value_true if row['Default'] == "true"
97+
defaultValue = default_value_false if row['Default'] == "false"
98+
defaultValue = default_value_zero if row['Default'] == "0"
99+
puts " #{row['Field']}: { #{allowNull}, #{defaultValue}, #{dataType} },"
100+
101+
end
102+
puts "}"
103+
puts single_return
104+
105+
# Print the model options
106+
puts "const options: Omit&lt;InitOptions&lt;Model&gt;, 'sequelize'&gt; = {"
107+
puts " modelName: tableName,"
108+
puts "}"
109+
puts single_return
110+
puts "class #{class_name} extends Model\<#{interface_name}\> {}"
111+
puts single_return
112+
puts "#{class_name}.init(columns, { ...options, sequelize })"
113+
puts single_return
114+
puts "export default #{class_name}"
115+
puts single_return
116+
# Print the closing of the HTML code wrapper
117+
puts "</code></pre></div>"

0 commit comments

Comments
 (0)