api_maker 0.0.1
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/MIT-LICENSE +20 -0
- data/README.md +476 -0
- data/Rakefile +27 -0
- data/app/channels/api_maker/subscriptions_channel.rb +80 -0
- data/app/controllers/api_maker/base_controller.rb +32 -0
- data/app/controllers/api_maker/commands_controller.rb +26 -0
- data/app/controllers/api_maker/devise_controller.rb +60 -0
- data/app/controllers/api_maker/session_statuses_controller.rb +33 -0
- data/app/services/api_maker/application_service.rb +7 -0
- data/app/services/api_maker/collection_command_service.rb +24 -0
- data/app/services/api_maker/command_response.rb +67 -0
- data/app/services/api_maker/command_service.rb +31 -0
- data/app/services/api_maker/create_command.rb +62 -0
- data/app/services/api_maker/create_command_service.rb +18 -0
- data/app/services/api_maker/destroy_command.rb +39 -0
- data/app/services/api_maker/destroy_command_service.rb +22 -0
- data/app/services/api_maker/generate_react_native_api_service.rb +61 -0
- data/app/services/api_maker/index_command.rb +96 -0
- data/app/services/api_maker/index_command_service.rb +22 -0
- data/app/services/api_maker/js_method_namer_service.rb +11 -0
- data/app/services/api_maker/member_command_service.rb +25 -0
- data/app/services/api_maker/model_content_generator_service.rb +108 -0
- data/app/services/api_maker/models_finder_service.rb +22 -0
- data/app/services/api_maker/models_generator_service.rb +104 -0
- data/app/services/api_maker/update_command.rb +43 -0
- data/app/services/api_maker/update_command_service.rb +21 -0
- data/app/services/api_maker/valid_command.rb +35 -0
- data/app/services/api_maker/valid_command_service.rb +21 -0
- data/app/views/api_maker/_data.html.erb +15 -0
- data/config/rails_best_practices.yml +55 -0
- data/config/routes.rb +7 -0
- data/lib/api_maker.rb +36 -0
- data/lib/api_maker/ability.rb +39 -0
- data/lib/api_maker/ability_loader.rb +21 -0
- data/lib/api_maker/action_controller_base_extensions.rb +5 -0
- data/lib/api_maker/base_command.rb +81 -0
- data/lib/api_maker/base_resource.rb +78 -0
- data/lib/api_maker/collection_serializer.rb +69 -0
- data/lib/api_maker/command_spec_helper.rb +57 -0
- data/lib/api_maker/configuration.rb +34 -0
- data/lib/api_maker/engine.rb +5 -0
- data/lib/api_maker/individual_command.rb +37 -0
- data/lib/api_maker/javascript/api.js +92 -0
- data/lib/api_maker/javascript/base-model.js +543 -0
- data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +16 -0
- data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +47 -0
- data/lib/api_maker/javascript/bootstrap/card.jsx +79 -0
- data/lib/api_maker/javascript/bootstrap/checkbox.jsx +127 -0
- data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +105 -0
- data/lib/api_maker/javascript/bootstrap/live-table.jsx +168 -0
- data/lib/api_maker/javascript/bootstrap/money-input.jsx +136 -0
- data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +80 -0
- data/lib/api_maker/javascript/bootstrap/select.jsx +168 -0
- data/lib/api_maker/javascript/bootstrap/string-input.jsx +203 -0
- data/lib/api_maker/javascript/cable-connection-pool.js +169 -0
- data/lib/api_maker/javascript/cable-subscription-pool.js +111 -0
- data/lib/api_maker/javascript/cable-subscription.js +33 -0
- data/lib/api_maker/javascript/collection.js +186 -0
- data/lib/api_maker/javascript/commands-pool.js +123 -0
- data/lib/api_maker/javascript/custom-error.js +14 -0
- data/lib/api_maker/javascript/deserializer.js +35 -0
- data/lib/api_maker/javascript/devise.js.erb +113 -0
- data/lib/api_maker/javascript/error-logger.js +119 -0
- data/lib/api_maker/javascript/event-connection.jsx +24 -0
- data/lib/api_maker/javascript/event-created.jsx +26 -0
- data/lib/api_maker/javascript/event-destroyed.jsx +26 -0
- data/lib/api_maker/javascript/event-emitter-listener.jsx +32 -0
- data/lib/api_maker/javascript/event-listener.jsx +41 -0
- data/lib/api_maker/javascript/event-updated.jsx +26 -0
- data/lib/api_maker/javascript/form-data-to-object.js +70 -0
- data/lib/api_maker/javascript/included.js +39 -0
- data/lib/api_maker/javascript/key-value-store.js +47 -0
- data/lib/api_maker/javascript/logger.js +23 -0
- data/lib/api_maker/javascript/model-name.js +21 -0
- data/lib/api_maker/javascript/model-template.js.erb +110 -0
- data/lib/api_maker/javascript/models-response-reader.js +43 -0
- data/lib/api_maker/javascript/paginate.jsx +128 -0
- data/lib/api_maker/javascript/params.js +68 -0
- data/lib/api_maker/javascript/resource-route.jsx +75 -0
- data/lib/api_maker/javascript/resource-routes.jsx +36 -0
- data/lib/api_maker/javascript/result.js +25 -0
- data/lib/api_maker/javascript/session-status-updater.js +113 -0
- data/lib/api_maker/javascript/sort-link.jsx +88 -0
- data/lib/api_maker/javascript/updated-attribute.jsx +60 -0
- data/lib/api_maker/loader.rb +14 -0
- data/lib/api_maker/memory_storage.rb +65 -0
- data/lib/api_maker/model_extensions.rb +96 -0
- data/lib/api_maker/permitted_params_argument.rb +12 -0
- data/lib/api_maker/preloader.rb +91 -0
- data/lib/api_maker/preloader_belongs_to.rb +58 -0
- data/lib/api_maker/preloader_has_many.rb +69 -0
- data/lib/api_maker/preloader_has_one.rb +70 -0
- data/lib/api_maker/preloader_through.rb +101 -0
- data/lib/api_maker/railtie.rb +14 -0
- data/lib/api_maker/relationship_includer.rb +42 -0
- data/lib/api_maker/resource_routing.rb +8 -0
- data/lib/api_maker/result_parser.rb +50 -0
- data/lib/api_maker/serializer.rb +86 -0
- data/lib/api_maker/spec_helper.rb +100 -0
- data/lib/api_maker/version.rb +3 -0
- data/lib/tasks/api_maker_tasks.rake +5 -0
- metadata +581 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class ApiMaker::CreateCommandService < ApiMaker::CommandService
|
|
2
|
+
def execute
|
|
3
|
+
ApiMaker::CreateCommand.execute_in_thread!(
|
|
4
|
+
ability: ability,
|
|
5
|
+
args: args,
|
|
6
|
+
collection: collection,
|
|
7
|
+
commands: commands,
|
|
8
|
+
command_response: command_response,
|
|
9
|
+
controller: controller
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
ServicePattern::Response.new(success: true)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def collection
|
|
16
|
+
@collection ||= model_class.accessible_by(@ability, :create)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
class ApiMaker::DestroyCommand < ApiMaker::BaseCommand
|
|
2
|
+
attr_reader :command, :model, :params, :serializer
|
|
3
|
+
|
|
4
|
+
def execute!
|
|
5
|
+
each_command do |command|
|
|
6
|
+
@command = command
|
|
7
|
+
@model = command.model
|
|
8
|
+
@params = command.args || {}
|
|
9
|
+
@serializer = serialized_resource(model)
|
|
10
|
+
|
|
11
|
+
if command.model.destroy
|
|
12
|
+
success_response
|
|
13
|
+
else
|
|
14
|
+
failure_response
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
ServicePattern::Response.new(success: true)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def failure_response
|
|
22
|
+
command.fail(
|
|
23
|
+
model: serializer.result,
|
|
24
|
+
success: false,
|
|
25
|
+
errors: model.errors.full_messages
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def serialized_resource(model)
|
|
30
|
+
ApiMaker::Serializer.new(ability: current_ability, args: api_maker_args, model: model)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def success_response
|
|
34
|
+
command.result(
|
|
35
|
+
model: serializer.result,
|
|
36
|
+
success: true
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class ApiMaker::DestroyCommandService < ApiMaker::CommandService
|
|
2
|
+
def execute
|
|
3
|
+
ApiMaker::DestroyCommand.execute_in_thread!(
|
|
4
|
+
ability: ability,
|
|
5
|
+
args: args,
|
|
6
|
+
collection: collection,
|
|
7
|
+
commands: commands,
|
|
8
|
+
command_response: command_response,
|
|
9
|
+
controller: controller
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
ServicePattern::Response.new(success: true)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def collection
|
|
16
|
+
@collection ||= model_class.accessible_by(@ability, :destroy).where(model_class.primary_key => ids)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ids
|
|
20
|
+
@commands.values.map { |command| command.fetch("primary_key") }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
class ApiMaker::GenerateReactNativeApiService < ApiMaker::ApplicationService
|
|
2
|
+
def execute
|
|
3
|
+
check_if_root_folder_defined
|
|
4
|
+
create_root_folder
|
|
5
|
+
create_model_files
|
|
6
|
+
|
|
7
|
+
ServicePattern::Response.new(success: true)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def root_folder
|
|
11
|
+
@root_folder ||= ApiMaker::Configuration.current.react_native_path
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def check_if_root_folder_defined
|
|
15
|
+
raise "No root folder detected" if root_folder.blank?
|
|
16
|
+
|
|
17
|
+
Dir.mkdir(model_path) unless Dir.exist?(model_path)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create_root_folder
|
|
21
|
+
Dir.mkdir(root_folder) unless Dir.exist?(root_folder)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def create_model_files
|
|
25
|
+
model_generator_service.models.each do |model|
|
|
26
|
+
next if model_generator_service.ignore_model?(model)
|
|
27
|
+
|
|
28
|
+
model_content_response = ApiMaker::ModelContentGeneratorService.execute!(model: model)
|
|
29
|
+
|
|
30
|
+
if model_content_response.success?
|
|
31
|
+
File.open(model_file(model), "w") { |fp| fp.write(model_content_response.result) }
|
|
32
|
+
else
|
|
33
|
+
puts model_content_response.errors.join(". ")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def model_generator_service
|
|
39
|
+
@model_generator_service ||= ApiMaker::ModelsGeneratorService.new
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def model_file(model)
|
|
43
|
+
resource_class = ApiMaker::MemoryStorage.current.resource_for_model(model)
|
|
44
|
+
File.join(model_path, "#{resource_class.short_name.underscore.dasherize}.js")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def model_path
|
|
48
|
+
@model_path ||= File.join(root_folder, "models")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def copy_base_model
|
|
52
|
+
files = %w[
|
|
53
|
+
api.js base-model.js cable-connection-pool.js cable-subscription.js cable-subscription-pool.js
|
|
54
|
+
collection.js commands-pool.js devise.js event-listener.jsx error-logger.js form-data-to-object.js logger.js
|
|
55
|
+
model-name.js models-response-reader.js result.js event-connection.jsx
|
|
56
|
+
]
|
|
57
|
+
path = File.join(__dir__, "..", "..", "..", "lib", "api_maker", "javascript")
|
|
58
|
+
|
|
59
|
+
copy_base_files(files, path, root_folder)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
class ApiMaker::IndexCommand < ApiMaker::BaseCommand
|
|
2
|
+
attr_reader :params
|
|
3
|
+
|
|
4
|
+
def execute!
|
|
5
|
+
ApiMaker::Configuration.profile("IndexCommand execute") do
|
|
6
|
+
each_command do |command|
|
|
7
|
+
@params = command.args || {}
|
|
8
|
+
|
|
9
|
+
set_collection
|
|
10
|
+
|
|
11
|
+
if params[:count]
|
|
12
|
+
count = @query.count
|
|
13
|
+
count = count.length if count.is_a?(Hash)
|
|
14
|
+
|
|
15
|
+
command.result(count: count)
|
|
16
|
+
else
|
|
17
|
+
collection = collection_from_query(@query.fix)
|
|
18
|
+
response = collection.as_json
|
|
19
|
+
include_pagination_data(response, @query)
|
|
20
|
+
|
|
21
|
+
command.result(response)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
ServicePattern::Response.new(success: true)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def filter_custom_accessible_by(collection)
|
|
30
|
+
return collection if params[:accessible_by].blank?
|
|
31
|
+
|
|
32
|
+
collection.accessible_by(current_ability, params[:accessible_by].to_sym)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def collection_from_query(collection)
|
|
36
|
+
ApiMaker::Configuration.profile("IndexCommand collection_from_query") do
|
|
37
|
+
select = parse_select(params[:select]&.permit!&.to_hash) if params[:select]
|
|
38
|
+
|
|
39
|
+
ApiMaker::CollectionSerializer.new(
|
|
40
|
+
ability: current_ability,
|
|
41
|
+
args: api_maker_args,
|
|
42
|
+
collection: collection,
|
|
43
|
+
include_param: params[:include],
|
|
44
|
+
select: select
|
|
45
|
+
).result
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def include_pagination_data(response, collection)
|
|
50
|
+
return if params[:page].blank?
|
|
51
|
+
|
|
52
|
+
response[:meta] = {
|
|
53
|
+
currentPage: collection.current_page,
|
|
54
|
+
totalCount: collection.try(:total_count) || collection.try(:total_entries),
|
|
55
|
+
totalPages: collection.total_pages
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def manage_through_relationship
|
|
60
|
+
return if params[:through].blank?
|
|
61
|
+
|
|
62
|
+
model_class = params[:through][:model].safe_constantize
|
|
63
|
+
through_model = model_class.accessible_by(current_ability).find(params[:through][:id])
|
|
64
|
+
association = ActiveRecord::Associations::Association.new(through_model, model_class.reflections.fetch(params[:through][:reflection]))
|
|
65
|
+
|
|
66
|
+
query_through = association.scope
|
|
67
|
+
query_through = query_through.accessible_by(current_ability)
|
|
68
|
+
query_through = filter_custom_accessible_by(query_through)
|
|
69
|
+
query_through
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# This converts the list of attributes to a hash that contains the data needed for the serializer (so the serializer doesn't have to do it for each model)
|
|
73
|
+
def parse_select(select)
|
|
74
|
+
new_select = {}
|
|
75
|
+
|
|
76
|
+
select.each do |model_collection_name, attributes|
|
|
77
|
+
model_class = model_collection_name.underscore.singularize.camelize
|
|
78
|
+
resource = "Resources::#{model_class}Resource".safe_constantize
|
|
79
|
+
raise "Resource not found for: #{model_collection_name}" unless resource
|
|
80
|
+
|
|
81
|
+
new_attributes = resource._attributes.select { |key| attributes.include?(key.to_s) }
|
|
82
|
+
new_select[resource.model_class] = new_attributes
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
new_select
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def set_collection
|
|
89
|
+
@query = manage_through_relationship || collection
|
|
90
|
+
@query = @query.distinct if params[:distinct]
|
|
91
|
+
@query = @query.ransack(params[:q]).result
|
|
92
|
+
@query = @query.limit(params[:limit]) if params[:limit].present?
|
|
93
|
+
@query = @query.page(params[:page]) if params[:page].present?
|
|
94
|
+
@query = filter_custom_accessible_by(@query)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class ApiMaker::IndexCommandService < ApiMaker::CommandService
|
|
2
|
+
def execute
|
|
3
|
+
ApiMaker::IndexCommand.execute_in_thread!(
|
|
4
|
+
ability: ability,
|
|
5
|
+
args: args,
|
|
6
|
+
collection: collection,
|
|
7
|
+
commands: commands,
|
|
8
|
+
command_response: command_response,
|
|
9
|
+
controller: controller
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
ServicePattern::Response.new(success: true)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def collection
|
|
16
|
+
@collection ||= model_class.accessible_by(@ability)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ids
|
|
20
|
+
@ids ||= @commands.values.map { |command| command.fetch("primary_key") }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class ApiMaker::JsMethodNamerService < ApiMaker::ApplicationService
|
|
2
|
+
def initialize(name:)
|
|
3
|
+
@name = name
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def execute
|
|
7
|
+
camelized = @name.to_s.camelize
|
|
8
|
+
new_name = "#{camelized[0..0].downcase}#{camelized[1..camelized.length]}"
|
|
9
|
+
ServicePattern::Response.new(result: new_name)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class ApiMaker::MemberCommandService < ApiMaker::CommandService
|
|
2
|
+
def execute
|
|
3
|
+
ability_name = @command_name.to_sym
|
|
4
|
+
collection = model_class.accessible_by(@ability, ability_name).where(model_class.primary_key => ids)
|
|
5
|
+
|
|
6
|
+
constant.execute_in_thread!(
|
|
7
|
+
ability: ability,
|
|
8
|
+
args: args,
|
|
9
|
+
collection: collection,
|
|
10
|
+
commands: commands,
|
|
11
|
+
command_response: command_response,
|
|
12
|
+
controller: controller
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
ServicePattern::Response.new(success: true)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def constant
|
|
19
|
+
@constant ||= "Commands::#{namespace}::#{@command_name.camelize}".constantize
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ids
|
|
23
|
+
@commands.values.map { |command| command.fetch("primary_key") }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
class ApiMaker::ModelContentGeneratorService < ApiMaker::ApplicationService
|
|
2
|
+
attr_reader :model
|
|
3
|
+
|
|
4
|
+
def initialize(model:)
|
|
5
|
+
@model = model
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def execute
|
|
9
|
+
if resource
|
|
10
|
+
ServicePattern::Response.new(result: model_content)
|
|
11
|
+
else
|
|
12
|
+
ServicePattern::Response.new(errors: ["No resource defined for #{model.name}"])
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def attributes
|
|
19
|
+
resource._attributes.map do |attribute, _data|
|
|
20
|
+
{name: attribute, type: model_type(attribute)}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def collection_commands
|
|
25
|
+
ApiMaker::Loader.load_everything
|
|
26
|
+
ApiMaker::MemoryStorage.current.storage_for(resource, :collection_commands)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def member_commands
|
|
30
|
+
ApiMaker::Loader.load_everything
|
|
31
|
+
ApiMaker::MemoryStorage.current.storage_for(resource, :member_commands)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def model_content
|
|
35
|
+
erb = ERB.new(File.read(model_template_path))
|
|
36
|
+
erb.filename = File.realpath(model_template_path)
|
|
37
|
+
erb.result(binding)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def model_template_path
|
|
41
|
+
File.join(__dir__, "..", "..", "..", "lib", "api_maker", "javascript", "model-template.js.erb")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def model_type(attribute_name)
|
|
45
|
+
model_type = model.columns_hash[attribute_name.to_s]&.type
|
|
46
|
+
model_type = :money if monetized_attributes.include?(attribute_name.to_s)
|
|
47
|
+
model_type ||= :unknown
|
|
48
|
+
model_type
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def monetized_attributes
|
|
52
|
+
@monetized_attributes ||= @model.try(:monetized_attributes).try(:map) { |attribute| attribute[0] } || []
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def reflections
|
|
56
|
+
@reflections ||= resource._relationships.map do |name, _data|
|
|
57
|
+
reflection = model.reflections.values.find { |reflection_i| reflection_i.name == name }
|
|
58
|
+
raise "Couldnt find reflection by that name: #{name}" unless reflection
|
|
59
|
+
|
|
60
|
+
reflection
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def reflection_has_many_parameters(reflection)
|
|
65
|
+
{
|
|
66
|
+
reflectionName: reflection.name,
|
|
67
|
+
model: "{{this}}",
|
|
68
|
+
modelName: reflection.class_name,
|
|
69
|
+
modelClass: "{{modelClass}}",
|
|
70
|
+
targetPathName: "/api_maker/#{reflection.klass.model_name.route_key}"
|
|
71
|
+
}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def reflection_has_many_parameters_query(reflection)
|
|
75
|
+
if reflection.options[:through]
|
|
76
|
+
{
|
|
77
|
+
params: {
|
|
78
|
+
through: {
|
|
79
|
+
model: model.name,
|
|
80
|
+
id: "{{id}}",
|
|
81
|
+
reflection: reflection.name
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else
|
|
86
|
+
{
|
|
87
|
+
ransack: {
|
|
88
|
+
"#{reflection.foreign_key}_eq" => "{{id}}"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def reflections_for_model_class_data
|
|
95
|
+
@reflections_for_model_class_data ||= reflections.map do |reflection|
|
|
96
|
+
{
|
|
97
|
+
className: reflection.class_name,
|
|
98
|
+
collectionName: ApiMaker::MemoryStorage.current.resource_for_model(reflection.klass).collection_name,
|
|
99
|
+
name: reflection.name,
|
|
100
|
+
macro: reflection.macro
|
|
101
|
+
}
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def resource
|
|
106
|
+
@resource ||= ApiMaker::MemoryStorage.current.resource_for_model(@model)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class ApiMaker::ModelsFinderService < ApiMaker::ApplicationService
|
|
2
|
+
def execute
|
|
3
|
+
ServicePattern::Response.new(result: models)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def models
|
|
9
|
+
result = []
|
|
10
|
+
Dir.glob(Rails.root.join("app", "api_maker", "resources", "**", "*.rb")) do |model_path|
|
|
11
|
+
next unless model_path.start_with?(Rails.root.to_s)
|
|
12
|
+
|
|
13
|
+
path_name = model_path.gsub(/\A#{Regexp.escape(Rails.root.to_s)}\/app\/api_maker\/resources\//, "").gsub(/\.rb\Z/, "")
|
|
14
|
+
next if path_name == "application_resource"
|
|
15
|
+
|
|
16
|
+
resource_class = "Resources::#{path_name.classify}".constantize
|
|
17
|
+
result << resource_class.model_class
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
result
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
class ApiMaker::ModelsGeneratorService < ApiMaker::ApplicationService
|
|
2
|
+
def execute
|
|
3
|
+
create_base_structure
|
|
4
|
+
copy_base_model
|
|
5
|
+
copy_base_controllers
|
|
6
|
+
|
|
7
|
+
models.each do |model|
|
|
8
|
+
next if ignore_model?(model)
|
|
9
|
+
|
|
10
|
+
model_content_response = ApiMaker::ModelContentGeneratorService.execute(model: model)
|
|
11
|
+
|
|
12
|
+
if model_content_response.success?
|
|
13
|
+
File.open(model_file(model), "w") { |fp| fp.write(model_content_response.result) }
|
|
14
|
+
else
|
|
15
|
+
puts model_content_response.errors.join(". ")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
ApiMaker::GenerateReactNativeApiService.execute! if ApiMaker::Configuration.current.react_native_path.present?
|
|
20
|
+
ServicePattern::Response.new(success: true)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def ignore_model?(model)
|
|
24
|
+
model.name.end_with?("::Translation") ||
|
|
25
|
+
model.name.start_with?("ActiveStorage::") ||
|
|
26
|
+
model.name.end_with?("::ApplicationRecord")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def models
|
|
30
|
+
ApiMaker::ModelsFinderService.execute!
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def api_maker_root_path
|
|
36
|
+
Rails.root.join("app", "javascript", "api-maker")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def controller_path
|
|
40
|
+
Rails.root.join("app", "controllers", "api_maker")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def copy_base_controllers
|
|
44
|
+
files = %w[devise_controller.rb]
|
|
45
|
+
path = File.join(__dir__, "..", "..", "controllers", "api_maker")
|
|
46
|
+
target_path = Rails.root.join("app", "controllers", "api_maker")
|
|
47
|
+
|
|
48
|
+
copy_base_files(files, path, target_path)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def copy_base_model
|
|
52
|
+
files = %w[
|
|
53
|
+
api.js base-model.js cable-connection-pool.js cable-subscription.js cable-subscription-pool.js collection.js
|
|
54
|
+
commands-pool.js event-created.jsx custom-error.js deserializer.js devise.js event-destroyed.jsx event-emitter-listener.jsx event-listener.jsx
|
|
55
|
+
event-updated.jsx error-logger.js form-data-to-object.js included.js key-value-store.js logger.js model-name.js models-response-reader.js
|
|
56
|
+
params.js result.js event-connection.jsx paginate.jsx sort-link.jsx updated-attribute.jsx resource-routes.jsx resource-route.jsx
|
|
57
|
+
session-status-updater.js bootstrap/attribute-row.jsx bootstrap/attribute-rows.jsx bootstrap/card.jsx
|
|
58
|
+
bootstrap/checkbox.jsx bootstrap/checkboxes.jsx bootstrap/money-input.jsx bootstrap/radio-buttons.jsx
|
|
59
|
+
bootstrap/select.jsx bootstrap/string-input.jsx bootstrap/live-table.jsx
|
|
60
|
+
]
|
|
61
|
+
path = File.join(__dir__, "..", "..", "..", "lib", "api_maker", "javascript")
|
|
62
|
+
target_path = api_maker_root_path
|
|
63
|
+
|
|
64
|
+
copy_base_files(files, path, target_path)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def copy_base_files(files, path, target_path)
|
|
68
|
+
files.each do |file|
|
|
69
|
+
base_model_source_path = File.join(path, file)
|
|
70
|
+
base_model_target_path = File.join(target_path, file)
|
|
71
|
+
|
|
72
|
+
base_model_target_dir = File.dirname(base_model_target_path)
|
|
73
|
+
FileUtils.mkdir_p(base_model_target_dir) unless File.exist?(base_model_target_dir)
|
|
74
|
+
|
|
75
|
+
if File.exist?(base_model_source_path)
|
|
76
|
+
content = File.read(base_model_source_path)
|
|
77
|
+
else
|
|
78
|
+
base_model_source_path << ".erb"
|
|
79
|
+
erb = ERB.new(File.read(base_model_source_path))
|
|
80
|
+
erb.filename = base_model_source_path
|
|
81
|
+
content = erb.result(binding)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
base_model_target_path = File.join(target_path, file)
|
|
85
|
+
|
|
86
|
+
File.open(base_model_target_path, "w") do |fp|
|
|
87
|
+
fp.write(content)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def create_base_structure
|
|
93
|
+
# Dont remove all the files. It messes up running Webpack Dev Servers which forces you to restart all the time.
|
|
94
|
+
# FileUtils.rm_rf(api_maker_root_path) if File.exist?(api_maker_root_path)
|
|
95
|
+
|
|
96
|
+
FileUtils.mkdir_p(api_maker_root_path.join("models"))
|
|
97
|
+
FileUtils.mkdir_p(controller_path) unless File.exist?(controller_path)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def model_file(model)
|
|
101
|
+
resource_class = ApiMaker::MemoryStorage.current.resource_for_model(model)
|
|
102
|
+
api_maker_root_path.join("models", "#{resource_class.short_name.underscore.dasherize}.js")
|
|
103
|
+
end
|
|
104
|
+
end
|