autorest 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +119 -0
- data/bin/autorest +5 -0
- data/lib/autorest/db/adapter.rb +180 -0
- data/lib/autorest/db/mysql.rb +57 -0
- data/lib/autorest/db/oracle.rb +90 -0
- data/lib/autorest/db/postgres.rb +80 -0
- data/lib/autorest/db/sqlite.rb +54 -0
- data/lib/autorest/server.rb +123 -0
- data/lib/autorest/version.rb +3 -0
- data/lib/autorest.rb +153 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c854866f2912dfd5420837fc1aadbb19e8c61de643682c72e2ccff56256d8685
|
4
|
+
data.tar.gz: c5be271a7e68f590840621f86ef96216406200fa1e3eafd2b3d4ed49efe7fc5c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eb6f7672555ac4bbd854a7a9635805a593a9a2562e3abd5e9d7974a93eaa02c866704a644268a9d65f97fd92094ad470d0905899e155c99b0276bccf895187ec
|
7
|
+
data.tar.gz: e6499256011494ed17c5b697c88777699599caeb4068903f5d11abd4ffa077f99ccaea32c0ab85b39d1ac28211705fb90f79c3622b004ef36bca3d0608ff0454
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Harish Kumar
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# 🌐 AutoREST
|
2
|
+
[](https://www.ruby-lang.org)
|
3
|
+
|
4
|
+
Generate full-featured API servers for your database tables in seconds.
|
5
|
+
|
6
|
+
# ℹ About
|
7
|
+
AutoREST is a database-agnostic RESTful API generator for Ruby. With just your database credentials, it scaffolds a live API server supporting CRUD operations — no Rails, no boilerplate.
|
8
|
+
|
9
|
+
**Supported Databases**:
|
10
|
+
|
11
|
+
* SQLite
|
12
|
+
* MySQL
|
13
|
+
* PostgreSQL
|
14
|
+
* Oracle
|
15
|
+
|
16
|
+
# ✨ Features
|
17
|
+
|
18
|
+
* 🛠 Generates RESTful APIs from your database schema
|
19
|
+
* 🔌 Pluggable DB adapter system
|
20
|
+
* 🎛 CLI interface powered by [Thor](https://github.com/rails/thor)
|
21
|
+
* 🗃 Supports major relational DBs via corresponding gems
|
22
|
+
* 🔥 Runs on Puma + Rack
|
23
|
+
|
24
|
+
# 🚀 Installation
|
25
|
+
Add this line to your application's Gemfile:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
gem 'autorest'
|
29
|
+
```
|
30
|
+
|
31
|
+
And then execute:
|
32
|
+
|
33
|
+
```bash
|
34
|
+
$ bundle install
|
35
|
+
```
|
36
|
+
|
37
|
+
Or install it as a gem:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
$ gem install autorest
|
41
|
+
```
|
42
|
+
> Note: Depending on the DB you use, you may need to install additional gems manually:
|
43
|
+
> * `sqlite3`
|
44
|
+
> * `mysql2`
|
45
|
+
> * `pg`
|
46
|
+
> * `ruby-oci8`
|
47
|
+
|
48
|
+
# 🏃🏻♀️ Quickstart
|
49
|
+
To get your hand on AutoREST, run:
|
50
|
+
|
51
|
+
```bash
|
52
|
+
autorest boot sqlite://[path/to/sqlite.db]/[table_name]
|
53
|
+
```
|
54
|
+
|
55
|
+
If you want to try with MySQL/PostgreSQL/Oracle, run:
|
56
|
+
|
57
|
+
```bash
|
58
|
+
autorest boot mysql://[username]:[password]@[host]:[port]/[database]/[table_name]
|
59
|
+
```
|
60
|
+
|
61
|
+
for PostgreSQL (or) Oracle, use `pg://` (or) `orcl://` respectively instead of `mysql://`
|
62
|
+
|
63
|
+
Now you can access the server at `http://localhost:7914`
|
64
|
+
|
65
|
+
# 🖥 CLI usage
|
66
|
+
1. Via Interactive CLI
|
67
|
+
|
68
|
+
```bash
|
69
|
+
$ autorest new
|
70
|
+
```
|
71
|
+
|
72
|
+
2. Via YAML config file
|
73
|
+
|
74
|
+
```bash
|
75
|
+
$ autorest server <path/to/config>.yml
|
76
|
+
```
|
77
|
+
|
78
|
+
3. Via DSN
|
79
|
+
|
80
|
+
```bash
|
81
|
+
$ autorest boot mysql://[username]:[password]@[host]:[port]/[database]/[table_name]
|
82
|
+
```
|
83
|
+
|
84
|
+
# 📦 Configuration example
|
85
|
+
```yaml
|
86
|
+
db:
|
87
|
+
kind: mysql # sqlite, mysql, pg, orcl
|
88
|
+
host: localhost
|
89
|
+
port: 3306
|
90
|
+
user: root
|
91
|
+
passwd: secret
|
92
|
+
name: mydb # for sqlite: path/to/sqlite.db, for oracle: SID
|
93
|
+
tables: [users, posts]
|
94
|
+
|
95
|
+
server:
|
96
|
+
host: 127.0.0.1
|
97
|
+
port: 8080
|
98
|
+
```
|
99
|
+
|
100
|
+
# 🌐 API endpoints
|
101
|
+
Once the server is running, you can access the following RESTful API endpoints for the selected tables:
|
102
|
+
|
103
|
+
* `GET /<table>` - Returns all rows from table
|
104
|
+
* `GET /<table>/:id` - Returns a single row by ID (or any primary key)
|
105
|
+
* `POST /<table>` - Creates a new row in table
|
106
|
+
* `PUT /<table>/:id` - Updates an existing row by ID (or any primary key)
|
107
|
+
* `PATCH /<table>/:id` - Updates an existing row by ID (or any primary key)
|
108
|
+
* `DELETE /users/:id` - Deletes a user by ID
|
109
|
+
|
110
|
+
The `PATCH` method simply allows one to update a subset of the columns, whereas the `PUT` method allows one to update all columns.
|
111
|
+
|
112
|
+
# ✍🏻 Contributing
|
113
|
+
Contributions are welcome! While the basic functionality of this project works, there is a lot of room for improvement. If you have any suggestions or find any bugs, please [open an issue](https://github.com/harishtpj/AutoREST/issues/new/choose) or [create a pull request](https://github.com/harishtpj/AutoREST/pulls).
|
114
|
+
|
115
|
+
# 📝 License
|
116
|
+
|
117
|
+
#### Copyright © 2025 [M.V.Harish Kumar](https://github.com/harishtpj). <br>
|
118
|
+
|
119
|
+
#### This project is [MIT](https://github.com/harishtpj/AutoREST/blob/0341e153b1a8a1df139ff7225cb5f997818db89b/LICENSE) licensed.
|
data/bin/autorest
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
# Database Adapter class for AutoREST.
|
2
|
+
#
|
3
|
+
# This abstract class serves as a base class for specific database adapters such as
|
4
|
+
# SQLite, MySQL, PostgreSQL, and Oracle. It defines the common interface that all
|
5
|
+
# adapters must implement. These include methods for preparing the database (e.g.,
|
6
|
+
# fetching table and column metadata), executing SQL queries, and managing database
|
7
|
+
# connections.
|
8
|
+
#
|
9
|
+
# @abstract
|
10
|
+
class AutoREST::DBAdapter
|
11
|
+
|
12
|
+
# Initializes a new DBAdapter instance.
|
13
|
+
#
|
14
|
+
# @param db_kind [Symbol] The type of database (e.g., :sqlite, :mysql, :pg, :orcl)
|
15
|
+
# @param db_name [String] The database name or SID (for Oracle)
|
16
|
+
# @param db_conn [Object] The database connection object
|
17
|
+
def initialize(db_kind, db_name, db_conn)
|
18
|
+
@db_kind = db_kind
|
19
|
+
@dbname = db_name
|
20
|
+
@db_conn = db_conn
|
21
|
+
@tables = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# Prepares the database by fetching metadata, such as tables and columns.
|
25
|
+
# This method must be implemented by subclasses.
|
26
|
+
#
|
27
|
+
# @raise [NotImplementedError] If the method is not implemented by a subclass.
|
28
|
+
# @abstract
|
29
|
+
def prepare
|
30
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Executes a raw SQL query.
|
34
|
+
#
|
35
|
+
# @param sql [String] The SQL query to execute
|
36
|
+
# @return [Array<Hash>] The result of the query as an array of hashes
|
37
|
+
# @abstract
|
38
|
+
def exec_sql(sql)
|
39
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Escapes input data to safely use in SQL queries.
|
43
|
+
#
|
44
|
+
# @param input [String] The raw input data
|
45
|
+
# @return [String] The escaped input data
|
46
|
+
# @abstract
|
47
|
+
def escape(input)
|
48
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sets the access tables for the database. If no tables are specified, all tables are accessible.
|
52
|
+
#
|
53
|
+
# @param access_tab [Array<String>] A list of tables the user has access to
|
54
|
+
# @return [void]
|
55
|
+
def set_access_tables(access_tab)
|
56
|
+
@access_tables = access_tab.empty? ? @tables.keys : access_tab
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the list of table names in the database.
|
60
|
+
#
|
61
|
+
# @return [Array<String>] A list of table names
|
62
|
+
def tables
|
63
|
+
prepare if @tables.nil?
|
64
|
+
@tables.keys
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the list of column names for a given table.
|
68
|
+
#
|
69
|
+
# @param table_name [String] The name of the table
|
70
|
+
# @return [Array<String>] A list of column names
|
71
|
+
def columns(table_name)
|
72
|
+
prepare if @tables.nil?
|
73
|
+
@tables[table_name].keys
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the rows of a table for the specified columns.
|
77
|
+
#
|
78
|
+
# @param table_name [String] The name of the table
|
79
|
+
# @param cols [String, Array<String>] The columns to retrieve (defaults to "*")
|
80
|
+
# @return [Array<Hash>, String] The rows of the table as an array of hashes, or an error message
|
81
|
+
def rows(table_name, cols = "*")
|
82
|
+
prepare if @tables.nil?
|
83
|
+
return "404: Table #{table_name} does not exist" unless @tables.include?(table_name)
|
84
|
+
return "403: Insufficient rights to access Table #{table_name}" unless @access_tables.include?(table_name)
|
85
|
+
result = exec_sql("select #{escape(cols)} from #{escape(table_name)}")
|
86
|
+
return "404: Table #{table_name} is empty" if result.empty?
|
87
|
+
result
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns a specific row in a table identified by its primary key value.
|
91
|
+
#
|
92
|
+
# @param table_name [String] The name of the table
|
93
|
+
# @param value [String, Integer] The value of the primary key
|
94
|
+
# @param cols [String, Array<String>] The columns to retrieve (defaults to "*")
|
95
|
+
# @return [Hash, String] The row as a hash, or an error message
|
96
|
+
def row(table_name, value, cols = "*")
|
97
|
+
prepare if @tables.nil?
|
98
|
+
return "404: Table #{table_name} does not exist" unless @tables.include?(table_name)
|
99
|
+
return "403: Insufficient rights to access Table #{table_name}" unless @access_tables.include?(table_name)
|
100
|
+
return "502: Table does not have primary key" if pkey(table_name).nil?
|
101
|
+
result = exec_sql("select #{escape(cols)} from #{table_name} where #{pkey(table_name)} = #{value.inspect}")
|
102
|
+
return "404: Row not found" if result.empty?
|
103
|
+
result
|
104
|
+
end
|
105
|
+
|
106
|
+
# Inserts a new row into a table.
|
107
|
+
#
|
108
|
+
# @param table_name [String] The name of the table
|
109
|
+
# @param data [Hash] The data to insert, where keys are column names and values are column values
|
110
|
+
# @return [Hash, String] The inserted row as a hash, or an error message
|
111
|
+
def insert(table_name, data)
|
112
|
+
prepare if @tables.nil?
|
113
|
+
return "404: Table #{table_name} does not exist" unless @tables.include?(table_name)
|
114
|
+
return "403: Insufficient rights to access Table #{table_name}" unless @access_tables.include?(table_name)
|
115
|
+
return "409: Row already exists" if has_row(table_name, data[pkey(table_name)])
|
116
|
+
cols = data.keys.join(", ")
|
117
|
+
values = data.values.map(&:inspect).join(", ")
|
118
|
+
exec_sql("insert into #{escape(table_name)} (#{cols}) values (#{values})")
|
119
|
+
row(table_name, data[pkey(table_name)])
|
120
|
+
end
|
121
|
+
|
122
|
+
# Updates an existing row in a table.
|
123
|
+
#
|
124
|
+
# @param table_name [String] The name of the table
|
125
|
+
# @param pk [String, Integer] The primary key of the row to update
|
126
|
+
# @param value [Hash] The new values for the row, where keys are column names and values are column values
|
127
|
+
# @param patch [Boolean] If true, allows the partial changes, for PATCH requests (defaults to false)
|
128
|
+
# @return [Hash, String] The updated row as a hash, or an error message
|
129
|
+
def update(table_name, pk, value, patch = false)
|
130
|
+
prepare if @tables.nil?
|
131
|
+
return "404: Table #{table_name} does not exist" unless @tables.include?(table_name)
|
132
|
+
return "403: Insufficient rights to update Table #{table_name}" unless @access_tables.include?(table_name)
|
133
|
+
return "404: Row not found" unless has_row(table_name, pk)
|
134
|
+
return "422: Primary key mismatch" if (pk != value[pkey(table_name)].to_s && !patch)
|
135
|
+
return "422: Invalid data" if (value.keys & columns(table_name) != value.keys)
|
136
|
+
kvpairs = value.map { |k, v| "#{k} = #{v.inspect}" }.join(", ")
|
137
|
+
exec_sql("update #{escape(table_name)} set #{kvpairs} where #{pkey(table_name)} = #{pk.inspect}")
|
138
|
+
row(table_name, pk)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Deletes a row from a table.
|
142
|
+
#
|
143
|
+
# @param table_name [String] The name of the table
|
144
|
+
# @param value [String, Integer] The value of the primary key of the row to delete
|
145
|
+
# @return [Hash, String] The deleted row as a hash, or an error message
|
146
|
+
def del_row(table_name, value)
|
147
|
+
prepare if @tables.nil?
|
148
|
+
return "404: Table #{table_name} does not exist" unless @tables.include?(table_name)
|
149
|
+
return "403: Insufficient rights to delete Table #{table_name}" unless @access_tables.include?(table_name)
|
150
|
+
result = row(table_name, value)
|
151
|
+
return result if result.is_a?(String)
|
152
|
+
exec_sql("delete from #{escape(table_name)} where #{pkey(table_name)} = #{value.inspect}")
|
153
|
+
result
|
154
|
+
end
|
155
|
+
|
156
|
+
# Closes the database connection.
|
157
|
+
#
|
158
|
+
# @return [void]
|
159
|
+
def close
|
160
|
+
@db_conn.close
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
# Returns the primary key column name for a given table.
|
165
|
+
#
|
166
|
+
# @param table_name [String] The name of the table
|
167
|
+
# @return [String, nil] The primary key column name, or nil if no primary key exists
|
168
|
+
def pkey(table_name)
|
169
|
+
@tables[table_name].keys.find { |k| @tables[table_name][k][:pk] }
|
170
|
+
end
|
171
|
+
|
172
|
+
# Checks if a row exists in the table based on the primary key value.
|
173
|
+
#
|
174
|
+
# @param table_name [String] The name of the table
|
175
|
+
# @param value [String, Integer] The value of the primary key
|
176
|
+
# @return [Boolean] True if the row exists, false otherwise
|
177
|
+
def has_row(table_name, value)
|
178
|
+
!row(table_name, value).is_a?(String)
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# DB Adapter for MySQL database
|
2
|
+
begin
|
3
|
+
require "mysql2"
|
4
|
+
rescue LoadError
|
5
|
+
warn "Please install the 'mysql2' gem to use MySQL database."
|
6
|
+
end
|
7
|
+
|
8
|
+
require_relative "adapter"
|
9
|
+
|
10
|
+
# MySQL adapter for AutoREST.
|
11
|
+
#
|
12
|
+
# Uses the `mysql2` gem to connect and interact with a MySQL database.
|
13
|
+
# Automatically detects tables and primary key columns.
|
14
|
+
#
|
15
|
+
# @example Initialize adapter
|
16
|
+
# db = AutoREST::MySQLDB.new("localhost", 3306, "root", "password", "mydb")
|
17
|
+
#
|
18
|
+
class AutoREST::MySQLDB < AutoREST::DBAdapter
|
19
|
+
|
20
|
+
# @param host [String] Hostname of the MySQL server
|
21
|
+
# @param port [Integer] Port number
|
22
|
+
# @param user [String] Username
|
23
|
+
# @param passwd [String] Password
|
24
|
+
# @param dbname [String] Name of the MySQL database
|
25
|
+
def initialize(host, port, user, passwd, dbname)
|
26
|
+
conn = Mysql2::Client.new(host: host, port: port, username: user, password: passwd, database: dbname)
|
27
|
+
super(:mysql, dbname, conn)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Loads table metadata including columns and primary keys.
|
31
|
+
# @return [void]
|
32
|
+
def prepare
|
33
|
+
@tables = {}
|
34
|
+
@db_conn.query("show tables").each do |t|
|
35
|
+
tname = t["Tables_in_#{@dbname}"]
|
36
|
+
row_details = @db_conn.query("desc #{tname}")
|
37
|
+
@tables[tname] = {}
|
38
|
+
row_details.each do |row|
|
39
|
+
@tables[tname][row["Field"]] = {type: row["Type"], pk: row["Key"] == "PRI"}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Executes a raw SQL query.
|
45
|
+
# @param sql [String] The SQL query to run
|
46
|
+
# @return [Array<Hash>] Resulting rows
|
47
|
+
def exec_sql(sql)
|
48
|
+
@db_conn.query(sql).to_a
|
49
|
+
end
|
50
|
+
|
51
|
+
# Escapes identifiers or values for safe usage in queries.
|
52
|
+
# @param input [String] Table or column name
|
53
|
+
# @return [String] Escaped string
|
54
|
+
def escape(input)
|
55
|
+
@db_conn.escape(input)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# DB Adapter for Oracle database
|
2
|
+
begin
|
3
|
+
ENV['NLS_LANG'] ||= 'AMERICAN_AMERICA.US7ASCII'
|
4
|
+
require "oci8"
|
5
|
+
rescue LoadError
|
6
|
+
warn "Please install the 'ruby-oci8' gem to use Oracle database."
|
7
|
+
end
|
8
|
+
|
9
|
+
require_relative "adapter"
|
10
|
+
|
11
|
+
# Oracle DB adapter for AutoREST.
|
12
|
+
#
|
13
|
+
# Uses the `oci8` gem to connect and interact with an Oracle database.
|
14
|
+
# Retrieves tables and primary key details from Oracle's user-owned tables and constraints.
|
15
|
+
#
|
16
|
+
# @example Initialize adapter
|
17
|
+
# db = AutoREST::OracleDB.new("localhost", 1521, "sys", "secret", "ORCL")
|
18
|
+
#
|
19
|
+
class AutoREST::OracleDB < AutoREST::DBAdapter
|
20
|
+
|
21
|
+
# @param host [String] Hostname of the Oracle server
|
22
|
+
# @param port [Integer] Port number
|
23
|
+
# @param user [String] Username
|
24
|
+
# @param passwd [String] Password
|
25
|
+
# @param sid [String] Oracle SID (System Identifier)
|
26
|
+
def initialize(host, port, user, passwd, sid)
|
27
|
+
conn = OCI8.new(user, passwd, "//#{host}:#{port}/#{sid}")
|
28
|
+
conn.autocommit = true
|
29
|
+
super(:orcl, sid, conn)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Loads table metadata including columns and primary keys.
|
33
|
+
#
|
34
|
+
# Queries Oracle's `user_tab_columns` and `user_cons_columns` system views to get
|
35
|
+
# the column details and primary key information.
|
36
|
+
#
|
37
|
+
# @return [void]
|
38
|
+
def prepare
|
39
|
+
desc_query = <<~SQL
|
40
|
+
SELECT c.column_name,
|
41
|
+
c.data_type,
|
42
|
+
CASE WHEN pk.pk_column IS NOT NULL THEN 'YES' ELSE 'NO' END AS primary_key
|
43
|
+
FROM user_tab_columns c
|
44
|
+
LEFT JOIN (
|
45
|
+
SELECT ucc.column_name AS pk_column
|
46
|
+
FROM user_cons_columns ucc
|
47
|
+
JOIN user_constraints uc
|
48
|
+
ON ucc.constraint_name = uc.constraint_name
|
49
|
+
WHERE uc.constraint_type = 'P'
|
50
|
+
AND uc.table_name = :1
|
51
|
+
) pk ON c.column_name = pk.pk_column
|
52
|
+
WHERE c.table_name = :1
|
53
|
+
SQL
|
54
|
+
|
55
|
+
@tables = {}
|
56
|
+
@db_conn.exec("select * from cat") do |t|
|
57
|
+
tname = t[0]
|
58
|
+
@tables[tname] = {}
|
59
|
+
@db_conn.exec(desc_query, tname) do |row|
|
60
|
+
@tables[tname][row[0]] = {type: row[1], pk: row[2] == "YES"}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Executes a raw SQL query.
|
66
|
+
# @param sql [String] The SQL query to run
|
67
|
+
# @return [Array<Hash>] Resulting rows
|
68
|
+
def exec_sql(sql)
|
69
|
+
cursor = @db_conn.exec(sql)
|
70
|
+
cols = cursor.get_col_names
|
71
|
+
res = []
|
72
|
+
while row = cursor.fetch
|
73
|
+
res << Hash[cols.zip(row)]
|
74
|
+
end
|
75
|
+
res
|
76
|
+
end
|
77
|
+
|
78
|
+
# Closes the database connection.
|
79
|
+
# @return [void]
|
80
|
+
def close
|
81
|
+
@db_conn.logoff
|
82
|
+
end
|
83
|
+
|
84
|
+
# Escapes a string input to safely use in SQL queries.
|
85
|
+
# @param input [String] Raw user input
|
86
|
+
# @return [String] Escaped string
|
87
|
+
def escape(input)
|
88
|
+
input.to_s.gsub("'", "''")
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# DB Adapter for PostgreSQL database
|
2
|
+
begin
|
3
|
+
require "pg"
|
4
|
+
rescue LoadError
|
5
|
+
warn "Please install the 'pg' gem to use PostgreSQL database."
|
6
|
+
end
|
7
|
+
|
8
|
+
require_relative "adapter"
|
9
|
+
|
10
|
+
# PostgreSQL adapter for AutoREST.
|
11
|
+
#
|
12
|
+
# Uses the `pg` gem to connect and interact with a PostgreSQL database.
|
13
|
+
# Detects tables and their primary keys by querying PostgreSQL system catalogs.
|
14
|
+
#
|
15
|
+
# @example Initialize adapter
|
16
|
+
# db = AutoREST::PostgresDB.new("localhost", 5432, "postgres", "secret", "mydb")
|
17
|
+
#
|
18
|
+
class AutoREST::PostgresDB < AutoREST::DBAdapter
|
19
|
+
|
20
|
+
# @param host [String] Hostname of the PostgreSQL server
|
21
|
+
# @param port [Integer] Port number
|
22
|
+
# @param user [String] Username
|
23
|
+
# @param passwd [String] Password
|
24
|
+
# @param dbname [String] Name of the PostgreSQL database
|
25
|
+
def initialize(host, port, user, passwd, dbname)
|
26
|
+
conn = PG.connect(host: host, port: port, user: user, password: passwd, dbname: dbname)
|
27
|
+
super(:pg, dbname, conn)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Loads table metadata including columns and primary keys.
|
31
|
+
#
|
32
|
+
# It excludes system tables by filtering out `pg_catalog` and `information_schema` schemas.
|
33
|
+
#
|
34
|
+
# @return [void]
|
35
|
+
def prepare
|
36
|
+
desc_query = <<-SQL
|
37
|
+
SELECT
|
38
|
+
a.attname AS cname,
|
39
|
+
pg_catalog.format_type(a.atttypid, a.atttypmod) AS dtype,
|
40
|
+
coalesce(i.indisprimary, false) AS pk
|
41
|
+
FROM
|
42
|
+
pg_catalog.pg_attribute a
|
43
|
+
JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
|
44
|
+
JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
|
45
|
+
LEFT JOIN pg_catalog.pg_index i
|
46
|
+
ON c.oid = i.indrelid
|
47
|
+
AND a.attnum = ANY(i.indkey)
|
48
|
+
AND i.indisprimary
|
49
|
+
WHERE
|
50
|
+
c.relname = $1
|
51
|
+
AND a.attnum > 0
|
52
|
+
AND NOT a.attisdropped
|
53
|
+
ORDER BY a.attnum;
|
54
|
+
SQL
|
55
|
+
@tables = {}
|
56
|
+
@db_conn.exec("SELECT tablename FROM pg_catalog.pg_tables
|
57
|
+
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')").each do |t|
|
58
|
+
tname = t["tablename"]
|
59
|
+
row_details = @db_conn.exec_params(desc_query, [tname])
|
60
|
+
@tables[tname] = {}
|
61
|
+
row_details.each do |row|
|
62
|
+
@tables[tname][row["cname"]] = {type: row["type"], pk: row["pk"]}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Executes a raw SQL query.
|
68
|
+
# @param sql [String] The SQL query to run
|
69
|
+
# @return [Array<Hash>] Resulting rows
|
70
|
+
def exec_sql(sql)
|
71
|
+
@db_conn.exec(sql).to_a
|
72
|
+
end
|
73
|
+
|
74
|
+
# Escapes a string input to safely use in SQL queries.
|
75
|
+
# @param input [String] Raw user input
|
76
|
+
# @return [String] Escaped string
|
77
|
+
def escape(input)
|
78
|
+
@db_conn.escape_string(input)
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# DB Adapter for SQLite database
|
2
|
+
begin
|
3
|
+
require "sqlite3"
|
4
|
+
rescue LoadError
|
5
|
+
warn "Please install the 'sqlite3' gem to use SQLite database."
|
6
|
+
end
|
7
|
+
|
8
|
+
require_relative "adapter"
|
9
|
+
|
10
|
+
# SQLite adapter for AutoREST.
|
11
|
+
#
|
12
|
+
# Uses the `sqlite3` gem to connect and query the SQLite database.
|
13
|
+
# Automatically discovers tables and primary keys.
|
14
|
+
#
|
15
|
+
# @example Initialize adapter
|
16
|
+
# db = AutoREST::SQLiteDB.new("data.db")
|
17
|
+
#
|
18
|
+
class AutoREST::SQLiteDB < AutoREST::DBAdapter
|
19
|
+
|
20
|
+
# @param dbname [String] Path to the SQLite database file
|
21
|
+
def initialize(dbname)
|
22
|
+
conn = SQLite3::Database.new(dbname)
|
23
|
+
conn.results_as_hash = true
|
24
|
+
super(:sqlite, dbname, conn)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Loads table metadata including columns and primary keys.
|
28
|
+
# @return [void]
|
29
|
+
def prepare
|
30
|
+
@tables = {}
|
31
|
+
@db_conn.execute("SELECT name FROM sqlite_master WHERE type='table'").each do |t|
|
32
|
+
tname = t['name']
|
33
|
+
row_details = @db_conn.execute("select name, type, pk from pragma_table_info('#{tname}')")
|
34
|
+
@tables[tname] = {}
|
35
|
+
row_details.each do |row|
|
36
|
+
@tables[tname][row['name']] = {type: row['type'], pk: row['pk'] == 1}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Executes a raw SQL query.
|
42
|
+
# @param sql [String] The SQL query to run
|
43
|
+
# @return [Array<Hash>] Resulting rows
|
44
|
+
def exec_sql(sql)
|
45
|
+
@db_conn.execute(sql)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Escapes identifiers or values for safe usage in queries.
|
49
|
+
# @param input [String] Table or column name
|
50
|
+
# @return [String] Escaped string
|
51
|
+
def escape(input)
|
52
|
+
SQLite3::Database.quote(input)
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# The main server instance for AutoREST
|
2
|
+
require "sinatra/base"
|
3
|
+
|
4
|
+
# The main server instance for AutoREST.
|
5
|
+
#
|
6
|
+
# This class sets up a Sinatra web server that acts as the interface for
|
7
|
+
# interacting with the AutoREST API. It handles requests related to various
|
8
|
+
# database operations such as querying, inserting, updating, and deleting rows.
|
9
|
+
#
|
10
|
+
# @note This server is designed to work with different database adapters like
|
11
|
+
# SQLite, MySQL, PostgreSQL, and Oracle, as provided by the AutoREST framework.
|
12
|
+
#
|
13
|
+
# @example Starting the server
|
14
|
+
# AutoREST::Server.new(db_conn).run!
|
15
|
+
#
|
16
|
+
# @see AutoREST::DBAdapter
|
17
|
+
class AutoREST::Server < Sinatra::Base
|
18
|
+
# Initializes a new AutoREST::Server instance.
|
19
|
+
#
|
20
|
+
# @param db_conn [AutoREST::DBAdapter] The database connection object to interact with
|
21
|
+
def initialize(db_conn)
|
22
|
+
super()
|
23
|
+
@db_conn = db_conn
|
24
|
+
end
|
25
|
+
|
26
|
+
before do
|
27
|
+
content_type :json
|
28
|
+
end
|
29
|
+
|
30
|
+
helpers do
|
31
|
+
# Helper method to return a formatted error response.
|
32
|
+
#
|
33
|
+
# @param msg [String] The error message to return
|
34
|
+
# @param status [Integer] The HTTP status code to return (default is 400)
|
35
|
+
# @return [String] The JSON-formatted error response
|
36
|
+
def error(msg, status = 400)
|
37
|
+
halt status, { error: msg }.to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
# Helper method to parse the body of the incoming request as JSON.
|
41
|
+
#
|
42
|
+
# @param req [Sinatra::Request] The incoming request object
|
43
|
+
# @return [Hash] The parsed JSON body
|
44
|
+
def get_body(req)
|
45
|
+
req.body.rewind
|
46
|
+
JSON.parse(req.body.read)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
get '/' do
|
51
|
+
{ message: "Welcome to AutoREST API Server" }.to_json
|
52
|
+
end
|
53
|
+
|
54
|
+
get '/:table/?' do |tname|
|
55
|
+
cols = params["only"] || "*"
|
56
|
+
q = @db_conn.rows(tname, cols)
|
57
|
+
if q.is_a?(String)
|
58
|
+
code, msg = q.split(": ", 2)
|
59
|
+
error(msg, code.to_i)
|
60
|
+
end
|
61
|
+
q.to_json
|
62
|
+
end
|
63
|
+
|
64
|
+
post '/:table/?' do |tname|
|
65
|
+
data = get_body(request)
|
66
|
+
error("Incomplete request body") if data.empty?
|
67
|
+
q = @db_conn.insert(tname, data)
|
68
|
+
if q.is_a?(String)
|
69
|
+
code, msg = q.split(": ", 2)
|
70
|
+
error(msg, code.to_i)
|
71
|
+
end
|
72
|
+
q.to_json
|
73
|
+
end
|
74
|
+
|
75
|
+
get '/:table/:pk/?' do |tname, pk|
|
76
|
+
cols = params["only"] || "*"
|
77
|
+
pk = pk.match?(/\A-?\d+(\.\d+)?\z/) ? pk.to_i : pk
|
78
|
+
q = @db_conn.row(tname, pk, cols)
|
79
|
+
if q.is_a?(String)
|
80
|
+
code, msg = q.split(": ", 2)
|
81
|
+
error(msg, code.to_i)
|
82
|
+
end
|
83
|
+
q.to_json
|
84
|
+
end
|
85
|
+
|
86
|
+
put '/:table/:pk/?' do |tname, pk|
|
87
|
+
data = get_body(request)
|
88
|
+
error("Incomplete request body") if (data.empty? || data.keys != @db_conn.columns(tname))
|
89
|
+
pk = pk.match?(/\A-?\d+(\.\d+)?\z/) ? pk.to_i : pk
|
90
|
+
q = @db_conn.update(tname, pk, data)
|
91
|
+
if q.is_a?(String)
|
92
|
+
code, msg = q.split(": ", 2)
|
93
|
+
error(msg, code.to_i)
|
94
|
+
end
|
95
|
+
q.to_json
|
96
|
+
end
|
97
|
+
|
98
|
+
patch '/:table/:pk/?' do |tname, pk|
|
99
|
+
data = get_body(request)
|
100
|
+
error("Incomplete request body") if data.empty?
|
101
|
+
pk = pk.match?(/\A-?\d+(\.\d+)?\z/) ? pk.to_i : pk
|
102
|
+
q = @db_conn.update(tname, pk, data, true)
|
103
|
+
if q.is_a?(String)
|
104
|
+
code, msg = q.split(": ", 2)
|
105
|
+
error(msg, code.to_i)
|
106
|
+
end
|
107
|
+
q.to_json
|
108
|
+
end
|
109
|
+
|
110
|
+
delete '/:table/:pk/?' do |tname, pk|
|
111
|
+
pk = pk.match?(/\A-?\d+(\.\d+)?\z/) ? pk.to_i : pk
|
112
|
+
q = @db_conn.del_row(tname, pk)
|
113
|
+
if q.is_a?(String)
|
114
|
+
code, msg = q.split(": ", 2)
|
115
|
+
error(msg, code.to_i)
|
116
|
+
end
|
117
|
+
q.to_json
|
118
|
+
end
|
119
|
+
|
120
|
+
error 500 do
|
121
|
+
{ error: "Internal Server Error" }.to_json
|
122
|
+
end
|
123
|
+
end
|
data/lib/autorest.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
# The main executable for AutoREST
|
2
|
+
#
|
3
|
+
# This file defines the command-line interface (CLI) for the AutoREST gem using Thor.
|
4
|
+
# The CLI allows users to generate a new AutoREST API server, start the server using
|
5
|
+
# a configuration file or DSN, and view the current version of the API.
|
6
|
+
#
|
7
|
+
# Commands:
|
8
|
+
# - `version`: Prints the version of AutoREST.
|
9
|
+
# - `new`: Creates a new AutoREST API server project.
|
10
|
+
# - `server FILE`: Starts the AutoREST API server using a configuration file.
|
11
|
+
# - `boot DSN`: Starts the AutoREST API server using a DSN.
|
12
|
+
#
|
13
|
+
# The commands involve setting up a database connection and starting a server with
|
14
|
+
# specified parameters such as the host, port, and database tables.
|
15
|
+
|
16
|
+
require "thor"
|
17
|
+
require "tty-prompt"
|
18
|
+
require "rack"
|
19
|
+
require "rack/handler/puma"
|
20
|
+
require "yaml"
|
21
|
+
require "uri"
|
22
|
+
|
23
|
+
require_relative "autorest/version"
|
24
|
+
require_relative "autorest/server"
|
25
|
+
|
26
|
+
class AutoREST::CLI < Thor
|
27
|
+
|
28
|
+
# Determines if the program should exit on failure
|
29
|
+
def self.exit_on_failure?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
map "-v" => "version"
|
34
|
+
desc "version", "Prints the version of AutoREST"
|
35
|
+
# Prints the version of AutoREST
|
36
|
+
def version
|
37
|
+
puts "AutoREST v#{AutoREST::VERSION}"
|
38
|
+
end
|
39
|
+
|
40
|
+
map "-n" => "new"
|
41
|
+
desc "new", "Creates a new AutoREST API server"
|
42
|
+
# Creates a new AutoREST API server project
|
43
|
+
# Prompts the user for database details and creates a configuration file.
|
44
|
+
def new
|
45
|
+
prompt = TTY::Prompt.new
|
46
|
+
opts = {db: {}, server: { host: "localhost", port: 7914 } }
|
47
|
+
puts "Welcome to AutoREST API Server Generator"
|
48
|
+
project_name = prompt.ask("Enter the project's name:")
|
49
|
+
opts[:db][:kind] = prompt.select("Select your database:",
|
50
|
+
{"SQLite" => :sqlite, "MySQL" => :mysql, "PostgreSQL" => :pg, "Oracle" => :orcl},
|
51
|
+
default: "SQLite")
|
52
|
+
|
53
|
+
if opts[:db][:kind] == :sqlite
|
54
|
+
require_relative "autorest/db/sqlite"
|
55
|
+
opts[:db][:name] = prompt.ask("Enter location of DB file:")
|
56
|
+
db = AutoREST::SQLiteDB.new(opts[:db][:name])
|
57
|
+
else
|
58
|
+
def_port = {mysql: 3306, pg: 5432, orcl: 1521}
|
59
|
+
def_usr = {mysql: "root", pg: "postgres", orcl: "SYS"}
|
60
|
+
opts[:db][:host] = prompt.ask("Enter hostname of DB:", default: "localhost")
|
61
|
+
opts[:db][:port] = prompt.ask("Enter port of DB:", default: def_port[opts[:db][:kind]])
|
62
|
+
opts[:db][:user] = prompt.ask("Enter username:", default: def_usr[opts[:db][:kind]])
|
63
|
+
opts[:db][:passwd] = prompt.ask("Enter password:", echo: false)
|
64
|
+
opts[:db][:name] = prompt.ask("Enter database #{opts[:db][:kind] == :orcl ? "SID" : "name"}:")
|
65
|
+
case opts[:db][:kind]
|
66
|
+
when :mysql
|
67
|
+
require_relative "autorest/db/mysql"
|
68
|
+
db = AutoREST::MySQLDB.new(opts[:db][:host], opts[:db][:port], opts[:db][:user], opts[:db][:passwd], opts[:db][:name])
|
69
|
+
when :pg
|
70
|
+
require_relative "autorest/db/postgres"
|
71
|
+
db = AutoREST::PostgresDB.new(opts[:db][:host], opts[:db][:port], opts[:db][:user], opts[:db][:passwd], opts[:db][:name])
|
72
|
+
when :orcl
|
73
|
+
require_relative "autorest/db/oracle"
|
74
|
+
db = AutoREST::OracleDB.new(opts[:db][:host], opts[:db][:port], opts[:db][:user], opts[:db][:passwd], opts[:db][:name])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
opts[:db][:tables] = prompt.multi_select("Select tables from database:", db.tables)
|
79
|
+
db.set_access_tables(opts[:db][:tables])
|
80
|
+
puts "Creating configuration file..."
|
81
|
+
File.open("#{project_name}.yml", "w") do |f|
|
82
|
+
f.write(opts.to_yaml)
|
83
|
+
end
|
84
|
+
puts "Successfully completed!"
|
85
|
+
start_server(db)
|
86
|
+
end
|
87
|
+
|
88
|
+
map "-S" => "server"
|
89
|
+
desc "server FILE", "Starts the AutoREST API server using a config file"
|
90
|
+
# Starts the AutoREST API server using a configuration file
|
91
|
+
# Loads the configuration file and starts the server with the specified database settings.
|
92
|
+
def server(file)
|
93
|
+
opts = YAML.load_file(file)
|
94
|
+
case opts[:db][:kind]
|
95
|
+
when :sqlite
|
96
|
+
require_relative "autorest/db/sqlite"
|
97
|
+
db = AutoREST::SQLiteDB.new(opts[:db][:name])
|
98
|
+
when :mysql
|
99
|
+
require_relative "autorest/db/mysql"
|
100
|
+
db = AutoREST::MySQLDB.new(opts[:db][:host], opts[:db][:port], opts[:db][:user], opts[:db][:passwd], opts[:db][:name])
|
101
|
+
when :pg
|
102
|
+
require_relative "autorest/db/postgres"
|
103
|
+
db = AutoREST::PostgresDB.new(opts[:db][:host], opts[:db][:port], opts[:db][:user], opts[:db][:passwd], opts[:db][:name])
|
104
|
+
when :orcl
|
105
|
+
require_relative "autorest/db/oracle"
|
106
|
+
db = AutoREST::OracleDB.new(opts[:db][:host], opts[:db][:port], opts[:db][:user], opts[:db][:passwd], opts[:db][:name])
|
107
|
+
end
|
108
|
+
db.prepare
|
109
|
+
db.set_access_tables(opts[:db][:tables])
|
110
|
+
servinfo = opts.fetch(:server, { host: "localhost", port: 7914})
|
111
|
+
start_server(db, servinfo[:host], servinfo[:port])
|
112
|
+
end
|
113
|
+
|
114
|
+
map "-s" => "boot"
|
115
|
+
desc "boot DSN", "Starts the AutoREST API server using a DSN"
|
116
|
+
# Starts the AutoREST API server using a DSN (Data Source Name)
|
117
|
+
# Parses the DSN and starts the server using the corresponding database.
|
118
|
+
def boot(dsn)
|
119
|
+
uri = URI.parse(dsn)
|
120
|
+
if uri.scheme == "sqlite"
|
121
|
+
require_relative "autorest/db/sqlite"
|
122
|
+
db = AutoREST::SQLiteDB.new(uri.host)
|
123
|
+
table, *_ = uri.path.sub(/^\//, '').split('/')
|
124
|
+
else
|
125
|
+
database, table = uri.path.sub(/^\//, '').split('/')
|
126
|
+
passwd = URI.decode_www_form_component(uri.password)
|
127
|
+
case uri.scheme
|
128
|
+
when "mysql"
|
129
|
+
require_relative "autorest/db/mysql"
|
130
|
+
db = AutoREST::MySQLDB.new(uri.host, uri.port, uri.user, passwd, database)
|
131
|
+
when "pg"
|
132
|
+
require_relative "autorest/db/postgres"
|
133
|
+
db = AutoREST::PostgresDB.new(uri.host, uri.port, uri.user, passwd, database)
|
134
|
+
when "orcl"
|
135
|
+
require_relative "autorest/db/oracle"
|
136
|
+
db = AutoREST::OracleDB.new(uri.host, uri.port, uri.user, passwd, database)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
db.prepare
|
140
|
+
db.set_access_tables([table])
|
141
|
+
start_server(db)
|
142
|
+
end
|
143
|
+
|
144
|
+
no_commands do
|
145
|
+
# Starts the AutoREST server
|
146
|
+
def start_server(db, host = "localhost", port = 7914)
|
147
|
+
server = AutoREST::Server.new(db)
|
148
|
+
puts "Starting server..."
|
149
|
+
Rack::Handler::Puma.run(server, Host: host, Port: port)
|
150
|
+
db.close
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: autorest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- M.V. Harish Kumar
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-05-22 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: sinatra
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: thor
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: tty-prompt
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: puma
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: yaml
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
description: AutoREST is a lightweight CLI tool that turns your SQL database into
|
83
|
+
a fully working RESTful API server using Puma and Rack. Supports SQLite, MySQL,
|
84
|
+
PostgreSQL, and Oracle.
|
85
|
+
email:
|
86
|
+
- harishtpj@outlook.com
|
87
|
+
executables:
|
88
|
+
- autorest
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- LICENSE
|
93
|
+
- README.md
|
94
|
+
- bin/autorest
|
95
|
+
- lib/autorest.rb
|
96
|
+
- lib/autorest/db/adapter.rb
|
97
|
+
- lib/autorest/db/mysql.rb
|
98
|
+
- lib/autorest/db/oracle.rb
|
99
|
+
- lib/autorest/db/postgres.rb
|
100
|
+
- lib/autorest/db/sqlite.rb
|
101
|
+
- lib/autorest/server.rb
|
102
|
+
- lib/autorest/version.rb
|
103
|
+
homepage: https://github.com/harishtpj/AutoREST
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata: {}
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubygems_version: 3.6.2
|
122
|
+
specification_version: 4
|
123
|
+
summary: Tool to generate RESTful APIs
|
124
|
+
test_files: []
|