railspp 0.0.3
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/README.md +168 -0
- data/bin/railspp +9 -0
- data/lib/commands/initialize.rb +67 -0
- data/lib/commands/make_test.rb +12 -0
- data/lib/commands/model.rb +36 -0
- data/lib/help/documentation.rb +30 -0
- data/lib/help/initialize.rb +21 -0
- data/lib/help/make_test.rb +13 -0
- data/lib/help/model.rb +30 -0
- data/lib/railspp.rb +78 -0
- data/lib/templates/api_documentation_initializer.txt +4 -0
- data/lib/templates/api_documentation_service.txt +206 -0
- data/lib/templates/controller.txt +2 -0
- data/lib/templates/documentation.index.erb.txt +129 -0
- data/lib/templates/documentation.layout.erb.txt +15 -0
- data/lib/templates/documentation_controller.txt +52 -0
- data/lib/templates/exception_handler.txt +19 -0
- data/lib/templates/global_controller.txt +190 -0
- data/lib/templates/rack_cors_initializer.txt +6 -0
- data/lib/templates/response.txt +7 -0
- data/lib/templates/routes_documentation.txt +10 -0
- data/lib/templates/routes_namespace.txt +10 -0
- data/lib/utils/strings.rb +61 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 12d4c74ed0ac4e2d6342bcdd8319d96e078c162a76aa422de2fab510e1bce423
|
4
|
+
data.tar.gz: c121223b43c6c248fa8066d8311a40526979729282f546764bbb36f51d9d03cd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a99075325a3cbeaa265eca334eb94b7827c572b129d5f1e49de13034535e5af35f450f795b0369a31924dfa743d7e343eaa1de093dae8706db44ae5f068fe5e3
|
7
|
+
data.tar.gz: beb144dee9e25d7876ca01f66a9e966f304b27de13f1de50f935d44c51ca3ab04af085983d8c19f284b000479f9652a1ad90ace189c8dd7ed89f86a93d9f2b81
|
data/README.md
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
# Rails++
|
2
|
+
|
3
|
+
Autogenerate your CRUD operations with Swagger like API documentation
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
Rails version >= 5
|
8
|
+
Ruby version >= 2.5
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
In your gem file
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'railspp'
|
16
|
+
```
|
17
|
+
|
18
|
+
In your terminal
|
19
|
+
|
20
|
+
```bash
|
21
|
+
gem isntall railspp
|
22
|
+
```
|
23
|
+
|
24
|
+
## CLI Commands
|
25
|
+
|
26
|
+
Your command CLI command bin path is `railspp`
|
27
|
+
|
28
|
+
<!--  -->
|
29
|
+
|
30
|
+
## Getting Started
|
31
|
+
|
32
|
+
For existing project use Git and keep all the
|
33
|
+
changes from the initialize method while keeping your changes as well.
|
34
|
+
|
35
|
+
Example:
|
36
|
+
Create a new project and then initialize.
|
37
|
+
|
38
|
+
```bash
|
39
|
+
rails new (project-name)
|
40
|
+
cd (project-name)
|
41
|
+
# Install dependency in gem file and globally
|
42
|
+
railspp init
|
43
|
+
```
|
44
|
+
|
45
|
+
Run the initialize command to get started:
|
46
|
+
|
47
|
+
```bash
|
48
|
+
railspp init
|
49
|
+
```
|
50
|
+
|
51
|
+
Run the model command to generate a migration, model, and completed controller:
|
52
|
+
|
53
|
+
This controller is overwritable when you declare the same method name in it's controller class.
|
54
|
+
|
55
|
+
```bash
|
56
|
+
railspp model (model-name)
|
57
|
+
```
|
58
|
+
|
59
|
+
Update your migration and model file. Then declare your routes with `resources`.
|
60
|
+
The controller has all api resources completed by default. These will fail if your migration and
|
61
|
+
model are not completed. And your routes are not defined.
|
62
|
+
|
63
|
+
Steps to do after:
|
64
|
+
|
65
|
+
- Update model file
|
66
|
+
- Update migration file
|
67
|
+
- Define your routes with resources
|
68
|
+
|
69
|
+
Run the make_test command to make unit tests:
|
70
|
+
|
71
|
+
```bash
|
72
|
+
railspp make_test (resource-route-name)
|
73
|
+
```
|
74
|
+
|
75
|
+
After your tests are completed. Update your update and create request bodies
|
76
|
+
based on the controller's `params.permit`. Add the keys available to your request body.
|
77
|
+
If you have special headers like "Authorization" add those to the public
|
78
|
+
variable `@headers` in that file.
|
79
|
+
|
80
|
+
Steps to do after:
|
81
|
+
|
82
|
+
- Update request body based on the controller's permitted keys
|
83
|
+
- Update your headers for all the requests
|
84
|
+
|
85
|
+
```bash
|
86
|
+
|
87
|
+
```
|
88
|
+
|
89
|
+
## API Documentation
|
90
|
+
|
91
|
+
There are Swagger-like API Documentation that requires no configuration.
|
92
|
+
This was created on your initialize command. This docs with regenerate the javascript
|
93
|
+
on API route changes.
|
94
|
+
|
95
|
+
The API documentation lives on your web route '/documentation'
|
96
|
+
Run the server and listen to url:
|
97
|
+
|
98
|
+
`http://localhost:3000/documentation`
|
99
|
+
|
100
|
+
For more information on the API docs check out the details here: [Autogenerated API Documentation Docs](./docs/API_DOCUMENTATION.md)
|
101
|
+
|
102
|
+
## Gloabl Controller
|
103
|
+
|
104
|
+
All of your CRUD operations were created in the GlobalController.
|
105
|
+
The GlobalController gets your model based on the class name of the controller.
|
106
|
+
Do not change the class name of the generated controller.
|
107
|
+
|
108
|
+
All methods are overwritable but have the functionality complete by default.
|
109
|
+
Restrict the routes you want accessible through the apiResource declaration.
|
110
|
+
Update the tests for ignoring the routes that do not exist.
|
111
|
+
|
112
|
+
By Default these methods are created:
|
113
|
+
|
114
|
+
- index
|
115
|
+
- show
|
116
|
+
- store
|
117
|
+
- update
|
118
|
+
- destroy
|
119
|
+
|
120
|
+
There are many querystrings available in the index method.
|
121
|
+
These contain options like:
|
122
|
+
|
123
|
+
- include=(associated model names comma separated)
|
124
|
+
- where=(key:value rows comma separated and key value pairs are semi-colon separated)
|
125
|
+
- order=(key:(DESC||ASC) rows comma separated and key DESC/ASC pairs semi-colon separated)
|
126
|
+
- limit=(amount returned the default is 25)
|
127
|
+
- offset=(number_to_skip in rows)
|
128
|
+
- page=(which page the default is 1)
|
129
|
+
|
130
|
+
For more information about the Global Controller check out the details here: [Global Controller Docs](./docs/GLOBAL_CONTROLLER.md)
|
131
|
+
|
132
|
+
## Unit tests
|
133
|
+
|
134
|
+
By default the tests check the format of the response and the status code.
|
135
|
+
You must provide request params for your store method and update method based
|
136
|
+
on the params options in your model. They are at the top of the file with TODOS.
|
137
|
+
There are tests for the querystrings. That you can update. The include
|
138
|
+
querystring test must be updated based on your schema.
|
139
|
+
|
140
|
+
The tests cover all querystrings with the same successful data format as default index currently.
|
141
|
+
|
142
|
+
Test cover:
|
143
|
+
|
144
|
+
Index:
|
145
|
+
|
146
|
+
- status_code = 200
|
147
|
+
- json_format = { data: (array), count: (integer) }
|
148
|
+
|
149
|
+
Show:
|
150
|
+
|
151
|
+
- status_code = 200
|
152
|
+
- json_format = { id: (integer) }
|
153
|
+
|
154
|
+
Create:
|
155
|
+
|
156
|
+
- status_code = 201
|
157
|
+
- json_format = { id: (integer) }
|
158
|
+
|
159
|
+
Update:
|
160
|
+
|
161
|
+
- status_code = 200
|
162
|
+
- json_format = { id: (integer) }
|
163
|
+
|
164
|
+
Destroy:
|
165
|
+
|
166
|
+
- status_code = 204
|
167
|
+
|
168
|
+
For more information about the Unit Tests check out the details here: [Generated Unit Test Docs](./docs/RAILSUNIT.md)
|
data/bin/railspp
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative '../utils/strings.rb'
|
2
|
+
|
3
|
+
|
4
|
+
class InitializeCommand < MoreUtils
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def run *args
|
8
|
+
lookup = flag_lookup(args)
|
9
|
+
|
10
|
+
# Add Initializers
|
11
|
+
cors_template = get_file_str("#{this_dir}/../templates/rack_cors_initializer.txt")
|
12
|
+
write_file("#{root}/config/initializers/rack_cors.rb", cors_template)
|
13
|
+
|
14
|
+
adi_template = get_file_str("#{this_dir}/../templates/api_documentation_initializer.txt")
|
15
|
+
write_file("#{root}/config/initializers/api_documentation_js.rb", adi_template)
|
16
|
+
|
17
|
+
# Add Controllers
|
18
|
+
dc_template = get_file_str("#{this_dir}/../templates/documentation_controller.txt")
|
19
|
+
write_file("#{root}/app/controllers/documentation_controller.rb", dc_template)
|
20
|
+
|
21
|
+
gc_template = get_file_str("#{this_dir}/../templates/global_controller.txt")
|
22
|
+
write_file("#{root}/app/controllers/global_controller.rb", gc_template)
|
23
|
+
|
24
|
+
# Add Concerns
|
25
|
+
ehc_template = get_file_str("#{this_dir}/../templates/exception_handler.txt")
|
26
|
+
write_file("#{root}/app/controllers/concerns/exception_handler.rb", ehc_template)
|
27
|
+
|
28
|
+
resp_template = get_file_str("#{this_dir}/../templates/response.txt")
|
29
|
+
write_file("#{root}/app/controllers/concerns/response.rb", resp_template)
|
30
|
+
|
31
|
+
# Add Service
|
32
|
+
system("mkdir -p #{root}/app/services")
|
33
|
+
aps_template = get_file_str("#{this_dir}/../templates/api_documentation_service.txt")
|
34
|
+
write_file("#{root}/app/services/api_documentation_service.rb", aps_template)
|
35
|
+
|
36
|
+
# Add Views
|
37
|
+
system("mkdir -p #{root}/app/views/documentation")
|
38
|
+
dihtml_template = get_file_str("#{this_dir}/../templates/documentation.index.erb.txt")
|
39
|
+
write_file("#{root}/app/views/documentation/index.html.erb", dihtml_template)
|
40
|
+
|
41
|
+
dlhtml_template = get_file_str("#{this_dir}/../templates/documentation.layout.erb.txt")
|
42
|
+
write_file("#{root}/app/views/layouts/documentation.html.erb", dlhtml_template)
|
43
|
+
|
44
|
+
# Update Routes
|
45
|
+
unless lookup[:"skip-routes"]
|
46
|
+
routes_template = get_file_str("#{this_dir}/../templates/routes_documentation.txt")
|
47
|
+
routes_file = get_file_str("#{root}/config/routes.rb")
|
48
|
+
routes_arr = routes_file.split("\n")
|
49
|
+
last_end_line = last_end_index(routes_arr)
|
50
|
+
|
51
|
+
new_routes = routes_arr.slice(0, last_end_line).join("\n") + "\n#{routes_template}\nend\n"
|
52
|
+
write_file("#{root}/config/routes.rb", new_routes)
|
53
|
+
end
|
54
|
+
|
55
|
+
puts "Your project has been initialized."
|
56
|
+
end
|
57
|
+
|
58
|
+
def last_end_index arr
|
59
|
+
arr.each_with_index.inject(0) do |acc, (e, i)|
|
60
|
+
acc = i if /(end)/.match(e)
|
61
|
+
acc
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative '../utils/strings.rb'
|
2
|
+
|
3
|
+
|
4
|
+
class ModelCommand < MoreUtils
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def run *args
|
8
|
+
lookup = flag_lookup(args)
|
9
|
+
arguments = get_args(args)
|
10
|
+
|
11
|
+
if arguments.length < 2
|
12
|
+
puts "Enter a valid model generation command."
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
model_name = arguments[0].camelcase
|
17
|
+
others = arguments[1..-1]
|
18
|
+
|
19
|
+
system("rails generate model #{model_name} #{others.join(' ')}")
|
20
|
+
|
21
|
+
api_version_path = lookup[:"api-version"] || 'api/v1'
|
22
|
+
system("mkdir -p #{root}/app/controllers/#{api_version_path}")
|
23
|
+
|
24
|
+
controller_prefix = api_version_path.split('/').map { |e| e.downcase.capitalize }.join('::')
|
25
|
+
controller_name = controller_prefix + '::' + model_name
|
26
|
+
|
27
|
+
controller_temp = get_file_str("#{this_dir}/../templates/controller.txt")
|
28
|
+
controller_regex = '{{ CONTROLLER_NAME }}'
|
29
|
+
controller_str = controller_temp.gsub(controller_regex, controller_name)
|
30
|
+
write_file("#{root}/app/controllers/#{api_version_path}/#{model_name.underscore}_controller.rb", controller_str)
|
31
|
+
|
32
|
+
puts "#{model_name} model, migration, and controller has been generated."
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative '../utils/strings.rb'
|
2
|
+
|
3
|
+
class DocumentationHelpCommand < MoreUtils
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def run descriptions
|
7
|
+
puts "#{ascii_art}
|
8
|
+
Rails Plus Plus Version: #{gem_version}
|
9
|
+
|
10
|
+
Rails Plus Plus: Command Line Interface to make your life easier.
|
11
|
+
=> The Rails Plus Plus command is 'railspp'. To blast this project into the fifth dimension.
|
12
|
+
=> Use '--help' on any of the commands listed below for more details.
|
13
|
+
|
14
|
+
List of commands:
|
15
|
+
#{descriptions}
|
16
|
+
"
|
17
|
+
end
|
18
|
+
|
19
|
+
def ascii_art
|
20
|
+
"______ _ __
|
21
|
+
| ___ \\ (_) | _ _
|
22
|
+
| |_/ /__ _ _| |___ _| |_ _| |_
|
23
|
+
| // _` | | / __|_ _|_ _|
|
24
|
+
| |\\ \\ (_| | | \\__ \\ |_| |_|
|
25
|
+
\\_| \\_\\__,_|_|_|___/
|
26
|
+
"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class InitializeHelpCommand
|
2
|
+
class << self
|
3
|
+
|
4
|
+
def run *args
|
5
|
+
puts "Options:
|
6
|
+
|
7
|
+
You have the availability to skip portions of the
|
8
|
+
initialize command.
|
9
|
+
|
10
|
+
If you would like to skip the initial route nmespace use:
|
11
|
+
|
12
|
+
--skip-routes
|
13
|
+
|
14
|
+
Run to initialize your project:
|
15
|
+
'railspp initialize'
|
16
|
+
"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
data/lib/help/model.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
class ModelHelpCommand
|
2
|
+
class << self
|
3
|
+
|
4
|
+
def run
|
5
|
+
puts "Options:
|
6
|
+
You have the abliity to enter an api-version through namespaces.
|
7
|
+
You can generate your controller in your nmespace directory.
|
8
|
+
By default the value is 'api/v1'
|
9
|
+
|
10
|
+
Enter your namespace directory-path from controllers directory without
|
11
|
+
a starting '/' in this option.
|
12
|
+
|
13
|
+
--api-version=(namespace-directory-path-from-controllers-directory)
|
14
|
+
|
15
|
+
Example:
|
16
|
+
|
17
|
+
--api-version=api/v2
|
18
|
+
|
19
|
+
Same command as:
|
20
|
+
rails generate model <model-name>
|
21
|
+
|
22
|
+
With your generated controller
|
23
|
+
|
24
|
+
Run to generate your model, migration, and controller:
|
25
|
+
'railspp model (model-name) (models-options) (your-api-version-flag)'
|
26
|
+
"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/railspp.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative './commands/initialize.rb'
|
2
|
+
require_relative './commands/make_test.rb'
|
3
|
+
require_relative './commands/model.rb'
|
4
|
+
require_relative './help/documentation.rb'
|
5
|
+
require_relative './help/initialize.rb'
|
6
|
+
require_relative './help/make_test.rb'
|
7
|
+
require_relative './help/model.rb'
|
8
|
+
require_relative './utils/strings.rb'
|
9
|
+
|
10
|
+
|
11
|
+
class RailsPlusPlus < MoreUtils
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def error_string
|
15
|
+
'ERROR: Must make railspp commands in the rails root directory'
|
16
|
+
end
|
17
|
+
|
18
|
+
def check_directory
|
19
|
+
File.file?(root + '/bin/rails')
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_command arguments
|
23
|
+
|
24
|
+
is_help = arguments.select { |e| e == '--help' || e == '-h' }.length > 0
|
25
|
+
command_name = arguments[0]
|
26
|
+
passable_args = arguments[1..-1]
|
27
|
+
lookup = is_help ? command_name_help_lookup : command_name_lookup
|
28
|
+
command_exists = !command_name.nil? && !lookup[command_name.to_sym].nil?
|
29
|
+
|
30
|
+
if command_exists
|
31
|
+
command_class = lookup[command_name.to_sym]
|
32
|
+
command_class.run(*passable_args)
|
33
|
+
else
|
34
|
+
DocumentationHelpCommand.run(descriptions.join("\n"))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def command_name_lookup
|
41
|
+
{
|
42
|
+
i: InitializeCommand,
|
43
|
+
init: InitializeCommand,
|
44
|
+
initialize: InitializeCommand,
|
45
|
+
m: ModelCommand,
|
46
|
+
model: ModelCommand,
|
47
|
+
make_test: MakeTestCommand,
|
48
|
+
mt: MakeTestCommand,
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def command_name_help_lookup
|
53
|
+
{
|
54
|
+
i: InitializeHelpCommand,
|
55
|
+
init: InitializeHelpCommand,
|
56
|
+
initialize: InitializeHelpCommand,
|
57
|
+
m: ModelHelpCommand,
|
58
|
+
model: ModelHelpCommand,
|
59
|
+
make_test: MakeTestHelpCommand,
|
60
|
+
mt: MakeTestHelpCommand,
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def descriptions
|
65
|
+
[
|
66
|
+
'- i => Initialize your project',
|
67
|
+
'- init => Initialize your project',
|
68
|
+
'- initialize => Initialize your project',
|
69
|
+
'- m => Generate your CRUD model, controller, and migration',
|
70
|
+
'- model => Generate your CRUD model, controller, and migration',
|
71
|
+
'- make_test => Generate your resource unit test',
|
72
|
+
'- mt => Generate your resource unit test'
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,206 @@
|
|
1
|
+
class APIDocumentationService
|
2
|
+
class << self
|
3
|
+
def generate_js_file
|
4
|
+
all_routes = get_api_routes
|
5
|
+
documentation_js_str = all_routes.map { |e| handle_js_for_route(e) }.join("\n")
|
6
|
+
|
7
|
+
File.open(output_path, "w+") { |f| f.write(documentation_js_str) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def namespaces
|
11
|
+
# Blacklist namespaces
|
12
|
+
[:api]
|
13
|
+
end
|
14
|
+
|
15
|
+
def docs_namespaces
|
16
|
+
namespaces.each_with_object({}) do |item, acc|
|
17
|
+
acc[item] = true
|
18
|
+
acc
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_api_routes
|
23
|
+
get_all_routes.select do |e|
|
24
|
+
path_array = e[:route].split('/')
|
25
|
+
path_array.length > 1 && docs_namespaces[path_array[1].to_sym]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def camelize_route route
|
30
|
+
camel_route = route[:route].split('/').reject { |e| e == '' }.map { |e| /\:/.match(e) ? e.gsub!(':', '') + 'Param' : e }.map { |e| e.capitalize }.join('')
|
31
|
+
route[:method].downcase + camel_route
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def output_path
|
37
|
+
__dir__ + '/../../app/assets/javascripts/documentation.js'
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_all_routes
|
41
|
+
Rails.application.routes.routes.map { |e| { route: e.path.spec.to_s.split('(')[0], method: e.verb } }.map { |e| e[:route] && e[:method] ? e.merge(camel_cased: camelize_route(e)) : e }.select { |e| e[:route] }
|
42
|
+
end
|
43
|
+
|
44
|
+
def handle_js_for_route route
|
45
|
+
camel_cased = route[:camel_cased]
|
46
|
+
javascript_string = ''
|
47
|
+
javascript_string += "window.#{camel_cased} = function() {
|
48
|
+
var allData = {
|
49
|
+
method: '#{route[:method]}',
|
50
|
+
route: '#{route[:route]}',
|
51
|
+
};
|
52
|
+
var paramList = document.getElementById('#{camel_cased}ParamsForm') && document.getElementById('#{camel_cased}ParamsForm').elements ? document.getElementById('#{camel_cased}ParamsForm').elements : [];
|
53
|
+
var headerList = document.getElementById('#{camel_cased}HeadersForm') && document.getElementById('#{camel_cased}HeadersForm').elements ? document.getElementById('#{camel_cased}HeadersForm').elements : [];
|
54
|
+
|
55
|
+
var headerObject = {};
|
56
|
+
var tempHeaderKey = null;
|
57
|
+
for (var i = 0; i < headerList.length; i++) {
|
58
|
+
var eleHeader = headerList[i].value;
|
59
|
+
if (i % 2 !== 0 && tempHeaderKey) {
|
60
|
+
headerObject[tempHeaderKey] = eleHeader;
|
61
|
+
tempHeaderKey = null;
|
62
|
+
} else {
|
63
|
+
tempHeaderKey = eleHeader;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
var hasHeaders = Object.keys(headerObject).length > 0;
|
68
|
+
var headers = { headers: headerObject };
|
69
|
+
|
70
|
+
var paramObject = {};
|
71
|
+
var tempParamKey = null;
|
72
|
+
for (var i = 0; i < paramList.length; i++) {
|
73
|
+
var eleParam = paramList[i].value;
|
74
|
+
if (i % 2 !== 0) {
|
75
|
+
paramObject[':' + tempParamKey] = eleParam;
|
76
|
+
tempParamKey = null;
|
77
|
+
} else {
|
78
|
+
tempParamKey = eleParam;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
var routeName = allData.route.split('/').map(function(e) { return paramObject[e] ? paramObject[e] : e; }).join('/');
|
83
|
+
|
84
|
+
if (allData.method !== 'GET') {
|
85
|
+
var bodyDataType = document.getElementById('#{camel_cased}DataType') && document.getElementById('#{camel_cased}DataType').value ? document.getElementById('#{camel_cased}DataType').value : false;
|
86
|
+
var formBoolean = bodyDataType === 'Form Data';
|
87
|
+
|
88
|
+
var bodyElements = [];
|
89
|
+
var bodyRawElements = document.getElementById('#{camel_cased}BodyForm') && document.getElementById('#{camel_cased}BodyForm').elements ? document.getElementById('#{camel_cased}BodyForm').elements : [];
|
90
|
+
for (var i = 0; i < bodyRawElements.length; i++) {
|
91
|
+
var eleParam = bodyRawElements[i].files ? bodyRawElements[i].files[0] : (bodyRawElements[i].value || null);
|
92
|
+
bodyElements.push(eleParam);
|
93
|
+
}
|
94
|
+
bodyElements = bodyElements.filter(function(e) {
|
95
|
+
return !!e;
|
96
|
+
});
|
97
|
+
|
98
|
+
var bodyObject = bodyDataType === 'Form Data' ? new FormData() : {};
|
99
|
+
var tempBodyKey = null;
|
100
|
+
bodyElements.forEach(function(e, i) {
|
101
|
+
if (i % 2 !== 0) {
|
102
|
+
if (formBoolean) {
|
103
|
+
bodyObject.append(tempBodyKey, e);
|
104
|
+
tempBodyKey = null;
|
105
|
+
} else {
|
106
|
+
bodyObject[tempBodyKey] = e;
|
107
|
+
tempBodyKey = null;
|
108
|
+
}
|
109
|
+
} else {
|
110
|
+
if (formBoolean) {
|
111
|
+
tempBodyKey = e;
|
112
|
+
} else {
|
113
|
+
bodyObject[e] = null;
|
114
|
+
tempBodyKey = e;
|
115
|
+
}
|
116
|
+
}
|
117
|
+
});
|
118
|
+
}
|
119
|
+
|
120
|
+
var qsElements = [];
|
121
|
+
var qsRawElements = document.getElementById('#{camel_cased}QSForm') && document.getElementById('#{camel_cased}QSForm').elements ? document.getElementById('#{camel_cased}QSForm').elements : [];
|
122
|
+
for (var i = 0; i < qsRawElements.length; i++) {
|
123
|
+
var eleParam = qsRawElements[i].value || null;
|
124
|
+
qsElements.push(eleParam);
|
125
|
+
}
|
126
|
+
|
127
|
+
qsElements = qsElements.filter(function(e) { return !!e; });
|
128
|
+
|
129
|
+
var qsObject = {};
|
130
|
+
var tempQSKey = null;
|
131
|
+
qsElements.forEach(function(e, i) {
|
132
|
+
if (i % 2 !== 0) {
|
133
|
+
qsObject[tempQSKey] = e;
|
134
|
+
tempQSKey = null;
|
135
|
+
} else {
|
136
|
+
qsObject[e] = null;
|
137
|
+
tempQSKey = e;
|
138
|
+
}
|
139
|
+
});
|
140
|
+
|
141
|
+
var qsLength = Object.keys(qsObject).length;
|
142
|
+
var querystring = qsLength > 0 ? '?' : '';
|
143
|
+
|
144
|
+
var qsCount = 0;
|
145
|
+
var qsArray = [];
|
146
|
+
if (querystring === '?') {
|
147
|
+
for (var qs in qsObject) {
|
148
|
+
if (qs && qsObject[qs]) {
|
149
|
+
qsArray.push(qs + '=' + qsObject[qs]);
|
150
|
+
}
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
querystring += qsArray.join('&');
|
155
|
+
|
156
|
+
var args = allData.method === 'GET' || allData.method === 'DELETE' ? [routeName + querystring, headers] : [routeName + querystring, bodyObject, headers];
|
157
|
+
if (!hasHeaders) args.pop();
|
158
|
+
var resultElement = document.getElementById('#{camel_cased}-results');
|
159
|
+
|
160
|
+
axios[allData.method.toLowerCase()](...args)
|
161
|
+
.then(function(resp) {
|
162
|
+
if (resp.status <= 300) {
|
163
|
+
resultElement.innerText = JSON.stringify(resp.data, null, 4);
|
164
|
+
} else {
|
165
|
+
resultElement.innerText = JSON.stringify(resp.data, null, 4);
|
166
|
+
}
|
167
|
+
})
|
168
|
+
.catch(function(err) {
|
169
|
+
var error_ajax = err && err.response && err.response.data ? err.response.data : err;
|
170
|
+
resultElement.innerText = JSON.stringify(error_ajax, null, 4);
|
171
|
+
});
|
172
|
+
};
|
173
|
+
"
|
174
|
+
|
175
|
+
javascript_string += "window.#{camel_cased}NewBody = function() {
|
176
|
+
var ele = document.getElementById('#{camel_cased}BodyForm');
|
177
|
+
ele.innerHTML += '<div class=\"d-flex f-row\"><input class=\"w-100 m-1 form-control\" type=\"text\" placeholder=\"Enter key\"><input class=\"w-100 m-1 form-control\" type=\"text\" placeholder=\"Enter value\"></div>';
|
178
|
+
};
|
179
|
+
|
180
|
+
"
|
181
|
+
|
182
|
+
javascript_string += "window.#{camel_cased}NewBodyFile = function() {
|
183
|
+
var ele = document.getElementById('#{camel_cased}BodyForm');
|
184
|
+
ele.innerHTML += '<div class=\"d-flex f-row\"><input class=\"w-100 m-1 form-control\" type=\"text\" placeholder=\"Enter key\"><input class=\"w-100 m-1 form-control\" type=\"file\" multiple accept=\"*/*\" placeholder=\"Enter value\"></div>';
|
185
|
+
};
|
186
|
+
|
187
|
+
"
|
188
|
+
|
189
|
+
javascript_string += "window.#{camel_cased}NewQS = function() {
|
190
|
+
var ele = document.getElementById('#{camel_cased}QSForm');
|
191
|
+
ele.innerHTML += '<div class=\"d-flex f-row\"><input class=\"w-100 m-1 form-control\" type=\"text\" placeholder=\"Enter key\"><input class=\"w-100 m-1 form-control\" type=\"text\" placeholder=\"Enter value\"></div>';
|
192
|
+
};
|
193
|
+
"
|
194
|
+
|
195
|
+
javascript_string += "window.#{camel_cased}NewHeader = function() {
|
196
|
+
var ele = document.getElementById('#{camel_cased}HeadersForm');
|
197
|
+
ele.innerHTML += '<div class=\"d-flex f-row\"><input class=\"w-100 m-1 form-control\" type=\"text\" placeholder=\"Enter key\"><input class=\"w-100 m-1 form-control\" type=\"text\" placeholder=\"Enter value\"></div>';
|
198
|
+
};
|
199
|
+
|
200
|
+
";
|
201
|
+
|
202
|
+
javascript_string
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
<div class="header p-3 bg-dark card-shadow d-flex flex-row justify-content-between">
|
2
|
+
<div class="d-flex flex-row">
|
3
|
+
<a href='/' class="h-100">
|
4
|
+
<img class="d-flex flex-column align-items-center justify-content-center" src="https://s3-us-west-1.amazonaws.com/manoftech/ruby.png" alt="" height='50' width='50' />
|
5
|
+
</a>
|
6
|
+
<h1 class="h1 h-100 align-items-center text-white ml-3">API Documentation</h1>
|
7
|
+
</div>
|
8
|
+
<a class="d-flex align-items-center cursor-pointer">
|
9
|
+
<h2 class="h5 text-white" onclick="goBack()">Go Back</h2>
|
10
|
+
</a>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div class="card-body p-3 w-100 p-1">
|
14
|
+
<% @all_routes.each do |data| %>
|
15
|
+
<div class="card card-outline-secondary w-100 flex-respond-row card-shadow">
|
16
|
+
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 p-1 card-shadow">
|
17
|
+
<div class='card-header <%= "#{data[:route_header]}" %>'>
|
18
|
+
<span class="mr-1"><%= data[:method] %>:</span>
|
19
|
+
<span><%= data[:route] %></span>
|
20
|
+
</div>
|
21
|
+
<div class="card-body p-2 w-100" style="max-height: 550px; overflow: auto;">
|
22
|
+
<% if data[:middleware] && data[:middleware].length > 0 %>
|
23
|
+
<h2 class="h5">Middleware Used:</h2>
|
24
|
+
<ul>
|
25
|
+
<% data[:middleware].each do |ware| %>
|
26
|
+
<li class="h6"><%= "#{ware}" %></li>
|
27
|
+
<% end %>
|
28
|
+
</ul>
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
<h2 class="h5">Description:
|
32
|
+
<ul>
|
33
|
+
<% data[:description].each do |ware| %>
|
34
|
+
<li class="h6"><%= "#{ware}" %></li>
|
35
|
+
<% end %>
|
36
|
+
</ul>
|
37
|
+
</h2>
|
38
|
+
|
39
|
+
<div class="d-flex f-row w-100">
|
40
|
+
<h2 class="w-100 h6">Headers:</h2>
|
41
|
+
<button class="<%= "#{data[:submit_button_color]} rounded-circle" %>" onclick="<%= "#{data[:submit_button_color]}NewHeader()" %>">+</button>
|
42
|
+
</div>
|
43
|
+
<div class="d-flex f-row w-100">
|
44
|
+
<form id="<%= data[:camel_cased] %>HeadersForm" class="w-100">
|
45
|
+
<div class="d-flex f-row">
|
46
|
+
<input type="text" class="w-100 m-1 d-flex f-row form-control" placeholder="Enter key">
|
47
|
+
<input type="text" class="w-100 m-1 form-control" placeholder="Enter value">
|
48
|
+
</div>
|
49
|
+
</form>
|
50
|
+
</div>
|
51
|
+
|
52
|
+
<% if data[:allow_params] %>
|
53
|
+
<div class="d-flex f-row w-100">
|
54
|
+
<h2 class="w-100 h6">Params:</h2>
|
55
|
+
</div>
|
56
|
+
<div class="d-flex f-row w-100">
|
57
|
+
<form id="<%= "#{data[:camel_cased]}ParamsForm" %>" class="w-100">
|
58
|
+
<%= data[:params].each do |param| %>
|
59
|
+
<div class="d-flex f-row">
|
60
|
+
<input id="<%= "#{data[:camel_cased]}-#{param}" %>" type="text" class="w-100 m-1 d-flex f-row form-control" value="<%= "#{param}" %>">
|
61
|
+
<input id="<%= "#{data[:camel_cased]}-#{param}-value" %>" type="text" class="w-100 m-1 form-control" placeholder="Enter value">
|
62
|
+
</div>
|
63
|
+
<% end %>
|
64
|
+
</form>
|
65
|
+
</div>
|
66
|
+
<% else %>
|
67
|
+
<span></span>
|
68
|
+
<% end %>
|
69
|
+
<% if data[:allow_body] %>
|
70
|
+
<div class="d-flex f-row w-100">
|
71
|
+
<h2 class="w-100 h6">Body:</h2>
|
72
|
+
<button class="<%= "#{data[:submit_button_color]} rounded mr-2" %>" onclick="<%= "#{data[:camel_cased]}NewBodyFile()" %>">Add File</button>
|
73
|
+
<button class="<%= "#{data[:submit_button_color]} rounded-circle" %>" onclick="<%= "#{data[:camel_cased]}NewBody()" %>">+</button>
|
74
|
+
</div>
|
75
|
+
<div class="d-flex f-row w-100">
|
76
|
+
<form id='<%= "#{data[:camel_cased]}BodyForm" %>'class='w-100'>
|
77
|
+
<div class='d-flex f-row'>
|
78
|
+
<input type='text' class='w-100 m-1 form-control' placeholder='Enter key'>
|
79
|
+
<input type='text' class='w-100 m-1 form-control' placeholder='Enter value'>
|
80
|
+
</div>
|
81
|
+
</form>
|
82
|
+
</div>
|
83
|
+
<% else %>
|
84
|
+
<span></span>
|
85
|
+
<% end %>
|
86
|
+
<div class="d-flex f-row w-100">
|
87
|
+
<h2 class="w-100 h6">Querystrings: </h2>
|
88
|
+
<button class="<%= "#{data[:submit_button_color]} rounded-circle" %>" onclick="<%= "#{data[:camel_cased]}NewQS()" %>">+</button>
|
89
|
+
</div>
|
90
|
+
<div class="d-flex f-row w-100">
|
91
|
+
<form id='<%= "#{data[:camel_cased]}QSForm" %>' class="w-100">
|
92
|
+
<div class="d-flex f-row">
|
93
|
+
<input type="text" class="w-100 m-1 form-control" placeholder="Enter key">
|
94
|
+
<input type="text" class="w-100 m-1 form-control" placeholder="Enter value">
|
95
|
+
</div>
|
96
|
+
</form>
|
97
|
+
</div>
|
98
|
+
<% if data[:allow_body] %>
|
99
|
+
<h2 class="h6">Body Data Type:</h2>
|
100
|
+
<select id="<%= "#{data[:camel_cased]}DataType" %>" class='form-control'>
|
101
|
+
<option value=''></option>
|
102
|
+
<option value='JSON'>JSON</option>
|
103
|
+
<option value='Form Data'>Form Data</option>
|
104
|
+
</select>
|
105
|
+
<% else %>
|
106
|
+
<span></span>
|
107
|
+
<% end %>
|
108
|
+
<div class="w-100 mt-2">
|
109
|
+
<button class="<%= "#{data[:submit_button_color]} w-100" %>" onclick="<%= "#{data[:camel_cased]}()" %>">Submit</button>
|
110
|
+
</div>
|
111
|
+
</div>
|
112
|
+
</div>
|
113
|
+
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 p-1 card-shadow p-1">
|
114
|
+
<div class="card-header">
|
115
|
+
<h2 class="h5">Sample Data</h2>
|
116
|
+
</div>
|
117
|
+
<div class='card-body' style="max-height: 550px; overflow: auto;">
|
118
|
+
<code style="overflow: auto; display: block;">
|
119
|
+
<span id="<%= "#{data[:camel_cased]}-results" %>" style="white-space: pre;"></span>
|
120
|
+
</code>
|
121
|
+
</div>
|
122
|
+
</div>
|
123
|
+
</div>
|
124
|
+
<% end %>
|
125
|
+
</div>
|
126
|
+
|
127
|
+
|
128
|
+
<script>function goBack() { window.history.back(); }</script>
|
129
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Automatic API Docs</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
8
|
+
<style>.flex-respond-row {display: flex !important;flex-direction: column !important;}@media (min-width: 992px) {.flex-respond-row {display: flex !important;flex-direction: row !important;}}.card-shadow {box-shadow: 0 7px 14px 0 rgba(50,50,93,.1), 0 3px 6px 0 rgba(0,0,0,.07);}.cursor-pointer{ cursor: pointer !important; }</style>
|
9
|
+
</head>
|
10
|
+
|
11
|
+
<body style="width: 100%; overflow-x: hidden;">
|
12
|
+
<%= yield %>
|
13
|
+
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
|
14
|
+
</body>
|
15
|
+
</html>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class DocumentationController < ApplicationController
|
2
|
+
|
3
|
+
def index
|
4
|
+
@all_routes = get_api_array
|
5
|
+
render template: 'documentation/index', layout: 'documentation'
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def get_api_array
|
11
|
+
get_api_routes.map { |e| get_api_object(e) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_api_object route_hash
|
15
|
+
{
|
16
|
+
method: route_hash[:method],
|
17
|
+
route: route_hash[:route],
|
18
|
+
params: get_params(route_hash[:route]),
|
19
|
+
middleware: ['Not Available'],
|
20
|
+
route_header: "text-white bg-#{get_method_color(route_hash[:method])}",
|
21
|
+
submit_button_color: "btn btn-outline-#{get_method_color(route_hash[:method])}",
|
22
|
+
camel_cased: camelize_route(route_hash),
|
23
|
+
allow_params: route_hash[:route].split(':').length > 1,
|
24
|
+
allow_body: route_hash[:method] != 'GET',
|
25
|
+
description: ['No Description available'],
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_method_color method
|
30
|
+
lookup = {
|
31
|
+
GET: 'success',
|
32
|
+
POST: 'info',
|
33
|
+
PATCH: 'warning',
|
34
|
+
PUT: 'warning',
|
35
|
+
DELETE: 'danger',
|
36
|
+
}
|
37
|
+
lookup[method.to_sym] || 'light'
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_params route
|
41
|
+
route.split('/').select { |e| /\:/.match(e) }.map { |e| e.split(':')[1] }
|
42
|
+
end
|
43
|
+
|
44
|
+
def camelize_route route
|
45
|
+
APIDocumentationService.camelize_route(route)
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_api_routes
|
49
|
+
APIDocumentationService.get_api_routes
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ExceptionHandler
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
rescue_from ActiveRecord::RecordNotFound do |e|
|
6
|
+
render_json({ error: e.message }, :not_found)
|
7
|
+
end
|
8
|
+
rescue_from ActiveRecord::RecordInvalid do |e|
|
9
|
+
render_json({ error: e.message }, :unprocessable_entity)
|
10
|
+
end
|
11
|
+
rescue_from ActiveRecord::ActiveRecordError do |e|
|
12
|
+
render_json({ error: e.message }, :bad_request)
|
13
|
+
end
|
14
|
+
rescue_from ActionController::InvalidAuthenticityToken do |e|
|
15
|
+
render_json({ error: e.message }, :forbidden)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
class GlobalController < ApplicationController
|
2
|
+
|
3
|
+
include Response
|
4
|
+
include ExceptionHandler
|
5
|
+
|
6
|
+
def default_per_page
|
7
|
+
25
|
8
|
+
end
|
9
|
+
|
10
|
+
def model
|
11
|
+
self.class.to_s.split('::').select { |e| /Controller/.match(e) }.first.chomp('Controller').singularize.constantize
|
12
|
+
end
|
13
|
+
|
14
|
+
def index
|
15
|
+
resp = index_response
|
16
|
+
past_pages = resp[:last_page] < resp[:current_page]
|
17
|
+
error = { error: 'Past the amount of pages available' }
|
18
|
+
render_json(past_pages ? error : index_response, past_pages ? 400 : 200)
|
19
|
+
end
|
20
|
+
|
21
|
+
def show
|
22
|
+
data = model.find(params[:id] || 0)
|
23
|
+
render_json(data, 200)
|
24
|
+
end
|
25
|
+
|
26
|
+
def create
|
27
|
+
data = model.create(create_params)
|
28
|
+
render_json(data, 201)
|
29
|
+
end
|
30
|
+
|
31
|
+
def update
|
32
|
+
data = model.where(params[:id] || 0).update(update_params).first
|
33
|
+
render_json(data, 200)
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy
|
37
|
+
model.destroy(params[:id] || 0)
|
38
|
+
render_json(nil, 204)
|
39
|
+
end
|
40
|
+
|
41
|
+
def bulk_csv_create
|
42
|
+
data = model.create(csv_to_json)
|
43
|
+
render_json(data, 201)
|
44
|
+
end
|
45
|
+
|
46
|
+
def bulk_create
|
47
|
+
data = model.create(bulk_create_params)
|
48
|
+
render_json(data, 201)
|
49
|
+
end
|
50
|
+
|
51
|
+
def bulk_update
|
52
|
+
data = model.where(id: bulk_update_params[:id]).update(bulk_update_params)
|
53
|
+
render_json(data, 201)
|
54
|
+
end
|
55
|
+
|
56
|
+
def bulk_destory
|
57
|
+
model.where(id: bulk_destroy_params)
|
58
|
+
render_json(nil, 204)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def csv_to_json param_key = :file, separated_by = ','
|
64
|
+
lines = params[param_key].split("\n")
|
65
|
+
keys = lines[0].split(separated_by)
|
66
|
+
rows = lines.slice(1).map { |e| e.split(separated_by) }
|
67
|
+
rows.map { |e| e.each_with_index.inject({}) { |acc, (e, i)| acc.merge("#{keys[i]}": e) } }
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_params
|
71
|
+
bl = array_to_hash(black_list_create)
|
72
|
+
params.permit(*get_model_key.reject { |e| bl[e.to_sym] })
|
73
|
+
end
|
74
|
+
|
75
|
+
def update_params
|
76
|
+
bl = array_to_hash(black_list_update)
|
77
|
+
params.permit(*get_model_key.reject { |e| bl[e.to_sym] })
|
78
|
+
end
|
79
|
+
|
80
|
+
def bulk_create_params
|
81
|
+
params.permit(bulk: get_model_key)
|
82
|
+
end
|
83
|
+
|
84
|
+
def bulk_update_params
|
85
|
+
params.permit(bulk: get_model_key)
|
86
|
+
end
|
87
|
+
|
88
|
+
def bulk_destroy_params
|
89
|
+
params.permit(bulk: [:id])
|
90
|
+
end
|
91
|
+
|
92
|
+
def black_list_create
|
93
|
+
[:id, :created_at, :updated_at]
|
94
|
+
end
|
95
|
+
|
96
|
+
def black_list_update
|
97
|
+
[:id, :created_at, :updated_at]
|
98
|
+
end
|
99
|
+
|
100
|
+
def array_to_hash array
|
101
|
+
array.each_with_object({}) do |e, acc|
|
102
|
+
acc[e] = true
|
103
|
+
acc
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def handle_pagination
|
108
|
+
limit = (params[:limit] || default_per_page).to_i
|
109
|
+
offset = (params[:offset] || 0).to_i
|
110
|
+
page = (params[:page] || 1).to_i
|
111
|
+
|
112
|
+
data = model.limit(limit).offset(offset).offset((page - 1) * limit)
|
113
|
+
data = data.where(get_where) if params[:where]
|
114
|
+
data = data.includes(*get_includes) if params[:include]
|
115
|
+
data = data.order(get_order) if params[:order]
|
116
|
+
data = data.all
|
117
|
+
data
|
118
|
+
end
|
119
|
+
|
120
|
+
def handle_pagination_count
|
121
|
+
data = model
|
122
|
+
data = data.where(get_where) if params[:where]
|
123
|
+
data.count
|
124
|
+
end
|
125
|
+
|
126
|
+
def index_response
|
127
|
+
page = (params[:page] || 1).to_i
|
128
|
+
per_page = (params[:limit] || default_per_page).to_i
|
129
|
+
total = handle_pagination_count
|
130
|
+
path = request.original_url
|
131
|
+
path_path = add_pg_qs(path)
|
132
|
+
last = (total / per_page.to_f).ceil
|
133
|
+
nxt = (page + 1) > last ? nil : (page + 1)
|
134
|
+
prev = (page - 1) == 0 ? nil : (page - 1)
|
135
|
+
last = last == 0 ? 1 : last
|
136
|
+
data = handle_pagination
|
137
|
+
data_first = data.first
|
138
|
+
data_last = data.last
|
139
|
+
|
140
|
+
{
|
141
|
+
data: data,
|
142
|
+
to: data_first ? data_first.id : nil,
|
143
|
+
from: data_last ? data_last.id : nil,
|
144
|
+
total: total,
|
145
|
+
path: path,
|
146
|
+
current_page: page,
|
147
|
+
per_page: per_page,
|
148
|
+
last_page: last,
|
149
|
+
first_page_url: path_path + '1',
|
150
|
+
last_page_url: path_path + last.to_s,
|
151
|
+
next_page_url: nxt ? (path_path + nxt.to_s) : nil,
|
152
|
+
prev_page_url: prev ? (path_path + prev.to_s) : nil,
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
def add_pg_qs str
|
157
|
+
has_qs = /\?/.match(str)
|
158
|
+
return "#{str}?page=" unless has_qs
|
159
|
+
before_qs, after = str.split('?')
|
160
|
+
after_qs = after.split('&').reject { |e| /page\=/.match(e) }
|
161
|
+
after_qs.push('page=')
|
162
|
+
"#{before_qs}?#{after_qs.join('&')}"
|
163
|
+
end
|
164
|
+
|
165
|
+
def get_includes
|
166
|
+
comma_separated(params[:include]).map(&:to_sym)
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_order
|
170
|
+
params[:order].split(',').flat_map { |e| e.split(':') }.join(' ')
|
171
|
+
end
|
172
|
+
|
173
|
+
def get_where
|
174
|
+
arrays = params[:where].split(',').map { |e| e.split(':') }
|
175
|
+
arrays.inject({}) do |acc, item|
|
176
|
+
key, val = item
|
177
|
+
acc[key] = val
|
178
|
+
acc
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def comma_separated key
|
183
|
+
params[:key].split(',')
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_model_key
|
187
|
+
model.columns.map { |e| e.name.to_sym }
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
def underscore
|
4
|
+
self.gsub(/::/, '/').
|
5
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
6
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
7
|
+
tr("-", "_").
|
8
|
+
downcase
|
9
|
+
end
|
10
|
+
|
11
|
+
def camelcase
|
12
|
+
self.split(/(?=[A-Z])|(_)/)
|
13
|
+
.reject { |e| e == '_' }
|
14
|
+
.map(&:capitalize)
|
15
|
+
.join('')
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class MoreUtils
|
21
|
+
class << self
|
22
|
+
|
23
|
+
def gem_version
|
24
|
+
"0.0.3"
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_file_str path
|
28
|
+
File.open(path, 'r:UTF-8', &:read)
|
29
|
+
end
|
30
|
+
|
31
|
+
def write_file path, str
|
32
|
+
File.write(path, str)
|
33
|
+
end
|
34
|
+
|
35
|
+
def this_dir
|
36
|
+
__dir__
|
37
|
+
end
|
38
|
+
|
39
|
+
def root
|
40
|
+
Dir.pwd
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_flags arr
|
44
|
+
arr.select { |e| /--/.match(e) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_args arr
|
48
|
+
arr.reject { |e| /--/.match(e) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def flag_lookup arr
|
52
|
+
arr.each_with_object({}) do |e, acc|
|
53
|
+
e = e.gsub('--', '')
|
54
|
+
key, val = e.split('=')
|
55
|
+
acc[key.to_sym] = val
|
56
|
+
acc
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: railspp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Layne Faler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-01-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack-cors
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faker
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.9'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.9'
|
55
|
+
description: Scaffolding your CRUD operations
|
56
|
+
email: laynefaler@gmail.com
|
57
|
+
executables:
|
58
|
+
- railspp
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- README.md
|
63
|
+
- bin/railspp
|
64
|
+
- lib/commands/initialize.rb
|
65
|
+
- lib/commands/make_test.rb
|
66
|
+
- lib/commands/model.rb
|
67
|
+
- lib/help/documentation.rb
|
68
|
+
- lib/help/initialize.rb
|
69
|
+
- lib/help/make_test.rb
|
70
|
+
- lib/help/model.rb
|
71
|
+
- lib/railspp.rb
|
72
|
+
- lib/templates/api_documentation_initializer.txt
|
73
|
+
- lib/templates/api_documentation_service.txt
|
74
|
+
- lib/templates/controller.txt
|
75
|
+
- lib/templates/documentation.index.erb.txt
|
76
|
+
- lib/templates/documentation.layout.erb.txt
|
77
|
+
- lib/templates/documentation_controller.txt
|
78
|
+
- lib/templates/exception_handler.txt
|
79
|
+
- lib/templates/global_controller.txt
|
80
|
+
- lib/templates/rack_cors_initializer.txt
|
81
|
+
- lib/templates/response.txt
|
82
|
+
- lib/templates/routes_documentation.txt
|
83
|
+
- lib/templates/routes_namespace.txt
|
84
|
+
- lib/utils/strings.rb
|
85
|
+
homepage:
|
86
|
+
licenses: []
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.5'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubygems_version: 3.0.1
|
104
|
+
signing_key:
|
105
|
+
specification_version: 4
|
106
|
+
summary: Scaffold your CRUD operations
|
107
|
+
test_files: []
|